Skip to content

Commit

Permalink
Merge pull request #81 from mnsrulz/main
Browse files Browse the repository at this point in the history
sync: main to stage
  • Loading branch information
mnsrulz authored Nov 8, 2024
2 parents ec9c660 + d59fa2c commit d06ecb8
Show file tree
Hide file tree
Showing 14 changed files with 530 additions and 148 deletions.
3 changes: 3 additions & 0 deletions api.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
GET https://api.tradier.com/beta/markets/fundamentals/calendars?symbols=COIN
Authorization: Bearer 18dpvoVoENkOaSGhGvec1q95jpMO
Accept: application/json
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"pgen": "prisma generate",
"pmigrate": "prisma migrate dev",
"dev": "next dev",
"devtz": "TZ=utc next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
Expand Down
73 changes: 64 additions & 9 deletions src/app/seasonal/[symbol]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,67 @@
import { HistoricalSeason } from '@/components/HistoricalSeason';
import { TickerSearchNavigation } from '@/components/TickerSearchNavigation';
import { getSeasonalView } from '@/lib/tradierService';
import { EarningsSeasonComponent, HistoricalSeason, SeasonHeader } from '@/components/HistoricalSeason';
import { getEarningDates, getSeasonalView } from '@/lib/tradierService';
import { EarningsSeason } from '@/lib/types';
import { Box, Divider } from '@mui/material';

export default async function Page({ params }: { params: { symbol: string } }) {
export default async function Page({ params, searchParams }: { params: { symbol: string }, searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) {
const { symbol } = params;
const dt = await getSeasonalView(symbol, '5y', 'monthly');
return <>
<TickerSearchNavigation basePath='/seasonal' />
<HistoricalSeason data={dt} symbol={symbol} />
</>
const p = await searchParams;
const mode = p['mode'] as string || 'Daily';
const component = await getComponent(mode, symbol);
return <Box sx={{ mt: 1 }}>
<SeasonHeader symbol={symbol} mode={mode} />
<Divider />
{component}
</Box>
}

async function getComponent(mode: string, symbol: string) {
switch (mode?.toLowerCase()) {
case 'daily':
const dailyData = await getSeasonalView(symbol, '1y', 'daily');
return <HistoricalSeason data={dailyData} symbol={symbol} mode={mode} />
case 'monthly':
const monthlyData = await getSeasonalView(symbol, '5y', 'monthly');
return <HistoricalSeason data={monthlyData} symbol={symbol} mode={mode} />
case 'earnings':
const earningsData = await getEarningsView(symbol);
return <EarningsSeasonComponent data={earningsData} symbol={symbol} mode={mode} />
}
return <div>Invlaid mode!</div>
}

async function getEarningsView(symbol: string) {
const earnings = await getEarningDates(symbol);
const { history } = await getSeasonalView(symbol, '5y', 'daily');
const data = history.day;

const entries: EarningsSeason[] = [];
const addData = (ix: number) => {
const lp = ix > 0 ? data[ix - 1].close : data[ix].open;
const closePercentage = ((data[ix].close - lp) / lp);
const openPercentage = ((data[ix].open - lp) / lp);

const nextOpenPercentage = data.length > ix + 1 ? ((data[ix + 1].open - data[ix].close) / data[ix].close) : undefined;
const nextClosePercentage = data.length > ix + 1 ? ((data[ix + 1].close - data[ix].close) / data[ix].close) : undefined;
const nextOpen = data.length > ix + 1 ? data[ix + 1].open : undefined;
const nextClose = data.length > ix + 1 ? data[ix + 1].close : undefined;

entries.push({
open: data[ix].open,
close: data[ix].close,
closePercentage,
openPercentage,
nextOpenPercentage,
nextClosePercentage,
nextOpen,
nextClose,
date: data[ix].date
})
}
for (const e of earnings) {
const ix = data.findIndex(j => j.date == e.begin_date_time);
if (ix < 0) continue;
addData(ix);
}
return entries.reverse();
}
5 changes: 4 additions & 1 deletion src/components/ConditionalFormattingBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ export const ConditionalFormattingBox = (props: IConditionalFormattingBoxProps)
backgroundColor: color,
width: "100%",
height: "100%",
padding: "2px"
padding: "2px", //padding seems to have no effect???
display: 'flex',
alignItems: 'center', // Align content vertically
justifyContent: 'flex-end'
}}
>
{formattedValue}
Expand Down
82 changes: 62 additions & 20 deletions src/components/DeltaGammaHedging.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,24 @@ interface ITickerProps {
}

