Table of Contents

Индикаторы

S# стандартно предоставляет более 140 индикаторов технического анализа. Это позволяет не создавать с нуля нужные индикаторы, а использовать уже готовые. Кроме того можно создавать собственные индикаторы, взяв за основу существующие, как показано в разделе Собственный индикатор. Все базовые классы для работы с индикаторами, а также сами индикаторы находятся в пространстве имен StockSharp.Algo.Indicators.

Подключение индикатора в робот

  1. В самом начале нужно создать индикатор. Индикатор создается как и обычный .NET объект:

    var longSma = new SimpleMovingAverage { Length = 80 };
    var shortSma = new SimpleMovingAverage { Length = 30 };
    
    // Рекомендуется добавить индикаторы в коллекцию стратегии
    Indicators.Add(longSma);
    Indicators.Add(shortSma);
    
  2. Далее, необходимо обрабатывать маркет-данные для индикаторов. Наиболее эффективный способ - это использование результата метода Process:

    private void ProcessCandle(ICandleMessage candle)
    {
        // Обрабатываем свечу индикаторами и сразу сохраняем результат
        var longValue = longSma.Process(candle);
        var shortValue = shortSma.Process(candle);
    
        // Используем результаты для принятия торговых решений
        if (shortValue.GetValue<decimal>() > longValue.GetValue<decimal>())
        {
            // Сигнал на покупку
            BuyAtMarket();
        }
    }
    

    Индикатор принимает на вход IIndicatorValue. Некоторые из индикаторов оперируют простым числом, как, например, SimpleMovingAverage. Другим требуются полностью свеча, как, например, MedianPrice. Поэтому входящие значения необходимо приводить или к DecimalIndicatorValue или к CandleIndicatorValue. Результирующее значение индикатора работает по тем же правилам, что и входящее значение.

  3. Результирующее и входящее значение индикатора имеют свойство IIndicatorValue.IsFinal, которое говорит о том, что значение является окончательным и индикатор не будет изменяться в данной точке времени. Например, индикатор SimpleMovingAverage формируется по цене закрытия свечи, но в текущий момент времени окончательная цена закрытия свечи неизвестна и меняется. В таком случае результирующее значение IIndicatorValue.IsFinal будет false. Eсли в индикатор передать законченную свечу, то входящее и результирующее значения IIndicatorValue.IsFinal будут true.

  4. Рекомендуемый подход: непосредственно использовать значения, полученные в результате вызова метода Process, вместо последующего обращения к GetCurrentValue:

    // Пример стратегии с двумя скользящими средними
    private void ProcessCandle(ICandleMessage candle)
    {
        // Обрабатываем свечу индикаторами и сразу сохраняем результаты
        var longValue = _longSma.Process(candle);
        var shortValue = _shortSma.Process(candle);
    
        // Рисуем на графике
        DrawCandlesAndIndicators(candle, longValue, shortValue);
    
        if (!IsFormedAndOnlineAndAllowTrading()) 
            return;
    
        // Используем полученные значения для сравнения
        var isShortLessCurrent = shortValue.GetValue<decimal>() < longValue.GetValue<decimal>();
        var isShortLessPrev = _shortSma.GetValue(1) < _longSma.GetValue(1);
    
        // Проверяем, произошло ли пересечение
        if (isShortLessCurrent == isShortLessPrev) 
            return;
    
        var volume = Volume + Math.Abs(Position);
    
        // Торговые действия на основе сигнала
        if (isShortLessCurrent)
            SellMarket(volume);
        else
            BuyMarket(volume);
    }
    

    Такой подход имеет следующие преимущества:

    • Соответствует стриминговой модели обработки данных (получили → обработали → использовали результат)
    • Более производительный, так как избегает повторного обращения к контейнеру накопленных значений
    • Устраняет возможные рассинхронизации между вызовом Process и последующим GetCurrentValue
  5. Не рекомендуемый подход (менее эффективный):

    // Неоптимальный подход
    foreach (var candle in candles)
    {
        // Обработали свечу, но игнорируем возвращаемое значение
        _longSma.Process(candle);
        _shortSma.Process(candle);
    }
    
    // Позже пытаемся получить значения через GetCurrentValue()
    var isShortLessThenLong = _shortSma.GetCurrentValue() < _longSma.GetCurrentValue();
    

    При таком подходе происходит дополнительное обращение к контейнеру исторических значений индикатора, что вносит задержки и нарушает стриминговую модель обработки данных.

  6. У всех индикаторов есть свойство BaseIndicator.IsFormed, которое говорит о том готов ли индикатор к использованию. Например, индикатор SimpleMovingAverage имеет период, и пока индикатор не обработает количество свечей, равное периоду индикатора, индикатор будет считаться не готовым к использованию. И свойство BaseIndicator.IsFormed будет false.

