update options page

This commit is contained in:
MuslemRahimi 2024-10-25 20:38:47 +02:00
parent c3ee0843e3
commit 67551ae6b2
4 changed files with 228 additions and 254 deletions

View File

@ -1,266 +1,214 @@
<script lang ='ts'>
import { optionsNetFlowComponent, stockTicker, assetType, etfTicker, getCache, setCache} from '$lib/store';
import InfoModal from '$lib/components/InfoModal.svelte';
import { Chart } from 'svelte-echarts'
import { init, use } from 'echarts/core'
import { BarChart } from 'echarts/charts'
import { GridComponent, TooltipComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
import { monthNames} from '$lib/utils';
<script lang="ts">
import { onMount } from "svelte";
export let data;
use([BarChart, GridComponent, TooltipComponent, CanvasRenderer])
import InfoModal from "$lib/components/InfoModal.svelte";
import { Chart } from "svelte-echarts";
import { init, use } from "echarts/core";
import { BarChart } from "echarts/charts";
import { GridComponent, TooltipComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
import { monthNames } from "$lib/utils";
export let rawData: Array<{
date: string;
price: number;
netCall: number;
netPut: number;
}>;
let isLoaded = false;
let rawData = [];
let optionsData;
let sentiment;
use([BarChart, GridComponent, TooltipComponent, CanvasRenderer]);
let isLoaded = false;
let optionsData;
let sentiment;
function getPlotOptions() {
let dates = [];
let priceList = [];
let netCallList = [];
let netPutList = [];
const dates = [];
const priceList = [];
const netCallList = [];
const netPutList = [];
// Iterate over the data and extract required information
rawData?.forEach(item => {
dates?.push(item?.date);
priceList?.push(item?.price);
netCallList?.push(item?.netCall)
netPutList?.push(item?.netPut)
});
sentiment = netCallList?.slice(-1)?.at(0) > netPutList?.slice(-1)?.at(0) ? 'bullish' : 'bearish';
const option = {
silent: true,
tooltip: {
trigger: 'axis',
hideDelay: 100, // Set the delay in milliseconds
},
animation: false,
grid: {
left: '3%',
right: '3%',
bottom: '0%',
top: '10%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: dates, // Use the full dates here
axisLabel: {
color: '#fff',
formatter: function (value, index) {
if (index % 2 === 0) {
const dateParts = value.split(' ')[0].split('-');
const day = dateParts[2]; // Extracting the day
const monthIndex = parseInt(dateParts[1], 10) - 1; // Zero-indexed months
return `${day} ${monthNames[monthIndex]}`; // Return formatted day and month
} else {
return '';
}
}
}
},
yAxis: [
{
type: 'value',
splitLine: {
show: false, // Disable x-axis grid lines
},
axisLabel: {
show: false // Hide y-axis labels
}
},
],
series: [
{
name: 'Net Call',
data: netCallList,
type: 'bar',
stack: 'NetFlow',
itemStyle: {
color: '#2256FF'
},
showSymbol: false,
},
{
name: 'Net Put',
data: netPutList,
type: 'bar',
stack: 'NetFlow',
itemStyle: {
color: '#FF2256'
},
showSymbol: false,
},
]
};
return option;
}
const getOptionsNetFlow = async (ticker) => {
// Get cached data for the specific tickerID
const cachedData = getCache(ticker, 'getOptionsNetFlow');
if (cachedData) {
rawData = cachedData;
} else {
const postData = {'ticker': ticker, path: 'options-net-flow-ticker'};
// make the POST request to the endpoint
const response = await fetch('/api/ticker-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postData)
if (rawData && rawData.length) {
// Populate arrays with data
rawData.forEach((item) => {
dates.push(item.date);
priceList.push(item.price);
netCallList.push(item.netCall);
netPutList.push(item.netPut);
});
rawData = (await response.json());
// Cache the data for this specific tickerID with a specific name 'getOptionsNetFlow'
setCache(ticker, rawData, 'getOptionsNetFlow');
}
if(rawData?.length !== 0) {
$optionsNetFlowComponent = true;
// Determine sentiment
sentiment =
netCallList.at(-1) > netPutList.at(-1) ? "bullish" : "bearish";
return {
silent: true,
tooltip: {
trigger: "axis",
hideDelay: 100, // Tooltip delay in milliseconds
},
animation: false,
grid: {
left: "3%",
right: "3%",
bottom: "0%",
top: "10%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: dates,
axisLabel: {
color: "#fff",
formatter: (value, index) => {
if (index % 2 === 0) {
const [year, month, day] = value.split("-");
return `${day} ${monthNames[parseInt(month, 10) - 1]}`;
}
return "";
},
},
},
yAxis: [
{
type: "value",
splitLine: { show: false },
axisLabel: { show: false },
},
],
series: [
{
name: "Net Call",
data: netCallList,
type: "bar",
stack: "NetFlow",
itemStyle: { color: "#2256FF" },
showSymbol: false,
},
{
name: "Net Put",
data: netPutList,
type: "bar",
stack: "NetFlow",
itemStyle: { color: "#FF2256" },
showSymbol: false,
},
],
};
} else {
$optionsNetFlowComponent = false;
console.warn("No raw data available to populate chart options");
return {};
}
};
}
$: {
if($assetType === 'stock' ? $stockTicker :$etfTicker && typeof window !== 'undefined') {
isLoaded=false;
const ticker = $assetType === 'stock' ? $stockTicker :$etfTicker
const asyncFunctions = [
getOptionsNetFlow(ticker)
];
Promise.all(asyncFunctions)
.then((results) => {
optionsData = getPlotOptions();
})
.catch((error) => {
console.error('An error occurred:', error);
});
onMount(() => {
optionsData = getPlotOptions();
isLoaded = true;
}
}
</script>
<section class="overflow-hidden text-white h-full pb-8">
<main class="overflow-hidden ">
<div class="flex flex-row items-center">
<label for="optionsNetFlowInfo" class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold">
Options Net Flow
</label>
<InfoModal
title={"Options Net Flow"}
content={"An Options Net Flow of XY% means the market expects significant price fluctuations for the stock, with an annualized potential range of ±XY% from its current price. This indicates high uncertainty and risk, leading to more expensive options but doesn't predict price direction."}
id={"optionsNetFlowInfo"}
/>
</div>
{#if isLoaded}
{#if rawData?.length !== 0}
});
</script>
<section class="overflow-hidden text-white h-full pb-8">
<main class="overflow-hidden">
<div class="flex flex-row items-center">
<label
for="optionsNetFlowInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
>
Options Net Flow
</label>
<InfoModal
title={"Options Net Flow"}
content={"An Options Net Flow of XY% means the market expects significant price fluctuations for the stock, with an annualized potential range of ±XY% from its current price. This indicates high uncertainty and risk, leading to more expensive options but doesn't predict price direction."}
id={"optionsNetFlowInfo"}
/>
</div>
{#if isLoaded}
{#if rawData?.length !== 0}
<div class="w-full flex flex-col items-start">
<div class="text-white text-[1rem] mt-2 mb-2 w-full">
The options net flow demonstrates a {sentiment} trend in the last 2 trading hours, characterized by the {sentiment === 'bullish' ? 'Net Call Flow exceeding the Net Put Flow' : 'Net Put Flow exceeding the Net Call Flow'}.
</div>
<div class="text-white text-[1rem] mt-2 mb-2 w-full">
The options net flow demonstrates a {sentiment} trend in the last 2 trading
hours, characterized by the {sentiment === "bullish"
? "Net Call Flow exceeding the Net Put Flow"
: "Net Put Flow exceeding the Net Call Flow"}.
</div>
</div>
<div class="pb-2 rounded-lg bg-[#09090B]">
<div class="app w-full h-[300px] mt-5">
<Chart {init} options={optionsData} class="chart" />
</div>
<div class="app w-full h-[300px] mt-5">
<Chart {init} options={optionsData} class="chart" />
</div>
</div>
<div class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12">
<div class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center">
<div class="h-full transform -translate-x-1/2 " aria-hidden="true"></div>
<div class="w-3 h-3 bg-[#2256FF] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2" aria-hidden="true"></div>
<span class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block">
Net Call
</span>
</div>
<div class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center">
<div class="h-full transform -translate-x-1/2 " aria-hidden="true"></div>
<div class="w-3 h-3 bg-[#FF2F1F] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2" aria-hidden="true"></div>
<span class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block">
Net Put
</span>
</div>
</div>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label class="bg-[#09090B] rounded-xl h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
<span class="loading loading-spinner loading-md text-gray-400"></span>
</label>
</div>
</div>
{/if}
<div
class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12"
>
<div
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#2256FF] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block"
>
Net Call
</span>
</div>
</main>
</section>
<style>
<div
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#FF2F1F] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block"
>
Net Put
</span>
</div>
</div>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-[#09090B] rounded-xl h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
>
<span class="loading loading-spinner loading-md text-gray-400"
></span>
</label>
</div>
</div>
{/if}
</main>
</section>
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 210px;
.app {
height: 210px;
}
}
}
.chart {
width: 100%;
width: 100%;
}
</style>
</style>

View File

@ -1,4 +1,23 @@
export const load = async ({ locals, params }) => {
const getOptionsNetFlow = async () => {
const postData = {
ticker: params.tickerID,
};
const response = await fetch(locals?.apiURL + "/options-net-flow-ticker", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": locals?.apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
const getOptionsPlotData = async () => {
const postData = {
ticker: params.tickerID,
@ -33,7 +52,7 @@ export const load = async ({ locals, params }) => {
"X-API-KEY": locals?.apiKey,
},
body: JSON.stringify(postData),
}
},
);
const output = await response.json();
@ -56,7 +75,7 @@ export const load = async ({ locals, params }) => {
"X-API-KEY": locals?.apiKey,
},
body: JSON.stringify(postData),
}
},
);
const output = await response.json();
@ -86,6 +105,7 @@ export const load = async ({ locals, params }) => {
// Make sure to return a promise
return {
getOptionsNetFlow: await getOptionsNetFlow(),
getOptionsPlotData: await getOptionsPlotData(),
getOptionsHistoricalData: await getOptionsHistoricalData(),
getOptionsChainData: await getOptionsChainData(),

View File

@ -561,9 +561,15 @@
>
<div class="sm:p-7 w-full m-auto mt-2 sm:mt-0">
<div class="w-full mb-6">
<h2 class="text-2xl sm:text-3xl text-gray-200 font-bold mb-4">
Unsual Options Activity
</h2>
<div
class="w-full m-auto sm:pb-6 {data?.getOptionsNetFlow?.length === 0
? 'hidden'
: ''}"
>
{#await import("$lib/components/OptionsNetFlow.svelte") then { default: Comp }}
<svelte:component this={Comp} rawData={data?.getOptionsNetFlow} />
{/await}
</div>
<div
class="w-fit text-white p-3 sm:p-5 mb-5 rounded-lg sm:flex sm:flex-row sm:items-center border border-slate-800 text-sm sm:text-[1rem]"

View File

@ -213,7 +213,7 @@ updateYearRange()
><span>Shares Outstanding</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
class="px-[5px] py-1.5 text-right font-semibold xs:px-2.5 xs:py-2"
title="3,194,640,415">3.19B</td
>
</tr><tr class="border-y border-gray-600 odd:bg-[#27272A]"
@ -221,7 +221,7 @@ updateYearRange()
><span>Shares Change (YoY)</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
class="px-[5px] py-1.5 text-right font-semibold xs:px-2.5 xs:py-2"
title="0.309%">+0.31%</td
>
</tr><tr class="border-y border-gray-600 odd:bg-[#27272A]"
@ -229,7 +229,7 @@ updateYearRange()
><span>Shares Change (QoQ)</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
class="px-[5px] py-1.5 text-right font-semibold xs:px-2.5 xs:py-2"
title="0.460%">+0.46%</td
>
</tr><tr class="border-y border-gray-600 odd:bg-[#27272A]"
@ -237,7 +237,7 @@ updateYearRange()
><span>Owned by Insiders (%)</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
class="px-[5px] py-1.5 text-right font-semibold xs:px-2.5 xs:py-2"
title="12.963%">12.96%</td
>
</tr><tr class="border-y border-gray-600 odd:bg-[#27272A]"
@ -245,7 +245,7 @@ updateYearRange()
><span>Owned by Institutions (%)</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
class="px-[5px] py-1.5 text-right font-semibold xs:px-2.5 xs:py-2"
title="45.989%">45.99%</td
>
</tr><tr class="border-y border-gray-600 odd:bg-[#27272A]"
@ -253,7 +253,7 @@ updateYearRange()
><span>Float</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
class="px-[5px] py-1.5 text-right font-semibold xs:px-2.5 xs:py-2"
title="2,777,647,654">2.78B</td
>
</tr></tbody