import * as am5 from "@amcharts/amcharts5"; import * as am5xy from "@amcharts/amcharts5/xy"; import * as am5stock from "@amcharts/amcharts5/stock"; import am5themes_Animated from "@amcharts/amcharts5/themes/Animated"; import data from "./data.json"; // Create root element // https://www.amcharts.com/docs/v5/getting-started/#Root_element const root = am5.Root.new("chartdiv"); // Set themes // https://www.amcharts.com/docs/v5/concepts/themes/ root.setThemes([ am5themes_Animated.new(root) ]); // Set global number format // https://www.amcharts.com/docs/v5/concepts/formatters/formatting-numbers/ root.numberFormatter.set("numberFormat", "#,###.00"); // Create a stock chart // https://www.amcharts.com/docs/v5/charts/stock-chart/#Instantiating_the_chart const stockChart = root.container.children.push(am5stock.StockChart.new(root, {})); // Create a main stock panel (chart) // https://www.amcharts.com/docs/v5/charts/stock-chart/#Adding_panels const mainPanel = stockChart.panels.push(am5stock.StockPanel.new(root, { wheelY: "zoomX", // the mouse wheel zooms in on the dates panX: true, // dragging the plot moves along the dates... panY: true // ...and up or down the price scale })); // Create axes // https://www.amcharts.com/docs/v5/charts/xy-chart/axes/ const valueAxis = mainPanel.yAxes.push(am5xy.ValueAxis.new(root, { renderer: am5xy.AxisRendererY.new(root, { pan: "zoom" // drag the price labels up or down to stretch or squeeze the scale }), tooltip: am5.Tooltip.new(root, {}), // shows the cursor's price on the axis... extraTooltipPrecision: 2 // ...with two more decimals than the labels })); // a GaplessDateAxis leaves out the dates without data, such as weekends const dateAxis = mainPanel.xAxes.push(am5xy.GaplessDateAxis.new(root, { baseInterval: { // matches the daily data the chart starts with; the switcher changes it timeUnit: "day", count: 1 }, renderer: am5xy.AxisRendererX.new(root, { minorGridEnabled: true // fainter grid lines between the labelled ones }), tooltip: am5.Tooltip.new(root, {}) // shows the cursor's date on the axis })); // The first label would be cut off by the edge of the chart dateAxis.get("renderer").labels.template.set("minPosition", 0.01); // Add series // https://www.amcharts.com/docs/v5/charts/xy-chart/series/ const valueSeries = mainPanel.series.push(am5xy.CandlestickSeries.new(root, { name: "MSFT", valueXField: "Date", valueYField: "Close", highValueYField: "High", lowValueYField: "Low", openValueYField: "Open", xAxis: dateAxis, yAxis: valueAxis, // the hovered candle's prices in the legend legendValueText: "open: [bold]{openValueY}[/] high: [bold]{highValueY}[/] low: [bold]{lowValueY}[/] close: [bold]{valueY}[/]", legendRangeValueText: "" // no values while the pointer is off the plot })); // Set main value series // https://www.amcharts.com/docs/v5/charts/stock-chart/#Setting_main_series stockChart.set("stockSeries", valueSeries); // Add a stock legend // https://www.amcharts.com/docs/v5/charts/stock-chart/stock-legend/ // in the plot container, so it sits over the top left of the plot const valueLegend = mainPanel.plotContainer.children.push(am5stock.StockLegend.new(root, { stockChart: stockChart })); valueLegend.data.setAll([valueSeries]); // Add cursor // https://www.amcharts.com/docs/v5/charts/xy-chart/cursor/ mainPanel.set("cursor", am5xy.XYCursor.new(root, { yAxis: valueAxis, xAxis: dateAxis, snapToSeries: [valueSeries], // the horizontal line jumps to the close of the candle... snapToSeriesBy: "y!" // ...at the date under the pointer })); // Set data: data.json has one dataset per interval, the chart starts with the daily one // https://www.amcharts.com/docs/v5/charts/stock-chart/#Setting_data valueSeries.data.setAll(data.day); // Set up interval switcher: the id of an item is the name of its dataset // https://www.amcharts.com/docs/v5/charts/stock/toolbar/interval-control/ const intervalSwitcher = am5stock.IntervalControl.new(root, { stockChart: stockChart, currentItem: "day", // the control starts on "1 day", to match the data loaded above items: [ { id: "minute", label: "1 minute", interval: { timeUnit: "minute", count: 1 } }, { id: "day", label: "1 day", interval: { timeUnit: "day", count: 1 } }, { id: "week", label: "1 week", interval: { timeUnit: "week", count: 1 } }, { id: "month", label: "1 month", interval: { timeUnit: "month", count: 1 } } ] }); // a picked interval loads its own dataset and sets the axis's base interval to match intervalSwitcher.events.on("selected", (ev) => { const item = ev.item as am5stock.IIntervalControlItem; // The new data covers another period: show all of it valueSeries.events.once("datavalidated", () => { mainPanel.zoomOut(); }); // Tell the axis how far apart the data items are, then swap the data dateAxis.set("baseInterval", { timeUnit: item.interval.timeUnit, count: item.interval.count || 1 }); valueSeries.data.setAll(data[item.id as keyof typeof data]); }); // Add stock toolbar // https://www.amcharts.com/docs/v5/charts/stock/toolbar/ am5stock.StockToolbar.new(root, { container: document.getElementById("chartcontrols")!, // the div above the chart that holds the toolbar stockChart: stockChart, controls: [ intervalSwitcher ] }); // Make stuff animate on load // https://www.amcharts.com/docs/v5/concepts/animations/#Forcing_appearance_animation valueSeries.appear(1000); stockChart.appear(1000, 100);