Estrategia de Movimiento Dirigido
Descripción general
Esta estrategia replica el asesor experto Directed Movement de MetaTrader. Aplica un Índice de Fuerza Relativa (RSI) que se suaviza dos veces mediante medias móviles. El primer suavizado forma una línea rápida mientras que el segundo suavizado crea una línea más lenta.
Las decisiones de trading se basan en el cruce de las líneas rápida y lenta de forma contraria:
- Comprar cuando la línea rápida cruza por debajo de la línea lenta.
- Vender cuando la línea rápida cruza por encima de la línea lenta.
Los niveles opcionales de stop-loss y take-profit se aplican como porcentajes del precio de entrada.
Indicadores
RelativeStrengthIndex – indicador de momentum base.
MovingAverage – primer suavizado del RSI (línea rápida).
MovingAverage – segundo suavizado de la línea rápida (línea lenta).
Reglas de trading
- Calcular el RSI a partir de los cierres de las velas.
- Suavizar el RSI con la primera media móvil para obtener la línea rápida.
- Suavizar la línea rápida con la segunda media móvil para obtener la línea lenta.
- Entrar en una posición larga cuando la línea rápida cruza por debajo de la línea lenta. Cerrar cualquier posición corta antes de abrir la nueva larga.
- Entrar en una posición corta cuando la línea rápida cruza por encima de la línea lenta. Cerrar cualquier posición larga antes de abrir la nueva corta.
- Aplicar protecciones de stop-loss y take-profit si sus parámetros son mayores que cero.
Parámetros
| Nombre |
Descripción |
CandleType |
Serie de velas utilizada para los cálculos. |
RsiPeriod |
Período de cálculo del RSI. |
FirstMaType |
Tipo de media móvil utilizada para la línea rápida. |
FirstMaLength |
Período de la media móvil rápida. |
SecondMaType |
Tipo de media móvil utilizada para la línea lenta. |
SecondMaLength |
Período de la media móvil lenta. |
StopLossPercent |
Stop-loss en porcentaje del precio de entrada. |
TakeProfitPercent |
Take-profit en porcentaje del precio de entrada. |
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>
/// Directed Movement Strategy - RSI cross system with two MA smoothing.
/// </summary>
public class DirectedMovementStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _fastMaLength;
private readonly StrategyParam<int> _slowMaLength;
private ExponentialMovingAverage _fastMa;
private ExponentialMovingAverage _slowMa;
private decimal _prevFast;
private decimal _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int FastMaLength { get => _fastMaLength.Value; set => _fastMaLength.Value = value; }
public int SlowMaLength { get => _slowMaLength.Value; set => _slowMaLength.Value = value; }
public DirectedMovementStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "RSI calculation period", "Indicators");
_fastMaLength = Param(nameof(FastMaLength), 12)
.SetDisplay("Fast MA Length", "Period of fast moving average", "Indicators");
_slowMaLength = Param(nameof(SlowMaLength), 5)
.SetDisplay("Slow MA Length", "Period of slow moving average", "Indicators");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastMa = null;
_slowMa = null;
_prevFast = 0m;
_prevSlow = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0m;
_prevSlow = 0m;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_fastMa = new ExponentialMovingAverage { Length = FastMaLength };
_slowMa = new ExponentialMovingAverage { Length = SlowMaLength };
Indicators.Add(_fastMa);
Indicators.Add(_slowMa);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
var t = candle.ServerTime;
var fastResult = _fastMa.Process(new DecimalIndicatorValue(_fastMa, rsiValue, t) { IsFinal = true });
if (!_fastMa.IsFormed)
return;
var fast = fastResult.GetValue<decimal>();
var slowResult = _slowMa.Process(new DecimalIndicatorValue(_slowMa, fast, t) { IsFinal = true });
if (!_slowMa.IsFormed)
{
_prevFast = fast;
_prevSlow = fast;
return;
}
var slow = slowResult.GetValue<decimal>();
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fast;
_prevSlow = slow;
return;
}
// Crossover: fast crosses below slow -> buy
if (_prevFast > _prevSlow && fast <= slow && Position <= 0)
BuyMarket();
// Crossover: fast crosses above slow -> sell
else if (_prevFast < _prevSlow && fast >= slow && Position >= 0)
SellMarket();
_prevFast = fast;
_prevSlow = slow;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class directed_movement_strategy(Strategy):
def __init__(self):
super(directed_movement_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
self._fast_ma_length = self.Param("FastMaLength", 12) \
.SetDisplay("Fast MA Length", "Period of fast moving average", "Indicators")
self._slow_ma_length = self.Param("SlowMaLength", 5) \
.SetDisplay("Slow MA Length", "Period of slow moving average", "Indicators")
self._fast_ma = None
self._slow_ma = None
self._prev_fast = 0.0
self._prev_slow = 0.0
@property
def candle_type(self):
return self._candle_type.Value
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def fast_ma_length(self):
return self._fast_ma_length.Value
@property
def slow_ma_length(self):
return self._slow_ma_length.Value
def OnReseted(self):
super(directed_movement_strategy, self).OnReseted()
self._fast_ma = None
self._slow_ma = None
self._prev_fast = 0.0
self._prev_slow = 0.0
def OnStarted2(self, time):
super(directed_movement_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
self._fast_ma = ExponentialMovingAverage()
self._fast_ma.Length = self.fast_ma_length
self._slow_ma = ExponentialMovingAverage()
self._slow_ma.Length = self.slow_ma_length
self.Indicators.Add(self._fast_ma)
self.Indicators.Add(self._slow_ma)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi_value = float(rsi_value)
t = candle.ServerTime
fast_result = process_float(self._fast_ma, rsi_value, t, True)
if not self._fast_ma.IsFormed:
return
fast = float(fast_result)
slow_result = process_float(self._slow_ma, fast, t, True)
if not self._slow_ma.IsFormed:
self._prev_fast = fast
self._prev_slow = fast
return
slow = float(slow_result)
if self._prev_fast > self._prev_slow and fast <= slow and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast < self._prev_slow and fast >= slow and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return directed_movement_strategy()