Table of Contents

Собственный индикатор

Для того, чтобы создать свой собственный индикатор, необходимо реализовать интерфейс IIndicator. В качестве примера можно взять исходные коды других индикаторов, которые находятся в репозитории GitHub/StockSharp. Вот как выглядит код реализации индикатора простой скользящей средней SimpleMovingAverage:

/// <summary>
/// Простая скользящая средняя.
/// </summary>
[DisplayName("SMA")]
[Description("Простая скользящая средняя.")]
public class SimpleMovingAverage : LengthIndicator<decimal>
{
	/// <summary>
	/// Создать <see cref="SimpleMovingAverage"/>.
	/// </summary>
    public SimpleMovingAverage()
    {
    	Length = 32;
    }
	/// <summary>
	/// Обработать входное значение.
	/// </summary>
	/// <param name="input">Входное значение.</param>
	/// <returns>Результирующее значение.</returns>
	protected override IIndicatorValue OnProcess(IIndicatorValue input)
	{
		var newValue = input.GetValue<decimal>();
		if (input.IsFinal)
		{
			Buffer.Add(newValue);
			if (Buffer.Count > Length)
			Buffer.RemoveAt(0);
		}
		if (input.IsFinal)
			return new DecimalIndicatorValue(this, Buffer.Sum() / Length);
		return new DecimalIndicatorValue(this, (Buffer.Skip(1).Sum() + newValue) / Length);
	}
}

SimpleMovingAverage наследуется от класса LengthIndicator<TResult>, от которого необходимо наследовать все индикаторы, имеющие в качестве параметра длину периода.

Некоторые индикаторы являются составными, и используют в своих рассчетах другие индикаторы. Поэтому индикаторы можно переиспользовать друг из друга, как показано в качестве примера реализация индикатора волатильности Чайкина ChaikinVolatility:

/// <summary>
/// Волатильность Чайкина.
/// </summary>
/// <remarks>
/// http://www2.wealth-lab.com/WL5Wiki/Volatility.ashx
/// http://www.incrediblecharts.com/indicators/chaikin_volatility.php
/// </remarks>
[DisplayName("Волатильность")]
[Description("Волатильность Чайкина.")]
public class ChaikinVolatility : BaseIndicator<IIndicatorValue>
{
	/// <summary>
	/// Создать <see cref="ChaikinVolatility"/>.
	/// </summary>
	public ChaikinVolatility()
	{
		Ema = new ExponentialMovingAverage();
		Roc = new RateOfChange();
	}
	/// <summary>
	/// Скользящая средняя.
	/// </summary>
	[ExpandableObject]
	[DisplayName("MA")]
	[Description("Скользящая средняя.")]
	[Category("Основное")]
	public ExponentialMovingAverage Ema { get; private set; }
	/// <summary>
	/// Скорость изменения.
	/// </summary>
	[ExpandableObject]
	[DisplayName("ROC")]
	[Description("Скорость изменения.")]
	[Category("Основное")]
	public RateOfChange Roc { get; private set; }
	/// <summary>
	/// Сформирован ли индикатор.
	/// </summary>
	public override bool IsFormed
	{
		get { return Roc.IsFormed; }
	}
	/// <summary>
	/// Обработать входное значение.
	/// </summary>
	/// <param name="input">Входное значение.</param>
	/// <returns>Результирующее значение.</returns>
	protected override IIndicatorValue OnProcess(IIndicatorValue input)
	{
		var candle = input.GetValue<Candle>();
		var emaValue = Ema.Process(input.SetValue(this, candle.HighPrice - candle.LowPrice));
		if (Ema.IsFormed)
		{
			return Roc.Process(emaValue);
		}
		return input;				
	}
}

Последний вид индикаторов - это те, которые не просто состоят из других индикаторов, но так же графически отображаются несколькими состояниями одновременно (несколькими линиями). Например, AverageDirectionalIndex:

/// <summary>
/// Индекс среднего направления движения Welles Wilder.
/// </summary>
[DisplayName("ADX")]
[Description("Индекс среднего направления движения Welles Wilder.")]
public class AverageDirectionalIndex : BaseComplexIndicator
{
	/// <summary>
	/// Создать <see cref="AverageDirectionalIndex"/>.
	/// </summary>
	public AverageDirectionalIndex()
		: this(new DirectionalIndex { Length = 14 }, new WilderMovingAverage { Length = 14 })
	{
	}
	/// <summary>
	/// Создать <see cref="AverageDirectionalIndex"/>.
	/// </summary>
	/// <param name="dx">Индекса направленного движения Welles Wilder.</param>
	/// <param name="movingAverage">Скользящая средняя.</param>
	public AverageDirectionalIndex(DirectionalIndex dx, LengthIndicator<decimal> movingAverage)
	{
		if (dx == null)
			throw new ArgumentNullException(nameof(dx));
		if (movingAverage == null)
			throw new ArgumentNullException(nameof(movingAverage));
		InnerIndicators.Add(Dx = dx);
		InnerIndicators.Add(MovingAverage = movingAverage);
		Mode = ComplexIndicatorModes.Sequence;
	}
	/// <summary>
	/// Индекса направленного движения Welles Wilder.
	/// </summary>
	[Browsable(false)]
	public DirectionalIndex Dx { get; private set; }
	/// <summary>
	/// Скользящая средняя.
	/// </summary>
	[Browsable(false)]
	public LengthIndicator<decimal> MovingAverage { get; private set; }
	/// <summary>
	/// Длина периода.
	/// </summary>
	[DisplayName("Период")]
	[Description("Период индикатора.")]
	[Category("Основные")]
	public virtual int Length
	{
		get { return MovingAverage.Length; }
		set
		{
			MovingAverage.Length = Dx.Length = value;
			Reset();
		}
	}
}

Такие индикаторы должны наследоваться от класса BaseComplexIndicator, и передавать в BaseComplexIndicator.InnerIndicators составные части индикатора. BaseComplexIndicator будет обрабатывать данные части один за другим. Если BaseComplexIndicator.Mode установлено в ComplexIndicatorModes.Sequence, то результирующее значение первого индикатора будет передано в качестве входного значения второму, и так далее до конца. Если же установлено значение ComplexIndicatorModes.Parallel, то результаты вложенных индикаторов игнорируются.