Estrategia de Ruptura de EMA Adaptativa
La estrategia Adaptive EMA Breakout está construida alrededor de la ruptura de la EMA Adaptativa con confirmación de tendencia.
Las pruebas indican un retorno anual promedio de aproximadamente el 166%. Funciona mejor en el mercado de acciones.
Las señales se disparan cuando sus indicadores confirman oportunidades de ruptura en datos intradía (5m). Esto hace que el método sea adecuado para traders activos.
Los stops se basan en múltiplos de ATR y factores como Fast, Slow. Ajuste estos valores predeterminados para equilibrar el riesgo y la recompensa.
Detalles
- Criterios de entrada: ver implementación para las condiciones del indicador.
- Largo/Corto: Ambos.
- Criterios de salida: señal opuesta o lógica de stop.
- Stops: Sí, usando cálculos basados en indicadores.
- Valores predeterminados:
Fast = 2Slow = 30Lookback = 10StopMultiplier = 2mCandleType = TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: múltiples indicadores
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía (5m)
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Breakout strategy that trades in the direction of a rising or falling adaptive moving average when price extends beyond an ATR buffer.
/// </summary>
public class AdaptiveEmaBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _fast;
private readonly StrategyParam<int> _slow;
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<decimal> _breakoutAtrMultiplier;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private KaufmanAdaptiveMovingAverage _adaptiveEma;
private AverageTrueRange _atr;
private decimal _previousAdaptiveEmaValue;
private bool _isInitialized;
private int _cooldown;
/// <summary>
/// Fast period for KAMA smoothing.
/// </summary>
public int Fast
{
get => _fast.Value;
set => _fast.Value = value;
}
/// <summary>
/// Slow period for KAMA smoothing.
/// </summary>
public int Slow
{
get => _slow.Value;
set => _slow.Value = value;
}
/// <summary>
/// Main lookback period for KAMA.
/// </summary>
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
/// <summary>
/// Minimum ATR multiple required above or below KAMA for entry.
/// </summary>
public decimal BreakoutAtrMultiplier
{
get => _breakoutAtrMultiplier.Value;
set => _breakoutAtrMultiplier.Value = value;
}
/// <summary>
/// Stop loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Bars to wait after each order.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public AdaptiveEmaBreakoutStrategy()
{
_fast = Param(nameof(Fast), 2)
.SetRange(1, 20)
.SetDisplay("Fast Period", "Fast period for KAMA smoothing", "KAMA");
_slow = Param(nameof(Slow), 30)
.SetRange(5, 100)
.SetDisplay("Slow Period", "Slow period for KAMA smoothing", "KAMA");
_lookback = Param(nameof(Lookback), 10)
.SetRange(2, 100)
.SetDisplay("Lookback", "Main lookback period for KAMA", "KAMA");
_breakoutAtrMultiplier = Param(nameof(BreakoutAtrMultiplier), 0.75m)
.SetRange(0.1m, 5m)
.SetDisplay("Breakout ATR", "ATR multiple required for entry", "Signals");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetRange(0.5m, 10m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_cooldownBars = Param(nameof(CooldownBars), 72)
.SetRange(1, 500)
.SetDisplay("Cooldown Bars", "Bars to wait after each order", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for the strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
if (Security != null)
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_adaptiveEma = null;
_atr = null;
_previousAdaptiveEmaValue = 0m;
_isInitialized = false;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (Security == null)
throw new InvalidOperationException("Security is not specified.");
_adaptiveEma = new KaufmanAdaptiveMovingAverage
{
Length = Lookback,
FastSCPeriod = Fast,
SlowSCPeriod = Slow,
};
_atr = new AverageTrueRange { Length = 14 };
_cooldown = 0;
_isInitialized = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_adaptiveEma, _atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _adaptiveEma);
DrawOwnTrades(area);
}
StartProtection(new Unit(0, UnitTypes.Absolute), new Unit(StopLossPercent, UnitTypes.Percent), false);
}
private void ProcessCandle(ICandleMessage candle, decimal adaptiveEmaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_adaptiveEma.IsFormed || !_atr.IsFormed)
return;
if (ProcessState != ProcessStates.Started)
return;
if (!_isInitialized)
{
_previousAdaptiveEmaValue = adaptiveEmaValue;
_isInitialized = true;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_previousAdaptiveEmaValue = adaptiveEmaValue;
return;
}
var isTrendUp = adaptiveEmaValue > _previousAdaptiveEmaValue;
var isTrendDown = adaptiveEmaValue < _previousAdaptiveEmaValue;
var breakoutDistance = candle.ClosePrice - adaptiveEmaValue;
var requiredDistance = atrValue * BreakoutAtrMultiplier;
if (Position == 0)
{
if (isTrendUp && breakoutDistance >= requiredDistance)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (isTrendDown && breakoutDistance <= -requiredDistance)
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (Position > 0)
{
if (candle.ClosePrice <= adaptiveEmaValue || isTrendDown)
{
SellMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
}
else if (Position < 0)
{
if (candle.ClosePrice >= adaptiveEmaValue || isTrendUp)
{
BuyMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
}
_previousAdaptiveEmaValue = adaptiveEmaValue;
}
}
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, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import KaufmanAdaptiveMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class adaptive_ema_breakout_strategy(Strategy):
"""
Breakout strategy that trades in the direction of a rising or falling adaptive
moving average when price extends beyond an ATR buffer.
"""
def __init__(self):
super(adaptive_ema_breakout_strategy, self).__init__()
self._fast = self.Param("Fast", 2) \
.SetDisplay("Fast Period", "Fast period for KAMA smoothing", "KAMA")
self._slow = self.Param("Slow", 30) \
.SetDisplay("Slow Period", "Slow period for KAMA smoothing", "KAMA")
self._lookback = self.Param("Lookback", 10) \
.SetDisplay("Lookback", "Main lookback period for KAMA", "KAMA")
self._breakout_atr_multiplier = self.Param("BreakoutAtrMultiplier", 0.75) \
.SetDisplay("Breakout ATR", "ATR multiple required for entry", "Signals")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 72) \
.SetDisplay("Cooldown Bars", "Bars to wait after each order", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles for the strategy", "General")
self._adaptive_ema = None
self._atr = None
self._previous_adaptive_ema_value = 0.0
self._is_initialized = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adaptive_ema_breakout_strategy, self).OnReseted()
self._adaptive_ema = None
self._atr = None
self._previous_adaptive_ema_value = 0.0
self._is_initialized = False
self._cooldown = 0
def OnStarted2(self, time):
super(adaptive_ema_breakout_strategy, self).OnStarted2(time)
self._adaptive_ema = KaufmanAdaptiveMovingAverage()
self._adaptive_ema.Length = int(self._lookback.Value)
self._adaptive_ema.FastSCPeriod = int(self._fast.Value)
self._adaptive_ema.SlowSCPeriod = int(self._slow.Value)
self._atr = AverageTrueRange()
self._atr.Length = 14
self._cooldown = 0
self._is_initialized = False
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._adaptive_ema, self._atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._adaptive_ema)
self.DrawOwnTrades(area)
self.StartProtection(Unit(0, UnitTypes.Absolute), Unit(self._stop_loss_percent.Value, UnitTypes.Percent), False)
def _process_candle(self, candle, adaptive_ema_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not self._adaptive_ema.IsFormed or not self._atr.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ae = float(adaptive_ema_value)
av = float(atr_value)
if not self._is_initialized:
self._previous_adaptive_ema_value = ae
self._is_initialized = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._previous_adaptive_ema_value = ae
return
is_trend_up = ae > self._previous_adaptive_ema_value
is_trend_down = ae < self._previous_adaptive_ema_value
close_price = float(candle.ClosePrice)
breakout_distance = close_price - ae
bam = float(self._breakout_atr_multiplier.Value)
required_distance = av * bam
cd = int(self._cooldown_bars.Value)
if self.Position == 0:
if is_trend_up and breakout_distance >= required_distance:
self.BuyMarket()
self._cooldown = cd
elif is_trend_down and breakout_distance <= -required_distance:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0:
if close_price <= ae or is_trend_down:
self.SellMarket(Math.Abs(self.Position))
self._cooldown = cd
elif self.Position < 0:
if close_price >= ae or is_trend_up:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown = cd
self._previous_adaptive_ema_value = ae
def CreateClone(self):
return adaptive_ema_breakout_strategy()