Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eba5344b15 | |||
| 39bb6c85f8 |
@@ -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()}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user