#region Setup using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using ChartForgeX.Core; using ChartForgeX.Primitives; using ChartForgeX.Rendering; using ChartForgeX.Themes; using ChartForgeX.Topology; using ChartForgeX.Typography; using ChartForgeX.VisualArtifacts; using ChartForgeX.VisualBlocks; var mode = VisualThemeMode.Dark; var theme = VisualTheme.Graphite(); var model = CandlestickRecipe.Create("options", mode); var context = new VisualRenderContext( new VisualLayoutOptions(new VisualSize(800, 440)), theme, mode, new VisualFrame("Daily opening and closing prices", "Hollow rising bodies, filled falling bodies and independent wick strokes", showLegend: null, showSurface: false)); var prepared = model.Prepare(context); File.WriteAllText("visual.svg", prepared.ToSvg()); File.WriteAllBytes("visual.png", prepared.ToPng()); File.WriteAllText("visual.html", prepared.ToArtifact("example", VisualArtifactKind.Chart).ToHtmlPage()); #endregion /// Builds daily opening, closing, high and low prices as candlesticks. public static class CandlestickRecipe { /// Creates the selected candlestick example with optional authored body and wick styling. public static Chart Create(string variant = "wide", VisualThemeMode mode = VisualThemeMode.Light) { if (variant is not ("wide" or "compact" or "sparse" or "options" or "compact-options" or "dashed" or "compact-dashed")) throw new ArgumentOutOfRangeException(nameof(variant)); var chart = Chart.Create(); if (variant is "options" or "compact-options" or "dashed" or "compact-dashed") { var prices = ChartSeries.CreateCandlestick(new ChartSeriesId("daily-prices"), "Daily prices", new[] { new ChartCandlestick(1, 44, 58, 38, 53), new ChartCandlestick(2, 53, 60, 42, 45), new ChartCandlestick(3, 46, 55, 41, 46), new ChartCandlestick(4, 47, 47, 47, 47), new ChartCandlestick(5, 47, 63, 44, 58) }); prices.ConfigureFinancial(financial => { financial.Rising.Stroke = ChartColor.FromHex("#2A967B"); financial.Rising.StrokeWidth = 2.4; financial.Falling.Stroke = ChartColor.FromHex("#DB825B"); financial.Falling.StrokeWidth = 2.8; financial.Falling.StrokeOpacity = .85; financial.Rising.FillOpacity = 0; financial.Rising.Wick.Stroke = ChartColor.FromHex("#729BB7"); financial.Rising.Wick.StrokeWidth = 1.2; financial.Falling.Fill = ChartColor.FromHex("#EBA85B"); financial.Falling.FillOpacity = .65; financial.Falling.Wick.Stroke = ChartColor.FromHex("#729BB7"); financial.Falling.Wick.StrokeWidth = 1.4; }).WithPointColor(4, "#729BB7"); if (variant is "dashed" or "compact-dashed") prices.ConfigureFinancial(financial => { financial.Rising.DashPattern = new[] { 7d, 5d, 2d }; financial.Rising.DashOffset = -3; financial.Falling.DashPattern = new[] { 4d, 4d }; financial.Falling.DashOffset = 2; financial.Rising.Wick.DashPattern = Array.Empty(); financial.Falling.Wick.DashOffset = -1; }); chart.WithXLabels("Mon", "Tue", "Wed", "Thu", "Fri").WithYAxis("Price") .WithPointLegend().WithDataLabels(false).AddSeries(prices); } else if (variant == "sparse") { var values = Enumerable.Range(1, 2).Select(index => new ChartCandlestick(index, 30 + index * 3, 44 + index * 3, 20 + index * 3, index % 2 == 0 ? 26 + index * 3 : 38 + index * 3)); chart.WithXLabels("Mon", "Tue", "Wed", "Thu", "Fri", "Sat").AddSeries(ChartSeries.CreateCandlestick(new ChartSeriesId("market-price"), "Market price", values)); } else { // Daily open, high, low and close for every October 2026 trading session. var sessions = new[] { "Oct 1", "Oct 2", "Oct 5", "Oct 6", "Oct 7", "Oct 8", "Oct 9", "Oct 12", "Oct 13", "Oct 14", "Oct 15", "Oct 16", "Oct 19", "Oct 20", "Oct 21", "Oct 22", "Oct 23", "Oct 26", "Oct 27", "Oct 28", "Oct 29", "Oct 30" }; var quotes = new[] { (48.1, 51.3, 47.8, 49.9), (49.7, 50.9, 47.8, 50.6), (51.0, 53.7, 49.1, 52.7), (52.6, 54.4, 50.3, 51.2), (51.6, 53.4, 50.9, 51.1), (51.4, 52.4, 50.3, 51.9), (51.8, 53.7, 49.8, 50.0), (50.0, 52.7, 49.3, 51.1), (50.6, 51.0, 49.1, 49.4), (49.8, 51.0, 46.6, 48.5), (49.0, 50.3, 48.7, 49.9), (50.0, 53.6, 48.8, 52.1), (52.5, 53.1, 49.4, 51.2), (51.4, 54.6, 49.7, 53.3), (53.5, 54.9, 51.6, 52.4), (52.7, 53.8, 50.5, 50.9), (50.4, 52.1, 48.4, 49.1), (48.5, 51.2, 47.1, 50.0), (50.4, 52.9, 50.0, 52.4), (52.3, 54.1, 49.9, 50.9), (50.7, 51.2, 49.6, 50.4), (50.4, 53.6, 49.8, 52.3) }; var prices = ChartSeries.CreateCandlestick(new ChartSeriesId("market-price"), "Market price", quotes.Select((quote, index) => new ChartCandlestick(index + 1, quote.Item1, quote.Item2, quote.Item3, quote.Item4))); // A five-session average of the close smooths the daily movement. var average = ChartSeries.CreateLine(new ChartSeriesId("five-session-average"), "Five-session average", quotes.Select((quote, index) => new ChartPoint(index + 1, Math.Round(quotes.Skip(Math.Max(0, index - 4)).Take(Math.Min(5, index + 1)).Average(item => item.Item4), 2)))); chart.WithXLabels(sessions).WithYAxis("Price (USD)").WithYAxisBounds(44, 58).AddSeries(prices).AddSeries(average); } return chart; } }