Table of Contents

Financial Charts

Install: dotnet add package MatPlotLibNet

Financial dashboard template

The built-in FigureTemplates.FinancialDashboard creates a 3-panel layout. The top panel shows the price with Bollinger Bands. The middle panel shows volume. The bottom panel shows an oscillator, such as RSI or CCI.

FigureTemplates.FinancialDashboard(
        open, high, low, close, vol,
        title: "ACME Corp — 50 Day",
        configurePricePanel: ax => ax.BollingerBands(20),
        configureVolumePanel: ax => ax
            .SetYTickLocator(new MaxNLocator(3))
            .SetYTickFormatter(new EngFormatter()),
        configureOscillatorPanel: ax => ax
            .Rsi(close, 14)
            .SetYLim(0, 100)
            .AxHLine(70, l => { l.Color = Colors.Red;   l.LineStyle = LineStyle.Dashed; })
            .AxHLine(20, l => { l.Color = Colors.Green; l.LineStyle = LineStyle.Dashed; }))
    .WithSize(1200, 900)
    .Save("financial_dashboard.svg");

Financial dashboard

Technical indicators

This example plots four indicators: Williams %R, On-Balance Volume, Parabolic SAR, and CCI:

Plt.Create()
    .WithTitle("Momentum and Volume Indicators")
    .WithSize(1000, 800)
    .WithGridSpec(2, 2)
    .TightLayout()
    .AddSubPlot(new GridPosition(0, 1, 0, 1), ax =>
    {
        ax.Plot(x, close);
        ax.ParabolicSar(high, low);
        ax.WithTitle("Parabolic SAR");
    })
    .AddSubPlot(new GridPosition(0, 1, 1, 2), ax =>
    {
        ax.WilliamsR(MatPlotLibNet.Indicators.OhlcvBars.Hlc(high, low, close), 14);
        ax.WithTitle("Williams %R");
    })
    .AddSubPlot(new GridPosition(1, 2, 0, 1), ax =>
    {
        ax.Obv(close, vol);
        ax.WithTitle("On-Balance Volume");
    })
    .AddSubPlot(new GridPosition(1, 2, 1, 2), ax =>
    {
        ax.Cci(MatPlotLibNet.Indicators.OhlcvBars.Hlc(high, low, close), 20);
        ax.WithTitle("CCI(20)");
    })
    .Save("phase_f_indicators.svg");

Technical indicators

Relative Rotation Graph (RRG)

The RelativeRotation method renders a JdK-style 2D scatter of (RS-Ratio, RS-Momentum) per asset against a benchmark. It adds a fading tail and a 100/100 quadrant grid.

Basic usage

// 104 weekly bars per asset, vs BTC benchmark
double[][] altCloses = [ethWeekly, bnbWeekly, solWeekly];
string[]   labels    = ["ETH", "BNB", "SOL"];

Plt.Create()
    .WithTitle("Crypto Rotation — Weekly DualEma(10,26)")
    .WithSize(900, 700)
    .AddSubPlot(1, 1, 1, ax => ax
        .SetXLabel("RS-Ratio")
        .SetYLabel("RS-Momentum")
        .RelativeRotation(altCloses, btcWeekly, labels, s =>
        {
            s.TailLength = 12;
            s.ColorMap   = QualitativeColorMaps.Tab10;
        }))
    .Save("rrg_crypto.svg");

With absorption + ENB overlays

When you have a portfolio-level absorption ratio and ENB (Effective Number of Bets), pass them as overlays. Each dot on the trail then also shows market stress:

// absorption[t] ∈ [0..1]: 0 = diversified (green), 1 = panic (red)
// enb[t]: larger = more diversified portfolio; radius ∝ ENB
double[] absorption = absorptionTimeSeries.AbsorptionRatio;
double[] enb        = absorptionTimeSeries.Enb;

Plt.Create()
    .WithTitle("Rotation + Risk Overlay")
    .WithSize(900, 700)
    .AddSubPlot(1, 1, 1, ax => ax
        .RelativeRotation(altCloses, btcWeekly, labels, s =>
        {
            s.AbsorptionRatioPerBar = absorption;
            s.EnbPerBar             = enb;
            s.TailLength            = 12;
        }))
    .Save("rrg_with_overlays.svg");

Visual encoding:

  • The dot fill maps through RdYlGn reversed. Green means low absorption, and red means high absorption (panic).
  • The asset colour becomes the edge ring of each dot.
  • The dot radius is max(1.5, enb × 1.5) px. The head dot is 1.5× larger than the trail dots.
  • A gray ghost trail is drawn behind the per-point circles.

Formula selection

// ZScore formula for equities-vs-index RRGs
ax.RelativeRotation(closes, benchmark, labels, s =>
{
    s.Formula          = RrgFormula.ZScore;
    s.ShortPeriod      = 14;   // rolling z-score window
    s.MomentumLookback = 14;   // ROC lookback before z-scoring momentum
});

See RelativeRotationSeries for minimum data requirements per formula.