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",
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];
@ -543,8 +547,8 @@
const preferredOrder = ["rank", "symbol", "name"];
// Create a mapping of rule to name and type from allRows
const ruleToMetadataMap = Object.fromEntries(
allRows.map((row) => [row.rule, { name: row.name, type: row.type }]),
const ruleToMetadataMap = Object?.fromEntries(
allRows?.map((row) => [row.rule, { name: row.name, type: row.type }]),
);
// Separate preferred keys and other keys, excluding "type"

View File

@ -8,19 +8,19 @@
const rawData = data?.getTopAnalystStocks;
const excludedRules = new Set([
"upside",
"priceTarget",
"topAnalystUpside",
"topAnalystPriceTarget",
"topAnalystCounter",
"marketCap",
"analystCounter",
"analystRating"
"topAnalystRating"
]);
const defaultList = [
{ name: "Analyst Count", rule: "analystCounter" },
{ name: "Upside", rule: "upside" },
{ name: "Price Target", rule: "priceTarget" },
{ name: "Market Cap", rule: "marketCap" },
{name: 'Analyst Rating', rule: 'analystRating'}
{ name: "Top Analyst Count", rule: "topAnalystCounter" },
{ name: "Top Analyst PT Upside", rule: "topAnalystUpside" },
{ name: "Top Analyst Price Target", rule: "topAnalystPriceTarget" },
{name: 'Top Analyst Rating', rule: 'topAnalystRating'},
{name: 'Market Cap', rule: 'marketCap'}
];
const hideLastRow = true;
</script>
@ -74,7 +74,7 @@
<div
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]">
<h1 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
Top Strong Buy Stocks
@ -255,66 +255,6 @@
</div>
</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>

View File

@ -1296,14 +1296,43 @@
category: "Forecasts, Analysts & Price Targets",
},
upside: {
label: "Price Target Upside [%]",
label: "Price Target Upside",
step: ["100%", "50%", "20%", "10%", "5%", "0%"],
defaultCondition: "over",
defaultValue: "any",
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: {
label: "Halal Stocks",
step: ["Compliant", "Non-Compliant"],
@ -1452,6 +1481,7 @@
?.filter((rule) =>
[
"analystRating",
"topAnalystRating",
"halalStocks",
"sector",
"country",
@ -1555,6 +1585,7 @@
?.filter((rule) =>
[
"analystRating",
"topAnalystRating",
"halalStocks",
"sector",
"country",
@ -1569,7 +1600,7 @@
}
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");
} else {
selectedPopularStrategy = "";
@ -1628,6 +1659,7 @@
switch (ruleName) {
case "analystRating":
case "topAnalystRating":
case "halalStocks":
case "score":
case "sector":
@ -1862,6 +1894,7 @@ const handleKeyDown = (event) => {
?.filter((rule) =>
[
"analystRating",
"topAnalystRating",
"halalStocks",
"sector",
"country",
@ -1949,6 +1982,7 @@ const handleKeyDown = (event) => {
"ema200",
"grahamNumber",
"analystRating",
"topAnalystRating",
"halalStocks",
"score",
"sector",
@ -2112,7 +2146,7 @@ const handleKeyDown = (event) => {
? sectorList
: ruleName === "industry"
? industryList
: ruleName === "analystRating" || ruleName === "score"
: ['analystRating','topAnalystRating','score']?.includes(ruleName)
? ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"]
: ["Compliant", "Non-Compliant"];
testList =
@ -2213,6 +2247,7 @@ const handleKeyDown = (event) => {
"score",
"sector",
"analystRating",
"topAnalystRating",
"halalStocks",
];
@ -2797,7 +2832,7 @@ const handleKeyDown = (event) => {
<DropdownMenu.Content
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
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"
class="{![
'analystRating',
"topAnalystRating",
'halalStocks',
'score',
'sector',
@ -2975,7 +3011,7 @@ const handleKeyDown = (event) => {
</div>
{/if}
<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}
{#if ruleCondition[row?.rule] === "between"}
{#if newValue && row?.step[index + 1]}
@ -3044,7 +3080,7 @@ const handleKeyDown = (event) => {
</DropdownMenu.Item>
{/each}
{: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">
<div
class="flex items-center"
@ -3285,7 +3321,7 @@ const handleKeyDown = (event) => {
<td
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]}
{:else}
{abbreviateNumber(item[row?.rule])}
@ -3413,7 +3449,7 @@ const handleKeyDown = (event) => {
>
{/if}
{:else if row?.rule === 'analystRating'}
{:else if ['analystRating','topAnalystRating']?.includes(row?.rule)}
{#if ["Strong Buy", "Buy"].includes(item[row?.rule])}
<span class="text-[#00FC50]">{item[row?.rule]}</span>
{:else if ["Strong Sell", "Sell"].includes(item[row?.rule])}
@ -3593,7 +3629,7 @@ const handleKeyDown = (event) => {
<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"
>
{#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)}>
<svg
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
const categoricalFields = [
'analystRating', 'halalStocks', 'score',
'analystRating', 'topAnalystRating', 'halalStocks', 'score',
'sector', 'industry', 'country'
];

View File

@ -17,9 +17,9 @@
let rawData = data?.getAnalystTickerHistory ?? [];
let historyList = [];
let priceTarget = "n/a";
let numOfAnalyst = 0;
let numOfAnalyst = "n/a";
let consensusRating = "n/a";
let changesPercentage = 0;
let changesPercentage = "n/a";
const tabs = [
{
@ -62,7 +62,7 @@
const key = `${entry.analyst}-${entry.name}`;
// Convert date and time to a Date object
const dateTimeStr = `${entry.date} ${entry.time}`;
const dateTimeStr = `${entry.date}`;
const dateTime = new Date(dateTimeStr);
// 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
const filteredAnalystCount = recentData?.length;
const filteredAnalystCount = recentData?.length ?? 'n/a';
const priceTargets = recentData
?.map((item) => parseFloat(item.adjusted_pt_current))
?.filter((pt) => !isNaN(pt));
@ -104,9 +104,9 @@
? priceTargets?.sort((a, b) => a - b)[
Math.floor(priceTargets?.length / 2)
]
: "-";
: "n/a";
numOfAnalyst = filteredAnalystCount;
numOfAnalyst = filteredAnalystCount === 0 ? 'n/a' : filteredAnalystCount;
priceTarget = medianPriceTarget;
changesPercentage =
medianPriceTarget !== "-" && data?.getStockQuote?.price != null
@ -141,7 +141,8 @@
? "Hold"
: averageRatingScore >= 1.5
? "Sell"
: "Strong Sell";
: averageRatingScore >= 1
? "Strong Sell" : 'n/a';
rawData = recentData;
historyList = rawData.slice(0, 50);