diff --git a/src/lib/components/AnalystEstimate.svelte b/src/lib/components/AnalystEstimate.svelte
index 93260148..1dc82f6c 100644
--- a/src/lib/components/AnalystEstimate.svelte
+++ b/src/lib/components/AnalystEstimate.svelte
@@ -3,13 +3,19 @@
import { Chart } from "svelte-echarts";
import { init, use } from "echarts/core";
- import { LineChart } from "echarts/charts";
+ import { LineChart, CustomChart } from "echarts/charts";
import { GridComponent, TooltipComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
import { abbreviateNumber } from "$lib/utils";
export let data;
- use([LineChart, GridComponent, TooltipComponent, CanvasRenderer]);
+ use([
+ LineChart,
+ CustomChart,
+ GridComponent,
+ TooltipComponent,
+ CanvasRenderer,
+ ]);
let analystEstimateList = [];
let isLoaded = false;
@@ -62,43 +68,48 @@
function computeGrowthList(tableActualRevenue, tableForecastRevenue) {
return tableActualRevenue?.map((item, index) => {
- // If it's the first item or the list is empty, return null
+ const currentFY = item?.FY;
+
+ // If it's the first item or the list is empty, return null growth
if (index === 0 || tableActualRevenue.length === 0) {
- return null;
+ return { FY: currentFY, growth: null };
}
- // If actual value is null, check forecast
+ // If actual value is null, compute growth based on forecast values
if (item?.val === null) {
const prevForecastVal = tableForecastRevenue[index - 1]?.val ?? 0;
const currentForecastVal = tableForecastRevenue[index]?.val ?? 0;
- // Avoid division by zero when calculating forecast growth
if (prevForecastVal === 0 || currentForecastVal === 0) {
- return null; // Return null if previous or current forecast is 0
+ return { FY: currentFY, growth: null };
}
const forecastGrowth =
((currentForecastVal - prevForecastVal) / Math.abs(prevForecastVal)) *
100;
- // Return rounded forecast growth value, or null if 0
- return forecastGrowth !== 0 ? Number(forecastGrowth.toFixed(2)) : null;
+ return {
+ FY: currentFY,
+ growth:
+ forecastGrowth !== 0 ? Number(forecastGrowth.toFixed(2)) : null,
+ };
}
// Compute actual growth for non-null actual values
const prevActualVal = tableActualRevenue[index - 1]?.val ?? 0;
const currentActualVal = item?.val ?? 0;
- // Avoid division by zero when calculating actual growth
if (prevActualVal === 0 || currentActualVal === 0) {
- return null; // Return null if previous or current actual value is 0
+ return { FY: currentFY, growth: null };
}
const actualGrowth =
((currentActualVal - prevActualVal) / Math.abs(prevActualVal)) * 100;
- // Return rounded actual growth value, or null if 0
- return actualGrowth !== 0 ? Number(actualGrowth.toFixed(2)) : null;
+ return {
+ FY: currentFY,
+ growth: actualGrowth !== 0 ? Number(actualGrowth.toFixed(2)) : null,
+ };
});
}
@@ -131,7 +142,6 @@
let avgList = [];
let lowList = [];
let highList = [];
-
let filteredData =
analystEstimateList?.filter((item) => item.date >= 2019) ?? [];
const stopIndex = findIndex(filteredData);
@@ -188,6 +198,16 @@
highEPSList = highList?.slice(currentYearIndex) || [];
}
+ const growthList = dates.map((date) => {
+ const fy = parseInt(date.replace("FY", ""), 10); // Extract numeric FY value
+ const listToUse =
+ dataType === "Revenue" ? revenueGrowthList : epsGrowthList; // Select the correct growth list
+ const growth = listToUse.find((r) => r.FY === fy); // Find matching FY
+ return growth ? growth.growth : null; // Return growth or null if not found
+ });
+
+ console.log(growthList);
+
const option = {
silent: true,
tooltip: {
@@ -297,7 +317,6 @@
splitLine: {
show: false, // Disable x-axis grid lines
},
-
axisLabel: {
show: false, // Hide y-axis labels
},
@@ -306,21 +325,96 @@
series: [
{
name: dataType === "Revenue" ? "Revenue Growth" : "EPS Growth",
- data: (dataType === "Revenue" ? revenueGrowthList : epsGrowthList)
- ?.slice(1)
- .map((value) => ({
- value,
- itemStyle: {
- color: value >= 0 ? "#00FC50" : "#D9220E", // Green for >= 0, Red for < 0
- },
- })),
+ data: growthList?.map((value) => ({
+ value,
+ itemStyle: {
+ color: value >= 0 ? "#00FC50" : "#D9220E", // Green for >= 0, Red for < 0
+ },
+ })),
type: "bar",
smooth: true,
+ z: 5, // Ensure the bar chart has a lower z-index than the error bars
+ },
+ {
+ name: "Error Bars",
+ type: "custom",
+ renderItem: (params, api) => {
+ const xValue = api.value(0);
+ const yValue = api.value(1);
+ const high = yValue + yValue / 2; // High value (half above the value)
+ const low = yValue - yValue / 2; // Low value (half below the value)
+ const x = api.coord([xValue, yValue])[0];
+ const highCoord = api.coord([xValue, high])[1];
+ const lowCoord = api.coord([xValue, low])[1];
+
+ return {
+ type: "group",
+ children: [
+ {
+ type: "line",
+ shape: {
+ x1: x,
+ y1: highCoord,
+ x2: x,
+ y2: lowCoord,
+ },
+ style: {
+ stroke: "#fff",
+ lineWidth: 1.5, // Set thicker line width
+ },
+ },
+ {
+ type: "line",
+ shape: {
+ x1: x - 5,
+ y1: highCoord,
+ x2: x + 5,
+ y2: highCoord,
+ },
+ style: {
+ stroke: "#fff",
+ lineWidth: 1.5, // Set thicker line width
+ },
+ },
+ {
+ type: "line",
+ shape: {
+ x1: x - 5,
+ y1: lowCoord,
+ x2: x + 5,
+ y2: lowCoord,
+ },
+ style: {
+ stroke: "#fff",
+ lineWidth: 1.5, // Set thicker line width
+ },
+ },
+ ],
+ };
+ },
+ encode: {
+ x: 0, // Map x-axis values
+ y: 1, // Map y-axis values
+ },
+ data: growthList?.map((value, index) => [index, value]), // Prepare data for error bars
+ z: 10, // Bring the error bars to the front
},
],
-
tooltip: {
trigger: "axis",
+ formatter: (params) => {
+ const dataIndex = params[0].dataIndex;
+ const mainValue = params[0].value;
+ const high = mainValue + mainValue / 2;
+ const low = mainValue - mainValue / 2;
+
+ return `
+ ${dates[dataIndex]}
+ High: ${high?.toFixed(2) + "%"}
+ Avg: ${mainValue?.toFixed(2) + "%"}
+ Low: ${low?.toFixed(2) + "%"}
+ `;
+ },
},
};
@@ -372,7 +466,6 @@
val: item?.estimatedEpsAvg,
});
});
-
//Values coincide with table values for crosscheck
revenueGrowthList = computeGrowthList(
tableActualRevenue,
@@ -453,29 +546,29 @@
>
Revenue Growth
- {#each computeGrowthList(tableActualRevenue, tableForecastRevenue) as growth, index}
+ {#each computeGrowthList(tableActualRevenue, tableForecastRevenue) as item, index}