Стратегия Two MA Other TimeFrame Correct Intersection
Обзор
Эта стратегия представляет собой порт MetaTrader 5 советника «Two MA Other TimeFrame Correct Intersection» на платформу StockSharp. В оригинале используются две скользящие средние, каждая из которых рассчитывается на собственном таймфрейме (например, H1 и D1), а торговые решения принимаются на таймфрейме графика. Конверсия полностью сохраняет мульти-таймфреймовую логику: длинные позиции открываются при пересечении быстрой средней выше медленной, короткие — когда быстрая опускается ниже медленной. Все сделки исполняются рыночными ордерами, перед открытием новой позиции стратегия закрывает противоположную, что соответствует работе торгового движка MQL5.
Торговая логика
Подписка на три потока свечей: основной торговый таймфрейм, таймфрейм быстрой средней и таймфрейм медленной средней.
Расчет быстрой и медленной скользящих средних на их собственных таймфреймах. Доступны те же методы сглаживания и источники цены, что и в исходном индикаторе iCustom.
При необходимости к значениям индикаторов применяется горизонтальный сдвиг, полностью повторяющий параметр ma_shift оригинального эксперта.
После закрытия каждой свечи основного таймфрейма проверяется факт пересечения между текущими и предыдущими значениями скользящих средних:
Если ранее быстрая средняя была ниже медленной, а теперь стала выше, закрываются шорты и открывается (или разворачивается в) длинная позиция.
Если ранее быстрая средняя была выше медленной, а теперь оказалась ниже, закрываются лонги и открывается (или разворачивается в) короткая позиция.
Все ордера используют заданный объем. При развороте позиции стратегия увеличивает размер заявки на величину противоположной позиции, чтобы переворот произошёл одним рыночным ордером.
Параметры
Параметр
Описание
TradeVolume
Базовый объем рыночных сделок для длинных и коротких входов.
CandleType
Основной торговый таймфрейм. Сигналы оцениваются при закрытии свечи этого типа.
FastTimeFrame
Таймфрейм, на котором считается быстрая скользящая средняя.
SlowTimeFrame
Таймфрейм, на котором считается медленная скользящая средняя.
FastLength
Количество свечей в расчете быстрой средней.
SlowLength
Количество свечей в расчете медленной средней.
FastShift
Горизонтальный сдвиг значения быстрой средней перед сравнением.
SlowShift
Горизонтальный сдвиг значения медленной средней перед сравнением.
FastMethod
Метод сглаживания быстрой средней (простая, экспоненциальная, сглаженная, линейно-взвешенная).
SlowMethod
Метод сглаживания медленной средней.
FastAppliedPrice
Цена свечи, используемая в быстрой средней (open, high, low, close, median, typical, weighted).
SlowAppliedPrice
Цена свечи, используемая в медленной средней.
Особенности реализации
Скользящие средние рассчитываются через высокоуровневые подписки StockSharp (SubscribeCandles().Bind(...)) и продолжают работу, даже если их таймфрейм отличается от торгового.
Параметры сдвига реализованы с помощью небольших очередей, которые задерживают выход индикатора на заданное число баров, полностью повторяя поведение ma_shift.
В OnStarted вызывается StartProtection(), что обеспечивает базовую защиту позиции, аналогичную защитным блокам оригинального движка.
В режиме бэктеста на график выводятся свечи основного таймфрейма и обе скользящие средние, что упрощает визуальный контроль пересечений.
В исходном советнике отсутствуют стоп-лосс, тейк-профит или трейлинг-стоп; при необходимости риск-менеджмент можно добавить отдельными модулями StockSharp.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Dual moving average crossover strategy.
/// Converted from the "Two MA Other TimeFrame Correct Intersection" MQL5 expert advisor.
/// Uses fast and slow SMA on a single timeframe with crossover signals.
/// </summary>
public class TwoMAOtherTimeFrameCorrectIntersectionStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private ExponentialMovingAverage _fastMa;
private ExponentialMovingAverage _slowMa;
private decimal? _prevFast;
private decimal? _prevSlow;
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public TwoMAOtherTimeFrameCorrectIntersectionStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe used for signal evaluation", "General");
_fastLength = Param(nameof(FastLength), 20)
.SetDisplay("Fast MA Length", "Number of bars for the fast moving average", "Indicators")
.SetGreaterThanZero();
_slowLength = Param(nameof(SlowLength), 50)
.SetDisplay("Slow MA Length", "Number of bars for the slow moving average", "Indicators")
.SetGreaterThanZero();
}
/// <summary>
/// Primary candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of bars used by the fast moving average.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// Number of bars used by the slow moving average.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
_fastMa = new ExponentialMovingAverage { Length = FastLength };
_slowMa = new ExponentialMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastMa, _slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fastMa.IsFormed || !_slowMa.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_prevFast is null || _prevSlow is null)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
// Fast crosses above slow => buy
if (_prevFast < _prevSlow && fastValue > slowValue)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
// Fast crosses below slow => sell
else if (_prevFast > _prevSlow && fastValue < slowValue)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_prevFast = null;
_prevSlow = null;
_fastMa = null;
_slowMa = null;
base.OnReseted();
}
}