This commit is contained in:
Avraham Sakal
2024-02-04 15:36:28 -05:00
commit dc63f31647
33 changed files with 3423 additions and 0 deletions
@@ -0,0 +1,321 @@
import { signal } from "@preact/signals";
import { useCallback, useEffect } from "preact/hooks";
import {trpc} from '../../trpc.js';
import {
Chart as ChartJS,
LinearScale,
CategoryScale,
PointElement,
Tooltip,
Title,
} from 'chart.js';
import { Scatter } from 'react-chartjs-2';
import './style.css';
ChartJS.register(LinearScale, CategoryScale, PointElement, Tooltip, Title,);
const availableUnderlyings = signal([]);
const chosenUnderlying = signal(null);
const chosenDaysToFrontExpiration = signal(14);
const chosenDaysBetweenFrontAndBackExpiration = signal(14);
const chosenStrikePercentageFromUnderlyingPrice = signal(1.4);
const chosenStrikePercentageFromUnderlyingPriceRadius = signal(0.05);
const chosenExitToFrontExpiration = signal(2);
const historicalStockQuoteChartData = signal([]);
const historicalCalendarQuoteChartData = signal([]);
const historicalCalendarExitQuoteChartData = signal([]);
export function HistoricalCalendarPrices(){
const refreshHistoricalStockQuoteChartData = useCallback(()=>{
trpc.getHistoricalStockQuoteChartData
.query({
underlying:chosenUnderlying.value,
})
.then((getHistoricalStockQuoteChartDataResponse)=>{
historicalStockQuoteChartData.value = getHistoricalStockQuoteChartDataResponse;
})
},[]);
const refreshHistoricalCalendarQuoteChartData = useCallback(()=>{
trpc.getHistoricalCalendarQuoteChartData
.query({
underlying:chosenUnderlying.value,
daysToFrontExpiration:chosenDaysToFrontExpiration.value,
daysBetweenFrontAndBackExpiration:chosenDaysBetweenFrontAndBackExpiration.value,
strikePercentageFromUnderlyingPriceRangeMin:chosenStrikePercentageFromUnderlyingPrice.value - chosenStrikePercentageFromUnderlyingPriceRadius.value,
strikePercentageFromUnderlyingPriceRangeMax:chosenStrikePercentageFromUnderlyingPrice.value + chosenStrikePercentageFromUnderlyingPriceRadius.value,
})
.then((getHistoricalCalendarQuoteChartDataResponse)=>{
historicalCalendarQuoteChartData.value = getHistoricalCalendarQuoteChartDataResponse;
})
},[]);
const refreshHistoricalCalendarExitQuoteChartData = useCallback(()=>{
trpc.getHistoricalCalendarExitQuoteChartData
.query({
underlying:chosenUnderlying.value,
daysToFrontExpiration:chosenExitToFrontExpiration.value,
daysBetweenFrontAndBackExpiration:chosenDaysBetweenFrontAndBackExpiration.value,
})
.then((getHistoricalCalendarExitQuoteChartDataResponse)=>{
historicalCalendarExitQuoteChartData.value = getHistoricalCalendarExitQuoteChartDataResponse;
})
},[]);
const handleInit = useCallback(()=>{
trpc.getAvailableUnderlyings
.query()
.then((availableUnderlyingsResponse)=>{
availableUnderlyings.value = availableUnderlyingsResponse;
chosenUnderlying.value = availableUnderlyingsResponse[0];
refreshHistoricalStockQuoteChartData();
refreshHistoricalCalendarQuoteChartData();
refreshHistoricalCalendarExitQuoteChartData();
});
},[]);
const handleUnderlyingChange = useCallback((e)=>{
chosenUnderlying.value = e.target.value;
refreshHistoricalStockQuoteChartData();
refreshHistoricalCalendarQuoteChartData();
refreshHistoricalCalendarExitQuoteChartData();
},[]);
const handleDaysToFrontExpirationChange = useCallback((e)=>{
chosenDaysToFrontExpiration.value = parseInt(e.target.value);
refreshHistoricalCalendarQuoteChartData();
},[]);
const handleDaysBetweenFrontAndBackExpirationChange = useCallback((e)=>{
chosenDaysBetweenFrontAndBackExpiration.value = parseInt(e.target.value);
refreshHistoricalCalendarQuoteChartData();
refreshHistoricalCalendarExitQuoteChartData();
},[]);
const handleStrikePercentageFromUnderlyingPriceChange = useCallback((e)=>{
chosenStrikePercentageFromUnderlyingPrice.value = parseFloat(e.target.value);
refreshHistoricalCalendarQuoteChartData();
},[]);
const handleStrikePercentageFromUnderlyingPriceRadiusChange = useCallback((e)=>{
chosenStrikePercentageFromUnderlyingPriceRadius.value = parseFloat(e.target.value);
refreshHistoricalCalendarQuoteChartData();
},[]);
const handleExitToFrontExpirationChange = useCallback((e)=>{
chosenExitToFrontExpiration.value = parseInt(e.target.value);
refreshHistoricalCalendarExitQuoteChartData();
},[]);
useEffect(handleInit, []);
return (
<div>
<div>
<label>Available Underlyings</label>
{
availableUnderlyings.value.length === 0
? "Loading..."
: <select onChange={handleUnderlyingChange}>
{availableUnderlyings.value.map((availableUnderlying)=>(
<option value={availableUnderlying}>{availableUnderlying}</option>
))}
</select>
}
</div>
<div>
<label>Now-to-Front-Month "Days to Expiration"</label>
<input type="text" onChange={handleDaysToFrontExpirationChange} value={chosenDaysToFrontExpiration.value} />
Days
</div>
<div>
<label>Front-to-Back-Month "Days to Expiration" Difference</label>
<input type="text" onChange={handleDaysBetweenFrontAndBackExpirationChange} value={chosenDaysBetweenFrontAndBackExpiration.value} />
Days Difference
</div>
<div>
<label>"Strike Percentage From Underlying Price" Range</label>
<input type="text" onChange={handleStrikePercentageFromUnderlyingPriceChange} value={chosenStrikePercentageFromUnderlyingPrice.value} />
%
+/-
<input type="text" onChange={handleStrikePercentageFromUnderlyingPriceRadiusChange} value={chosenStrikePercentageFromUnderlyingPriceRadius.value} />
% from ATM
</div>
<div>
<label>Exit-to-Front-Month "Days to Expiration"</label>
<input type="text" onChange={handleExitToFrontExpirationChange} value={chosenExitToFrontExpiration.value} />
Days
</div>
<div className="chart-container">
{chosenUnderlying.value!==null && historicalStockQuoteChartData.value.length>0
? <div className="chart">
<Scatter
data={{
datasets: [
{
label: "Stock Open Price",
data: historicalStockQuoteChartData.value
}
]
}}
options={{
scales: {
x: {
title: {
display: true,
text: "Time"
},
ticks: {
callback: function(value, index, ticks) {
return (new Date(value as number*1000)).toISOString().substring(0,10);
}
}
},
y: {
beginAtZero: false,
ticks: {
callback: function(value, index, ticks) {
return "$"+value.toString();
}
}
},
},
elements: {
point: {
radius: 1,
borderWidth: 0,
}
},
plugins: {
tooltip: {
enabled: false,
},
legend: {
display: false,
},
title: {
display: true,
text: "Stock Price"
},
},
animation: false,
maintainAspectRatio: false,
}}
/>
</div>
: <div>Loading Chart...</div>}
</div>
<div className="chart-container">
{chosenUnderlying.value!==null && historicalCalendarQuoteChartData.value.length>0
? <div className="chart">
<Scatter
data={{
datasets: [
{
label: "Calendar Open Price",
data: historicalCalendarQuoteChartData.value
}
]
}}
options={{
scales: {
x: {
title: {
display: true,
text: "Time"
},
ticks: {
callback: function(value, index, ticks) {
return (new Date(value as number*1000)).toISOString().substring(0,10);
}
}
},
y: {
beginAtZero: true,
ticks: {
callback: function(value, index, ticks) {
return "$"+value.toString();
}
}
}
},
plugins: {
tooltip: {
enabled: false,
},
legend: {
display: false,
},
title: {
display: true,
text: "Calendar Price (Under Like Conditions)"
},
},
animation: false,
maintainAspectRatio: false,
}}
/>
</div>
: <div>Loading Chart...</div>}
{chosenUnderlying.value!==null && historicalCalendarQuoteChartData.value.length>0
? <div className="chart">
<Scatter
data={{
datasets: [
{
label: "Calendar Exit Price",
data: historicalCalendarExitQuoteChartData.value
}
]
}}
options={{
scales: {
x: {
type: 'linear',
beginAtZero: false,
title: {
display: true,
text: "%-From-the-Money"
},
ticks: {
callback: function(value, index, ticks) {
return value.toString()+"%";
}
}
},
y: {
beginAtZero: true,
ticks: {
callback: function(value, index, ticks) {
return "$"+value.toString();
}
}
},
},
elements: {
point: {
borderWidth: 0,
},
},
plugins: {
tooltip: {
enabled: false,
},
legend: {
display: false,
},
title: {
display: true,
text: ["Calendar Prices at Exit","by %-age from-the-money"]
},
},
animation: false,
maintainAspectRatio: false,
}}
/>
</div>
: <div>Loading Chart...</div>}
</div>
</div>
);
}
@@ -0,0 +1,11 @@
.chart-container {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-around;
width: 1800px;
height: 480px;
}
.chart-container > .chart {
width: 880px;
}