diff --git a/src/lib/components/AnalystEstimate.svelte b/src/lib/components/AnalystEstimate.svelte
index 14bec02c..4e65c624 100644
--- a/src/lib/components/AnalystEstimate.svelte
+++ b/src/lib/components/AnalystEstimate.svelte
@@ -17,6 +17,8 @@
let xData = [];
let optionsRevenue = null;
let optionsEPS = null;
+ let optionsRevenueGrowth = null;
+ let optionsEPSGrowth = null;
let revenueDateList = [];
let avgRevenueList = [];
let lowRevenueList = [];
@@ -26,8 +28,53 @@
let lowEPSList = [];
let highEPSList = [];
+ let revenueGrowthList = [];
+ let epsGrowthList = [];
+
let displayData = "Revenue";
+ function computeGrowthList(tableActualRevenue, tableForecastRevenue) {
+ return tableActualRevenue?.map((item, index) => {
+ // If it's the first item or the list is empty, return null
+ if (index === 0 || tableActualRevenue.length === 0) {
+ return null;
+ }
+
+ // If actual value is null, check forecast
+ 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
+ }
+
+ 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;
+ }
+
+ // 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
+ }
+
+ 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;
+ });
+ }
+
function findIndex(data) {
const currentYear = new Date().getFullYear();
@@ -105,8 +152,8 @@
},
animation: false,
grid: {
- left: "2%",
- right: "2%",
+ left: "5%",
+ right: "5%",
bottom: "2%",
top: "5%",
containLabel: true,
@@ -183,6 +230,56 @@
],
};
+ const optionsGrowth = {
+ animation: false,
+ grid: {
+ left: "5%",
+ right: "5%",
+ bottom: "2%",
+ top: "5%",
+ containLabel: true,
+ },
+ xAxis: {
+ type: "category",
+ boundaryGap: false,
+ data: dates,
+ axisLabel: {
+ color: "#fff",
+ },
+ },
+ yAxis: [
+ {
+ type: "value",
+ splitLine: {
+ show: false, // Disable x-axis grid lines
+ },
+
+ axisLabel: {
+ show: false, // Hide y-axis labels
+ },
+ },
+ ],
+ 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
+ },
+ })),
+ type: "bar",
+ smooth: true,
+ },
+ ],
+
+ tooltip: {
+ trigger: "axis",
+ },
+ };
+
let currentYearSuffix = new Date().getFullYear().toString().slice(-2);
let searchString = `FY${currentYearSuffix}`;
@@ -190,6 +287,7 @@
if (dataType === "Revenue") {
let currentYearIndex = dates?.findIndex((date) => date === searchString);
optionsRevenue = option;
+ optionsRevenueGrowth = optionsGrowth;
revenueDateList = dates?.slice(currentYearIndex) || [];
avgRevenueList = avgList?.slice(currentYearIndex) || [];
lowRevenueList = lowList?.slice(currentYearIndex) || [];
@@ -198,6 +296,8 @@
let currentYearIndex = dates?.findIndex((date) => date === searchString);
optionsEPS = option;
+ optionsEPSGrowth = optionsGrowth;
+
epsDateList = dates?.slice(currentYearIndex) || [];
avgEPSList = avgList?.slice(currentYearIndex) || [];
lowEPSList = lowList?.slice(currentYearIndex) || [];
@@ -213,6 +313,9 @@
tableActualEPS = [];
tableForecastEPS = [];
+ revenueGrowthList = [];
+ epsGrowthList = [];
+
let filteredData =
analystEstimateList?.filter((item) => item.date >= 2015) ?? [];
@@ -229,38 +332,26 @@
numOfAnalysts: item?.numOfAnalysts,
});
});
+
//EPS Data
- let forwardPeStart = false;
filteredData?.forEach((item) => {
- const fy = Number(String(item?.date)?.slice(-2));
- const actualVal = item?.eps ?? null;
- const forecastVal = item?.estimatedEpsAvg;
-
tableActualEPS?.push({
- FY: fy,
- val: actualVal,
+ FY: Number(String(item?.date)?.slice(-2)),
+ val: item?.eps,
+ });
+ tableForecastEPS?.push({
+ FY: Number(String(item?.date)?.slice(-2)),
+ val: item?.estimatedEpsAvg,
});
-
- if (actualVal === null) {
- forwardPeStart = true;
- }
-
- const forecastEntry: any = {
- FY: fy,
- val: forecastVal,
- numOfAnalysts: item?.numOfAnalysts,
- };
-
- // Add forwardPe if the condition is met
- if (forwardPeStart && forecastVal !== null) {
- forecastEntry.forwardPe =
- Math.round((data.getStockQuote.price / forecastVal) * 100) / 100;
- } else {
- forecastEntry.forwardPe = null;
- }
-
- tableForecastEPS?.push(forecastEntry);
});
+
+ //Values coincide with table values for crosscheck
+ revenueGrowthList = computeGrowthList(
+ tableActualRevenue,
+ tableForecastRevenue,
+ );
+
+ epsGrowthList = computeGrowthList(tableActualEPS, tableForecastEPS);
}
$: {
@@ -269,11 +360,10 @@
analystEstimateList = [];
analystEstimateList = data?.getAnalystEstimate || [];
if (analystEstimateList?.length !== 0) {
+ prepareData();
$analystEstimateComponent = true;
getPlotOptions("Revenue");
getPlotOptions("EPS");
-
- prepareData();
} else {
$analystEstimateComponent = false;
}
@@ -335,66 +425,30 @@
>
Revenue Growth
- {#each tableActualRevenue as item, index}
+ {#each computeGrowthList(tableActualRevenue, tableForecastRevenue) as growth, index}
- {#if index === 0 || tableActualRevenue?.length === 0}
+ {#if index === 0 || growth === null}
n/a
- {:else if item?.val === null}
- {#if tableForecastRevenue[index]?.val - tableForecastRevenue[index - 1]?.val > 0}
-
- {(() => {
- const previousVal =
- tableForecastRevenue[index - 1]?.val ?? 0;
- const currentVal =
- tableForecastRevenue[index]?.val ?? 0;
- const change =
- ((currentVal - previousVal) /
- Math.abs(previousVal)) *
- 100;
- return isFinite(change)
- ? `${change.toFixed(2)}%*`
- : "n/a";
- })()}
-
- {:else if tableForecastRevenue[index]?.val - tableForecastRevenue[index - 1]?.val < 0}
-
- {(() => {
- const previousVal =
- tableForecastRevenue[index - 1]?.val ?? 0;
- const currentVal =
- tableForecastRevenue[index]?.val ?? 0;
- const change =
- ((currentVal - previousVal) /
- Math.abs(previousVal)) *
- 100;
- return isFinite(change)
- ? `${change.toFixed(2)}%*`
- : "n/a";
- })()}
-
- {:else}
- n/a
- {/if}
- {:else if item?.val - tableActualRevenue[index - 1]?.val > 0}
-
- {(
- ((item?.val - tableActualRevenue[index - 1]?.val) /
- Math.abs(tableActualRevenue[index - 1]?.val)) *
- 100
- )?.toFixed(2)}%
-
- {:else if item?.val - tableActualRevenue[index - 1]?.val < 0}
-
- {(
- ((item?.val - tableActualRevenue[index - 1]?.val) /
- Math.abs(tableActualRevenue[index - 1]?.val)) *
- 100
- )?.toFixed(2)}%
+ {:else if tableActualRevenue[index]?.val === null}
+
+ {growth}%*
{:else}
- 0.00%
+ 0
+ ? "text-[#00FC50] before:content-['+']"
+ : growth < 0
+ ? "text-[#FF2F1F]"
+ : ""}
+ >
+ {growth}%
+
{/if}
|
{/each}
@@ -425,50 +479,30 @@
>
EPS Growth
- {#each tableActualEPS as item, index}
+ {#each computeGrowthList(tableActualEPS, tableForecastEPS) as growth, index}
- {#if index === 0 || tableActualEPS?.length === 0}
+ {#if index === 0 || growth === null}
n/a
- {:else if item?.val === null}
- {#if tableForecastEPS[index]?.val - tableForecastEPS[index - 1]?.val > 0}
-
- {(
- ((tableForecastEPS[index]?.val -
- tableForecastEPS[index - 1]?.val) /
- Math.abs(tableForecastEPS[index - 1]?.val)) *
- 100
- )?.toFixed(2)}%*
-
- {:else if tableForecastEPS[index]?.val - tableForecastEPS[index - 1]?.val < 0}
-
- {(
- ((tableForecastEPS[index]?.val -
- tableForecastEPS[index - 1]?.val) /
- Math.abs(tableForecastEPS[index - 1]?.val)) *
- 100
- )?.toFixed(2)}%*
-
- {/if}
- {:else if item?.val - tableActualEPS[index - 1]?.val > 0}
-
- {(
- ((item?.val - tableActualEPS[index - 1]?.val) /
- Math.abs(tableActualEPS[index - 1]?.val)) *
- 100
- )?.toFixed(2)}%
-
- {:else if item?.val - tableActualEPS[index - 1]?.val < 0}
-
- {(
- ((item?.val - tableActualEPS[index - 1]?.val) /
- Math.abs(tableActualEPS[index - 1]?.val)) *
- 100
- )?.toFixed(2)}%
+ {:else if tableActualRevenue[index]?.val === null}
+
+ {growth}%*
{:else}
- 0.00%
+ 0
+ ? "text-[#00FC50] before:content-['+']"
+ : growth < 0
+ ? "text-[#FF2F1F]"
+ : ""}
+ >
+ {growth}%
+
{/if}
|
{/each}
@@ -647,6 +681,115 @@
+
+
Revenue Growth
+
+
+ {#if optionsRevenueGrowth !== null}
+
+ {/if}
+
+
+
+ | Revenue Growth |
+ {#each revenueDateList as date}
+ {date} |
+ {/each}
+
+ | High |
+ {#each highRevenueList as val, index}
+
+ {#if data?.user?.tier !== "Pro" && index >= highRevenueList?.length - 2}
+ Pro
+ {:else}
+ {abbreviateNumber(val)}
+ {/if}
+ |
+ {/each}
+
| Avg |
+ {#each avgRevenueList as val, index}
+
+ {#if data?.user?.tier !== "Pro" && index >= avgRevenueList?.length - 2}
+ Pro
+ {:else}
+ {abbreviateNumber(val)}
+ {/if}
+ |
+ {/each}
+
| Low |
+ {#each lowRevenueList as val, index}
+
+ {#if data?.user?.tier !== "Pro" && index >= lowRevenueList?.length - 2}
+ Pro
+ {:else}
+ {abbreviateNumber(val)}
+ {/if}
+ |
+ {/each}
+
+
+
+
+
+
EPS Forecast
@@ -755,6 +898,114 @@
+
+
EPS Growth
+
+
+ {#if optionsEPSGrowth !== null}
+
+ {/if}
+
+
+
+ | EPS Growth |
+ {#each epsDateList as date}
+ {date} |
+ {/each}
+
+ | High |
+ {#each highEPSList as val, index}
+
+ {#if data?.user?.tier !== "Pro" && index >= highEPSList?.length - 2}
+ Pro
+ {:else}
+ {abbreviateNumber(val)}
+ {/if}
+ |
+ {/each}
+
| Avg |
+ {#each avgEPSList as val, index}
+
+ {#if data?.user?.tier !== "Pro" && index >= avgEPSList?.length - 2}
+ Pro
+ {:else}
+ {abbreviateNumber(val)}
+ {/if}
+ |
+ {/each}
+
| Low |
+ {#each lowEPSList as val, index}
+
+ {#if data?.user?.tier !== "Pro" && index >= lowEPSList?.length - 2}
+ Pro
+ {:else}
+ {abbreviateNumber(val)}
+ {/if}
+ |
+ {/each}
+
+
+
+
+