Стратегия пересечения EMA 5/8 повторяет работу советника MetaTrader 5_8macrossv2.mq4, сопоставляя две настраиваемые скользящие средние по одному инструменту. Бычье пересечение быстрой скользящей над медленной открывает длинную позицию, медвежье пересечение открывает короткую. Перенос использует высокоуровневый API StockSharp и добавляет управление рисками (тейк-профит, стоп-лосс, трейлинг-стоп).
Торговая логика
На выбранном таймфрейме рассчитываются две скользящие средние. По умолчанию используется 5-периодная экспоненциальная средняя по ценам закрытия и 8-периодная экспоненциальная средняя по ценам открытия.
Когда быстрая средняя пересекает медленную снизу вверх на последней завершённой свече, стратегия открывает или разворачивает длинную позицию. Если открыта короткая позиция, её объём добавляется к рыночному ордеру на покупку для разворота.
Когда быстрая средняя пересекает медленную сверху вниз, стратегия аналогично открывает или разворачивает короткую позицию.
Параметры сдвига смещают сигналы на заданное количество закрытых свечей. Отрицательные значения приводятся к нулю, так как значения «из будущего» недоступны при онлайновом расчёте.
Управление рисками
Тейк-профит и стоп-лосс задаются в пунктах (шагах цены). При открытии позиции целевые уровни рассчитываются от цены входа. Для коротких позиций логика симметрична.
Трейлинг-стоп также задаётся в пунктах и подтягивается в сторону прибыли только в благоприятную сторону (вверх для лонга, вниз для шорта).
Если на завершённой свече достигается любой защитный уровень (хай достигает тейк-профита, лоу — стоп-лосса или трейлинг-стопа), позиция закрывается рыночным ордером, а внутреннее состояние стратегии сбрасывается.
Параметры
Имя
Тип
Значение по умолчанию
Описание
TradeVolume
decimal
0.1
Объём ордера при открытии. При развороте добавляется абсолютная величина текущей позиции.
TakeProfitPips
decimal
40
Расстояние до тейк-профита в пунктах. 0 отключает уровень.
StopLossPips
decimal
0
Расстояние до стоп-лосса в пунктах. 0 отключает уровень.
TrailingStopPips
decimal
0
Дистанция трейлинг-стопа в пунктах. 0 отключает подтягивание.
FastPeriod
int
5
Период быстрой скользящей средней.
FastShift
int
-1
Горизонтальный сдвиг быстрой средней. Отрицательные значения приводятся к нулю.
FastMethod
MovingAverageMethod
Exponential
Тип сглаживания быстрой средней: Simple, Exponential, Smoothed, LinearWeighted.
FastPrice
AppliedPrice
Close
Цена свечи для расчёта быстрой средней.
SlowPeriod
int
8
Период медленной скользящей средней.
SlowShift
int
0
Горизонтальный сдвиг медленной средней.
SlowMethod
MovingAverageMethod
Exponential
Тип сглаживания медленной средней.
SlowPrice
AppliedPrice
Open
Цена свечи для расчёта медленной средней.
CandleType
DataType
TimeSpan.FromMinutes(30).TimeFrame()
Тип свечей, используемых в расчётах.
Примечания
Расчёты выполняются только по завершённым свечам, что предотвращает ложные сигналы.
Защитные уровни рассчитываются через Security.PriceStep. Если шаг цены не задан, параметры управления рисками не активируются.
Версия на Python намеренно не создавалась в рамках задания.
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>
/// 5/8 MA Cross Protect strategy - EMA(5) and EMA(8) crossover.
/// Buys when EMA(5) crosses above EMA(8).
/// Sells when EMA(5) crosses below EMA(8).
/// </summary>
public class FiveEightMaCrossProtectStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FiveEightMaCrossProtectStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 8)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevFast = fast;
_prevSlow = slow;
}
}
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
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class five_eight_ma_cross_protect_strategy(Strategy):
def __init__(self):
super(five_eight_ma_cross_protect_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 5).SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 8).SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
@property
def fast_period(self): return self._fast_period.Value
@property
def slow_period(self): return self._slow_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(five_eight_ma_cross_protect_strategy, self).OnReseted()
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
def OnStarted2(self, time):
super(five_eight_ma_cross_protect_strategy, self).OnStarted2(time)
self._has_prev = False
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.process_candle).Start()
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished: return
f = float(fast); s = float(slow)
if not self._has_prev:
self._prev_fast = f; self._prev_slow = s; self._has_prev = True; return
if self._prev_fast <= self._prev_slow and f > s and self.Position <= 0:
if self.Position < 0: self.BuyMarket()
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and f < s and self.Position >= 0:
if self.Position > 0: self.SellMarket()
self.SellMarket()
self._prev_fast = f; self._prev_slow = s
def CreateClone(self): return five_eight_ma_cross_protect_strategy()