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 El stop se sitúa a Multiplier veces el ATR de Period del cierre de entrada 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: 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.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 Breakout strategy.
/// A close more than Multiplier times the Period ATR above the Period simple moving average goes long and one that far below it goes short,
/// reversing an opposite position. The position stays open until the opposite breakout, or until the stop Multiplier ATR from the entry
/// close is hit, checked on candle closes.
/// </summary>
public class VolatilityBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _period;
	private readonly StrategyParam<decimal> _multiplier;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _stopPrice;

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

	/// <summary>
	/// ATR multiplier for the breakout 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 VolatilityBreakoutStrategy()
	{
		_period = Param(nameof(Period), 20)
			.SetGreaterThanZero()
			.SetDisplay("Period", "Period for SMA and ATR", "Parameters");

		_multiplier = Param(nameof(Multiplier), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Multiplier", "ATR multiplier for the breakout 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 subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(sma, atr, 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)
	{
		if (candle.State != CandleStates.Finished)
			return;

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var sma = smaValue.GetValue<decimal>();
		var distance = Multiplier * atrValue.GetValue<decimal>();
		var close = candle.ClosePrice;

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