add advanced filter to options flow
This commit is contained in:
parent
9e8197e1e3
commit
949f5e0bc1
@ -25,8 +25,9 @@ const allRules = {
|
||||
cost_basis: { label: 'Premium', step: ['10M','5M','1M','500K','100K','50K','10K','5K'], defaultCondition: 'over', defaultValue: '50K' },
|
||||
put_call: { label: 'Contract Type', step: ["Calls", "Puts"], defaultValue: 'any' },
|
||||
sentiment: { label: 'Sentiment', step: ["Bullish","Neutral", "Bearish"], defaultValue: 'any' },
|
||||
execution_estimate: { label: 'Execution', step: ["At Ask","At Bid"], defaultValue: 'any' },
|
||||
execution_estimate: { label: 'Execution', step: ["At Ask","At Bid", "Below Ask", "Below Bid"], defaultValue: 'any' },
|
||||
option_activity_type: { label: 'Option Type', step: ["Sweep","Trade"], defaultValue: 'any' },
|
||||
date_expiration: { label: 'Date Expiration', step: ["1 day","1 Week","2 Weeks","1 Month","3 Months","6 Months","1 Year","3 Years"], defaultValue: 'any' },
|
||||
|
||||
};
|
||||
|
||||
@ -80,8 +81,13 @@ async function handleResetAll() {
|
||||
ruleOfList = [];
|
||||
ruleOfList = [...ruleOfList];
|
||||
ruleName = '';
|
||||
checkedItems = new Set();
|
||||
Object.keys(allRules).forEach(ruleName => {
|
||||
ruleCondition[ruleName] = allRules[ruleName].defaultCondition;
|
||||
valueMappings[ruleName] = allRules[ruleName].defaultValue;
|
||||
});
|
||||
displayRules = allRows?.filter(row => ruleOfList.some(rule => rule.name === row.rule));
|
||||
|
||||
displayedData = rawData;
|
||||
}
|
||||
|
||||
function changeRule(state: string)
|
||||
@ -107,8 +113,9 @@ function handleAddRule() {
|
||||
switch (ruleName) {
|
||||
case "put_call":
|
||||
case "sentiment":
|
||||
case "execution_estimate","option_activity_type":
|
||||
case "execution_estimate":
|
||||
case "option_activity_type":
|
||||
case "date_expiration":
|
||||
newRule = { name: ruleName, value: Array.isArray(valueMappings[ruleName]) ? valueMappings[ruleName] : [valueMappings[ruleName]] }; // Ensure value is an array
|
||||
break;
|
||||
default:
|
||||
@ -152,12 +159,13 @@ async function handleRule(newRule) {
|
||||
|
||||
const loadWorker = async () => {
|
||||
syncWorker.postMessage({ rawData, ruleOfList });
|
||||
console.log(ruleOfList)
|
||||
};
|
||||
|
||||
const handleMessage = (event) => {
|
||||
displayRules = allRows?.filter(row => ruleOfList.some(rule => rule.name === row.rule));
|
||||
displayedData = event.data?.filteredData ?? [];
|
||||
calculateStats(displayedData);
|
||||
|
||||
//console.log(displayedData)
|
||||
};
|
||||
|
||||
@ -181,7 +189,7 @@ async function handleChangeValue(value) {
|
||||
} else {
|
||||
checkedItems.add(value);
|
||||
}
|
||||
if (["put_call","sentiment","execution_estimate","option_activity_type"]?.includes(ruleName)) {
|
||||
if (["put_call","sentiment","execution_estimate","option_activity_type","date_expiration"]?.includes(ruleName)) {
|
||||
// Ensure valueMappings[ruleName] is initialized as an array
|
||||
if (!Array.isArray(valueMappings[ruleName])) {
|
||||
valueMappings[ruleName] = []; // Initialize as an empty array if not already
|
||||
@ -292,36 +300,9 @@ const nyseDate = new Date(data?.getOptionsFlowFeed?.at(0)?.date ?? null)?.toLoca
|
||||
let optionOpenInterest;
|
||||
let optionSentiment;
|
||||
let optionPrice;
|
||||
let optionTradeCount;
|
||||
let optionexecution_estimate;
|
||||
let optionExchange;
|
||||
|
||||
/*
|
||||
async function infiniteHandler({ detail: { loaded, complete, error } }) {
|
||||
|
||||
try {
|
||||
const lastId = rawData?.at(-1)?.id;
|
||||
const postData = {'lastId': lastId};
|
||||
const response = await fetch(data?.apiURL + '/options-flow-feed', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json", "X-API-KEY": data?.apiKey
|
||||
},
|
||||
body: JSON.stringify(postData),
|
||||
});
|
||||
const newData = await response.json();
|
||||
if(newData?.length === 0) {
|
||||
complete();
|
||||
}
|
||||
else {
|
||||
rawData = [...rawData,...newData];
|
||||
loaded();
|
||||
}
|
||||
} catch (e) {
|
||||
error();
|
||||
}
|
||||
}
|
||||
*/
|
||||
function toggleMode()
|
||||
{
|
||||
if ($isOpen) {
|
||||
@ -366,9 +347,9 @@ function handleViewData(optionData) {
|
||||
optionOpenInterest = new Intl.NumberFormat("en", {minimumFractionDigits: 0, maximumFractionDigits: 0}).format(optionData?.open_interest);
|
||||
optionSentiment = optionData?.sentiment;
|
||||
optionPrice = optionData?.price;
|
||||
optionTradeCount = optionData?.tradeCount;
|
||||
//optionTradeCount = optionData?.tradeCount;
|
||||
optionexecution_estimate = optionData?.execution_estimate;
|
||||
optionExchange = optionData?.exchange;
|
||||
//optionExchange = optionData?.exchange;
|
||||
|
||||
const openPopup = $screenWidth < 640 ? document.getElementById("optionDetailsMobileModal") : document.getElementById("optionDetailsDesktopModal");
|
||||
openPopup?.dispatchEvent(new MouseEvent('click'))
|
||||
@ -639,6 +620,7 @@ function calculateStats(data) {
|
||||
// Update a separate variable for displayed data, not rawData itself
|
||||
displayedData = filteredData;
|
||||
|
||||
|
||||
calculateStats(displayedData);
|
||||
}, 200);
|
||||
}
|
||||
@ -657,40 +639,6 @@ const debouncedHandleInput = debounce(handleInput, 300);
|
||||
|
||||
|
||||
|
||||
// Function to filter elements with date_expiration within a given number of days
|
||||
const filterExpiringSoon = (data, days) => {
|
||||
const currentDate = new Date(); // Get today's date
|
||||
return data.filter(item => {
|
||||
const expirationDate = new Date(item?.date_expiration);
|
||||
const timeDiff = expirationDate - currentDate; // Time difference in milliseconds
|
||||
const daysDiff = timeDiff / (1000 * 60 * 60 * 24); // Convert to days
|
||||
return daysDiff <= days && daysDiff >= 0; // Ensure it's within the specified number of days and not in the past
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$: {
|
||||
if(filterList && typeof window !== 'undefined' && mode === false)
|
||||
{
|
||||
if(filterList?.length !== 0)
|
||||
{
|
||||
const newData = filterExpiringSoon(rawData, Math.max(...filterList));
|
||||
if (newData?.length !== 0) {
|
||||
rawData = newData;
|
||||
notFound = false;
|
||||
} else {
|
||||
notFound = true;
|
||||
rawData = data?.getOptionsFlowFeed;
|
||||
}
|
||||
|
||||
}
|
||||
else if (filterQuery?.length === 0) {
|
||||
rawData = data?.getOptionsFlowFeed;
|
||||
|
||||
}
|
||||
calculateStats(rawData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -858,7 +806,7 @@ $: {
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content class="w-56 h-fit max-h-72 overflow-y-auto scroller">
|
||||
{#if !['put_call',"sentiment", "execution_estimate","option_activity_type"]?.includes(row?.rule)}
|
||||
{#if !['put_call',"sentiment", "execution_estimate","option_activity_type","date_expiration"]?.includes(row?.rule)}
|
||||
<DropdownMenu.Label class="absolute mt-2 h-11 border-gray-800 border-b -top-1 z-20 fixed sticky bg-[#09090B]">
|
||||
<div class="flex items-center justify-start gap-x-1">
|
||||
<div class="relative inline-block flex flex-row items-center justify-center">
|
||||
@ -882,7 +830,7 @@ $: {
|
||||
</div>
|
||||
{/if}
|
||||
<DropdownMenu.Group class="min-h-10 mt-2">
|
||||
{#if !['put_call',"sentiment", "execution_estimate","option_activity_type"]?.includes(row?.rule)}
|
||||
{#if !['put_call',"sentiment", "execution_estimate","option_activity_type","date_expiration"]?.includes(row?.rule)}
|
||||
{#each row?.step as newValue}
|
||||
<DropdownMenu.Item class="sm:hover:bg-[#27272A]">
|
||||
|
||||
@ -891,7 +839,7 @@ $: {
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
{/each}
|
||||
{:else if ['put_call',"sentiment", "execution_estimate","option_activity_type"]?.includes(row?.rule)}
|
||||
{:else if ['put_call',"sentiment", "execution_estimate","option_activity_type","date_expiration"]?.includes(row?.rule)}
|
||||
{#each row?.step as item}
|
||||
<DropdownMenu.Item class="sm:hover:bg-[#27272A]">
|
||||
<div class="flex items-center" on:click|capture={(event) => event.preventDefault()}>
|
||||
@ -1254,18 +1202,7 @@ $: {
|
||||
Add Filters
|
||||
</div>
|
||||
|
||||
<!--Start Search bar-->
|
||||
<form class="w-11/12 h-8 mb-8" on:keydown={(e) => e?.key === 'Enter' ? e.preventDefault() : '' }>
|
||||
<label for="search" class="mb-2 text-sm font-medium text-gray-200 sr-only">Search</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
|
||||
<svg class="w-4 h-4 text-gray-200 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- End Search bar-->
|
||||
|
||||
|
||||
<div class="text-white text-sm bg-[#262626] bg-opacity-[0.4] overflow-y-scroll scroller pt-3 rounded-lg max-h-[500px] sm:max-h-[420px] md:max-h-[540px] lg:max-h-[600px]">
|
||||
|
||||
@ -1306,35 +1243,18 @@ $: {
|
||||
<div class="drawer-side overflow-x-hidden">
|
||||
|
||||
|
||||
<div class="menu w-screen min-h-full bg-[#000] text-base-content overflow-hidden">
|
||||
<div style="top: 0rem;" class="flex flex-row fixed sticky h-14 z-40 bg-[#000] justify-center items-center w-full border-b border-slate-900">
|
||||
<div class="menu w-screen min-h-full bg-[#141417] text-base-content overflow-hidden">
|
||||
<div style="top: 0rem;" class="flex flex-row fixed sticky h-14 z-40 justify-center items-center w-full border-b border-slate-900">
|
||||
<label for="ruleModal" class="cursor-pointer mr-auto ml-3">
|
||||
<svg class="w-6 h-6 inline-block " xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#fff" d="M9.125 21.1L.7 12.7q-.15-.15-.213-.325T.425 12q0-.2.063-.375T.7 11.3l8.425-8.425q.35-.35.875-.35t.9.375q.375.375.375.875t-.375.875L3.55 12l7.35 7.35q.35.35.35.863t-.375.887q-.375.375-.875.375t-.875-.375Z"/></svg>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center justify-center w-full m-auto mb-8 mt-5">
|
||||
|
||||
<!--Start Search bar-->
|
||||
<form class="w-11/12 h-8" on:keydown={(e) => e?.key === 'Enter' ? e.preventDefault() : '' }>
|
||||
<label for="search" class="mb-2 text-sm font-medium text-gray-200 sr-only">Search</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
|
||||
<svg class="w-4 h-4 text-gray-200 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- End Search bar-->
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<table class="table table-sm table-compact overflow-y-scroll text-white mb-10">
|
||||
<table class="table table-sm table-compact overflow-y-scroll text-white mb-10 mt-10">
|
||||
<!-- head -->
|
||||
<tbody>
|
||||
{#each allRows as row, index}
|
||||
|
||||
@ -19,8 +19,20 @@ function convertUnitToValue(
|
||||
"bearish",
|
||||
"at bid",
|
||||
"at ask",
|
||||
"below bid",
|
||||
"below ask",
|
||||
"sweep",
|
||||
"trade",
|
||||
...[
|
||||
"1 day",
|
||||
"1 Week",
|
||||
"2 Weeks",
|
||||
"1 Month",
|
||||
"3 Months",
|
||||
"6 Months",
|
||||
"1 Year",
|
||||
"3 Years",
|
||||
],
|
||||
]);
|
||||
if (nonNumericValues.has(lowerInput)) return input;
|
||||
if (input.endsWith("%")) {
|
||||
@ -51,6 +63,34 @@ function isAny(value: string | string[]): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isDateWithinRange(dateString: string, range: string): boolean {
|
||||
const now = new Date();
|
||||
const expirationDate = new Date(dateString);
|
||||
const timeDiff = expirationDate.getTime() - now.getTime();
|
||||
const daysDiff = timeDiff / (1000 * 60 * 60 * 24);
|
||||
|
||||
switch (range.toLowerCase()) {
|
||||
case "1 day":
|
||||
return daysDiff >= 0 && daysDiff <= 1;
|
||||
case "1 week":
|
||||
return daysDiff > 1 && daysDiff <= 7;
|
||||
case "2 weeks":
|
||||
return daysDiff > 7 && daysDiff <= 14;
|
||||
case "1 month":
|
||||
return daysDiff > 14 && daysDiff <= 30;
|
||||
case "3 months":
|
||||
return daysDiff > 30 && daysDiff <= 90;
|
||||
case "6 months":
|
||||
return daysDiff > 90 && daysDiff <= 180;
|
||||
case "1 year":
|
||||
return daysDiff > 180 && daysDiff <= 365;
|
||||
case "3 years":
|
||||
return daysDiff > 365 && daysDiff <= 1095;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function filterRawData(rawData, ruleOfList) {
|
||||
return rawData?.filter((item) => {
|
||||
return ruleOfList.every((rule) => {
|
||||
@ -58,6 +98,15 @@ async function filterRawData(rawData, ruleOfList) {
|
||||
const ruleValue = convertUnitToValue(rule.value);
|
||||
const ruleName = rule.name.toLowerCase();
|
||||
|
||||
// Handle date_expiration
|
||||
if (ruleName === "date_expiration") {
|
||||
if (isAny(ruleValue)) return true;
|
||||
if (Array.isArray(ruleValue)) {
|
||||
return ruleValue.some((range) => isDateWithinRange(itemValue, range));
|
||||
}
|
||||
return isDateWithinRange(itemValue, ruleValue as string);
|
||||
}
|
||||
|
||||
// Handle categorical data like analyst ratings, sector, country
|
||||
if (
|
||||
[
|
||||
@ -67,14 +116,14 @@ async function filterRawData(rawData, ruleOfList) {
|
||||
"option_activity_type",
|
||||
].includes(ruleName)
|
||||
) {
|
||||
if (isAny(ruleValue)) return true; // Return true for "any" or ["any"]
|
||||
if (isAny(ruleValue)) return true;
|
||||
if (Array.isArray(ruleValue)) return ruleValue.includes(itemValue);
|
||||
return itemValue === ruleValue;
|
||||
}
|
||||
|
||||
// Default numeric or string comparison
|
||||
if (typeof ruleValue === "string") return true; // Skip non-numeric comparisons
|
||||
if (itemValue === null) return false; // Null values do not meet any condition
|
||||
if (typeof ruleValue === "string") return true;
|
||||
if (itemValue === null) return false;
|
||||
if (rule.condition === "over" && itemValue <= ruleValue) return false;
|
||||
if (rule.condition === "under" && itemValue > ruleValue) return false;
|
||||
return true;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user