Table of Contents

Built-in graphic shapes

An indicator can place ready-made shapes on the chart without drawing them itself: horizontal levels, trend lines, rectangles, horizontal lines that last until the first touch, and text labels. For anything else, draw in OnRender (see the drawing article).

Horizontal levels

A LineSeries is a horizontal line at a fixed value, added to the LineSeries collection of the indicator; the user sees it in the settings like a data series. The oscillator below lives in its own panel and has two levels, 30 and 70, like the RSI:

using System.Windows.Media;

using ATAS.Indicators;
using OFT.Rendering.Settings;

namespace ATAS.Docs.Samples.ChartIndicators;

public class HorizontalLevelsIndicator : Indicator
{
    public HorizontalLevelsIndicator()
    {
        Panel = IndicatorDataProvider.NewPanel;

        LineSeries.Add(new LineSeries("Down")
        {
            Color = Colors.Orange,
            LineDashStyle = LineDashStyle.Dash,
            Value = 30,
            Width = 1
        });

        LineSeries.Add(new LineSeries("Up")
        {
            Color = Colors.Orange,
            LineDashStyle = LineDashStyle.Dash,
            Value = 70,
            Width = 1
        });
    }

    protected override void OnCalculate(int bar, decimal value)
    {
        // Position of the close price inside the bar range, in percent (0..100)
        var candle = GetCandle(bar);
        var range = candle.High - candle.Low;

        this[bar] = range == 0 ? 50 : (candle.Close - candle.Low) * 100 / range;
    }
}

The color of a LineSeries is a System.Windows.Media.Color, like the colors of the indicator settings. The shapes below use the System.Drawing types (Pen, Brush, Color); to convert a color from one type to the other, use the Convert() extension method of the ATAS.Indicators namespace.

Trend lines

Create a TrendLine with the first bar and price, the second bar and price and the pen, and add it to the TrendLines collection. The IsRay flag turns the line into a ray.

public TrendLine(int firstBar, decimal firstPrice, int secondBar, decimal secondPrice, Pen pen)

Rectangles

Create a DrawingRectangle with the two corners (bar and price), the pen of the border and the brush of the fill, and add it to the Rectangles collection. The second constructor also takes the pen of the middle horizontal line, drawn when MidLineEnabled is set.

public DrawingRectangle(int firstBar, decimal firstPrice, int secondBar, decimal secondPrice, Pen outlinePen, Brush fillBrush)
public DrawingRectangle(int firstBar, decimal firstPrice, int secondBar, decimal secondPrice, Pen outlinePen, Brush fillBrush, Pen midPen)

Horizontal lines until the first touch

A LineTillTouch starts at a bar and a price and is prolonged until the price touches it. Pass it to AddHorizontalLineTillTouch rather than adding it to the HorizontalLinesTillTouch collection directly: a direct change of the collection works, but makes the indicator rescan all the lines to find the ones that are not touched yet.

public LineTillTouch(int bar, decimal price, Pen pen)                      // until the first touch
public LineTillTouch(int bar, decimal price, Pen pen, int fixedBarsCount)  // a fixed length, completed at once, no touch tracking

Text labels

AddText places a text label at a bar and a price and returns the DrawingText object, whose parameters can be changed later. Adding a text with an existing tag replaces the previous label.

AddText(string tag, string text, bool isAbovePrice, int bar, decimal price,
        int yOffset, int xOffset, Color textColor, Color outlineColor, Color fillColor,
        float fontSize, DrawingText.TextAlign align, bool autoSize = false)

AddText(string tag, string text, bool isAbovePrice, int bar, decimal price,
        Color textColor, Color outlineColor, Color fillColor,
        float fontSize, DrawingText.TextAlign align, bool autoSize = false)

AddText(string tag, string text, bool isAbovePrice, int bar, decimal price,
        Color textColor, Color fillColor, float fontSize, DrawingText.TextAlign align,
        bool autoSize = false)
Parameter Meaning
tag A unique identifier of the label
text The displayed text
isAbovePrice Whether the text is placed above the price or below it
bar, price The position
yOffset, xOffset Offsets in pixels
textColor, outlineColor, fillColor The text, the frame and the background colors (System.Drawing.Color)
fontSize The font size
align Left, right or center
autoSize Scale the text with the chart

Recalculation

When the indicator is recalculated (for example, after RecalculateValues()), it clears the trend lines, the rectangles, the lines until the first touch and the text labels. Add these objects in OnCalculate, so that they are created again during the recalculation. The horizontal levels of the LineSeries collection are not cleared.

The indicator below adds a trend line, a rectangle, a line until the first touch and a text label every 50 bars:

using System.Drawing;

using ATAS.Indicators;
using ATAS.Indicators.Drawing;

namespace ATAS.Docs.Samples.ChartIndicators;

public class ShapesEveryFiftyBarsIndicator : Indicator
{
    private readonly Pen _linePen = new(Color.DodgerBlue, 2);
    private readonly Pen _levelPen = new(Color.Orange, 1);
    private readonly Brush _fillBrush = new SolidBrush(Color.FromArgb(60, Color.Green));
    private int _lastProcessedBar = -1;

    public ShapesEveryFiftyBarsIndicator() : base(true)
    {
        DenyToChangePanel = true;
        DataSeries[0].IsHidden = true;
    }

    protected override void OnCalculate(int bar, decimal value)
    {
        if (bar == 0)
            _lastProcessedBar = -1;

        // OnCalculate is called repeatedly for the last bar, so add the shapes once, for a closed bar
        var closedBar = bar - 1;

        if (closedBar < 10 || closedBar % 50 != 0 || closedBar == _lastProcessedBar)
            return;

        _lastProcessedBar = closedBar;

        var candle = GetCandle(closedBar);
        var prevCandle = GetCandle(closedBar - 10);

        // A trend line between the closes of two bars
        TrendLines.Add(new TrendLine(closedBar - 10, prevCandle.Close, closedBar, candle.Close, _linePen));

        // A rectangle around the range of the bar
        Rectangles.Add(new DrawingRectangle(closedBar - 2, candle.High, closedBar + 2, candle.Low, _linePen, _fillBrush));

        // A horizontal line from the high of the bar until the price touches it
        AddHorizontalLineTillTouch(new LineTillTouch(closedBar, candle.High, _levelPen));

        // A text label above the high of the bar
        AddText($"label{closedBar}", "High", true, closedBar, candle.High, Color.White, Color.Black, Color.DimGray, 10, DrawingText.TextAlign.Center);
    }
}