diff --git a/src/lib/components/OptionsData.svelte b/src/lib/components/OptionsData.svelte index 55f3c96f..544a333d 100644 --- a/src/lib/components/OptionsData.svelte +++ b/src/lib/components/OptionsData.svelte @@ -96,6 +96,17 @@ function prepareData() { } +const timePeriods = { + oneDay: '24h', + oneWeek: 'week', + oneMonth: 'month', + threeMonth: '3 months', + sixMonth: '6 months', + oneYear: '1 year' +}; + +$: period = timePeriods[displayTimePeriod] || displayTimePeriod; + $: { if(($assetType === 'stock' ? $stockTicker : $etfTicker) && typeof window !== 'undefined') { @@ -148,15 +159,17 @@ $: { @@ -209,7 +222,7 @@ $: { - In the past {displayTimePeriod ==='oneDay' ? '24h': displayTimePeriod==='oneWeek' ? 'week' : displayTimePeriod==='oneMonth' ? 'month' : 'three months'}, hedge funds and major institutional traders have bought {abbreviateNumber(callVolume)} calls and {abbreviateNumber(putVolume)} puts with an average DTE of {dataset?.avgDTE} days. + In the past {period}, hedge funds and major institutional traders have bought {abbreviateNumber(callVolume)} calls and {abbreviateNumber(putVolume)} puts with an average DTE of {dataset?.avgDTE} days. diff --git a/src/routes/etf/[tickerID]/options/+page.svelte b/src/routes/etf/[tickerID]/options/+page.svelte index 4be29e86..7b06d7d5 100644 --- a/src/routes/etf/[tickerID]/options/+page.svelte +++ b/src/routes/etf/[tickerID]/options/+page.svelte @@ -1,683 +1,794 @@ - + if(data?.user?.tier === 'Pro') { + window.addEventListener('scroll', handleScroll); + return () => { + window.removeEventListener('scroll', handleScroll); + }; + } +}) + + +$: { + if ((displayTimePeriod || displayData) && optionsPlotData?.length !== 0 && typeof window !== 'undefined') { + // Filter the raw plot data based on the selected time period + filteredList = filterDate(rawPlotData, displayTimePeriod); + + // Process the filtered list to generate the plot data + processPlotData(filteredList); + } +} + + - - - - - - {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ''} {$displayCompanyName} ({$etfTicker}) Options Activity · stocknear - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-

- Unsual Options Activity -

- -
- - - {#if optionsPlotData?.length !== 0} - Last 3 months of options activity involving {$displayCompanyName} by major institutional traders and hedge funds. - {:else} - There's no data available, indicating that major traders may not be actively betting on {$displayCompanyName}. - {/if} - -
- -
+ + + + + + + + + + {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ''} {$displayCompanyName} ({$etfTicker}) Options Activity · stocknear + + + + + + + + + + + + + + + + + + - {#if optionsPlotData?.length !== 0} - -
- -
- -
-
-
+ + + + + \ No newline at end of file diff --git a/src/routes/etf/[tickerID]/options/+page.ts b/src/routes/etf/[tickerID]/options/+page.ts index 800bda47..2cad2a5f 100644 --- a/src/routes/etf/[tickerID]/options/+page.ts +++ b/src/routes/etf/[tickerID]/options/+page.ts @@ -15,16 +15,17 @@ function daysLeft(targetDate) { export const load = async ({ parent, params }) => { + const {apiKey, apiURL} = await parent(); + const getOptionsPlotData = async () => { - let res; const cachedData = getCache(params.tickerID, 'getOptionsPlotData'); if (cachedData) { - res = cachedData; + return cachedData; } else { - + const postData = { ticker: params.tickerID }; @@ -38,43 +39,12 @@ export const load = async ({ parent, params }) => { }); const output = await response.json(); - - const totalCallVolume = output?.reduce((acc, obj) => acc + obj?.CALL?.volume, 0); - const totalPutVolume = output?.reduce((acc, obj) => acc + obj?.PUT?.volume, 0); - const putCallRatio = (totalPutVolume/totalCallVolume)?.toFixed(2); - - const totalVolume = output?.reduce((acc, obj) => { - // Summing volume from both CALL and PUT - return acc + obj?.CALL?.volume + obj?.PUT?.volume; - }, 0); - const totalOpenInterest = output?.reduce((acc, obj) => { - // Summing volume from both CALL and PUT - return acc + obj?.CALL?.open_interest + obj?.PUT?.open_interest; - }, 0); - - - // Summing the total open interest of CALL and PUT options - const totalCallOpenInterest = output?.reduce((acc, obj) => acc + obj?.CALL.open_interest, 0); - const totalPutOpenInterest = output?.reduce((acc, obj) => acc + obj?.PUT.open_interest, 0); - - // Computing the put-call ratio for open interest - const putCallOpenInterestRatio = (totalPutOpenInterest / totalCallOpenInterest)?.toFixed(2); - - //Plot Data - const dateList = output?.map(item => item.date); - const callVolumeList = output?.map(item => item?.CALL?.volume); - const putVolumeList = output?.map(item => item?.PUT?.volume); - const callOpenInterestList = output?.map(item => item?.CALL?.open_interest); - const putOpenInterestList = output?.map(item => item?.PUT?.open_interest); - - res = {plot: output, 'dateList': dateList, 'callOpenInterestList': callOpenInterestList, 'putOpenInterestList': putOpenInterestList, 'callVolumeList': callVolumeList, 'putVolumeList': putVolumeList, 'putCallRatio': putCallRatio, 'putCallOpenInterestRatio': putCallOpenInterestRatio,'totalVolume': totalVolume, 'totalOpenInterest': totalOpenInterest }; - setCache(params.tickerID, res, 'getOptionsPlotData'); + setCache(params.tickerID, output, 'getOptionsPlotData'); + return output; } - return res; - }; const getOptionsFlowData = async () => { diff --git a/src/routes/stocks/[tickerID]/options/+page.svelte b/src/routes/stocks/[tickerID]/options/+page.svelte index 7bc4852a..21e73a6f 100644 --- a/src/routes/stocks/[tickerID]/options/+page.svelte +++ b/src/routes/stocks/[tickerID]/options/+page.svelte @@ -15,6 +15,8 @@ export let data; + let rawPlotData = data?.getOptionsPlotData; + let filteredList = []; const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; let optionsPlotData = data?.getOptionsPlotData?.plot; @@ -30,29 +32,29 @@ let latestPutCallRatio; let displayOTMRatio; - const totalPutCallRatio = data?.getOptionsPlotData?.putCallRatio; - const totalVolume = data?.getOptionsPlotData?.totalVolume; + let totalVolume //= data?.getOptionsPlotData?.totalVolume; - const totalOpenInterest = data?.getOptionsPlotData?.totalOpenInterest; + let totalOpenInterest //= data?.getOptionsPlotData?.totalOpenInterest; // Computing the put-call ratio for open interest - const putCallOpenInterestRatio = data?.getOptionsPlotData?.putCallOpenInterestRatio; - - const displayTotalVolume = new Intl.NumberFormat("en", {minimumFractionDigits: 0, maximumFractionDigits: 0})?.format(totalVolume); - const displayTotalOpenInterest = new Intl.NumberFormat("en", {minimumFractionDigits: 0, maximumFractionDigits: 0})?.format(totalOpenInterest); - - const dateList = data?.getOptionsPlotData?.dateList; - - const callVolumeList = data?.getOptionsPlotData?.callVolumeList; - const putVolumeList = data?.getOptionsPlotData?.putVolumeList; - const callOpenInterestList = data?.getOptionsPlotData?.callOpenInterestList; - const putOpenInterestList = data?.getOptionsPlotData?.putOpenInterestList; + let putCallOpenInterestRatio //= data?.getOptionsPlotData?.putCallOpenInterestRatio; + let putCallRatio; + let displayTotalVolume //= new Intl.NumberFormat("en", {minimumFractionDigits: 0, maximumFractionDigits: 0})?.format(totalVolume); + let displayTotalOpenInterest //= new Intl.NumberFormat("en", {minimumFractionDigits: 0, maximumFractionDigits: 0})?.format(totalOpenInterest); + let displayTotalPutCall + let dateList //= data?.getOptionsPlotData?.dateList; + let callVolumeList //= data?.getOptionsPlotData?.callVolumeList; + let putVolumeList //= data?.getOptionsPlotData?.putVolumeList; + let callOpenInterestList //= data?.getOptionsPlotData?.callOpenInterestList; + let putOpenInterestList //= data?.getOptionsPlotData?.putOpenInterestList; + let displayTimePeriod = 'threeMonths' + function formatDate(dateStr) { // Parse the input date string (YYYY-mm-dd) @@ -96,6 +98,11 @@ displayData = event.target.value; } + function changeTimePeriod(event) + { + displayTimePeriod = event.target.value; + } + function plotData(callData, putData) { @@ -220,7 +227,74 @@ // Calculate OTM/ITM ratio displayOTMRatio = otmVolume / (itmVolume+otmVolume) ?? 0; } - + +function filterDate(filteredList, displayTimePeriod) { + const now = Date.now(); + let cutoffDate; + + switch (displayTimePeriod) { + case 'oneWeek': + cutoffDate = now - 7 * 24 * 60 * 60 * 1000; + break; + case 'oneMonth': + cutoffDate = now - 30 * 24 * 60 * 60 * 1000; + break; + case 'threeMonths': + cutoffDate = now - 90 * 24 * 60 * 60 * 1000; + break; + case 'sixMonths': + cutoffDate = now - 180 * 24 * 60 * 60 * 1000; + break; + case 'oneYear': + cutoffDate = now - 365 * 24 * 60 * 60 * 1000; + break; + default: + throw new Error('Invalid time period'); + } + + return filteredList?.filter(item => { + // Convert YYYY-MM-DD to a timestamp + const [year, month, day] = item?.date?.split('-')?.map(Number); + const itemTimestamp = new Date(year, month - 1, day)?.getTime(); + + return itemTimestamp >= cutoffDate; + }); +} + + +function processPlotData(filteredList: any[]) { + const totals = filteredList?.reduce((acc, obj) => { + acc.callVolume += obj?.CALL?.volume; + acc.putVolume += obj?.PUT?.volume; + acc.callOpenInterest += obj?.CALL?.open_interest; + acc.putOpenInterest += obj?.PUT?.open_interest; + return acc; + }, { callVolume: 0, putVolume: 0, callOpenInterest: 0, putOpenInterest: 0 }); + + putCallRatio = (totals.putVolume / totals.callVolume)?.toFixed(2); + totalVolume = totals.callVolume + totals.putVolume; + totalOpenInterest = totals.callOpenInterest + totals.putOpenInterest; + putCallOpenInterestRatio = (totals.putOpenInterest / totals.callOpenInterest)?.toFixed(2); + + dateList = filteredList?.map(item => item.date); + callVolumeList = filteredList?.map(item => item?.CALL?.volume); + putVolumeList = filteredList?.map(item => item?.PUT?.volume); + callOpenInterestList = filteredList?.map(item => item?.CALL?.open_interest); + putOpenInterestList = filteredList?.map(item => item?.PUT?.open_interest); + + displayTotalVolume = new Intl.NumberFormat("en", {minimumFractionDigits: 0, maximumFractionDigits: 0}).format(totalVolume); + displayTotalPutCall = new Intl.NumberFormat("en", {minimumFractionDigits: 0, maximumFractionDigits: 0}).format(putCallRatio); + displayTotalOpenInterest = new Intl.NumberFormat("en", {minimumFractionDigits: 0, maximumFractionDigits: 0}).format(totalOpenInterest); + + // Determine the type of plot data to generate based on displayData + if (displayData === 'volume') { + options = plotData(callVolumeList, putVolumeList); + } else if (displayData === 'openInterest') { + options = plotData(callOpenInterestList, putOpenInterestList); + } + +} + async function handleScroll() { const scrollThreshold = document.body.offsetHeight * 0.8; // 80% of the website height const isBottom = window.innerHeight + window.scrollY >= scrollThreshold; @@ -233,35 +307,36 @@ - onMount(async () => { - calculateStats(); - - if(data?.user?.tier === 'Pro') { - window.addEventListener('scroll', handleScroll); - return () => { - window.removeEventListener('scroll', handleScroll); - }; - } - }) +onMount(async () => { + calculateStats(); + + if(data?.user?.tier === 'Pro') { + window.addEventListener('scroll', handleScroll); + return () => { + window.removeEventListener('scroll', handleScroll); + }; + } +}) - $: { - if(displayData && optionsPlotData?.length !== 0 && typeof window !== 'undefined') { - if (displayData === 'volume') { - options = plotData(callVolumeList, putVolumeList) - } - else if (displayData === 'openInterest') { - options = plotData(callOpenInterestList, putOpenInterestList) - } - } - } +$: { + if ((displayTimePeriod || displayData) && optionsPlotData?.length !== 0 && typeof window !== 'undefined') { + // Filter the raw plot data based on the selected time period + filteredList = filterDate(rawPlotData, displayTimePeriod); + // Process the filtered list to generate the plot data + processPlotData(filteredList); + } +} + + + - + @@ -300,7 +375,7 @@ {#if optionsPlotData?.length !== 0} - Last 3 months of options activity involving {$displayCompanyName} by major institutional traders and hedge funds. + 1 Year of options activity involving {$displayCompanyName} by major institutional traders and hedge funds. {:else} There's no data available, indicating that major traders may not be actively betting on {$displayCompanyName}. {/if} @@ -311,90 +386,126 @@ {#if optionsPlotData?.length !== 0} - -
- -
- -
-
- - -
-
{displayTotalVolume}
-
- - - -
-
- - -
- -
{displayTotalOpenInterest}
-
- -
-
- - -
-
- {totalPutCallRatio !== 'Infinity' ? totalPutCallRatio : '> 1'} -
-
- -
-
- - -
-
{putCallOpenInterestRatio !== 'Infinity' ? putCallOpenInterestRatio : '> 1'}
-
- -
+
+
+
+ Total Volume + + + + +
+
+ {displayTotalVolume} +
+
+
+
+ Total OI + + + + +
+
+ {displayTotalOpenInterest} +
+
+
+
+ P/C Ratio + + + + +
+
+ {putCallRatio !== 'Infinity' ? putCallRatio : '> 1'} +
+
+
+
+ OI P/C Ratio + + + + +
+
+ {putCallOpenInterestRatio !== 'Infinity' ? putCallOpenInterestRatio : '> 1'} +
+
- - - - -
- + + + + + + + + +
+ +
- + {#if filteredList?.length !== 0} + + {:else} + +
+ + No Options activity found +
+
+ {/if}
@@ -567,7 +678,7 @@ - + {formatTime(item?.time)} @@ -583,11 +694,11 @@ {item?.strike_price} - + {item?.put_call} - + {item?.sentiment} diff --git a/src/routes/stocks/[tickerID]/options/+page.ts b/src/routes/stocks/[tickerID]/options/+page.ts index 21cd28c1..2cad2a5f 100644 --- a/src/routes/stocks/[tickerID]/options/+page.ts +++ b/src/routes/stocks/[tickerID]/options/+page.ts @@ -20,11 +20,10 @@ export const load = async ({ parent, params }) => { const getOptionsPlotData = async () => { - let res; const cachedData = getCache(params.tickerID, 'getOptionsPlotData'); if (cachedData) { - res = cachedData; + return cachedData; } else { const postData = { @@ -40,43 +39,12 @@ export const load = async ({ parent, params }) => { }); const output = await response.json(); - - const totalCallVolume = output?.reduce((acc, obj) => acc + obj?.CALL?.volume, 0); - const totalPutVolume = output?.reduce((acc, obj) => acc + obj?.PUT?.volume, 0); - const putCallRatio = (totalPutVolume/totalCallVolume)?.toFixed(2); - - const totalVolume = output?.reduce((acc, obj) => { - // Summing volume from both CALL and PUT - return acc + obj?.CALL?.volume + obj?.PUT?.volume; - }, 0); - const totalOpenInterest = output?.reduce((acc, obj) => { - // Summing volume from both CALL and PUT - return acc + obj?.CALL?.open_interest + obj?.PUT?.open_interest; - }, 0); - - - // Summing the total open interest of CALL and PUT options - const totalCallOpenInterest = output?.reduce((acc, obj) => acc + obj?.CALL.open_interest, 0); - const totalPutOpenInterest = output?.reduce((acc, obj) => acc + obj?.PUT.open_interest, 0); - - // Computing the put-call ratio for open interest - const putCallOpenInterestRatio = (totalPutOpenInterest / totalCallOpenInterest)?.toFixed(2); - - //Plot Data - const dateList = output?.map(item => item.date); - const callVolumeList = output?.map(item => item?.CALL?.volume); - const putVolumeList = output?.map(item => item?.PUT?.volume); - const callOpenInterestList = output?.map(item => item?.CALL?.open_interest); - const putOpenInterestList = output?.map(item => item?.PUT?.open_interest); - - res = {plot: output, 'dateList': dateList, 'callOpenInterestList': callOpenInterestList, 'putOpenInterestList': putOpenInterestList, 'callVolumeList': callVolumeList, 'putVolumeList': putVolumeList, 'putCallRatio': putCallRatio, 'putCallOpenInterestRatio': putCallOpenInterestRatio,'totalVolume': totalVolume, 'totalOpenInterest': totalOpenInterest }; - setCache(params.tickerID, res, 'getOptionsPlotData'); + setCache(params.tickerID, output, 'getOptionsPlotData'); + return output; } - return res; - }; const getOptionsFlowData = async () => {