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.

154 lines
3.7 KiB
TypeScript

import type { CalendarDatabase } from "./calendardb.interfaces.js";
import { open } from "lmdbx";
const calendarAggregatesDb = open({
path: "/tmp/calendar-aggregates.db",
// any options go here, we can turn on compression like this:
compression: true,
});
const calendarExistenceDb = open({
path: "/tmp/calendar-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 makeCalendarDatabase(): CalendarDatabase {
const calendarDatabase: Omit<CalendarDatabase, "getCalendars"> = {
getKeys: async ({ key: { symbol }, date }) => {
return calendarExistenceDb
.getRange({
start: [date, symbol],
end: [date, symbol, MAXIMUM_KEY],
})
.map(({ key }) => ({
symbol,
frontExpirationDate: key[2],
backExpirationDate: key[3],
strike: key[4],
type: key[5],
})).asArray;
},
getAggregates: async ({
key: { symbol, frontExpirationDate, backExpirationDate, strike, type },
date,
}) => {
const startOfDayUnix = new Date(`${date}T00:00:00Z`).valueOf();
const endOfDayUnix = startOfDayUnix + 3600 * 24 * 1000;
return calendarAggregatesDb
.getRange({
start: [
symbol,
frontExpirationDate,
backExpirationDate,
strike,
type,
startOfDayUnix,
],
end: [
symbol,
frontExpirationDate,
backExpirationDate,
strike,
type,
endOfDayUnix,
],
})
.map(({ value }) => ({
tsStart: value.tsStart,
open: value.open,
close: value.close,
high: value.high,
low: value.low,
})).asArray;
},
insertAggregates: async (aggregates) => {
await calendarExistenceDb.batch(() => {
for (const aggregate of aggregates) {
calendarExistenceDb.put(
[
new Date(aggregate.tsStart).toISOString().substring(0, 10),
aggregate.key.symbol,
aggregate.key.frontExpirationDate,
aggregate.key.backExpirationDate,
aggregate.key.strike,
aggregate.key.type,
],
null,
);
}
});
await calendarAggregatesDb.batch(() => {
for (const aggregate of aggregates) {
calendarAggregatesDb.put(
[
aggregate.key.symbol,
aggregate.key.frontExpirationDate,
aggregate.key.backExpirationDate,
aggregate.key.strike,
aggregate.key.type,
aggregate.tsStart,
],
{
open: aggregate.open,
close: aggregate.close,
high: aggregate.high,
low: aggregate.low,
},
);
}
});
},
getClosingPrice: async ({
key: { symbol, strike, type, frontExpirationDate, backExpirationDate },
}) => {
const startOfLastHourUnix = new Date(
`${frontExpirationDate}T00:00:00Z`,
).valueOf();
const endOfLastHourUnix = startOfLastHourUnix + 3600 * 1000;
let minPrice = 0;
for (const { value } of calendarAggregatesDb.getRange({
start: [
symbol,
frontExpirationDate,
backExpirationDate,
strike,
type,
startOfLastHourUnix,
],
end: [
symbol,
frontExpirationDate,
backExpirationDate,
strike,
type,
endOfLastHourUnix,
],
})) {
if (value.close < minPrice || minPrice === 0) {
minPrice = value.close;
}
}
return minPrice;
},
getTargetPriceByProbability: async ({
symbol,
calendarSpan,
strikePercentageFromTheMoney,
historicalProbabilityOfSuccess,
}) => {
return 0.24;
},
};
return {
...calendarDatabase,
getCalendars: calendarDatabase.getKeys,
};
}
export const calendarDatabase: CalendarDatabase = makeCalendarDatabase();