fix: `getClosingPrice()` for two calendardb modules

main
avraham 9 months ago
parent 5b3e9f85f6
commit 8d908521fd

@ -2,67 +2,67 @@ import type { CalendarDatabase } from "./interfaces.js";
import { open } from "lmdbx"; import { open } from "lmdbx";
const calendarAggregatesDb = open({ const calendarAggregatesDb = open({
path: "./calendar-aggregates.db", path: "./calendar-aggregates.db",
compression: true, compression: true,
}); });
const calendarExistenceDb = open({ const calendarExistenceDb = open({
path: "./calendar-existence.db", path: "./calendar-existence.db",
compression: true, compression: true,
}); });
/** Largest possible key according to the `ordered-binary` (used by lmdbx) docs. */ /** Largest possible key according to the `ordered-binary` (used by lmdbx) docs. */
const MAXIMUM_KEY = Buffer.from([0xff]); const MAXIMUM_KEY = Buffer.from([0xff]);
function makeCalendarDatabase(): CalendarDatabase { function makeCalendarDatabase(): CalendarDatabase {
const calendarDatabase: Omit<CalendarDatabase, "getCalendars"> = { const calendarDatabase: Omit<CalendarDatabase, "getCalendars"> = {
getKeys: async ({ key: { symbol }, date }) => { getKeys: async ({ key: { symbol }, date }) => {
return calendarExistenceDb return calendarExistenceDb
.getRange({ .getRange({
start: [date, symbol], start: [date, symbol],
end: [date, symbol, MAXIMUM_KEY], end: [date, symbol, MAXIMUM_KEY],
}) })
.map(({ key }) => ({ .map(({ key }) => ({
symbol, symbol,
frontExpirationDate: key[2], frontExpirationDate: key[2],
backExpirationDate: key[3], backExpirationDate: key[3],
strike: key[4], strike: key[4],
type: key[5], type: key[5],
})).asArray; })).asArray;
}, },
getAggregates: async ({ getAggregates: async ({
key: { symbol, frontExpirationDate, backExpirationDate, strike, type }, key: { symbol, frontExpirationDate, backExpirationDate, strike, type },
date, date,
}) => { }) => {
const startOfDayUnix = new Date(`${date}T00:00:00Z`).valueOf(); const startOfDayUnix = new Date(`${date}T00:00:00Z`).valueOf();
const endOfDayUnix = startOfDayUnix + 3600 * 24 * 1000; const endOfDayUnix = startOfDayUnix + 3600 * 24 * 1000;
return calendarAggregatesDb return calendarAggregatesDb
.getRange({ .getRange({
start: [ start: [
symbol, symbol,
frontExpirationDate, frontExpirationDate,
backExpirationDate, backExpirationDate,
strike, strike,
type, type,
startOfDayUnix, startOfDayUnix,
], ],
end: [ end: [
symbol, symbol,
frontExpirationDate, frontExpirationDate,
backExpirationDate, backExpirationDate,
strike, strike,
type, type,
endOfDayUnix, endOfDayUnix,
], ],
}) })
.map(({ value }) => ({ .map(({ value }) => ({
tsStart: value.tsStart, tsStart: value.tsStart,
open: value.open, open: value.open,
close: value.close, close: value.close,
high: value.high, high: value.high,
low: value.low, low: value.low,
})).asArray; })).asArray;
}, },
getAggregate: async ({ getAggregate: async ({
key: { symbol, frontExpirationDate, backExpirationDate, strike, type }, key: { symbol, frontExpirationDate, backExpirationDate, strike, type },
tsStart, tsStart,
@ -76,89 +76,91 @@ function makeCalendarDatabase(): CalendarDatabase {
tsStart, tsStart,
]); ]);
}, },
insertAggregates: async (aggregates) => { insertAggregates: async (aggregates) => {
await calendarExistenceDb.batch(() => { await calendarExistenceDb.batch(() => {
for (const aggregate of aggregates) { for (const aggregate of aggregates) {
calendarExistenceDb.put( calendarExistenceDb.put(
[ [
new Date(aggregate.tsStart).toISOString().substring(0, 10), new Date(aggregate.tsStart).toISOString().substring(0, 10),
aggregate.key.symbol, aggregate.key.symbol,
aggregate.key.frontExpirationDate, aggregate.key.frontExpirationDate,
aggregate.key.backExpirationDate, aggregate.key.backExpirationDate,
aggregate.key.strike, aggregate.key.strike,
aggregate.key.type, aggregate.key.type,
], ],
null null
); );
} }
}); });
await calendarAggregatesDb.batch(() => { await calendarAggregatesDb.batch(() => {
for (const aggregate of aggregates) { for (const aggregate of aggregates) {
calendarAggregatesDb.put( calendarAggregatesDb.put(
[ [
aggregate.key.symbol, aggregate.key.symbol,
aggregate.key.frontExpirationDate, aggregate.key.frontExpirationDate,
aggregate.key.backExpirationDate, aggregate.key.backExpirationDate,
aggregate.key.strike, aggregate.key.strike,
aggregate.key.type, aggregate.key.type,
aggregate.tsStart, aggregate.tsStart,
], ],
{ {
open: aggregate.open, open: aggregate.open,
close: aggregate.close, close: aggregate.close,
high: aggregate.high, high: aggregate.high,
low: aggregate.low, low: aggregate.low,
} }
); );
} }
}); });
}, },
getClosingPrice: async ({ getClosingPrice: async ({
key: { symbol, strike, type, frontExpirationDate, backExpirationDate }, key: { symbol, strike, type, frontExpirationDate, backExpirationDate },
}) => { }) => {
const startOfLastHourUnix = new Date( const startOfExpirationDateUnix = new Date(
`${frontExpirationDate}T00:00:00Z`, `${frontExpirationDate}T23:59:59Z`
).valueOf(); ).valueOf();
const endOfLastHourUnix = startOfLastHourUnix + 3600 * 1000; const endOfExpirationDateUnix = new Date(
let minPrice = 0; `${frontExpirationDate}T00:00:00Z`
for (const { value } of calendarAggregatesDb.getRange({ ).valueOf();
start: [ for (const { value } of calendarAggregatesDb.getRange({
symbol, start: [
frontExpirationDate, symbol,
backExpirationDate, frontExpirationDate,
strike, backExpirationDate,
type, strike,
startOfLastHourUnix, type,
], startOfExpirationDateUnix,
end: [ ],
symbol, end: [
frontExpirationDate, symbol,
backExpirationDate, frontExpirationDate,
strike, backExpirationDate,
type, strike,
endOfLastHourUnix, type,
], endOfExpirationDateUnix,
})) { ],
if (value.close < minPrice || minPrice === 0) { reverse: true,
minPrice = value.close; })) {
} if (value.close > 0) {
} return value.close;
return minPrice; }
}, }
getTargetPriceByProbability: async ({ return 0;
symbol, },
calendarSpan, getTargetPriceByProbability: async ({
strikePercentageFromTheMoney, symbol,
historicalProbabilityOfSuccess, calendarSpan,
}) => { strikePercentageFromTheMoney,
return 0.24; historicalProbabilityOfSuccess,
}, }) => {
}; return 0.24;
},
};
return { return {
...calendarDatabase, ...calendarDatabase,
getCalendars: calendarDatabase.getKeys, getCalendars: calendarDatabase.getKeys,
}; };
} }
export const calendarDatabase: CalendarDatabase = makeCalendarDatabase(); export const calendarDatabase: CalendarDatabase = makeCalendarDatabase();

