Стратегия Multi EMA Crossover
Стратегия открывает отдельные длинные позиции по четырём парам EMA при пересечении быстрой EMA выше медленной. Каждая позиция закрывается, когда быстрая EMA опускается ниже своей медленной EMA.
Детали
- Условия входа:
- Лонг: быстрая EMA пересекает медленную снизу вверх для любой пары (1/5, 3/10, 5/20, 10/40).
- Лонг/Шорт: только лонг.
- Условия выхода:
- быстрая EMA опускается ниже медленной для соответствующей пары.
- Стопы: нет.
- Значения по умолчанию:
EMA1= 1EMA3= 3EMA5= 5EMA10= 10EMA20= 20EMA40= 40
- Фильтры:
- Категория: Следование тренду
- Направление: Лонг
- Индикаторы: EMA
- Стопы: Нет
- Сложность: Базовая
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Низкий
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Multi EMA crossover strategy.
/// Opens long on bullish crossover and exits on bearish crossover.
/// </summary>
public class MultiEmaCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrevValues;
private int _cooldownRemaining;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MultiEmaCrossoverStrategy()
{
_fastLength = Param(nameof(FastLength), 8)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Parameters");
_slowLength = Param(nameof(SlowLength), 34)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Parameters");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 12)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait after entries and exits", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0m;
_prevSlow = 0m;
_hasPrevValues = false;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastLength };
var slowEma = new ExponentialMovingAverage { Length = SlowLength };
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 fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_hasPrevValues)
{
_prevFast = fastValue;
_prevSlow = slowValue;
_hasPrevValues = true;
return;
}
if (_cooldownRemaining > 0)
_cooldownRemaining--;
if (Position > 0 && _prevFast >= _prevSlow && fastValue < slowValue)
{
SellMarket(Position);
_cooldownRemaining = SignalCooldownBars;
}
else if (Position == 0 && _cooldownRemaining == 0 && _prevFast <= _prevSlow && fastValue > slowValue)
{
BuyMarket();
_cooldownRemaining = SignalCooldownBars;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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 multi_ema_crossover_strategy(Strategy):
def __init__(self):
super(multi_ema_crossover_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 8) \
.SetGreaterThanZero() \
.SetDisplay("Fast EMA", "Fast EMA period", "Parameters")
self._slow_length = self.Param("SlowLength", 34) \
.SetGreaterThanZero() \
.SetDisplay("Slow EMA", "Slow EMA period", "Parameters")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 12) \
.SetGreaterThanZero() \
.SetDisplay("Signal Cooldown", "Bars to wait after entries and exits", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(multi_ema_crossover_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(multi_ema_crossover_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._cooldown_remaining = 0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self._fast_length.Value
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self._slow_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast_ema, self._slow_ema, self.OnProcess).Start()
def OnProcess(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self._has_prev:
self._prev_fast = fv
self._prev_slow = sv
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
if self.Position > 0 and self._prev_fast >= self._prev_slow and fv < sv:
self.SellMarket()
self._cooldown_remaining = self._signal_cooldown_bars.Value
elif self.Position == 0 and self._cooldown_remaining == 0 and self._prev_fast <= self._prev_slow and fv > sv:
self.BuyMarket()
self._cooldown_remaining = self._signal_cooldown_bars.Value
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return multi_ema_crossover_strategy()