Индикаторы
S# стандартно предоставляет более 140 индикаторов технического анализа. Это позволяет не создавать с нуля нужные индикаторы, а использовать уже готовые. Кроме того можно создавать собственные индикаторы, взяв за основу существующие, как показано в разделе Собственный индикатор. Все базовые классы для работы с индикаторами, а также сами индикаторы находятся в пространстве имен StockSharp.Algo.Indicators.
Подключение индикатора в робот
В самом начале нужно создать индикатор. Индикатор создается как и обычный .NET объект:
var longSma = new SimpleMovingAverage { Length = 80 }; var shortSma = new SimpleMovingAverage { Length = 30 }; // Рекомендуется добавить индикаторы в коллекцию стратегии Indicators.Add(longSma); Indicators.Add(shortSma);
Далее, необходимо обрабатывать маркет-данные для индикаторов. Наиболее эффективный способ - это использование результата метода 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. Результирующее значение индикатора работает по тем же правилам, что и входящее значение.
Результирующее и входящее значение индикатора имеют свойство IIndicatorValue.IsFinal, которое говорит о том, что значение является окончательным и индикатор не будет изменяться в данной точке времени. Например, индикатор SimpleMovingAverage формируется по цене закрытия свечи, но в текущий момент времени окончательная цена закрытия свечи неизвестна и меняется. В таком случае результирующее значение IIndicatorValue.IsFinal будет false. Eсли в индикатор передать законченную свечу, то входящее и результирующее значения IIndicatorValue.IsFinal будут true.
Рекомендуемый подход: непосредственно использовать значения, полученные в результате вызова метода 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
Не рекомендуемый подход (менее эффективный):
// Неоптимальный подход foreach (var candle in candles) { // Обработали свечу, но игнорируем возвращаемое значение _longSma.Process(candle); _shortSma.Process(candle); } // Позже пытаемся получить значения через GetCurrentValue() var isShortLessThenLong = _shortSma.GetCurrentValue() < _longSma.GetCurrentValue();
При таком подходе происходит дополнительное обращение к контейнеру исторических значений индикатора, что вносит задержки и нарушает стриминговую модель обработки данных.
У всех индикаторов есть свойство 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.