Открыть на GitHub

Стратегия Triple SMA Spread

Общее описание

Данная стратегия — это порт экспертного советника MetaTrader 5 3sma.mq5 (id 21495) на платформу StockSharp. Логика полностью сохраняет идею оригинала: сделки открываются, когда три скользящие средние расходятся друг от друга не менее чем на заданный спред. Реализация использует высокоуровневый API StockSharp с подпиской на свечи и автоматическим связыванием индикаторов, поэтому вручную управлять временными рядами не требуется.

Поведение оригинального советника MT5

Изначальная версия опирается на три простые скользящие средние с разными периодами и смещениями. Быстрая средняя рассчитывается по текущему бару, вторая и третья смещены на один и два бара назад. На каждом тике советник выполняет следующие действия:

  1. Переводит пользовательский спред из пунктов (pips) в ценовые единицы, учитывая точность котировок инструмента.
  2. Закрывает длинные позиции, когда быстрая SMA опускается ниже средней SMA минимум на половину спреда, и закрывает короткие позиции, когда быстрая SMA поднимается выше средней SMA на половину спреда.
  3. Открывает новую длинную позицию, если одновременно выполняются условия MA1 > MA2 + spread и MA2 > MA3 + spread, при этом ранее открытые длинные сделки советника отсутствуют. Аналогично, короткая позиция открывается при зеркальном расположении средних.
  4. Работает только рыночными ордерами, использует фиксированный лот и не ставит стоп-лосс/тейк-профит.

Особенности реализации на StockSharp

  • Индикаторы. Используются три экземпляра SimpleMovingAverage, подписанные на один и тот же источник свечей. Небольшие буферы истории воспроизводят MT5-параметр «смещение», чтобы сравнения выполнялись по значениям закрытых баров с нужным лагом.
  • Работа со спредом. Параметр спреда задаётся в пунктах. Стратегия вычисляет размер пункта из Security.PriceStep (или Security.Step) и умножает его на десять для инструментов с трёх- и пятизнакными котировками, повторяя MT5-подстройку под дробные пункты.
  • Ордерный поток. Заявки отправляются методами BuyMarket/SellMarket. При смене направления объём автоматически увеличивается на величину текущей нетто-позиции, что позволяет одной сделкой закрыть противоположное плечо и открыть новую позицию нужного направления.
  • Визуализация. При наличии графика стратегия отображает свечи, три скользящие средние и совершённые сделки.

Параметры

Имя Описание Значение по умолчанию
Volume Объём рыночного ордера. 0.1
FastMaPeriod Период быстрой SMA (MA1). 9
FastMaShift Смещение быстрой SMA по числу закрытых баров. 0
MiddleMaPeriod Период средней SMA (MA2). 14
MiddleMaShift Смещение средней SMA по числу закрытых баров. 1
SlowMaPeriod Период медленной SMA (MA3). 29
SlowMaShift Смещение медленной SMA по числу закрытых баров. 2
MaSpreadPips Минимальный спред между соседними SMA в пунктах. 10
CandleType Тип свечей для расчётов. Таймфрейм 1 минута

Логика торговли

  1. Дождаться формирования всех трёх скользящих средних и заполнения буферов истории значениями с требуемыми смещениями.
  2. Перевести спред из пунктов в денежные единицы и вычислить половину спреда для фильтра выхода.
  3. Выходы.
    • Закрыть длинную позицию, если смещённая быстрая SMA опустилась ниже смещённой средней SMA минимум на половину спреда.
    • Закрыть короткую позицию, если смещённая быстрая SMA поднялась выше смещённой средней SMA минимум на половину спреда.
  4. Входы.
    • Открыть (или развернуть в) длинную позицию, когда быстрая SMA превышает среднюю SMA на полный спред и средняя SMA превышает медленную SMA на тот же спред.
    • Открыть (или развернуть в) короткую позицию при зеркальном расположении — обе разницы меньше отрицательного спреда.

Отличия от версии MT5

  • StockSharp оперирует единой нетто-позицией. При развороте отправляется один рыночный ордер, который сначала закрывает противоположную позицию, а затем открывает новую. В MT5 эксперт мог одновременно держать независимые buy- и sell-ордера.
  • Конвертация пунктов использует доступные свойства Security. Если брокер не предоставляет шаг цены, стратегия применяет значение 1 как резервный вариант.
  • Расчёты выполняются на закрытии свечей, а не на каждом тике, поскольку высокоуровневый API работает со свечными подписками.
  • Подробные сообщения о результатах сделок из оригинального кода не переносятся — при необходимости можно задействовать стандартный логгер StockSharp.

Рекомендации по использованию

  • Подбирайте таймфрейм свечей в соответствии с настройками, использованными в MT5.
  • Корректируйте значение спреда при торговле инструментами с нестандартным размером пункта.
  • Помните, что сигналы появляются только после завершения текущей свечи.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Triple SMA strategy that trades when three moving averages are properly aligned.
/// Enters long when fast > middle > slow, enters short when fast &lt; middle &lt; slow.
/// </summary>
public class TripleSmaSpreadStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _middlePeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private int _prevSignal;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int MiddlePeriod
	{
		get => _middlePeriod.Value;
		set => _middlePeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public TripleSmaSpreadStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast SMA period", "Indicators");

		_middlePeriod = Param(nameof(MiddlePeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Middle Period", "Middle SMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 29)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow SMA period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevSignal = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevSignal = 0;

		var fastSma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowSma = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastSma, slowSma, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastSma);
			DrawIndicator(area, slowSma);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var close = candle.ClosePrice;
		var signal = 0;
		if (fast > slow && close > fast)
			signal = 1;
		else if (fast < slow && close < fast)
			signal = -1;

		if (signal == _prevSignal)
			return;

		var oldSignal = _prevSignal;
		_prevSignal = signal;

		if (signal == 1 && oldSignal <= 0)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		else if (signal == -1 && oldSignal >= 0)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}
	}
}