You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
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<StockDatabase, "getSymbols"> = {
|
|
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();
|