update dark pool flow page
This commit is contained in:
parent
9bae61ff29
commit
fa939b926b
@ -4,58 +4,52 @@
|
|||||||
|
|
||||||
import VirtualList from "svelte-tiny-virtual-list";
|
import VirtualList from "svelte-tiny-virtual-list";
|
||||||
import HoverStockChart from "$lib/components/HoverStockChart.svelte";
|
import HoverStockChart from "$lib/components/HoverStockChart.svelte";
|
||||||
import toast from "svelte-french-toast";
|
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
export let optionsWatchlist;
|
|
||||||
export let displayedData = [];
|
export let displayedData = [];
|
||||||
export let filteredData = [];
|
export let filteredData = [];
|
||||||
export let rawData = [];
|
export let rawData = [];
|
||||||
|
|
||||||
let animationClass = "";
|
function formatToNewYorkTime(isoString) {
|
||||||
let animationId = "";
|
const date = new Date(isoString);
|
||||||
|
|
||||||
function formatTime(timeString) {
|
// Get the date components in New York time zone
|
||||||
// Split the time string into components
|
const options = {
|
||||||
const [hours, minutes, seconds] = timeString?.split(":").map(Number);
|
year: "numeric",
|
||||||
|
month: "numeric",
|
||||||
|
day: "numeric",
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
second: "numeric",
|
||||||
|
timeZone: "America/New_York",
|
||||||
|
hour12: false,
|
||||||
|
};
|
||||||
|
|
||||||
// Determine AM or PM
|
// Format date for New York timezone
|
||||||
const period = hours >= 12 ? "PM" : "AM";
|
const formatter = new Intl.DateTimeFormat("en-US", options);
|
||||||
|
const parts = formatter.formatToParts(date);
|
||||||
|
|
||||||
// Convert hours from 24-hour to 12-hour format
|
const year = parts.find((p) => p.type === "year").value;
|
||||||
const formattedHours = hours % 12 || 12; // Converts 0 to 12 for midnight
|
const day = parts.find((p) => p.type === "day").value;
|
||||||
|
const hour = parts.find((p) => p.type === "hour").value.padStart(2, "0");
|
||||||
|
const minute = parts
|
||||||
|
.find((p) => p.type === "minute")
|
||||||
|
.value.padStart(2, "0");
|
||||||
|
const second = parts
|
||||||
|
.find((p) => p.type === "second")
|
||||||
|
.value.padStart(2, "0");
|
||||||
|
|
||||||
// Format the time string
|
return `${day}/${year} ${hour}:${minute}:${second}`;
|
||||||
const formattedTimeString = `${formattedHours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")} ${period}`;
|
|
||||||
|
|
||||||
return formattedTimeString;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reformatDate(dateString) {
|
|
||||||
return (
|
|
||||||
dateString.substring(5, 7) +
|
|
||||||
"/" +
|
|
||||||
dateString.substring(8) +
|
|
||||||
"/" +
|
|
||||||
dateString.substring(2, 4)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let sortOrders = {
|
let sortOrders = {
|
||||||
time: "none",
|
date: "none",
|
||||||
ticker: "none",
|
ticker: "none",
|
||||||
expiry: "none",
|
|
||||||
dte: "none",
|
|
||||||
strike: "none",
|
|
||||||
callPut: "none",
|
|
||||||
sentiment: "none",
|
|
||||||
spot: "none",
|
|
||||||
price: "none",
|
price: "none",
|
||||||
premium: "none",
|
premium: "none",
|
||||||
type: "none",
|
assetType: "none",
|
||||||
exec: "none",
|
volume: "none",
|
||||||
vol: "none",
|
size: "none",
|
||||||
oi: "none",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generalized sorting function
|
// Generalized sorting function
|
||||||
@ -83,11 +77,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const compareFunctions = {
|
const compareFunctions = {
|
||||||
time: (a, b) => {
|
|
||||||
const timeA = new Date("1970-01-01T" + a.time).getTime();
|
|
||||||
const timeB = new Date("1970-01-01T" + b.time).getTime();
|
|
||||||
return sortOrder === "asc" ? timeA - timeB : timeB - timeA;
|
|
||||||
},
|
|
||||||
ticker: (a, b) => {
|
ticker: (a, b) => {
|
||||||
const tickerA = a.ticker.toUpperCase();
|
const tickerA = a.ticker.toUpperCase();
|
||||||
const tickerB = b.ticker.toUpperCase();
|
const tickerB = b.ticker.toUpperCase();
|
||||||
@ -95,34 +84,19 @@
|
|||||||
? tickerA.localeCompare(tickerB)
|
? tickerA.localeCompare(tickerB)
|
||||||
: tickerB.localeCompare(tickerA);
|
: tickerB.localeCompare(tickerA);
|
||||||
},
|
},
|
||||||
expiry: (a, b) => {
|
date: (a, b) => {
|
||||||
const timeA = new Date(a.date_expiration);
|
const timeA = new Date(a.date);
|
||||||
const timeB = new Date(b.date_expiration);
|
const timeB = new Date(b.date);
|
||||||
return sortOrder === "asc" ? timeA - timeB : timeB - timeA;
|
return sortOrder === "asc" ? timeA - timeB : timeB - timeA;
|
||||||
},
|
},
|
||||||
dte: (a, b) => {
|
|
||||||
const timeA = new Date(a.date_expiration);
|
|
||||||
const timeB = new Date(b.date_expiration);
|
|
||||||
return sortOrder === "asc" ? timeA - timeB : timeB - timeA;
|
|
||||||
},
|
|
||||||
strike: (a, b) => {
|
|
||||||
const strikeA = parseFloat(a.strike_price);
|
|
||||||
const strikeB = parseFloat(b.strike_price);
|
|
||||||
return sortOrder === "asc" ? strikeA - strikeB : strikeB - strikeA;
|
|
||||||
},
|
|
||||||
spot: (a, b) => {
|
|
||||||
const spotA = parseFloat(a.underlying_price);
|
|
||||||
const spotB = parseFloat(b.underlying_price);
|
|
||||||
return sortOrder === "asc" ? spotA - spotB : spotB - spotA;
|
|
||||||
},
|
|
||||||
price: (a, b) => {
|
price: (a, b) => {
|
||||||
const priceA = parseFloat(a.price);
|
const priceA = parseFloat(a.price);
|
||||||
const priceB = parseFloat(b.price);
|
const priceB = parseFloat(b.price);
|
||||||
return sortOrder === "asc" ? priceA - priceB : priceB - priceA;
|
return sortOrder === "asc" ? priceA - priceB : priceB - priceA;
|
||||||
},
|
},
|
||||||
premium: (a, b) => {
|
premium: (a, b) => {
|
||||||
const premiumA = parseFloat(a.cost_basis);
|
const premiumA = parseFloat(a.premium);
|
||||||
const premiumB = parseFloat(b.cost_basis);
|
const premiumB = parseFloat(b.premium);
|
||||||
return sortOrder === "asc" ? premiumA - premiumB : premiumB - premiumA;
|
return sortOrder === "asc" ? premiumA - premiumB : premiumB - premiumA;
|
||||||
},
|
},
|
||||||
size: (a, b) => {
|
size: (a, b) => {
|
||||||
@ -130,44 +104,27 @@
|
|||||||
const volB = parseFloat(b?.size);
|
const volB = parseFloat(b?.size);
|
||||||
return sortOrder === "asc" ? volA - volB : volB - volA;
|
return sortOrder === "asc" ? volA - volB : volB - volA;
|
||||||
},
|
},
|
||||||
vol: (a, b) => {
|
volume: (a, b) => {
|
||||||
const volA = parseFloat(a.volume);
|
const volA = parseFloat(a.volume);
|
||||||
const volB = parseFloat(b.volume);
|
const volB = parseFloat(b.volume);
|
||||||
return sortOrder === "asc" ? volA - volB : volB - volA;
|
return sortOrder === "asc" ? volA - volB : volB - volA;
|
||||||
},
|
},
|
||||||
oi: (a, b) => {
|
dailyVolume: (a, b) => {
|
||||||
const oiA = parseFloat(a.open_interest);
|
const volA = parseFloat(a.dailyVolumePercentage);
|
||||||
const oiB = parseFloat(b.open_interest);
|
const volB = parseFloat(b.dailyVolumePercentage);
|
||||||
return sortOrder === "asc" ? oiA - oiB : oiB - oiA;
|
return sortOrder === "asc" ? volA - volB : volB - volA;
|
||||||
},
|
},
|
||||||
callPut: (a, b) => {
|
avgVolume: (a, b) => {
|
||||||
const callPutA = a.put_call?.toUpperCase();
|
const volA = parseFloat(a.avgVolumePercentage);
|
||||||
const callPutB = b.put_call?.toUpperCase();
|
const volB = parseFloat(b.avgVolumePercentage);
|
||||||
return sortOrder === "asc"
|
return sortOrder === "asc" ? volA - volB : volB - volA;
|
||||||
? callPutA.localeCompare(callPutB)
|
|
||||||
: callPutB.localeCompare(callPutA);
|
|
||||||
},
|
},
|
||||||
sentiment: (a, b) => {
|
assetType: (a, b) => {
|
||||||
const sentimentOrder = { BULLISH: 1, NEUTRAL: 2, BEARISH: 3 };
|
|
||||||
const sentimentA = sentimentOrder[a?.sentiment?.toUpperCase()] || 4;
|
|
||||||
const sentimentB = sentimentOrder[b?.sentiment?.toUpperCase()] || 4;
|
|
||||||
return sortOrder === "asc"
|
|
||||||
? sentimentA - sentimentB
|
|
||||||
: sentimentB - sentimentA;
|
|
||||||
},
|
|
||||||
type: (a, b) => {
|
|
||||||
const typeOrder = { SWEEP: 1, TRADE: 2 };
|
const typeOrder = { SWEEP: 1, TRADE: 2 };
|
||||||
const typeA = typeOrder[a.option_activity_type?.toUpperCase()] || 3;
|
const typeA = typeOrder[a.assetType?.toUpperCase()] || 3;
|
||||||
const typeB = typeOrder[b.option_activity_type?.toUpperCase()] || 3;
|
const typeB = typeOrder[b.assetType?.toUpperCase()] || 3;
|
||||||
return sortOrder === "asc" ? typeA - typeB : typeB - typeA;
|
return sortOrder === "asc" ? typeA - typeB : typeB - typeA;
|
||||||
},
|
},
|
||||||
exec: (a, b) => {
|
|
||||||
const tickerA = a?.execution_estimate?.toUpperCase();
|
|
||||||
const tickerB = b?.execution_estimate?.toUpperCase();
|
|
||||||
return sortOrder === "asc"
|
|
||||||
? tickerA.localeCompare(tickerB)
|
|
||||||
: tickerB.localeCompare(tickerA);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sort using the appropriate comparison function
|
// Sort using the appropriate comparison function
|
||||||
@ -183,15 +140,18 @@
|
|||||||
itemCount={displayedData.length}
|
itemCount={displayedData.length}
|
||||||
itemSize={40}
|
itemSize={40}
|
||||||
>
|
>
|
||||||
<div slot="header" class="tr th sticky z-40 top-0">
|
<div
|
||||||
|
slot="header"
|
||||||
|
class="tr th m-auto sticky z-40 top-0 border-b border-gray-800 shadow-xl"
|
||||||
|
>
|
||||||
<!-- Table headers -->
|
<!-- Table headers -->
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("time")}
|
on:click={() => sortData("date")}
|
||||||
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
Time
|
Date
|
||||||
<svg
|
<svg
|
||||||
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['time'] ===
|
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['date'] ===
|
||||||
'asc'
|
'asc'
|
||||||
? 'rotate-180'
|
? 'rotate-180'
|
||||||
: ''} "
|
: ''} "
|
||||||
@ -207,7 +167,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("ticker")}
|
on:click={() => sortData("ticker")}
|
||||||
class="td cursor-pointer select-none bg-[#1E222D] font-bold text-slate-300 text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] font-bold text-slate-300 text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
Symbol
|
Symbol
|
||||||
<svg
|
<svg
|
||||||
@ -230,7 +190,7 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("price")}
|
on:click={() => sortData("price")}
|
||||||
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
Price
|
Price
|
||||||
<svg
|
<svg
|
||||||
@ -252,7 +212,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("premium")}
|
on:click={() => sortData("premium")}
|
||||||
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
Premium
|
Premium
|
||||||
<svg
|
<svg
|
||||||
@ -275,7 +235,7 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("size")}
|
on:click={() => sortData("size")}
|
||||||
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
Size
|
Size
|
||||||
<svg
|
<svg
|
||||||
@ -296,15 +256,15 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("vol")}
|
on:click={() => sortData("volume")}
|
||||||
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
Volume
|
Volume
|
||||||
<svg
|
<svg
|
||||||
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['vol'] ===
|
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['volume'] ===
|
||||||
'asc'
|
'asc'
|
||||||
? 'rotate-180'
|
? 'rotate-180'
|
||||||
: sortOrders['vol'] === 'desc'
|
: sortOrders['volume'] === 'desc'
|
||||||
? ''
|
? ''
|
||||||
: 'hidden'} "
|
: 'hidden'} "
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -319,15 +279,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("vol")}
|
on:click={() => sortData("dailyVolume")}
|
||||||
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
% Daily Volume
|
% Daily Volume
|
||||||
<svg
|
<svg
|
||||||
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['vol'] ===
|
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
|
||||||
'asc'
|
'dailyVolume'
|
||||||
|
] === 'asc'
|
||||||
? 'rotate-180'
|
? 'rotate-180'
|
||||||
: sortOrders['vol'] === 'desc'
|
: sortOrders['dailyVolume'] === 'desc'
|
||||||
? ''
|
? ''
|
||||||
: 'hidden'} "
|
: 'hidden'} "
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -342,15 +303,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("vol")}
|
on:click={() => sortData("avgVolume")}
|
||||||
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
% 30D Volume
|
% 30D Volume
|
||||||
<svg
|
<svg
|
||||||
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['vol'] ===
|
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
|
||||||
'asc'
|
'avgVolume'
|
||||||
|
] === 'asc'
|
||||||
? 'rotate-180'
|
? 'rotate-180'
|
||||||
: sortOrders['vol'] === 'desc'
|
: sortOrders['avgVolume'] === 'desc'
|
||||||
? ''
|
? ''
|
||||||
: 'hidden'} "
|
: 'hidden'} "
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -365,15 +327,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("vol")}
|
on:click={() => sortData("sector")}
|
||||||
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
Sector
|
Sector
|
||||||
<svg
|
<svg
|
||||||
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['vol'] ===
|
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['sector'] ===
|
||||||
'asc'
|
'asc'
|
||||||
? 'rotate-180'
|
? 'rotate-180'
|
||||||
: sortOrders['vol'] === 'desc'
|
: sortOrders['sector'] === 'desc'
|
||||||
? ''
|
? ''
|
||||||
: 'hidden'} "
|
: 'hidden'} "
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -389,7 +351,7 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("vol")}
|
on:click={() => sortData("vol")}
|
||||||
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
Issue Type
|
Issue Type
|
||||||
<svg
|
<svg
|
||||||
@ -416,15 +378,15 @@
|
|||||||
let:index
|
let:index
|
||||||
let:style
|
let:style
|
||||||
{style}
|
{style}
|
||||||
class="tr {index % 2 === 0 ? 'bg-secondary' : 'bg-[#09090B]'}"
|
class="tr {index % 2 === 0 ? 'bg-[#19191F]' : 'bg-[#121217]'}"
|
||||||
>
|
>
|
||||||
<!-- Row data -->
|
<!-- Row data -->
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style="justify-content: center; "
|
style="justify-content: center; "
|
||||||
class="td text-white text-sm sm:text-[1rem] text-start m-auto whitespace-nowrap"
|
class=" td w-full text-white text-xs sm:text-sm whitespace-nowrap m-auto"
|
||||||
>
|
>
|
||||||
{reformatDate(displayedData[index]?.date_expiration)}
|
{formatToNewYorkTime(displayedData[index]?.date)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
on:click|stopPropagation
|
on:click|stopPropagation
|
||||||
@ -433,7 +395,7 @@
|
|||||||
>
|
>
|
||||||
<HoverStockChart
|
<HoverStockChart
|
||||||
symbol={displayedData[index]?.ticker}
|
symbol={displayedData[index]?.ticker}
|
||||||
assetType={displayedData[index]?.underlying_type}
|
assetType={displayedData[index]?.assetType}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -448,7 +410,7 @@
|
|||||||
style="justify-content: center;"
|
style="justify-content: center;"
|
||||||
class="td text-sm sm:text-[1rem] text-start text-white"
|
class="td text-sm sm:text-[1rem] text-start text-white"
|
||||||
>
|
>
|
||||||
{@html abbreviateNumber(displayedData[index]?.cost_basis, true, true)}
|
{@html abbreviateNumber(displayedData[index]?.premium, true, true)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -465,44 +427,41 @@
|
|||||||
style="justify-content: center;"
|
style="justify-content: center;"
|
||||||
class="td text-sm sm:text-[1rem] text-white text-end"
|
class="td text-sm sm:text-[1rem] text-white text-end"
|
||||||
>
|
>
|
||||||
{new Intl.NumberFormat("en", {
|
{@html abbreviateNumber(displayedData[index]?.volume, false, true)}
|
||||||
minimumFractionDigits: 0,
|
|
||||||
maximumFractionDigits: 0,
|
|
||||||
}).format(displayedData[index]?.volume)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style="justify-content: center;"
|
style="justify-content: center;"
|
||||||
class="td text-sm sm:text-[1rem] text-white text-end"
|
class="td text-sm sm:text-[1rem] text-white text-end"
|
||||||
>
|
>
|
||||||
{new Intl.NumberFormat("en", {
|
{displayedData[index]?.dailyVolumePercentage > 0.01
|
||||||
minimumFractionDigits: 0,
|
? displayedData[index]?.dailyVolumePercentage?.toFixed(2) + "%"
|
||||||
maximumFractionDigits: 0,
|
: "< 0.01%"}
|
||||||
}).format(displayedData[index]?.volume)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style="justify-content: center;"
|
style="justify-content: center;"
|
||||||
class="td text-sm sm:text-[1rem] text-white text-end"
|
class="td text-sm sm:text-[1rem] text-white text-end"
|
||||||
>
|
>
|
||||||
{new Intl.NumberFormat("en", {
|
{displayedData[index]?.avgVolume > 0.01
|
||||||
minimumFractionDigits: 0,
|
? displayedData[index]?.avgVolume?.toFixed(2) + "%"
|
||||||
maximumFractionDigits: 0,
|
: "< 0.01%"}
|
||||||
}).format(displayedData[index]?.volume)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style="justify-content: center;"
|
style="justify-content: start;"
|
||||||
class="td text-sm sm:text-[1rem] text-white text-end truncate"
|
class="td text-sm sm:text-[1rem] text-white text-start"
|
||||||
>
|
>
|
||||||
Healthcare
|
{displayedData[index]?.sector?.length > 13
|
||||||
|
? displayedData[index]?.sector?.slice(0, 13) + "..."
|
||||||
|
: displayedData[index]?.sector}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style="justify-content: center;"
|
style="justify-content: center;"
|
||||||
class="td text-sm sm:text-[1rem] text-white text-end"
|
class="td text-sm sm:text-[1rem] text-white text-end"
|
||||||
>
|
>
|
||||||
Stock
|
{displayedData[index]?.assetType}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</VirtualList>
|
</VirtualList>
|
||||||
@ -537,7 +496,6 @@
|
|||||||
.th {
|
.th {
|
||||||
display: none;
|
display: none;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
background-color: #09090b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.th > .td {
|
.th > .td {
|
||||||
@ -571,27 +529,4 @@
|
|||||||
min-width: 0px;
|
min-width: 0px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heartbeat {
|
|
||||||
animation: heartbeat-animation 0.3s;
|
|
||||||
animation-timing-function: ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes heartbeat-animation {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg) scale(0.95);
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
transform: rotate(10deg) scale(1.05);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: rotate(0deg) scale(1.2);
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
transform: rotate(-10deg) scale(1.05);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: rotate(0deg) scale(0.95);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -657,11 +657,11 @@ export function abbreviateNumber(number, addDollarSign = false, color = false) {
|
|||||||
|
|
||||||
if (color) {
|
if (color) {
|
||||||
if (suffix === "K") {
|
if (suffix === "K") {
|
||||||
suffix = '<span class=\"font-semibold text-[#A4F720]\">K</span>';
|
suffix = '<span class=\"font-semibold text-[#8374DC]\">K</span>';
|
||||||
} else if (suffix === "M") {
|
} else if (suffix === "M") {
|
||||||
suffix = '<span class=\"font-semibold text-[#A4F720]\">M</span>';
|
suffix = '<span class=\"font-semibold text-[#FACD38]\">M</span>';
|
||||||
} else if (suffix === "B") {
|
} else if (suffix === "B") {
|
||||||
suffix = '<span class=\"font-semibold text-[#A4F720]\">B</span>';
|
suffix = '<span class=\"font-semibold text-[#FACD38]\">B</span>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
export const load = async ({ locals, cookies }) => {
|
export const load = async ({ locals }) => {
|
||||||
const { apiURL, apiKey, pb, user } = locals;
|
const { apiURL, apiKey } = locals;
|
||||||
|
|
||||||
const getOptionsFlowFeed = async () => {
|
const getFlowData = async () => {
|
||||||
// make the POST request to the endpoint
|
// make the POST request to the endpoint
|
||||||
const response = await fetch(apiURL + "/options-flow-feed", {
|
const response = await fetch(apiURL + "/dark-pool-flow-feed", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@ -15,42 +15,8 @@ export const load = async ({ locals, cookies }) => {
|
|||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPredefinedCookieRuleOfList = async () => {
|
|
||||||
// make the POST request to the endpoint
|
|
||||||
const ruleOfList = cookies.get("options-flow-filter-cookie") ?? [];
|
|
||||||
const output =
|
|
||||||
ruleOfList?.length !== 0
|
|
||||||
? JSON.parse(ruleOfList)
|
|
||||||
: [
|
|
||||||
{ name: "cost_basis", value: "any" },
|
|
||||||
{ name: "date_expiration", value: "any" },
|
|
||||||
];
|
|
||||||
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getOptionsWatchlist = async () => {
|
|
||||||
let output;
|
|
||||||
try {
|
|
||||||
output = (
|
|
||||||
await pb?.collection("optionsWatchlist").getFullList({
|
|
||||||
filter: `user="${user?.id}"`,
|
|
||||||
})
|
|
||||||
)?.at(0);
|
|
||||||
if (output === undefined) {
|
|
||||||
output = { optionsId: [] };
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
//console.log(e)
|
|
||||||
output = { optionsId: [] };
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure to return a promise
|
|
||||||
return {
|
return {
|
||||||
getOptionsFlowFeed: await getOptionsFlowFeed(),
|
getFlowData: await getFlowData(),
|
||||||
getPredefinedCookieRuleOfList: await getPredefinedCookieRuleOfList(),
|
|
||||||
getOptionsWatchlist: await getOptionsWatchlist(),
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,7 +14,6 @@
|
|||||||
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
|
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
|
||||||
import * as Popover from "$lib/components/shadcn/popover/index.js";
|
import * as Popover from "$lib/components/shadcn/popover/index.js";
|
||||||
import { Button } from "$lib/components/shadcn/button/index.js";
|
import { Button } from "$lib/components/shadcn/button/index.js";
|
||||||
import { Calendar } from "$lib/components/shadcn/calendar/index.js";
|
|
||||||
import CalendarIcon from "lucide-svelte/icons/calendar";
|
import CalendarIcon from "lucide-svelte/icons/calendar";
|
||||||
|
|
||||||
import { page } from "$app/stores";
|
import { page } from "$app/stores";
|
||||||
@ -25,8 +24,6 @@
|
|||||||
export let data;
|
export let data;
|
||||||
let shouldLoadWorker = writable(false);
|
let shouldLoadWorker = writable(false);
|
||||||
|
|
||||||
let optionsWatchlist = data?.getOptionsWatchlist;
|
|
||||||
|
|
||||||
let ruleOfList = data?.getPredefinedCookieRuleOfList || [];
|
let ruleOfList = data?.getPredefinedCookieRuleOfList || [];
|
||||||
|
|
||||||
let displayRules = [];
|
let displayRules = [];
|
||||||
@ -62,25 +59,7 @@
|
|||||||
defaultCondition: "over",
|
defaultCondition: "over",
|
||||||
defaultValue: "any",
|
defaultValue: "any",
|
||||||
},
|
},
|
||||||
open_interest: {
|
premium: {
|
||||||
label: "Open Interest",
|
|
||||||
step: ["100K", "10K", "1K"],
|
|
||||||
defaultCondition: "over",
|
|
||||||
defaultValue: "any",
|
|
||||||
},
|
|
||||||
volumeOIRatio: {
|
|
||||||
label: "Volume / Open Interest",
|
|
||||||
step: ["100%", "80%", "60%", "50%", "30%", "15%", "10%", "5%"],
|
|
||||||
defaultCondition: "over",
|
|
||||||
defaultValue: "any",
|
|
||||||
},
|
|
||||||
sizeOIRatio: {
|
|
||||||
label: "Size / Open Interest",
|
|
||||||
step: ["100%", "80%", "60%", "50%", "30%", "15%", "10%", "5%"],
|
|
||||||
defaultCondition: "over",
|
|
||||||
defaultValue: "any",
|
|
||||||
},
|
|
||||||
cost_basis: {
|
|
||||||
label: "Premium",
|
label: "Premium",
|
||||||
step: [
|
step: [
|
||||||
"10M",
|
"10M",
|
||||||
@ -98,43 +77,12 @@
|
|||||||
defaultCondition: "over",
|
defaultCondition: "over",
|
||||||
defaultValue: "any",
|
defaultValue: "any",
|
||||||
},
|
},
|
||||||
moneyness: {
|
|
||||||
label: "Moneyness",
|
|
||||||
step: ["ITM", "OTM"],
|
|
||||||
defaultValue: "any",
|
|
||||||
},
|
|
||||||
flowType: {
|
|
||||||
label: "Flow Type",
|
|
||||||
step: ["Repeated Flow"],
|
|
||||||
defaultValue: "any",
|
|
||||||
},
|
|
||||||
put_call: {
|
|
||||||
label: "Contract Type",
|
|
||||||
step: ["Calls", "Puts"],
|
|
||||||
defaultValue: "any",
|
|
||||||
},
|
|
||||||
sentiment: {
|
|
||||||
label: "Sentiment",
|
|
||||||
step: ["Bullish", "Neutral", "Bearish"],
|
|
||||||
defaultValue: "any",
|
|
||||||
},
|
|
||||||
execution_estimate: {
|
|
||||||
label: "Execution",
|
|
||||||
step: ["Above Ask", "Below Bid", "At Ask", "At Bid", "At Midpoint"],
|
|
||||||
defaultValue: "any",
|
|
||||||
},
|
|
||||||
option_activity_type: {
|
option_activity_type: {
|
||||||
label: "Option Type",
|
label: "Option Type",
|
||||||
step: ["Sweep", "Trade"],
|
step: ["Sweep", "Trade"],
|
||||||
defaultValue: "any",
|
defaultValue: "any",
|
||||||
},
|
},
|
||||||
date_expiration: {
|
assetType: {
|
||||||
label: "Date Expiration",
|
|
||||||
step: ["250", "180", "100", "80", "60", "50", "30", "20", "10", "5", "0"],
|
|
||||||
defaultCondition: "over",
|
|
||||||
defaultValue: "any",
|
|
||||||
},
|
|
||||||
underlying_type: {
|
|
||||||
label: "Asset Type",
|
label: "Asset Type",
|
||||||
step: ["Stock", "ETF"],
|
step: ["Stock", "ETF"],
|
||||||
defaultValue: "any",
|
defaultValue: "any",
|
||||||
@ -148,7 +96,7 @@
|
|||||||
"sentiment",
|
"sentiment",
|
||||||
"execution_estimate",
|
"execution_estimate",
|
||||||
"option_activity_type",
|
"option_activity_type",
|
||||||
"underlying_type",
|
"assetType",
|
||||||
];
|
];
|
||||||
|
|
||||||
// Generate allRows from allRules
|
// Generate allRows from allRules
|
||||||
@ -199,7 +147,7 @@
|
|||||||
ruleOfList?.some((rule) => rule.name === row.rule),
|
ruleOfList?.some((rule) => rule.name === row.rule),
|
||||||
);
|
);
|
||||||
shouldLoadWorker.set(true);
|
shouldLoadWorker.set(true);
|
||||||
await saveCookieRuleOfList();
|
//await saveCookieRuleOfList();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleResetAll() {
|
async function handleResetAll() {
|
||||||
@ -217,7 +165,7 @@
|
|||||||
ruleOfList.some((rule) => rule.name === row.rule),
|
ruleOfList.some((rule) => rule.name === row.rule),
|
||||||
);
|
);
|
||||||
displayedData = rawData;
|
displayedData = rawData;
|
||||||
await saveCookieRuleOfList();
|
//await saveCookieRuleOfList();
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeRule(state: string) {
|
function changeRule(state: string) {
|
||||||
@ -249,7 +197,7 @@
|
|||||||
case "underlying_type":
|
case "underlying_type":
|
||||||
newRule = {
|
newRule = {
|
||||||
name: ruleName,
|
name: ruleName,
|
||||||
value: Array.isArray(valueMappings[ruleName])
|
value: Array?.isArray(valueMappings[ruleName])
|
||||||
? valueMappings[ruleName]
|
? valueMappings[ruleName]
|
||||||
: [valueMappings[ruleName]],
|
: [valueMappings[ruleName]],
|
||||||
}; // Ensure value is an array
|
}; // Ensure value is an array
|
||||||
@ -310,7 +258,6 @@
|
|||||||
filteredData = event.data?.filteredData ?? [];
|
filteredData = event.data?.filteredData ?? [];
|
||||||
displayedData = filteredData;
|
displayedData = filteredData;
|
||||||
console.log("handle Message");
|
console.log("handle Message");
|
||||||
calculateStats(displayedData);
|
|
||||||
|
|
||||||
//console.log(displayedData)
|
//console.log(displayedData)
|
||||||
};
|
};
|
||||||
@ -435,7 +382,7 @@
|
|||||||
|
|
||||||
// Trigger worker load and save cookie
|
// Trigger worker load and save cookie
|
||||||
shouldLoadWorker.set(true);
|
shouldLoadWorker.set(true);
|
||||||
await saveCookieRuleOfList();
|
//await saveCookieRuleOfList();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stepSizeValue(value, condition) {
|
async function stepSizeValue(value, condition) {
|
||||||
@ -467,12 +414,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentTime = new Date(
|
|
||||||
new Date().toLocaleString("en-US", { timeZone: "America/New_York" }),
|
|
||||||
)?.getTime();
|
|
||||||
|
|
||||||
const nyseDate = new Date(
|
const nyseDate = new Date(
|
||||||
data?.getOptionsFlowFeed?.at(0)?.date ?? null,
|
data?.getFlowData?.at(0)?.date ?? null,
|
||||||
)?.toLocaleString("en-US", {
|
)?.toLocaleString("en-US", {
|
||||||
month: "short",
|
month: "short",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
@ -480,7 +423,7 @@
|
|||||||
timeZone: "Europe/Berlin",
|
timeZone: "Europe/Berlin",
|
||||||
});
|
});
|
||||||
|
|
||||||
let rawData = data?.getOptionsFlowFeed?.filter((item) =>
|
let rawData = data?.getFlowData?.filter((item) =>
|
||||||
Object?.values(item)?.every(
|
Object?.values(item)?.every(
|
||||||
(value) =>
|
(value) =>
|
||||||
value !== null &&
|
value !== null &&
|
||||||
@ -491,9 +434,6 @@
|
|||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
rawData?.forEach((item) => {
|
|
||||||
item.dte = daysLeft(item?.date_expiration);
|
|
||||||
});
|
|
||||||
|
|
||||||
let displayedData = [];
|
let displayedData = [];
|
||||||
|
|
||||||
@ -517,7 +457,7 @@
|
|||||||
mode = !mode;
|
mode = !mode;
|
||||||
if (mode === true && selectedDate !== undefined) {
|
if (mode === true && selectedDate !== undefined) {
|
||||||
selectedDate = undefined;
|
selectedDate = undefined;
|
||||||
rawData = data?.getOptionsFlowFeed;
|
rawData = data?.getFlowData;
|
||||||
displayedData = [...rawData];
|
displayedData = [...rawData];
|
||||||
shouldLoadWorker.set(true);
|
shouldLoadWorker.set(true);
|
||||||
}
|
}
|
||||||
@ -527,17 +467,12 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
async function websocketRealtimeData() {
|
async function websocketRealtimeData() {
|
||||||
newData = [];
|
newData = [];
|
||||||
try {
|
try {
|
||||||
socket = new WebSocket(data?.wsURL + "/options-flow-reader");
|
socket = new WebSocket(data?.wsURL + "/options-flow-reader");
|
||||||
/*
|
|
||||||
socket.addEventListener("open", () => {
|
|
||||||
const ids = rawData.map(item => item.id);
|
|
||||||
sendMessage(JSON.stringify({ ids }));
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
socket.addEventListener("message", (event) => {
|
socket.addEventListener("message", (event) => {
|
||||||
const totalVolume = displayCallVolume + displayPutVolume;
|
const totalVolume = displayCallVolume + displayPutVolume;
|
||||||
@ -567,11 +502,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
if (previousCallVolume !== displayCallVolume && !muted && audio) {
|
|
||||||
audio?.play();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error processing WebSocket message:", e);
|
console.error("Error processing WebSocket message:", e);
|
||||||
}
|
}
|
||||||
@ -603,16 +534,7 @@
|
|||||||
setTimeout(() => websocketRealtimeData(), 400);
|
setTimeout(() => websocketRealtimeData(), 400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
function daysLeft(targetDate) {
|
|
||||||
const targetTime = new Date(targetDate).getTime();
|
|
||||||
const difference = targetTime - currentTime;
|
|
||||||
|
|
||||||
const millisecondsPerDay = 1000 * 60 * 60 * 24;
|
|
||||||
const daysLeft = Math?.ceil(difference / millisecondsPerDay);
|
|
||||||
|
|
||||||
return daysLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveCookieRuleOfList() {
|
async function saveCookieRuleOfList() {
|
||||||
const postData = {
|
const postData = {
|
||||||
@ -628,9 +550,11 @@
|
|||||||
}); // make a POST request to the server with the FormData object
|
}); // make a POST request to the server with the FormData object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
$: if ($isOpen) {
|
$: if ($isOpen) {
|
||||||
websocketRealtimeData();
|
websocketRealtimeData();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (filterQuery?.length > 0) {
|
if (filterQuery?.length > 0) {
|
||||||
@ -647,7 +571,6 @@
|
|||||||
|
|
||||||
audio = new Audio(notifySound);
|
audio = new Audio(notifySound);
|
||||||
displayedData = rawData;
|
displayedData = rawData;
|
||||||
calculateStats(rawData);
|
|
||||||
|
|
||||||
if (!syncWorker) {
|
if (!syncWorker) {
|
||||||
const SyncWorker = await import("./workers/filterWorker?worker");
|
const SyncWorker = await import("./workers/filterWorker?worker");
|
||||||
@ -678,65 +601,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function calculateStats(data) {
|
/*
|
||||||
const {
|
|
||||||
callVolumeSum,
|
|
||||||
putVolumeSum,
|
|
||||||
bullishCount,
|
|
||||||
bearishCount,
|
|
||||||
neutralCount,
|
|
||||||
} = data?.reduce(
|
|
||||||
(acc, item) => {
|
|
||||||
const volume = parseInt(item?.volume);
|
|
||||||
|
|
||||||
if (item?.put_call === "Calls") {
|
|
||||||
acc.callVolumeSum += volume;
|
|
||||||
} else if (item?.put_call === "Puts") {
|
|
||||||
acc.putVolumeSum += volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item?.sentiment === "Bullish") {
|
|
||||||
acc.bullishCount += 1;
|
|
||||||
} else if (item?.sentiment === "Bearish") {
|
|
||||||
acc.bearishCount += 1;
|
|
||||||
} else if (item?.sentiment === "Neutral") {
|
|
||||||
acc.neutralCount += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
callVolumeSum: 0,
|
|
||||||
putVolumeSum: 0,
|
|
||||||
bullishCount: 0,
|
|
||||||
bearishCount: 0,
|
|
||||||
neutralCount: 0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (bullishCount > bearishCount) {
|
|
||||||
flowSentiment = "Bullish";
|
|
||||||
} else if (bullishCount < bearishCount) {
|
|
||||||
flowSentiment = "Bearish";
|
|
||||||
} else if (neutralCount > bearishCount && neutralCount > bullishCount) {
|
|
||||||
flowSentiment = "Neutral";
|
|
||||||
} else {
|
|
||||||
flowSentiment = "-";
|
|
||||||
}
|
|
||||||
|
|
||||||
putCallRatio = callVolumeSum !== 0 ? putVolumeSum / callVolumeSum : 0;
|
|
||||||
|
|
||||||
callPercentage =
|
|
||||||
callVolumeSum + putVolumeSum !== 0
|
|
||||||
? Math.floor((callVolumeSum / (callVolumeSum + putVolumeSum)) * 100)
|
|
||||||
: 0;
|
|
||||||
putPercentage =
|
|
||||||
callVolumeSum + putVolumeSum !== 0 ? 100 - callPercentage : 0;
|
|
||||||
|
|
||||||
displayCallVolume = callVolumeSum;
|
|
||||||
displayPutVolume = putVolumeSum;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getHistoricalFlow = async () => {
|
const getHistoricalFlow = async () => {
|
||||||
// Create a delay using setTimeout wrapped in a Promise
|
// Create a delay using setTimeout wrapped in a Promise
|
||||||
if (data?.user?.tier === "Pro") {
|
if (data?.user?.tier === "Pro") {
|
||||||
@ -747,7 +612,6 @@
|
|||||||
ruleOfList.some((rule) => rule.name === row.rule),
|
ruleOfList.some((rule) => rule.name === row.rule),
|
||||||
);
|
);
|
||||||
displayedData = [];
|
displayedData = [];
|
||||||
calculateStats(displayedData);
|
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||||
|
|
||||||
@ -769,12 +633,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
rawData = await response?.json();
|
rawData = await response?.json();
|
||||||
if (rawData?.length !== 0) {
|
|
||||||
rawData?.forEach((item) => {
|
|
||||||
item.dte = daysLeft(item?.date_expiration);
|
|
||||||
});
|
|
||||||
shouldLoadWorker.set(true);
|
shouldLoadWorker.set(true);
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching historical flow:", error);
|
console.error("Error fetching historical flow:", error);
|
||||||
rawData = [];
|
rawData = [];
|
||||||
@ -787,6 +646,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
function handleInput(event) {
|
function handleInput(event) {
|
||||||
filterQuery = event.target.value;
|
filterQuery = event.target.value;
|
||||||
@ -863,7 +723,7 @@
|
|||||||
|
|
||||||
<body class="overflow-y-auto">
|
<body class="overflow-y-auto">
|
||||||
<section
|
<section
|
||||||
class="w-full max-w-screen sm:max-w-7xl xl:max-w-screen-2xl flex justify-center items-center bg-[#09090B] pb-20"
|
class="w-full max-w-screen sm:max-w-7xl xl:max-w-screen-2xl flex justify-center items-center bg-[#09090B] pb-20 mt-5 sm:mt-0 px-3 sm:px-0"
|
||||||
>
|
>
|
||||||
<div class="w-full m-auto min-h-screen">
|
<div class="w-full m-auto min-h-screen">
|
||||||
<!--
|
<!--
|
||||||
@ -884,8 +744,7 @@
|
|||||||
: nyseDate} (NYSE Time)
|
: nyseDate} (NYSE Time)
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="rounded-md border border-gray-700 bg-[#121217] p-2">
|
||||||
<div class="rounded-md border border-gray-700 bg-[#1E222D] p-2">
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-col sm:flex-row items-center pt-3 sm:pt-1 pb-3 sm:border-b sm:border-gray-600"
|
class="flex flex-col sm:flex-row items-center pt-3 sm:pt-1 pb-3 sm:border-b sm:border-gray-600"
|
||||||
>
|
>
|
||||||
@ -936,11 +795,14 @@
|
|||||||
class="inline-flex items-center cursor-pointer focus-none focus:outline-none"
|
class="inline-flex items-center cursor-pointer focus-none focus:outline-none"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
on:click={toggleMode}
|
on:click={() =>
|
||||||
|
toast("Feature is coming soon 🔥", {
|
||||||
|
style:
|
||||||
|
"border-radius: 200px; background: #272B37; color: #fff; border: 1px solid #4B5563; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);",
|
||||||
|
})}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={mode}
|
checked={mode}
|
||||||
value={mode}
|
value={mode}
|
||||||
disabled={!$isOpen}
|
|
||||||
class="sr-only peer"
|
class="sr-only peer"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -963,14 +825,14 @@
|
|||||||
<div class="sm:ml-auto w-full sm:w-fit pt-5">
|
<div class="sm:ml-auto w-full sm:w-fit pt-5">
|
||||||
<div class="relative flex flex-col sm:flex-row items-center">
|
<div class="relative flex flex-col sm:flex-row items-center">
|
||||||
<div
|
<div
|
||||||
class="relative w-full sm:w-fit pl-3 sm:mr-5 mb-4 sm:mb-0 flex-auto text-center bg-[#2A2E39] rounded-md border border-gray-600"
|
class="relative w-full sm:w-fit pl-3 sm:mr-5 mb-4 sm:mb-0 flex-auto text-center bg-[#19191F] rounded-md border border-gray-600"
|
||||||
>
|
>
|
||||||
<label class="flex flex-row items-center">
|
<label class="flex flex-row items-center">
|
||||||
<input
|
<input
|
||||||
id="modal-search"
|
id="modal-search"
|
||||||
type="search"
|
type="search"
|
||||||
class="text-white sm:ml-2 text-[1rem] placeholder-gray-300 border-transparent focus:border-transparent focus:ring-0 flex items-center justify-center w-full px-0 py-1.5 bg-inherit"
|
class="text-white sm:ml-2 text-[1rem] placeholder-gray-300 border-transparent focus:border-transparent focus:ring-0 flex items-center justify-center w-full px-0 py-1.5 bg-inherit"
|
||||||
placeholder="Search AAPL, SPY,..."
|
placeholder="Stock or ETF symbol..."
|
||||||
bind:value={filterQuery}
|
bind:value={filterQuery}
|
||||||
on:input={debouncedHandleInput}
|
on:input={debouncedHandleInput}
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@ -997,6 +859,11 @@
|
|||||||
<Popover.Root>
|
<Popover.Root>
|
||||||
<Popover.Trigger asChild let:builder>
|
<Popover.Trigger asChild let:builder>
|
||||||
<Button
|
<Button
|
||||||
|
on:click={() =>
|
||||||
|
toast("Feature is coming soon 🔥", {
|
||||||
|
style:
|
||||||
|
"border-radius: 200px; background: #272B37; color: #fff; border: 1px solid #4B5563; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);",
|
||||||
|
})}
|
||||||
class={cn(
|
class={cn(
|
||||||
"w-full sm:w-[160px] truncate sm:mr-3 py-3 bg-[#000] sm:hover:bg-[#000] sm:hover:text-white text-white justify-center sm:justify-start text-center sm:text-left font-normal border-none rounded-md",
|
"w-full sm:w-[160px] truncate sm:mr-3 py-3 bg-[#000] sm:hover:bg-[#000] sm:hover:text-white text-white justify-center sm:justify-start text-center sm:text-left font-normal border-none rounded-md",
|
||||||
!selectedDate && "text-gray-300",
|
!selectedDate && "text-gray-300",
|
||||||
@ -1004,19 +871,9 @@
|
|||||||
builders={[builder]}
|
builders={[builder]}
|
||||||
>
|
>
|
||||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
{selectedDate
|
Pick a Date
|
||||||
? df.format(selectedDate?.toDate())
|
|
||||||
: "Pick a date"}
|
|
||||||
</Button>
|
</Button>
|
||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
<Popover.Content class="w-auto p-0 border-gray-500">
|
|
||||||
<Calendar
|
|
||||||
class="bg-[#09090B] text-white"
|
|
||||||
bind:value={selectedDate}
|
|
||||||
initialFocus
|
|
||||||
onValueChange={getHistoricalFlow}
|
|
||||||
/>
|
|
||||||
</Popover.Content>
|
|
||||||
</Popover.Root>
|
</Popover.Root>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1442,212 +1299,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if isLoaded}
|
{#if isLoaded}
|
||||||
<div class="w-full mt-5 m-auto flex justify-center items-center">
|
|
||||||
<div class="w-full grid grid-cols-1 lg:grid-cols-4 gap-y-3 gap-x-3">
|
|
||||||
<!--Start Flow Sentiment-->
|
|
||||||
<div
|
|
||||||
class="flex flex-row items-center flex-wrap w-full px-5 bg-[#1E222D] shadow-lg rounded-md h-20"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-start">
|
|
||||||
<span class="font-semibold text-gray-200 text-sm sm:text-[1rem]"
|
|
||||||
>Flow Sentiment</span
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="text-start text-[1rem] font-semibold {flowSentiment ===
|
|
||||||
'Bullish'
|
|
||||||
? 'text-[#00FC50]'
|
|
||||||
: flowSentiment === 'Bearish'
|
|
||||||
? 'text-[#FF2F1F]'
|
|
||||||
: flowSentiment === 'Neutral'
|
|
||||||
? 'text-[#fff]'
|
|
||||||
: 'text-white'}">{flowSentiment}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--End Flow Sentiment-->
|
|
||||||
<!--Start Put/Call-->
|
|
||||||
<div
|
|
||||||
class="flex flex-row items-center flex-wrap w-full px-5 bg-[#1E222D] shadow-lg rounded-md h-20"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-start">
|
|
||||||
<span class="font-semibold text-gray-200 text-sm sm:text-[1rem]"
|
|
||||||
>Put/Call</span
|
|
||||||
>
|
|
||||||
<span class="text-start text-[1rem] font-semibold text-white">
|
|
||||||
{putCallRatio?.toFixed(3)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- Circular Progress -->
|
|
||||||
<div class="relative size-14 ml-auto">
|
|
||||||
<svg
|
|
||||||
class="size-full w-14 h-14"
|
|
||||||
viewBox="0 0 36 36"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<!-- Background Circle -->
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current text-[#3E3E3E]"
|
|
||||||
stroke-width="3"
|
|
||||||
></circle>
|
|
||||||
<!-- Progress Circle inside a group with rotation -->
|
|
||||||
<g class="origin-center -rotate-90 transform">
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current text-blue-500"
|
|
||||||
stroke-width="3"
|
|
||||||
stroke-dasharray="100"
|
|
||||||
stroke-dashoffset={putCallRatio >= 1
|
|
||||||
? 0
|
|
||||||
: 100 - (putCallRatio * 100)?.toFixed(2)}
|
|
||||||
></circle>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<!-- Percentage Text -->
|
|
||||||
<div
|
|
||||||
class="absolute top-1/2 start-1/2 transform -translate-y-1/2 -translate-x-1/2"
|
|
||||||
>
|
|
||||||
<span class="text-center text-white text-sm"
|
|
||||||
>{putCallRatio?.toFixed(2)}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- End Circular Progress -->
|
|
||||||
</div>
|
|
||||||
<!--End Put/Call-->
|
|
||||||
<!--Start Call Flow-->
|
|
||||||
<div
|
|
||||||
class="flex flex-row items-center flex-wrap w-full px-5 bg-[#1E222D] shadow-lg rounded-md h-20"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-start">
|
|
||||||
<span class="font-semibold text-gray-200 text-sm sm:text-[1rem]"
|
|
||||||
>Call Flow</span
|
|
||||||
>
|
|
||||||
<span class="text-start text-[1rem] font-semibold text-white">
|
|
||||||
{new Intl.NumberFormat("en", {
|
|
||||||
minimumFractionDigits: 0,
|
|
||||||
maximumFractionDigits: 0,
|
|
||||||
}).format(displayCallVolume)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- Circular Progress -->
|
|
||||||
<div class="relative size-14 ml-auto">
|
|
||||||
<svg
|
|
||||||
class="size-full w-14 h-14"
|
|
||||||
viewBox="0 0 36 36"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<!-- Background Circle -->
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current text-[#3E3E3E]"
|
|
||||||
stroke-width="3"
|
|
||||||
></circle>
|
|
||||||
<!-- Progress Circle inside a group with rotation -->
|
|
||||||
<g class="origin-center -rotate-90 transform">
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current text-[#00FC50]"
|
|
||||||
stroke-width="3"
|
|
||||||
stroke-dasharray="100"
|
|
||||||
stroke-dashoffset={100 - callPercentage?.toFixed(2)}
|
|
||||||
></circle>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<!-- Percentage Text -->
|
|
||||||
<div
|
|
||||||
class="absolute top-1/2 start-1/2 transform -translate-y-1/2 -translate-x-1/2"
|
|
||||||
>
|
|
||||||
<span class="text-center text-white text-sm"
|
|
||||||
>{callPercentage}%</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- End Circular Progress -->
|
|
||||||
</div>
|
|
||||||
<!--End Call Flow-->
|
|
||||||
<!--Start Put Flow-->
|
|
||||||
<div
|
|
||||||
class="flex flex-row items-center flex-wrap w-full px-5 bg-[#1E222D] shadow-lg rounded-md h-20"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-start">
|
|
||||||
<span class="font-semibold text-gray-200 text-sm sm:text-[1rem]"
|
|
||||||
>Put Flow</span
|
|
||||||
>
|
|
||||||
<span class="text-start text-[1rem] font-semibold text-white">
|
|
||||||
{new Intl.NumberFormat("en", {
|
|
||||||
minimumFractionDigits: 0,
|
|
||||||
maximumFractionDigits: 0,
|
|
||||||
}).format(displayPutVolume)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- Circular Progress -->
|
|
||||||
<div class="relative size-14 ml-auto">
|
|
||||||
<svg
|
|
||||||
class="size-full w-14 h-14"
|
|
||||||
viewBox="0 0 36 36"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<!-- Background Circle -->
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current text-[#3E3E3E]"
|
|
||||||
stroke-width="3"
|
|
||||||
></circle>
|
|
||||||
<!-- Progress Circle inside a group with rotation -->
|
|
||||||
<g class="origin-center -rotate-90 transform">
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current text-[#EE5365]"
|
|
||||||
stroke-width="3"
|
|
||||||
stroke-dasharray="100"
|
|
||||||
stroke-dashoffset={100 - putPercentage?.toFixed(2)}
|
|
||||||
></circle>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<!-- Percentage Text -->
|
|
||||||
<div
|
|
||||||
class="absolute top-1/2 start-1/2 transform -translate-y-1/2 -translate-x-1/2"
|
|
||||||
>
|
|
||||||
<span class="text-center text-white text-sm"
|
|
||||||
>{putPercentage}%</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- End Circular Progress -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Page wrapper -->
|
<!-- Page wrapper -->
|
||||||
<div class="flex w-full m-auto h-full overflow-hidden">
|
<div class="flex w-full m-auto h-full overflow-hidden">
|
||||||
{#if displayedData?.length !== 0}
|
{#if displayedData?.length !== 0}
|
||||||
<div class="mt-8 w-full overflow-x-auto h-[850px] overflow-hidden">
|
<div class="mt-3 w-full overflow-x-auto h-[850px] overflow-hidden">
|
||||||
<DarkPoolTable
|
<DarkPoolTable {data} {displayedData} {filteredData} {rawData} />
|
||||||
{data}
|
|
||||||
{optionsWatchlist}
|
|
||||||
{displayedData}
|
|
||||||
{filteredData}
|
|
||||||
{rawData}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
<div
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user