import type { StockDatabase, StockKey } from "./interfaces.js"; import { open } from "lmdbx"; const stockAggregatesDb = open({ path: "./stock-aggregates.db", // any options go here, we can turn on compression like this: compression: true, }); const stockExistenceDb = open({ path: "./stock-existence.db", // any options go here, we can turn on compression like this: compression: true, }); /** Largest possible key according to the `ordered-binary` (used by lmdbx) docs. */ const MAXIMUM_KEY = Buffer.from([0xff]); function makeStockDatabase(): StockDatabase { const stockDatabase: Omit = { getKeys: async ({ date, key }) => { if (key?.symbol) { return [key as StockKey]; } return stockExistenceDb .getRange({ start: [date], end: [date, MAXIMUM_KEY], }) .map(({ key }) => ({ symbol: key[1] })).asArray; }, getAggregates: async ({ key: { symbol }, date }) => { const startOfDayUnix = new Date(`${date}T00:00:00Z`).valueOf(); const endOfDayUnix = startOfDayUnix + 3600 * 24 * 1000; return stockAggregatesDb .getRange({ start: [symbol, startOfDayUnix], end: [symbol, endOfDayUnix], }) .map(({ key, value }) => ({ tsStart: key[1], open: value.open, close: value.close, high: value.high, low: value.low, })).asArray; }, getAggregate: async ({ key: { symbol }, tsStart }) => { return stockAggregatesDb.get([symbol, tsStart]); }, insertAggregates: async (aggregates) => { await stockExistenceDb.batch(() => { for (const aggregate of aggregates) { stockExistenceDb.put( [ new Date(aggregate.tsStart).toISOString().substring(0, 10), aggregate.key.symbol, ], null ); } }); await stockAggregatesDb.batch(() => { for (const aggregate of aggregates) { stockAggregatesDb.put([aggregate.key.symbol, aggregate.tsStart], { open: aggregate.open, close: aggregate.close, high: aggregate.high, low: aggregate.low, }); } }); }, getClosingPrice: async ({ key }) => { // no-op: not used since stocks don't have a "closing" price, unlike options. return 0; }, }; return { ...stockDatabase, getSymbols: stockDatabase.getKeys, }; } export const database: StockDatabase = makeStockDatabase();