Estrategia DMI Power Move
Estrategia basada en movimientos de poder del DMI (Índice de Movimiento Direccional)
Las pruebas indican un retorno anual promedio de aproximadamente 76%. Funciona mejor en el mercado de forex.
DMI Power Move combina las diferencias del indicador direccional con el ADX para capturar tendencias poderosas. Las operaciones entran cuando +DI supera notablemente a -DI (o viceversa) y el ADX es fuerte. Salen cuando el ADX se debilita o la diferencia entre DI se estrecha.
Este enfoque filtra las señales débiles al requerir tanto un movimiento direccional fuerte como un ADX en ascenso. El resultado son menos operaciones, pero potencialmente de mayor calidad en tendencia.
Detalles
- Criterios de entrada: Señales basadas en ADX, ATR, DMI.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: Señal opuesta o stop.
- Stops: Sí.
- Valores predeterminados:
DmiPeriod= 14DiDifferenceThreshold= 5mAdxThreshold= 30mAdxExitThreshold= 25mAtrMultiplier= 2mCandleType= TimeSpan.FromMinutes(15)
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: ADX, ATR, DMI
- Stops: Sí
- Complejidad: Básico
- Marco temporal: Intradía (15m)
- Estacionalidad: No
- Neural Networks: 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>
/// Trades strong, rising ADX with a directional DI spread; exits on weakening or an ATR stop.
/// </summary>
public class DmiPowerMoveStrategy : Strategy
{
private readonly StrategyParam<int> _dmiPeriod;
private readonly StrategyParam<decimal> _diDifferenceThreshold;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<decimal> _adxExitThreshold;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal? _previousAdx;
private decimal _stopPrice;
public int DmiPeriod { get => _dmiPeriod.Value; set => _dmiPeriod.Value = value; }
public decimal DiDifferenceThreshold { get => _diDifferenceThreshold.Value; set => _diDifferenceThreshold.Value = value; }
public decimal AdxThreshold { get => _adxThreshold.Value; set => _adxThreshold.Value = value; }
public decimal AdxExitThreshold { get => _adxExitThreshold.Value; set => _adxExitThreshold.Value = value; }
public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DmiPowerMoveStrategy()
{
_dmiPeriod = Param(nameof(DmiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("DMI Period", "Period for DMI and ATR", "Indicators");
_diDifferenceThreshold = Param(nameof(DiDifferenceThreshold), 5m)
.SetRange(0m, 100m)
.SetDisplay("DI Difference Threshold", "Minimum directional DI spread", "Trading");
_adxThreshold = Param(nameof(AdxThreshold), 30m)
.SetRange(0m, 100m)
.SetDisplay("ADX Threshold", "Minimum ADX for entry", "Trading");
_adxExitThreshold = Param(nameof(AdxExitThreshold), 25m)
.SetRange(0m, 100m)
.SetDisplay("ADX Exit Threshold", "Close when ADX falls below this value", "Trading");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Stop distance in entry ATR multiples", "Protection");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_previousAdx = null;
_stopPrice = 0m;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var dmi = new AverageDirectionalIndex { Length = DmiPeriod };
var atr = new AverageTrueRange { Length = DmiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(dmi, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, dmi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue dmiValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished || !dmiValue.IsFormed || !atrValue.IsFormed)
return;
if (dmiValue is not AverageDirectionalIndexValue dmi
|| dmi.MovingAverage is not decimal adx || dmi.Dx.Plus is not decimal plus || dmi.Dx.Minus is not decimal minus)
return;
var rising = _previousAdx is decimal previous && adx > previous;
_previousAdx = adx;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var spread = plus - minus;
var stopped = _stopPrice != 0m && (Position > 0 ? candle.ClosePrice <= _stopPrice : candle.ClosePrice >= _stopPrice);
if (Position > 0 && (stopped || adx < AdxExitThreshold || spread <= DiDifferenceThreshold))
{
SellMarket(Position);
_stopPrice = 0m;
return;
}
if (Position < 0 && (stopped || adx < AdxExitThreshold || spread >= -DiDifferenceThreshold))
{
BuyMarket(-Position);
_stopPrice = 0m;
return;
}
var atr = atrValue.GetValue<decimal>();
if (Position != 0 || !rising || adx <= AdxThreshold || atr <= 0)
return;
if (spread > DiDifferenceThreshold)
{
BuyMarket();
_stopPrice = candle.ClosePrice - AtrMultiplier * atr;
}
else if (spread < -DiDifferenceThreshold)
{
SellMarket();
_stopPrice = candle.ClosePrice + AtrMultiplier * atr;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AverageDirectionalIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class dmi_power_move_strategy(Strategy):
"""Trades strong, rising ADX with a DI spread; exits on weakening or an ATR stop."""
def __init__(self):
super(dmi_power_move_strategy, self).__init__()
self._dmi_period = self.Param("DmiPeriod", 14) \
.SetDisplay("DMI Period", "Period for DMI and ATR", "Indicators")
self._di_difference_threshold = self.Param("DiDifferenceThreshold", 5.0) \
.SetDisplay("DI Difference Threshold", "Minimum directional DI spread", "Trading")
self._adx_threshold = self.Param("AdxThreshold", 30.0) \
.SetDisplay("ADX Threshold", "Minimum ADX for entry", "Trading")
self._adx_exit_threshold = self.Param("AdxExitThreshold", 25.0) \
.SetDisplay("ADX Exit Threshold", "Close when ADX falls below this value", "Trading")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetDisplay("ATR Multiplier", "Stop distance in entry ATR multiples", "Protection")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._previous_adx = None
self._stop_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def OnReseted(self):
super(dmi_power_move_strategy, self).OnReseted()
self._previous_adx = None
self._stop_price = 0.0
def OnStarted2(self, time):
super(dmi_power_move_strategy, self).OnStarted2(time)
dmi = AverageDirectionalIndex()
dmi.Length = int(self._dmi_period.Value)
atr = AverageTrueRange()
atr.Length = int(self._dmi_period.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(dmi, atr, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, dmi)
self.DrawOwnTrades(area)
def process_candle(self, candle, dmi, atr_value):
if candle.State != CandleStates.Finished or not dmi.IsFormed or not atr_value.IsFormed:
return
if dmi.MovingAverage is None or dmi.Dx.Plus is None or dmi.Dx.Minus is None:
return
adx = float(dmi.MovingAverage)
spread = float(dmi.Dx.Plus) - float(dmi.Dx.Minus)
rising = self._previous_adx is not None and adx > self._previous_adx
self._previous_adx = adx
if not self.IsFormedAndOnlineAndAllowTrading():
return
price = float(candle.ClosePrice)
threshold = float(self._di_difference_threshold.Value)
stopped = self._stop_price != 0.0 and (price <= self._stop_price if self.Position > 0 else price >= self._stop_price)
if self.Position > 0 and (stopped or adx < float(self._adx_exit_threshold.Value) or spread <= threshold):
self.SellMarket(self.Position)
self._stop_price = 0.0
return
if self.Position < 0 and (stopped or adx < float(self._adx_exit_threshold.Value) or spread >= -threshold):
self.BuyMarket(-self.Position)
self._stop_price = 0.0
return
atr = float(atr_value.GetValue[Decimal](None))
if self.Position != 0 or not rising or adx <= float(self._adx_threshold.Value) or atr <= 0:
return
offset = float(self._atr_multiplier.Value) * atr
if spread > threshold:
self.BuyMarket()
self._stop_price = price - offset
elif spread < -threshold:
self.SellMarket()
self._stop_price = price + offset
def CreateClone(self):
return dmi_power_move_strategy()