diff --git a/src/lib/components/Options/GreekByExpiry.svelte b/src/lib/components/Options/GreekByExpiry.svelte new file mode 100644 index 00000000..226d868c --- /dev/null +++ b/src/lib/components/Options/GreekByExpiry.svelte @@ -0,0 +1,362 @@ + + +
+

+ Gamma Exposure By Expiry +

+ +
+ {#if options !== null} +
+ +
+ {:else} +
+
+ +
+
+ {/if} +
+
+ + + + + + {#each data?.user?.tier === "Pro" ? displayList : displayList?.slice(0, 3) 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} +
+
+ + +
+ + diff --git a/src/lib/components/Options/GreekByStrike.svelte b/src/lib/components/Options/GreekByStrike.svelte new file mode 100644 index 00000000..1e922b8d --- /dev/null +++ b/src/lib/components/Options/GreekByStrike.svelte @@ -0,0 +1,350 @@ + + +
+

+ {title} Exposure By Strike +

+ +
+ {#if options !== null} +
+ +
+ {:else} +
+
+ +
+
+ {/if} +
+
+ + + + + + {#each data?.user?.tier === "Pro" ? displayList : displayList?.slice(0, 3) as item, index} + + + + + + + + + + {/each} + +
+ {item?.strike?.toFixed(2)} + + {@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} +
+
+ + +
+ + diff --git a/src/lib/components/Options/GreekExposure.svelte b/src/lib/components/Options/GreekExposure.svelte index 160b2a16..7512c355 100644 --- a/src/lib/components/Options/GreekExposure.svelte +++ b/src/lib/components/Options/GreekExposure.svelte @@ -33,14 +33,27 @@ let rawData = data?.getData || []; - rawData = rawData?.map((item) => ({ - ...item, - net_gamma: (item?.call_gamma || 0) + (item?.put_gamma || 0), - put_call_ratio: - item?.call_gamma > 0 - ? Math.abs((item?.put_gamma || 0) / item?.call_gamma) - : null, - })); + rawData = rawData?.map((item) => { + if (title === "Gamma") { + return { + ...item, + net_gamma: (item?.call_gamma || 0) + (item?.put_gamma || 0), + put_call_ratio: + item?.call_gamma > 0 + ? Math.abs((item?.put_gamma || 0) / item?.call_gamma) + : null, + }; + } else { + return { + ...item, + net_delta: (item?.call_delta || 0) + (item?.put_delta || 0), + put_call_ratio: + item?.call_delta > 0 + ? Math.abs((item?.put_delta || 0) / item?.call_delta) + : null, + }; + } + }); let displayList = rawData?.slice(0, 150); let timePeriod = "3M"; @@ -84,15 +97,17 @@ // 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 dataList = filteredData?.map((item) => + title === "Gamma" ? item.net_gamma : item.net_delta, + ); const priceList = filteredData?.map((item) => item.price); - return { dateList, gammaList, priceList }; + return { dateList, dataList, priceList }; } function plotData() { const data = rawData?.sort((a, b) => new Date(a?.date) - new Date(b?.date)); - const { dateList, gammaList, priceList } = filterDataByPeriod( + const { dateList, dataList, priceList } = filterDataByPeriod( data, timePeriod, ); @@ -197,9 +212,9 @@ showSymbol: false, }, { - name: "Gamma", + name: title, type: "bar", - data: gammaList, + data: dataList, itemStyle: { color: "#9B5DC4", }, @@ -248,21 +263,39 @@ }; }); - $: columns = [ - { key: "date", label: "Date", align: "left" }, - { key: "call_gamma", label: "Call GEX", align: "right" }, - { key: "put_gamma", label: "Put GEX", align: "right" }, - { key: "net_gamma", label: "Net GEX", align: "right" }, - { key: "put_call_ratio", label: "P/C GEX", align: "right" }, - ]; + $: columns = + title === "Gamma" + ? [ + { key: "date", label: "Date", align: "left" }, + { key: "call_gamma", label: "Call GEX", align: "right" }, + { key: "put_gamma", label: "Put GEX", align: "right" }, + { key: "net_gamma", label: "Net GEX", align: "right" }, + { key: "put_call_ratio", label: "P/C GEX", align: "right" }, + ] + : [ + { key: "date", label: "Date", align: "left" }, + { key: "call_delta", label: "Call Delta", align: "right" }, + { key: "put_delta", label: "Put Delta", align: "right" }, + { key: "net_delta", label: "Net Delta", align: "right" }, + { key: "put_call_ratio", label: "P/C Delta", 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" }, - put_call_ratio: { order: "none", type: "number" }, - }; + $: sortOrders = + title === "Gamma" + ? { + date: { order: "none", type: "date" }, + call_gamma: { order: "none", type: "number" }, + put_gamma: { order: "none", type: "number" }, + net_gamma: { order: "none", type: "number" }, + put_call_ratio: { order: "none", type: "number" }, + } + : { + date: { order: "none", type: "date" }, + call_delta: { order: "none", type: "number" }, + put_delta: { order: "none", type: "number" }, + net_delta: { order: "none", type: "number" }, + put_call_ratio: { order: "none", type: "number" }, + }; const sortData = (key) => { // Reset all other keys to 'none' except the current key @@ -332,7 +365,7 @@

- Daily Gamma Exposure + Daily {title} Exposure

@@ -391,18 +424,30 @@ - {@html abbreviateNumberWithColor(item?.call_gamma, false, true)} + {@html abbreviateNumberWithColor( + title === "Gamma" ? item?.call_gamma : item?.call_delta, + false, + true, + )} - {@html abbreviateNumberWithColor(item?.put_gamma, false, true)} + {@html abbreviateNumberWithColor( + title === "Gamma" ? item?.put_gamma : item?.put_delta, + false, + true, + )} - {@html abbreviateNumberWithColor(item?.net_gamma, false, true)} + {@html abbreviateNumberWithColor( + title === "Gamma" ? item?.net_gamma : item?.net_delta, + false, + true, + )} - import { - abbreviateNumberWithColor, - abbreviateNumber, - monthNames, - } from "$lib/utils"; import { stockTicker, - screenWidth, numberOfUnreadNotification, displayCompanyName, } from "$lib/store"; - import { onMount } from "svelte"; - import TableHeader from "$lib/components/Table/TableHeader.svelte"; - import UpgradeToPro from "$lib/components/UpgradeToPro.svelte"; import Infobox from "$lib/components/Infobox.svelte"; - import { Chart } from "svelte-echarts"; - - import { init, use } from "echarts/core"; - import { LineChart, BarChart } from "echarts/charts"; - import { - GridComponent, - TooltipComponent, - LegendComponent, - } from "echarts/components"; - import { CanvasRenderer } from "echarts/renderers"; - - use([ - LineChart, - BarChart, - GridComponent, - TooltipComponent, - LegendComponent, - CanvasRenderer, - ]); + import GreekExposure from "$lib/components/Options/GreekExposure.svelte"; export let data; - let rawData = data?.getData || []; - - rawData = rawData?.map((item) => ({ - ...item, - net_delta: (item?.call_delta || 0) + (item?.put_delta || 0), - put_call_ratio: - item?.call_delta > 0 - ? Math.abs((item?.put_delta || 0) / item?.call_delta) - : 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_delta); - 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, - ); - 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 - textStyle: { - color: "#fff", // Optional: Text color for better visibility - }, - formatter: function (params) { - // Get the timestamp from the first parameter - const timestamp = params[0].axisValue; - - // 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", - }, - }, - }, - silent: true, - grid: { - left: $screenWidth < 640 ? "5%" : "0%", - right: $screenWidth < 640 ? "5%" : "0%", - bottom: "10%", - 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}`; - }, - }, - }, - ], - 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 - }, - }, - ], - series: [ - { - name: "Price", - type: "line", - data: priceList, - yAxisIndex: 1, - lineStyle: { width: 2 }, - itemStyle: { - color: "#fff", - }, - smooth: true, - showSymbol: false, - }, - { - name: "Gamma", - type: "bar", - data: gammaList, - itemStyle: { - color: "#9B5DC4", - }, - }, - ], - }; - 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; - - if (isBottom && displayList?.length !== rawData?.length) { - const nextIndex = displayList?.length; - const filteredNewResults = rawData?.slice(nextIndex, nextIndex + 50); - displayList = [...displayList, ...filteredNewResults]; - } - } - - onMount(() => { - window.addEventListener("scroll", handleScroll); - return () => { - window.removeEventListener("scroll", handleScroll); - }; - }); - - $: columns = [ - { key: "date", label: "Date", align: "left" }, - { key: "call_delta", label: "Call Delta", align: "right" }, - { key: "put_delta", label: "Put Delta", align: "right" }, - { key: "net_delta", label: "Net Delta", align: "right" }, - { key: "put_call_ratio", label: "P/C Delta", align: "right" }, - ]; - - $: sortOrders = { - date: { order: "none", type: "date" }, - call_delta: { order: "none", type: "number" }, - put_delta: { order: "none", type: "number" }, - net_delta: { order: "none", type: "number" }, - put_call_ratio: { order: "none", type: "number" }, - }; - - const sortData = (key) => { - // Reset all other keys to 'none' except the current key - for (const k in sortOrders) { - if (k !== key) { - sortOrders[k].order = "none"; - } - } - - // Cycle through 'none', 'asc', 'desc' for the clicked key - const orderCycle = ["none", "asc", "desc"]; - let originalData = rawData; - const currentOrderIndex = orderCycle.indexOf(sortOrders[key].order); - sortOrders[key].order = - orderCycle[(currentOrderIndex + 1) % orderCycle.length]; - const sortOrder = sortOrders[key].order; - - // Reset to original data when 'none' and stop further sorting - if (sortOrder === "none") { - originalData = [...rawData]; // Reset originalData to rawDataVolume - displayList = originalData; - return; - } - - // Define a generic comparison function - const compareValues = (a, b) => { - const { type } = sortOrders[key]; - let valueA, valueB; - - switch (type) { - case "date": - valueA = new Date(a[key]); - valueB = new Date(b[key]); - break; - case "string": - valueA = a[key].toUpperCase(); - valueB = b[key].toUpperCase(); - return sortOrder === "asc" - ? valueA.localeCompare(valueB) - : valueB.localeCompare(valueA); - case "number": - default: - valueA = parseFloat(a[key]); - valueB = parseFloat(b[key]); - break; - } - - if (sortOrder === "asc") { - return valueA < valueB ? -1 : valueA > valueB ? 1 : 0; - } else { - return valueA > valueB ? -1 : valueA < valueB ? 1 : 0; - } - }; - - // Sort using the generic comparison function - displayList = [...originalData].sort(compareValues); - }; - - $: { - if (typeof window !== "undefined" && timePeriod) { - options = plotData(); - } - } @@ -376,127 +54,10 @@
- {#if rawData?.length > 0} -
-

- Daily Delta Exposure -

- -
- {#if options !== null} -
-
- {#each ["3M", "6M", "1Y"] as item} - - {/each} -
- - -
- {:else} -
-
- -
-
- {/if} -
-
- - - - - - {#each data?.user?.tier === "Pro" ? displayList : displayList?.slice(0, 3) as item, index} - - - - - - - - - - {/each} - -
- {formatDate(item?.date)} - - {@html abbreviateNumberWithColor( - item?.call_delta, - false, - true, - )} - - {@html abbreviateNumberWithColor( - item?.put_delta, - false, - true, - )} - - {@html abbreviateNumberWithColor( - item?.net_delta, - false, - true, - )} - - {#if item?.put_call_ratio <= 1} - {item?.put_call_ratio?.toFixed(2)} - {:else} - {item?.put_call_ratio?.toFixed(2)} - {/if} -
-
- - -
+ {#if data?.getData?.length > 0} + {:else}
-

- Hottest Contracts -

@@ -505,21 +66,3 @@
- - diff --git a/src/routes/stocks/[tickerID]/options/gex/+page.svelte b/src/routes/stocks/[tickerID]/options/gex/+page.svelte index d4d6d53c..70d2a777 100644 --- a/src/routes/stocks/[tickerID]/options/gex/+page.svelte +++ b/src/routes/stocks/[tickerID]/options/gex/+page.svelte @@ -55,7 +55,7 @@ class="w-full relative flex justify-center items-center overflow-hidden" > {#if data?.getData?.length > 0} - + {:else}
diff --git a/src/routes/stocks/[tickerID]/options/gex/expiry/+page.svelte b/src/routes/stocks/[tickerID]/options/gex/expiry/+page.svelte index 2167c4c9..9c9d1e23 100644 --- a/src/routes/stocks/[tickerID]/options/gex/expiry/+page.svelte +++ b/src/routes/stocks/[tickerID]/options/gex/expiry/+page.svelte @@ -1,254 +1,15 @@ @@ -296,106 +57,7 @@ class="w-full relative flex justify-center items-center overflow-hidden" > {#if rawData?.length > 0} -
-

- Gamma Exposure By Expiry -

- -
- {#if options !== null} -
- -
- {:else} -
-
- -
-
- {/if} -
-
- - - - - - {#each data?.user?.tier === "Pro" ? displayList : displayList?.slice(0, 3) 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}

- - diff --git a/src/routes/stocks/[tickerID]/options/gex/strike/+page.svelte b/src/routes/stocks/[tickerID]/options/gex/strike/+page.svelte index acf67f84..a77df318 100644 --- a/src/routes/stocks/[tickerID]/options/gex/strike/+page.svelte +++ b/src/routes/stocks/[tickerID]/options/gex/strike/+page.svelte @@ -1,242 +1,15 @@ @@ -284,107 +57,8 @@
- {#if rawData?.length > 0} -
-

- Gamma Exposure By Strike -

- -
- {#if options !== null} -
- -
- {:else} -
-
- -
-
- {/if} -
-
- - - - - - {#each data?.user?.tier === "Pro" ? displayList : displayList?.slice(0, 3) as item, index} - - - - - - - - - - {/each} - -
- {item?.strike?.toFixed(2)} - - {@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} -
-
- - -
+ {#if data?.getData?.length > 0} + {:else}

- -