add top analyst rule

This commit is contained in:
MuslemRahimi 2024-12-22 11:09:42 +01:00
parent 1e2f545af0
commit 2660c0b822
5 changed files with 70 additions and 89 deletions

View File

@ -146,6 +146,10 @@
rule: "institutionalOwnership", rule: "institutionalOwnership",
type: "percent", type: "percent",
}, },
{ name: "Top Analyst Rating", rule: "topAnalystRating", type: "rating" },
{ name: "Top Analyst Count", rule: "topAnalystCounter", type: "int" },
{ name: "Top Analyst Price Target", rule: "topAnalystPriceTarget", type: "float" },
{ name: "Top Analyst PT Upside", rule: "topAnalystUpside", type: "percentSign" },
]; ];
allRows = [...allRows, ...specificRows]; allRows = [...allRows, ...specificRows];
@ -543,8 +547,8 @@
const preferredOrder = ["rank", "symbol", "name"]; const preferredOrder = ["rank", "symbol", "name"];
// Create a mapping of rule to name and type from allRows // Create a mapping of rule to name and type from allRows
const ruleToMetadataMap = Object.fromEntries( const ruleToMetadataMap = Object?.fromEntries(
allRows.map((row) => [row.rule, { name: row.name, type: row.type }]), allRows?.map((row) => [row.rule, { name: row.name, type: row.type }]),
); );
// Separate preferred keys and other keys, excluding "type" // Separate preferred keys and other keys, excluding "type"

View File

