Skip to content

Commit

Permalink
delta hedging improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
mnsrulz committed Aug 16, 2024
1 parent bad7a09 commit 19925a0
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 79 deletions.
38 changes: 32 additions & 6 deletions src/app/api/symbols/[symbol]/options/analyze/tradier/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ky from "ky";
import dayjs from 'dayjs';
import { NextResponse } from "next/server";
import { dnmodel } from "@/lib/socket";
import { OptionsHedgingDataset } from "@/lib/socket";
const tradierBaseUri = 'https://sandbox.tradier.com/';
const optionsChain = `${tradierBaseUri}v1/markets/options/chains`;
const optionsExpiration = `${tradierBaseUri}v1/markets/options/expirations`;
Expand All @@ -15,6 +15,10 @@ const client = ky.create({
});

export async function GET(request: Request, p: { params: { symbol: string } }) {
const { searchParams } = new URL(request.url);
const dteValue = parseInt(searchParams.get("dte") || '30');
const strikeCountValue = parseInt(searchParams.get("sc") || '30');
console.log(`calling with ${dteValue} dtes`);
const { symbol } = p.params;
const currentPrice = await getCurrentPrice(symbol);
if (!currentPrice) throw new Error('Unable to evaluate current price')
Expand All @@ -25,18 +29,22 @@ export async function GET(request: Request, p: { params: { symbol: string } }) {
}
}).json<{ expirations: { date: string[] } }>();

const tillDate = dayjs().add(8, 'weeks');
const tillDate = dayjs().add(dteValue, 'days');
console.log(`all expirations: ${expresp.expirations.date}`);
const allDates = [...new Set(expresp.expirations.date.filter(j => dayjs(j).isBefore(tillDate)))];
const allOptionChains = await Promise.all(allDates.map(d => getOptionData(symbol, d)));

const allStrikes = [...new Set(allOptionChains.flatMap(j => j.options.option.map(s => s.strike)))].filter(s => currentPrice > s * .9 && currentPrice < s * 1.1);
const allStrikes = getCalculatedStrikes(currentPrice, strikeCountValue, [...new Set(allOptionChains.flatMap(j => j.options.option.map(s => s.strike)))]);

const allOp = allOptionChains.flatMap(j => j.options.option.map(s => s));

console.log(`Rendering with dates: ${allDates} and strikes: ${allStrikes}`);
const model: Record<number, { puts: number[], calls: number[], data: number[] }> = {};
const dmodel: dnmodel[] = [];
const dmodel: OptionsHedgingDataset[] = [];
let maxPosition = 0;
for (const sp of allStrikes) {
const md: dnmodel = { strike: sp };
const md: OptionsHedgingDataset = { strike: sp };
let sumOfPv = 0, sumOfCv = 0;
dmodel.push(md);
model[sp] = {
calls: [],
Expand All @@ -55,15 +63,22 @@ export async function GET(request: Request, p: { params: { symbol: string } }) {

md[`${dt}-call`] = -cv;
md[`${dt}-put`] = -pv;

sumOfPv = sumOfPv + Math.abs(pv);
sumOfCv = sumOfCv + Math.abs(cv);

}
maxPosition = Math.max(maxPosition, sumOfPv, sumOfCv);
}

const finalResponse = {
dh: {
data: model,
dataset: dmodel,
strikes: allStrikes,
expirations: allDates
expirations: allDates,
currentPrice,
maxPosition
},
raw: allOptionChains
}
Expand Down Expand Up @@ -109,4 +124,15 @@ async function getCurrentPrice(symbol: string) {
return cp.quotes.quote
//.find(x => x.symbol === symbol)?
.last;
}

///responsible for returning the strikes which we have to return in response.
function getCalculatedStrikes(currentPrice: number, maxStrikes: number, strikes: number[]) {
const currentOrAboveStrikes = strikes.filter(j => j >= currentPrice).sort((a, b) => a - b).reverse();
const belowCurrentStrikes = strikes.filter(j => j < currentPrice).sort((a, b) => a - b);
let result = [];
while (result.length < maxStrikes && (currentOrAboveStrikes.length > 0 || belowCurrentStrikes.length > 0)) {
result.push(...[currentOrAboveStrikes.pop(), belowCurrentStrikes.pop()].filter(j => j));
}
return result.map(Number).sort((a, b) => a - b);
}
167 changes: 100 additions & 67 deletions src/components/delta-heding.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Dialog, DialogContent, DialogActions, Button, Typography, LinearProgress } from "@mui/material";
import { Dialog, DialogContent, DialogActions, Button, Typography, LinearProgress, FormControl, InputLabel, MenuItem, Select } from "@mui/material";
import { BarChart } from '@mui/x-charts/BarChart';
import { axisClasses } from '@mui/x-charts';
import { ChartsReferenceLine } from '@mui/x-charts';
import { useDeltaHedging } from "@/lib/socket";
import { useState } from "react";

