friendlier variable names; use sliders for input

main
avraham 2 months ago
parent b2169e1da7
commit b7f9d60715

@ -16,6 +16,7 @@ import {
Paper, Paper,
Popper, Popper,
ClickAwayListener, ClickAwayListener,
Stack,
} from "@mui/material"; } from "@mui/material";
import { import {
availableUnderlyings, availableUnderlyings,
@ -38,9 +39,9 @@ import {
refreshStockPriceChartData, refreshStockPriceChartData,
} from "./HistoricalCalendarPrices/actions.js"; } from "./HistoricalCalendarPrices/actions.js";
import { EditableUnderlying } from "./HistoricalCalendarPrices/EditableUnderlying.js"; import { EditableUnderlying } from "./HistoricalCalendarPrices/EditableUnderlying.js";
import { EditableDaysToFrontExpiration } from "./HistoricalCalendarPrices/EditableDaysToFrontExpiration.js"; import { EditableOpenDTE } from "./HistoricalCalendarPrices/EditableOpenDTE.js";
import { EditableExitToFrontExpiration } from "./HistoricalCalendarPrices/EditableExitToFrontExpiration.js"; import { EditableExitDTE } from "./HistoricalCalendarPrices/EditableExitDTE.js";
import { EditableDaysBetweenFrontAndBackExpiration } from "./HistoricalCalendarPrices/EditableDaysBetweenFrontAndBackExpiration.js"; import { EditableSpan } from "./HistoricalCalendarPrices/EditableSpan.js";
import { EditableLookbackPeriodStart } from "./HistoricalCalendarPrices/EditableLookbackPeriodStart.js"; import { EditableLookbackPeriodStart } from "./HistoricalCalendarPrices/EditableLookbackPeriodStart.js";
import { EditableLookbackPeriodEnd } from "./HistoricalCalendarPrices/EditableLookbackPeriodEnd.js"; import { EditableLookbackPeriodEnd } from "./HistoricalCalendarPrices/EditableLookbackPeriodEnd.js";
@ -64,16 +65,16 @@ export function HistoricalCalendarPrices() {
return ( return (
<Container maxWidth="lg"> <Container maxWidth="lg">
<Grid2 container spacing={4} columns={12}> <Grid2 container spacing={4} columns={12}>
<Grid2 size={{ xs: 12 }}> {/* <Grid2 size={{ xs: 12 }}>
<Typography variant="h4" gutterBottom> <Typography variant="h4" gutterBottom>
<EditableUnderlying /> : <EditableUnderlying /> :
<EditableDaysBetweenFrontAndBackExpiration /> <EditableSpan />
-Day Calendar @ <EditableStrike /> -Day Calendar @ <EditableStrike />
%-from-the-money %-from-the-money
</Typography> </Typography>
<Typography variant="h5" gutterBottom sx={{ pl: 1 }}> <Typography variant="h5" gutterBottom sx={{ pl: 1 }}>
Opening at <EditableDaysToFrontExpiration /> DTE, Closing at{" "} Opening at <EditableOpenDTE /> DTE, Closing at <EditableExitDTE />
<EditableExitToFrontExpiration />
DTE DTE
</Typography> </Typography>
<Typography variant="h5" gutterBottom> <Typography variant="h5" gutterBottom>
@ -93,17 +94,65 @@ export function HistoricalCalendarPrices() {
</Paper> </Paper>
</Popper> </Popper>
</ClickAwayListener> </ClickAwayListener>
</Grid2> </Grid2> */}
<Grid2 size={{ xs: 12 }}> <Grid2 size={{ xs: 12 }}>
<Paper elevation={3} sx={{ p: 3, minHeight: "28em", height: "100%" }}> <Stack direction="row" spacing={2}>
<Typography gutterBottom minWidth={"8em"}>
Underlying
</Typography>
<EditableUnderlying />
</Stack>
<Stack direction="row" spacing={2}>
<Typography gutterBottom minWidth={"8em"}>
Open DTE
</Typography>
<EditableOpenDTE />
</Stack>
<Stack direction="row" spacing={2}>
<Typography gutterBottom minWidth={"8em"}>
Exit DTE
</Typography>
<EditableExitDTE />
</Stack>
<Stack direction="row" spacing={2}>
<Typography gutterBottom minWidth={"8em"}>
Span
</Typography>
<EditableSpan />
</Stack>
<Stack direction="row" spacing={2}>
<Typography gutterBottom minWidth={"8em"}>
Lookback Period
</Typography>
<EditableLookbackPeriodStart />-
<EditableLookbackPeriodEnd />
</Stack>
<ClickAwayListener
onClickAway={() => {
isPopperOpen.value = false;
// refreshSimilarCalendarPriceChartData();
console.log("clicked away");
}}
>
<Popper open={isPopperOpen.value} anchorEl={popperAnchorEl.value}>
<Paper elevation={3} sx={{ p: 3 }}>
{popperContent.value}
</Paper>
</Popper>
</ClickAwayListener>
</Grid2>
<Grid2 size={{ xs: 12, md: 6 }}>
<Paper elevation={3} sx={{ p: 3, minHeight: "28em" }}>
{underlying.value !== null && {underlying.value !== null &&
stockPriceChartData.value.length > 0 ? ( similarCalendarPriceChartData.value.length > 0 ? (
<Scatter <Scatter
data={{ data={{
datasets: [ datasets: [
{ {
label: "Stock Open Price", label: "Calendar Open Price",
data: stockPriceChartData.value, data: similarCalendarPriceChartData.value,
}, },
], ],
}} }}
@ -124,17 +173,13 @@ export function HistoricalCalendarPrices() {
max: new Date(lookbackPeriodEnd.value).getTime() / 1000, max: new Date(lookbackPeriodEnd.value).getTime() / 1000,
}, },
y: { y: {
beginAtZero: false, beginAtZero: true,
ticks: { ticks: {
callback: (value, index, ticks) => callback: (value, index, ticks) =>
`$${value.toString()}`, `$${value.toString()}`,
}, },
}, min: 0,
}, max: maxChartPrice.value,
elements: {
point: {
radius: 1,
borderWidth: 0,
}, },
}, },
plugins: { plugins: {
@ -146,7 +191,7 @@ export function HistoricalCalendarPrices() {
}, },
title: { title: {
display: true, display: true,
text: "Stock Price", text: "Calendar Price (Under Like Conditions)",
}, },
}, },
animation: false, animation: false,
@ -167,26 +212,24 @@ export function HistoricalCalendarPrices() {
data={{ data={{
datasets: [ datasets: [
{ {
label: "Calendar Open Price", label: "Calendar Exit Price",
data: similarCalendarPriceChartData.value, data: calendarExitPriceChartData.value,
}, },
], ],
}} }}
options={{ options={{
scales: { scales: {
x: { x: {
type: "linear",
beginAtZero: false,
title: { title: {
display: true, display: true,
text: "Time", text: "%-From-the-Money",
}, },
ticks: { ticks: {
callback: (value, index, ticks) => callback: (value, index, ticks) =>
new Date((value as number) * 1000) `${value.toString()}%`,
.toISOString()
.substring(0, 10),
}, },
min: new Date(lookbackPeriodStart.value).getTime() / 1000,
max: new Date(lookbackPeriodEnd.value).getTime() / 1000,
}, },
y: { y: {
beginAtZero: true, beginAtZero: true,
@ -198,6 +241,18 @@ export function HistoricalCalendarPrices() {
max: maxChartPrice.value, max: maxChartPrice.value,
}, },
}, },
elements: {
point: {
borderWidth: 0,
backgroundColor: (context) => {
const n = (
context.raw as { x: number; y: number; n: number }
).n;
const alpha = n / maxN.value;
return `rgba(0, 0, 0, ${alpha})`;
},
},
},
plugins: { plugins: {
tooltip: { tooltip: {
enabled: false, enabled: false,
@ -207,7 +262,10 @@ export function HistoricalCalendarPrices() {
}, },
title: { title: {
display: true, display: true,
text: "Calendar Price (Under Like Conditions)", text: [
"Calendar Prices at Exit",
"by %-age from-the-money",
],
}, },
}, },
animation: false, animation: false,
@ -220,53 +278,47 @@ export function HistoricalCalendarPrices() {
)} )}
</Paper> </Paper>
</Grid2> </Grid2>
<Grid2 size={{ xs: 12, md: 6 }}> <Grid2 size={{ xs: 12 }}>
<Paper elevation={3} sx={{ p: 3, minHeight: "28em" }}> <Paper elevation={3} sx={{ p: 3, minHeight: "28em", height: "100%" }}>
{underlying.value !== null && {underlying.value !== null &&
similarCalendarPriceChartData.value.length > 0 ? ( stockPriceChartData.value.length > 0 ? (
<Scatter <Scatter
data={{ data={{
datasets: [ datasets: [
{ {
label: "Calendar Exit Price", label: "Stock Open Price",
data: calendarExitPriceChartData.value, data: stockPriceChartData.value,
}, },
], ],
}} }}
options={{ options={{
scales: { scales: {
x: { x: {
type: "linear",
beginAtZero: false,
title: { title: {
display: true, display: true,
text: "%-From-the-Money", text: "Time",
}, },
ticks: { ticks: {
callback: (value, index, ticks) => callback: (value, index, ticks) =>
`${value.toString()}%`, new Date((value as number) * 1000)
.toISOString()
.substring(0, 10),
}, },
min: new Date(lookbackPeriodStart.value).getTime() / 1000,
max: new Date(lookbackPeriodEnd.value).getTime() / 1000,
}, },
y: { y: {
beginAtZero: true, beginAtZero: false,
ticks: { ticks: {
callback: (value, index, ticks) => callback: (value, index, ticks) =>
`$${value.toString()}`, `$${value.toString()}`,
}, },
min: 0,
max: maxChartPrice.value,
}, },
}, },
elements: { elements: {
point: { point: {
radius: 1,
borderWidth: 0, borderWidth: 0,
backgroundColor: (context) => {
const n = (
context.raw as { x: number; y: number; n: number }
).n;
const alpha = n / maxN.value;
return `rgba(0, 0, 0, ${alpha})`;
},
}, },
}, },
plugins: { plugins: {
@ -278,10 +330,7 @@ export function HistoricalCalendarPrices() {
}, },
title: { title: {
display: true, display: true,
text: [ text: "Stock Price",
"Calendar Prices at Exit",
"by %-age from-the-money",
],
}, },
}, },
animation: false, animation: false,

@ -1,38 +0,0 @@
import TextField from "@mui/material/TextField";
import {
refreshcalendarExitPriceChartData,
refreshSimilarCalendarPriceChartData,
} from "./actions";
import { daysBetweenFrontAndBackExpiration } from "./state";
import { EditableValue } from "./EditableValue";
const handleDaysBetweenFrontAndBackExpirationChange = (e) => {
if (
daysBetweenFrontAndBackExpiration.value !== Number.parseInt(e.target.value)
) {
daysBetweenFrontAndBackExpiration.value = Number.parseInt(e.target.value);
refreshSimilarCalendarPriceChartData();
refreshcalendarExitPriceChartData();
}
};
function DaysBetweenFrontAndBackExpirationChooser() {
return (
<TextField
fullWidth
label="Front-to-Back-Month Days to Expiration Difference"
type="number"
value={daysBetweenFrontAndBackExpiration.value}
onChange={handleDaysBetweenFrontAndBackExpirationChange}
InputProps={{ endAdornment: "Days Difference" }}
/>
);
}
export function EditableDaysBetweenFrontAndBackExpiration() {
return (
<EditableValue text={daysBetweenFrontAndBackExpiration.value}>
<DaysBetweenFrontAndBackExpirationChooser />
</EditableValue>
);
}

@ -1,32 +0,0 @@
import TextField from "@mui/material/TextField";
import { refreshSimilarCalendarPriceChartData } from "./actions";
import { EditableValue } from "./EditableValue";
import { daysToFrontExpiration } from "./state";
const handleDaysToFrontExpirationChange = (e) => {
if (daysToFrontExpiration.value !== Number.parseInt(e.target.value)) {
daysToFrontExpiration.value = Number.parseInt(e.target.value);
refreshSimilarCalendarPriceChartData();
}
};
function DaysToFrontExpirationChooser() {
return (
<TextField
fullWidth
label="Now-to-Front-Month Days to Expiration"
type="number"
value={daysToFrontExpiration.value}
onChange={handleDaysToFrontExpirationChange}
InputProps={{ endAdornment: "Days" }}
/>
);
}
export function EditableDaysToFrontExpiration() {
return (
<EditableValue text={daysToFrontExpiration.value}>
<DaysToFrontExpirationChooser />
</EditableValue>
);
}

@ -0,0 +1,28 @@
import TextField from "@mui/material/TextField";
import { EditableValue } from "./EditableValue";
import { exitDTE } from "./state";
import { refreshcalendarExitPriceChartData } from "./actions";
import Slider from "@mui/material/Slider";
const handleExitDTEChange = (e) => {
if (exitDTE.value !== Number.parseInt(e.target.value)) {
exitDTE.value = Number.parseInt(e.target.value);
refreshcalendarExitPriceChartData();
}
};
export function EditableExitDTE() {
return (
<Slider
value={exitDTE.value}
onChange={handleExitDTEChange}
min={0}
max={5}
step={1}
valueLabelDisplay="on"
/>
// <EditableValue text={exitDTE.value}>
// <ExitToFrontExpirationChooser />
// </EditableValue>
);
}

@ -1,32 +0,0 @@
import TextField from "@mui/material/TextField";
import { EditableValue } from "./EditableValue";
import { exitToFrontExpiration } from "./state";
import { refreshcalendarExitPriceChartData } from "./actions";
const handleExitToFrontExpirationChange = (e) => {
if (exitToFrontExpiration.value !== Number.parseInt(e.target.value)) {
exitToFrontExpiration.value = Number.parseInt(e.target.value);
refreshcalendarExitPriceChartData();
}
};
function ExitToFrontExpirationChooser() {
return (
<TextField
fullWidth
label="Exit-to-Front-Month Days to Expiration"
type="number"
value={exitToFrontExpiration.value}
onChange={handleExitToFrontExpirationChange}
InputProps={{ endAdornment: "Days" }}
/>
);
}
export function EditableExitToFrontExpiration() {
return (
<EditableValue text={exitToFrontExpiration.value}>
<ExitToFrontExpirationChooser />
</EditableValue>
);
}

@ -0,0 +1,26 @@
import { refreshSimilarCalendarPriceChartData } from "./actions";
import { openDTE } from "./state";
import Slider from "@mui/material/Slider";
const handleOpenDTEChange = (e) => {
if (openDTE.value !== Number.parseInt(e.target.value)) {
openDTE.value = Number.parseInt(e.target.value);
refreshSimilarCalendarPriceChartData();
}
};
export function EditableOpenDTE() {
return (
<Slider
value={openDTE.value}
onChange={handleOpenDTEChange}
min={0}
max={5}
step={1}
valueLabelDisplay="on"
/>
// <EditableValue text={openDTE.value}>
// <DaysToFrontExpirationChooser />
// </EditableValue>
);
}

@ -0,0 +1,45 @@
import TextField from "@mui/material/TextField";
import {
refreshcalendarExitPriceChartData,
refreshSimilarCalendarPriceChartData,
} from "./actions";
import { span } from "./state";
import { EditableValue } from "./EditableValue";
import Slider from "@mui/material/Slider";
const handleSpanChange = (e) => {
if (span.value !== Number.parseInt(e.target.value)) {
span.value = Number.parseInt(e.target.value);
refreshSimilarCalendarPriceChartData();
refreshcalendarExitPriceChartData();
}
};
function DaysBetweenFrontAndBackExpirationChooser() {
return (
<TextField
fullWidth
label="Front-to-Back-Month Days to Expiration Difference"
type="number"
value={span.value}
onChange={handleSpanChange}
InputProps={{ endAdornment: "Days Difference" }}
/>
);
}
export function EditableSpan() {
return (
<Slider
value={span.value}
onChange={handleSpanChange}
min={3}
max={45}
step={1}
valueLabelDisplay="on"
/>
// <EditableValue text={span.value}>
// <DaysBetweenFrontAndBackExpirationChooser />
// </EditableValue>
);
}

@ -1,24 +1,21 @@
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { EditableValue } from "./EditableValue"; import { EditableValue } from "./EditableValue";
import { import { moniness, moninessRadius } from "./state";
strikePercentageFromUnderlyingPrice,
strikePercentageFromUnderlyingPriceRadius,
} from "./state";
import Slider from "@mui/material/Slider"; import Slider from "@mui/material/Slider";
import { refreshSimilarCalendarPriceChartData } from "./actions"; import { refreshSimilarCalendarPriceChartData } from "./actions";
function StrikePercentageFromUnderlyingPriceChooser() { function MoninessChooser() {
return ( return (
<Slider <Slider
fullWidth fullWidth
label="Strike % From Underlying Price" label="Strike % From Underlying Price"
value={strikePercentageFromUnderlyingPrice.value} value={moniness.value}
valueLabelDisplay="on" valueLabelDisplay="on"
min={0} min={0}
max={10} max={10}
step={1} step={1}
onChange={(e, value) => { onChange={(e, value) => {
strikePercentageFromUnderlyingPrice.value = value as number; moniness.value = value as number;
}} }}
onChangeCommitted={(e, value) => { onChangeCommitted={(e, value) => {
refreshSimilarCalendarPriceChartData(); refreshSimilarCalendarPriceChartData();
@ -28,18 +25,18 @@ function StrikePercentageFromUnderlyingPriceChooser() {
); );
} }
function StrikePercentageFromUnderlyingPriceRadiusChooser() { function MoninessRadiusChooser() {
return ( return (
<Slider <Slider
fullWidth fullWidth
label="Strike % Radius" label="Strike % Radius"
value={strikePercentageFromUnderlyingPriceRadius.value} value={moninessRadius.value}
valueLabelDisplay="on" valueLabelDisplay="on"
min={0} min={0}
max={10} max={10}
step={1} step={1}
onChange={(e, value) => { onChange={(e, value) => {
strikePercentageFromUnderlyingPriceRadius.value = value as number; moninessRadius.value = value as number;
}} }}
onChangeCommitted={(e, value) => { onChangeCommitted={(e, value) => {
refreshSimilarCalendarPriceChartData(); refreshSimilarCalendarPriceChartData();
@ -57,13 +54,11 @@ function StrikePercentageFromUnderlyingPriceRadiusChooser() {
export function EditableStrike() { export function EditableStrike() {
return ( return (
<EditableValue <EditableValue
text={`${strikePercentageFromUnderlyingPrice.value.toFixed( text={`${moniness.value.toFixed(1)}±${moninessRadius.value.toFixed(2)}`}
1
)}±${strikePercentageFromUnderlyingPriceRadius.value.toFixed(2)}`}
> >
<Box sx={{ minWidth: "20em" }}> <Box sx={{ minWidth: "20em" }}>
<StrikePercentageFromUnderlyingPriceChooser /> <MoninessChooser />
<StrikePercentageFromUnderlyingPriceRadiusChooser /> <MoninessRadiusChooser />
</Box> </Box>
</EditableValue> </EditableValue>
); );

@ -1,15 +1,15 @@
import { trpc } from "../../trpc"; import { trpc } from "../../trpc";
import { import {
calendarExitPriceChartData, calendarExitPriceChartData,
daysBetweenFrontAndBackExpiration, span,
daysToFrontExpiration, openDTE,
exitToFrontExpiration, exitDTE,
lookbackPeriodEnd, lookbackPeriodEnd,
lookbackPeriodStart, lookbackPeriodStart,
similarCalendarPriceChartData, similarCalendarPriceChartData,
stockPriceChartData, stockPriceChartData,
strikePercentageFromUnderlyingPrice, moniness,
strikePercentageFromUnderlyingPriceRadius, moninessRadius,
underlying, underlying,
} from "./state"; } from "./state";
@ -30,17 +30,12 @@ export const refreshSimilarCalendarPriceChartData = () => {
trpc.SimilarCalendarPriceChart.getChartData trpc.SimilarCalendarPriceChart.getChartData
.query({ .query({
underlying: underlying.value, underlying: underlying.value,
daysToFrontExpiration: daysToFrontExpiration.value, daysToFrontExpiration: openDTE.value,
daysBetweenFrontAndBackExpiration: daysBetweenFrontAndBackExpiration: span.value,
daysBetweenFrontAndBackExpiration.value,
strikePercentageFromUnderlyingPriceRangeMin: strikePercentageFromUnderlyingPriceRangeMin:
(strikePercentageFromUnderlyingPrice.value - (moniness.value - moninessRadius.value) / 100,
strikePercentageFromUnderlyingPriceRadius.value) /
100,
strikePercentageFromUnderlyingPriceRangeMax: strikePercentageFromUnderlyingPriceRangeMax:
(strikePercentageFromUnderlyingPrice.value + (moniness.value + moninessRadius.value) / 100,
strikePercentageFromUnderlyingPriceRadius.value) /
100,
lookbackPeriodStart: lookbackPeriodStart.value, lookbackPeriodStart: lookbackPeriodStart.value,
lookbackPeriodEnd: lookbackPeriodEnd.value, lookbackPeriodEnd: lookbackPeriodEnd.value,
}) })
@ -53,9 +48,8 @@ export const refreshcalendarExitPriceChartData = () => {
trpc.CalendarExitPriceChart.getChartData trpc.CalendarExitPriceChart.getChartData
.query({ .query({
underlying: underlying.value, underlying: underlying.value,
daysToFrontExpiration: exitToFrontExpiration.value, daysToFrontExpiration: exitDTE.value,
daysBetweenFrontAndBackExpiration: daysBetweenFrontAndBackExpiration: span.value,
daysBetweenFrontAndBackExpiration.value,
lookbackPeriodStart: lookbackPeriodStart.value, lookbackPeriodStart: lookbackPeriodStart.value,
lookbackPeriodEnd: lookbackPeriodEnd.value, lookbackPeriodEnd: lookbackPeriodEnd.value,
}) })

@ -7,14 +7,14 @@ export const popperContent = signal(null);
export const availableUnderlyings = signal([]); export const availableUnderlyings = signal([]);
export const underlying = signal(null); export const underlying = signal(null);
export const daysToFrontExpiration = signal(14); export const openDTE = signal(14);
export const daysBetweenFrontAndBackExpiration = signal(14); export const span = signal(14);
export const strikePercentageFromUnderlyingPrice = signal(1); export const moniness = signal(1);
export const strikePercentageFromUnderlyingPriceRadius = signal(1); export const moninessRadius = signal(1);
export const exitToFrontExpiration = signal(2); export const exitDTE = signal(2);
export const stockPriceChartData = signal([]); export const stockPriceChartData = signal([]);

Loading…
Cancel
Save