#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 = FinancialLayersRecipe.Create("wide", mode); var context = new VisualRenderContext( new VisualLayoutOptions(new VisualSize(800, 460)), theme, mode, new VisualFrame("Prices across trading sessions", "Candles, reference close and expected range", showLegend: true, 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 /// Combines retained price observations, a reference line and a range across explicit trading sessions. public static class FinancialLayersRecipe { /// Creates a compact or wide chart after a keyed price update, preserving the surviving quote's datum style. public static Chart Create(string variant = "wide", VisualThemeMode mode = VisualThemeMode.Light) { if (variant is not ("wide" or "compact")) throw new ArgumentOutOfRangeException(nameof(variant)); var friday = new DateTime(2026, 10, 16, 9, 0, 0, DateTimeKind.Utc); var monday = friday.AddDays(3); var times = new[] { friday.AddHours(1), friday.AddHours(3), friday.AddHours(5), monday.AddHours(1), monday.AddHours(3), monday.AddHours(5) }; var prices = ChartSeries.CreateCandlestick(new ChartSeriesId("101"), "Prices", new[] { new ChartCandlestick(times[0], 102, 106, 100, 105), new ChartCandlestick(times[1], 105, 108, 102, 104), new ChartCandlestick(times[2], 104, 109, 103, 108), new ChartCandlestick(times[3], 108, 112, 106, 110), new ChartCandlestick(times[4], 110, 113, 107, 109), new ChartCandlestick(times[5], 109, 115, 108, 114) }).WithObservationIds(Enumerable.Range(1001, times.Length).Select(value => new ChartObservationId(value.ToString(CultureInfo.InvariantCulture)))); prices.ConfigureFinancial(options => { options.Rising.Stroke = ChartColor.FromHex("#218C74"); options.Rising.FillOpacity = .25; options.Falling.Stroke = ChartColor.FromHex("#CF6D45"); options.Falling.FillOpacity = .25; }); prices.ConfigureFinancialValue(1, options => { options.Stroke = ChartColor.FromHex("#3987E5"); options.StrokeWidth = 2.5; options.Wick.Stroke = ChartColor.FromHex("#3987E5"); }); prices.ApplyFinancialChanges(new[] { ChartObservationChange.Remove(new ChartObservationId("1001")), ChartObservationChange.Replace(new ChartObservationId("1002"), new ChartCandlestick(times[1], 105, 109, 102, 107)), ChartObservationChange.Append(new ChartObservationId("1007"), new ChartCandlestick(monday.AddHours(7), 114, 118, 112, 116)) }, maximumObservations: 6); var expected = ChartSeries.CreateRangeArea(new ChartSeriesId("102"), "Expected range", prices.FinancialValues.Select((value, index) => new ChartRangeBand(value.X, 101 + index * 1.5, 110 + index * 1.5))) .WithColor(ChartColor.FromHex("#729BB7")); var reference = ChartSeries.CreateLine(new ChartSeriesId("103"), "Reference close", prices.FinancialValues.Select((value, index) => new ChartPoint(value.X, 104 + index * 1.8))) .WithColor(ChartColor.FromHex("#E2A336")); reference.ConfigureMarkers(options => { options.Enabled = true; options.Radius = 2.5; }); return Chart.Create().WithSize(variant == "compact" ? 390 : 800, 460).WithTheme(VisualTheme.Graphite(), mode) .WithTitle("Prices across trading sessions").WithSubtitle("Candles, reference close and expected range") .WithLegend().WithDataLabels(false).WithYAxis("Price").WithYAxisBounds(98, 120) .WithValueFormat(ChartValueFormat.Number("0.0", CultureInfo.InvariantCulture)) .WithXAxisTimeScale(TimeZoneInfo.Utc, showTimeZone: true) .WithXAxisBounds(friday.ToOADate(), monday.AddHours(8).ToOADate()) .ConfigureXAxis(axis => axis.WithTimeSessions(new ChartTimeSessionDomain(new[] { new ChartTimeSession(friday, friday.AddHours(8)), new ChartTimeSession(monday, monday.AddHours(8)) }))) .AddSeries(expected).AddSeries(prices).AddSeries(reference); } }