@ -8,19 +8,19 @@
const rawData = data?.getTopAnalystStocks; const rawData = data?.getTopAnalystStocks;
const excludedRules = new Set([ const excludedRules = new Set([
"upside", "topAnalystUpside",
"priceTarget", "topAnalystPriceTarget",
"topAnalystCounter",
"marketCap", "marketCap",
"analystCounter", "topAnalystRating"
"analystRating"
]); ]);
const defaultList = [ const defaultList = [
{ name: "Analyst Count", rule: "analystCounter" }, { name: "Top Analyst Count", rule: "topAnalystCounter" },
{ name: "Upside", rule: "upside" }, { name: "Top Analyst PT Upside", rule: "topAnalystUpside" },
{ name: "Price Target", rule: "priceTarget" }, { name: "Top Analyst Price Target", rule: "topAnalystPriceTarget" },
{ name: "Market Cap", rule: "marketCap" }, {name: 'Top Analyst Rating', rule: 'topAnalystRating'},
{name: 'Analyst Rating', rule: 'analystRating'} {name: 'Market Cap', rule: 'marketCap'}
]; ];
const hideLastRow = true; const hideLastRow = true;
</script> </script>
@ -74,7 +74,7 @@
<div <div
class="relative flex justify-center items-start overflow-hidden w-full" class="relative flex justify-center items-start overflow-hidden w-full"
> >
<main class="w-full lg:w-3/4 lg:pr-5"> <main class="w-full lg:pr-5">
<div class="mb-6 border-b-[2px]"> <div class="mb-6 border-b-[2px]">
<h1 class="mb-1 text-white text-2xl sm:text-3xl font-bold"> <h1 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
Top Strong Buy Stocks Top Strong Buy Stocks
@ -255,66 +255,6 @@
</div> </div>
</main> </main>
<aside class="hidden lg:block relative fixed w-1/4 ml-4">
{#if data?.user?.tier !== "Pro"}
<div
class="w-full text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer"
>
<a
href={"/pricing"}
class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0"
>
<div class="w-full flex justify-between items-center p-3 mt-3">
<h2 class="text-start text-xl font-semibold text-white ml-3">
Pro Subscription
</h2>
<ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0" />
</div>
<span class="text-white p-3 ml-3 mr-3">
Upgrade now for unlimited access to all data and tools
</span>
</a>
</div>
{/if}
<div
class="w-full text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer"
>
<a
href={"/analysts"}
class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0"
>
<div class="w-full flex justify-between items-center p-3 mt-3">
<h2 class="text-start text-xl font-semibold text-white ml-3">
Top Analyst
</h2>
<ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0" />
</div>
<span class="text-white p-3 ml-3 mr-3">
Get the latest top Wall Street analyst ratings
</span>
</a>
</div>
<div
class="w-full text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer"
>
<a
href={"/most-shorted-stocks"}
class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0"
>
<div class="w-full flex justify-between items-center p-3 mt-3">
<h2 class="text-start text-xl font-semibold text-white ml-3">
Top Shorted Stocks
</h2>
<ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0" />
</div>
<span class="text-white p-3 ml-3 mr-3">
Never miss out another short squeeze
</span>
</a>
</div>
</aside>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1296,14 +1296,43 @@
category: "Forecasts, Analysts & Price Targets", category: "Forecasts, Analysts & Price Targets",
}, },
upside: { upside: {
label: "Price Target Upside [%]", label: "Price Target Upside",
step: ["100%", "50%", "20%", "10%", "5%", "0%"], step: ["100%", "50%", "20%", "10%", "5%", "0%"],
defaultCondition: "over", defaultCondition: "over",
defaultValue: "any", defaultValue: "any",
category: "Forecasts, Analysts & Price Targets", category: "Forecasts, Analysts & Price Targets",
}, },
topAnalystRating: {
label: "Top Analyst Rating",
step: ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"],
defaultCondition: "",
defaultValue: "any",
category: "Forecasts, Analysts & Price Targets",
},
topAnalystCounter: {
label: "Top Analyst Count",
step: ["10", "5", "3", "1"],
defaultCondition: "over",
defaultValue: "any",
category: "Forecasts, Analysts & Price Targets",
},
topAnalystUpside: {
label: "Top Analyst Price Target Upside",
step: ["100%", "50%", "20%", "10%", "5%", "0%"],
defaultCondition: "over",
defaultValue: "any",
category: "Forecasts, Analysts & Price Targets",
},
topAnalystPriceTarget: {
label: "Top Analyst Price Target",
step: ["1000", "500", "100", "10", "5", "1"],
defaultCondition: "over",
defaultValue: "any",
category: "Forecasts, Analysts & Price Targets",
},
halalStocks: { halalStocks: {
label: "Halal Stocks", label: "Halal Stocks",
step: ["Compliant", "Non-Compliant"], step: ["Compliant", "Non-Compliant"],
@ -1452,6 +1481,7 @@
?.filter((rule) => ?.filter((rule) =>
[ [
"analystRating", "analystRating",
"topAnalystRating",
"halalStocks", "halalStocks",
"sector", "sector",
"country", "country",
@ -1555,6 +1585,7 @@
?.filter((rule) => ?.filter((rule) =>
[ [
"analystRating", "analystRating",
"topAnalystRating",
"halalStocks", "halalStocks",
"sector", "sector",
"country", "country",
@ -1569,7 +1600,7 @@
} }
function changeRule(state: string) { function changeRule(state: string) {
if (data?.user?.tier !== "Pro" && state === "score") { if (data?.user?.tier !== "Pro" && ['topAnalystRating','topAnalystCounter','topAnalystPriceTarget','topAnalystUpside','score']?.includes(state)) {
goto("/pricing"); goto("/pricing");
} else { } else {
selectedPopularStrategy = ""; selectedPopularStrategy = "";
@ -1628,6 +1659,7 @@
switch (ruleName) { switch (ruleName) {
case "analystRating": case "analystRating":
case "topAnalystRating":
case "halalStocks": case "halalStocks":
case "score": case "score":
case "sector": case "sector":
@ -1862,6 +1894,7 @@ const handleKeyDown = (event) => {
?.filter((rule) => ?.filter((rule) =>
[ [
"analystRating", "analystRating",
"topAnalystRating",
"halalStocks", "halalStocks",
"sector", "sector",
"country", "country",
@ -1949,6 +1982,7 @@ const handleKeyDown = (event) => {
"ema200", "ema200",
"grahamNumber", "grahamNumber",
"analystRating", "analystRating",
"topAnalystRating",
"halalStocks", "halalStocks",
"score", "score",
"sector", "sector",
@ -2112,7 +2146,7 @@ const handleKeyDown = (event) => {
? sectorList ? sectorList
: ruleName === "industry" : ruleName === "industry"
? industryList ? industryList
: ruleName === "analystRating" || ruleName === "score" : ['analystRating','topAnalystRating','score']?.includes(ruleName)
? ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"] ? ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"]
: ["Compliant", "Non-Compliant"]; : ["Compliant", "Non-Compliant"];
testList = testList =
@ -2213,6 +2247,7 @@ const handleKeyDown = (event) => {
"score", "score",
"sector", "sector",
"analystRating", "analystRating",
"topAnalystRating",
"halalStocks", "halalStocks",
]; ];
@ -2797,7 +2832,7 @@ const handleKeyDown = (event) => {
<DropdownMenu.Content <DropdownMenu.Content
class="w-64 min-h-auto max-h-72 overflow-y-auto scroller" class="w-64 min-h-auto 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)} {#if !["sma20", "sma50", "sma100", "sma200", "ema20", "ema50", "ema100", "ema200", "grahamNumber", "analystRating", "topAnalystRating", "halalStocks", "score", "sector", "industry", "country"]?.includes(row?.rule)}
<DropdownMenu.Label <DropdownMenu.Label
class="absolute mt-2 h-11 border-gray-800 border-b -top-1 z-20 fixed sticky bg-[#09090B]" class="absolute mt-2 h-11 border-gray-800 border-b -top-1 z-20 fixed sticky bg-[#09090B]"
> >
@ -2960,6 +2995,7 @@ const handleKeyDown = (event) => {
autocomplete="off" autocomplete="off"
class="{![ class="{![
'analystRating', 'analystRating',
"topAnalystRating",
'halalStocks', 'halalStocks',
'score', 'score',
'sector', 'sector',
@ -2975,7 +3011,7 @@ const handleKeyDown = (event) => {
</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", "grahamNumber", "analystRating", "halalStocks", "score", "sector", "industry", "country"]?.includes(row?.rule)} {#if !["sma20", "sma50", "sma100", "sma200", "ema20", "ema50", "ema100", "ema200", "grahamNumber", "analystRating", "topAnalystRating","halalStocks", "score", "sector", "industry", "country"]?.includes(row?.rule)}
{#each row?.step as newValue, index} {#each row?.step as newValue, index}
{#if ruleCondition[row?.rule] === "between"} {#if ruleCondition[row?.rule] === "between"}
{#if newValue && row?.step[index + 1]} {#if newValue && row?.step[index + 1]}
@ -3044,7 +3080,7 @@ const handleKeyDown = (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 : ruleName === "analystRating" || ruleName === "score" ? ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"] : ["Compliant", "Non-Compliant"] 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 : ['analystRating','topAnalystRating','score']?.includes(ruleName) ? ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"] : ["Compliant", "Non-Compliant"] as item}
<DropdownMenu.Item class="sm:hover:bg-primary"> <DropdownMenu.Item class="sm:hover:bg-primary">
<div <div
class="flex items-center" class="flex items-center"
@ -3285,7 +3321,7 @@ const handleKeyDown = (event) => {
<td <td
class="whitespace-nowrap text-sm sm:text-[1rem] text-end text-white border-b-[#09090B]" class="whitespace-nowrap text-sm sm:text-[1rem] text-end text-white border-b-[#09090B]"
> >
{#if ["ema20", "ema50", "ema100", "ema200", "analystRating", "halalStocks", "score", "sector", "industry", "country"]?.includes(row?.rule)} {#if ["ema20", "ema50", "ema100", "ema200", "analystRating", "topAnalystRating", "halalStocks", "score", "sector", "industry", "country"]?.includes(row?.rule)}
{item[row?.rule]} {item[row?.rule]}
{:else} {:else}
{abbreviateNumber(item[row?.rule])} {abbreviateNumber(item[row?.rule])}
@ -3413,7 +3449,7 @@ const handleKeyDown = (event) => {
> >
{/if} {/if}
{:else if row?.rule === 'analystRating'} {:else if ['analystRating','topAnalystRating']?.includes(row?.rule)}
{#if ["Strong Buy", "Buy"].includes(item[row?.rule])} {#if ["Strong Buy", "Buy"].includes(item[row?.rule])}
<span class="text-[#00FC50]">{item[row?.rule]}</span> <span class="text-[#00FC50]">{item[row?.rule]}</span>
{:else if ["Strong Sell", "Sell"].includes(item[row?.rule])} {:else if ["Strong Sell", "Sell"].includes(item[row?.rule])}
@ -3593,7 +3629,7 @@ const handleKeyDown = (event) => {
<div <div
class="flex w-full items-center space-x-1.5 py-1.5 md:w-1/2 lg:w-1/3 lg:py-1" class="flex w-full items-center space-x-1.5 py-1.5 md:w-1/2 lg:w-1/3 lg:py-1"
> >
{#if row?.rule === "score" && data?.user?.tier !== "Pro"} {#if ['topAnalystRating','topAnalystCounter','topAnalystPriceTarget','topAnalystUpside','score']?.includes(row?.rule) && data?.user?.tier !== "Pro"}
<label id={row?.rule} on:click={() => changeRule(row?.rule)}> <label id={row?.rule} on:click={() => changeRule(row?.rule)}>
<svg <svg
class="w-4 h-4 mb-1 inline-block text-[#A3A3A3] sm:hover:text-white cursor-pointer" class="w-4 h-4 mb-1 inline-block text-[#A3A3A3] sm:hover:text-white cursor-pointer"

View File

@ -112,7 +112,7 @@ function createRuleCheck(rule, ruleName, ruleValue) {
// Categorical checks // Categorical checks
const categoricalFields = [ const categoricalFields = [
'analystRating', 'halalStocks', 'score', 'analystRating', 'topAnalystRating', 'halalStocks', 'score',
'sector', 'industry', 'country' 'sector', 'industry', 'country'
]; ];

View File

@ -17,9 +17,9 @@
let rawData = data?.getAnalystTickerHistory ?? []; let rawData = data?.getAnalystTickerHistory ?? [];
let historyList = []; let historyList = [];
let priceTarget = "n/a"; let priceTarget = "n/a";
let numOfAnalyst = 0; let numOfAnalyst = "n/a";
let consensusRating = "n/a"; let consensusRating = "n/a";
let changesPercentage = 0; let changesPercentage = "n/a";
const tabs = [ const tabs = [
{ {
@ -62,7 +62,7 @@
const key = `${entry.analyst}-${entry.name}`; const key = `${entry.analyst}-${entry.name}`;
// Convert date and time to a Date object // Convert date and time to a Date object
const dateTimeStr = `${entry.date} ${entry.time}`; const dateTimeStr = `${entry.date}`;
const dateTime = new Date(dateTimeStr); const dateTime = new Date(dateTimeStr);
// Check if this entry is the latest for the given key // Check if this entry is the latest for the given key
@ -96,7 +96,7 @@
}) })
?.slice(0, 30); //Consider only the last 30 ratings in the last 12 months ?.slice(0, 30); //Consider only the last 30 ratings in the last 12 months
const filteredAnalystCount = recentData?.length; const filteredAnalystCount = recentData?.length ?? 'n/a';
const priceTargets = recentData const priceTargets = recentData
?.map((item) => parseFloat(item.adjusted_pt_current)) ?.map((item) => parseFloat(item.adjusted_pt_current))
?.filter((pt) => !isNaN(pt)); ?.filter((pt) => !isNaN(pt));
@ -104,9 +104,9 @@
? priceTargets?.sort((a, b) => a - b)[ ? priceTargets?.sort((a, b) => a - b)[
Math.floor(priceTargets?.length / 2) Math.floor(priceTargets?.length / 2)
] ]
: "-"; : "n/a";
numOfAnalyst = filteredAnalystCount; numOfAnalyst = filteredAnalystCount === 0 ? 'n/a' : filteredAnalystCount;
priceTarget = medianPriceTarget; priceTarget = medianPriceTarget;
changesPercentage = changesPercentage =
medianPriceTarget !== "-" && data?.getStockQuote?.price != null medianPriceTarget !== "-" && data?.getStockQuote?.price != null
@ -141,7 +141,8 @@
? "Hold" ? "Hold"
: averageRatingScore >= 1.5 : averageRatingScore >= 1.5
? "Sell" ? "Sell"
: "Strong Sell"; : averageRatingScore >= 1
? "Strong Sell" : 'n/a';
rawData = recentData; rawData = recentData;
historyList = rawData.slice(0, 50); historyList = rawData.slice(0, 50);