Estrategia de Cruce de Tres EMA
La estrategia Three EMA Cross combina un clásico cruce de media móvil rápida/lenta con un filtro de tendencia más largo. Después de que la EMA rápida cruza por encima de la EMA lenta, la estrategia espera un retroceso hacia la media rápida mientras el precio de cierre permanece por encima de una EMA de tendencia más amplia. Esta configuración intenta capturar movimientos de continuación después de una breve corrección dentro de la tendencia prevalente.
Las posiciones se cierran cuando el momentum se desvanece y la EMA rápida cruza de nuevo por debajo de la EMA lenta. Un stop loss basado en porcentaje protege la posición si el precio se mueve en contra de la operación. La técnica funciona bien en mercados con tendencias persistentes y tiende a evitar rangos laterales.
Detalles
- Criterios de entrada:
- Cruce reciente de EMA rápida por encima de EMA lenta dentro de los últimos N barras.
- Cierre actual ≥ EMA rápida y mínimo de sesión ≤ EMA rápida.
- EMA de tendencia ≤ cierre actual.
- Largo/Corto: Solo largos.
- Criterios de salida:
- EMA rápida cae por debajo de EMA lenta.
- Stops: Stop loss en
stop_loss_percentdel precio de entrada. - Valores predeterminados:
FastEmaLength= 10SlowEmaLength= 20TrendEmaLength= 100StopLossPercent= 2.0CrossBackBars= 10
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Largo
- Indicadores: EMA
- Stops: Sí
- Complejidad: Medio
- Marco temporal: Cualquiera
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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()