bugfixing
This commit is contained in:
parent
b900b009c8
commit
955e6d942b
@ -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]"
|
||||||
|
|||||||
@ -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,113 +64,12 @@ 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);
|
||||||
@ -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) => {
|
return (item) => {
|
||||||
if (Array.isArray(ruleValue)) {
|
const expirationDate = new Date(item[rule.name]);
|
||||||
return ruleValue.some(range => isDateWithinRange(item[rule.name], range));
|
if (isNaN(expirationDate)) return false; // Handle invalid dates
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
return isDateWithinRange(item[rule.name], ruleValue);
|
|
||||||
|
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.)
|
||||||
// Categorical data handling
|
if (categoricalFields?.includes(ruleName)) {
|
||||||
const categoricalFields = [
|
|
||||||
'put_call',
|
|
||||||
'sentiment',
|
|
||||||
'execution_estimate',
|
|
||||||
'option_activity_type',
|
|
||||||
'underlying_type'
|
|
||||||
];
|
|
||||||
|
|
||||||
if (categoricalFields.includes(ruleName)) {
|
|
||||||
// If 'any' is specified
|
|
||||||
if (isAny(ruleValue)) return () => true;
|
|
||||||
|
|
||||||
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())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Single value comparison
|
// If itemValue is a string, check if it's in the validRuleValues array
|
||||||
return lowerItemValue === ruleValue.toString().toLowerCase();
|
return validRuleValues.some(val =>
|
||||||
|
itemValue?.toLowerCase() === val.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) => {
|
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()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user