Ver no GitHub

Estratégia de volatilidade por rompimento

A estratégia de volatilidade por rompimento procura rajadas curtas de volatilidade intrabar. Ela espera por um candle cujo intervalo se expanda além do candle anterior, mas apenas dentro de uma faixa estreita (dois equivalentes de pip depois da normalização de dígitos). Quando esse candle fecha em alta, a estratégia compra; quando fecha em baixa, ela vende. Stops de proteção, um trailing stop opcional e uma sequência automática de reversão após perda gerenciam o risco e tentam recuperar movimentos adversos.

Lógica de negociação

  1. Filtro de expansão do intervalo
    • Calcule o intervalo do candle atual (High - Low) e compare-o com o candle anterior.
    • Exija que o intervalo atual seja maior, mas que não exceda o intervalo anterior por mais de dois pips normalizados.
    • Isso cria uma configuração em que a volatilidade está aumentando, mas ainda contida, apontando para um possível rompimento sem ruído excessivo.
  2. Viés direcional
    • Se o candle fechar acima da abertura, entre comprado.
    • Se o candle fechar abaixo da abertura, entre vendido.
    • A estratégia pode opcionalmente impedir mais de uma entrada por barra para evitar sinais repetidos no mesmo candle.
  3. Gestão da posição
    • Stop-loss inicial e take-profit são atribuídos em pontos (equivalentes de pip) relativos ao preço de entrada.
    • Um trailing stop opcional aperta o nível de proteção depois que o preço se move uma distância especificada a favor da operação. Um passo de trailing evita ajustes minúsculos.
    • Quando uma posição fecha com perda, a estratégia pode inverter a direção imediatamente. Cada reversão aumenta a distância de take-profit para compensar o risco adicional. Um limite para o número de reversões consecutivas impede comportamento de martingale infinito.

Parâmetros

Nome Descrição Padrão Otimizável
TradeVolume Volume base da ordem para entradas a mercado. 0.1 Sim
StopLossPoints Distância do stop-loss em pontos. 20 Sim
TakeProfitPoints Distância do take-profit em pontos. 10 Sim
TrailingStopPoints Distância do trailing stop em pontos. Defina como 0 para desabilitar. 25 Não
TrailingStepPoints Passo incremental mínimo ao mover o trailing stop. 5 Não
OnlyOnePositionPerBar Proíbe múltiplas entradas durante o mesmo candle. true Não
UseAutoDigits Multiplica o tamanho do ponto por 10 para símbolos com 3 ou 5 casas decimais, convertendo para unidades de pip. true Não
ReverseAfterStop Habilita o fluxo de reversão após perda. true Não
MaxReverseOrders Número máximo de operações reversas consecutivas. 2 Não
TakeProfitIncrease Pontos extras de take-profit adicionados para cada ordem reversa. 100 Não
CandleType Tipo de candle e período para os cálculos. TimeSpan.FromMinutes(1) Não

Gestão de risco

  • Os deslocamentos de stop-loss e take-profit são recalculados usando o passo de preço do instrumento. A detecção automática de dígitos converte cotações de cinco dígitos em distâncias do tamanho de um pip.
  • A lógica de trailing só é ativada depois que o mercado avança pela distância de trailing especificada e impõe um passo mínimo antes de modificar o stop.
  • A negociação reversa é reiniciada depois de uma saída lucrativa ou depois de atingir o limite configurado de reversões consecutivas.

Notas práticas

  • Funciona melhor em pares de moedas com spreads apertados, onde pequenas mudanças de volatilidade podem indicar rajadas de momentum.
  • Considere alinhar o período do candle com a sessão de mercado alvo; o período padrão de 1 minuto captura rompimentos de alta frequência.
  • Como as reversões são executadas imediatamente após um fechamento perdedor, garanta que haja margem suficiente para operações consecutivas.
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;

public class BreakthroughVolatilityStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public BreakthroughVolatilityStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}