From 5db8309c7ecd27938acb1e3094db5e1eba525323 Mon Sep 17 00:00:00 2001 From: MuslemRahimi Date: Wed, 8 Jan 2025 01:11:43 +0100 Subject: [PATCH] add expiry page --- .../[tickerID]/options/gex/+page.svelte | 4 +- .../options/gex/expiry/+page.server.ts | 35 ++ .../options/gex/expiry/+page.svelte | 431 ++++++++++++++++++ .../options/gex/strike/+page.server.ts | 17 +- .../options/gex/strike/+page.svelte | 292 ++++-------- 5 files changed, 562 insertions(+), 217 deletions(-) create mode 100644 src/routes/stocks/[tickerID]/options/gex/expiry/+page.server.ts diff --git a/src/routes/stocks/[tickerID]/options/gex/+page.svelte b/src/routes/stocks/[tickerID]/options/gex/+page.svelte index 25348aea..d854d4cb 100644 --- a/src/routes/stocks/[tickerID]/options/gex/+page.svelte +++ b/src/routes/stocks/[tickerID]/options/gex/+page.svelte @@ -145,7 +145,7 @@ grid: { left: $screenWidth < 640 ? "5%" : "0%", right: $screenWidth < 640 ? "5%" : "0%", - bottom: "20%", + bottom: "10%", containLabel: true, }, xAxis: [ @@ -256,7 +256,7 @@ { key: "date", label: "Date", align: "left" }, { key: "call_gamma", label: "Call GEX", align: "right" }, { key: "put_gamma", label: "Put GEX", align: "right" }, - { key: "put_gamma", label: "Net GEX", align: "right" }, + { key: "net_gamma", label: "Net GEX", align: "right" }, { key: "put_call_ratio", label: "P/C GEX", align: "right" }, ]; diff --git a/src/routes/stocks/[tickerID]/options/gex/expiry/+page.server.ts b/src/routes/stocks/[tickerID]/options/gex/expiry/+page.server.ts new file mode 100644 index 00000000..9e7bd003 --- /dev/null +++ b/src/routes/stocks/[tickerID]/options/gex/expiry/+page.server.ts @@ -0,0 +1,35 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + params: params.tickerID, + category: "expiry" + }; + + const response = await fetch(apiURL + "/options-gex-dex", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-KEY": apiKey, + }, + body: JSON.stringify(postData), + }); + const output = await response.json(); + + return output; + }; + + + + + + // Make sure to return a promise + return { + getData: await getData(), + }; +}; + + diff --git a/src/routes/stocks/[tickerID]/options/gex/expiry/+page.svelte b/src/routes/stocks/[tickerID]/options/gex/expiry/+page.svelte index e69de29b..ad8a35a2 100644 --- a/src/routes/stocks/[tickerID]/options/gex/expiry/+page.svelte +++ b/src/routes/stocks/[tickerID]/options/gex/expiry/+page.svelte @@ -0,0 +1,431 @@ + + + + + + + {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} + {$displayCompanyName} ({$stockTicker}) Gamma Exposure by Strike Price · + Stocknear + + + + + + + + + + + + + + + + +
+
+
+ {#if rawData?.length > 0} +
+

+ Gamma Exposure By Expiry +

+ +
+ {#if options !== null} +
+ +
+ {:else} +
+
+ +
+
+ {/if} +
+
+ + + + + + {#each displayList as item, index} + + + + + + + + + + {/each} + +
+ {formatDate(item?.expiry)} + + {@html abbreviateNumberWithColor( + item?.call_gex?.toFixed(2), + false, + true, + )} + + {@html abbreviateNumberWithColor( + item?.put_gex?.toFixed(2), + false, + true, + )} + + {@html abbreviateNumberWithColor( + item?.net_gex?.toFixed(2), + false, + true, + )} + + {#if item?.put_call_ratio <= 1 && item?.put_call_ratio !== null} + {item?.put_call_ratio?.toFixed(2)} + {:else if item?.put_call_ratio > 1 && item?.put_call_ratio !== null} + {item?.put_call_ratio?.toFixed(2)} + {:else} + n/a + {/if} +
+
+ + +
+ {:else} +
+

+ Hottest Contracts +

+
+ +
+
+ {/if} +
+
+
+ + diff --git a/src/routes/stocks/[tickerID]/options/gex/strike/+page.server.ts b/src/routes/stocks/[tickerID]/options/gex/strike/+page.server.ts index a6db54dc..f33b0e12 100644 --- a/src/routes/stocks/[tickerID]/options/gex/strike/+page.server.ts +++ b/src/routes/stocks/[tickerID]/options/gex/strike/+page.server.ts @@ -6,7 +6,7 @@ export const load = async ({ locals, params }) => { const getData = async () => { const postData = { params: params.tickerID, - category: "overview" + category: "strike" }; const response = await fetch(apiURL + "/options-gex-dex", { @@ -23,27 +23,12 @@ export const load = async ({ locals, params }) => { }; - const getHistoricalPrice = async () => { - const postData = { ticker: params.tickerID, timePeriod: "one-year" }; - const response = await fetch(apiURL + "/historical-price", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-API-KEY": apiKey, - }, - body: JSON.stringify(postData), - }); - - const output = await response.json(); - return output; - }; // Make sure to return a promise return { getData: await getData(), - getHistoricalPrice: await getHistoricalPrice(), }; }; diff --git a/src/routes/stocks/[tickerID]/options/gex/strike/+page.svelte b/src/routes/stocks/[tickerID]/options/gex/strike/+page.svelte index 3747adfc..a1ecbc8a 100644 --- a/src/routes/stocks/[tickerID]/options/gex/strike/+page.svelte +++ b/src/routes/stocks/[tickerID]/options/gex/strike/+page.svelte @@ -39,201 +39,107 @@ rawData = rawData?.map((item) => ({ ...item, - net_gamma: (item?.call_gamma || 0) + (item?.put_gamma || 0), + net_gex: (item?.call_gex || 0) + (item?.put_gex || 0), put_call_ratio: - item?.call_gamma > 0 - ? Math.abs((item?.put_gamma || 0) / item?.call_gamma) + item?.call_gex > 0 + ? Math.abs((item?.put_gex || 0) / item?.call_gex) : null, })); let displayList = rawData?.slice(0, 150); - let timePeriod = "3M"; - let options = null; - function filterDataByPeriod(historicalData, period = "3M") { - const currentDate = new Date(); - let startDate = new Date(); - - // Calculate the start date based on the period input - switch (period) { - case "3M": - startDate.setMonth(currentDate.getMonth() - 3); - break; - case "6M": - startDate.setMonth(currentDate.getMonth() - 6); - break; - case "1Y": - startDate.setFullYear(currentDate.getFullYear() - 1); - break; - default: - throw new Error(`Unsupported period: ${period}`); - } - - // Filter the data based on the calculated start date - let filteredData = historicalData?.filter((item) => { - if (!item?.date) return false; - const itemDate = new Date(item.date); - return itemDate >= startDate && itemDate <= currentDate; - }); - - filteredData?.forEach((entry) => { - const matchingData = data?.getHistoricalPrice?.find( - (d) => d?.time === entry?.date, - ); - if (matchingData) { - entry.price = matchingData?.close; - } - }); - - // Extract the dates and gamma values from the filtered data - const dateList = filteredData?.map((item) => item.date); - const gammaList = filteredData?.map((item) => item.net_gamma); - const priceList = filteredData?.map((item) => item.price); - - return { dateList, gammaList, priceList }; - } - function plotData() { - const data = rawData?.sort((a, b) => new Date(a?.date) - new Date(b?.date)); - const { dateList, gammaList, priceList } = filterDataByPeriod( - data, - timePeriod, - ); + // Process and sort data by strike in descending order + const processedData = rawData + ?.map((d) => ({ + strike: d?.strike, + callGamma: d?.call_gex, + putGamma: d?.put_gex, + netGamma: d?.net_gex, + })) + .sort((a, b) => a.strike - b.strike); + + const strikes = processedData.map((d) => d.strike); + const callGamma = processedData.map((d) => d.callGamma?.toFixed(2)); + const putGamma = processedData.map((d) => d.putGamma?.toFixed(2)); + const netGamma = processedData.map((d) => d.netGamma?.toFixed(2)); + const options = { - animation: false, tooltip: { trigger: "axis", - hideDelay: 100, - borderColor: "#969696", // Black border color - borderWidth: 1, // Border width of 1px - backgroundColor: "#313131", // Optional: Set background color for contrast + axisPointer: { + type: "shadow", + }, + backgroundColor: "#313131", textStyle: { - color: "#fff", // Optional: Text color for better visibility + color: "#fff", }, formatter: function (params) { - // Get the timestamp from the first parameter - const timestamp = params[0].axisValue; + const strike = params[0].axisValue; + const put = params[0].data; + const call = params[1].data; + const net = params[2].data; - // Initialize result with timestamp - let result = timestamp + "
"; - - // Add each series data - params?.forEach((param) => { - const marker = - ''; - result += - marker + - param.seriesName + - ": " + - abbreviateNumber(param.value) + - "
"; - }); - - return result; - }, - axisPointer: { - lineStyle: { - color: "#fff", - }, + return ` +
+ Strike: ${strike}
+ ● Put Gamma: ${abbreviateNumberWithColor(put, false, true)}
+ ● Call Gamma: ${abbreviateNumberWithColor(call, false, true)}
+ ● Net Gamma: ${abbreviateNumberWithColor(net, false, true)}
+
`; }, }, - silent: true, grid: { left: $screenWidth < 640 ? "5%" : "0%", right: $screenWidth < 640 ? "5%" : "0%", - bottom: "20%", + bottom: "5%", containLabel: true, }, - xAxis: [ - { - type: "category", - data: dateList, - axisLabel: { - color: "#fff", - - formatter: function (value) { - // Assuming dates are in the format 'yyyy-mm-dd' - const dateParts = value.split("-"); - const monthIndex = parseInt(dateParts[1]) - 1; // Months are zero-indexed in JavaScript Date objects - const year = parseInt(dateParts[0]); - const day = parseInt(dateParts[2]); - return `${day} ${monthNames[monthIndex]} ${year}`; - }, - }, + xAxis: { + type: "value", + name: "Gamma", + nameTextStyle: { color: "#fff" }, + splitLine: { show: false }, + axisLabel: { + show: false, // Hide y-axis labels }, - ], - yAxis: [ - { - type: "value", - splitLine: { - show: false, // Disable x-axis grid lines - }, - axisLabel: { - show: false, // Hide y-axis labels - }, - }, - { - type: "value", - splitLine: { - show: false, // Disable x-axis grid lines - }, - position: "right", - axisLabel: { - show: false, // Hide y-axis labels - }, - }, - ], + }, + yAxis: { + type: "category", + data: strikes, + axisLine: { lineStyle: { color: "#fff" } }, + axisLabel: { color: "#fff" }, + splitLine: { show: false }, + }, series: [ { - name: "Price", - type: "line", - data: priceList, - yAxisIndex: 1, - lineStyle: { width: 2 }, - itemStyle: { - color: "#fff", - }, - smooth: true, - showSymbol: false, + name: "Put Gamma", + type: "bar", + data: putGamma, + stack: "gamma", + itemStyle: { color: "#9B5DC4" }, }, { - name: "Gamma", + name: "Net Gamma", type: "bar", - data: gammaList, - itemStyle: { - color: "#9B5DC4", - }, + data: netGamma, + stack: "gamma", + itemStyle: { color: "#FF2F1F" }, + }, + { + name: "Call Gamma", + type: "bar", + data: callGamma, + stack: "gamma", + itemStyle: { color: "#C4E916" }, }, ], }; + return options; } - function formatDate(dateStr) { - // Parse the input date string (YYYY-mm-dd) - var date = new Date(dateStr); - - // Get month, day, and year - var month = date.getMonth() + 1; // Month starts from 0 - var day = date.getDate(); - var year = date.getFullYear(); - - // Extract the last two digits of the year - var shortYear = year.toString().slice(-2); - - // Add leading zeros if necessary - month = (month < 10 ? "0" : "") + month; - day = (day < 10 ? "0" : "") + day; - - var formattedDate = month + "/" + day + "/" + year; - - return formattedDate; - } - async function handleScroll() { const scrollThreshold = document.body.offsetHeight * 0.8; // 80% of the website height const isBottom = window.innerHeight + window.scrollY >= scrollThreshold; @@ -253,18 +159,18 @@ }); $: columns = [ - { key: "date", label: "Date", align: "left" }, - { key: "call_gamma", label: "Call GEX", align: "right" }, - { key: "put_gamma", label: "Put GEX", align: "right" }, - { key: "put_gamma", label: "Net GEX", align: "right" }, + { key: "strike", label: "Strike Price", align: "left" }, + { key: "call_gex", label: "Call GEX", align: "right" }, + { key: "put_gex", label: "Put GEX", align: "right" }, + { key: "net_gex", label: "Net GEX", align: "right" }, { key: "put_call_ratio", label: "P/C GEX", align: "right" }, ]; $: sortOrders = { - date: { order: "none", type: "date" }, - call_gamma: { order: "none", type: "number" }, - put_gamma: { order: "none", type: "number" }, - net_gamma: { order: "none", type: "number" }, + strike: { order: "none", type: "number" }, + call_gex: { order: "none", type: "number" }, + put_gex: { order: "none", type: "number" }, + net_gex: { order: "none", type: "number" }, put_call_ratio: { order: "none", type: "number" }, }; @@ -326,7 +232,7 @@ }; $: { - if (typeof window !== "undefined" && timePeriod) { + if (typeof window !== "undefined") { options = plotData(); } } @@ -337,7 +243,8 @@ {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} - {$displayCompanyName} ({$stockTicker}) Gamma Exposure · Stocknear + {$displayCompanyName} ({$stockTicker}) Gamma Exposure by Strike Price · + Stocknear - Daily Gamma Exposure + Gamma Exposure By Strike -
+
{#if options !== null}
-
- {#each ["3M", "6M", "1Y"] as item} - - {/each} -
-
{:else} @@ -420,7 +312,7 @@
@@ -438,13 +330,13 @@ @@ -508,14 +402,14 @@
- {formatDate(item?.date)} + {item?.strike?.toFixed(2)} {@html abbreviateNumberWithColor( - item?.call_gamma, + item?.call_gex?.toFixed(2), false, true, )} @@ -453,7 +345,7 @@ class="text-white text-sm sm:text-[1rem] text-end whitespace-nowrap" > {@html abbreviateNumberWithColor( - item?.put_gamma, + item?.put_gex?.toFixed(2), false, true, )} @@ -463,7 +355,7 @@ class="text-white text-sm sm:text-[1rem] text-end whitespace-nowrap" > {@html abbreviateNumberWithColor( - item?.net_gamma, + item?.net_gex?.toFixed(2), false, true, )} @@ -472,14 +364,16 @@ - {#if item?.put_call_ratio <= 1} + {#if item?.put_call_ratio <= 1 && item?.put_call_ratio !== null} {item?.put_call_ratio?.toFixed(2)} - {:else} + {:else if item?.put_call_ratio > 1 && item?.put_call_ratio !== null} {item?.put_call_ratio?.toFixed(2)} + {:else} + n/a {/if}