Composite indicators
An indicator can be built on top of other indicators: the inner indicators are calculated first, and their values go
into your own calculation. The built-in indicators (EMA, SMA, MACD and the others) live in the
ATAS.Indicators.Technical namespace of the ATAS.Indicators.Technical assembly, which is part of the SDK
package.
Calculating an inner indicator yourself
The Calculate(bar, value) extension method (class ATAS.Indicators.Extensions) writes value into the source
series of the inner indicator for the bar, calculates it and returns its value for this bar. A MACD built from two
EMAs and an SMA:
using System.Windows.Media;
using ATAS.Indicators;
using ATAS.Indicators.Technical;
using OFT.Rendering.Settings;
namespace ATAS.Docs.Samples.ChartIndicators;
public class MacdCalculateIndicator : Indicator
{
private readonly EMA _long = new();
private readonly EMA _short = new();
private readonly SMA _sma = new();
public int LongPeriod
{
get => _long.Period;
set
{
if (value <= 0)
return;
_long.Period = value;
RecalculateValues();
}
}
public int ShortPeriod
{
get => _short.Period;
set
{
if (value <= 0)
return;
_short.Period = value;
RecalculateValues();
}
}
public int SignalPeriod
{
get => _sma.Period;
set
{
if (value <= 0)
return;
_sma.Period = value;
RecalculateValues();
}
}
public MacdCalculateIndicator()
{
Panel = IndicatorDataProvider.NewPanel;
((ValueDataSeries)DataSeries[0]).VisualType = VisualMode.Histogram;
((ValueDataSeries)DataSeries[0]).Color = Colors.CadetBlue;
DataSeries.Add(new ValueDataSeries("Signal")
{
VisualType = VisualMode.Line,
LineDashStyle = LineDashStyle.Dash
});
LongPeriod = 26;
ShortPeriod = 12;
SignalPeriod = 9;
}
protected override void OnCalculate(int bar, decimal value)
{
var macd = _short.Calculate(bar, value) - _long.Calculate(bar, value);
var signal = _sma.Calculate(bar, macd);
this[bar] = macd;
DataSeries[1][bar] = signal;
}
}
The inner indicators keep their own state from bar to bar, so call Calculate for every bar in ascending order,
unconditionally, from your OnCalculate; skipping bars or calling it
out of order gives wrong values.
Internal indicators
When an inner indicator takes the same source as your indicator, add it with Add
in the constructor instead. An added indicator gets the same source data series and data provider as yours, is
recalculated together with it (before your OnCalculate is called for the bar) and is disposed together with it. Its
values are then read by bar index:
using System.Windows.Media;
using ATAS.Indicators;
using ATAS.Indicators.Technical;
using OFT.Rendering.Settings;
namespace ATAS.Docs.Samples.ChartIndicators;
public class MacdInternalIndicator : Indicator
{
private readonly EMA _long = new();
private readonly EMA _short = new();
private readonly SMA _sma = new();
public int LongPeriod
{
get => _long.Period;
set
{
if (value <= 0)
return;
_long.Period = value;
RecalculateValues();
}
}
public int ShortPeriod
{
get => _short.Period;
set
{
if (value <= 0)
return;
_short.Period = value;
RecalculateValues();
}
}
public int SignalPeriod
{
get => _sma.Period;
set
{
if (value <= 0)
return;
_sma.Period = value;
RecalculateValues();
}
}
public MacdInternalIndicator()
{
Panel = IndicatorDataProvider.NewPanel;
((ValueDataSeries)DataSeries[0]).VisualType = VisualMode.Histogram;
((ValueDataSeries)DataSeries[0]).Color = Colors.CadetBlue;
DataSeries.Add(new ValueDataSeries("Signal")
{
VisualType = VisualMode.Line,
LineDashStyle = LineDashStyle.Dash
});
LongPeriod = 26;
ShortPeriod = 12;
SignalPeriod = 9;
Add(_short);
Add(_long);
}
protected override void OnCalculate(int bar, decimal value)
{
var macd = _short[bar] - _long[bar];
var signal = _sma.Calculate(bar, macd);
this[bar] = macd;
DataSeries[1][bar] = signal;
}
}
Using the data series of an inner indicator
An internal indicator with several DataSeries exposes all of them. The
Signal series of the built-in MACD read from another indicator:
using ATAS.Indicators;
using ATAS.Indicators.Technical;
namespace ATAS.Docs.Samples.ChartIndicators;
public class MacdSignalConsumerIndicator : Indicator
{
private readonly MACD _macd = new();
public MacdSignalConsumerIndicator()
{
Add(_macd);
}
protected override void OnCalculate(int bar, decimal value)
{
var macdSignalSeries = (ValueDataSeries)_macd.DataSeries[1];
var macdSignal = macdSignalSeries[bar];
this[bar] = macdSignal * 2;
}
}
To show a series of an inner indicator on the chart, add it to the DataSeries collection of your indicator:
using ATAS.Indicators;
using ATAS.Indicators.Technical;
namespace ATAS.Docs.Samples.ChartIndicators;
public class MacdSignalDisplayIndicator : Indicator
{
private readonly MACD _macd = new();
public MacdSignalDisplayIndicator()
{
Add(_macd);
DataSeries.Add(_macd.DataSeries[1]);
}
protected override void OnCalculate(int bar, decimal value)
{
}
}