improve and extract `nextDate()` function; improve clickhouse-to-lmdbx sync script
parent
85cafd985d
commit
bf094de461
@ -0,0 +1,155 @@
|
||||
import { optionContractDatabase } from "./optiondb.lmdbx.js";
|
||||
import type { CalendarDatabase } from "./calendardb.interfaces.js";
|
||||
|
||||
/** 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 }) => {
|
||||
const optionContracts = await optionContractDatabase.getOptionContracts({
|
||||
date,
|
||||
key: { symbol },
|
||||
});
|
||||
return optionContracts.flatMap(
|
||||
(frontOptionContract, i, optionContracts) =>
|
||||
optionContracts
|
||||
.filter((_, j) => i !== j)
|
||||
.map((backOptionContract) => ({
|
||||
symbol,
|
||||
frontExpirationDate: frontOptionContract.expirationDate,
|
||||
backExpirationDate: backOptionContract.expirationDate,
|
||||
strike: frontOptionContract.strike,
|
||||
type: frontOptionContract.type,
|
||||
})),
|
||||
);
|
||||
},
|
||||
getAggregates: async ({
|
||||
key: { symbol, frontExpirationDate, backExpirationDate, strike, type },
|
||||
date,
|
||||
}) => {
|
||||
const frontOptionContractAggregates =
|
||||
await optionContractDatabase.getAggregates({
|
||||
date,
|
||||
key: { symbol, expirationDate: frontExpirationDate, strike, type },
|
||||
});
|
||||
const backOptionContractAggregates =
|
||||
await optionContractDatabase.getAggregates({
|
||||
date,
|
||||
key: { symbol, expirationDate: backExpirationDate, strike, type },
|
||||
});
|
||||
const calendarAggregates = [];
|
||||
let i = 0;
|
||||
let j = 0;
|
||||
while (
|
||||
i < frontOptionContractAggregates.length &&
|
||||
j < backOptionContractAggregates.length
|
||||
) {
|
||||
if (
|
||||
frontOptionContractAggregates[i].tsStart ===
|
||||
backOptionContractAggregates[j].tsStart
|
||||
) {
|
||||
calendarAggregates.push({
|
||||
tsStart: frontOptionContractAggregates[i].tsStart,
|
||||
open:
|
||||
backOptionContractAggregates[j].open -
|
||||
frontOptionContractAggregates[i].open,
|
||||
close:
|
||||
backOptionContractAggregates[j].close -
|
||||
frontOptionContractAggregates[i].close,
|
||||
// the high and low are not exactly correct since we don't know if each contract's high and low happened ata the same moment as the other:
|
||||
high:
|
||||
backOptionContractAggregates[j].high -
|
||||
frontOptionContractAggregates[i].high,
|
||||
low:
|
||||
backOptionContractAggregates[j].low -
|
||||
frontOptionContractAggregates[i].low,
|
||||
});
|
||||
i++;
|
||||
j++;
|
||||
} else if (
|
||||
frontOptionContractAggregates[i].tsStart >
|
||||
backOptionContractAggregates[j].tsStart
|
||||
) {
|
||||
j++;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return calendarAggregates;
|
||||
},
|
||||
insertAggregates: async (aggregates) => {
|
||||
// right now, no-op
|
||||
},
|
||||
getClosingPrice: async ({
|
||||
key: { symbol, strike, type, frontExpirationDate, backExpirationDate },
|
||||
}) => {
|
||||
const startOfLastHourUnix = new Date(
|
||||
`${frontExpirationDate}T00:00:00Z`,
|
||||
).valueOf();
|
||||
const endOfLastHourUnix = startOfLastHourUnix + 3600 * 1000;
|
||||
const frontOptionContractAggregates = (
|
||||
await optionContractDatabase.getAggregates({
|
||||
date: frontExpirationDate,
|
||||
key: { symbol, expirationDate: frontExpirationDate, strike, type },
|
||||
})
|
||||
).filter(
|
||||
({ tsStart }) =>
|
||||
tsStart >= startOfLastHourUnix && tsStart < endOfLastHourUnix,
|
||||
);
|
||||
const backOptionContractAggregates = (
|
||||
await optionContractDatabase.getAggregates({
|
||||
date: frontExpirationDate,
|
||||
key: { symbol, expirationDate: backExpirationDate, strike, type },
|
||||
})
|
||||
).filter(
|
||||
({ tsStart }) =>
|
||||
tsStart >= startOfLastHourUnix && tsStart < endOfLastHourUnix,
|
||||
);
|
||||
let i = 0;
|
||||
let j = 0;
|
||||
let minPrice = 0;
|
||||
while (
|
||||
i < frontOptionContractAggregates.length &&
|
||||
j < backOptionContractAggregates.length
|
||||
) {
|
||||
if (
|
||||
frontOptionContractAggregates[i].tsStart ===
|
||||
backOptionContractAggregates[j].tsStart
|
||||
) {
|
||||
const calendarClosePrice =
|
||||
backOptionContractAggregates[j].close -
|
||||
frontOptionContractAggregates[j].close;
|
||||
if (calendarClosePrice < minPrice || minPrice === 0) {
|
||||
minPrice = calendarClosePrice;
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
} else if (
|
||||
frontOptionContractAggregates[i].tsStart >
|
||||
backOptionContractAggregates[j].tsStart
|
||||
) {
|
||||
j++;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return minPrice;
|
||||
},
|
||||
getTargetPriceByProbability: async ({
|
||||
symbol,
|
||||
calendarSpan,
|
||||
strikePercentageFromTheMoney,
|
||||
historicalProbabilityOfSuccess,
|
||||
}) => {
|
||||
return 0.24;
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
...calendarDatabase,
|
||||
getCalendars: calendarDatabase.getKeys,
|
||||
};
|
||||
}
|
||||
|
||||
export const calendarDatabase: CalendarDatabase = makeCalendarDatabase();
|
@ -0,0 +1,6 @@
|
||||
export function nextDate(date: string) {
|
||||
const [year, month, day] = date.split('-').map(Number);
|
||||
const nextDay = new Date(Date.UTC(year, month - 1, day + 1));
|
||||
const nextDateString = nextDay.toISOString().substring(0, 10);
|
||||
return nextDateString;
|
||||
}
|
Loading…
Reference in New Issue