diff --git a/src/routes/stock-screener/[strategyId]/+page.svelte b/src/routes/stock-screener/[strategyId]/+page.svelte index 315b40e6..8957c937 100644 --- a/src/routes/stock-screener/[strategyId]/+page.svelte +++ b/src/routes/stock-screener/[strategyId]/+page.svelte @@ -47,10 +47,10 @@ const allRules = { sma50: { label: 'SMA50', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' }, sma100: { label: 'SMA100', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' }, sma200: { label: 'SMA200', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' }, - ema20: { label: 'EMA20', step: ['Stock Price > EMA20', 'EMA20 > EMA50'], category: 'ta', defaultValue: 'any' }, - ema50: { label: 'EMA50', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' }, - ema100: { label: 'EMA100', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' }, - ema200: { label: 'EMA200', step: ['Stock Price'], category: 'ta', defaultCondition: 'over', defaultValue: 'Stock Price' }, + ema20: { label: 'EMA20', step: ['Stock Price > EMA20', 'EMA20 > EMA50', 'EMA20 > EMA100', 'EMA20 > EMA200'], category: 'ta', defaultValue: 'any' }, + ema50: { label: 'EMA50', step: ['Stock Price > EMA50', 'EMA50 > EMA20', 'EMA50 > EMA100', 'EMA50 > EMA200'], category: 'ta', defaultCondition: 'over', defaultValue: 'any' }, + ema100: { label: 'EMA100', step: ['Stock Price > EMA100', 'EMA100 > EMA20', 'EMA100 > EMA50', 'EMA100 > EMA200'], category: 'ta', defaultCondition: 'over', defaultValue: 'any' }, + ema200: { label: 'EMA200', step: ['Stock Price > EMA200', 'EMA200 > EMA20', 'EMA200 > EMA50', 'EMA200 > EMA100'], category: 'ta', defaultCondition: 'over', defaultValue: 'any' }, 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%' }, @@ -158,16 +158,31 @@ const allRules = { const getStockScreenerData = async (rules) => { - console.log('Fetching new data from API'); + // 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) + // Define the EMA parameters to check + const emaParameters = ['ema20', 'ema50', 'ema100', 'ema200']; + + // Function to check and add missing EMA parameters + const ensureAllEmaParameters = (params) => { + const includedEmaParameters = params.filter(param => emaParameters.includes(param)); + if (includedEmaParameters.length > 0) { + emaParameters.forEach(param => { + if (!params.includes(param)) { + params.push(param); + } + }); + } + }; + + // Ensure all required EMA parameters are included + ensureAllEmaParameters(getRuleOfList); + + console.log(getRuleOfList); + const postData = { ruleOfList: getRuleOfList }; const response = await fetch(data?.apiURL + '/stock-screener-data', { method: 'POST', @@ -177,11 +192,13 @@ const getStockScreenerData = async (rules) => { }, body: JSON.stringify(postData) }); + const output = await response.json(); return output; }; + let filteredData = []; let displayResults = []; let isSaved = false; @@ -272,6 +289,9 @@ function handleAddRule() { case 'sector': case 'country': case 'ema20': + case 'ema50': + case 'ema100': + case 'ema200': newRule = { name: ruleName, value: Array.isArray(valueMappings[ruleName]) ? valueMappings[ruleName] : [valueMappings[ruleName]] }; // Ensure value is an array break; default: @@ -529,7 +549,7 @@ function changeRuleCondition(name: string, state: string) { async function handleChangeValue(value) { // Check if the current rule is "country" - if (['ema20', 'analystRating','sector','country']?.includes(ruleName)) { + if (['ema20', 'ema50', 'ema100', 'ema200','analystRating','sector','country']?.includes(ruleName)) { // Ensure valueMappings[ruleName] is initialized as an array searchQuery = ''; if (!Array.isArray(valueMappings[ruleName])) { @@ -855,7 +875,7 @@ function handleInput(event) { - {#if !['ema20', 'analystRating','sector','country']?.includes(row?.rule)} + {#if !['ema20', 'ema50', 'ema100', 'ema200', 'analystRating','sector','country']?.includes(row?.rule)}
@@ -885,7 +905,7 @@ function handleInput(event) {
{/if} - {#if !['ema20','analystRating','sector','country']?.includes(row?.rule)} + {#if !['ema20', 'ema50', 'ema100', 'ema200', 'analystRating','sector','country']?.includes(row?.rule)} {#each row?.step as newValue} @@ -894,7 +914,7 @@ function handleInput(event) { {/each} - {:else if ['ema20']?.includes(row?.rule)} + {:else if ['ema20', 'ema50', 'ema100', 'ema200']?.includes(row?.rule)} {#each row?.step as item}
event.preventDefault()}> @@ -1162,14 +1182,8 @@ function handleInput(event) { {#each displayRules as row (row?.rule)} {#if row?.rule !== 'marketCap'} - {#if row?.rule === 'analystRating'} - {item?.analystRating} - {:else if row?.rule === 'sector'} - {item?.sector} - {:else if row?.rule === 'country'} - {item?.country} - {:else if row?.rule === 'ema20'} - {item?.ema20} + {#if ['ema20', 'ema50', 'ema100', 'ema200', 'analystRating','sector','country','analystRating']?.includes(row?.rule)} + {item[row?.rule]} {:else if ['fundamentalAnalysis','trendAnalysis']?.includes(row?.rule)} {item[row?.rule]?.accuracy}% {:else} diff --git a/src/routes/stock-screener/[strategyId]/+page.ts b/src/routes/stock-screener/[strategyId]/+page.ts index 1c4383f7..4c4e5524 100644 --- a/src/routes/stock-screener/[strategyId]/+page.ts +++ b/src/routes/stock-screener/[strategyId]/+page.ts @@ -1,62 +1,76 @@ -import { getCache, setCache } from '$lib/store'; +import { getCache, setCache } from "$lib/store"; +// Define the EMA parameters to check +const emaParameters = ["ema20", "ema50", "ema100", "ema200"]; +// Function to check and add missing EMA parameters +const ensureAllEmaParameters = (params) => { + const includedEmaParameters = params.filter((param) => + emaParameters.includes(param) + ); + if (includedEmaParameters.length > 0) { + emaParameters.forEach((param) => { + if (!params.includes(param)) { + params.push(param); + } + }); + } +}; +export const load = async ({ parent, params }) => { + const { apiURL, apiKey, fastifyURL } = await parent(); + const getStrategyId = async () => { + return params.strategyId; + }; -export const load = async ({parent, params}) => { + const getStrategy = async () => { + let output; + // make the POST request to the endpoint + const postData = { strategyId: params.strategyId }; + const response = await fetch(fastifyURL + "/get-strategy", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(postData), + }); - const { apiURL, apiKey, fastifyURL} = await parent(); + output = (await response.json())?.items; - const getStrategyId = async () => { - return params.strategyId; - }; + return output; + }; - const getStrategy = async () => { - let output; - - // make the POST request to the endpoint - const postData = {'strategyId': params.strategyId} - const response = await fetch(fastifyURL+'/get-strategy', { - method: 'POST', - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(postData) - }); - - output = (await response.json())?.items; - - return output; - }; - - - const getStockScreenerData = async () => { let output; const strategy = await getStrategy(); - const ruleOfList = strategy?.rules?.map(item => item?.name) || []; - const ruleNames = ruleOfList.sort().join(','); + let getRuleOfList = strategy?.rules?.map((item) => item?.name) || []; + + // Ensure all required EMA parameters are included + ensureAllEmaParameters(getRuleOfList); + + const ruleNames = getRuleOfList.sort().join(","); + // Get cached data for the specific tickerID - const cachedData = getCache(ruleNames, 'getStockScreenerData'); + const cachedData = getCache(ruleNames, "getStockScreenerData"); if (cachedData) { output = cachedData; } else { - - const postData = {'ruleOfList': ruleOfList} + const postData = { ruleOfList: getRuleOfList }; // make the POST request to the endpoint - const response = await fetch(apiURL + '/stock-screener-data', { - method: 'POST', + const response = await fetch(apiURL + "/stock-screener-data", { + method: "POST", headers: { - "Content-Type": "application/json", "X-API-KEY": apiKey + "Content-Type": "application/json", + "X-API-KEY": apiKey, }, - body: JSON.stringify(postData) + body: JSON.stringify(postData), }); output = await response.json(); // Cache the data for this specific tickerID with a specific name 'getStockScreenerData' - setCache(ruleNames, output, 'getStockScreenerData'); + setCache(ruleNames, output, "getStockScreenerData"); } return output; @@ -68,4 +82,4 @@ export const load = async ({parent, params}) => { getStrategy: await getStrategy(), getStrategyId: await getStrategyId(), }; -}; \ No newline at end of file +}; diff --git a/src/routes/stock-screener/[strategyId]/workers/filterWorker.ts b/src/routes/stock-screener/[strategyId]/workers/filterWorker.ts index 4133c8eb..15229080 100644 --- a/src/routes/stock-screener/[strategyId]/workers/filterWorker.ts +++ b/src/routes/stock-screener/[strategyId]/workers/filterWorker.ts @@ -1,5 +1,25 @@ import { sectorList, listOfRelevantCountries } from "$lib/utils"; +const movingAverageConditions = { + "Stock Price > EMA20": (item) => item.price > item.ema20, + "Stock Price > EMA50": (item) => item.price > item.ema50, + "Stock Price > EMA100": (item) => item.price > item.ema100, + "Stock Price > EMA200": (item) => item.price > item.ema200, + "EMA20 > EMA50": (item) => item.ema20 > item.ema50, + "EMA20 > EMA100": (item) => item.ema20 > item.ema100, + "EMA20 > EMA200": (item) => item.ema20 > item.ema200, + "EMA50 > EMA20": (item) => item.ema50 > item.ema20, + "EMA50 > EMA100": (item) => item.ema50 > item.ema100, + "EMA50 > EMA200": (item) => item.ema50 > item.ema200, + "EMA100 > EMA20": (item) => item.ema100 > item.ema20, + "EMA100 > EMA50": (item) => item.ema100 > item.ema50, + "EMA100 > EMA200": (item) => item.ema100 > item.ema200, + "EMA200 > EMA20": (item) => item.ema200 > item.ema20, + "EMA200 > EMA50": (item) => item.ema200 > item.ema50, + "EMA200 > EMA100": (item) => item.ema200 > item.ema100, + // Add additional conditions here +}; + // Convert the input to a value or return it as-is if it's already an array function convertUnitToValue( input: string | number | string[] @@ -58,15 +78,14 @@ async function filterStockScreenerData(stockScreenerData, ruleOfList) { const ruleName = rule.name.toLowerCase(); // Handle trend and fundamental analysis - if (["trendanalysis", "fundamentalanalysis"].includes(ruleName)) { - const accuracy = item[ruleName]?.accuracy; + 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; - return true; } // Handle categorical data like analyst ratings, sector, country - if (["analystrating", "sector", "country"].includes(ruleName)) { + else if (["analystrating", "sector", "country"].includes(ruleName)) { if (ruleValue === "any") return true; return Array.isArray(ruleValue) ? ruleValue.includes(itemValue) @@ -74,7 +93,7 @@ async function filterStockScreenerData(stockScreenerData, ruleOfList) { } // Handle moving averages - if ( + else if ( [ "ema20", "ema50", @@ -89,19 +108,11 @@ async function filterStockScreenerData(stockScreenerData, ruleOfList) { 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 (movingAverageConditions[condition]) { + if (!movingAverageConditions[condition](item)) return false; + } else { + console.warn(`Unknown condition: ${condition}`); } - 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 @@ -117,6 +128,7 @@ async function filterStockScreenerData(stockScreenerData, ruleOfList) { }); }); } + onmessage = async (event: MessageEvent) => { const { stockScreenerData, ruleOfList } = event.data || {};