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.
60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
import type { StockDatabase, StockKey } from "./stockdb.interfaces.js";
|
|
import type { Aggregate } from "./interfaces.js";
|
|
import { clickhouse, query } from "./lib/clickhouse.js";
|
|
|
|
function makeStockDatabase(): StockDatabase {
|
|
const stockDatabase: Omit<StockDatabase, "getSymbols"> = {
|
|
getKeys: async ({ date }) => {
|
|
return (
|
|
await query(`
|
|
SELECT DISTINCT symbol FROM stock_aggregates WHERE toDate(tsStart) = '${date}'
|
|
`)
|
|
).map(({ symbol }) => symbol);
|
|
},
|
|
getAggregates: async ({ key: symbol, date }) => {
|
|
return (
|
|
await query<Omit<Aggregate<StockKey>, "key">>(`
|
|
SELECT
|
|
toUnixTimestamp(tsStart) as tsStart,
|
|
open,
|
|
close,
|
|
high,
|
|
low
|
|
FROM stock_aggregates
|
|
WHERE symbol = '${symbol}'
|
|
AND toDate(tsStart) = '${date}'
|
|
ORDER BY tsStart ASC
|
|
`)
|
|
).map((aggregate) => ({
|
|
...aggregate,
|
|
tsStart: aggregate.tsStart * 1000, // unfortunately, `toUnixTimestamp` only returns second-precision
|
|
}));
|
|
},
|
|
insertAggregates: async (aggregates) => {
|
|
// stock existence is taken care of by clickhouse materialized view
|
|
await clickhouse.insert({
|
|
table: "stock_aggregates",
|
|
values: aggregates.map(({ key, tsStart, open, close, high, low }) => ({
|
|
symbol: key,
|
|
tsStart,
|
|
open,
|
|
close,
|
|
high,
|
|
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 stockDatabase: StockDatabase = makeStockDatabase();
|