bugfixing: analyst page!
This commit is contained in:
parent
240ad1f993
commit
419279453e
@ -3,13 +3,19 @@
|
|||||||
|
|
||||||
import { Chart } from "svelte-echarts";
|
import { Chart } from "svelte-echarts";
|
||||||
import { init, use } from "echarts/core";
|
import { init, use } from "echarts/core";
|
||||||
import { LineChart } from "echarts/charts";
|
import { LineChart, CustomChart } 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 { abbreviateNumber } from "$lib/utils";
|
import { abbreviateNumber } from "$lib/utils";
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
use([LineChart, GridComponent, TooltipComponent, CanvasRenderer]);
|
use([
|
||||||
|
LineChart,
|
||||||
|
CustomChart,
|
||||||
|
GridComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
CanvasRenderer,
|
||||||
|
]);
|
||||||
|
|
||||||
let analystEstimateList = [];
|
let analystEstimateList = [];
|
||||||
let isLoaded = false;
|
let isLoaded = false;
|
||||||
@ -62,43 +68,48 @@
|
|||||||
|
|
||||||
function computeGrowthList(tableActualRevenue, tableForecastRevenue) {
|
function computeGrowthList(tableActualRevenue, tableForecastRevenue) {
|
||||||
return tableActualRevenue?.map((item, index) => {
|
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) {
|
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) {
|
if (item?.val === null) {
|
||||||
const prevForecastVal = tableForecastRevenue[index - 1]?.val ?? 0;
|
const prevForecastVal = tableForecastRevenue[index - 1]?.val ?? 0;
|
||||||
const currentForecastVal = tableForecastRevenue[index]?.val ?? 0;
|
const currentForecastVal = tableForecastRevenue[index]?.val ?? 0;
|
||||||
|
|
||||||
// Avoid division by zero when calculating forecast growth
|
|
||||||
if (prevForecastVal === 0 || currentForecastVal === 0) {
|
if (prevForecastVal === 0 || currentForecastVal === 0) {
|
||||||
return null; // Return null if previous or current forecast is 0
|
return { FY: currentFY, growth: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
const forecastGrowth =
|
const forecastGrowth =
|
||||||
((currentForecastVal - prevForecastVal) / Math.abs(prevForecastVal)) *
|
((currentForecastVal - prevForecastVal) / Math.abs(prevForecastVal)) *
|
||||||
100;
|
100;
|
||||||
|
|
||||||
// Return rounded forecast growth value, or null if 0
|
return {
|
||||||
return forecastGrowth !== 0 ? Number(forecastGrowth.toFixed(2)) : null;
|
FY: currentFY,
|
||||||
|
growth:
|
||||||
|
forecastGrowth !== 0 ? Number(forecastGrowth.toFixed(2)) : null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute actual growth for non-null actual values
|
// Compute actual growth for non-null actual values
|
||||||
const prevActualVal = tableActualRevenue[index - 1]?.val ?? 0;
|
const prevActualVal = tableActualRevenue[index - 1]?.val ?? 0;
|
||||||
const currentActualVal = item?.val ?? 0;
|
const currentActualVal = item?.val ?? 0;
|
||||||
|
|
||||||
// Avoid division by zero when calculating actual growth
|
|
||||||
if (prevActualVal === 0 || currentActualVal === 0) {
|
if (prevActualVal === 0 || currentActualVal === 0) {
|
||||||
return null; // Return null if previous or current actual value is 0
|
return { FY: currentFY, growth: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
const actualGrowth =
|
const actualGrowth =
|
||||||
((currentActualVal - prevActualVal) / Math.abs(prevActualVal)) * 100;
|
((currentActualVal - prevActualVal) / Math.abs(prevActualVal)) * 100;
|
||||||
|
|
||||||
// Return rounded actual growth value, or null if 0
|
return {
|
||||||
return actualGrowth !== 0 ? Number(actualGrowth.toFixed(2)) : null;
|
FY: currentFY,
|
||||||
|
growth: actualGrowth !== 0 ? Number(actualGrowth.toFixed(2)) : null,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +142,6 @@
|
|||||||
let avgList = [];
|
let avgList = [];
|
||||||
let lowList = [];
|
let lowList = [];
|
||||||
let highList = [];
|
let highList = [];
|
||||||
|
|
||||||
let filteredData =
|
let filteredData =
|
||||||
analystEstimateList?.filter((item) => item.date >= 2019) ?? [];
|
analystEstimateList?.filter((item) => item.date >= 2019) ?? [];
|
||||||
const stopIndex = findIndex(filteredData);
|
const stopIndex = findIndex(filteredData);
|
||||||
@ -188,6 +198,16 @@
|
|||||||
highEPSList = highList?.slice(currentYearIndex) || [];
|
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 = {
|
const option = {
|
||||||
silent: true,
|
silent: true,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@ -297,7 +317,6 @@
|
|||||||
splitLine: {
|
splitLine: {
|
||||||
show: false, // Disable x-axis grid lines
|
show: false, // Disable x-axis grid lines
|
||||||
},
|
},
|
||||||
|
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
show: false, // Hide y-axis labels
|
show: false, // Hide y-axis labels
|
||||||
},
|
},
|
||||||
@ -306,21 +325,96 @@
|
|||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: dataType === "Revenue" ? "Revenue Growth" : "EPS Growth",
|
name: dataType === "Revenue" ? "Revenue Growth" : "EPS Growth",
|
||||||
data: (dataType === "Revenue" ? revenueGrowthList : epsGrowthList)
|
data: growthList?.map((value) => ({
|
||||||
?.slice(1)
|
value,
|
||||||
.map((value) => ({
|
itemStyle: {
|
||||||
value,
|
color: value >= 0 ? "#00FC50" : "#D9220E", // Green for >= 0, Red for < 0
|
||||||
itemStyle: {
|
},
|
||||||
color: value >= 0 ? "#00FC50" : "#D9220E", // Green for >= 0, Red for < 0
|
})),
|
||||||
},
|
|
||||||
})),
|
|
||||||
type: "bar",
|
type: "bar",
|
||||||
smooth: true,
|
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: {
|
tooltip: {
|
||||||
trigger: "axis",
|
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 `
|
||||||
|
<b>${dates[dataIndex]}</b><br>
|
||||||
|
High: ${high?.toFixed(2) + "%"}<br>
|
||||||
|
Avg: ${mainValue?.toFixed(2) + "%"}<br>
|
||||||
|
Low: ${low?.toFixed(2) + "%"}
|
||||||
|
`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -372,7 +466,6 @@
|
|||||||
val: item?.estimatedEpsAvg,
|
val: item?.estimatedEpsAvg,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//Values coincide with table values for crosscheck
|
//Values coincide with table values for crosscheck
|
||||||
revenueGrowthList = computeGrowthList(
|
revenueGrowthList = computeGrowthList(
|
||||||
tableActualRevenue,
|
tableActualRevenue,
|
||||||
@ -453,29 +546,29 @@
|
|||||||
>
|
>
|
||||||
Revenue Growth
|
Revenue Growth
|
||||||
</th>
|
</th>
|
||||||
{#each computeGrowthList(tableActualRevenue, tableForecastRevenue) as growth, index}
|
{#each computeGrowthList(tableActualRevenue, tableForecastRevenue) as item, index}
|
||||||
<td
|
<td
|
||||||
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-[#09090B]"
|
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-[#09090B]"
|
||||||
>
|
>
|
||||||
{#if index === 0 || growth === null}
|
{#if index === 0 || item?.growth === null}
|
||||||
n/a
|
n/a
|
||||||
{:else if tableActualRevenue[index]?.val === null}
|
{:else if tableActualRevenue[index]?.val === null}
|
||||||
<span
|
<span
|
||||||
class="text-orange-400 {growth > 0
|
class="text-orange-400 {item?.growth > 0
|
||||||
? "before:content-['+']"
|
? "before:content-['+']"
|
||||||
: ''}"
|
: ''}"
|
||||||
>
|
>
|
||||||
{growth}%*
|
{item?.growth}%*
|
||||||
</span>
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span
|
<span
|
||||||
class={growth > 0
|
class={item?.growth > 0
|
||||||
? "text-[#00FC50] before:content-['+']"
|
? "text-[#00FC50] before:content-['+']"
|
||||||
: growth < 0
|
: item?.growth < 0
|
||||||
? "text-[#FF2F1F]"
|
? "text-[#FF2F1F]"
|
||||||
: ""}
|
: ""}
|
||||||
>
|
>
|
||||||
{growth}%
|
{item?.growth}%
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
@ -507,29 +600,29 @@
|
|||||||
>
|
>
|
||||||
EPS Growth
|
EPS Growth
|
||||||
</th>
|
</th>
|
||||||
{#each computeGrowthList(tableActualEPS, tableForecastEPS) as growth, index}
|
{#each computeGrowthList(tableActualEPS, tableForecastEPS) as item, index}
|
||||||
<td
|
<td
|
||||||
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-[#09090B]"
|
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-[#09090B]"
|
||||||
>
|
>
|
||||||
{#if index === 0 || growth === null}
|
{#if index === 0 || item?.growth === null}
|
||||||
n/a
|
n/a
|
||||||
{:else if tableActualRevenue[index]?.val === null}
|
{:else if tableActualRevenue[index]?.val === null}
|
||||||
<span
|
<span
|
||||||
class="text-orange-400 {growth > 0
|
class="text-orange-400 {item?.growth > 0
|
||||||
? "before:content-['+']"
|
? "before:content-['+']"
|
||||||
: ''}"
|
: ''}"
|
||||||
>
|
>
|
||||||
{growth}%*
|
{item?.growth}%*
|
||||||
</span>
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span
|
<span
|
||||||
class={growth > 0
|
class={item?.growth > 0
|
||||||
? "text-[#00FC50] before:content-['+']"
|
? "text-[#00FC50] before:content-['+']"
|
||||||
: growth < 0
|
: item?.growth < 0
|
||||||
? "text-[#FF2F1F]"
|
? "text-[#FF2F1F]"
|
||||||
: ""}
|
: ""}
|
||||||
>
|
>
|
||||||
{growth}%
|
{item?.growth}%
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
<svelte:options immutable={true} />
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { enhance } from "$app/forms";
|
import { enhance } from "$app/forms";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user