fine tune screener with between rule
This commit is contained in:
parent
2f9c4cf65a
commit
085d3c3abe
@ -1374,7 +1374,7 @@
|
||||
|
||||
// Check if the default condition is "between"
|
||||
if (allRules[ruleName].defaultCondition === "between") {
|
||||
valueMappings[ruleName] = allRules[ruleName].defaultValue || ["", ""];
|
||||
valueMappings[ruleName] = allRules[ruleName].defaultValue || [null, null];
|
||||
} else {
|
||||
valueMappings[ruleName] = allRules[ruleName].defaultValue;
|
||||
}
|
||||
@ -1883,28 +1883,54 @@ const handleKeyDown = (event) => {
|
||||
return checkedItems?.has(ruleName) && checkedItems?.get(ruleName).has(item);
|
||||
}
|
||||
|
||||
// Utility function to convert values to comparable numbers
|
||||
function parseValue(val) {
|
||||
if (typeof val === "string") {
|
||||
// Handle percentage values
|
||||
if (val.endsWith("%")) {
|
||||
return parseFloat(val);
|
||||
}
|
||||
|
||||
// Handle values with suffixes like K (thousand), M (million), B (billion)
|
||||
const suffixMap = {
|
||||
K: 1e3,
|
||||
M: 1e6,
|
||||
B: 1e9,
|
||||
};
|
||||
|
||||
const suffix = val.slice(-1).toUpperCase();
|
||||
const numberPart = parseFloat(val);
|
||||
|
||||
if (suffix in suffixMap) {
|
||||
return numberPart * suffixMap[suffix];
|
||||
}
|
||||
}
|
||||
|
||||
return parseFloat(val);
|
||||
}
|
||||
|
||||
// Custom sorting function
|
||||
function customSort(a, b) {
|
||||
return parseValue(a) - parseValue(b);
|
||||
}
|
||||
|
||||
// Main function
|
||||
async function handleChangeValue(value) {
|
||||
if (checkedItems.has(ruleName)) {
|
||||
const itemsSet = checkedItems.get(ruleName);
|
||||
|
||||
// For "between", value is expected to be an array [min, max]
|
||||
const sortedValue = Array.isArray(value)
|
||||
? value.sort((a, b) => a - b)
|
||||
: value;
|
||||
const sortedValue = Array.isArray(value) ? value.sort(customSort) : value;
|
||||
const valueKey = Array.isArray(sortedValue)
|
||||
? sortedValue.join("-")
|
||||
: sortedValue;
|
||||
|
||||
if (itemsSet?.has(valueKey)) {
|
||||
itemsSet?.delete(valueKey); // Remove the value if it's already in the set
|
||||
itemsSet?.delete(valueKey);
|
||||
} else {
|
||||
itemsSet?.add(valueKey); // Add the value if it's not in the set
|
||||
itemsSet?.add(valueKey);
|
||||
}
|
||||
} else {
|
||||
// If the ruleName is not in checkedItems, create a new set for this rule
|
||||
const sortedValue = Array.isArray(value)
|
||||
? value.sort((a, b) => a - b)
|
||||
: value;
|
||||
const sortedValue = Array.isArray(value) ? value.sort(customSort) : value;
|
||||
const valueKey = Array.isArray(sortedValue)
|
||||
? sortedValue.join("-")
|
||||
: sortedValue;
|
||||
@ -1932,39 +1958,34 @@ const handleKeyDown = (event) => {
|
||||
) {
|
||||
searchQuery = "";
|
||||
|
||||
// Ensure valueMappings[ruleName] is initialized as an array
|
||||
if (!Array.isArray(valueMappings[ruleName])) {
|
||||
valueMappings[ruleName] = [];
|
||||
}
|
||||
|
||||
const sortedValue = Array.isArray(value)
|
||||
? value.sort((a, b) => a - b)
|
||||
const sortedValue = Array?.isArray(value)
|
||||
? value?.sort(customSort)
|
||||
: value;
|
||||
const valueKey = Array.isArray(sortedValue)
|
||||
const valueKey = Array?.isArray(sortedValue)
|
||||
? sortedValue.join("-")
|
||||
: sortedValue;
|
||||
const index = valueMappings[ruleName].indexOf(valueKey);
|
||||
|
||||
if (index === -1) {
|
||||
// Add the value if it's not already selected
|
||||
valueMappings[ruleName].push(valueKey);
|
||||
} else {
|
||||
// Remove the value if it's already selected
|
||||
valueMappings[ruleName].splice(index, 1);
|
||||
}
|
||||
|
||||
// If no values are selected, set the value to "any"
|
||||
if (valueMappings[ruleName].length === 0) {
|
||||
valueMappings[ruleName] = "any";
|
||||
}
|
||||
|
||||
await updateStockScreenerData();
|
||||
} else if (ruleName in valueMappings) {
|
||||
// Handle "over", "under", and "between" conditions
|
||||
if (ruleCondition[ruleName] === "between" && Array.isArray(value)) {
|
||||
valueMappings[ruleName] = value.sort((a, b) => a - b); // Ensure lower value is first
|
||||
if (ruleCondition[ruleName] === "between" && Array?.isArray(value)) {
|
||||
valueMappings[ruleName] = value?.sort(customSort);
|
||||
} else {
|
||||
valueMappings[ruleName] = value; // Store single values for "over" and "under"
|
||||
valueMappings[ruleName] = value;
|
||||
}
|
||||
} else {
|
||||
console.warn(`Unhandled rule: ${ruleName}`);
|
||||
@ -1988,14 +2009,17 @@ const handleKeyDown = (event) => {
|
||||
await handleChangeValue(newValue);
|
||||
}
|
||||
|
||||
async function handleValueInput(event) {
|
||||
async function handleValueInput(event, ruleName, index = null) {
|
||||
const newValue = event.target.value;
|
||||
if (newValue?.length > 0) {
|
||||
console.log("yes");
|
||||
|
||||
if (ruleCondition[ruleName] === "between") {
|
||||
const currentValues = valueMappings[ruleName] || ["", ""];
|
||||
currentValues[index] = newValue;
|
||||
await handleChangeValue(currentValues);
|
||||
} else {
|
||||
await handleChangeValue(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
async function popularStrategy(state: string) {
|
||||
ruleOfList = [];
|
||||
const strategies = {
|
||||
@ -2757,12 +2781,15 @@ const handleKeyDown = (event) => {
|
||||
<span class="truncate ml-2 text-sm sm:text-[1rem]">
|
||||
{#if valueMappings[row?.rule] === "any"}
|
||||
Any
|
||||
{:else}
|
||||
{:else if ["under", "over"]?.includes(ruleCondition[row?.rule])}
|
||||
{ruleCondition[row?.rule]
|
||||
?.replace("under", "Under")
|
||||
?.replace("over", "Over")
|
||||
?.replace("between", "Between")}
|
||||
?.replace("over", "Over")}
|
||||
{valueMappings[row?.rule]}
|
||||
{:else if ruleCondition[row?.rule] === "between"}
|
||||
{Array.isArray(valueMappings[row?.rule])
|
||||
? `${valueMappings[row?.rule][0]}-${valueMappings[row?.rule][1] ?? "Any"}`
|
||||
: "Any"}
|
||||
{/if}
|
||||
</span>
|
||||
<svg
|
||||
@ -2781,7 +2808,7 @@ const handleKeyDown = (event) => {
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
class="w-64 h-fit max-h-72 overflow-y-auto scroller"
|
||||
class="w-64 min-h-64 max-h-72 overflow-y-auto scroller"
|
||||
>
|
||||
{#if !["sma20", "sma50", "sma100", "sma200", "ema20", "ema50", "ema100", "ema200", "grahamNumber", "analystRating", "halalStocks", "score", "sector", "industry", "country"]?.includes(row?.rule)}
|
||||
<DropdownMenu.Label
|
||||
@ -2840,13 +2867,49 @@ const handleKeyDown = (event) => {
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Value"
|
||||
value={valueMappings[row?.rule]}
|
||||
on:input={(e) => handleValueInput(e)}
|
||||
class=" ios-zoom-fix block max-w-[4.8rem] rounded-sm placeholder:text-gray-200 font-normal p-1 text-sm shadow-sm focus:border-blue-500 focus:ring-blue-500 bg-secondary"
|
||||
/>
|
||||
{#if ruleCondition[row?.rule] === "between"}
|
||||
<div class="flex gap-x-1 -ml-2 z-10 -mt-1">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Min"
|
||||
value={Array.isArray(
|
||||
valueMappings[row?.rule],
|
||||
)
|
||||
? (valueMappings[row?.rule][0] ?? "")
|
||||
: ""}
|
||||
on:input={(e) =>
|
||||
handleValueInput(e, row?.rule, 0)}
|
||||
class="ios-zoom-fix block max-w-[3.5rem] rounded-sm placeholder:text-gray-200 font-normal p-1 text-sm shadow-sm focus:border-blue-500 focus:ring-blue-500 bg-secondary"
|
||||
/>
|
||||
<span
|
||||
class="text-white text-[1rem] font-normal mt-1"
|
||||
>
|
||||
&
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Max"
|
||||
value={Array.isArray(
|
||||
valueMappings[row?.rule],
|
||||
)
|
||||
? (valueMappings[row?.rule][1] ?? "")
|
||||
: ""}
|
||||
on:input={(e) =>
|
||||
handleValueInput(e, row?.rule, 1)}
|
||||
class="ios-zoom-fix block max-w-[3.5rem] rounded-sm placeholder:text-gray-200 font-normal p-1 text-sm shadow-sm focus:border-blue-500 focus:ring-blue-500 bg-secondary"
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Value"
|
||||
value={valueMappings[row?.rule]}
|
||||
on:input={(e) =>
|
||||
handleValueInput(e, row?.rule)}
|
||||
class="ios-zoom-fix block max-w-[4.8rem] rounded-sm placeholder:text-gray-200 font-normal p-1 text-sm shadow-sm focus:border-blue-500 focus:ring-blue-500 bg-secondary"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if ["over", "under"]?.includes(ruleCondition[ruleName]?.toLowerCase())}
|
||||
<div
|
||||
class="ml-2 flex touch-manipulation flex-row items-center gap-x-1.5"
|
||||
@ -2926,39 +2989,45 @@ const handleKeyDown = (event) => {
|
||||
<DropdownMenu.Group class="min-h-10 mt-2">
|
||||
{#if !["sma20", "sma50", "sma100", "sma200", "ema20", "ema50", "ema100", "ema200", "grahamNumber", "analystRating", "halalStocks", "score", "sector", "industry", "country"]?.includes(row?.rule)}
|
||||
{#each row?.step as newValue, index}
|
||||
{#if ruleCondition[row?.rule] === "between" && newValue && row?.step[index + 1]}
|
||||
{#if ruleCondition[row?.rule] === "between"}
|
||||
{#if newValue && row?.step[index + 1]}
|
||||
<DropdownMenu.Item
|
||||
class="sm:hover:bg-primary"
|
||||
>
|
||||
<button
|
||||
on:click={() => {
|
||||
handleChangeValue([
|
||||
row?.step[index],
|
||||
row?.step[index + 1],
|
||||
]);
|
||||
}}
|
||||
class="block w-full border-b border-gray-600 px-4 py-1.5 text-left text-sm sm:text-[1rem] rounded text-white last:border-0 sm:hover:bg-primary focus:bg-blue-100 focus:text-gray-900 focus:outline-none"
|
||||
>
|
||||
{ruleCondition[row?.rule]?.replace(
|
||||
"between",
|
||||
"Between",
|
||||
)}
|
||||
{row?.step[index + 1]} - {row?.step[
|
||||
index
|
||||
]}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
{/if}
|
||||
{:else}
|
||||
<DropdownMenu.Item class="sm:hover:bg-primary">
|
||||
<button
|
||||
on:click={() => {
|
||||
handleChangeValue([
|
||||
row?.step[index],
|
||||
row?.step[index + 1],
|
||||
]);
|
||||
handleChangeValue(newValue);
|
||||
}}
|
||||
class="block w-full border-b border-gray-600 px-4 py-1.5 text-left text-sm sm:text-[1rem] rounded text-white last:border-0 sm:hover:bg-primary focus:bg-blue-100 focus:text-gray-900 focus:outline-none"
|
||||
>
|
||||
{ruleCondition[row?.rule]?.replace(
|
||||
"between",
|
||||
"Between",
|
||||
)}
|
||||
{row?.step[index + 1]} - {row?.step[index]}
|
||||
{ruleCondition[row?.rule]
|
||||
?.replace("under", "Under")
|
||||
?.replace("over", "Over")}
|
||||
{newValue}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
{/if}
|
||||
{:else}
|
||||
<DropdownMenu.Item class="sm:hover:bg-primary">
|
||||
<button
|
||||
on:click={() => {
|
||||
handleChangeValue(newValue);
|
||||
}}
|
||||
class="block w-full border-b border-gray-600 px-4 py-1.5 text-left text-sm sm:text-[1rem] rounded text-white last:border-0 sm:hover:bg-primary focus:bg-blue-100 focus:text-gray-900 focus:outline-none"
|
||||
>
|
||||
{ruleCondition[row?.rule]
|
||||
?.replace("under", "Under")
|
||||
?.replace("over", "Over")}
|
||||
{newValue}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
{/each}
|
||||
{:else if ["sma20", "sma50", "sma100", "sma200", "ema20", "ema50", "ema100", "ema200", "grahamNumber"]?.includes(row?.rule)}
|
||||
{#each row?.step as item}
|
||||
|
||||
@ -43,139 +43,215 @@ const movingAverageConditions = {
|
||||
};
|
||||
|
||||
// Convert the input to a value or return it as-is if it's already an array
|
||||
function convertUnitToValue(
|
||||
input: string | number | string[]
|
||||
): number | string[] | string {
|
||||
if (Array.isArray(input)) return input;
|
||||
if (typeof input === "number") return input;
|
||||
if (typeof input !== "string") {
|
||||
throw new TypeError(
|
||||
`Expected a string or number, but received ${typeof input}`
|
||||
);
|
||||
}
|
||||
const lowerInput = input.toLowerCase();
|
||||
// Pre-compute the set for quick lookups
|
||||
const nonNumericValues = new Set([
|
||||
"any",
|
||||
...sectorList,
|
||||
...industryList,
|
||||
...listOfRelevantCountries,
|
||||
"hold",
|
||||
"sell",
|
||||
"buy",
|
||||
"strong buy",
|
||||
"strong sell",
|
||||
"compliant",
|
||||
"non-compliant",
|
||||
"stock price",
|
||||
]);
|
||||
if (nonNumericValues.has(lowerInput)) return input;
|
||||
|
||||
if (input.endsWith("%")) {
|
||||
const numericValue = parseFloat(input.slice(0, -1));
|
||||
if (isNaN(numericValue)) {
|
||||
throw new Error(`Unable to convert ${input} to a number`);
|
||||
function convertUnitToValue(input: string | number | string[]) {
|
||||
try {
|
||||
if (Array.isArray(input)) {
|
||||
return input.map(convertUnitToValue); // Recursively convert array elements
|
||||
}
|
||||
if (typeof input === "number") return input;
|
||||
if (typeof input !== "string") {
|
||||
return input; // Return as-is if not a string or number
|
||||
}
|
||||
|
||||
const lowerInput = input.toLowerCase();
|
||||
|
||||
// Pre-compute the set for quick lookups
|
||||
const nonNumericValues = new Set([
|
||||
"any",
|
||||
...sectorList,
|
||||
...industryList,
|
||||
...listOfRelevantCountries,
|
||||
"hold",
|
||||
"sell",
|
||||
"buy",
|
||||
"strong buy",
|
||||
"strong sell",
|
||||
"compliant",
|
||||
"non-compliant",
|
||||
"stock price",
|
||||
]);
|
||||
|
||||
if (nonNumericValues.has(lowerInput)) return input;
|
||||
|
||||
// Handle percentage values
|
||||
if (input.endsWith("%")) {
|
||||
const numericValue = parseFloat(input.slice(0, -1)); // Remove '%' and convert to number
|
||||
if (isNaN(numericValue)) {
|
||||
return input; // Return original input if conversion fails
|
||||
}
|
||||
return numericValue / 100; // Convert percentage to a decimal
|
||||
}
|
||||
|
||||
// Handle units (B, M, K)
|
||||
const units = { B: 1_000_000_000, M: 1_000_000, K: 1_000 };
|
||||
const match = input.match(/^(\d+(\.\d+)?)([BMK])?$/);
|
||||
|
||||
if (match) {
|
||||
const value = parseFloat(match[1]);
|
||||
const unit = match[3] as keyof typeof units;
|
||||
return unit ? value * units[unit] : value;
|
||||
}
|
||||
|
||||
// Default numeric conversion (if no unit specified)
|
||||
const numericValue = parseFloat(input);
|
||||
if (isNaN(numericValue)) {
|
||||
return input; // Return original input if conversion fails
|
||||
}
|
||||
|
||||
return numericValue;
|
||||
} catch (error) {
|
||||
console.warn(`Error converting value: ${input}`, error);
|
||||
return input; // Return original input in case of any unexpected errors
|
||||
}
|
||||
|
||||
const units = { B: 1_000_000_000, M: 1_000_000, K: 1_000 };
|
||||
const match = input.match(/^(\d+(\.\d+)?)([BMK])?$/);
|
||||
|
||||
if (match) {
|
||||
const value = parseFloat(match[1]);
|
||||
const unit = match[3] as keyof typeof units;
|
||||
return unit ? value * units[unit] : value;
|
||||
}
|
||||
|
||||
const numericValue = parseFloat(input);
|
||||
if (isNaN(numericValue)) {
|
||||
throw new Error(`Unable to convert ${input} to a number`);
|
||||
}
|
||||
|
||||
return numericValue;
|
||||
}
|
||||
|
||||
// Filter the stock screener data based on the provided rules
|
||||
async function filterStockScreenerData(stockScreenerData, ruleOfList) {
|
||||
return stockScreenerData?.filter((item) => {
|
||||
return ruleOfList.every((rule) => {
|
||||
const itemValue = item[rule.name];
|
||||
const ruleValue = convertUnitToValue(rule.value);
|
||||
const ruleName = rule.name.toLowerCase();
|
||||
try {
|
||||
return stockScreenerData?.filter((item) => {
|
||||
return ruleOfList.every((rule) => {
|
||||
try {
|
||||
const itemValue = item[rule.name];
|
||||
const ruleValue = convertUnitToValue(rule.value);
|
||||
const ruleName = rule.name.toLowerCase();
|
||||
|
||||
// Handle trend and fundamental analysis
|
||||
if (["trendAnalysis", "fundamentalAnalysis"].includes(rule.name)) {
|
||||
const accuracy = item[rule.name]?.accuracy;
|
||||
if (rule.condition === "over" && accuracy <= ruleValue) return false;
|
||||
if (rule.condition === "under" && accuracy > ruleValue) return false;
|
||||
}
|
||||
// If ruleValue is the original input (conversion failed),
|
||||
// we'll treat it as a special case
|
||||
if (typeof ruleValue === "string") {
|
||||
// For most string inputs, we'll consider it a match
|
||||
if (rule.value === "any") return true;
|
||||
|
||||
// Handle categorical data like analyst ratings, sector, country
|
||||
else if (
|
||||
[
|
||||
"analystRating",
|
||||
"halalStocks",
|
||||
"score",
|
||||
"sector",
|
||||
"industry",
|
||||
"country",
|
||||
].includes(rule.name)
|
||||
) {
|
||||
if (rule.value === "any") return true;
|
||||
// For specific categorical checks
|
||||
if (
|
||||
[
|
||||
"analystRating",
|
||||
"halalStocks",
|
||||
"score",
|
||||
"sector",
|
||||
"industry",
|
||||
"country",
|
||||
].includes(rule.name)
|
||||
) {
|
||||
if (Array.isArray(ruleValue) && !ruleValue.includes(itemValue))
|
||||
return false;
|
||||
if (!Array.isArray(ruleValue) && itemValue !== ruleValue) return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(ruleValue) && !ruleValue.includes(itemValue))
|
||||
return false;
|
||||
if (!Array.isArray(ruleValue) && itemValue !== ruleValue) return false;
|
||||
}
|
||||
|
||||
// Handle moving averages
|
||||
else if (
|
||||
[
|
||||
"ema20",
|
||||
"ema50",
|
||||
"ema100",
|
||||
"ema200",
|
||||
"sma20",
|
||||
"sma50",
|
||||
"sma100",
|
||||
"sma200",
|
||||
"grahamnumber", //grahamNumber into lowerCase form
|
||||
].includes(ruleName)
|
||||
) {
|
||||
if (ruleValue === "any") return true;
|
||||
|
||||
for (const condition of ruleValue) {
|
||||
if (movingAverageConditions[condition]) {
|
||||
if (!movingAverageConditions[condition](item)) return false;
|
||||
} else {
|
||||
//console.warn(`Unknown condition: ${condition}`);
|
||||
// For other cases, we'll skip filtering
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle categorical data like analyst ratings, sector, country
|
||||
if (
|
||||
[
|
||||
"analystRating",
|
||||
"halalStocks",
|
||||
"score",
|
||||
"sector",
|
||||
"industry",
|
||||
"country",
|
||||
].includes(rule.name)
|
||||
) {
|
||||
if (rule.value === "any") return true;
|
||||
|
||||
if (Array.isArray(ruleValue) && !ruleValue.includes(itemValue))
|
||||
return false;
|
||||
if (!Array.isArray(ruleValue) && itemValue !== ruleValue) return false;
|
||||
}
|
||||
|
||||
// Handle moving averages
|
||||
else if (
|
||||
[
|
||||
"ema20",
|
||||
"ema50",
|
||||
"ema100",
|
||||
"ema200",
|
||||
"sma20",
|
||||
"sma50",
|
||||
"sma100",
|
||||
"sma200",
|
||||
"grahamnumber", // grahamNumber into lowerCase form
|
||||
].includes(ruleName)
|
||||
) {
|
||||
if (ruleValue === "any") return true;
|
||||
|
||||
for (const condition of ruleValue) {
|
||||
if (movingAverageConditions[condition]) {
|
||||
if (!movingAverageConditions[condition](item)) return false;
|
||||
} else {
|
||||
// console.warn(`Unknown condition: ${condition}`);
|
||||
}
|
||||
}
|
||||
|
||||
return true; // If all conditions are met
|
||||
}
|
||||
|
||||
// Handle "between" condition
|
||||
else if (rule.condition === "between" && Array?.isArray(ruleValue)) {
|
||||
// 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) {
|
||||
if (itemValue >= max) return false; // If min is missing, only check against max
|
||||
} else if (max === "" || max === undefined || max === null) {
|
||||
if (itemValue <= min) return false; // If max is missing, only check against min
|
||||
} else {
|
||||
// If both min and max are defined, proceed with the normal comparison
|
||||
if (itemValue <= min || itemValue >= max) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Default numeric or string comparison
|
||||
else if (typeof ruleValue === "string") {
|
||||
return true; // Skip non-numeric comparisons
|
||||
} else if (itemValue === null) {
|
||||
return false; // Null values do not meet any condition
|
||||
} else if (rule.condition === "over" && itemValue <= ruleValue) {
|
||||
return false;
|
||||
} else if (rule.condition === "under" && itemValue > ruleValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (ruleError) {
|
||||
console.warn(`Error processing rule for item:`, rule, ruleError);
|
||||
return true; // Default to including the item if rule processing fails
|
||||
}
|
||||
|
||||
return true; // If all conditions are met
|
||||
}
|
||||
|
||||
// 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 (rule.condition === "over" && itemValue <= ruleValue) return false;
|
||||
if (rule.condition === "under" && itemValue > ruleValue) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
}) || stockScreenerData; // Return original data if filtering completely fails
|
||||
} catch (error) {
|
||||
console.error('Error in filterStockScreenerData:', error);
|
||||
return stockScreenerData; // Return original data if any catastrophic error occurs
|
||||
}
|
||||
}
|
||||
|
||||
onmessage = async (event: MessageEvent) => {
|
||||
const { stockScreenerData, ruleOfList } = event.data || {};
|
||||
|
||||
const filteredData = await filterStockScreenerData(
|
||||
stockScreenerData,
|
||||
ruleOfList
|
||||
);
|
||||
try {
|
||||
const filteredData = await filterStockScreenerData(
|
||||
stockScreenerData,
|
||||
ruleOfList
|
||||
);
|
||||
|
||||
postMessage({ message: "success", filteredData });
|
||||
postMessage({
|
||||
message: "success",
|
||||
filteredData,
|
||||
originalDataLength: stockScreenerData?.length || 0,
|
||||
filteredDataLength: filteredData?.length || 0
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error in onmessage handler:', error);
|
||||
postMessage({
|
||||
message: "error",
|
||||
originalData: stockScreenerData,
|
||||
error: error.toString()
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export {};
|
||||
export {};
|
||||
Loading…
x
Reference in New Issue
Block a user