Table of Contents

Annotations

Install: dotnet add package MatPlotLibNet

Arrow annotations with background

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .WithTitle("Annotation Enhancements")
        .Plot(x, y, line => { line.Label = "Data"; })
        .Annotate("Peak", 8, 8.9, ann =>
        {
            ann.ArrowTargetX = 8;
            ann.ArrowTargetY = 8.9;
            ann.ArrowStyle   = ArrowStyle.FancyArrow;
            ann.BackgroundColor = Colors.White;
        })
        .Annotate("Rotated label", 2, 4.5, ann =>
        {
            ann.Rotation  = -30;
            ann.Alignment = TextAlignment.Center;
        }))
    .Save("annotations.svg");

Annotations

Horizontal reference line

Mark thresholds, targets, or boundaries:

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y, s => s.Label = "Data")
        .AxHLine(5.0, l =>
        {
            l.Color = Colors.Red;
            l.LineStyle = LineStyle.Dashed;
            l.LineWidth = 1.5;
            l.Label = "Threshold";
        })
        .WithLegend())
    .Save("hline.svg");

Vertical reference line

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y)
        .AxVLine(5.0, l =>
        {
            l.Color = Colors.Green;
            l.LineStyle = LineStyle.DashDot;
            l.Label = "Event";
        })
        .WithLegend())
    .Save("vline.svg");

Shaded span regions

Highlight a range on the axis:

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y, s => s.Label = "Signal")
        // Vertical span: highlight an X range
        .AxVSpan(3.0, 6.0, s =>
        {
            s.Color = Colors.Yellow;
            s.Alpha = 0.3;
            s.Label = "Active period";
        })
        // Horizontal span: highlight a Y range
        .AxHSpan(4.0, 7.0, s =>
        {
            s.Color = Css4Colors.LightBlue;
            s.Alpha = 0.2;
            s.Label = "Target range";
        })
        .WithLegend())
    .Save("spans.svg");

Multiple reference lines

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y, s => s.Label = "Price")
        .AxHLine(20, l => { l.Color = Colors.Green; l.LineStyle = LineStyle.Dashed; l.Label = "Support"; })
        .AxHLine(70, l => { l.Color = Colors.Red;   l.LineStyle = LineStyle.Dashed; l.Label = "Resistance"; })
        .AxVLine(5,  l => { l.Color = Colors.Gray;  l.LineStyle = LineStyle.Dotted; l.Label = "Earnings date"; })
        .WithLegend(LegendPosition.UpperLeft))
    .Save("multi_ref.svg");

A reference line takes part in the automatic range, as matplotlib's axhline and axvline do: a line beyond the data widens the axis to hold it, exactly as a data point at that value would, so the line is always drawn inside the plot. The same holds for AddLevel, the levels of AddFibonacci and the line of Threshold. Under a limit you set (SetYLim, SetXLim, a zoom), a line beyond the limit is not drawn, and neither is its label; a line standing exactly on a limit is drawn on that edge. A line at a value the axis cannot place (double.NaN, an infinity, 0 or less on a log axis) is left out.

A shaded span does not widen the range. It is cut at the edge of the plot: an edge beyond what the axis shows runs to the plot edge, and a span wholly beyond it draws nothing.

Threshold convenience

Threshold(...) is a single-call shortcut. It adds a dashed reference line and a shaded breach-zone span in one step. Use it instead of calling AxHLine and AxHSpan yourself when you only need to mark a breach boundary.

double[] x = Enumerable.Range(0, 100).Select(i => (double)i).ToArray();
double[] y = x.Select(v => 50 + 30 * Math.Sin(v * 0.15)).ToArray();

// Shade everything ABOVE 70 (overbought zone)
Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y, s => s.Label = "Signal")
        .Threshold(70.0, Orientation.Horizontal, ThresholdBreach.Above,
            color: Colors.Red, label: "Overbought")
        .WithLegend())
    .Save("threshold_above.svg");

