Ver en GitHub

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

  1. Calcular el RSI a partir de los cierres de las velas.
  2. Suavizar el RSI con la primera media móvil para obtener la línea rápida.
  3. Suavizar la línea rápida con la segunda media móvil para obtener la línea lenta.
  4. 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.
  5. 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.
  6. 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;
	}
}