Открыть на GitHub

Стратегия пересечения скользящих средних

Обзор

Стратегия переносит логику советника MetaTrader 5 «MA Cross» (файл MA Cross.mq5) на платформу StockSharp. Она отслеживает две настраиваемые скользящие средние и открывает рыночные позиции при их пересечении. Параметры стратегии позволяют выбрать тип скользящей средней, используемую цену и сдвиг индикаторов — полностью аналогично исходному скрипту.

Логика работы

  1. Подписка на поток свечей, определяемый параметром CandleType.
  2. Расчёт быстрой и медленной скользящих средних на каждой закрытой свече. Для обеих линий доступны методы SMA, EMA, SMMA и LWMA, а также все варианты прикладных цен из MetaTrader: Close, Open, High, Low, Median, Typical и Weighted.
  3. Сохранение последних значений индикаторов с учётом заданного сдвига, чтобы тестировать пересечения на данных предыдущих баров при необходимости.
  4. Фиксация бычьего сигнала, когда быстрая средняя переходит из области ниже медленной в область выше; фиксация медвежьего сигнала при обратном движении.
  5. Отправка рыночных приказов только после формирования обеих средних и проверки готовности стратегии. При появлении сигнала выполняется разворот позиции: закрывается существующая позиция и открывается новая позиция объёмом OrderVolume в направлении сигнала.

Стратегия работает исключительно с завершёнными свечами и не анализирует незакрытые бары. Вызов StartProtection() включает стандартную защиту StockSharp, контролирующую открытые позиции.

Параметры

Имя Значение по умолчанию Описание
FastPeriod 3 Период быстрой скользящей средней.
SlowPeriod 13 Период медленной скользящей средней.
FastMethod Simple Метод расчёта быстрой средней (SMA, EMA, SMMA или LWMA).
SlowMethod LinearWeighted Метод расчёта медленной средней.
FastPriceType Close Прикладная цена для быстрой средней.
SlowPriceType Median Прикладная цена для медленной средней.
FastShift 0 Сдвиг значений быстрой средней в барах.
SlowShift 0 Сдвиг значений медленной средней в барах.
OrderVolume 1 Объём рыночного приказа.
CandleType Таймфрейм 1 минута Тип свечей, на основе которых ведётся расчёт.

Все параметры зарегистрированы через StrategyParam, поэтому их можно оптимизировать в стандартных инструментах StockSharp.

Правила торговли

  • Открытие длинной позиции: Быстрая средняя пересекает медленную снизу вверх (с учётом сдвигов). Если открыта короткая позиция, отправляется один ордер BuyMarket объёмом, достаточным для закрытия шорта и открытия новой длинной позиции. При отсутствии позиции покупается объём OrderVolume.
  • Открытие короткой позиции: Быстрая средняя пересекает медленную сверху вниз. Аналогично, длинная позиция закрывается и открывается шорт объёмом OrderVolume.
  • Без наращивания позиции: Повторные сигналы в направлении текущей позиции игнорируются до появления пересечения в противоположную сторону.
  • Исполнение: Используются только рыночные ордера BuyMarket и SellMarket. Стоп-лоссы и тейк-профиты не задаются и могут настраиваться внешними модулями.

Особенности конверсии

  • Перечень методов скользящих средних полностью повторяет перечисление ENUM_MA_METHOD из MetaTrader и сопоставляется с классами SimpleMovingAverage, ExponentialMovingAverage, SmoothedMovingAverage и WeightedMovingAverage в StockSharp.
  • Расчёт прикладных цен реализован вручную по формулам MetaTrader, что обеспечивает идентичные значения для Median, Typical и Weighted.
  • Логика сдвига (FastShift, SlowShift) сохранена: значения индикаторов буферизуются и берутся с нужного отставания по числу баров.
  • Управление позициями повторяет исходный алгоритм: противоположный сигнал закрывает текущую позицию и сразу открывает новую в обратную сторону.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Moving average crossover strategy converted from MetaTrader 5 script.
/// Implements dual SMA crossover signals.
/// </summary>
public class MaCrossStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrevValues;

	/// <summary>
	/// Fast moving average period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow moving average period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Candle type used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public MaCrossStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Period for the fast moving average", "Moving Averages")
			.SetOptimize(2, 20, 1);

		_slowPeriod = Param(nameof(SlowPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Period for the slow moving average", "Moving Averages")
			.SetOptimize(5, 60, 1);

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles for calculations", "Data");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0m;
		_prevSlow = 0m;
		_hasPrevValues = false;
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrevValues = false;

		var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, ProcessCandle)
			.Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_hasPrevValues)
		{
			var crossUp = _prevFast <= _prevSlow && fast > slow;
			var crossDown = _prevFast >= _prevSlow && fast < slow;

			if (crossUp && Position <= 0)
			{
				BuyMarket(Volume + Math.Abs(Position));
			}
			else if (crossDown && Position >= 0)
			{
				SellMarket(Volume + Math.Abs(Position));
			}
		}

		_prevFast = fast;
		_prevSlow = slow;
		_hasPrevValues = true;
	}
}