Стратегия 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
- Стопы: Нет
- Сложность: Базовая
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Низкий
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Multi EMA crossover strategy.
/// Four EMA pairs (EMA1/EMA5, EMA3/EMA10, EMA5/EMA20, EMA10/EMA40) trade independently: each pair adds one long lot when its
/// fast EMA crosses above its slow EMA and removes that lot when the fast EMA falls below the slow EMA. Long only, no stops.
/// </summary>
public class MultiEmaCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _ema1;
private readonly StrategyParam<int> _ema3;
private readonly StrategyParam<int> _ema5;
private readonly StrategyParam<int> _ema10;
private readonly StrategyParam<int> _ema20;
private readonly StrategyParam<int> _ema40;
private readonly StrategyParam<DataType> _candleType;
private readonly decimal?[] _prevFast = new decimal?[4];
private readonly decimal?[] _prevSlow = new decimal?[4];
private readonly bool[] _isOpen = new bool[4];
/// <summary>
/// Period of EMA1 (fast EMA of the first pair).
/// </summary>
public int EMA1
{
get => _ema1.Value;
set => _ema1.Value = value;
}
/// <summary>
/// Period of EMA3 (fast EMA of the second pair).
/// </summary>
public int EMA3
{
get => _ema3.Value;
set => _ema3.Value = value;
}
/// <summary>
/// Period of EMA5 (slow EMA of the first pair, fast EMA of the third pair).
/// </summary>
public int EMA5
{
get => _ema5.Value;
set => _ema5.Value = value;
}
/// <summary>
/// Period of EMA10 (slow EMA of the second pair, fast EMA of the fourth pair).
/// </summary>
public int EMA10
{
get => _ema10.Value;
set => _ema10.Value = value;
}
/// <summary>
/// Period of EMA20 (slow EMA of the third pair).
/// </summary>
public int EMA20
{
get => _ema20.Value;
set => _ema20.Value = value;
}
/// <summary>
/// Period of EMA40 (slow EMA of the fourth pair).
/// </summary>
public int EMA40
{
get => _ema40.Value;
set => _ema40.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MultiEmaCrossoverStrategy()
{
_ema1 = Param(nameof(EMA1), 1)
.SetGreaterThanZero()
.SetDisplay("EMA1", "Fast EMA of the 1/5 pair", "Indicators");
_ema3 = Param(nameof(EMA3), 3)
.SetGreaterThanZero()
.SetDisplay("EMA3", "Fast EMA of the 3/10 pair", "Indicators");
_ema5 = Param(nameof(EMA5), 5)
.SetGreaterThanZero()
.SetDisplay("EMA5", "Slow EMA of the 1/5 pair and fast EMA of the 5/20 pair", "Indicators");
_ema10 = Param(nameof(EMA10), 10)
.SetGreaterThanZero()
.SetDisplay("EMA10", "Slow EMA of the 3/10 pair and fast EMA of the 10/40 pair", "Indicators");
_ema20 = Param(nameof(EMA20), 20)
.SetGreaterThanZero()
.SetDisplay("EMA20", "Slow EMA of the 5/20 pair", "Indicators");
_ema40 = Param(nameof(EMA40), 40)
.SetGreaterThanZero()
.SetDisplay("EMA40", "Slow EMA of the 10/40 pair", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var ema1 = new ExponentialMovingAverage { Length = EMA1 };
var ema3 = new ExponentialMovingAverage { Length = EMA3 };
var ema5 = new ExponentialMovingAverage { Length = EMA5 };
var ema10 = new ExponentialMovingAverage { Length = EMA10 };
var ema20 = new ExponentialMovingAverage { Length = EMA20 };
var ema40 = new ExponentialMovingAverage { Length = EMA40 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema1, ema3, ema5, ema10, ema20, ema40, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema1);
DrawIndicator(area, ema3);
DrawIndicator(area, ema5);
DrawIndicator(area, ema10);
DrawIndicator(area, ema20);
DrawIndicator(area, ema40);
DrawOwnTrades(area);
}
}
private void ResetState()
{
Array.Clear(_prevFast);
Array.Clear(_prevSlow);
Array.Clear(_isOpen);
}
private void ProcessCandle(ICandleMessage candle, decimal ema1, decimal ema3, decimal ema5, decimal ema10, decimal ema20, decimal ema40)
{
if (candle.State != CandleStates.Finished)
return;
var fast = new[] { ema1, ema3, ema5, ema10 };
var slow = new[] { ema5, ema10, ema20, ema40 };
var canTrade = IsFormedAndOnlineAndAllowTrading();
for (var i = 0; i < 4; i++)
{
var prevFast = _prevFast[i];
var prevSlow = _prevSlow[i];
_prevFast[i] = fast[i];
_prevSlow[i] = slow[i];
if (!canTrade)
continue;
// Each pair owns one lot of the combined long position.
if (_isOpen[i])
{
if (fast[i] < slow[i])
{
SellMarket(Volume);
_isOpen[i] = false;
}
}
else if (prevFast is decimal pf && prevSlow is decimal ps && pf <= ps && fast[i] > slow[i])
{
BuyMarket(Volume);
_isOpen[i] = 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
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class multi_ema_crossover_strategy(Strategy):
"""
Multi EMA crossover strategy.
Four EMA pairs (EMA1/EMA5, EMA3/EMA10, EMA5/EMA20, EMA10/EMA40) trade independently: each pair adds one long lot when its
fast EMA crosses above its slow EMA and removes that lot when the fast EMA falls below the slow EMA. Long only, no stops.
"""
def __init__(self):
super(multi_ema_crossover_strategy, self).__init__()
self._ema1 = self.Param("EMA1", 1).SetGreaterThanZero().SetDisplay("EMA1", "Fast EMA of the 1/5 pair", "Indicators")
self._ema3 = self.Param("EMA3", 3).SetGreaterThanZero().SetDisplay("EMA3", "Fast EMA of the 3/10 pair", "Indicators")
self._ema5 = self.Param("EMA5", 5).SetGreaterThanZero().SetDisplay("EMA5", "Slow EMA of the 1/5 pair and fast EMA of the 5/20 pair", "Indicators")
self._ema10 = self.Param("EMA10", 10).SetGreaterThanZero().SetDisplay("EMA10", "Slow EMA of the 3/10 pair and fast EMA of the 10/40 pair", "Indicators")
self._ema20 = self.Param("EMA20", 20).SetGreaterThanZero().SetDisplay("EMA20", "Slow EMA of the 5/20 pair", "Indicators")
self._ema40 = self.Param("EMA40", 40).SetGreaterThanZero().SetDisplay("EMA40", "Slow EMA of the 10/40 pair", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_fast = [None] * 4
self._prev_slow = [None] * 4
self._is_open = [False] * 4
def OnReseted(self):
super(multi_ema_crossover_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(multi_ema_crossover_strategy, self).OnStarted2(time)
self._reset_state()
emas = []
for p in (self._ema1, self._ema3, self._ema5, self._ema10, self._ema20, self._ema40):
ema = ExponentialMovingAverage()
ema.Length = p.Value
emas.append(ema)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(emas[0], emas[1], emas[2], emas[3], emas[4], emas[5], self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
for ema in emas:
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema1, ema3, ema5, ema10, ema20, ema40):
if candle.State != CandleStates.Finished:
return
fast = (ema1, ema3, ema5, ema10)
slow = (ema5, ema10, ema20, ema40)
can_trade = self.IsFormedAndOnlineAndAllowTrading()
for i in range(4):
prev_fast = self._prev_fast[i]
prev_slow = self._prev_slow[i]
self._prev_fast[i] = fast[i]
self._prev_slow[i] = slow[i]
if not can_trade:
continue
# Each pair owns one lot of the combined long position.
if self._is_open[i]:
if fast[i] < slow[i]:
self.SellMarket(self.Volume)
self._is_open[i] = False
elif prev_fast is not None and prev_slow is not None and prev_fast <= prev_slow and fast[i] > slow[i]:
self.BuyMarket(self.Volume)
self._is_open[i] = True
def CreateClone(self):
return multi_ema_crossover_strategy()