Ver no GitHub

Estratégia de Barra Interna Pequena

Visão geral

A Estratégia de Barra Interna Pequena busca um padrão compacto de barra interna seguido de uma mudança de momentum entre duas velas consecutivas. O especialista original do MetaTrader 5 foi traduzido para a API de alto nível do StockSharp e agora opera apenas em velas completadas. A abordagem é projetada para traders que preferem entradas de estilo rompimento acionadas por fases de volatilidade comprimida.

Definição do padrão

A estratégia avalia as duas velas completadas mais recentes:

  1. Condição de barra interna – a última vela finalizada deve estar completamente contida dentro do range da anterior.
  2. Filtro de ratio de range – o range da barra mãe (duas barras atrás) deve ser pelo menos um múltiplo configurável do range da barra interna. O ratio padrão é 2:1.
  3. Filtros direcionais
    • Um setup comprado requer uma barra interna de alta formando-se na metade inferior da barra mãe junto com uma barra mãe de baixa.
    • Um setup vendido requer uma barra interna de baixa formando-se na metade superior da barra mãe junto com uma barra mãe de alta.
  4. A inversão opcional troca as interpretações comprada e vendida mantendo os mesmos requisitos geométricos.

Gerenciamento de posição

O parâmetro OpenMode espelha o comportamento do EA original:

  • AnySignal – envia uma nova ordem a mercado em cada sinal. Quando existe uma posição oposta, ela é parcialmente compensada porque o StockSharp usa contas de netting.
  • SwingWithRefill – achata a exposição oposta antes de entrar e permite múltiplas adições na mesma direção.
  • SingleSwing – mantém no máximo uma operação direcional; novos sinais são ignorados enquanto um posição alinhada estiver aberta.

Tanto as entradas compradas quanto as vendidas podem ser habilitadas independentemente. O trading de reversão simplesmente inverte qual setup produz ordens compradas ou vendidas.

Parâmetros

Nome Padrão Descrição
CandleType Período de 1 hora Assinatura de velas usada para detecção de padrão.
RangeRatioThreshold 2.0 Ratio mínimo de range mãe para interno.
EnableLong true Permitir trades de alta.
EnableShort true Permitir trades de baixa.
ReverseSignals false Trocar as direções de padrão comprado e vendido.
OpenMode SwingWithRefill Controla como a exposição existente é tratada em um novo sinal.

Lógica de trading

  1. Assinar a série de velas configurada e aguardar barras finalizadas.
  2. Manter as duas últimas velas completadas para avaliar o padrão.
  3. Quando o padrão e os filtros de ratio se alinham, determinar o sinal direcional, aplicando opcionalmente a reversão.
  4. Confirmar que o trading é permitido (IsFormedAndOnlineAndAllowTrading) e que a direção relevante está habilitada.
  5. Calcular o tamanho da ordem com base no OpenMode selecionado e enviar uma ordem a mercado usando o volume base da estratégia.
  6. Atualizar o histórico de velas interno para que a vela mais recente faça parte do próximo ciclo de avaliação.

Notas de implementação

  • A estratégia usa StartProtection() para habilitar o gerenciador de risco integrado (sem valores predefinidos de stop ou take-profit). Filtros extras podem ser adicionados externamente se necessário.
  • O estado do indicador não é armazenado em coleções; apenas as duas últimas velas são mantidas conforme necessário para o padrão.
  • O algoritmo depende exclusivamente de velas completadas, evitando cálculos intrabarra em linha com as melhores práticas da API de alto nível.
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>
/// Implements the "Small Inside Bar" pattern strategy converted from MetaTrader 5.
/// The strategy searches for an inside bar with a small range compared to the mother bar
/// and opens positions following the direction of the pattern conditions.
/// </summary>
public class SmallInsideBarStrategy : Strategy
{
	/// <summary>
	/// Defines how the strategy manages simultaneous entries.
	/// </summary>
	public enum SmallInsideBarOpenModes
	{
		/// <summary>
		/// Open a new position on every signal without forcing opposite positions to close.
		/// </summary>
		AnySignal,

		/// <summary>
		/// Close opposite positions first and allow adding to the current swing direction.
		/// </summary>
		SwingWithRefill,

		/// <summary>
		/// Maintain a single position in the market and ignore additional entries while it is active.
		/// </summary>
		SingleSwing
	}

	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _rangeRatioThreshold;
	private readonly StrategyParam<bool> _enableLong;
	private readonly StrategyParam<bool> _enableShort;
	private readonly StrategyParam<bool> _reverseSignals;
	private readonly StrategyParam<SmallInsideBarOpenModes> _openMode;

	private ICandleMessage _previousCandle;
	private ICandleMessage _twoBackCandle;

	/// <summary>
	/// Type of candles used for pattern detection.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Minimum ratio between the mother bar range and the inside bar range.
	/// </summary>
	public decimal RangeRatioThreshold
	{
		get => _rangeRatioThreshold.Value;
		set => _rangeRatioThreshold.Value = value;
	}

