update sma/ema rules
This commit is contained in:
parent
6f5ca8982e
commit
a6b5d56a66
@ -43,14 +43,14 @@ const allRules = {
|
|||||||
mfi: { label: 'MFI', step: [90,80,70,60,50,40,30,20], category: 'ta', defaultCondition: 'over', defaultValue: 40 },
|
mfi: { label: 'MFI', step: [90,80,70,60,50,40,30,20], category: 'ta', defaultCondition: 'over', defaultValue: 40 },
|
||||||
cci: { label: 'CCI', step: [250,200,100,50,20,0,-20,-50,-100,-200,-250], category: 'ta', defaultCondition: 'over', defaultValue: 0 },
|
cci: { label: 'CCI', step: [250,200,100,50,20,0,-20,-50,-100,-200,-250], category: 'ta', defaultCondition: 'over', defaultValue: 0 },
|
||||||
atr: { label: 'ATR', step: [20,15,10,5,3,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
|
atr: { label: 'ATR', step: [20,15,10,5,3,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
|
||||||
sma20: { label: 'SMA-20', step: [500,250,100,50,10,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
|
sma20: { label: 'SMA20', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' },
|
||||||
sma50: { label: 'SMA-50', step: [500,250,100,50,10,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
|
sma50: { label: 'SMA50', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' },
|
||||||
sma100: { label: 'SMA-100', step: [500,250,100,50,10,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
|
sma100: { label: 'SMA100', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' },
|
||||||
sma200: { label: 'SMA-200', step: [500,250,100,50,10,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
|
sma200: { label: 'SMA200', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' },
|
||||||
ema20: { label: 'EMA-20', step: [500,250,100,50,10,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
|
ema20: { label: 'EMA20', step: ['Stock Price > EMA20', 'EMA20 > EMA50'], category: 'ta', defaultValue: 'any' },
|
||||||
ema50: { label: 'EMA-50', step: [500,250,100,50,10,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
|
ema50: { label: 'EMA50', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' },
|
||||||
ema100: { label: 'EMA-100', step: [500,250,100,50,10,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
|
ema100: { label: 'EMA100', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' },
|
||||||
ema200: { label: 'EMA-200', step: [500,250,100,50,10,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
|
ema200: { label: 'EMA200', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' },
|
||||||
price: { label: 'Stock Price', step: [1000,500,400,300,200,150,100,80,60,50,20,10,1], category: 'fund', defaultCondition: 'over', defaultValue: 10 },
|
price: { label: 'Stock Price', step: [1000,500,400,300,200,150,100,80,60,50,20,10,1], category: 'fund', defaultCondition: 'over', defaultValue: 10 },
|
||||||
|
|
||||||
change1W: { label: 'Price Change 1W', step: ['20%','10%','5%','1%','-1%','-5%','-10%','-20%'], category: 'ta', defaultCondition: 'over', defaultValue: '1%' },
|
change1W: { label: 'Price Change 1W', step: ['20%','10%','5%','1%','-1%','-5%','-10%','-20%'], category: 'ta', defaultCondition: 'over', defaultValue: '1%' },
|
||||||
@ -160,7 +160,15 @@ const allRules = {
|
|||||||
const getStockScreenerData = async (rules) => {
|
const getStockScreenerData = async (rules) => {
|
||||||
|
|
||||||
console.log('Fetching new data from API');
|
console.log('Fetching new data from API');
|
||||||
const postData = { ruleOfList: rules?.map(rule => rule.name) };
|
// Extract the rule names
|
||||||
|
let getRuleOfList = rules?.map(rule => rule.name) || [];
|
||||||
|
|
||||||
|
// If 'ema20' is included, ensure 'ema50' is also added
|
||||||
|
if (getRuleOfList?.includes("ema20") && !getRuleOfList?.includes("ema50")) {
|
||||||
|
getRuleOfList.push("ema50");
|
||||||
|
}
|
||||||
|
console.log(getRuleOfList)
|
||||||
|
const postData = { ruleOfList: getRuleOfList };
|
||||||
const response = await fetch(data?.apiURL + '/stock-screener-data', {
|
const response = await fetch(data?.apiURL + '/stock-screener-data', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@ -263,6 +271,7 @@ function handleAddRule() {
|
|||||||
case 'analystRating':
|
case 'analystRating':
|
||||||
case 'sector':
|
case 'sector':
|
||||||
case 'country':
|
case 'country':
|
||||||
|
case 'ema20':
|
||||||
newRule = { name: ruleName, value: Array.isArray(valueMappings[ruleName]) ? valueMappings[ruleName] : [valueMappings[ruleName]] }; // Ensure value is an array
|
newRule = { name: ruleName, value: Array.isArray(valueMappings[ruleName]) ? valueMappings[ruleName] : [valueMappings[ruleName]] }; // Ensure value is an array
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -520,7 +529,7 @@ function changeRuleCondition(name: string, state: string) {
|
|||||||
|
|
||||||
async function handleChangeValue(value) {
|
async function handleChangeValue(value) {
|
||||||
// Check if the current rule is "country"
|
// Check if the current rule is "country"
|
||||||
if (['analystRating','sector','country']?.includes(ruleName)) {
|
if (['ema20', 'analystRating','sector','country']?.includes(ruleName)) {
|
||||||
// Ensure valueMappings[ruleName] is initialized as an array
|
// Ensure valueMappings[ruleName] is initialized as an array
|
||||||
searchQuery = '';
|
searchQuery = '';
|
||||||
if (!Array.isArray(valueMappings[ruleName])) {
|
if (!Array.isArray(valueMappings[ruleName])) {
|
||||||
@ -846,7 +855,7 @@ function handleInput(event) {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
<DropdownMenu.Content class="w-56 h-fit max-h-72 overflow-y-auto scroller">
|
<DropdownMenu.Content class="w-56 h-fit max-h-72 overflow-y-auto scroller">
|
||||||
{#if !['analystRating','sector','country']?.includes(row?.rule)}
|
{#if !['ema20', 'analystRating','sector','country']?.includes(row?.rule)}
|
||||||
<DropdownMenu.Label class="absolute mt-2 h-11 border-gray-800 border-b -top-1 z-20 fixed sticky bg-[#09090B]">
|
<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="flex items-center justify-start gap-x-1">
|
||||||
<div class="relative inline-block flex flex-row items-center justify-center">
|
<div class="relative inline-block flex flex-row items-center justify-center">
|
||||||
@ -876,16 +885,33 @@ function handleInput(event) {
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<DropdownMenu.Group class="min-h-10 mt-2">
|
<DropdownMenu.Group class="min-h-10 mt-2">
|
||||||
{#if !['analystRating','sector','country']?.includes(row?.rule)}
|
{#if !['ema20','analystRating','sector','country']?.includes(row?.rule)}
|
||||||
{#each row?.step as newValue}
|
{#each row?.step as newValue}
|
||||||
<DropdownMenu.Item class="sm:hover:bg-[#27272A]">
|
<DropdownMenu.Item class="sm:hover:bg-[#27272A]">
|
||||||
|
|
||||||
<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-[#27272A]
|
<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-[#27272A] focus:bg-blue-100 focus:text-gray-900 focus:outline-none">
|
||||||
focus:bg-blue-100 focus:text-gray-900 focus:outline-none">
|
|
||||||
{ruleCondition[row?.rule] !== undefined ? ruleCondition[row?.rule] : ''} {newValue}
|
{ruleCondition[row?.rule] !== undefined ? ruleCondition[row?.rule] : ''} {newValue}
|
||||||
</button>
|
</button>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
{/each}
|
{/each}
|
||||||
|
{:else if ['ema20']?.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()}>
|
||||||
|
<label on:click={() => {handleChangeValue(item)}} class="cursor-pointer text-white" for={item}>
|
||||||
|
<input type="checkbox" checked={ruleOfList?.some(rule =>
|
||||||
|
rule?.name === row?.rule &&
|
||||||
|
Array?.isArray(rule?.value) &&
|
||||||
|
rule?.value.includes(item))}
|
||||||
|
class="h-4 w-4 rounded bg-dark-500 border border-gray-500 text-blue-600 focus:ring-blue-500" id={ruleOfList?.some(rule =>
|
||||||
|
rule?.name === row?.rule &&
|
||||||
|
Array?.isArray(rule?.value) &&
|
||||||
|
rule?.value.includes(item))}>
|
||||||
|
<span class="ml-2">{item}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
{#each (testList.length > 0 && searchQuery?.length > 0 ? testList : searchQuery?.length > 0 && testList?.length === 0 ? [] : (row?.rule === 'country' ? listOfRelevantCountries : row?.rule === 'sector' ? sectorList : ['Buy','Hold','Sell'])) as item}
|
{#each (testList.length > 0 && searchQuery?.length > 0 ? testList : searchQuery?.length > 0 && testList?.length === 0 ? [] : (row?.rule === 'country' ? listOfRelevantCountries : row?.rule === 'sector' ? sectorList : ['Buy','Hold','Sell'])) as item}
|
||||||
<DropdownMenu.Item class="sm:hover:bg-[#27272A]">
|
<DropdownMenu.Item class="sm:hover:bg-[#27272A]">
|
||||||
@ -1142,6 +1168,8 @@ function handleInput(event) {
|
|||||||
{item?.sector}
|
{item?.sector}
|
||||||
{:else if row?.rule === 'country'}
|
{:else if row?.rule === 'country'}
|
||||||
{item?.country}
|
{item?.country}
|
||||||
|
{:else if row?.rule === 'ema20'}
|
||||||
|
{item?.ema20}
|
||||||
{:else if ['fundamentalAnalysis','trendAnalysis']?.includes(row?.rule)}
|
{:else if ['fundamentalAnalysis','trendAnalysis']?.includes(row?.rule)}
|
||||||
{item[row?.rule]?.accuracy}%
|
{item[row?.rule]?.accuracy}%
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@ -3,19 +3,15 @@ import { sectorList, listOfRelevantCountries } from "$lib/utils";
|
|||||||
// Convert the input to a value or return it as-is if it's already an array
|
// Convert the input to a value or return it as-is if it's already an array
|
||||||
function convertUnitToValue(
|
function convertUnitToValue(
|
||||||
input: string | number | string[]
|
input: string | number | string[]
|
||||||
): number | string[] {
|
): number | string[] | string {
|
||||||
if (Array.isArray(input)) return input;
|
if (Array.isArray(input)) return input;
|
||||||
|
|
||||||
if (typeof input === "number") return input;
|
if (typeof input === "number") return input;
|
||||||
|
|
||||||
if (typeof input !== "string") {
|
if (typeof input !== "string") {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
`Expected a string or number, but received ${typeof input}`
|
`Expected a string or number, but received ${typeof input}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const lowerInput = input.toLowerCase();
|
const lowerInput = input.toLowerCase();
|
||||||
|
|
||||||
// Pre-compute the set for quick lookups
|
// Pre-compute the set for quick lookups
|
||||||
const nonNumericValues = new Set([
|
const nonNumericValues = new Set([
|
||||||
"any",
|
"any",
|
||||||
@ -24,9 +20,9 @@ function convertUnitToValue(
|
|||||||
"hold",
|
"hold",
|
||||||
"sell",
|
"sell",
|
||||||
"buy",
|
"buy",
|
||||||
|
"stock price", // Add "stock price" to non-numeric values
|
||||||
]);
|
]);
|
||||||
|
if (nonNumericValues.has(lowerInput)) return input;
|
||||||
if (nonNumericValues.has(lowerInput)) return "any";
|
|
||||||
|
|
||||||
if (input.endsWith("%")) {
|
if (input.endsWith("%")) {
|
||||||
const numericValue = parseFloat(input.slice(0, -1));
|
const numericValue = parseFloat(input.slice(0, -1));
|
||||||
@ -59,36 +55,68 @@ async function filterStockScreenerData(stockScreenerData, ruleOfList) {
|
|||||||
return ruleOfList.every((rule) => {
|
return ruleOfList.every((rule) => {
|
||||||
const itemValue = item[rule.name];
|
const itemValue = item[rule.name];
|
||||||
const ruleValue = convertUnitToValue(rule.value);
|
const ruleValue = convertUnitToValue(rule.value);
|
||||||
|
const ruleName = rule.name.toLowerCase();
|
||||||
|
|
||||||
if (["trendAnalysis", "fundamentalAnalysis"].includes(rule.name)) {
|
// Handle trend and fundamental analysis
|
||||||
const accuracy = item[rule.name]?.accuracy;
|
if (["trendanalysis", "fundamentalanalysis"].includes(ruleName)) {
|
||||||
|
const accuracy = item[ruleName]?.accuracy;
|
||||||
if (rule.condition === "over" && accuracy <= ruleValue) return false;
|
if (rule.condition === "over" && accuracy <= ruleValue) return false;
|
||||||
if (rule.condition === "under" && accuracy > ruleValue) return false;
|
if (rule.condition === "under" && accuracy > ruleValue) return false;
|
||||||
} else if (["analystRating", "sector", "country"].includes(rule.name)) {
|
return true;
|
||||||
if (rule.value === "any") return true;
|
|
||||||
|
|
||||||
if (Array.isArray(ruleValue) && !ruleValue.includes(itemValue))
|
|
||||||
return false;
|
|
||||||
if (!Array.isArray(ruleValue) && itemValue !== ruleValue) return false;
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
rule.condition === "over" &&
|
|
||||||
itemValue !== null &&
|
|
||||||
itemValue <= ruleValue
|
|
||||||
)
|
|
||||||
return false;
|
|
||||||
if (
|
|
||||||
rule.condition === "under" &&
|
|
||||||
itemValue !== null &&
|
|
||||||
itemValue > ruleValue
|
|
||||||
)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle categorical data like analyst ratings, sector, country
|
||||||
|
if (["analystrating", "sector", "country"].includes(ruleName)) {
|
||||||
|
if (ruleValue === "any") return true;
|
||||||
|
return Array.isArray(ruleValue)
|
||||||
|
? ruleValue.includes(itemValue)
|
||||||
|
: itemValue === ruleValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle moving averages
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
"ema20",
|
||||||
|
"ema50",
|
||||||
|
"ema100",
|
||||||
|
"ema200",
|
||||||
|
"sma20",
|
||||||
|
"sma50",
|
||||||
|
"sma100",
|
||||||
|
"sma200",
|
||||||
|
].includes(ruleName)
|
||||||
|
) {
|
||||||
|
if (ruleValue === "any") return true;
|
||||||
|
|
||||||
|
for (const condition of ruleValue) {
|
||||||
|
if (condition === "Stock Price > EMA20") {
|
||||||
|
const stockPrice = item["price"];
|
||||||
|
const maValue = item["ema20"];
|
||||||
|
if (!stockPrice || !maValue) return false;
|
||||||
|
if (!(stockPrice > maValue)) return false;
|
||||||
|
}
|
||||||
|
if (condition === "EMA20 > EMA50") {
|
||||||
|
const ema20 = item["ema20"];
|
||||||
|
const ema50 = item["ema50"];
|
||||||
|
if (!ema20 || !ema50) return false;
|
||||||
|
if (ema20 < ema50) return false;
|
||||||
|
}
|
||||||
|
// Add additional conditions here
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onmessage = async (event: MessageEvent) => {
|
onmessage = async (event: MessageEvent) => {
|
||||||
const { stockScreenerData, ruleOfList } = event.data || {};
|
const { stockScreenerData, ruleOfList } = event.data || {};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user