interface ITickerProps {
symbol: string,
Expand Down Expand Up @@ -52,40 +53,30 @@ interface ITickerProps {
// },
// };

const uData = [-900, -100, 4000, 3000, 2000, 2780, 1890, 2390, 3490];
const pData = [1000, -900, 2400, 1398, -9800, 3908, 4800, -3800, 4300];

// const xLabels = [
// '$50',
// '$70',
// '$85',
// '$90',
// '$100',
// '$105',
// '$120',
// '$200',
// '$210',
// ];
const colorCodes = ['#4e79a7',
'#f28e2c',
'#e15759',
'#76b7b2',
'#59a14f',
'#edc949',
'#af7aa1',
'#ff9da7',
'#9c755f',
'#bab0ab'];
const colorCodes = ['#ae6867',
'#dd8f77',
'#fab079',
'#f6b17f',
'#ece990',
'#94ab5e',
'#d7f888',
'#519693',
'#7be3de',
'#8eb8f0',
'#7d7283',
'#c5b4cf',
'#f2ddff'
];
export const DeltaHeding = (props: ITickerProps) => {
const { onClose } = props;
// const rrs = await fetch('')
const { data, isLoading } = useDeltaHedging(props.symbol);
const [dte, setDte] = useState(50);
const [strikeCounts, setStrikesCount] = useState(30);
const { data, isLoading } = useDeltaHedging(props.symbol, dte, strikeCounts);

if (isLoading) return <LinearProgress />;
if (!data) return <div>No data to show!!!</div>;

const xLabels = Object.keys(data.data);

const height = data.strikes.length * 15;
const yaxisline = Math.max(...data.strikes.filter(j => j <= data.currentPrice));
const series = data.expirations.flatMap(j => {
return [{
dataKey: `${j}-call`, label: `${j}`, stack: `stack`, color: colorCodes[data.expirations.indexOf(j)]
Expand All @@ -98,43 +89,57 @@ export const DeltaHeding = (props: ITickerProps) => {
<Dialog fullWidth={true} fullScreen={true} open={true} onClose={onClose} aria-labelledby="delta-hedging-dialog">
<DialogContent>
<Typography variant="h5" align="center" gutterBottom>
${props.symbol.toUpperCase()} ABS Delta Hedging Exposure (50 DTE)
${props.symbol.toUpperCase()} ABS Delta Hedging Exposure ({dte} DTE)
</Typography>
{/* <BarChart
xAxis={[{ dataKey: 'strike', label: 'Strike', type: 'number' }]}
yAxis={[{ label: 'Delta Hedging Exposure', type: 'number' }]}
width={800}
height={500}
>
<BarSeries dataKey="exposure" data={data} />
</BarChart>
[
{
v: 100
}
]
*/}


<FormControl sx={{ m: 1, minWidth: 120 }} size="small">
<InputLabel>DTE</InputLabel>
<Select
id="dte"
value={dte}
label="DTE"
onChange={(e) => setDte(e.target.value as number)}
>
<MenuItem value={30}>30</MenuItem>
<MenuItem value={50}>50</MenuItem>
<MenuItem value={90}>90</MenuItem>
</Select>
</FormControl>
<FormControl sx={{ m: 1, minWidth: 120 }} size="small">
<InputLabel>Strikes</InputLabel>
<Select
id="strikes"
value={strikeCounts}
label="Strikes"

onChange={(e) => setStrikesCount(e.target.value as number)}
>
<MenuItem value={20}>20</MenuItem>
<MenuItem value={30}>30</MenuItem>
<MenuItem value={50}>50</MenuItem>
<MenuItem value={80}>80</MenuItem>
<MenuItem value={100}>100</MenuItem>
</Select>
</FormControl>

<BarChart
// width={500}
// height={300}
// width={960}
height={height}
dataset={data.dataset}
series={series}
grid={
{
vertical: true,
horizontal: true
}
}
tooltip={{
trigger: 'none'
}}
// grid={
// {
// vertical: true,
// horizontal: true
// }
// }
yAxis={[
{
// data: xLabels,
label: 'Strike',

dataKey: 'strike',
scaleType: 'band',
reverse: true
Expand All @@ -147,32 +152,60 @@ export const DeltaHeding = (props: ITickerProps) => {
{
label: 'Delta Hedging Exposure',
scaleType: 'linear',
// tickNumber: 5,
// tickMinStep: 100000,

min: -data.maxPosition,
max: data.maxPosition,

valueFormatter: (tick) => {
tick = Math.abs(tick);
if (tick >= 1000000) {
return `${(tick / 1000000).toFixed(1)}M`; // Millions
} else if (tick >= 1000) {
return `${(tick / 1000).toFixed(1)}K`; // Thousands
}
return `${tick}`;
}
}
]
}
slotProps={{
legend: {
seriesToDisplay: data.expirations.map(j=>{
seriesToDisplay: data.expirations.map(j => {
return {
id: j,
color: colorCodes[data.expirations.indexOf(j)],
label: j
}
}),
// hidden: true,
direction: 'row',
direction: 'column',
position: {
vertical: 'top',
horizontal: 'middle',
horizontal: 'right',
},
itemMarkWidth: 20,
itemMarkHeight: 2,
itemMarkWidth: 32,
itemMarkHeight: 12,
markGap: 5,
itemGap: 10,
itemGap: 5,
}
}}
/>
}}>

<ChartsReferenceLine x={0} />
<ChartsReferenceLine y={yaxisline} label={"SPOT PRICE: $" + data.currentPrice}
labelAlign="start"
lineStyle={{
color: 'red',
stroke: 'red'
}}
labelStyle={
{
color: 'red',
stroke: 'red'
}
} />
</BarChart>

{/* <BarChart
width={600}
Expand Down
18 changes: 12 additions & 6 deletions src/lib/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,17 @@ type OptionsData = {
currentPrice: number,
options: Record<string, OptionsInnerData>
}
export type dnmodel = { strike: number, [x: string]: number; }
export type OptionsHedgingDataset = { strike: number, [x: string]: number; }

type OptionsHedgingData = {
expirations: string[],
strikes: number[],
data: { puts: number[], calls: number[], data: number[] },
dataset: dnmodel[]
dataset: OptionsHedgingDataset[],
currentPrice: number,
maxPosition: number
}


export const useOptionTracker = (symbol: string) => {
const [data, setOd] = useState<OptionsData>();
const [isLoading, setIsLoading] = useState(true);
Expand Down Expand Up @@ -136,16 +137,21 @@ export const useOptionTracker = (symbol: string) => {



export const useDeltaHedging = (symbol: string) => {
export const useDeltaHedging = (symbol: string, dte: number, sc: number) => {
const [data, setOd] = useState<OptionsHedgingData>();
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
setIsLoading(true);
ky(`/api/symbols/${symbol}/options/analyze/tradier`).json<{dh: OptionsHedgingData}>().then(r => {
ky(`/api/symbols/${symbol}/options/analyze/tradier`, {
searchParams: {
dte,
sc
}
}).json<{dh: OptionsHedgingData}>().then(r => {
setOd(r.dh);
}).finally(() => setIsLoading(false));
}, []);
}, [symbol, dte, sc]);
return { data, isLoading };
}

Expand Down

0 comments on commit 19925a0

Please sign in to comment.