Пример полной стратегии на скользящих средних

Ниже приведен пример стратегии, которая правильно использует индикаторы, обрабатывая свечи и используя результаты метода Process:

public class SmaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _series;
	private readonly StrategyParam<int> _longSmaLength;
	private readonly StrategyParam<int> _shortSmaLength;

	private SimpleMovingAverage _longSma;
	private SimpleMovingAverage _shortSma;

	private IChartIndicatorElement _longSmaIndicatorElement;
	private IChartIndicatorElement _shortSmaIndicatorElement;
	private IChartCandleElement _chartCandleElement;
	private IChartTradeElement _tradesElem;
	private IChart _chart;

	public SmaStrategy()
	{
		base.Name = "SMA strategy";

		// Инициализация параметров стратегии
		_longSmaLength = Param(nameof(LongSmaLength), 80);
		_shortSmaLength = Param(nameof(ShortSmaLength), 30);
		_series = Param(nameof(Series), DataType.TimeFrame(TimeSpan.FromMinutes(15)));
	}

	protected override void OnStarted(DateTimeOffset time)
	{
		base.OnStarted(time);

		// Создание индикаторов
		_shortSma = new SimpleMovingAverage { Length = _shortSmaLength.Value };
		_longSma = new SimpleMovingAverage { Length = _longSmaLength.Value };

		// Добавление индикаторов в коллекцию стратегии
		Indicators.Add(_shortSma);
		Indicators.Add(_longSma);

		// Инициализация графика
		_chart = GetChart();
		if (_chart != null)
		{
			InitChart();
		}
		
		// Подписка на свечи
		var subscription = new Subscription(_series.Value, Security);

		Connector
			.WhenCandlesFinished(subscription)
			.Do(ProcessCandle)
			.Apply(this);

		Connector.Subscribe(subscription);
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		// Обработка свечи индикаторами и сохранение результатов
		var longValue = _longSma.Process(candle);
		var shortValue = _shortSma.Process(candle);
		
		// Отрисовка на графике
		DrawCandlesAndIndicators(candle, longValue, shortValue);
		
		// Проверка условий для торговли
		if (!IsFormedAndOnlineAndAllowTrading()) 
			return;

		// Сравнение текущих и предыдущих значений индикаторов
		var isShortLessCurrent = shortValue.GetValue<decimal>() < longValue.GetValue<decimal>();
		var isShortLessPrev = _shortSma.GetValue(1) < _longSma.GetValue(1);

		// Проверка на пересечение
		if (isShortLessCurrent == isShortLessPrev) 
			return;

		var volume = Volume + Math.Abs(Position);

		// Торговые действия на основе сигнала
		if (isShortLessCurrent)
			SellMarket(volume);
		else
			BuyMarket(volume);
	}

	private void DrawCandlesAndIndicators(ICandleMessage candle, IIndicatorValue longSma, IIndicatorValue shortSma)
	{
		if (_chart == null) return;
		var data = _chart.CreateData();
		data.Group(candle.OpenTime)
			.Add(_chartCandleElement, candle)
			.Add(_longSmaIndicatorElement, longSma)
			.Add(_shortSmaIndicatorElement, shortSma);
		_chart.Draw(data);
	}

	// Другие методы инициализации графика опущены для краткости
}

Этот пример демонстрирует правильный подход к работе с индикаторами в стриминговой модели StockSharp.