@ -93,9 +93,10 @@ function makeCalendarDatabase(): CalendarDatabase {
getClosingPrice: async ({ getClosingPrice: async ({
key: { symbol, strike, type, frontExpirationDate, backExpirationDate }, key: { symbol, strike, type, frontExpirationDate, backExpirationDate },
}) => { }) => {
// get unix timestamp, in milliseconds, of the start of the last hour, which is 03:30PM in the `America/New_York` timezone on the front expiration date:
const startOfLastHourUnix = new Date( const startOfLastHourUnix = new Date(
`${frontExpirationDate}T00:00:00Z`, `${frontExpirationDate}T19:30:00Z`
).valueOf(); ).getTime();
const endOfLastHourUnix = startOfLastHourUnix + 3600 * 1000; const endOfLastHourUnix = startOfLastHourUnix + 3600 * 1000;
const frontOptionContractAggregates = ( const frontOptionContractAggregates = (
await optionContractDatabase.getAggregates({ await optionContractDatabase.getAggregates({
@ -104,7 +105,7 @@ function makeCalendarDatabase(): CalendarDatabase {
}) })
).filter( ).filter(
({ tsStart }) => ({ tsStart }) =>
tsStart >= startOfLastHourUnix && tsStart < endOfLastHourUnix, tsStart >= startOfLastHourUnix && tsStart < endOfLastHourUnix
); );
const backOptionContractAggregates = ( const backOptionContractAggregates = (
await optionContractDatabase.getAggregates({ await optionContractDatabase.getAggregates({
@ -113,7 +114,7 @@ function makeCalendarDatabase(): CalendarDatabase {
}) })
).filter( ).filter(
({ tsStart }) => ({ tsStart }) =>
tsStart >= startOfLastHourUnix && tsStart < endOfLastHourUnix, tsStart >= startOfLastHourUnix && tsStart < endOfLastHourUnix
); );
let i = 0; let i = 0;
let j = 0; let j = 0;
@ -128,7 +129,7 @@ function makeCalendarDatabase(): CalendarDatabase {
) { ) {
const calendarClosePrice = const calendarClosePrice =
backOptionContractAggregates[j].close - backOptionContractAggregates[j].close -
frontOptionContractAggregates[j].close; frontOptionContractAggregates[i].close;
if (calendarClosePrice < minPrice || minPrice === 0) { if (calendarClosePrice < minPrice || minPrice === 0) {
minPrice = calendarClosePrice; minPrice = calendarClosePrice;
} }

Loading…
Cancel
Save