From e307dd19d8faf2f14a8cdcf30b0251b6fb453e67 Mon Sep 17 00:00:00 2001 From: MuslemRahimi Date: Tue, 15 Oct 2024 12:49:43 +0200 Subject: [PATCH] update watchlist --- src/routes/watchlist/stocks/+page.svelte | 248 ++++++++++++++--------- 1 file changed, 157 insertions(+), 91 deletions(-) diff --git a/src/routes/watchlist/stocks/+page.svelte b/src/routes/watchlist/stocks/+page.svelte index b6248ee8..656f9254 100644 --- a/src/routes/watchlist/stocks/+page.svelte +++ b/src/routes/watchlist/stocks/+page.svelte @@ -10,6 +10,9 @@ import { Button } from "$lib/components/shadcn/button/index.js"; import { Combobox } from "bits-ui"; export let data; + +let currentWatchlistLocalStorage = '1.0'; // Increment this whenever the structure of allRows changes + let searchQuery = ''; let editMode = false; let numberOfChecked = 0; @@ -19,33 +22,61 @@ let watchList: any[] = []; let news = []; let checkedItems; - let allRows = [ - { name: 'Volume', rule: 'volume' }, - { name: 'Market Cap', rule: 'marketCap' }, - { name: 'Price', rule: 'price' }, - { name: 'Change', rule: 'changesPercentage' }, - { name: 'EPS', rule: 'eps' }, - { name: 'PE', rule: 'pe' }, - { name: 'AI Score', rule: 'score' }, - { name: 'Revenue', rule: 'revenue'}, - { name: 'Net Income', rule: 'netIncome'}, - { name: 'Free Cash Flow', rule: 'freeCashFlow'}, - { name: 'Industry', rule: 'industry'}, - { name: 'Sector', rule: 'sector'}, - { name: 'Price Change 1W', rule: 'change1W' }, - { name: 'Price Change 1M', rule: 'change1M' }, - { name: 'Price Change 3M', rule: 'change3M' }, - { name: 'Price Change 6M', rule: 'change6M' }, - { name: 'Price Change 1Y', rule: 'change1Y' }, - + { name: 'Volume', rule: 'volume', type: 'int'}, + { name: 'Market Cap', rule: 'marketCap', type: 'int'}, + { name: 'Price', rule: 'price', type: 'float'}, + { name: 'Change', rule: 'changesPercentage', type: 'percentSign'}, + { name: 'EPS', rule: 'eps', type: 'float'}, + { name: 'PE', rule: 'pe', type: 'float'}, + { name: 'AI Score', rule: 'score', type: 'rating'}, + { name: 'Revenue', rule: 'revenue', type: 'int'}, + { name: 'EBITDA', rule: 'ebitda', type: 'int'}, + { name: 'Net Income', rule: 'netIncome', type: 'int'}, + { name: 'FCF', rule: 'freeCashFlow', type: 'int'}, + { name: 'Industry', rule: 'industry', type: 'str'}, + { name: 'Sector', rule: 'sector', type: 'str'}, + { name: 'Price Change 1W', rule: 'change1W', type: 'percentSign'}, + { name: 'Price Change 1M', rule: 'change1M', type: 'percentSign'}, + { name: 'Price Change 3M', rule: 'change3M', type: 'percentSign'}, + { name: 'Price Change 6M', rule: 'change6M',type: 'percentSign'}, + { name: 'Price Change 1Y', rule: 'change1Y', type: 'percentSign'}, + { name: 'Enterprise Value', rule: 'enterpriseValue', type: 'int'}, + { name: 'Forward PE', rule: 'forwardPE', type: 'float'}, + { name: 'Forward PS', rule: 'forwardPS', type: 'float'}, + { name: 'Dividend Yield', rule: 'dividendYield', type: 'percent'}, + { name: 'Current Ratio', rule: 'currentRatio', type: 'float'}, + { name: 'Quick Ratio', rule: 'quickRatio', type: 'float'}, + { name: 'Analyst Rating', rule: 'analystRating', type: 'rating'}, + { name: 'Country', rule: 'country', type: 'str'}, + { name: 'Gross Profit', rule: 'grossProfit', type: 'int'}, + { name: 'Revenue Growth', rule: 'growthRevenue', type: 'percentSign'}, + { name: 'Gross Profit Growth', rule: 'growthGrossProfit', type: 'percentSign'}, + { name: 'Net Income Growth', rule: 'growthNetIncome', type: 'percentSign'}, + { name: 'EBITDA Growth', rule: 'growthEBITDA', type: 'percentSign'}, + { name: 'EPS Growth', rule: 'growthEPS', type: 'percentSign'}, + { name: 'Total Debt', rule: 'totalDebt', type: 'int'}, + { name: 'Return on Assets', rule: 'returnOnAssets', type: 'int'}, + { name: 'Return on Equity', rule: 'returnOnEquity', type: 'int'}, + { name: 'Value-at-Risk', rule: 'var', type: 'percentSign'}, + { name: 'Asset Turnover', rule: 'assetTurnover', type: 'int'}, + { name: 'Earnings Yield', rule: 'earningsYield', type: 'percent'}, + { name: 'Altman-Z-Score Yield', rule: 'altmanZScore', type: 'float'}, + { name: 'Piotroski F-Score', rule: 'piotroskiScore', type: 'float'}, + { name: 'Total Liabilities', rule: 'totalLiabilities', type: 'int'}, + { name: 'Short Ratio', rule: 'shortRatio', type: 'int'}, + { name: 'FCF Yield', rule: 'freeCashFlowYield', type: 'percent'}, + { name: 'Employees', rule: 'employees', type: 'int'}, + { name: 'Debt Ratio', rule: 'debtRatio', type: 'float'}, + { name: 'Debt / Equity', rule: 'debtEquityRatio', type: 'int'}, ]; + let ruleOfList = [ - { name: 'Volume', rule: 'volume' }, - { name: 'Market Cap', rule: 'marketCap' }, - { name: 'Price', rule: 'price' }, - { name: 'Change', rule: 'changesPercentage' }, + { name: 'Volume', rule: 'volume', type: 'int' }, + { name: 'Market Cap', rule: 'marketCap', type: 'int' }, + { name: 'Price', rule: 'price', type: 'float' }, + { name: 'Change', rule: 'changesPercentage', type: 'percent' }, ]; const excludedRules = new Set(['volume', 'price', 'changesPercentage', 'eps']); @@ -348,7 +379,19 @@ if (!watchList?.some(item => item?.symbol === ticker)) { } - +async function handleResetAll() { + searchQuery = ''; + ruleOfList = [ + { name: 'Volume', rule: 'volume', type: 'int' }, + { name: 'Market Cap', rule: 'marketCap', type: 'int' }, + { name: 'Price', rule: 'price', type: 'float' }, + { name: 'Change', rule: 'changesPercentage', type: 'percent' }, + ]; + ruleOfList = [...ruleOfList]; + checkedItems = new Set(ruleOfList.map(item => item.name)); + allRows = sortIndicatorCheckMarks(allRows); + saveRules() +} function changeWatchList(newWatchList) { @@ -359,49 +402,57 @@ function changeWatchList(newWatchList) function saveRules() { try { + // Save the version along with the rules localStorage?.setItem('watchlist-ruleOfList', JSON?.stringify(ruleOfList)); - } catch(e) { - console.log('Failed saving indicator rules: ', e) + localStorage?.setItem('watchlist-ruleOfList-version', currentWatchlistLocalStorage); // Save the current version + } catch (e) { + console.log('Failed saving indicator rules: ', e); } } - onMount(async () => { try { const savedRules = localStorage?.getItem('watchlist-ruleOfList'); - if (savedRules) { + const savedVersion = localStorage?.getItem('watchlist-ruleOfList-version'); + + // If the version doesn't match, reset the local storage + if (savedVersion !== currentWatchlistLocalStorage) { + localStorage?.removeItem('watchlist-ruleOfList'); // Clear old data + localStorage?.setItem('watchlist-ruleOfList-version', currentWatchlistLocalStorage); // Save new version + localStorage?.setItem('watchlist-ruleOfList', JSON?.stringify(ruleOfList)); // Save new rules + } else if (savedRules) { ruleOfList = JSON.parse(savedRules); + + // Check for the user's tier and filter out paywalled features if (data?.user?.tier !== 'Pro') { - //Check if user was Pro Member and has past checks of previous paywalled features. If so remove them from the ruleOfList ruleOfList = ruleOfList.filter(item => excludedRules.has(item?.rule)); // Use Set to filter - console.log(ruleOfList) } } - } catch(e) { - console.log(e) - } - checkedItems = new Set(ruleOfList.map(item => item.name)) - allRows = sortIndicatorCheckMarks(allRows) + // Update checked items and sort the indicators + checkedItems = new Set(ruleOfList.map(item => item.name)); + allRows = sortIndicatorCheckMarks(allRows); -if(allList?.length !== 0) - { - displayWatchList = allList?.at(0) - } - else { + // Display the first watchlist if available + if (allList?.length !== 0) { + displayWatchList = allList?.at(0); + } else { displayWatchList = ''; } - await getWatchlistData(); - - if (!downloadWorker) { - const DownloadWorker = await import('./workers/downloadWorker?worker'); - downloadWorker = new DownloadWorker.default(); - downloadWorker.onmessage = handleDownloadMessage; - } - - - isLoaded = true; + await getWatchlistData(); + // Initialize the download worker if not already done + if (!downloadWorker) { + const DownloadWorker = await import('./workers/downloadWorker?worker'); + downloadWorker = new DownloadWorker.default(); + downloadWorker.onmessage = handleDownloadMessage; + } + + isLoaded = true; + + } catch (e) { + console.log(e); + } }); onDestroy( () => { @@ -704,51 +755,64 @@ function search() {
- - - - - - {#each (searchQuery?.length !== 0 ? testList : allRows) as item} - -
- {#if (data?.user?.tier === 'Pro') || excludedRules?.has(item?.rule)} - - {:else} - - - - - {item?.name} - - {/if} -
-
+ + +
+
+ + + + + {#if searchQuery?.length > 0} + + {/if} +
+
+ + + {#each (searchQuery?.length !== 0 ? testList : allRows) as item} + +
+ {#if (data?.user?.tier === 'Pro') || excludedRules?.has(item?.rule)} + + {:else} + + + + + {item?.name} + + {/if} +
+
{/each}
+ +
+ +
+
@@ -825,19 +889,21 @@ function search() { {#if isChecked(row?.name)} {#if item?.[row?.rule] !== undefined && item?.[row?.rule] !== null} - {#if ['marketCap', 'volume','revenue','netIncome','freeCashFlow'].includes(row?.rule)} + {#if row?.type === 'int'} {abbreviateNumber(item[row?.rule])} - {:else if ['industry','sector'].includes(row?.rule)} + {:else if row?.type === 'str'} {item[row?.rule] !== null ? item[row?.rule] : '-'} - {:else if ['eps', 'pe', 'price','freeCashFlow'].includes(row?.rule)} + {:else if row?.type === 'float'} {item[row?.rule] !== null ? item[row?.rule]?.toFixed(2) : '-'} - {:else if ['changesPercentage','change1W','change1M','change3M','change6M','change1Y','change3Y'].includes(row?.rule)} + {:else if row?.type === 'percent'} + {item[row?.rule] !== null ? item[row?.rule]?.toFixed(2)+'%' : '-'} + {:else if row?.type === 'percentSign'} {#if item[row?.rule] >= 0} +{item[row?.rule]?.toFixed(2)}% {:else} {item[row?.rule]?.toFixed(2)}% {/if} - {:else if "score" === row?.rule} + {:else if row?.type === 'rating'} {#if ['Strong Buy', 'Buy'].includes(item[row?.rule])} {item[row?.rule]} {:else if ['Strong Sell', 'Sell'].includes(item[row?.rule])}