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:
Condição de barra interna – a última vela finalizada deve estar completamente contida dentro do range da anterior.
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.
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.
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
Assinar a série de velas configurada e aguardar barras finalizadas.
Manter as duas últimas velas completadas para avaliar o padrão.
Quando o padrão e os filtros de ratio se alinham, determinar o sinal direcional, aplicando opcionalmente a reversão.
Confirmar que o trading é permitido (IsFormedAndOnlineAndAllowTrading) e que a direção relevante está habilitada.
Calcular o tamanho da ordem com base no OpenMode selecionado e enviar uma ordem a mercado usando o volume base da estratégia.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Messages import DataType, CandleStates
from System import TimeSpan, Math
class small_inside_bar_strategy(Strategy):
def __init__(self):
super(small_inside_bar_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._range_ratio_threshold = self.Param("RangeRatioThreshold", 2.25)
self._enable_long = self.Param("EnableLong", True)
self._enable_short = self.Param("EnableShort", True)
self._reverse_signals = self.Param("ReverseSignals", False)
self._prev_candle_high = None
self._prev_candle_low = None
self._prev_candle_open = None
self._prev_candle_close = None
self._two_back_high = None
self._two_back_low = None
self._two_back_open = None
self._two_back_close = None
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(small_inside_bar_strategy, self).OnStarted2(time)
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._prev_candle_high is None:
self._cache_prev(candle)
return
if self._two_back_high is None:
self._shift_history(candle)
return
inside_high = self._prev_candle_high
inside_low = self._prev_candle_low
mother_high = self._two_back_high
mother_low = self._two_back_low
if inside_high <= inside_low or mother_high <= mother_low:
self._shift_history(candle)
return
if not (inside_high < mother_high and inside_low > mother_low):
self._shift_history(candle)
return
inside_range = inside_high - inside_low
mother_range = mother_high - mother_low
ratio = mother_range / inside_range if inside_range != 0 else 1e18
if ratio <= self._range_ratio_threshold.Value:
self._shift_history(candle)
return
midpoint = (mother_high + mother_low) / 2.0
bullish_inside = (self._prev_candle_close > self._prev_candle_open and
inside_high < midpoint and
self._two_back_close < self._two_back_open)
bearish_inside = (self._prev_candle_close < self._prev_candle_open and
inside_low < midpoint and
self._two_back_close > self._two_back_open)
if self._reverse_signals.Value:
bullish_inside, bearish_inside = bearish_inside, bullish_inside
should_open_long = bullish_inside and self._enable_long.Value
should_open_short = bearish_inside and self._enable_short.Value
if should_open_long:
volume = self._calc_order_volume(True)
if volume > 0:
self.BuyMarket(volume)
if should_open_short:
volume = self._calc_order_volume(False)
if volume > 0:
self.SellMarket(volume)
self._shift_history(candle)
def _calc_order_volume(self, is_long):
base_volume = float(self.Volume)
if base_volume <= 0:
return 0
position = self.Position
if is_long:
if position < 0:
base_volume += abs(position)
else:
if position > 0:
base_volume += abs(position)
return base_volume
def _cache_prev(self, candle):
self._prev_candle_high = float(candle.HighPrice)
self._prev_candle_low = float(candle.LowPrice)
self._prev_candle_open = float(candle.OpenPrice)
self._prev_candle_close = float(candle.ClosePrice)
def _shift_history(self, candle):
self._two_back_high = self._prev_candle_high
self._two_back_low = self._prev_candle_low
self._two_back_open = self._prev_candle_open
self._two_back_close = self._prev_candle_close
self._cache_prev(candle)
def OnReseted(self):
super(small_inside_bar_strategy, self).OnReseted()
self._prev_candle_high = None
self._prev_candle_low = None
self._prev_candle_open = None
self._prev_candle_close = None
self._two_back_high = None
self._two_back_low = None
self._two_back_open = None
self._two_back_close = None
def CreateClone(self):
return small_inside_bar_strategy()