Compare commits

..

2 Commits

Author SHA1 Message Date
avraham eba5344b15 fix biome kvetches 2024-08-02 17:00:35 -04:00
avraham 39bb6c85f8 fix biome kvetches 2024-08-02 17:00:01 -04:00
5 changed files with 629 additions and 621 deletions
+10 -9
View File
@@ -86,7 +86,7 @@ function chooseStrike(strike: string) {
.query({ .query({
underlying: chosenUnderlying.value, underlying: chosenUnderlying.value,
expirationDate: chosenExpiration.value, expirationDate: chosenExpiration.value,
strike: parseFloat(strike), strike: Number.parseFloat(strike),
}) })
.then((getOpensForOptionContractResponse) => { .then((getOpensForOptionContractResponse) => {
optionContractUplotData.value = getOpensForOptionContractResponse; optionContractUplotData.value = getOpensForOptionContractResponse;
@@ -201,8 +201,9 @@ export function CalendarOptimizer() {
</Paper> </Paper>
</Grid> </Grid>
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Paper elevation={3} sx={{ p: 3, height: '100%' }}> <Paper elevation={3} sx={{ p: 3, height: "100%" }}>
{chosenUnderlying.value !== null && underlyingUplotData.value.length > 0 ? ( {chosenUnderlying.value !== null &&
underlyingUplotData.value.length > 0 ? (
<Scatter <Scatter
data={{ data={{
datasets: [ datasets: [
@@ -220,7 +221,7 @@ export function CalendarOptimizer() {
text: "Time", text: "Time",
}, },
ticks: { ticks: {
callback: function (value, index, ticks) { callback: (value, index, ticks) => {
return new Date((value as number) * 1000) return new Date((value as number) * 1000)
.toISOString() .toISOString()
.substring(0, 10); .substring(0, 10);
@@ -230,8 +231,8 @@ export function CalendarOptimizer() {
y: { y: {
beginAtZero: false, beginAtZero: false,
ticks: { ticks: {
callback: function (value, index, ticks) { callback: (value, index, ticks) => {
return "$" + value.toString(); return `$${value.toString()}`;
}, },
}, },
}, },
@@ -287,7 +288,7 @@ export function CalendarOptimizer() {
text: "Time", text: "Time",
}, },
ticks: { ticks: {
callback: function (value, index, ticks) { callback: (value, index, ticks) => {
return new Date((value as number) * 1000) return new Date((value as number) * 1000)
.toISOString() .toISOString()
.substring(0, 10); .substring(0, 10);
@@ -297,8 +298,8 @@ export function CalendarOptimizer() {
y: { y: {
beginAtZero: false, beginAtZero: false,
ticks: { ticks: {
callback: function (value, index, ticks) { callback: (value, index, ticks) => {
return "$" + value.toString(); return `$${value.toString()}`;
}, },
}, },
}, },
+53 -46
View File
@@ -49,20 +49,20 @@ const maxChartPrice = computed(() =>
Math.max( Math.max(
Math.max.apply( Math.max.apply(
null, null,
historicalCalendarQuoteChartData.value.map((d) => d.y) historicalCalendarQuoteChartData.value.map((d) => d.y),
), ),
Math.max.apply( Math.max.apply(
null, null,
historicalCalendarExitQuoteChartData.value.map((d) => d.y) historicalCalendarExitQuoteChartData.value.map((d) => d.y),
) ),
) ),
); );
const maxN = computed(() => const maxN = computed(() =>
Math.max.apply( Math.max.apply(
null, null,
historicalCalendarExitQuoteChartData.value.map((d) => d.n) historicalCalendarExitQuoteChartData.value.map((d) => d.n),
) ),
); );
const refreshHistoricalStockQuoteChartData = () => { const refreshHistoricalStockQuoteChartData = () => {
@@ -134,16 +134,19 @@ const handleUnderlyingChange = (e) => {
} }
}; };
const handleDaysToFrontExpirationChange = (e) => { const handleDaysToFrontExpirationChange = (e) => {
if (chosenDaysToFrontExpiration.value !== parseInt(e.target.value)) { if (chosenDaysToFrontExpiration.value !== Number.parseInt(e.target.value)) {
chosenDaysToFrontExpiration.value = parseInt(e.target.value); chosenDaysToFrontExpiration.value = Number.parseInt(e.target.value);
refreshHistoricalCalendarQuoteChartData(); refreshHistoricalCalendarQuoteChartData();
} }
}; };
const handleDaysBetweenFrontAndBackExpirationChange = (e) => { const handleDaysBetweenFrontAndBackExpirationChange = (e) => {
if ( if (
chosenDaysBetweenFrontAndBackExpiration.value !== parseInt(e.target.value) chosenDaysBetweenFrontAndBackExpiration.value !==
Number.parseInt(e.target.value)
) { ) {
chosenDaysBetweenFrontAndBackExpiration.value = parseInt(e.target.value); chosenDaysBetweenFrontAndBackExpiration.value = Number.parseInt(
e.target.value,
);
refreshHistoricalCalendarQuoteChartData(); refreshHistoricalCalendarQuoteChartData();
refreshHistoricalCalendarExitQuoteChartData(); refreshHistoricalCalendarExitQuoteChartData();
} }
@@ -151,10 +154,10 @@ const handleDaysBetweenFrontAndBackExpirationChange = (e) => {
const handleStrikePercentageFromUnderlyingPriceChange = (e) => { const handleStrikePercentageFromUnderlyingPriceChange = (e) => {
if ( if (
chosenStrikePercentageFromUnderlyingPrice.value !== chosenStrikePercentageFromUnderlyingPrice.value !==
parseFloat(e.target.value) Number.parseFloat(e.target.value)
) { ) {
chosenStrikePercentageFromUnderlyingPrice.value = parseFloat( chosenStrikePercentageFromUnderlyingPrice.value = Number.parseFloat(
e.target.value e.target.value,
); );
refreshHistoricalCalendarQuoteChartData(); refreshHistoricalCalendarQuoteChartData();
} }
@@ -162,17 +165,17 @@ const handleStrikePercentageFromUnderlyingPriceChange = (e) => {
const handleStrikePercentageFromUnderlyingPriceRadiusChange = (e) => { const handleStrikePercentageFromUnderlyingPriceRadiusChange = (e) => {
if ( if (
chosenStrikePercentageFromUnderlyingPriceRadius.value !== chosenStrikePercentageFromUnderlyingPriceRadius.value !==
parseFloat(e.target.value) Number.parseFloat(e.target.value)
) { ) {
chosenStrikePercentageFromUnderlyingPriceRadius.value = parseFloat( chosenStrikePercentageFromUnderlyingPriceRadius.value = Number.parseFloat(
e.target.value e.target.value,
); );
refreshHistoricalCalendarQuoteChartData(); refreshHistoricalCalendarQuoteChartData();
} }
}; };
const handleExitToFrontExpirationChange = (e) => { const handleExitToFrontExpirationChange = (e) => {
if (chosenExitToFrontExpiration.value !== parseInt(e.target.value)) { if (chosenExitToFrontExpiration.value !== Number.parseInt(e.target.value)) {
chosenExitToFrontExpiration.value = parseInt(e.target.value); chosenExitToFrontExpiration.value = Number.parseInt(e.target.value);
refreshHistoricalCalendarExitQuoteChartData(); refreshHistoricalCalendarExitQuoteChartData();
} }
}; };
@@ -260,7 +263,9 @@ export function HistoricalCalendarPrices() {
label="Strike % Radius" label="Strike % Radius"
type="number" type="number"
value={chosenStrikePercentageFromUnderlyingPriceRadius.value} value={chosenStrikePercentageFromUnderlyingPriceRadius.value}
onChange={handleStrikePercentageFromUnderlyingPriceRadiusChange} onChange={
handleStrikePercentageFromUnderlyingPriceRadiusChange
}
InputProps={{ endAdornment: "%" }} InputProps={{ endAdornment: "%" }}
/> />
</Grid> </Grid>
@@ -280,25 +285,33 @@ export function HistoricalCalendarPrices() {
label="Lookback Period Start" label="Lookback Period Start"
type="date" type="date"
value={chosenLookbackPeriodStart.value} value={chosenLookbackPeriodStart.value}
onChange={(e) => handleLookbackPeriodStartChange({ target: { value: e.target.value } })} onChange={(e) =>
handleLookbackPeriodStartChange({
target: { value: e.target.value },
})
}
InputLabelProps={{ shrink: true }} InputLabelProps={{ shrink: true }}
/> />
</Grid> </Grid>
<Grid item xs={6}> <Grid item xs={6}>
<TextField <TextField
fullWidth fullWidth
label="Lookback Period End" label="Lookback Period End"
type="date" type="date"
value={chosenLookbackPeriodEnd.value} value={chosenLookbackPeriodEnd.value}
onChange={(e) => handleLookbackPeriodEndChange({ target: { value: e.target.value } })} onChange={(e) =>
handleLookbackPeriodEndChange({
target: { value: e.target.value },
})
}
InputLabelProps={{ shrink: true }} InputLabelProps={{ shrink: true }}
/> />
</Grid> </Grid>
</Grid> </Grid>
</Paper> </Paper>
</Grid> </Grid>
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Paper elevation={3} sx={{ p: 3, height: '100%' }}> <Paper elevation={3} sx={{ p: 3, height: "100%" }}>
{chosenUnderlying.value !== null && {chosenUnderlying.value !== null &&
historicalStockQuoteChartData.value.length > 0 ? ( historicalStockQuoteChartData.value.length > 0 ? (
<Scatter <Scatter
@@ -318,11 +331,10 @@ export function HistoricalCalendarPrices() {
text: "Time", text: "Time",
}, },
ticks: { ticks: {
callback: function (value, index, ticks) { callback: (value, index, ticks) =>
return new Date((value as number) * 1000) new Date((value as number) * 1000)
.toISOString() .toISOString()
.substring(0, 10); .substring(0, 10),
},
}, },
min: min:
new Date(chosenLookbackPeriodStart.value).getTime() / new Date(chosenLookbackPeriodStart.value).getTime() /
@@ -334,9 +346,8 @@ export function HistoricalCalendarPrices() {
y: { y: {
beginAtZero: false, beginAtZero: false,
ticks: { ticks: {
callback: function (value, index, ticks) { callback: (value, index, ticks) =>
return "$" + value.toString(); `$${value.toString()}`,
},
}, },
}, },
}, },
@@ -389,11 +400,10 @@ export function HistoricalCalendarPrices() {
text: "Time", text: "Time",
}, },
ticks: { ticks: {
callback: function (value, index, ticks) { callback: (value, index, ticks) =>
return new Date((value as number) * 1000) new Date((value as number) * 1000)
.toISOString() .toISOString()
.substring(0, 10); .substring(0, 10),
},
}, },
min: min:
new Date(chosenLookbackPeriodStart.value).getTime() / new Date(chosenLookbackPeriodStart.value).getTime() /
@@ -405,9 +415,8 @@ export function HistoricalCalendarPrices() {
y: { y: {
beginAtZero: true, beginAtZero: true,
ticks: { ticks: {
callback: function (value, index, ticks) { callback: (value, index, ticks) =>
return "$" + value.toString(); `$${value.toString()}`,
},
}, },
min: 0, min: 0,
max: maxChartPrice.value, max: maxChartPrice.value,
@@ -458,17 +467,15 @@ export function HistoricalCalendarPrices() {
text: "%-From-the-Money", text: "%-From-the-Money",
}, },
ticks: { ticks: {
callback: function (value, index, ticks) { callback: (value, index, ticks) =>
return value.toString() + "%"; `${value.toString()}%`,
},
}, },
}, },
y: { y: {
beginAtZero: true, beginAtZero: true,
ticks: { ticks: {
callback: function (value, index, ticks) { callback: (value, index, ticks) =>
return "$" + value.toString(); `$${value.toString()}`,
},
}, },
min: 0, min: 0,
max: maxChartPrice.value, max: maxChartPrice.value,
@@ -477,7 +484,7 @@ export function HistoricalCalendarPrices() {
elements: { elements: {
point: { point: {
borderWidth: 0, borderWidth: 0,
backgroundColor: function (context) { backgroundColor: (context) => {
const n = ( const n = (
context.raw as { x: number; y: number; n: number } context.raw as { x: number; y: number; n: number }
).n; ).n;
+7 -7
View File
@@ -48,7 +48,7 @@ export async function backtest({
...calendar, ...calendar,
}, },
date, date,
}), })
); );
} }
// for each minute of that day for which we have a stock candlestick: // for each minute of that day for which we have a stock candlestick:
@@ -57,12 +57,12 @@ export async function backtest({
// filter-out calendars that are far-from-the-money (10%) // filter-out calendars that are far-from-the-money (10%)
const calendarsNearTheMoney = calendars.filter( const calendarsNearTheMoney = calendars.filter(
({ strike }) => ({ strike }) =>
Math.abs((stockAggregate.open - strike) / stockAggregate.open) < 0.1, Math.abs((stockAggregate.open - strike) / stockAggregate.open) < 0.1
); );
// for each relevant calendar on that day: // for each relevant calendar on that day:
for (const calendar of calendarsNearTheMoney) { for (const calendar of calendarsNearTheMoney) {
const strikePercentageFromTheMoney = Math.abs( const strikePercentageFromTheMoney = Math.abs(
(stockAggregate.open - calendar.strike) / stockAggregate.open, (stockAggregate.open - calendar.strike) / stockAggregate.open
); );
/** In days. */ /** In days. */
const calendarSpan = const calendarSpan =
@@ -78,14 +78,14 @@ export async function backtest({
}); });
const calendarAggregates = calendarsAggregates.get(calendar); const calendarAggregates = calendarsAggregates.get(calendar);
const calendarAggregateAtCurrentTime = calendarAggregates.find( const calendarAggregateAtCurrentTime = calendarAggregates.find(
({ tsStart }) => tsStart === stockAggregate.tsStart, ({ tsStart }) => tsStart === stockAggregate.tsStart
); );
// if there exists a matching calendar candlestick for the current minute: // if there exists a matching calendar candlestick for the current minute:
if (calendarAggregateAtCurrentTime) { if (calendarAggregateAtCurrentTime) {
// if the current candlestick is a good price (i.e. less than the target price): // if the current candlestick is a good price (i.e. less than the target price):
const minCalendarPriceInCandlestick = Math.min( const minCalendarPriceInCandlestick = Math.min(
calendarAggregateAtCurrentTime.open, calendarAggregateAtCurrentTime.open,
calendarAggregateAtCurrentTime.close, calendarAggregateAtCurrentTime.close
); );
if ( if (
minCalendarPriceInCandlestick < targetCalendarPrice && minCalendarPriceInCandlestick < targetCalendarPrice &&
@@ -104,7 +104,7 @@ export async function backtest({
minCalendarPriceInCandlestick * 100, minCalendarPriceInCandlestick * 100,
"...$", "...$",
buyingPower, buyingPower,
"left", "left"
); );
didBuyCalendar = true; didBuyCalendar = true;
} }
@@ -136,7 +136,7 @@ export async function backtest({
calendarClosingPrice, calendarClosingPrice,
"...$", "...$",
buyingPower, buyingPower,
"left", "left"
); );
} }
} }
+1 -1
View File
@@ -57,7 +57,7 @@ function makeCalendarDatabase(): CalendarDatabase {
close: close:
backOptionContractAggregates[j].close - backOptionContractAggregates[j].close -
frontOptionContractAggregates[i].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: // the high and low are not exactly correct since we don't know if each contract's high and low happened at the same moment as the other:
high: high:
backOptionContractAggregates[j].high - backOptionContractAggregates[j].high -
frontOptionContractAggregates[i].high, frontOptionContractAggregates[i].high,
+1 -1
View File
@@ -17,7 +17,7 @@ async function syncAggregates<T>({
date: string; date: string;
}) { }) {
const aggregatesFrom = (await fromDatabase.getAggregates({ key, date })).map( const aggregatesFrom = (await fromDatabase.getAggregates({ key, date })).map(
(aggregateWithoutKey) => ({ ...aggregateWithoutKey, key }), (aggregateWithoutKey) => ({ ...aggregateWithoutKey, key })
); );
await toDatabase.insertAggregates(aggregatesFrom); await toDatabase.insertAggregates(aggregatesFrom);
} }