This commit is contained in:
MuslemRahimi 2024-10-31 17:04:04 +01:00
parent 811627f30d
commit edf98dda63
3 changed files with 164 additions and 32 deletions

View File

@ -1,11 +1,9 @@
export const load = async ({ locals, params }) => { export const load = async ({ locals, params }) => {
const getAnalystEstimate = async () => {
const { apiURL, apiKey } = locals; const { apiURL, apiKey } = locals;
const postData = { const postData = {
ticker: params.tickerID, ticker: params.tickerID,
}; };
const getAnalystEstimate = async () => {
// make the POST request to the endpoint // make the POST request to the endpoint
const response = await fetch(apiURL + "/analyst-estimate", { const response = await fetch(apiURL + "/analyst-estimate", {
method: "POST", method: "POST",
@ -21,8 +19,23 @@ export const load = async ({ locals, params }) => {
return output; return output;
}; };
const getAnalystInsight = async () => {
const response = await fetch(apiURL + "/analyst-insight", {
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(),
}; };
}; };

View File

@ -7,7 +7,17 @@
} from "$lib/store"; } from "$lib/store";
import { abbreviateNumber } from "$lib/utils"; import { abbreviateNumber } from "$lib/utils";
import { Chart } from "svelte-echarts";
import { init, use } from "echarts/core";
import { BarChart } from "echarts/charts";
import { GridComponent, TooltipComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
import { onMount } from "svelte";
export let data; export let data;
use([BarChart, GridComponent, TooltipComponent, CanvasRenderer]);
let index = 0; let index = 0;
let changeRevenue = 0; let changeRevenue = 0;
let changeNetIncome = 0; let changeNetIncome = 0;
@ -74,6 +84,95 @@
} }
}; };
function getPlotOptions() {
if (!rawAnalystList || rawAnalystList.length === 0) {
return null;
}
// Define categories in the exact order you specified
const categories = ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"];
const colors = ["#008A00", "#31B800", "#FF9E21", "#D9220E", "#9E190A"];
// Create a consistent mapping for data
const formattedData = rawAnalystList.map((item) =>
categories.map((cat) => item[cat] || 0),
);
// Normalize data to percentages
const normalizedData = formattedData.map((row) => {
const total = row.reduce((sum, val) => sum + val, 0);
return row.map((val) => (total > 0 ? (val / total) * 100 : 0));
});
// Calculate total percentage for each category across all dates
const totalData = [];
for (let i = 0; i < categories.length; ++i) {
let sum = 0;
for (let j = 0; j < normalizedData.length; ++j) {
sum += normalizedData[j][i];
}
totalData.push(sum / normalizedData.length);
}
// Define series based on categories with color mapping
const series = categories.map((name, idx) => ({
name,
type: "bar",
stack: "total",
barWidth: "60%",
data: normalizedData.map((row) => row[idx]),
itemStyle: {
color: colors[idx],
},
tooltip: {
valueFormatter: (value) => `${value.toFixed(2)}%`,
},
}));
// Define chart option
const option = {
grid: {
left: "2%",
right: "2%",
bottom: "10%",
top: "5%",
containLabel: true,
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
legend: {
data: categories,
bottom: 0,
},
xAxis: {
type: "category",
data: rawAnalystList.map((item) => item.date),
axisLabel: {
color: "#fff",
},
},
yAxis: {
type: "value",
max: 100,
axisLabel: {
show: false, // Hide y-axis labels
},
splitLine: {
show: false,
},
},
series,
animation: false,
};
return option;
}
if (data?.getAnalystEstimate?.length !== 0) { if (data?.getAnalystEstimate?.length !== 0) {
index = findIndex(data?.getAnalystEstimate); index = findIndex(data?.getAnalystEstimate);
@ -97,6 +196,8 @@
changeEBITDA = calculateChange(estimatedEbitdaAvg, ebitda); changeEBITDA = calculateChange(estimatedEbitdaAvg, ebitda);
changeEPS = calculateChange(estimatedEpsAvg, eps); changeEPS = calculateChange(estimatedEpsAvg, eps);
} }
let optionsData = getPlotOptions() || null;
</script> </script>
<svelte:head> <svelte:head>
@ -258,7 +359,10 @@
class="flex flex-col justify-between p-1 lg:max-w-[32%] text-white" class="flex flex-col justify-between p-1 lg:max-w-[32%] text-white"
> >
<div> <div>
<h2 class="mb-1 text-xl font-bold">Analyst Ratings</h2> <h2 class="mb-1 text-xl font-bold">Latest Analyst Report</h2>
{#if Object?.keys(data?.getAnalystInsight)?.length > 0}
<p>{data?.getAnalystInsight?.insight}</p>
{:else}
<p> <p>
According to {numOfAnalyst} stock analyst, the rating for GameStop According to {numOfAnalyst} stock analyst, the rating for GameStop
is "{consensusRating}". This means that the analyst believes is "{consensusRating}". This means that the analyst believes
@ -271,16 +375,14 @@
? "higher" ? "higher"
: "similar"} returns than market as a whole. : "similar"} returns than market as a whole.
</p> </p>
{/if}
</div> </div>
</div> </div>
<div class="grow pt-2 md:pt-4 lg:pl-4 lg:pt-0"> <div class="grow pt-2 md:pt-4 lg:pl-4 lg:pt-0">
<div class="h-[250px] xs:h-[275px]"> <div class="app h-[250px] xs:h-[275px]">
<canvas {#if optionsData !== null}
id="myChart" <Chart {init} options={optionsData} class="chart" />
style="display: block; box-sizing: border-box; height: 275px; width: 728px;" {/if}
width="1092"
height="412"
></canvas>
</div> </div>
<div <div
class="hide-scroll mb-1 mt-2 overflow-x-auto px-1.5 text-center md:mb-0 md:px-0 lg:mt-2" class="hide-scroll mb-1 mt-2 overflow-x-auto px-1.5 text-center md:mb-0 md:px-0 lg:mt-2"
@ -563,3 +665,20 @@
</div> </div>
</div> </div>
</section> </section>
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 210px;
}
}
.chart {
width: 100%;
}
</style>

View File

@ -250,23 +250,23 @@
> >
<thead class=""> <thead class="">
<tr class="border-b border-[#27272A]"> <tr class="border-b border-[#27272A]">
<td class="text-white font-semibold text-[1rem] text-start" <td class="text-white font-semibold text-sm text-start"
>Analyst</td >Analyst</td
> >
<td class="text-white font-semibold text-[1rem] text-start" <td class="text-white font-semibold text-sm text-start"
>Firm</td >Firm</td
> >
<td class="text-white font-semibold text-[1rem] text-end" <td class="text-white font-semibold text-sm text-end"
>Rating</td >Rating</td
> >
<td class="text-white font-semibold text-[1rem] text-end" <td class="text-white font-semibold text-sm text-end"
>Action</td >Action</td
> >
<td class="text-white font-semibold text-[1rem] text-end" <td class="text-white font-semibold text-sm text-end"
>Price Target</td >Price Target</td
> >
<td class="text-white font-semibold text-[1rem] text-end" <td class="text-white font-semibold text-sm text-end"
>Date</td >Date</td
> >
</tr> </tr>