diff --git a/src/app/api/symbols/[symbol]/options/analyze/tradier/route.ts b/src/app/api/symbols/[symbol]/options/analyze/tradier/route.ts index 5a7e915..867a71c 100644 --- a/src/app/api/symbols/[symbol]/options/analyze/tradier/route.ts +++ b/src/app/api/symbols/[symbol]/options/analyze/tradier/route.ts @@ -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`; @@ -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') @@ -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 = {}; - 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: [], @@ -55,7 +63,12 @@ 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 = { @@ -63,7 +76,9 @@ export async function GET(request: Request, p: { params: { symbol: string } }) { data: model, dataset: dmodel, strikes: allStrikes, - expirations: allDates + expirations: allDates, + currentPrice, + maxPosition }, raw: allOptionChains } @@ -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); } \ No newline at end of file diff --git a/src/components/delta-heding.tsx b/src/components/delta-heding.tsx index 4c65c7d..44fc7c2 100644 --- a/src/components/delta-heding.tsx +++ b/src/components/delta-heding.tsx @@ -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, @@ -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 ; if (!data) return
No data to show!!!
; - - 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)] @@ -98,43 +89,57 @@ export const DeltaHeding = (props: ITickerProps) => { - ${props.symbol.toUpperCase()} ABS Delta Hedging Exposure (50 DTE) + ${props.symbol.toUpperCase()} ABS Delta Hedging Exposure ({dte} DTE) - {/* - - - - [ - - { - v: 100 - } - ] - - */} - - + + DTE + + + + Strikes + + { { 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)], @@ -161,18 +180,32 @@ export const DeltaHeding = (props: ITickerProps) => { } }), // 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, } - }} - /> + }}> + + + + {/* } -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(); const [isLoading, setIsLoading] = useState(true); @@ -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(); 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 }; }