Стратегия пересечения трёх EMA
Стратегия Three EMA Cross сочетает классическое пересечение быстрой и медленной экспоненциальных средних с дополнительным фильтром тренда. После того как быстрая EMA пересекает медленную снизу вверх, система ждёт отката к быстрой средней при условии, что цена закрытия остаётся выше более длинной трендовой EMA. Такой подход позволяет ловить продолжение движения после краткосрочного отката в рамках основного тренда.
Выход из позиции происходит при ослаблении импульса, когда быстрая EMA снова опускается ниже медленной. Процентный стоп‑лосс защищает сделку от неблагоприятного движения. Стратегия лучше всего проявляет себя на рынках с устойчивыми трендами и старается избегать боковых фаз.
Подробности
- Условия входа:
- Недавнее пересечение быстрой EMA выше медленной в пределах последних N баров.
- Текущая цена закрытия ≥ быстрой EMA и минимум бара ≤ быстрой EMA.
- Трендовая EMA ≤ текущей цены закрытия.
- Направление: Только длинные позиции.
- Условия выхода:
- Быстрая EMA опускается ниже медленной.
- Стопы: Стоп‑лосс на уровне
stop_loss_percentот цены входа. - Параметры по умолчанию:
FastEmaLength= 10SlowEmaLength= 20TrendEmaLength= 100StopLossPercent= 2.0CrossBackBars= 10
- Фильтры:
- Категория: Следование тренду
- Направление: Лонг
- Индикаторы: EMA
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Three EMA Cross Strategy.
/// Uses fast/slow EMA crossover with trend EMA filter.
/// Buys when fast EMA crosses above slow EMA while above trend EMA.
/// Sells when fast EMA crosses below slow EMA while below trend EMA.
/// </summary>
public class ThreeEmaCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _trendEmaLength;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _fastEma;
private ExponentialMovingAverage _slowEma;
private ExponentialMovingAverage _trendEma;
private decimal _prevFastEma;
private decimal _prevSlowEma;
private int _cooldownRemaining;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastEmaLength
{
get => _fastEmaLength.Value;
set => _fastEmaLength.Value = value;
}
public int SlowEmaLength
{
get => _slowEmaLength.Value;
set => _slowEmaLength.Value = value;
}
public int TrendEmaLength
{
get => _trendEmaLength.Value;
set => _trendEmaLength.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public ThreeEmaCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 10)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA length", "Moving Averages");
_slowEmaLength = Param(nameof(SlowEmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA length", "Moving Averages");
_trendEmaLength = Param(nameof(TrendEmaLength), 100)
.SetGreaterThanZero()
.SetDisplay("Trend EMA", "Trend EMA length", "Moving Averages");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastEma = null;
_slowEma = null;
_trendEma = null;
_prevFastEma = 0;
_prevSlowEma = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
_slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
_trendEma = new ExponentialMovingAverage { Length = TrendEmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastEma, _slowEma, _trendEma, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastEma);
DrawIndicator(area, _slowEma);
DrawIndicator(area, _trendEma);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal fastEma, decimal slowEma, decimal trendEma)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fastEma.IsFormed || !_slowEma.IsFormed || !_trendEma.IsFormed)
{
_prevFastEma = fastEma;
_prevSlowEma = slowEma;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFastEma = fastEma;
_prevSlowEma = slowEma;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevFastEma = fastEma;
_prevSlowEma = slowEma;
return;
}
if (_prevFastEma == 0 || _prevSlowEma == 0)
{
_prevFastEma = fastEma;
_prevSlowEma = slowEma;
return;
}
// EMA crossovers
var crossUp = fastEma > slowEma && _prevFastEma <= _prevSlowEma;
var crossDown = fastEma < slowEma && _prevFastEma >= _prevSlowEma;
// Buy: fast crosses above slow + price above trend EMA
if (crossUp && candle.ClosePrice > trendEma && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: fast crosses below slow + price below trend EMA
else if (crossDown && candle.ClosePrice < trendEma && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long: fast crosses below slow
else if (Position > 0 && crossDown)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short: fast crosses above slow
else if (Position < 0 && crossUp)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevFastEma = fastEma;
_prevSlowEma = slowEma;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class three_ema_cross_strategy(Strategy):
"""Three EMA Cross Strategy."""
def __init__(self):
super(three_ema_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._fast_ema_length = self.Param("FastEmaLength", 10) \
.SetDisplay("Fast EMA", "Fast EMA length", "Moving Averages")
self._slow_ema_length = self.Param("SlowEmaLength", 20) \
.SetDisplay("Slow EMA", "Slow EMA length", "Moving Averages")
self._trend_ema_length = self.Param("TrendEmaLength", 100) \
.SetDisplay("Trend EMA", "Trend EMA length", "Moving Averages")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._fast_ema = None
self._slow_ema = None
self._trend_ema = None
self._prev_fast_ema = 0.0
self._prev_slow_ema = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(three_ema_cross_strategy, self).OnReseted()
self._fast_ema = None
self._slow_ema = None
self._trend_ema = None
self._prev_fast_ema = 0.0
self._prev_slow_ema = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(three_ema_cross_strategy, self).OnStarted2(time)
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = int(self._fast_ema_length.Value)
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = int(self._slow_ema_length.Value)
self._trend_ema = ExponentialMovingAverage()
self._trend_ema.Length = int(self._trend_ema_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast_ema, self._slow_ema, self._trend_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._fast_ema)
self.DrawIndicator(area, self._slow_ema)
self.DrawIndicator(area, self._trend_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_ema, slow_ema, trend_ema):
if candle.State != CandleStates.Finished:
return
if not self._fast_ema.IsFormed or not self._slow_ema.IsFormed or not self._trend_ema.IsFormed:
self._prev_fast_ema = float(fast_ema)
self._prev_slow_ema = float(slow_ema)
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast_ema = float(fast_ema)
self._prev_slow_ema = float(slow_ema)
return
fe = float(fast_ema)
se = float(slow_ema)
te = float(trend_ema)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_fast_ema = fe
self._prev_slow_ema = se
return
if self._prev_fast_ema == 0.0 or self._prev_slow_ema == 0.0:
self._prev_fast_ema = fe
self._prev_slow_ema = se
return
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
cross_up = fe > se and self._prev_fast_ema <= self._prev_slow_ema
cross_down = fe < se and self._prev_fast_ema >= self._prev_slow_ema
if cross_up and close > te and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif cross_down and close < te and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and cross_down:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and cross_up:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_fast_ema = fe
self._prev_slow_ema = se
def CreateClone(self):
return three_ema_cross_strategy()