const colorCodes = getColorPallete();

type OptionsDatasetType = "dex" | "gex" | "oi" | "volume"
enum DexGexType {
'DEX' = 'DEX',
'GEX' = 'GEX'
'GEX' = 'GEX',
'OI' = 'OI',
'VOLUME' = 'VOLUME'
}

const typeMap = {
'DEX': 'dex' as OptionsDatasetType,
'GEX': 'gex' as OptionsDatasetType,
'OI': 'oi' as OptionsDatasetType,
'VOLUME': 'volume' as OptionsDatasetType
}

interface IExpo {
data: OptionsHedgingData,
exposure: 'dex' | 'gex',
exposure: OptionsDatasetType,
symbol: string,
dte: number,
skipAnimation?: boolean
Expand All @@ -40,9 +49,37 @@ export const Expo = (props: IExpo) => {
dataKey: `${j}-put`, label: `${j}`, stack: `stack`, color: colorCodes[data.expirations.indexOf(j)]
}]
});
const gammaOrDelta = (props.exposure == 'dex' ? 'ABS Delta' : 'NET Gamma')

const { dataset, maxPosition } = props.exposure == 'dex' ? data.deltaDataset : data.gammaDataset;
const fn = () => {
switch (props.exposure) {
case 'dex':
return {
gammaOrDelta: 'ABS Delta',
ds: data.deltaDataset
}
case 'gex':
return {
gammaOrDelta: 'NET Gamma',
ds: data.gammaDataset
}

case 'oi':
return {
gammaOrDelta: 'Open interest',
ds: data.oiDataset
}
case 'volume':
return {
gammaOrDelta: 'Volume',
ds: data.volumeDataset
}
}
}

// const gammaOrDelta = (props.exposure == 'dex' ? 'ABS Delta' : 'NET Gamma');
// const { dataset, maxPosition } = props.exposure == 'dex' ? data.deltaDataset : data.gammaDataset;
const { gammaOrDelta, ds } = fn();
const { dataset, maxPosition } = ds;
const title = `$${symbol.toUpperCase()} ${gammaOrDelta} Hedging Exposure (${dte} DTE)`;
return <Paper><Typography variant="h6" align="center" gutterBottom>
{title}
Expand Down Expand Up @@ -116,6 +153,7 @@ export const Expo = (props: IExpo) => {
</BarChart></Paper>
}


