bugfixing

This commit is contained in:
MuslemRahimi 2024-12-09 20:30:01 +01:00
parent b900b009c8
commit 955e6d942b
2 changed files with 126 additions and 206 deletions

View File

@ -95,7 +95,7 @@
}, },
execution_estimate: { execution_estimate: {
label: "Execution", label: "Execution",
step: ["At Ask", "At Bid", "At Midpoint", "Above Ask", "Below Bid"], step: ["Above Ask", "Below Bid", "At Ask", "At Bid", "At Midpoint"],
defaultValue: "any", defaultValue: "any",
}, },
option_activity_type: { option_activity_type: {
@ -105,17 +105,8 @@
}, },
date_expiration: { date_expiration: {
label: "Date Expiration", label: "Date Expiration",
step: [ step: ["250", "180", "100", "80", "60", "50", "30", "20", "10", "5", "0"],
"Same Day", defaultCondition: "over",
"1 day",
"1 Week",
"2 Weeks",
"1 Month",
"3 Months",
"6 Months",
"1 Year",
"3 Years",
],
defaultValue: "any", defaultValue: "any",
}, },
underlying_type: { underlying_type: {
@ -218,7 +209,6 @@
case "sentiment": case "sentiment":
case "execution_estimate": case "execution_estimate":
case "option_activity_type": case "option_activity_type":
case "date_expiration":
case "underlying_type": case "underlying_type":
newRule = { newRule = {
name: ruleName, name: ruleName,
@ -350,7 +340,6 @@
"sentiment", "sentiment",
"execution_estimate", "execution_estimate",
"option_activity_type", "option_activity_type",
"date_expiration",
"underlying_type", "underlying_type",
]?.includes(ruleName) ]?.includes(ruleName)
) { ) {
@ -1135,7 +1124,7 @@ function sendMessage(message) {
<DropdownMenu.Content <DropdownMenu.Content
class="w-64 min-h-auto max-h-72 overflow-y-auto scroller" class="w-64 min-h-auto max-h-72 overflow-y-auto scroller"
> >
{#if !["put_call", "sentiment", "execution_estimate", "option_activity_type", "date_expiration", "underlying_type"]?.includes(row?.rule)} {#if !["put_call", "sentiment", "execution_estimate", "option_activity_type", "underlying_type"]?.includes(row?.rule)}
<DropdownMenu.Label <DropdownMenu.Label
class="absolute mt-2 h-11 border-gray-800 border-b -top-1 z-20 fixed sticky bg-[#09090B]" class="absolute mt-2 h-11 border-gray-800 border-b -top-1 z-20 fixed sticky bg-[#09090B]"
> >
@ -1295,7 +1284,7 @@ function sendMessage(message) {
></div> ></div>
{/if} {/if}
<DropdownMenu.Group class="min-h-10 mt-2"> <DropdownMenu.Group class="min-h-10 mt-2">
{#if !["put_call", "sentiment", "execution_estimate", "option_activity_type", "date_expiration", "underlying_type"]?.includes(row?.rule)} {#if !["put_call", "sentiment", "execution_estimate", "option_activity_type", "underlying_type"]?.includes(row?.rule)}
{#each row?.step as newValue, index} {#each row?.step as newValue, index}
{#if ruleCondition[row?.rule] === "between"} {#if ruleCondition[row?.rule] === "between"}
{#if newValue && row?.step[index + 1]} {#if newValue && row?.step[index + 1]}
@ -1339,7 +1328,7 @@ function sendMessage(message) {
</DropdownMenu.Item> </DropdownMenu.Item>
{/if} {/if}
{/each} {/each}
{:else if ["put_call", "sentiment", "execution_estimate", "option_activity_type", "date_expiration", "underlying_type"]?.includes(row?.rule)} {:else if ["put_call", "sentiment", "execution_estimate", "option_activity_type", "underlying_type"]?.includes(row?.rule)}
{#each row?.step as item} {#each row?.step as item}
<DropdownMenu.Item <DropdownMenu.Item
class="sm:hover:bg-[#2A2E39]" class="sm:hover:bg-[#2A2E39]"

View File

@ -1,3 +1,12 @@
const categoricalFields = [
'put_call',
'sentiment',
'execution_estimate',
'option_activity_type',
'underlying_type'
];
function convertUnitToValue( function convertUnitToValue(
input: string | number | string[], input: string | number | string[],
): number | string[] | string { ): number | string[] | string {
@ -9,7 +18,6 @@ function convertUnitToValue(
); );
} }
const lowerInput = input.toLowerCase(); const lowerInput = input.toLowerCase();
// Pre-compute the set for quick lookups
const nonNumericValues = new Set([ const nonNumericValues = new Set([
"any", "any",
"puts", "puts",
@ -26,16 +34,6 @@ function convertUnitToValue(
"trade", "trade",
"stock", "stock",
"etf", "etf",
...[
"1 day",
"1 Week",
"2 Weeks",
"1 Month",
"3 Months",
"6 Months",
"1 Year",
"3 Years",
],
]); ]);
if (nonNumericValues.has(lowerInput)) return input; if (nonNumericValues.has(lowerInput)) return input;
if (input.endsWith("%")) { if (input.endsWith("%")) {
@ -66,114 +64,13 @@ function isAny(value: string | string[]): boolean {
return false; return false;
} }
function isSameDay(date1: Date, date2: Date): boolean {
return (
date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate()
);
}
function isDateWithinRange(dateString: string, range: string): boolean {
let now = new Date();
const expirationDate = new Date(dateString);
const dayOfWeek = now.getDay();
// Special handling for "1 day" range when it's Friday, Saturday, or Sunday
if (
range.toLowerCase() === "1 day" &&
(dayOfWeek === 5 || dayOfWeek === 6 || dayOfWeek === 0)
) {
// Calculate next Monday
const daysUntilMonday =
dayOfWeek === 5
? 3 // From Friday
: dayOfWeek === 6
? 2 // From Saturday
: 1; // From Sunday
const monday = new Date(now);
monday.setDate(now.getDate() + daysUntilMonday);
// Compare with next Monday
return isSameDay(expirationDate, monday);
}
// Adjust now to Friday if it falls on a weekend (for other ranges)
if (dayOfWeek === 6) {
// Saturday
now.setDate(now.getDate() - 1); // Move to Friday
} else if (dayOfWeek === 0) {
// Sunday
now.setDate(now.getDate() - 2); // Move to Friday
}
const timeDiff = expirationDate.getTime() - now.getTime();
const daysDiff = timeDiff / (1000 * 60 * 60 * 24);
switch (range.toLowerCase()) {
case "same day":
return isSameDay(now, expirationDate);
case "1 day":
return daysDiff >= 0 && daysDiff <= 1;
case "1 week":
return daysDiff >= 0 && daysDiff <= 7;
case "2 weeks":
return daysDiff >= 0 && daysDiff <= 14;
case "1 month":
return daysDiff >= 0 && daysDiff <= 30;
case "3 months":
return daysDiff >= 0 && daysDiff <= 90;
case "6 months":
return daysDiff >= 0 && daysDiff <= 180;
case "1 year":
return daysDiff >= 0 && daysDiff <= 365;
case "3 years":
return daysDiff >= 0 && daysDiff <= 1095;
default:
return false;
}
}
async function filterRawData(rawData, ruleOfList, filterQuery) {
// Early return for empty inputs
if (!rawData?.length ) {
return rawData || [];
}
// Preprocess filter tickers
const filterTickers = filterQuery
? filterQuery.split(",").map((ticker) => ticker.trim().toUpperCase())
: [];
// Precompile rules for more efficient filtering
const compiledRules = ruleOfList.map(rule => {
const ruleName = rule.name.toLowerCase();
const ruleValue = convertUnitToValue(rule.value);
return {
...rule,
compiledCheck: createRuleCheck(rule, ruleName, ruleValue)
};
});
// Optimized filtering with precompiled rules
return rawData.filter(item => {
// Early ticker filtering
if (filterTickers.length > 0 &&
!filterTickers.includes(item.ticker.toUpperCase())) {
return false;
}
// Apply all precompiled rules
return compiledRules.every(rule => rule.compiledCheck(item));
});
}
// Centralized rule checking logic
function createRuleCheck(rule, ruleName, ruleValue) { function createRuleCheck(rule, ruleName, ruleValue) {
// Handle volumeOIRatio const now = new Date(new Date().toLocaleString("en-US", { timeZone: "America/New_York" }));
if (ruleName === 'volumeoiratio') {
if (ruleName === 'volumeoiratio') {
return (item) => { return (item) => {
const volume = parseFloat(item.volume); const volume = parseFloat(item.volume);
const openInterest = parseFloat(item.open_interest); const openInterest = parseFloat(item.open_interest);
@ -190,99 +87,99 @@ function createRuleCheck(rule, ruleName, ruleValue) {
}; };
} }
// Handle "between" condition
if (rule.condition === 'between') {
return (item) => {
const itemValue = parseFloat(item[rule.name]);
// Handle array of ruleValue for between condition
if (!Array.isArray(ruleValue)) return true;
// Convert rule values, ensuring they are valid
const [min, max] = ruleValue.map(convertUnitToValue);
// Handle the case where one or both values are missing (empty string or undefined)
if ((min === '' || min === undefined || min === null) &&
(max === '' || max === undefined || max === null)) {
return true; // If both values are empty or undefined, consider the condition as met (open-ended)
}
// If only one of min or max is missing, handle it as open-ended
if (min === '' || min === undefined || min === null) {
return itemValue <= max; // If min is missing, only check against max
}
if (max === '' || max === undefined || max === null) {
return itemValue >= min; // If max is missing, only check against min
}
// If both min and max are defined, proceed with the normal comparison
return itemValue > min && itemValue < max;
};
}
// Handle date_expiration
if (ruleName === 'date_expiration') { if (ruleName === 'date_expiration') {
// If 'any' is specified or no specific range is set // If ruleValue is empty, undefined, "any", or an array containing only "any", return a function that always returns true
if (isAny(ruleValue)) return () => true; if (ruleValue === "" || ruleValue === undefined || isAny(ruleValue)) {
return () => true;
return (item) => {
if (Array.isArray(ruleValue)) {
return ruleValue.some(range => isDateWithinRange(item[rule.name], range));
}
return isDateWithinRange(item[rule.name], ruleValue);
};
} }
// Categorical data handling return (item) => {
const categoricalFields = [ const expirationDate = new Date(item[rule.name]);
'put_call', if (isNaN(expirationDate)) return false; // Handle invalid dates
'sentiment',
'execution_estimate',
'option_activity_type',
'underlying_type'
];
if (categoricalFields.includes(ruleName)) { const daysDiff = (expirationDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24);
// If 'any' is specified
if (isAny(ruleValue)) return () => true;
if (rule.condition === 'between' && Array.isArray(ruleValue)) {
const [minDays, maxDays] = ruleValue.map(val =>
val === '' || val === null || val === undefined ? null : parseFloat(val.toString())
);
if (minDays === null && maxDays === null) return true;
if (minDays === null) return daysDiff <= maxDays;
if (maxDays === null) return daysDiff >= minDays;
return daysDiff >= minDays && daysDiff <= maxDays;
}
if (rule.condition === 'over' && typeof ruleValue === 'number') {
return daysDiff >= ruleValue;
}
if (rule.condition === 'under' && typeof ruleValue === 'number') {
return daysDiff <= ruleValue;
}
return false;
};
}
// Handle string-based conditions (sentiment, option_type, etc.)
if (categoricalFields?.includes(ruleName)) {
return (item) => { return (item) => {
// If ruleValue is empty, undefined, or "any", return true for all items
if (ruleValue === "" || ruleValue === undefined || isAny(ruleValue)) {
return true;
}
const itemValue = item[rule.name]; const itemValue = item[rule.name];
// Handle null or undefined // Handle array of values for categorical fields
if (itemValue === null || itemValue === undefined) return false;
const lowerItemValue = itemValue.toString().toLowerCase();
// Handle array of values
if (Array.isArray(ruleValue)) { if (Array.isArray(ruleValue)) {
return ruleValue.some( // Remove any empty or undefined values from ruleValue
value => lowerItemValue === value.toString().toLowerCase() const validRuleValues = ruleValue.filter(val => val !== "" && val !== undefined);
// If no valid values remain, return true for all items
if (validRuleValues.length === 0) {
return true;
}
// If itemValue is an array, check if any of the values match
if (Array.isArray(itemValue)) {
return validRuleValues.some(val =>
itemValue.some(iv => iv.toLowerCase() === val.toLowerCase())
);
}
// If itemValue is a string, check if it's in the validRuleValues array
return validRuleValues.some(val =>
itemValue?.toLowerCase() === val.toLowerCase()
); );
} }
// Single value comparison // Handle single string value
return lowerItemValue === ruleValue.toString().toLowerCase(); if (typeof ruleValue === 'string') {
// If itemValue is an array, check if any value matches
if (Array.isArray(itemValue)) {
return itemValue.some(iv => iv.toLowerCase() === ruleValue.toLowerCase());
}
// If both are strings, do a direct comparison
return itemValue?.toLowerCase() === ruleValue.toLowerCase();
}
return false;
}; };
} }
// Default numeric comparison // Fallback to other numeric conditions
return (item) => { return (item) => {
const itemValue = item[rule.name]; const itemValue = item[rule.name];
if (typeof ruleValue === 'string') return true; // Handle cases where the rule value is a string but not 'sentiment' or 'option_type'
// Skip string rule values
if (typeof ruleValue === 'string') return true;
// Handle null or undefined
if (itemValue === null || itemValue === undefined) return false; if (itemValue === null || itemValue === undefined) return false;
const numericItemValue = parseFloat(itemValue); const numericItemValue = parseFloat(itemValue);
// Invalid numeric conversion
if (isNaN(numericItemValue)) return false; if (isNaN(numericItemValue)) return false;
// Comparison conditions
if (rule.condition === 'over' && numericItemValue <= ruleValue) return false; if (rule.condition === 'over' && numericItemValue <= ruleValue) return false;
if (rule.condition === 'under' && numericItemValue >= ruleValue) return false; if (rule.condition === 'under' && numericItemValue >= ruleValue) return false;
@ -290,13 +187,47 @@ function createRuleCheck(rule, ruleName, ruleValue) {
}; };
} }
async function filterRawData(rawData, ruleOfList, filterQuery) {
// Early return for empty inputs
if (!rawData?.length ) {
return rawData || [];
}
// Preprocess filter tickers
const filterTickers = filterQuery
? filterQuery.split(",").map((ticker) => ticker.trim().toUpperCase())
: [];
// Precompile rules for more efficient filtering
const compiledRules = ruleOfList.map(rule => {
const ruleName = rule?.name?.toLowerCase();
const ruleValue = convertUnitToValue(rule.value);
return {
...rule,
compiledCheck: createRuleCheck(rule, ruleName, ruleValue)
};
});
// Optimized filtering with precompiled rules
return rawData?.filter(item => {
// Early ticker filtering
if (filterTickers?.length > 0 &&
!filterTickers?.includes(item.ticker.toUpperCase())) {
return false;
}
// Apply all precompiled rules
return compiledRules.every(rule => rule?.compiledCheck(item));
});
}
// Web Worker message handler // Web Worker message handler
onmessage = async (event: MessageEvent) => { onmessage = async (event: MessageEvent) => {
const { rawData, ruleOfList, filterQuery } = event.data || {}; const { rawData, ruleOfList, filterQuery } = event.data || {};
// Filter the data // Filter the data
let filteredData = await filterRawData(rawData, ruleOfList, filterQuery); let filteredData = await filterRawData(rawData, ruleOfList, filterQuery);
// Remove duplicates based on id // Remove duplicates based on id
filteredData = Array.from( filteredData = Array.from(
new Map(filteredData?.map((item) => [item?.id, item]))?.values() new Map(filteredData?.map((item) => [item?.id, item]))?.values()