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 f9eb85f..42a5098 100644 --- a/src/app/api/symbols/[symbol]/options/analyze/tradier/route.ts +++ b/src/app/api/symbols/[symbol]/options/analyze/tradier/route.ts @@ -4,6 +4,7 @@ import { NextResponse } from "next/server"; const tradierBaseUri = 'https://sandbox.tradier.com/'; const optionsChain = `${tradierBaseUri}v1/markets/options/chains`; const optionsExpiration = `${tradierBaseUri}v1/markets/options/expirations`; +const getQuotes = `${tradierBaseUri}v1/markets/quotes`; const client = ky.create({ headers: { @@ -14,18 +15,47 @@ const client = ky.create({ export async function GET(request: Request, p: { params: { symbol: string } }) { const { symbol } = p.params; + const currentPrice = await getCurrentPrice(symbol); + if (!currentPrice) throw new Error('Unable to evaluate current price') + const expresp = await client(optionsExpiration, { searchParams: { symbol } }).json<{ expirations: { date: string[] } }>(); - const tillDate = dayjs().add(6, 'weeks'); - const allDates = expresp.expirations.date.filter(j => dayjs(j).isBefore(tillDate)); - + const tillDate = dayjs().add(4, 'weeks'); + const allDates = [...new Set(expresp.expirations.date.filter(j => dayjs(j).isBefore(tillDate)))]; const allOptionChains = await Promise.all(allDates.map(d => getOptionData(symbol, d))); - return NextResponse.json(allOptionChains); + const allStrikes = [...new Set(allOptionChains.flatMap(j => j.options.option.map(s => s.strike)))].filter(s => currentPrice > s * .9 && currentPrice < s * 1.1); + const allOp = allOptionChains.flatMap(j => j.options.option.map(s => s)); + + console.log(`Rendering with dates: ${allDates} and strikes: ${allStrikes}`); + const model: Record = {}; + for (const sp of allStrikes) { + model[sp] = { + calls: [], + puts: [], + data: [] + } + for (const dt of allDates) { + const cv = allOp.find(j => j.strike == sp && j.expiration_date == dt && j.option_type == 'call')?.open_interest || 0; + const pv = allOp.find(j => j.strike == sp && j.expiration_date == dt && j.option_type == 'put')?.open_interest || 0; + model[sp].calls.push(cv); + model[sp].puts.push(pv); + model[sp].data.push(-cv, pv); + } + } + + const finalResponse = { + dh: { + data: model, + strikes: allStrikes + }, + raw: allOptionChains + } + return NextResponse.json(finalResponse); } function getOptionData(symbol: string, expiration: string) { @@ -39,8 +69,26 @@ function getOptionData(symbol: string, expiration: string) { options: { option: { strike: number, - option_type: 'PUT' | 'CALL' - } + open_interest: number, + expiration_date: string, + option_type: 'put' | 'call' + }[] } }>(); } + +async function getCurrentPrice(symbol: string) { + const cp = await client(getQuotes, { + searchParams: { + symbol + } + }).json<{ + quotes: { + quote: { + symbol: string, + last: number + }[] + } + }>(); + return cp.quotes.quote.find(x => x.symbol === symbol)?.last; +} \ No newline at end of file diff --git a/src/components/delta-heding.tsx b/src/components/delta-heding.tsx index 752a47e..1c16c46 100644 --- a/src/components/delta-heding.tsx +++ b/src/components/delta-heding.tsx @@ -1,74 +1,81 @@ -import { IOptionsGrid, NumberRange, OptionsInnerData } from "@/lib/types"; -import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography, Grid } from "@mui/material"; +import { Dialog, DialogContent, DialogActions, Button, Typography, LinearProgress } from "@mui/material"; import { BarChart } from '@mui/x-charts/BarChart'; -import { StrikePriceSlider } from "./StrikePriceSlider"; import { axisClasses } from '@mui/x-charts'; -import { useState } from "react"; +import { useDeltaHedging } from "@/lib/socket"; interface ITickerProps { symbol: string, onClose: () => void } -const data = [ - { strike: 67.5, exposure: 200 }, - { strike: 69.0, exposure: -300 }, - { strike: 70.5, exposure: 400 }, - { strike: 72.0, exposure: -150 }, - { strike: 73.5, exposure: 350 }, - { strike: 75.0, exposure: -250 }, - { strike: 76.5, exposure: 100 }, - { strike: 78.0, exposure: -50 }, - { strike: 79.5, exposure: 450 }, - { strike: 81.0, exposure: -100 }, - { strike: 82.5, exposure: 200 }, -]; - -const seriesA = { - data: [2, 3, 1, 4, 5], - label: 'Series A', -}; -const seriesB = { - data: [3, 1, 4, 2, 1], - label: 'Series B', -}; -const seriesC = { - data: [3, 2, 4, 5, 1], - label: 'Series C', -}; - -const valueFormatter = (value: number | null) => `${value}`; -const chartSetting = { - yAxis: [ - { - label: 'Open interest', - }, - ], - // width: 500, - colors: ['red', 'green'], - height: 500, - sx: { - [`.${axisClasses.left} .${axisClasses.label}`]: { - //transform: 'translate(-20px, 0)', - }, - }, -}; - -const uData = [9, -100,4000, 3000, 2000, 2780, 1890, 2390, 3490]; -const pData = [10, -900, 2400, 1398, -9800, 3908, 4800, -3800, 4300]; - -const xLabels = [ - 'Page A', - 'Page B', - 'Page C', - 'Page D', - 'Page E', - 'Page F', - 'Page G', -]; +// const data = [ +// { strike: 67.5, exposure: 200 }, +// { strike: 69.0, exposure: -300 }, +// { strike: 70.5, exposure: 400 }, +// { strike: 72.0, exposure: -150 }, +// { strike: 73.5, exposure: 350 }, +// { strike: 75.0, exposure: -250 }, +// { strike: 76.5, exposure: 100 }, +// { strike: 78.0, exposure: -50 }, +// { strike: 79.5, exposure: 450 }, +// { strike: 81.0, exposure: -100 }, +// { strike: 82.5, exposure: 200 }, +// ]; + +// const seriesA = { +// data: [2, 3, 1, 4, 5], +// label: 'Series A', +// }; +// const seriesB = { +// data: [3, 1, 4, 2, 1], +// label: 'Series B', +// }; +// const seriesC = { +// data: [3, 2, 4, 5, 1], +// label: 'Series C', +// }; + +// const valueFormatter = (value: number | null) => `${value}`; +// const chartSetting = { +// yAxis: [ +// { +// label: 'Open interest', +// }, +// ], +// // width: 500, +// colors: ['red', 'green'], +// height: 500, +// sx: { +// [`.${axisClasses.left} .${axisClasses.label}`]: { +// //transform: 'translate(-20px, 0)', +// }, +// }, +// }; + +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', +// ]; export const DeltaHeding = (props: ITickerProps) => { const { onClose } = props; + // const rrs = await fetch('') + const { data, isLoading } = useDeltaHedging(props.symbol); + + if (isLoading) return ; + if (!data) return
No data to show!!!
; + + const xLabels = Object.keys(data.data); return ( @@ -84,29 +91,45 @@ export const DeltaHeding = (props: ITickerProps) => { height={500} > - */} + + + [ + + { + v: 100 + } + ] + + */} + + diff --git a/src/lib/socket.ts b/src/lib/socket.ts index 50c2834..82d1b61 100644 --- a/src/lib/socket.ts +++ b/src/lib/socket.ts @@ -96,6 +96,11 @@ type OptionsData = { options: Record } +type OptionsHedgingData = { + dates: string[], + strikes: number[], + data: { puts: number[], calls: number[], data: number[] } +} export const useOptionTracker = (symbol: string) => { @@ -128,6 +133,20 @@ export const useOptionTracker = (symbol: string) => { } + +export const useDeltaHedging = (symbol: string) => { + const [data, setOd] = useState(); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + setIsLoading(true); + ky(`/api/symbols/${symbol}/options/analyze/tradier`).json<{dh: OptionsHedgingData}>().then(r => { + setOd(r.dh); + }).finally(() => setIsLoading(false)); + }, []); + return { data, isLoading }; +} + export const useStockPrice = (item: SearchTickerItem) => { const [od, setOd] = useState(); const fn = async () => {