From a8211fc1b8d3fc40d31413f825c1746450e75758 Mon Sep 17 00:00:00 2001 From: MuslemRahimi Date: Fri, 28 Feb 2025 16:38:31 +0100 Subject: [PATCH] update chart --- src/lib/components/AnalystEstimate.svelte | 422 ++++++++---------- src/lib/components/EstimationGraph.svelte | 45 +- src/lib/highcharts.ts | 10 +- .../stocks/[tickerID]/forecast/+page.svelte | 2 +- 4 files changed, 203 insertions(+), 276 deletions(-) diff --git a/src/lib/components/AnalystEstimate.svelte b/src/lib/components/AnalystEstimate.svelte index 10e13321..0bb25f8b 100644 --- a/src/lib/components/AnalystEstimate.svelte +++ b/src/lib/components/AnalystEstimate.svelte @@ -303,119 +303,115 @@ }); const option = { - silent: true, - tooltip: { - trigger: "axis", - hideDelay: 100, // Set the delay in milliseconds + credits: { + enabled: false, }, - animation: false, - grid: { - left: "5%", - right: "5%", - bottom: "2%", - top: "5%", - containLabel: true, + legend: { + enabled: false, + }, + plotOptions: { + series: { + animation: false, + }, + }, + chart: { + type: "line", + backgroundColor: "#09090B", + plotBackgroundColor: "#09090B", + height: 360, + animation: false, + }, + title: { + text: null, + }, + tooltip: { + shared: true, + useHTML: true, + backgroundColor: "rgba(0, 0, 0, 0.8)", // Semi-transparent black + borderColor: "rgba(255, 255, 255, 0.2)", // Slightly visible white border + borderWidth: 1, + style: { + color: "#fff", + fontSize: "16px", + padding: "10px", + }, + borderRadius: 4, + formatter: function () { + // Format the x value to display time in hh:mm format + let tooltipContent = `${ + this?.x + }
`; + + // Loop through each point in the shared tooltip + this.points?.forEach((point) => { + tooltipContent += `${point.series.name}: + ${abbreviateNumber( + point.y, + )}
`; + }); + + return tooltipContent; + }, }, xAxis: { - type: "category", - boundaryGap: false, - data: dates, - axisLabel: { - color: "#fff", - }, - }, - tooltip: { - trigger: "axis", - hideDelay: 100, - borderColor: "#969696", // Black border color - borderWidth: 1, // Border width of 1px - backgroundColor: "#313131", // Optional: Set background color for contrast - textStyle: { - color: "#fff", // Optional: Text color for better visibility - }, - formatter: function (params) { - // Get the timestamp from the first parameter - const timestamp = params[0].axisValue; - - // Sort the params array to arrange High, Avg, Low - const sortedParams = params.sort((a, b) => { - const order = { High: 0, Avg: 1, Low: 2 }; - return order[a.seriesName] - order[b.seriesName]; - }); - - // Initialize result with timestamp - let result = timestamp + "
"; - - // Loop through each sorted series data - sortedParams.forEach((param) => { - result += - param.seriesName + ": " + abbreviateNumber(param.value) + "
"; - }); - - return result; - }, - }, - - yAxis: [ - { - type: "value", - splitLine: { - show: false, // Disable x-axis grid lines - }, - - axisLabel: { - show: false, // Hide y-axis labels + categories: dates, + type: "datetime", + labels: { + style: { + color: "#fff", + fontSize: "12px", }, }, - ], + }, + yAxis: { + gridLineWidth: 1, + gridLineColor: "#111827", + labels: { + style: { color: "white" }, + }, + title: { text: null }, + opposite: true, + }, series: [ { name: "Actual", data: valueList, - type: "line", - itemStyle: { - color: "#fff", // Change line plot color to white + color: "#fff", + animation: false, + marker: { + enabled: false, // Hide point symbols }, - showSymbol: false, // Show symbols for line plot points }, { name: "Avg", data: avgList, - type: "line", - itemStyle: { - color: "#fff", // Change line plot color to green + color: "#fff", + dashStyle: "Dash", // Dashed line style + animation: false, + marker: { + enabled: false, }, - lineStyle: { - type: "dashed", // Set the line type to dashed - }, - showSymbol: false, // Show symbols for line plot points }, { name: "Low", data: lowList, - type: "line", - itemStyle: { - color: "#3CB2EF", // Change line plot color to green + // If you want a dashed line with a different color, set the series color to that color. + color: "#c2c7cf", + dashStyle: "Dash", + animation: false, + marker: { + enabled: false, }, - lineStyle: { - type: "dashed", // Set the line type to dashed - color: "#c2c7cf", - }, - - showSymbol: false, // Show symbols for line plot points }, { name: "High", data: highList, - type: "line", - itemStyle: { - color: "#3CB2EF", // Change line plot color to green + color: "#c2c7cf", + dashStyle: "Dash", + animation: false, + marker: { + enabled: false, }, - lineStyle: { - type: "dashed", // Set the line type to dashed - color: "#c2c7cf", - }, - showSymbol: false, // Show symbols for line plot points }, ], }; @@ -451,35 +447,87 @@ ); const optionsGrowth = { - animation: false, - grid: { - left: "5%", - right: "5%", - bottom: "2%", - top: "5%", - containLabel: true, + credits: { + enabled: false, }, - xAxis: { - type: "category", - boundaryGap: false, - data: dates, - axisLabel: { + legend: { + enabled: false, + }, + plotOptions: { + series: { + animation: false, + }, + }, + chart: { + type: "column", + backgroundColor: "#09090B", + plotBackgroundColor: "#09090B", + height: 360, + animation: false, + }, + title: { + text: null, + }, + tooltip: { + shared: true, + useHTML: true, + backgroundColor: "rgba(0, 0, 0, 0.8)", // Semi-transparent black + borderColor: "rgba(255, 255, 255, 0.2)", // Slightly visible white border + borderWidth: 1, + style: { color: "#fff", + fontSize: "14px", + padding: "10px", + }, + borderRadius: 4, + formatter: function () { + // Find the main series point (exclude error bar points) + const mainPoint = this.points.find( + (p) => p.series.type !== "errorbar", + ); + const idx = mainPoint.point.index; + const mainValue = mainPoint.y; + let tooltipContent = `${dates[idx]}
`; + + // Use highGrowthList and lowGrowthList from outer scope + const high = highGrowthList[idx]; + const low = lowGrowthList[idx]; + + if (high && high !== "N/A") { + tooltipContent += `High: ${high.toFixed(2)}
`; + } + if (mainValue && mainValue !== "N/A") { + tooltipContent += `Avg: ${mainValue.toFixed(2)}
`; + } + if (low && low !== "N/A") { + tooltipContent += `Low: ${low.toFixed(2)}
`; + } + return tooltipContent; }, }, - yAxis: [ - { - type: "value", - splitLine: { - show: false, // Disable x-axis grid lines - }, - axisLabel: { - show: false, // Hide y-axis labels + + xAxis: { + categories: dates, + type: "datetime", + labels: { + style: { + color: "#fff", + fontSize: "12px", }, }, - ], + }, + yAxis: { + gridLineWidth: 1, + gridLineColor: "#111827", + labels: { + style: { color: "white" }, + }, + title: { text: null }, + opposite: true, + }, series: [ { + // Dynamically set the series name based on dataType name: dataType === "Revenue" ? "Revenue Growth" @@ -489,129 +537,37 @@ ? "Net Income Growth" : "EBITDA Growth", data: growthList?.map((value) => ({ - value, - itemStyle: { - color: value >= 0 ? "#00FC50" : "#D9220E", // Green for >= 0, Red for < 0 - }, + y: value, + // Set color based on the sign of the value + color: value >= 0 ? "#338D73" : "#ED3333", + borderColor: value >= 0 ? "#338D73" : "#ED3333", + borderRadius: "1px", })), - type: "bar", - smooth: true, - z: 5, // Ensure the bar chart has a lower z-index than the error bars + zIndex: 5, + // 'smooth' is not applicable for column charts }, { name: "Error Bars", - type: "custom", - renderItem: (params, api) => { - const xValue = api.value(0); - const yValue = api.value(1); - - // Select high and low lists based on dataType - const highList = highGrowthList; - const lowList = lowGrowthList; - - // Retrieve the corresponding high and low values - const high = highList[params.dataIndex]; - const low = lowList[params.dataIndex]; - - // Skip rendering error bars if high or low values are null or undefined - if (high == null || low == null) return; // Null or undefined values are skipped - - 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: 2, // Set thicker line width - }, - }, - { - type: "line", - shape: { - x1: x - 5, - y1: highCoord, - x2: x + 5, - y2: highCoord, - }, - style: { - stroke: "#fff", - lineWidth: 2, // Set thicker line width - }, - }, - { - type: "line", - shape: { - x1: x - 5, - y1: lowCoord, - x2: x + 5, - y2: lowCoord, - }, - style: { - stroke: "#fff", - lineWidth: 2, // Set thicker line width - }, - }, - ], - }; + type: "errorbar", + // Prepare data as [low, high] pairs for each index. + data: growthList?.map((value, index) => { + const high = highGrowthList[index]; + const low = lowGrowthList[index]; + // If either high or low is null/undefined, return nulls. + return high != null && low != null ? [low, high] : [null, null]; + }), + color: "#fff", + lineWidth: 2, // Thicker lines for error bars + whiskerLength: 10, // Adjust whisker length as needed + zIndex: 10, + // Disable tooltip for error bar points + tooltip: { + pointFormatter: function () { + return ""; + }, }, - 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", - hideDelay: 100, - borderColor: "#969696", // Black border color - borderWidth: 1, // Border width of 1px - backgroundColor: "#313131", // Optional: Set background color for contrast - textStyle: { - color: "#fff", // Optional: Text color for better visibility - }, - formatter: (params) => { - const dataIndex = params[0].dataIndex; - const mainValue = params[0].value; - - // Select high and low lists based on dataType - const highList = highGrowthList; - const lowList = lowGrowthList; - - // Retrieve the corresponding high and low values - const high = highList[dataIndex]; - const low = lowList[dataIndex]; - - // Only show High and Low if they are not "N/A" - let tooltipContent = `${dates[dataIndex]}
`; - - if (high && high !== "N/A") { - tooltipContent += `High: ${high.toFixed(2)}
`; - } - - if (mainValue && mainValue !== "N/A") { - tooltipContent += `Avg: ${mainValue.toFixed(2)}
`; - } - - if (low && low !== "N/A") { - tooltipContent += `Low: ${low.toFixed(2)}
`; - } - - return tooltipContent; - }, - }, }; if (dataType === "Revenue") { @@ -1096,7 +1052,7 @@ - import { Chart } from "svelte-echarts"; - import { init, use } from "echarts/core"; - import { LineChart, CustomChart } from "echarts/charts"; - import { GridComponent, TooltipComponent } from "echarts/components"; - import { CanvasRenderer } from "echarts/renderers"; + import highcharts from "$lib/highcharts.ts"; import { abbreviateNumber, computeGrowthSingleList } from "$lib/utils"; - use([ - LineChart, - CustomChart, - GridComponent, - TooltipComponent, - CanvasRenderer, - ]); - export let userTier; export let title; - export let options; + export let config; export let tableDataList; export let highDataList; export let avgDataList; @@ -29,9 +17,9 @@

{title} Forecast

-
- {#if options !== null} - +
+ {#if config !== null} +
{/if}
@@ -129,9 +117,9 @@

{title}

-
- {#if options !== null} - +
+ {#if config !== null} +
{/if}
@@ -256,20 +244,3 @@
{/if} - - diff --git a/src/lib/highcharts.ts b/src/lib/highcharts.ts index f72ff679..8d1467c4 100644 --- a/src/lib/highcharts.ts +++ b/src/lib/highcharts.ts @@ -1,7 +1,9 @@ import Highcharts from 'highcharts'; +import HighchartsMore from 'highcharts/highcharts-more'; // Add this import import { browser } from '$app/environment'; if (browser) { + HighchartsMore(Highcharts); // Initialize the extension Highcharts.setOptions({ lang: { numericSymbols: ['K', 'M', 'B', 'T', 'P', 'E'] @@ -58,13 +60,11 @@ export default (node, config) => { createChart(); - // Resize observer with optimized logic + // Resize observer remains the same const resizeObserver = new ResizeObserver(() => { if (chart) { const newWidth = node.clientWidth; - const newHeight = 360; // Let height be auto-adjusted - - // **Dynamically update size without recreating the chart** + const newHeight = 360; chart?.setSize(newWidth, newHeight, false); } }); @@ -79,4 +79,4 @@ export default (node, config) => { if (chart) chart?.destroy(); } }; -}; +}; \ No newline at end of file diff --git a/src/routes/stocks/[tickerID]/forecast/+page.svelte b/src/routes/stocks/[tickerID]/forecast/+page.svelte index 6b2c8d21..aa5ef863 100644 --- a/src/routes/stocks/[tickerID]/forecast/+page.svelte +++ b/src/routes/stocks/[tickerID]/forecast/+page.svelte @@ -652,7 +652,7 @@