Shade the below side for a support floor:

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y, s => s.Label = "Signal")
        .Threshold(30.0, Orientation.Horizontal, ThresholdBreach.Below,
            color: Colors.Tab10Green, label: "Oversold")
        .WithLegend())
    .Save("threshold_below.svg");

Stack both to create a corridor with upper and lower bounds:

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y, s => s.Label = "RSI")
        .Threshold(70.0, Orientation.Horizontal, ThresholdBreach.Above, Colors.Red,   "Overbought")
        .Threshold(30.0, Orientation.Horizontal, ThresholdBreach.Below, Colors.Tab10Green, "Oversold")
        .SetYLabel("RSI")
        .WithLegend())
    .Save("threshold_corridor.svg");

Vertical thresholds work the same way. Orientation.Vertical marks a constant X:

// Shade everything to the RIGHT of an event date
DateTime eventDate = new DateTime(2026, 6, 15);

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(dates, prices, s => s.Label = "Price")
        .SetXDateAxis()
        .Threshold(eventDate.ToOADate(), Orientation.Vertical, ThresholdBreach.Above,
            color: Colors.Tab10Orange, label: "Event")
        .WithLegend())
    .Save("threshold_vertical.svg");

Threshold parameters

Parameter Type Default Description
value double (required) Threshold position in data coordinates.
orientation Orientation (required) Horizontal draws a constant Y line; Vertical draws a constant X line.
breach ThresholdBreach (required) Above shades above or to the right of the line; Below shades below or to the left.
color Color? null Colour for both the dashed line and the span fill. null uses the renderer cycle colour.
label string? null Annotation text placed at the threshold value. null adds no annotation.

The dashed line is a ReferenceLine with LineStyle.Dashed. You can change it afterwards through axes.ReferenceLines. The zone runs from the line to the edge of the plot (double.MaxValue for Above, double.MinValue for Below), cut where the plot ends. The line takes part in the automatic range, so a threshold above everything the chart shows is still drawn, with its zone up to the top of the plot. Beyond a limit you set, neither the line nor the zone is drawn.

Combining annotations with math text

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y)
        .Annotate(@"$R^{2} = 0.97$", 3, 7, ann =>
        {
            ann.BackgroundColor = Colors.White;
            ann.Font = new Font { Size = 14 };
        })
        .Annotate(@"$\frac{\partial f}{\partial x} = 0$", 8, 3, ann =>
        {
            ann.ArrowTargetX = 8;
            ann.ArrowTargetY = y[8];
            ann.ArrowStyle = ArrowStyle.FancyArrow;
        }))
    .Save("math_annotations.svg");

Fluent API reference

Method Parameters Description
.Annotate(text, x, y, cfg?) text, position Text annotation at (x, y)
.Annotate(text, x, y, arrowX, arrowY, cfg?) text, position, arrow target Text with an arrow pointing to the target
.AxHLine(y, cfg?) y position Horizontal reference line
.AxVLine(x, cfg?) x position Vertical reference line
.AxHSpan(yMin, yMax, cfg?) y range Horizontal shaded band
.AxVSpan(xMin, xMax, cfg?) x range Vertical shaded band
.Threshold(value, orientation, breach, color?, label?) value, orientation, breach Dashed reference line and shaded breach span

Annotation properties

Property Type Description
ArrowTargetX/Y double Arrow endpoint
ArrowStyle ArrowStyle FancyArrow, Simple, ...
BackgroundColor Color Text background
FontSize double Font size
Rotation double Text rotation in degrees
Alignment TextAlignment Left, Center, Right

ReferenceLine properties

Property Type Description
Color Color Line color
LineStyle LineStyle Solid, Dashed, Dotted, DashDot
LineWidth double Line width
Label string Legend label

SpanRegion properties

Property Type Description
Color Color Fill color
Alpha double Transparency (0–1)
Label string Legend label