This commit is contained in:
MuslemRahimi 2024-09-07 17:41:14 +02:00
parent 46985e413a
commit a5758649b2
3 changed files with 303 additions and 142 deletions

View File

@ -7,10 +7,10 @@
import { onMount } from 'svelte' import { onMount } from 'svelte'
import UpgradeToPro from '$lib/components/UpgradeToPro.svelte'; import UpgradeToPro from '$lib/components/UpgradeToPro.svelte';
import { init, use } from 'echarts/core' import { init, use } from 'echarts/core'
import { BarChart } from 'echarts/charts' import { BarChart,LineChart } from 'echarts/charts'
import { GridComponent, TooltipComponent } from 'echarts/components' import { GridComponent, TooltipComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers' import { CanvasRenderer } from 'echarts/renderers'
use([BarChart, GridComponent, TooltipComponent, CanvasRenderer]) use([BarChart,LineChart, GridComponent, TooltipComponent, CanvasRenderer])
export let data; export let data;
@ -22,6 +22,7 @@
let optionsPlotData = data?.getOptionsPlotData?.plot; let optionsPlotData = data?.getOptionsPlotData?.plot;
let displayData = 'volume'; let displayData = 'volume';
let options; let options;
let optionsGEX;
let rawData = data?.getOptionsFlowData let rawData = data?.getOptionsFlowData
let optionList = rawData?.slice(0,30); let optionList = rawData?.slice(0,30);
let flowSentiment = 'n/a'; let flowSentiment = 'n/a';
@ -55,6 +56,21 @@
let displayTimePeriod = 'threeMonths' let displayTimePeriod = 'threeMonths'
function normalizer(value) {
if (Math?.abs(value) >= 1e18) {
return { unit: 'Q', denominator: 1e18 };
} else if (Math?.abs(value) >= 1e12) {
return { unit: 'T', denominator: 1e12 };
} else if (Math?.abs(value) >= 1e9) {
return { unit: 'B', denominator: 1e9 };
} else if (Math?.abs(value) >= 1e6) {
return { unit: 'M', denominator: 1e6 };
} else if (Math?.abs(value) >= 1e5) {
return { unit: 'K', denominator: 1e5 };
} else {
return { unit: '', denominator: 1 };
}
}
function formatDate(dateStr) { function formatDate(dateStr) {
// Parse the input date string (YYYY-mm-dd) // Parse the input date string (YYYY-mm-dd)
@ -105,7 +121,7 @@
function plotData(callData, putData) { function plotData(callData, putData) {
const options = { const options = {
animation: false, animation: false,
tooltip: { tooltip: {
@ -176,7 +192,119 @@
] ]
}; };
return options; return options;
}
function getGEXPlot() {
let dates = [];
let gexList = [];
let priceList = [];
data?.getOptionsGexData?.forEach(item => {
dates?.push(item?.date);
gexList?.push(item?.gex);
priceList?.push(item?.close)
});
const {unit, denominator } = normalizer(Math.max(...gexList) ?? 0)
const option = {
silent: true,
animation: false,
tooltip: {
trigger: 'axis',
hideDelay: 100, // Set the delay in milliseconds
},
grid: {
left: '0%',
right: '0%',
bottom: '0%',
top: '5%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: dates,
axisLabel: {
color: '#fff',
formatter: function (value) {
// Assuming dates are in the format 'yyyy-mm-dd'
// Extract the month and day from the date string and convert the month to its abbreviated name
const dateParts = value.split('-');
const day = dateParts[2].substring(0); // Extracting the last two digits of the year
const monthIndex = parseInt(dateParts[1]) - 1; // Months are zero-indexed in JavaScript Date objects
return `${day} ${monthNames[monthIndex]}`;
} }
}
},
yAxis: [
{
type: 'value',
splitLine: {
show: false, // Disable x-axis grid lines
},
position: 'left',
axisLabel: {
color: '#fff',
formatter: function (value, index) {
if (index % 2 === 0) {
return value?.toFixed(2); // Format the sentiment value
} else {
return ''; // Hide this tick
}
}
}
},
{
position: 'right',
type: 'value',
splitLine: {
show: false, // Disable x-axis grid lines
},
axisLabel: {
color: '#fff', // Change label color to white
formatter: function (value, index) {
// Display every second tick
if (index % 2 === 0) {
value = Math.max(value, 0);
return (value / denominator)?.toFixed(1) + unit; // Format value in millions
} else {
return ''; // Hide this tick
}
}
},
},
],
series: [
{
name: 'Price',
data: priceList,
type: 'line',
yAxisIndex: 0,
itemStyle: {
color: '#fff' // Change bar color to white
},
showSymbol: false
},
{
name: 'GEX',
data: gexList,
type: 'bar',
yAxisIndex: 1,
itemStyle: {
color: '#8e53f4' // Change bar color to white
},
},
]
};
return option;
}
function calculateStats() { function calculateStats() {
const currentPrice = parseFloat(data?.getStockQuote?.price); const currentPrice = parseFloat(data?.getStockQuote?.price);
@ -309,6 +437,10 @@ function processPlotData(filteredList: any[]) {
onMount(async () => { onMount(async () => {
calculateStats(); calculateStats();
if(data?.getOptionsGexData?.length !== 0) {
optionsGEX = getGEXPlot();
}
if(data?.user?.tier === 'Pro') { if(data?.user?.tier === 'Pro') {
window.addEventListener('scroll', handleScroll); window.addEventListener('scroll', handleScroll);
@ -479,6 +611,17 @@ $: {
</div> </div>
{#if data?.getOptionsGexData?.length !== 0}
<h3 class="text-2xl sm:text-2xl text-gray-200 font-bold mb-4 text-center sm:text-start">
Daily Gamma Exposure (GEX)
</h3>
<div class="app w-full bg-[#09090B] rounded-xl mb-24">
<Chart {init} options={optionsGEX} class="chart" />
</div>
{/if}
<h3 class="text-2xl sm:text-3xl text-gray-200 font-bold mb-4 text-center sm:text-start"> <h3 class="text-2xl sm:text-3xl text-gray-200 font-bold mb-4 text-center sm:text-start">
Latest Options Activity Latest Options Activity
@ -747,14 +890,14 @@ $: {
<style> <style>
.app { .app {
height: 420px; height: 400px;
max-width: 1500px; width: 100%;
} }
@media (max-width: 560px) { @media (max-width: 560px) {
.app { .app {
max-width: 520px; width: 100%;
height: 420px; height: 300px;
} }
} }

View File

@ -1,5 +1,4 @@
import { getCache, setCache } from '$lib/store'; import { getCache, setCache } from "$lib/store";
function daysLeft(targetDate) { function daysLeft(targetDate) {
const targetTime = new Date(targetDate).getTime(); const targetTime = new Date(targetDate).getTime();
@ -12,69 +11,89 @@ function daysLeft(targetDate) {
return daysLeft; return daysLeft;
} }
export const load = async ({ parent, params }) => { export const load = async ({ parent, params }) => {
const { apiKey, apiURL } = await parent();
const {apiKey, apiURL} = await parent();
const getOptionsPlotData = async () => { const getOptionsPlotData = async () => {
const cachedData = getCache(params.tickerID, "getOptionsPlotData");
const cachedData = getCache(params.tickerID, 'getOptionsPlotData');
if (cachedData) { if (cachedData) {
return cachedData; return cachedData;
} else { } else {
const postData = { const postData = {
ticker: params.tickerID ticker: params.tickerID,
}; };
const response = await fetch(apiURL + '/options-plot-ticker', { const response = await fetch(apiURL + "/options-plot-ticker", {
method: 'POST', method: "POST",
headers: { headers: {
"Content-Type": "application/json", "X-API-KEY": apiKey "Content-Type": "application/json",
"X-API-KEY": apiKey,
}, },
body: JSON.stringify(postData) body: JSON.stringify(postData),
}); });
const output = await response.json(); const output = await response.json();
setCache(params.tickerID, output, 'getOptionsPlotData'); setCache(params.tickerID, output, "getOptionsPlotData");
return output; return output;
} }
}; };
const getOptionsFlowData = async () => { const getOptionsFlowData = async () => {
let output; let output;
const cachedData = getCache(params.tickerID, 'getOptionsFlowData'); const cachedData = getCache(params.tickerID, "getOptionsFlowData");
if (cachedData) { if (cachedData) {
output = cachedData; output = cachedData;
} else { } else {
const postData = { const postData = {
ticker: params.tickerID ticker: params.tickerID,
}; };
// make the POST request to the endpoint // make the POST request to the endpoint
const response = await fetch(apiURL + '/options-flow-ticker', { const response = await fetch(apiURL + "/options-flow-ticker", {
method: 'POST', method: "POST",
headers: { headers: {
"Content-Type": "application/json", "X-API-KEY": apiKey "Content-Type": "application/json",
"X-API-KEY": apiKey,
}, },
body: JSON.stringify(postData) body: JSON.stringify(postData),
}); });
output = await response.json(); output = await response.json();
output?.forEach(item => { output?.forEach((item) => {
item.dte = daysLeft(item?.date_expiration); item.dte = daysLeft(item?.date_expiration);
}); });
setCache(params.tickerID, output, 'getOptionsFlowData'); setCache(params.tickerID, output, "getOptionsFlowData");
}
return output;
};
const getOptionsGexData = async () => {
let output;
const cachedData = getCache(params.tickerID, "getOptionsGexData");
if (cachedData) {
output = cachedData;
} else {
const postData = {
ticker: params.tickerID,
};
// make the POST request to the endpoint
const response = await fetch(apiURL + "/options-gex-ticker", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
setCache(params.tickerID, output, "getOptionsGexData");
} }
return output; return output;
@ -83,7 +102,7 @@ export const load = async ({ parent, params }) => {
// Make sure to return a promise // Make sure to return a promise
return { return {
getOptionsPlotData: await getOptionsPlotData(), getOptionsPlotData: await getOptionsPlotData(),
getOptionsFlowData: await getOptionsFlowData() getOptionsFlowData: await getOptionsFlowData(),
getOptionsGexData: await getOptionsGexData(),
}; };
}; };

View File

@ -887,17 +887,16 @@ $: {
<style> <style>
.app { .app {
height: 420px; height: 400px;
max-width: 1500px; width: 100%;
} }
@media (max-width: 560px) { @media (max-width: 560px) {
.app { .app {
max-width: 520px; width: 100%;
height: 420px; height: 300px;
} }
} }