This commit is contained in:
MuslemRahimi 2025-02-21 23:36:40 +01:00
parent 5cf8d27ad1
commit e442fb10d5
17 changed files with 170 additions and 75 deletions

View File

@ -163,7 +163,8 @@
<div class="absolute inset-0 rounded-md bg-[#fff]"></div> <div class="absolute inset-0 rounded-md bg-[#fff]"></div>
{/if} {/if}
<span <span
class="relative text-sm block font-semibold {activeIdx === i class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i
? 'text-black' ? 'text-black'
: 'text-white'}" : 'text-white'}"
> >

View File

@ -58,23 +58,23 @@
website = info?.website; website = info?.website;
snippet = description?.slice(0, 450) + "..."; snippet = description?.slice(0, 450) + "...";
numOfAnalyst = data?.getAnalystRating?.numOfAnalyst; numOfAnalyst = data?.getAnalystSummary?.numOfAnalyst;
buyCount = ((data?.getAnalystRating?.Buy / numOfAnalyst) * 100)?.toFixed( buyCount = ((data?.getAnalystSummary?.Buy / numOfAnalyst) * 100)?.toFixed(
2, 2,
); );
holdCount = ( holdCount = (
(data?.getAnalystRating?.Hold / numOfAnalyst) * (data?.getAnalystSummary?.Hold / numOfAnalyst) *
100 100
)?.toFixed(2); )?.toFixed(2);
sellCount = ( sellCount = (
(data?.getAnalystRating?.Sell / numOfAnalyst) * (data?.getAnalystSummary?.Sell / numOfAnalyst) *
100 100
)?.toFixed(2); )?.toFixed(2);
priceTarget = priceTarget =
data?.getAnalystRating?.medianPriceTarget !== ("n/a" && 0) data?.getAnalystSummary?.medianPriceTarget !== ("n/a" && 0)
? data?.getAnalystRating?.medianPriceTarget ? data?.getAnalystSummary?.medianPriceTarget
: "n/a"; : "n/a";
consensusRating = data?.getAnalystRating?.consensusRating; consensusRating = data?.getAnalystSummary?.consensusRating;
try { try {
changesPercentage = changesPercentage =
@ -165,9 +165,9 @@
</div> </div>
</div> </div>
{#if Object?.keys(data?.getAnalystRating ?? {})?.length !== 0} {#if Object?.keys(data?.getAnalystSummary ?? {})?.length !== 0}
<div <div
class="space-y-3 pt-10 sm:pt-5 {Object?.keys(data?.getAnalystRating ?? {}) class="space-y-3 pt-10 sm:pt-5 {Object?.keys(data?.getAnalystSummary ?? {})
?.length !== 0 ?.length !== 0
? '' ? ''
: 'hidden'}" : 'hidden'}"

View File

@ -783,7 +783,7 @@
<div class="absolute inset-0 rounded-md bg-[#fff]"></div> <div class="absolute inset-0 rounded-md bg-[#fff]"></div>
{/if} {/if}
<span <span
class="relative text-sm block font-semibold {activeIdx === class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i i
? 'text-black' ? 'text-black'
: 'text-white'}" : 'text-white'}"

View File

@ -96,7 +96,7 @@ export const load = async ({ params, locals }) => {
try { try {
const [ const [
getStockDeck, getStockDeck,
getAnalystRating, getAnalystSummary,
getStockQuote, getStockQuote,
getPrePostQuote, getPrePostQuote,
getWhyPriceMoved, getWhyPriceMoved,
@ -118,7 +118,7 @@ export const load = async ({ params, locals }) => {
return { return {
getStockDeck, getStockDeck,
getAnalystRating, getAnalystSummary,
getStockQuote, getStockQuote,
getPrePostQuote, getPrePostQuote,
getWhyPriceMoved, getWhyPriceMoved,

View File

@ -941,7 +941,7 @@
>Metrics</a >Metrics</a
> >
{#if Object?.keys(data?.getAnalystRating ?? {})?.length > 0} {#if Object?.keys(data?.getAnalystSummary ?? {})?.length > 0}
<a <a
href={`/stocks/${$stockTicker}/forecast`} href={`/stocks/${$stockTicker}/forecast`}
on:click={() => changeSection("forecast")} on:click={() => changeSection("forecast")}

View File

@ -1031,11 +1031,11 @@
><td ><td
class="whitespace-nowrap px-0.5 py-[1px] xs:px-1 sm:py-2 text-[1rem]" class="whitespace-nowrap px-0.5 py-[1px] xs:px-1 sm:py-2 text-[1rem]"
><a ><a
href={data?.getAnalystRating?.consensusRating !== href={data?.getAnalystSummary?.consensusRating !==
undefined undefined
? `/stocks/${$stockTicker}/forecast` ? `/stocks/${$stockTicker}/forecast`
: ""} : ""}
class={data?.getAnalystRating?.consensusRating !== class={data?.getAnalystSummary?.consensusRating !==
undefined undefined
? "sm:hover:text-blue-400 text-white underline underline-offset-4" ? "sm:hover:text-blue-400 text-white underline underline-offset-4"
: "text-white cursor-text"}>Analyst</a : "text-white cursor-text"}>Analyst</a
@ -1043,10 +1043,10 @@
</td> </td>
<td <td
class="whitespace-nowrap px-0.5 py-[1px] text-left text-sm font-semibold xs:px-1 sm:py-2 sm:text-right sm:text-[1rem]" class="whitespace-nowrap px-0.5 py-[1px] text-left text-sm font-semibold xs:px-1 sm:py-2 sm:text-right sm:text-[1rem]"
>{data?.getAnalystRating?.consensusRating !== null && >{data?.getAnalystSummary?.consensusRating !== null &&
data?.getAnalystRating?.consensusRating !== "n/a" && data?.getAnalystSummary?.consensusRating !== "n/a" &&
data?.getAnalystRating?.consensusRating !== undefined data?.getAnalystSummary?.consensusRating !== undefined
? data?.getAnalystRating?.consensusRating ? data?.getAnalystSummary?.consensusRating
: "n/a"}</td : "n/a"}</td
></tr ></tr
> >

View File

@ -491,7 +491,7 @@
></div> ></div>
{/if} {/if}
<span <span
class="relative text-sm block font-semibold {activeIdx === class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i i
? 'text-black' ? 'text-black'
: 'text-white'}" : 'text-white'}"

View File

@ -21,7 +21,6 @@
import Infobox from "$lib/components/Infobox.svelte"; import Infobox from "$lib/components/Infobox.svelte";
import SEO from "$lib/components/SEO.svelte"; import SEO from "$lib/components/SEO.svelte";
use([LineChart, BarChart, GridComponent, TooltipComponent, CanvasRenderer]); use([LineChart, BarChart, GridComponent, TooltipComponent, CanvasRenderer]);
export let data; export let data;
@ -478,8 +477,6 @@
} }
</script> </script>
<SEO <SEO
title={`${$displayCompanyName} (${$stockTicker}) Balance Sheet · Stocknear`} title={`${$displayCompanyName} (${$stockTicker}) Balance Sheet · Stocknear`}
description={`Detailed balance sheet for ${$displayCompanyName} (${$stockTicker}), including cash, debt, assets, liabilities, and book value.`} description={`Detailed balance sheet for ${$displayCompanyName} (${$stockTicker}), including cash, debt, assets, liabilities, and book value.`}
@ -550,7 +547,7 @@
></div> ></div>
{/if} {/if}
<span <span
class="relative text-sm block font-semibold {activeIdx === class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i i
? 'text-black' ? 'text-black'
: 'text-white'}" : 'text-white'}"

View File

@ -20,7 +20,6 @@
import Infobox from "$lib/components/Infobox.svelte"; import Infobox from "$lib/components/Infobox.svelte";
import SEO from "$lib/components/SEO.svelte"; import SEO from "$lib/components/SEO.svelte";
use([LineChart, BarChart, GridComponent, TooltipComponent, CanvasRenderer]); use([LineChart, BarChart, GridComponent, TooltipComponent, CanvasRenderer]);
export let data; export let data;
@ -424,8 +423,6 @@
} }
</script> </script>
<SEO <SEO
title={`${$displayCompanyName} (${$stockTicker}) Cash Flow Statement · Stocknear`} title={`${$displayCompanyName} (${$stockTicker}) Cash Flow Statement · Stocknear`}
description={`Detailed cash flow statements for ${$displayCompanyName} (${$stockTicker}), including operating cash flow, capex and free cash flow.`} description={`Detailed cash flow statements for ${$displayCompanyName} (${$stockTicker}), including operating cash flow, capex and free cash flow.`}
@ -494,7 +491,7 @@
></div> ></div>
{/if} {/if}
<span <span
class="relative text-sm block font-semibold {activeIdx === class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i i
? 'text-black' ? 'text-black'
: 'text-white'}" : 'text-white'}"

View File

@ -478,7 +478,7 @@
></div> ></div>
{/if} {/if}
<span <span
class="relative text-sm block font-semibold {activeIdx === class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i i
? 'text-black' ? 'text-black'
: 'text-white'}" : 'text-white'}"

View File

@ -50,10 +50,26 @@ export const load = async ({ locals, params }) => {
return output; return output;
}; };
const getTopAnalystSummary = async () => {
const response = await fetch(apiURL + "/top-analyst-summary-rating", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise // Make sure to return a promise
return { return {
getAnalystEstimate: await getAnalystEstimate(), getAnalystEstimate: await getAnalystEstimate(),
getAnalystInsight: await getAnalystInsight(), getAnalystInsight: await getAnalystInsight(),
getAnalystTickerHistory: await getAnalystTickerHistory(), getAnalystTickerHistory: await getAnalystTickerHistory(),
getTopAnalystSummary: await getTopAnalystSummary(),
}; };
}; };

View File

@ -2,6 +2,10 @@ import { error, fail, redirect } from "@sveltejs/kit";
import { validateData } from "$lib/utils"; import { validateData } from "$lib/utils";
import { loginUserSchema, registerUserSchema } from "$lib/schemas"; import { loginUserSchema, registerUserSchema } from "$lib/schemas";
export const actions = { export const actions = {
login: async ({ url, request, locals }) => { login: async ({ url, request, locals }) => {

View File

@ -16,6 +16,8 @@
import { LineChart, BarChart, GaugeChart } from "echarts/charts"; import { LineChart, BarChart, GaugeChart } from "echarts/charts";
import { GridComponent, TooltipComponent } from "echarts/components"; import { GridComponent, TooltipComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers"; import { CanvasRenderer } from "echarts/renderers";
import { goto } from "$app/navigation";
import SEO from "$lib/components/SEO.svelte"; import SEO from "$lib/components/SEO.svelte";
export let data; export let data;
@ -37,21 +39,75 @@
const calculatePriceChange = (targetPrice) => const calculatePriceChange = (targetPrice) =>
targetPrice && price ? ((targetPrice / price - 1) * 100)?.toFixed(2) : 0; targetPrice && price ? ((targetPrice / price - 1) * 100)?.toFixed(2) : 0;
const numOfAnalyst = data?.getAnalystRating?.numOfAnalyst || 0;
const avgPriceTarget = data?.getAnalystRating?.avgPriceTarget || 0;
const medianPriceTarget = data?.getAnalystRating?.medianPriceTarget || 0;
const lowPriceTarget = data?.getAnalystRating?.lowPriceTarget || 0;
const highPriceTarget = data?.getAnalystRating?.highPriceTarget || 0;
const consensusRating = data?.getAnalystRating?.consensusRating;
const lowChange = calculatePriceChange(lowPriceTarget); let numOfAnalyst = data?.getAnalystSummary?.numOfAnalyst || 0;
const medianChange = calculatePriceChange(medianPriceTarget); let avgPriceTarget = data?.getAnalystSummary?.avgPriceTarget || 0;
const avgChange = calculatePriceChange(avgPriceTarget); let medianPriceTarget = data?.getAnalystSummary?.medianPriceTarget || 0;
const highChange = calculatePriceChange(highPriceTarget); let lowPriceTarget = data?.getAnalystSummary?.lowPriceTarget || 0;
const rawAnalystList = data?.getAnalystRating?.recommendationList || []; let highPriceTarget = data?.getAnalystSummary?.highPriceTarget || 0;
const recommendationList = let consensusRating = data?.getAnalystSummary?.consensusRating;
let lowChange = calculatePriceChange(lowPriceTarget);
let medianChange = calculatePriceChange(medianPriceTarget);
let avgChange = calculatePriceChange(avgPriceTarget);
let highChange = calculatePriceChange(highPriceTarget);
let rawAnalystList = data?.getAnalystSummary?.recommendationList || [];
let recommendationList =
rawAnalystList?.length > 5 ? rawAnalystList?.slice(-6) : rawAnalystList; rawAnalystList?.length > 5 ? rawAnalystList?.slice(-6) : rawAnalystList;
const categories = ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"]; let categories = ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"];
const tabs = [
{
title: "All Analysts",
},
{
title: "Top Analysts",
},
];
let activeIdx = 0;
function changeTab(index) {
activeIdx = index;
if (activeIdx === 0) {
numOfAnalyst = data?.getAnalystSummary?.numOfAnalyst || 0;
avgPriceTarget = data?.getAnalystSummary?.avgPriceTarget || 0;
medianPriceTarget = data?.getAnalystSummary?.medianPriceTarget || 0;
lowPriceTarget = data?.getAnalystSummary?.lowPriceTarget || 0;
highPriceTarget = data?.getAnalystSummary?.highPriceTarget || 0;
consensusRating = data?.getAnalystSummary?.consensusRating;
lowChange = calculatePriceChange(lowPriceTarget);
medianChange = calculatePriceChange(medianPriceTarget);
avgChange = calculatePriceChange(avgPriceTarget);
highChange = calculatePriceChange(highPriceTarget);
rawAnalystList = data?.getAnalystSummary?.recommendationList || [];
recommendationList =
rawAnalystList?.length > 5 ? rawAnalystList?.slice(-6) : rawAnalystList;
categories = ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"];
} else {
numOfAnalyst = data?.getTopAnalystSummary?.numOfAnalyst || 0;
avgPriceTarget = data?.getTopAnalystSummary?.avgPriceTarget || 0;
medianPriceTarget = data?.getTopAnalystSummary?.medianPriceTarget || 0;
lowPriceTarget = data?.getTopAnalystSummary?.lowPriceTarget || 0;
highPriceTarget = data?.getTopAnalystSummary?.highPriceTarget || 0;
consensusRating = data?.getTopAnalystSummary?.consensusRating;
lowChange = calculatePriceChange(lowPriceTarget);
medianChange = calculatePriceChange(medianPriceTarget);
avgChange = calculatePriceChange(avgPriceTarget);
highChange = calculatePriceChange(highPriceTarget);
rawAnalystList = data?.getTopAnalystSummary?.recommendationList || [];
recommendationList =
rawAnalystList?.length > 5 ? rawAnalystList?.slice(-6) : rawAnalystList;
categories = ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"];
console.log(recommendationList);
}
optionsData = getPlotOptions() || null;
optionsPieChart = getPieChart() || null;
optionsPriceForecast = getPriceForecastChart() || null;
}
function findIndex(data) { function findIndex(data) {
let year = new Date().getFullYear() - 1; let year = new Date().getFullYear() - 1;
@ -78,8 +134,8 @@
return -1; // Return -1 if no matching index is found return -1; // Return -1 if no matching index is found
} }
function getTotalForDate(index) { function getTotalForDate(index, recommendationList) {
return categories.reduce( return categories?.reduce(
(sum, cat) => sum + recommendationList[index][cat], (sum, cat) => sum + recommendationList[index][cat],
0, 0,
); );
@ -291,7 +347,7 @@
} }
function getPriceForecastChart() { function getPriceForecastChart() {
const historicalData = data?.getAnalystRating?.pastPriceList || []; const historicalData = data?.getAnalystSummary?.pastPriceList || [];
const forecastTargets = { const forecastTargets = {
low: lowPriceTarget, low: lowPriceTarget,
avg: avgPriceTarget, avg: avgPriceTarget,
@ -472,28 +528,52 @@
{removeCompanyStrings($displayCompanyName)} Forecast {removeCompanyStrings($displayCompanyName)} Forecast
</h1> </h1>
<div class="inline-flex justify-center w-full rounded-md sm:w-auto">
<div <div
class="mb-2 sm:mb-0 mt-2 sm:mt-0 inline-flex w-full rounded-md shadow-sm shadow-[#1E222D] sm:w-auto ml-auto text-sm border border-gray-800" class="bg-secondary w-full sm:w-fit relative flex flex-wrap items-center justify-center rounded-md p-1 mt-4"
> >
{#each tabs as item, i}
{#if data?.user?.tier !== "Pro" && i > 0}
<button <button
class="w-full text-sm rounded-none rounded-l-md px-2 bp:px-3 sm:w-auto sm:px-4 py-1.5 bg-secondary text-white" on:click={() => goto("/pricing")}
>All Analysts</button class="group relative z-[1] rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1"
> >
<button <span class="relative text-sm block font-semibold">
class="text-sm w-full rounded-none rounded-r-md px-2 bp:px-3 sm:w-auto sm:px-4 py-1.5 bg-table sm:hover:bg-secondary transition duration-50 ease-out" {item.title}
>Top Analysts
<svg <svg
class="w-4 h-4 ml-1 text-icon-faded inline-block" class="inline-block ml-0.5 -mt-1 w-3.5 h-3.5"
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
fill="currentColor" viewBox="0 0 24 24"
style="max-width:40px"
><path ><path
fill-rule="evenodd" fill="#A3A3A3"
d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
clip-rule="evenodd" /></svg
></path></svg
></button
> >
</span>
</button>
{:else}
<button
on:click={() => changeTab(i)}
class="group relative z-[1] rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1 {activeIdx ===
i
? 'z-0'
: ''} "
>
{#if activeIdx === i}
<div class="absolute inset-0 rounded-md bg-[#fff]"></div>
{/if}
<span
class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i
? 'text-black'
: 'text-white'}"
>
{item.title}
</span>
</button>
{/if}
{/each}
</div>
</div> </div>
</div> </div>
@ -714,7 +794,7 @@
<td <td
class="px-1 py-[3px] text-sm sm:text-[1rem] text-right" class="px-1 py-[3px] text-sm sm:text-[1rem] text-right"
> >
{getTotalForDate(i)} {getTotalForDate(i, recommendationList)}
</td> </td>
{/each} {/each}
</tr> </tr>

View File

@ -134,7 +134,7 @@
} }
function getPriceForecastChart() { function getPriceForecastChart() {
const historicalData = data?.getAnalystRating?.pastPriceList || []; const historicalData = data?.getAnalystSummary?.pastPriceList || [];
const forecastTargets = { const forecastTargets = {
low: lowPriceTarget, low: lowPriceTarget,
avg: avgPriceTarget, avg: avgPriceTarget,

View File

@ -13,7 +13,7 @@
import SEO from "$lib/components/SEO.svelte"; import SEO from "$lib/components/SEO.svelte";
export let data; export let data;
let analystRating = data?.getAnalystRating ?? {}; let analystRating = data?.getAnalystSummary ?? {};
let rawData = data?.getAnalystTickerHistory ?? []; let rawData = data?.getAnalystTickerHistory ?? [];
let historyList = []; let historyList = [];
@ -126,8 +126,8 @@
}; };
const totalRatingScore = recentData const totalRatingScore = recentData
.map((item) => ratingScores[item.rating_current] || 0) ?.map((item) => ratingScores[item.rating_current] || 0)
.reduce((sum, score) => sum + score, 0); ?.reduce((sum, score) => sum + score, 0);
const averageRatingScore = filteredAnalystCount const averageRatingScore = filteredAnalystCount
? totalRatingScore / filteredAnalystCount ? totalRatingScore / filteredAnalystCount
@ -262,7 +262,7 @@
></div> ></div>
{/if} {/if}
<span <span
class="relative text-sm block font-semibold {activeIdx === class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i i
? 'text-black' ? 'text-black'
: 'text-white'}" : 'text-white'}"

View File

@ -659,7 +659,7 @@
></div> ></div>
{/if} {/if}
<span <span
class="relative text-sm block font-semibold {activeIdx === class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i i
? 'text-black' ? 'text-black'
: 'text-white'}" : 'text-white'}"

View File

@ -398,7 +398,7 @@
></div> ></div>
{/if} {/if}
<span <span
class="relative text-sm block font-semibold {activeIdx === class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i i
? 'text-black' ? 'text-black'
: 'text-white'}" : 'text-white'}"