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: {
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",
},
option_activity_type: {
@ -105,17 +105,8 @@
},
date_expiration: {
label: "Date Expiration",
step: [
"Same Day",
"1 day",
"1 Week",
"2 Weeks",
"1 Month",
"3 Months",
"6 Months",
"1 Year",
"3 Years",
],
step: ["250", "180", "100", "80", "60", "50", "30", "20", "10", "5", "0"],
defaultCondition: "over",
defaultValue: "any",
},
underlying_type: {
@ -218,7 +209,6 @@
case "sentiment":
case "execution_estimate":
case "option_activity_type":
case "date_expiration":
case "underlying_type":
newRule = {
name: ruleName,
@ -350,7 +340,6 @@
"sentiment",
"execution_estimate",
"option_activity_type",
"date_expiration",
"underlying_type",
]?.includes(ruleName)
) {
@ -1135,7 +1124,7 @@ function sendMessage(message) {
<DropdownMenu.Content
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
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>
{/if}
<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}
{#if ruleCondition[row?.rule] === "between"}
{#if newValue && row?.step[index + 1]}
@ -1339,7 +1328,7 @@ function sendMessage(message) {
</DropdownMenu.Item>
{/if}
{/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}
<DropdownMenu.Item
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(
input: string | number | string[],
): number | string[] | string {
@ -9,7 +18,6 @@ function convertUnitToValue(
);
}
const lowerInput = input.toLowerCase();
// Pre-compute the set for quick lookups
const nonNumericValues = new Set([
"any",
"puts",
@ -26,16 +34,6 @@ function convertUnitToValue(
"trade",
"stock",
"etf",
...[
"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("%")) {
@ -66,114 +64,13 @@ function isAny(value: string | string[]): boolean {
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) {
// Handle volumeOIRatio
if (ruleName === 'volumeoiratio') {
const now = new Date(new Date().toLocaleString("en-US", { timeZone: "America/New_York" }));
if (ruleName === 'volumeoiratio') {
return (item) => {
const volume = parseFloat(item.volume);
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 'any' is specified or no specific range is set
if (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);
};
// If ruleValue is empty, undefined, "any", or an array containing only "any", return a function that always returns true
if (ruleValue === "" || ruleValue === undefined || isAny(ruleValue)) {
return () => true;
}
// Categorical data handling
const categoricalFields = [
'put_call',
'sentiment',
'execution_estimate',
'option_activity_type',
'underlying_type'
];
return (item) => {
const expirationDate = new Date(item[rule.name]);
if (isNaN(expirationDate)) return false; // Handle invalid dates
if (categoricalFields.includes(ruleName)) {
// If 'any' is specified
if (isAny(ruleValue)) return () => true;
const daysDiff = (expirationDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24);
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) => {
// 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];
// Handle null or undefined
if (itemValue === null || itemValue === undefined) return false;
const lowerItemValue = itemValue.toString().toLowerCase();
// Handle array of values
// Handle array of values for categorical fields
if (Array.isArray(ruleValue)) {
return ruleValue.some(
value => lowerItemValue === value.toString().toLowerCase()
// Remove any empty or undefined values from ruleValue
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
return lowerItemValue === ruleValue.toString().toLowerCase();
// Handle single string value
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) => {
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;
const numericItemValue = parseFloat(itemValue);
// Invalid numeric conversion
if (isNaN(numericItemValue)) return false;
// Comparison conditions
if (rule.condition === 'over' && 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
onmessage = async (event: MessageEvent) => {
const { rawData, ruleOfList, filterQuery } = event.data || {};
// Filter the data
let filteredData = await filterRawData(rawData, ruleOfList, filterQuery);
// Remove duplicates based on id
filteredData = Array.from(
new Map(filteredData?.map((item) => [item?.id, item]))?.values()