export const DeltaGammaHedging = (props: ITickerProps) => {
const { onClose } = props;
const [printMode] = useQueryState('print', parseAsBoolean.withDefault(false));
Expand All @@ -132,7 +170,7 @@ export const DeltaGammaHedging = (props: ITickerProps) => {
return (
<Dialog fullWidth={true} fullScreen={true} open={true} onClose={onClose} aria-labelledby="delta-hedging-dialog" >
<DialogContent style={{ padding: '8px' }}>
{!printMode && (<Box>
{!printMode && (<Box>
<FormControl sx={{ marginTop: 1 }} size="small">
<InputLabel>DTE</InputLabel>
<Select
Expand Down Expand Up @@ -162,6 +200,8 @@ export const DeltaGammaHedging = (props: ITickerProps) => {
<MenuItem value={50}>50</MenuItem>
<MenuItem value={80}>80</MenuItem>
<MenuItem value={100}>100</MenuItem>
<MenuItem value={150}>150</MenuItem>
<MenuItem value={200}>200</MenuItem>
</Select>
</FormControl>
<FormControl sx={{ m: 1, minWidth: 120 }} size="small">
Expand All @@ -180,22 +220,24 @@ export const DeltaGammaHedging = (props: ITickerProps) => {
}
</Select>
</FormControl>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs
value={gexTab}
onChange={(e, v) => setGexTab(v)}
indicatorColor="secondary"
textColor="inherit"
variant="fullWidth"
aria-label="full width tabs example"
>
<Tab label="Dex" value={'DEX'}></Tab>
<Tab label="Gex" value={'GEX'}></Tab>
</Tabs>
</Box>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs
value={gexTab}
onChange={(e, v) => setGexTab(v)}
indicatorColor="secondary"
textColor="inherit"
variant="fullWidth"
aria-label="full width tabs example"
>
<Tab label="Dex" value={'DEX'}></Tab>
<Tab label="Gex" value={'GEX'}></Tab>
<Tab label="Open Interest" value={'OI'}></Tab>
<Tab label="Volume" value={'VOLUME'}></Tab>
</Tabs>
</Box>
</Box>)}
{
isLoading ? <LinearProgress /> : data ? <Expo data={data} exposure={gexTab == DexGexType.DEX ? 'dex' : 'gex'} symbol={props.symbol} dte={dte} skipAnimation={props.skipAnimation} /> : <div>no data...</div>
isLoading ? <LinearProgress /> : data ? <Expo data={data} exposure={typeMap[gexTab]} symbol={props.symbol} dte={dte} skipAnimation={props.skipAnimation} /> : <div>no data...</div>
}
</DialogContent>
{!printMode && (<DialogActions>
Expand Down
54 changes: 54 additions & 0 deletions src/components/HeatMap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ConditionalFormattingBox } from "./ConditionalFormattingBox";
import { numberFormatter, percentageFormatter } from "@/lib/formatters";
import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";

type MyProps = {
zeroHeaderLabel: string,
xLabels: string[],
yLabels: string[],
data: number[][],
formatter: 'percent' | 'number'
}
const formatters = { 'percent': percentageFormatter, number: numberFormatter }
export const HeatMap = (props: MyProps) => {
const { xLabels, yLabels, data, formatter, zeroHeaderLabel } = props;
const fmt = formatters[formatter];
return <TableContainer component={Paper} sx={{
width: 'auto', // Set width of the TableContainer to auto
maxWidth: '100%', // Optional: prevent it from exceeding the parent width
display: 'inline-block', // Make sure the container doesn't stretch
mt: 1
}}>
<Table size="small" sx={{
tableLayout: 'auto', // Allow table to auto-adjust to content
width: 'auto' // Ensure the table takes only the width it needs
}} padding='none'>
<TableHead>
<TableRow key="tablehead-row">
<TableCell key='month' sx={{ px: 1 }}>{zeroHeaderLabel}</TableCell>
{
xLabels.map(c => <TableCell align="right" sx={{ px: 1 }} key={c}>{c}</TableCell>)
}
</TableRow>
</TableHead>
<TableBody>
{data.map((row, ix) => (
<TableRow
key={`row-${ix}`}
sx={{ '&:last-child td, &:last-child th': { border: 0 }, padding: 0 }}
>
<TableCell key={`${ix}-${yLabels[ix]}`} component="th" scope="row" sx={{ flex: 1, px: 1, textAlign: 'right' }}>
{yLabels[ix]}
</TableCell>
{
row.map((c, ixx) => <TableCell key={`${ix}-${ixx}`} align="right" sx={{ flex: 1, height: '32px' }}>
{/* {row[`d${c}`]} */}
<ConditionalFormattingBox value={c * 1000} formattedValue={`${fmt(c)}`} />
</TableCell>)
}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
}
Loading

0 comments on commit d06ecb8

Please sign in to comment.