update option page by including gex
This commit is contained in:
parent
9bbba4b427
commit
db566b7021
@ -31,6 +31,7 @@
|
||||
const parts = formatter.formatToParts(date);
|
||||
|
||||
const year = parts.find((p) => p.type === "year").value;
|
||||
const month = parts.find((p) => p.type === "month").value;
|
||||
const day = parts.find((p) => p.type === "day").value;
|
||||
const hour = parts.find((p) => p.type === "hour").value.padStart(2, "0");
|
||||
const minute = parts
|
||||
@ -39,7 +40,7 @@
|
||||
|
||||
const ampm = parts.find((p) => p.type === "dayPeriod").value; // AM/PM
|
||||
|
||||
return `${day}/${year} ${hour}:${minute} ${ampm}`;
|
||||
return `${month}/${day}/${year} ${hour}:${minute} ${ampm}`;
|
||||
}
|
||||
|
||||
$: columns = [
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
const parts = formatter.formatToParts(date);
|
||||
|
||||
const year = parts.find((p) => p.type === "year").value;
|
||||
const month = parts.find((p) => p.type === "month").value;
|
||||
const day = parts.find((p) => p.type === "day").value;
|
||||
const hour = parts.find((p) => p.type === "hour").value.padStart(2, "0");
|
||||
const minute = parts
|
||||
@ -38,7 +39,7 @@
|
||||
|
||||
const ampm = parts.find((p) => p.type === "dayPeriod").value; // AM/PM
|
||||
|
||||
return `${day}/${year} ${hour}:${minute} ${ampm}`;
|
||||
return `${month}/${day}/${year} ${hour}:${minute} ${ampm}`;
|
||||
}
|
||||
|
||||
let sortOrders = {
|
||||
|
||||
@ -4,7 +4,7 @@ import { loginUserSchema, registerUserSchema } from "$lib/schemas";
|
||||
|
||||
|
||||
export const load = async ({ params, locals }) => {
|
||||
const { apiURL, apiKey } = locals;
|
||||
const { apiURL, apiKey, user } = locals;
|
||||
|
||||
const postData = {
|
||||
ticker: params.tickerID,
|
||||
@ -22,7 +22,9 @@ export const load = async ({ params, locals }) => {
|
||||
body: JSON.stringify(postData),
|
||||
});
|
||||
|
||||
const output = await response.json();
|
||||
let output = await response.json();
|
||||
output.hottestTrades = user?.tier !== "Pro" ? output?.hottestTrades?.slice(0, 3) : output.hottestTrades;
|
||||
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
let priceLevel = data?.getPriceLevel?.priceLevel || [];
|
||||
let hottestTrades = data?.getPriceLevel?.hottestTrades || [];
|
||||
let isLoaded = false;
|
||||
|
||||
onMount(() => {
|
||||
isLoaded = true;
|
||||
});
|
||||
@ -66,24 +65,24 @@
|
||||
>
|
||||
<div class="sm:p-7 w-full m-auto mt-2 sm:mt-0">
|
||||
<div class="w-full mb-2">
|
||||
<div class="flex flex-row items-center mb-4 sm:mb-0">
|
||||
<label
|
||||
for="darkPoolInfo"
|
||||
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-2xl font-bold"
|
||||
>
|
||||
Dark Pool Data
|
||||
</label>
|
||||
<InfoModal
|
||||
title={"Dark Pool Data"}
|
||||
content={"Dark pool data refers to information on trading activities that occur in private, non-public financial exchanges known as dark pools. These venues are used by hedge funds and major institutional traders to buy and sell large blocks of securities without exposing their orders to the public, minimizing market impact and price fluctuations. Currently, nearly 50% of all trades are executed in these dark pools, highlighting their significant role in the trading landscape."}
|
||||
id={"darkPoolInfo"}
|
||||
/>
|
||||
</div>
|
||||
{#if priceLevel?.length === 0 && hottestTrades?.length === 0 && historicalDarkPool?.length === 0}
|
||||
<Infobox
|
||||
text={`No Dark Pool activity are detected for ${$displayCompanyName}`}
|
||||
/>
|
||||
{:else}
|
||||
{:else if priceLevel?.length === 0 && hottestTrades?.length === 0}{:else}
|
||||
<div class="flex flex-row items-center mb-4 sm:mb-0">
|
||||
<label
|
||||
for="darkPoolInfo"
|
||||
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-2xl font-bold"
|
||||
>
|
||||
Dark Pool Data
|
||||
</label>
|
||||
<InfoModal
|
||||
title={"Dark Pool Data"}
|
||||
content={"Dark pool data refers to information on trading activities that occur in private, non-public financial exchanges known as dark pools. These venues are used by hedge funds and major institutional traders to buy and sell large blocks of securities without exposing their orders to the public, minimizing market impact and price fluctuations. Currently, nearly 50% of all trades are executed in these dark pools, highlighting their significant role in the trading landscape."}
|
||||
id={"darkPoolInfo"}
|
||||
/>
|
||||
</div>
|
||||
<Infobox
|
||||
text="Track the Dark Pool Trades of major whales to monitor hidden trading activity and trends."
|
||||
/>
|
||||
@ -96,11 +95,11 @@
|
||||
metrics={data?.getPriceLevel?.metrics}
|
||||
/>
|
||||
{/if}
|
||||
{#if hottestTrades?.length > 0}
|
||||
<HottestTrades {data} rawData={hottestTrades} />
|
||||
{/if}
|
||||
{#if data?.user?.tier === "Pro"}
|
||||
{#if hottestTrades?.length > 0}
|
||||
<HottestTrades rawData={hottestTrades} />
|
||||
{/if}
|
||||
{#if historicalDarkPool?.length > 0}
|
||||
{#if historicalDarkPool?.length > 10}
|
||||
<HistoricalVolume rawData={historicalDarkPool} />
|
||||
{/if}
|
||||
{:else}
|
||||
|
||||
@ -11,6 +11,8 @@
|
||||
const subSectionMap = {
|
||||
overview: "/options",
|
||||
"hottest-contracts": "/options/hottest-contracts",
|
||||
GEX: "/options/gex",
|
||||
DEX: "/options/dex",
|
||||
};
|
||||
|
||||
if (state !== "overview" && subSectionMap[state]) {
|
||||
@ -28,6 +30,8 @@
|
||||
const sectionMap = {
|
||||
overview: "overview",
|
||||
"hottest-contracts": "hottest-contracts",
|
||||
gex: "gex",
|
||||
dex: "dex",
|
||||
};
|
||||
|
||||
const foundSection = parts?.find((part) =>
|
||||
@ -73,6 +77,24 @@
|
||||
>
|
||||
Hottest Contracts
|
||||
</a>
|
||||
<a
|
||||
href={`/stocks/${$stockTicker}/options/gex`}
|
||||
on:click={() => changeSubSection("gex")}
|
||||
class="p-2 px-5 cursor-pointer {displaySubSection === 'gex'
|
||||
? 'text-white bg-primary sm:hover:bg-opacity-[0.95]'
|
||||
: 'text-gray-400 sm:hover:text-white sm:hover:bg-primary sm:hover:bg-opacity-[0.95]'}"
|
||||
>
|
||||
GEX
|
||||
</a>
|
||||
<a
|
||||
href={`/stocks/${$stockTicker}/options/dex`}
|
||||
on:click={() => changeSubSection("dex")}
|
||||
class="p-2 px-5 cursor-pointer {displaySubSection === 'dex'
|
||||
? 'text-white bg-primary sm:hover:bg-opacity-[0.95]'
|
||||
: 'text-gray-400 sm:hover:text-white sm:hover:bg-primary sm:hover:bg-opacity-[0.95]'}"
|
||||
>
|
||||
DEX
|
||||
</a>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="mt-2 sm:mt-0">
|
||||
|
||||
49
src/routes/stocks/[tickerID]/options/gex/+page.server.ts
Normal file
49
src/routes/stocks/[tickerID]/options/gex/+page.server.ts
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
|
||||
export const load = async ({ locals, params }) => {
|
||||
const { apiKey, apiURL, user } = locals;
|
||||
|
||||
const getData = async () => {
|
||||
const postData = {
|
||||
ticker: params.tickerID,
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
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(),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
525
src/routes/stocks/[tickerID]/options/gex/+page.svelte
Normal file
525
src/routes/stocks/[tickerID]/options/gex/+page.svelte
Normal file
@ -0,0 +1,525 @@
|
||||
<script lang="ts">
|
||||
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,
|
||||
]);
|
||||
|
||||
export let data;
|
||||
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,
|
||||
}));
|
||||
|
||||
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,
|
||||
);
|
||||
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 + "<br/>";
|
||||
|
||||
// Add each series data
|
||||
params?.forEach((param) => {
|
||||
const marker =
|
||||
'<span style="display:inline-block;margin-right:4px;' +
|
||||
"border-radius:10px;width:10px;height:10px;background-color:" +
|
||||
param.color +
|
||||
'"></span>';
|
||||
result +=
|
||||
marker +
|
||||
param.seriesName +
|
||||
": " +
|
||||
abbreviateNumber(param.value) +
|
||||
"<br/>";
|
||||
});
|
||||
|
||||
return result;
|
||||
},
|
||||
axisPointer: {
|
||||
lineStyle: {
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
},
|
||||
silent: true,
|
||||
grid: {
|
||||
left: $screenWidth < 640 ? "5%" : "2%",
|
||||
right: $screenWidth < 640 ? "5%" : "2%",
|
||||
bottom: "20%",
|
||||
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_gamma", label: "Call GEX", align: "right" },
|
||||
{ key: "put_gamma", label: "Put GEX", align: "right" },
|
||||
{ key: "put_gamma", 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" },
|
||||
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();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>
|
||||
{$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""}
|
||||
{$displayCompanyName} ({$stockTicker}) Gamma Exposure · Stocknear
|
||||
</title>
|
||||
<meta
|
||||
name="description"
|
||||
content={`Explore historic volume & open interest of option chains & save individual contracts for later`}
|
||||
/>
|
||||
|
||||
<!-- Other meta tags -->
|
||||
<meta
|
||||
property="og:title"
|
||||
content={`${$displayCompanyName} (${$stockTicker}) Gamma Exposure · Stocknear`}
|
||||
/>
|
||||
<meta
|
||||
property="og:description"
|
||||
content={`Explore historic volume & open interest of option chains & save individual contracts for later`}
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<!-- Add more Open Graph meta tags as needed -->
|
||||
|
||||
<!-- Twitter specific meta tags -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta
|
||||
name="twitter:title"
|
||||
content={`${$displayCompanyName} (${$stockTicker}) Gamma Exposure · Stocknear`}
|
||||
/>
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content={`Explore historic volume & open interest of option chains & save individual contracts for later`}
|
||||
/>
|
||||
<!-- Add more Twitter meta tags as needed -->
|
||||
</svelte:head>
|
||||
|
||||
<section
|
||||
class="w-full bg-default overflow-hidden text-white min-h-screen pb-40"
|
||||
>
|
||||
<div class="w-full flex h-full overflow-hidden">
|
||||
<div
|
||||
class="w-full relative flex justify-center items-center overflow-hidden"
|
||||
>
|
||||
{#if rawData?.length > 0}
|
||||
<div class="sm:p-7 w-full m-auto mt-2 sm:mt-0">
|
||||
<h2
|
||||
class=" flex flex-row items-center text-white text-xl sm:text-2xl font-bold w-fit"
|
||||
>
|
||||
Daily Gamma Exposure
|
||||
</h2>
|
||||
|
||||
<div class="w-full overflow-hidden m-auto mt-5">
|
||||
{#if options !== null}
|
||||
<div class="app w-full relative">
|
||||
<div
|
||||
class="flex justify-start space-x-2 absolute right-0 top-0 z-10"
|
||||
>
|
||||
{#each ["3M", "6M", "1Y"] as item}
|
||||
<label
|
||||
on:click={() => (timePeriod = item)}
|
||||
class="px-3 py-1 text-sm {timePeriod === item
|
||||
? 'bg-white text-black '
|
||||
: 'text-white bg-table text-opacity-[0.6]'} transition ease-out duration-100 sm:hover:bg-white sm:hover:text-black rounded-md cursor-pointer"
|
||||
>
|
||||
{item}
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<Chart {init} {options} class="chart" />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex justify-center items-center h-80">
|
||||
<div class="relative">
|
||||
<label
|
||||
class="bg-primary rounded-md 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 sm:loading-[1rem] text-gray-400"
|
||||
></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="w-full overflow-x-scroll text-white">
|
||||
<table
|
||||
class="w-full table table-sm table-compact bg-table border border-gray-800 rounded-none sm:rounded-md m-auto mt-4 overflow-x-auto"
|
||||
>
|
||||
<thead>
|
||||
<TableHeader {columns} {sortOrders} {sortData} />
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each displayList as item, index}
|
||||
<tr
|
||||
class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-odd border-b border-gray-800 {index +
|
||||
1 ===
|
||||
displayList?.slice(0, 3)?.length &&
|
||||
data?.user?.tier !== 'Pro'
|
||||
? 'opacity-[0.1]'
|
||||
: ''}"
|
||||
>
|
||||
<td
|
||||
class="text-white text-sm sm:text-[1rem] text-start whitespace-nowrap"
|
||||
>
|
||||
{formatDate(item?.date)}
|
||||
</td>
|
||||
<td
|
||||
class="text-white text-sm sm:text-[1rem] text-end whitespace-nowrap"
|
||||
>
|
||||
{@html abbreviateNumberWithColor(
|
||||
item?.call_gamma,
|
||||
false,
|
||||
true,
|
||||
)}
|
||||
</td>
|
||||
<td
|
||||
class="text-white text-sm sm:text-[1rem] text-end whitespace-nowrap"
|
||||
>
|
||||
{@html abbreviateNumberWithColor(
|
||||
item?.put_gamma,
|
||||
false,
|
||||
true,
|
||||
)}
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="text-white text-sm sm:text-[1rem] text-end whitespace-nowrap"
|
||||
>
|
||||
{@html abbreviateNumberWithColor(
|
||||
item?.net_gamma,
|
||||
false,
|
||||
true,
|
||||
)}
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="text-white text-sm sm:text-[1rem] text-end whitespace-nowrap"
|
||||
>
|
||||
{#if item?.put_call_ratio <= 1}
|
||||
<span class="text-[#00FC50]"
|
||||
>{item?.put_call_ratio?.toFixed(2)}</span
|
||||
>
|
||||
{:else}
|
||||
<span class="text-[#FF2F1F]"
|
||||
>{item?.put_call_ratio?.toFixed(2)}</span
|
||||
>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<UpgradeToPro {data} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="sm:p-7 w-full m-auto mt-2 sm:mt-0">
|
||||
<h2
|
||||
class=" flex flex-row items-center text-white text-xl sm:text-2xl font-bold w-fit"
|
||||
>
|
||||
Hottest Contracts
|
||||
</h2>
|
||||
<div class="mt-2">
|
||||
<Infobox text="No data is available" />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.app {
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.app {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user