Стратегия повторяет логику советника EMA_6_12 для MetaTrader: рассчитывает EMA(6) и EMA(12) на выбранных свечах (по умолчанию — часовые), и после закрытия свечи реагирует на пересечения скользящих средних.
Логика торговли
Вход в позицию
Сигнал на покупку возникает, когда EMA(6) пересекает EMA(12) снизу вверх. Если позиции нет, открывается длинная.
Сигнал на продажу возникает, когда EMA(6) пересекает EMA(12) сверху вниз. Если позиции нет, открывается короткая.
Выход
При включённом параметре UseCloseSignals (по умолчанию) текущая позиция закрывается при противоположном пересечении. После закрытия стратегия ждёт следующего пересечения, что соответствует поведению оригинального эксперта.
Необязательные уровни тейк-профита и трейлинг-стопа управляются через StartProtection из StockSharp.
Размер позиции
Объём ордера определяется параметром OrderVolume (по умолчанию 1 лот) и приводится к допустимому значению инструмента перед отправкой.
Управление рисками
Трейлинг-стоп: переводит исходный параметр "points" в шаг цены. Если значение больше нуля, стоп автоматически подтягивается вслед за прибылью.
Тейк-профит: указывается в шагах цены. Ноль отключает ограничение прибыли.
Стратегия не усредняется и не накапливает несколько позиций — на инструмент открывается только одна позиция.
Параметры
Параметр
Описание
CandleType
Таймфрейм свечей и индикаторов (по умолчанию 1 час).
OrderVolume
Торговый объём в лотах.
ShortEmaLength
Период быстрой EMA (значение 6 по умолчанию).
LongEmaLength
Период медленной EMA (значение 12 по умолчанию).
UseCloseSignals
Закрывать позицию при противоположном пересечении (включено по умолчанию).
TrailingStopSteps
Дистанция трейлинг-стопа в шагах цены, 0 — отключён.
TakeProfitSteps
Дистанция тейк-профита в шагах цены, 0 — отключён.
Дополнительные замечания
Сигналы обрабатываются только на завершённых свечах, чтобы исключить шум внутри бара.
После выхода из позиции предыдущие значения EMA сбрасываются, что предотвращает ложные повторные срабатывания.
Комментарии в коде написаны на английском языке, а для отступов используются табуляции согласно правилам репозитория.
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>
/// EMA 6/12 crossover strategy.
/// Buys when EMA(6) crosses above EMA(12).
/// Sells when EMA(6) crosses below EMA(12).
/// </summary>
public class Ema612Strategy : 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 Ema612Strategy()
{
_fastPeriod = Param(nameof(FastPeriod), 6)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 12)
.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 ema612_strategy(Strategy):
def __init__(self):
super(ema612_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 6).SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 12).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(ema612_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(ema612_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 ema612_strategy()