Ver en GitHub

Estrategia de Reversión con Barra Exterior

Una barra exterior ocurre cuando el rango de una vela supera el de la vela anterior, creando un breve aumento de volatilidad. Esta estrategia opera en contra del movimiento si la barra exterior cierra en la dirección opuesta a la tendencia anterior, esperando un retorno hacia el equilibrio.

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

Cuando se forma una barra exterior, el algoritmo determina si la vela es alcista o bajista. Una barra exterior alcista tras una caída abre una posición larga con un stop por debajo del mínimo de la barra. Una barra exterior bajista tras una subida activa un corto con un stop por encima de su máximo. Las operaciones salen si el precio posteriormente rompe ese extremo.

La configuración busca reversiones rápidas tras un impulso agotador y se usa mejor cuando los mercados están agitados en lugar de seguir una tendencia fuerte.

Detalles

  • Criterios de entrada: Barra exterior cerrando en dirección opuesta al movimiento anterior.
  • Largo/Corto: Ambos.
  • Criterios de salida: Precio rompiendo el máximo/mínimo de la barra exterior o stop-loss.
  • Stops: Sí, colocados más allá del patrón.
  • Valores predeterminados:
    • CandleType = 5 minute
    • StopLossPercent = 1 Un stop de protección a este porcentaje del precio de entrada, vigilado también entre velas; la posición también se cierra cuando un cierre rompe el extremo opuesto de la barra exterior.
  • Filtros:
    • Categoría: Patrón
    • Dirección: Ambos
    • Indicadores: Candlestick
    • 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>
/// Outside Bar Reversal strategy.
/// An outside bar's high and low both exceed the previous candle's. While flat, a bullish outside bar after a bearish candle buys
/// and a bearish one after a bullish candle sells. The position closes when a close breaks through the outside bar's opposite
/// extreme, and a percent stop from the entry price, watched between candles, limits the loss.
/// </summary>
public class OutsideBarReversalStrategy : Strategy
{
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private ICandleMessage _prevCandle;
	private decimal _exitLevel;

	/// <summary>
	/// Stop-loss percentage from the entry price.
	/// </summary>
	public decimal StopLossPercent
	{
		get => _stopLossPercent.Value;
		set => _stopLossPercent.Value = value;
	}

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

	/// <summary>
	/// Constructor.
	/// </summary>
	public OutsideBarReversalStrategy()
	{
		_stopLossPercent = Param(nameof(StopLossPercent), 1m)
			.SetNotNegative()
			.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");

		_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();
		_prevCandle = null;
		_exitLevel = default;
	}

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

		_prevCandle = null;
		_exitLevel = default;

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

		StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);

		// The stop has to see prices between candles, not only at their close.
		foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
		{
			var quotes = new Subscription(DataType.Level1, Security);
			quotes.MarketData.BuildField = field;
			SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
		}

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

	private void ObserveProtectionQuote(Level1ChangeMessage quote)
	{
		// The high-level handler activates native protection before this callback, also between signal bars.
	}

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

		var previous = _prevCandle;
		_prevCandle = candle;

		if (previous == null || !IsFormedAndOnlineAndAllowTrading())
			return;

		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (close < _exitLevel)
				SellMarket(Position);

			return;
		}

		if (Position < 0)
		{
			if (close > _exitLevel)
				BuyMarket(-Position);

			return;
		}

		var isOutsideBar = candle.HighPrice > previous.HighPrice && candle.LowPrice < previous.LowPrice;

		if (!isOutsideBar)
			return;

		var afterDecline = previous.ClosePrice < previous.OpenPrice;
		var afterRally = previous.ClosePrice > previous.OpenPrice;

		if (close > candle.OpenPrice && afterDecline)
		{
			BuyMarket(Volume);
			_exitLevel = candle.LowPrice;
		}
		else if (close < candle.OpenPrice && afterRally)
		{
			SellMarket(Volume);
			_exitLevel = candle.HighPrice;
		}
	}
}