Ver en GitHub

Estrategia de impulso de reversión a la media

Descripción general

La estrategia Mean Reversion es una adaptación directa del asesor experto MetaTrader Mean reversion.mq4. La versión StockSharp mantiene la idea comercial original: comprar después de una serie prolongada de cierres a la baja y vender después de una carrera alcista similar. Las entradas se confirman mediante la alineación de tendencias utilizando dos promedios móviles ponderados lineales, la fuerza del impulso en un período de tiempo más alto y un filtro MACD mensual.

Una vez en posición, la estrategia recrea las reglas de administración de dinero de la versión MQL: stop-loss y take-profit configurables en pips, reubicación opcional del punto de equilibrio y un trailing stop que bloquea las ganancias a medida que el mercado se mueve a favor de la operación.

Lógica de trading

  1. Período de tiempo de la señal: la estrategia opera en la serie de velas seleccionada (predeterminado 15 minutos).
  2. Detección de agotamiento: recopila los últimos BarsToCount cierres. Una configuración larga requiere que el cierre más reciente sea más bajo que cada uno de los cierres anteriores, lo que indica una venta masiva. Una configuración corta necesita la condición opuesta.
  3. Filtro de tendencias: el LWMA rápido (longitud FastMaLength) debe estar por encima del LWMA lento (SlowMaLength) para largos y por debajo para cortos.
  4. Filtro de impulso: el indicador de impulso (período MomentumLength) se calcula en el período de tiempo superior de estilo MetaTrader (M15 → H1, H1 → D1, etc.). Al menos una de las últimas tres lecturas de impulso debe desviarse de 100 en más de MomentumThreshold.
  5. MACD confirmación: un MACD mensual (26/12/9) debe tener la línea principal por encima de la línea de señal para largos y por debajo para cortos.

Si se cumplen todas las condiciones, la estrategia abre una posición usando OrderVolume. Las operaciones opuestas aplanan la posición actual antes de revertirse.

Gestión de Puestos

  • Stop-loss & take-profit: configurado en pips mediante StopLossPips y TakeProfitPips.
  • Equipo: cuando está habilitado, el tope se mueve al precio de entrada más BreakEvenOffsetPips después de que el precio avanza en BreakEvenTriggerPips.
  • Trailing stop: si EnableTrailing es verdadero y el beneficio no realizado supera TrailingStopPips, el stop sigue al precio con el paso TrailingStepPips.

Todas las conversiones de precios utilizan el tamaño del pip del instrumento para coincidir con el comportamiento de MetaTrader.

Parámetros

Nombre Descripción Predeterminado
OrderVolume Tamaño de orden utilizado para las entradas al mercado. 1
CandleType Serie de velas primarias utilizadas para señales. M15
BarsToCount Número de cierres anteriores comprobados por agotamiento. 10
FastMaLength Período LWMA rápido. 6
SlowMaLength Período LWMA lento. 85
MomentumLength Período de impulso en el marco temporal superior. 14
MomentumThreshold Desviación absoluta mínima de 100 para confirmación del impulso. 0.3
StopLossPips Distancia de stop-loss en pips. 20
TakeProfitPips Distancia de toma de ganancias en pips. 50
UseBreakEven Habilite la reubicación de parada para alcanzar el punto de equilibrio. false
BreakEvenTriggerPips Beneficio en pips necesarios antes de mover el stop. 30
BreakEvenOffsetPips Se agregan puntos adicionales al pasar al punto de equilibrio. 30
EnableTrailing Activar la gestión de trailing stop. true
TrailingStopPips Beneficio en pips necesarios para comenzar a seguir. 40
TrailingStepPips Distancia mantenida por el trailing stop. 40

Notas

  • El marco de tiempo superior para el impulso sigue MetaTrader pasos: M1→M15, M5→M30, M15→H1, M30→H4, H1→D1, H4→W1, D1→MN1, W1→MN1.
  • La confirmación de MACD siempre utiliza el período de tiempo mensual (MN1).
  • La estrategia espera tipos de velas basadas en plazos; No se admiten velas de tick o de rango.
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>
/// Simplified from "Mean Reversion" MetaTrader expert.
/// Buys after multi-bar sell-off when RSI is oversold, sells after multi-bar rally when RSI is overbought.
/// Uses consecutive bar count for exhaustion detection with RSI confirmation.
/// </summary>
public class MeanReversionMomentumStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _barsToCount;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<decimal> _rsiOversold;

	private RelativeStrengthIndex _rsi;
	private readonly List<decimal> _closeHistory = new();

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int BarsToCount
	{
		get => _barsToCount.Value;
		set => _barsToCount.Value = value;
	}

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	public decimal RsiOverbought
	{
		get => _rsiOverbought.Value;
		set => _rsiOverbought.Value = value;
	}

	public decimal RsiOversold
	{
		get => _rsiOversold.Value;
		set => _rsiOversold.Value = value;
	}

	public MeanReversionMomentumStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe", "General");

		_barsToCount = Param(nameof(BarsToCount), 5)
			.SetGreaterThanZero()
			.SetDisplay("Bars To Count", "Number of consecutive bars for exhaustion detection", "Signal");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period for confirmation", "Indicators");

		_rsiOverbought = Param(nameof(RsiOverbought), 70m)
			.SetDisplay("RSI Overbought", "RSI level for sell signal", "Signals");

		_rsiOversold = Param(nameof(RsiOversold), 30m)
			.SetDisplay("RSI Oversold", "RSI level for buy signal", "Signals");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_closeHistory.Clear();
		_rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_rsi, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_closeHistory.Add(candle.ClosePrice);
		if (_closeHistory.Count > BarsToCount + 1)
			_closeHistory.RemoveAt(0);

		if (!_rsi.IsFormed || _closeHistory.Count < BarsToCount + 1)
			return;

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// Count consecutive down bars
		var downCount = 0;
		for (int i = _closeHistory.Count - 1; i >= 1; i--)
		{
			if (_closeHistory[i] < _closeHistory[i - 1])
				downCount++;
			else
				break;
		}

		// Count consecutive up bars
		var upCount = 0;
		for (int i = _closeHistory.Count - 1; i >= 1; i--)
		{
			if (_closeHistory[i] > _closeHistory[i - 1])
				upCount++;
			else
				break;
		}

		// Multi-bar sell-off + RSI oversold -> mean reversion buy
		if (downCount >= BarsToCount && rsiValue < RsiOversold)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));

			if (Position <= 0)
				BuyMarket(volume);
		}
		// Multi-bar rally + RSI overbought -> mean reversion sell
		else if (upCount >= BarsToCount && rsiValue > RsiOverbought)
		{
			if (Position > 0)
				SellMarket(Position);

			if (Position >= 0)
				SellMarket(volume);
		}
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_closeHistory.Clear();
		_rsi = null;

		base.OnReseted();
	}
}