Стратегия переносит MQL4-советник «EA - Moving Average» (файл EA - Moving Average.mq4) в инфраструктуру StockSharp.
Она отслеживает пересечения двух экспоненциальных скользящих средних (EMA) и открывает сделку в начале каждой новой свечи, если условия входа выполняются.
Логика работы
Подписывается на поток свечей, задаваемый параметром CandleType, и рассчитывает на нём быструю и медленную EMA.
Анализирует только закрытые свечи: используются значения EMA предыдущей (shift=1) и позапрошлой (shift=2) свечей, что полностью повторяет логику вызовов iMA в оригинальном коде.
Покупка выполняется, если на предыдущей свече быстрая EMA расположилась выше медленной, а на позапрошлой — ниже, и при этом угол наклона остаётся восходящим.
Продажа выполняется, если на предыдущей свече быстрая EMA опустилась ниже медленной, а на позапрошлой оставалась выше, что соответствует «мертвому кресту».
Пока позиция не закрыта и нет активных заявок, новых ордеров не создаётся (OrdersTotal()==0 в терминах MetaTrader).
Управление рисками и ордерами
Перед подачей рыночного ордера стратегия проверяет текущий спред. Если доступны котировки best bid/ask, разница переводится в «пункты» через PriceStep и сравнивается с порогом MaxSpreadPoints. При превышении лимита сигнал пропускается, что соответствует проверке MarketInfo(..., MODE_SPREAD) в MQL4.
После отправки рыночного ордера автоматически активируются защитные уровни:
Стоп-лосс выставляется на значение медленной EMA предыдущей свечи плюс-минус StopLossPoints пунктов.
Тейк-профит рассчитывается зеркально относительно цены входа: расстояние до цели равно расстоянию до стопа (Ask + (Ask - StopLoss) / Bid - (StopLoss - Bid) из оригинала).
Любые расстояния, заданные в пунктах, конвертируются в абсолютные цены через Security.PriceStep, что обеспечивает идентичное поведение с настройками MetaTrader.
Особенности портирования
В исходном коде присутствует выбор типа скользящей средней, но значение по умолчанию — EMA (MAMode = 1). Перенос реализует именно этот вариант, чтобы не усложнять интерфейс параметров. При необходимости можно расширить стратегию дополнительными вариантами сглаживания.
Объём сделки задаётся параметром TradeVolume и синхронизируется со свойством Strategy.Volume в методе OnStarted.
Для соблюдения требований высокоуровневого API не используются дополнительные коллекции: хранится только пара последних значений EMA, необходимых для детекции пересечения.
Параметры
CandleType — тип и таймфрейм свечей, используемых в расчётах.
FastPeriod — период быстрой EMA (по умолчанию 21).
SlowPeriod — период медленной EMA (по умолчанию 84).
StopLossPoints — расстояние до стоп-лосса в пунктах относительно медленной EMA предыдущей свечи.
MaxSpreadPoints — максимально допустимый спред в пунктах.
TradeVolume — объём рыночных ордеров.
Практические рекомендации
Перед запуском выберите нужный инструмент и таймфрейм, чтобы EMA в StockSharp совпадали с расчётами в терминале MetaTrader.
Для корректной работы фильтра по спреду требуется поток Level1-котировок (best bid/ask). При отсутствии котировок фильтр считается пройденным.
Проверьте, что у инструмента задан PriceStep. Без него стратегия не сможет пересчитать пункты в цены и пропустит установку стопов и тейков.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Moving average crossover strategy using two EMAs.
/// Enters long on golden cross, short on death cross.
/// </summary>
public class MovingAverageCrossoverSpreadStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public MovingAverageCrossoverSpreadStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "General");
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 50)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastMa = new ExponentialMovingAverage { Length = FastPeriod };
var slowMa = new ExponentialMovingAverage { Length = SlowPeriod };
decimal? prevFast = null;
decimal? prevSlow = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastMa, slowMa, (candle, fastValue, slowValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (prevFast.HasValue && prevSlow.HasValue)
{
var crossUp = prevFast.Value <= prevSlow.Value && fastValue > slowValue;
var crossDown = prevFast.Value >= prevSlow.Value && fastValue < slowValue;
if (crossUp && Position <= 0)
BuyMarket();
else if (crossDown && Position >= 0)
SellMarket();
}
prevFast = fastValue;
prevSlow = slowValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastMa);
DrawIndicator(area, slowMa);
DrawOwnTrades(area);
}
}
}