Estrategia DMI Winner
DMI Winner es una estrategia de seguimiento de tendencia basada en el Índice de
Movimiento Direccional (DMI). Abre operaciones cuando las líneas +DI y -DI se
cruzan y el Índice de Dirección Promedio (ADX) sube por encima de un umbral clave,
señalando una tendencia fuerte.
Un filtro de media móvil opcional mantiene las operaciones en la dirección de la tendencia más amplia. También se puede habilitar un stop-loss para limitar el riesgo a la baja, aunque por defecto el sistema se basa en las reversiones de señal para las salidas.
Detalles
- Datos: Velas de precio.
- Criterios de entrada:
- Largo:
+DIcruza por encima de-DIYADX>KeyLevel(con filtro de MA opcional). - Corto:
-DIcruza por encima de+DIYADX>KeyLevel(con filtro de MA opcional).
- Largo:
- Criterios de salida: Cruce de DI opuesto o stop-loss si está habilitado.
- Stops: Stop-loss opcional (
UseSL). - Valores predeterminados:
DILength= 14KeyLevel= 23UseMA= TrueUseSL= False
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Largo/Corto
- Indicadores: DMI, Moving Average
- Complejidad: Moderado
- Nivel de riesgo: Medio
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>
/// Directional Movement Index Winner Strategy.
/// Uses DMI crossover with ADX confirmation and EMA trend filter.
/// </summary>
public class DmiWinnerStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _diLength;
private readonly StrategyParam<int> _adxSmoothing;
private readonly StrategyParam<decimal> _keyLevel;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<int> _cooldownBars;
private DirectionalIndex _dmi;
private AverageDirectionalIndex _adx;
private ExponentialMovingAverage _ma;
private decimal _prevDiPlus;
private decimal _prevDiMinus;
private int _cooldownRemaining;
public DmiWinnerStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_diLength = Param(nameof(DILength), 14)
.SetGreaterThanZero()
.SetDisplay("DI Length", "Directional Indicator period", "DMI");
_adxSmoothing = Param(nameof(ADXSmoothing), 13)
.SetGreaterThanZero()
.SetDisplay("ADX Smoothing", "ADX smoothing period", "DMI");
_keyLevel = Param(nameof(KeyLevel), 20m)
.SetDisplay("Key Level", "ADX key level threshold", "DMI");
_maLength = Param(nameof(MALength), 50)
.SetGreaterThanZero()
.SetDisplay("MA Length", "Moving average period", "Moving Average");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int DILength
{
get => _diLength.Value;
set => _diLength.Value = value;
}
public int ADXSmoothing
{
get => _adxSmoothing.Value;
set => _adxSmoothing.Value = value;
}
public decimal KeyLevel
{
get => _keyLevel.Value;
set => _keyLevel.Value = value;
}
public int MALength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_dmi = null;
_adx = null;
_ma = null;
_prevDiPlus = 0;
_prevDiMinus = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_dmi = new DirectionalIndex { Length = DILength };
_adx = new AverageDirectionalIndex { Length = ADXSmoothing };
_ma = new ExponentialMovingAverage { Length = MALength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_dmi, _adx, _ma, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ma);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue dmiValue, IIndicatorValue adxValue, IIndicatorValue maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_dmi.IsFormed || !_adx.IsFormed || !_ma.IsFormed)
return;
var dmiTyped = (DirectionalIndexValue)dmiValue;
if (dmiTyped.Plus is not decimal diPlus || dmiTyped.Minus is not decimal diMinus)
return;
var adxTyped = (AverageDirectionalIndexValue)adxValue;
if (adxTyped.MovingAverage is not decimal adxVal)
return;
if (maValue.IsEmpty)
return;
var maVal = maValue.ToDecimal();
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevDiPlus = diPlus;
_prevDiMinus = diMinus;
return;
}
var close = candle.ClosePrice;
// DI crossover detection
var diPlusCrossUp = diPlus > diMinus && _prevDiPlus <= _prevDiMinus && _prevDiPlus > 0;
var diPlusCrossDown = diPlus < diMinus && _prevDiPlus >= _prevDiMinus && _prevDiPlus > 0;
// Buy: DI+ crosses above DI-, ADX above key level, price above MA
if (diPlusCrossUp && adxVal > KeyLevel && close > maVal && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: DI- crosses above DI+, ADX above key level, price below MA
else if (diPlusCrossDown && adxVal > KeyLevel && close < maVal && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long if DI+ crosses below DI-
else if (Position > 0 && diPlusCrossDown)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short if DI+ crosses above DI-
else if (Position < 0 && diPlusCrossUp)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevDiPlus = diPlus;
_prevDiMinus = diMinus;
}
}
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 DirectionalIndex, AverageDirectionalIndex, ExponentialMovingAverage, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class dmi_winner_strategy(Strategy):
"""Directional Movement Index Winner Strategy.
Uses DMI crossover with ADX confirmation and EMA trend filter."""
def __init__(self):
super(dmi_winner_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._di_length = self.Param("DILength", 14) \
.SetDisplay("DI Length", "Directional Indicator period", "DMI")
self._adx_smoothing = self.Param("ADXSmoothing", 13) \
.SetDisplay("ADX Smoothing", "ADX smoothing period", "DMI")
self._key_level = self.Param("KeyLevel", 20.0) \
.SetDisplay("Key Level", "ADX key level threshold", "DMI")
self._ma_length = self.Param("MALength", 50) \
.SetDisplay("MA Length", "Moving average period", "Moving Average")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._dmi = None
self._adx = None
self._ma = None
self._prev_di_plus = 0.0
self._prev_di_minus = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(dmi_winner_strategy, self).OnReseted()
self._dmi = None
self._adx = None
self._ma = None
self._prev_di_plus = 0.0
self._prev_di_minus = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(dmi_winner_strategy, self).OnStarted2(time)
self._dmi = DirectionalIndex()
self._dmi.Length = int(self._di_length.Value)
self._adx = AverageDirectionalIndex()
self._adx.Length = int(self._adx_smoothing.Value)
self._ma = ExponentialMovingAverage()
self._ma.Length = int(self._ma_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._dmi, self._adx, self._ma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ma)
self.DrawOwnTrades(area)
def _on_process(self, candle, dmi_value, adx_value, ma_value):
if candle.State != CandleStates.Finished:
return
if not self._dmi.IsFormed or not self._adx.IsFormed or not self._ma.IsFormed:
return
if dmi_value.Plus is None or dmi_value.Minus is None:
return
if adx_value.MovingAverage is None:
return
if ma_value.IsEmpty:
return
di_plus = float(dmi_value.Plus)
di_minus = float(dmi_value.Minus)
adx_val = float(adx_value.MovingAverage)
ma_val = float(IndicatorHelper.ToDecimal(ma_value))
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_di_plus = di_plus
self._prev_di_minus = di_minus
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_di_plus = di_plus
self._prev_di_minus = di_minus
return
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
key_level = float(self._key_level.Value)
di_plus_cross_up = di_plus > di_minus and self._prev_di_plus <= self._prev_di_minus and self._prev_di_plus > 0
di_plus_cross_down = di_plus < di_minus and self._prev_di_plus >= self._prev_di_minus and self._prev_di_plus > 0
if di_plus_cross_up and adx_val > key_level and close > ma_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif di_plus_cross_down and adx_val > key_level and close < ma_val 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 di_plus_cross_down:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and di_plus_cross_up:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_di_plus = di_plus
self._prev_di_minus = di_minus
def CreateClone(self):
return dmi_winner_strategy()