Ver en GitHub

Estrategia de Ruptura de Volatilidad

La estrategia de Ruptura de Volatilidad busca movimientos direccionales fuertes cuando el precio escapa de su rango promedio. Midiendo la distancia desde una media móvil simple usando el ATR, el algoritmo define umbrales de ruptura que escalan con la volatilidad.

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

Una orden de compra se activa cuando el cierre sube por encima de la SMA en más de Multiplier veces el ATR. Una señal de venta aparece cuando el cierre cae por debajo de la SMA la misma distancia. Las posiciones permanecen abiertas hasta que se produce una ruptura opuesta o se alcanza un stop de protección.

Esta técnica está orientada a los traders intradía que prosperan con los impulsos de momentum. Usar umbrales basados en ATR ayuda a filtrar el ruido para que solo los movimientos significativos generen operaciones.

Detalles

  • Criterios de entrada:
    • Largo: Close > SMA + Multiplier * ATR
    • Corto: Close < SMA - Multiplier * ATR
  • Largo/Corto: Ambos lados.
  • Criterios de salida:
    • Largo: Salir cuando se activa una ruptura opuesta o se alcanza el stop-loss
    • Corto: Salir cuando se activa una ruptura opuesta o se alcanza el stop-loss
  • Stops: Sí, stop-loss a Multiplier * ATR desde la entrada.
  • Valores predeterminados:
    • Period = 20
    • Multiplier = 2.0m
    • CandleType = TimeSpan.FromMinutes(5)
  • Filtros:
    • Categoría: Ruptura
    • Dirección: Ambos
    • Indicadores: SMA, ATR
    • Stops: Sí
    • Complejidad: Intermedio
    • Marco temporal: Intradía
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;
	
/// <summary>
/// Volatility Breakout strategy. Enters trades when price breaks out from average price with volatility threshold.
/// </summary>
public class VolatilityBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _periodParam;
	private readonly StrategyParam<decimal> _multiplierParam;
	private readonly StrategyParam<DataType> _candleTypeParam;

	private SimpleMovingAverage _sma;
	private AverageTrueRange _atr;
	
	private decimal _prevSma;
	private decimal _prevAtr;

	/// <summary>
	/// Period for SMA and ATR calculations.
	/// </summary>
	public int Period
	{
		get => _periodParam.Value;
		set => _periodParam.Value = value;
	}

	/// <summary>
	/// Volatility multiplier for breakout threshold.
	/// </summary>
	public decimal Multiplier
	{
		get => _multiplierParam.Value;
		set => _multiplierParam.Value = value;
	}

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

	/// <summary>
	/// Constructor.
	/// </summary>
	public VolatilityBreakoutStrategy()
	{
		_periodParam = Param(nameof(Period), 20)
			.SetGreaterThanZero()
			.SetDisplay("Period", "Period for SMA and ATR", "Parameters")
			
			.SetOptimize(10, 50, 5);

		_multiplierParam = Param(nameof(Multiplier), 2.0m)
			.SetRange(0.1m, decimal.MaxValue)
			.SetDisplay("Multiplier", "Volatility multiplier for breakout threshold", "Parameters")
			
			.SetOptimize(1.0m, 3.0m, 0.5m);

		_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle type for strategy", "Common");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_sma = null;
		_atr = null;
		_prevSma = 0;
		_prevAtr = 0;
	}

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

// Create indicators
		_sma = new SMA { Length = Period };
		_atr = new AverageTrueRange { Length = Period };

		// Create subscription and bind indicators
		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_sma, _atr, ProcessCandle)
			.Start();

		// Setup chart visualization if available
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _sma);
			DrawOwnTrades(area);
		}
		
		// Enable position protection
		StartProtection(
			takeProfit: new Unit(0, UnitTypes.Absolute), // No take profit
			stopLoss: new Unit(Multiplier, UnitTypes.Absolute) // Stop loss at 2*ATR
		);
	}

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;
		
		// Save values for the next candle
		var currentSma = smaValue;
		var currentAtr = atrValue;
		
		// Skip first candle after indicators become formed
		if (_prevSma == 0 || _prevAtr == 0)
		{
			_prevSma = currentSma;
			_prevAtr = currentAtr;
			return;
		}
		
		// Calculate volatility threshold
		var threshold = Multiplier * currentAtr;
		
		// Check for long setup - price breaks above SMA + threshold
		if (candle.ClosePrice > currentSma + threshold && Position <= 0)
		{
			// Close any short position and open long
			BuyMarket(Volume + Math.Abs(Position));
		}
		// Check for short setup - price breaks below SMA - threshold
		else if (candle.ClosePrice < currentSma - threshold && Position >= 0)
		{
			// Close any long position and open short
			SellMarket(Volume + Math.Abs(Position));
		}
		
		// Update previous values for next candle
		_prevSma = currentSma;
		_prevAtr = currentAtr;
	}
}