add ai score to screener

This commit is contained in:
MuslemRahimi 2024-10-01 01:27:46 +02:00
parent f9ce2b0dbb
commit 755fa5e8b9
2 changed files with 17 additions and 11 deletions

View File

@ -172,6 +172,7 @@ const allRules = {
piotroskiScore: { label: 'Piotroski F-Score', step: [9,8,7,6,5,4,3,2,1], category: 'fund', defaultCondition: 'over', defaultValue: 'any' }, piotroskiScore: { label: 'Piotroski F-Score', step: [9,8,7,6,5,4,3,2,1], category: 'fund', defaultCondition: 'over', defaultValue: 'any' },
analystRating: { label: 'Analyst Rating', step: ['Buy', 'Hold', 'Sell'], category: 'fund', defaultCondition: '', defaultValue: 'any' }, analystRating: { label: 'Analyst Rating', step: ['Buy', 'Hold', 'Sell'], category: 'fund', defaultCondition: '', defaultValue: 'any' },
score: { label: 'AI Score', step: ['Strong Buy', 'Buy', 'Hold', 'Sell', 'Strong Sell'], category: 'fund', defaultCondition: '', defaultValue: 'any' },
sector: { label: 'Sector', step: sectorList, category: 'fund', defaultCondition: '', defaultValue: 'any' }, sector: { label: 'Sector', step: sectorList, category: 'fund', defaultCondition: '', defaultValue: 'any' },
industry: { label: 'Industry', step: industryList, category: 'fund', defaultCondition: '', defaultValue: 'any' }, industry: { label: 'Industry', step: industryList, category: 'fund', defaultCondition: '', defaultValue: 'any' },
country: { label: 'Country', step: listOfRelevantCountries, category: 'fund', defaultCondition: '', defaultValue: 'any' }, country: { label: 'Country', step: listOfRelevantCountries, category: 'fund', defaultCondition: '', defaultValue: 'any' },
@ -421,6 +422,7 @@ function handleAddRule() {
switch (ruleName) { switch (ruleName) {
case 'analystRating': case 'analystRating':
case 'score':
case 'sector': case 'sector':
case 'industry': case 'industry':
case 'country': case 'country':
@ -695,7 +697,7 @@ async function handleChangeValue(value) {
} else { } else {
checkedItems.add(value); checkedItems.add(value);
} }
if (['sma20','sma50','sma100','sma200','ema20', 'ema50', 'ema100', 'ema200','analystRating','sector','industry','country']?.includes(ruleName)) { if (['sma20','sma50','sma100','sma200','ema20', 'ema50', 'ema100', 'ema200','analystRating','score','sector','industry','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])) {
@ -821,7 +823,7 @@ function handleInput(event) {
if (searchQuery.length > 0) { if (searchQuery.length > 0) {
const rawList = ruleName === 'country' ? listOfRelevantCountries : ruleName === 'sector' ? sectorList : ruleName === 'industry' ? industryList : ['Buy','Hold','Sell']; const rawList = ruleName === 'country' ? listOfRelevantCountries : ruleName === 'sector' ? sectorList : ruleName === 'industry' ? industryList : ruleName === 'analystRating' ? ['Buy','Hold','Sell'] : ['Strong Buy','Hold','Sell','Strong Sell'];
testList = rawList?.filter(item => { testList = rawList?.filter(item => {
const index = item?.toLowerCase(); const index = item?.toLowerCase();
// Check if country starts with searchQuery // Check if country starts with searchQuery
@ -1106,7 +1108,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 !['sma20','sma50','sma100','sma200','ema20', 'ema50', 'ema100', 'ema200', 'analystRating','sector','industry','country']?.includes(row?.rule)} {#if !['sma20','sma50','sma100','sma200','ema20', 'ema50', 'ema100', 'ema200', 'analystRating','score','sector','industry','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">
@ -1129,14 +1131,14 @@ function handleInput(event) {
<input bind:value={searchQuery} <input bind:value={searchQuery}
on:input={handleInput} on:input={handleInput}
autocomplete="off" autocomplete="off"
class="{!['analystRating','sector','industry','country']?.includes(row?.rule) ? 'hidden' : ''} absolute fixed sticky w-full border-0 bg-[#09090B] border-b border-gray-200 class="{!['analystRating','score','sector','industry','country']?.includes(row?.rule) ? 'hidden' : ''} absolute fixed sticky w-full border-0 bg-[#09090B] border-b border-gray-200
focus:border-gray-200 focus:ring-0 text-white placeholder:text-gray-300" focus:border-gray-200 focus:ring-0 text-white placeholder:text-gray-300"
type="search" type="search"
placeholder="Search..."> placeholder="Search...">
</div> </div>
{/if} {/if}
<DropdownMenu.Group class="min-h-10 mt-2"> <DropdownMenu.Group class="min-h-10 mt-2">
{#if !['sma20','sma50','sma100','sma200','ema20', 'ema50', 'ema100', 'ema200', 'analystRating','sector','industry','country']?.includes(row?.rule)} {#if !['sma20','sma50','sma100','sma200','ema20', 'ema50', 'ema100', 'ema200', 'analystRating','score','sector','industry','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]">
@ -1157,7 +1159,7 @@ function handleInput(event) {
</DropdownMenu.Item> </DropdownMenu.Item>
{/each} {/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 : row?.rule === 'industry' ? industryList : ['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 : row?.rule === 'industry' ? industryList : ruleName === 'analystRating' ? ['Buy','Hold','Sell'] : ['Strong Buy','Buy','Hold','Sell','Strong Sell'])) as item}
<DropdownMenu.Item class="sm:hover:bg-[#27272A]"> <DropdownMenu.Item class="sm:hover:bg-[#27272A]">
<div class="flex items-center" on:click|capture={(event) => event.preventDefault()}> <div class="flex items-center" on:click|capture={(event) => event.preventDefault()}>
<label on:click={() => {handleChangeValue(item)}} class="cursor-pointer text-white" for={item}> <label on:click={() => {handleChangeValue(item)}} class="cursor-pointer text-white" for={item}>
@ -1399,7 +1401,7 @@ function handleInput(event) {
{#each displayRules as row (row?.rule)} {#each displayRules as row (row?.rule)}
{#if row?.rule !== 'marketCap'} {#if row?.rule !== 'marketCap'}
<td class="whitespace-nowrap text-sm sm:text-[1rem] text-end text-white border-b-[#09090B]"> <td class="whitespace-nowrap text-sm sm:text-[1rem] text-end text-white border-b-[#09090B]">
{#if ['ema20', 'ema50', 'ema100', 'ema200', 'analystRating','sector','industry','country','analystRating']?.includes(row?.rule)} {#if ['ema20', 'ema50', 'ema100', 'ema200', 'analystRating','score','sector','industry','country']?.includes(row?.rule)}
{item[row?.rule]} {item[row?.rule]}
{:else if ['fundamentalAnalysis','trendAnalysis']?.includes(row?.rule)} {:else if ['fundamentalAnalysis','trendAnalysis']?.includes(row?.rule)}
{item[row?.rule]?.accuracy}% {item[row?.rule]?.accuracy}%

View File

@ -41,13 +41,13 @@ const movingAverageConditions = {
// 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[] | 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();
@ -60,6 +60,8 @@ function convertUnitToValue(
"hold", "hold",
"sell", "sell",
"buy", "buy",
"strong buy",
"strong sell",
"stock price", // Add "stock price" to non-numeric values "stock price", // Add "stock price" to non-numeric values
]); ]);
if (nonNumericValues.has(lowerInput)) return input; if (nonNumericValues.has(lowerInput)) return input;
@ -106,7 +108,9 @@ async function filterStockScreenerData(stockScreenerData, ruleOfList) {
// Handle categorical data like analyst ratings, sector, country // Handle categorical data like analyst ratings, sector, country
else if ( else if (
["analystRating", "sector", "industry", "country"].includes(rule.name) ["analystRating", "score", "sector", "industry", "country"].includes(
rule.name
)
) { ) {
if (rule.value === "any") return true; if (rule.value === "any") return true;
@ -157,7 +161,7 @@ onmessage = async (event: MessageEvent) => {
const filteredData = await filterStockScreenerData( const filteredData = await filterStockScreenerData(
stockScreenerData, stockScreenerData,
ruleOfList, ruleOfList
); );
postMessage({ message: "success", filteredData }); postMessage({ message: "success", filteredData });