Стратегия переносит логику советника MetaTrader 5 «MA Cross» (файл MA Cross.mq5) на платформу StockSharp. Она отслеживает две настраиваемые скользящие средние и открывает рыночные позиции при их пересечении. Параметры стратегии позволяют выбрать тип скользящей средней, используемую цену и сдвиг индикаторов — полностью аналогично исходному скрипту.
Логика работы
Подписка на поток свечей, определяемый параметром CandleType.
Расчёт быстрой и медленной скользящих средних на каждой закрытой свече. Для обеих линий доступны методы SMA, EMA, SMMA и LWMA, а также все варианты прикладных цен из MetaTrader: Close, Open, High, Low, Median, Typical и Weighted.
Сохранение последних значений индикаторов с учётом заданного сдвига, чтобы тестировать пересечения на данных предыдущих баров при необходимости.
Фиксация бычьего сигнала, когда быстрая средняя переходит из области ниже медленной в область выше; фиксация медвежьего сигнала при обратном движении.
Отправка рыночных приказов только после формирования обеих средних и проверки готовности стратегии. При появлении сигнала выполняется разворот позиции: закрывается существующая позиция и открывается новая позиция объёмом 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_cross_strategy(Strategy):
def __init__(self):
super(ma_cross_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 5)
self._slow_period = self.Param("SlowPeriod", 21)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15)))
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev_values = False
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(ma_cross_strategy, self).OnStarted2(time)
self._has_prev_values = False
self._prev_fast = 0.0
self._prev_slow = 0.0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
fast = float(fast_val)
slow = float(slow_val)
if self._has_prev_values:
cross_up = self._prev_fast <= self._prev_slow and fast > slow
cross_down = self._prev_fast >= self._prev_slow and fast < slow
if cross_up and self.Position <= 0:
self.BuyMarket()
elif cross_down and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast
self._prev_slow = slow
self._has_prev_values = True
def OnReseted(self):
super(ma_cross_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev_values = False
def CreateClone(self):
return ma_cross_strategy()