	/// <summary>
	/// Allow long trades.
	/// </summary>
	public bool EnableLong
	{
		get => _enableLong.Value;
		set => _enableLong.Value = value;
	}

	/// <summary>
	/// Allow short trades.
	/// </summary>
	public bool EnableShort
	{
		get => _enableShort.Value;
		set => _enableShort.Value = value;
	}

	/// <summary>
	/// Reverse long and short signals.
	/// </summary>
	public bool ReverseSignals
	{
		get => _reverseSignals.Value;
		set => _reverseSignals.Value = value;
	}

	/// <summary>
	/// Mode for handling position entries.
	/// </summary>
	public SmallInsideBarOpenModes OpenMode
	{
		get => _openMode.Value;
		set => _openMode.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public SmallInsideBarStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Time frame used for pattern detection", "General");

		_rangeRatioThreshold = Param(nameof(RangeRatioThreshold), 2.25m)
			.SetGreaterThanZero()
			.SetDisplay("Range Ratio", "Minimum mother-to-inside bar range ratio", "Pattern")
			
			.SetOptimize(1.5m, 3m, 0.25m);

		_enableLong = Param(nameof(EnableLong), true)
			.SetDisplay("Enable Long", "Allow bullish trades", "Trading");

		_enableShort = Param(nameof(EnableShort), true)
			.SetDisplay("Enable Short", "Allow bearish trades", "Trading");

		_reverseSignals = Param(nameof(ReverseSignals), false)
			.SetDisplay("Reverse Signals", "Invert long and short signals", "Trading");

		_openMode = Param(nameof(OpenMode), SmallInsideBarOpenModes.SwingWithRefill)
			.SetDisplay("Open Mode", "Position management mode", "Trading");
	}

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

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

		_previousCandle = null;
		_twoBackCandle = null;
	}

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

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(ProcessCandle)
			.Start();

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

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

		if (_previousCandle == null)
		{
			_previousCandle = candle;
			return;
		}

		if (_twoBackCandle == null)
		{
			_twoBackCandle = _previousCandle;
			_previousCandle = candle;
			return;
		}

		var insideHigh = _previousCandle.HighPrice;
		var insideLow = _previousCandle.LowPrice;
		var motherHigh = _twoBackCandle.HighPrice;
		var motherLow = _twoBackCandle.LowPrice;

		if (insideHigh <= insideLow || motherHigh <= motherLow)
		{
			ShiftHistory(candle);
			return;
		}

		if (!(insideHigh < motherHigh && insideLow > motherLow))
		{
			ShiftHistory(candle);
			return;
		}

		var insideRange = insideHigh - insideLow;
		var motherRange = motherHigh - motherLow;
		var ratio = insideRange == 0 ? decimal.MaxValue : motherRange / insideRange;

		if (ratio <= RangeRatioThreshold)
		{
			ShiftHistory(candle);
			return;
		}

		var midpoint = (motherHigh + motherLow) / 2m;

		var bullishInside = _previousCandle.ClosePrice > _previousCandle.OpenPrice && insideHigh < midpoint && _twoBackCandle.ClosePrice < _twoBackCandle.OpenPrice;
		var bearishInside = _previousCandle.ClosePrice < _previousCandle.OpenPrice && insideLow < midpoint && _twoBackCandle.ClosePrice > _twoBackCandle.OpenPrice;

		if (ReverseSignals)
		{
			(bullishInside, bearishInside) = (bearishInside, bullishInside);
		}

		var shouldOpenLong = bullishInside && EnableLong;
		var shouldOpenShort = bearishInside && EnableShort;

		if (shouldOpenLong)
		{
			var volume = CalculateOrderVolume(true);

			if (volume > 0)
				BuyMarket(volume);
		}

		if (shouldOpenShort)
		{
			var volume = CalculateOrderVolume(false);

			if (volume > 0)
				SellMarket(volume);
		}

		ShiftHistory(candle);
	}

	private decimal CalculateOrderVolume(bool isLong)
	{
		var baseVolume = Volume;

		if (baseVolume <= 0)
			return 0;

		var position = Position;

		if (isLong)
		{
			if (OpenMode == SmallInsideBarOpenModes.SingleSwing && position > 0)
				return 0;

			if (position < 0 && OpenMode != SmallInsideBarOpenModes.AnySignal)
				baseVolume += Math.Abs(position);
		}
		else
		{
			if (OpenMode == SmallInsideBarOpenModes.SingleSwing && position < 0)
				return 0;

			if (position > 0 && OpenMode != SmallInsideBarOpenModes.AnySignal)
				baseVolume += Math.Abs(position);
		}

		return baseVolume;
	}

	private void ShiftHistory(ICandleMessage candle)
	{
		// Keep track of the last two finished candles for pattern evaluation.
		_twoBackCandle = _previousCandle;
		_previousCandle = candle;
	}
}