Ver en GitHub

Estrategia de Reversión a la Media Ajustada por Volatilidad

Esta variación de la reversión a la media escala los umbrales de entrada por la relación entre ATR y desviación estándar. Cuando la volatilidad aumenta en relación con el ruido típico, la distancia necesaria para activar una operación crece, ayudando a evitar señales prematuras durante oscilaciones caóticas.

Las pruebas indican un retorno anual promedio de aproximadamente 115%. Funciona mejor en el mercado de acciones.

Una posición larga se abre cuando el precio cae por debajo de la media móvil en más del umbral ajustado. Una posición corta se abre cuando el precio sube por encima de la media la misma medida. Las posiciones se cierran una vez que el precio cierra de nuevo cerca del nivel medio.

El umbral adaptativo hace que esta estrategia sea adecuada para mercados con regímenes de volatilidad cambiantes. Un stop-loss igual al doble del ATR limita el riesgo mientras se espera la reversión.

Detalles

  • Criterios de entrada:
    • Largo: Cierre < MA - Multiplier * ATR / (ATR/StdDev)
    • Corto: Cierre > MA + Multiplier * ATR / (ATR/StdDev)
  • Largo/Corto: Ambos lados.
  • Criterios de salida:
    • Largo: Salir cuando cierre >= MA
    • Corto: Salir cuando cierre <= MA
  • Stops: Sí, dinámico basado en ATR.
  • Valores predeterminados:
    • Period = 20
    • Multiplier = 2 MA, ATR y StdDev (la desviación estándar de los cierres) abarcan Period velas; el umbral se reduce a Multiplier veces StdDev. El stop se sitúa a Multiplier veces el ATR del cierre de entrada, dos ATR por defecto, y se comprueba al cierre de las velas. Una señal de entrada contra una posición abierta la invierte.
    • CandleType = TimeSpan.FromMinutes(5)
  • Filtros:
    • Categoría: Reversión a la media
    • Dirección: Ambos
    • Indicadores: ATR, StdDev
    • Stops: Sí
    • Complejidad: Intermedio
    • Marco temporal: Intradía
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • 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>
/// Volatility Adjusted Mean Reversion strategy.
/// The threshold is Multiplier times the ATR divided by the ATR to standard deviation ratio, all over Period candles. A close more than the
/// threshold below the Period simple moving average goes long and one that far above it goes short, reversing an opposite position.
/// A long closes once the close is back at or above the average and a short once it is back at or below it. The stop lies Multiplier ATR
/// from the entry close and is checked on candle closes.
/// </summary>
public class VolatilityAdjustedMeanReversionStrategy : Strategy
{
	private readonly StrategyParam<int> _period;
	private readonly StrategyParam<decimal> _multiplier;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _stopPrice;

	/// <summary>
	/// Period of the SMA, ATR and standard deviation.
	/// </summary>
	public int Period
	{
		get => _period.Value;
		set => _period.Value = value;
	}

	/// <summary>
	/// Multiplier of the threshold and the stop.
	/// </summary>
	public decimal Multiplier
	{
		get => _multiplier.Value;
		set => _multiplier.Value = value;
	}

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Constructor.
	/// </summary>
	public VolatilityAdjustedMeanReversionStrategy()
	{
		_period = Param(nameof(Period), 20)
			.SetGreaterThanZero()
			.SetDisplay("Period", "Period of the SMA, ATR and standard deviation", "Parameters");

		_multiplier = Param(nameof(Multiplier), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Multiplier", "Multiplier of the threshold and the stop", "Parameters");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_stopPrice = default;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_stopPrice = default;

		var sma = new SimpleMovingAverage { Length = Period };
		var atr = new AverageTrueRange { Length = Period };
		var stdev = new StandardDeviation { Length = Period };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(sma, atr, stdev, ProcessCandle)
			.Start();

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

			var oscillators = CreateChartArea();
			if (oscillators != null)
			{
				DrawIndicator(oscillators, atr);
			}
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue smaValue, IIndicatorValue atrValue, IIndicatorValue stdevValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!smaValue.IsFormed || !atrValue.IsFormed || !stdevValue.IsFormed)
			return;

		var atr = atrValue.GetValue<decimal>();
		var deviation = stdevValue.GetValue<decimal>();

		if (atr <= 0 || deviation <= 0)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var sma = smaValue.GetValue<decimal>();
		var threshold = Multiplier * atr / (atr / deviation);
		var close = candle.ClosePrice;

		if (close < sma - threshold && Position <= 0)
		{
			BuyMarket(Volume + Math.Abs(Position));
			_stopPrice = close - Multiplier * atr;
		}
		else if (close > sma + threshold && Position >= 0)
		{
			SellMarket(Volume + Math.Abs(Position));
			_stopPrice = close + Multiplier * atr;
		}
		else if (Position > 0 && (close >= sma || close <= _stopPrice))
		{
			SellMarket(Position);
		}
		else if (Position < 0 && (close <= sma || close >= _stopPrice))
		{
			BuyMarket(-Position);
		}
	}
}