Early Bird Range Breakout é uma versão C# do MetaTrader 4 consultor especialista earlyBird1. O sistema rastreia a máxima e a mínima de uma faixa pré-mercado configurável, aplica um filtro RSI de 14 períodos para decidir a tendência comercial e entra no primeiro rompimento assim que a sessão regular for aberta. Ele preserva a restrição de negociação única por direção do consultor especialista original, a lógica de rastreamento controlada pela volatilidade e a disciplina de fechamento diário.
Lógica estratégica
Construção de intervalo
Janela de tempo – o intervalo é calculado entre Range Start Hour e Range End Hour (após aplicar a lógica de deslocamento do horário de verão). Cada vela que cruza esta janela expande o limite superior/inferior.
Buffer de entrada – um deslocamento configurável em pips é adicionado acima do intervalo alto e subtraído abaixo do intervalo baixo para imitar o buffer de breakout ±2/Fakt do script ±2/Fakt.
Redefinição diária – o intervalo, os gatilhos de entrada e os contadores de negociação são redefinidos com a primeira vela concluída de cada novo dia de negociação.
Filtro direcional
RSI em aberturas – a estratégia alimenta o RSI com preços de abertura de velas, correspondendo à implementação MT4 que usou iRSI(..., PRICE_OPEN).
Seleção de polarização – quando RSI está acima de 50 apenas o gatilho longo é armado; quando RSI é 50 ou inferior, apenas o gatilho curto está ativo. Isso garante uma configuração direcional única por vela, assim como o EA original.
Regras de entrada
Sessão de negociação – novas posições são permitidas apenas em dias úteis entre Session Start e Session End após a formação do intervalo de breakout.
Tentativa única por lado – assim que uma posição longa (ou curta) é aberta, o lado correspondente é desativado pelo resto do dia, refletindo os contadores de negociação diários no código MT4.
Mudança de hedge – com Allow Hedging ativado, a estratégia pode reverter de curta para longa (ou vice-versa) enviando volume suficiente para nivelar a exposição existente e mudar imediatamente de direção. Quando a cobertura está desativada, as entradas são ignoradas, a menos que a posição seja plana.
Regras de saída
Risco e meta fixos – os níveis de stop-loss e take-profit são expressos em pips. A meta de lucro é automaticamente limitada pela distância de parada e pela largura do intervalo, reproduzindo a lógica MathMin do consultor especialista original.
Trailing orientado pela volatilidade – quando o intervalo da vela atual excede o intervalo médio de 16 períodos multiplicado por Trailing Risk, e a negociação tem lucro de pelo menos Trailing Trigger, o stop é seguido pela distância do stop completo enquanto o take-profit é puxado para mais perto (metade do gatilho final), correspondendo ao comportamento de OrderModify no código MQL.
Encerramento da sessão – na hora de fechamento configurada, as negociações lucrativas são fechadas imediatamente. As posições perdedoras movem seu lucro para o preço de entrada, assim como a aplicação do ponto de equilíbrio do MT4.
Parâmetros
Negociação Automática – interruptor mestre de habilitação para entradas automatizadas.
Permitir Hedging – permite a reversão na direção oposta mesmo quando uma posição já está aberta.
Direção de negociação – limita a estratégia apenas para compra (1), apenas para venda (2) ou ambas as direções (0).
Volume – volume de pedidos para entradas no mercado.
Take Profit (pips) – distância máxima para a meta de lucro; a distância efetiva é limitada pelo stop loss e pela largura do intervalo.
Stop Loss (pips) – distância de parada de proteção fixa em pips.
Trailing Trigger (pips) – excursão favorável mínima necessária antes que a lógica de trailing possa ajustar o stop e o take-profit.
Risco de trilha – multiplicador aplicado ao intervalo médio de velas de 16 períodos ao avaliar se a volatilidade é alta o suficiente para trilhar.
Buffer de entrada (pips) – deslocamento de pip aplicado aos limites do intervalo ao calcular os níveis de rompimento.
Hora/minuto de início da sessão – início da janela de negociação ativa (tempo do gráfico antes do ajuste do horário de verão).
Hora de Fim da Sessão – fim da janela de negociação para novas posições.
Hora de fechamento – hora após a qual as posições são forçadas a atingir o ponto de equilíbrio ou fechar.
Hora de início do intervalo/Hora de término do intervalo – horas que definem o intervalo de pré-sessão usado para intervalos.
Início do horário de verão/Início do horário de inverno – marcadores do dia do ano usados para alternar entre deslocamentos de uma e duas horas, imitando a lógica Sommerzeit/Winterzeit.
RSI Comprimento – número de períodos para o filtro RSI (padrão 14).
Tipo de vela – período principal que orienta os cálculos (o padrão é velas de 15 minutos).
Notas adicionais
O tamanho do pip é derivado do nível de preço atual (≥ 10 unidades → 0.01, caso contrário 0.0001) exatamente como o cálculo Fakt no script MT4.
As estatísticas finais usam as últimas 16 velas concluídas, excluindo a barra atual, correspondendo à lógica de média original.
A estratégia StockSharp usa posições líquidas, portanto, posições longas e curtas simultâneas são emuladas pela compra ou venda excessiva da exposição existente quando o hedge está ativado.
Somente a implementação C# é fornecida; nenhuma versão Python acompanha esta estratégia.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Early Bird Range Breakout: N-bar high/low breakout with EMA filter and ATR stops.
/// </summary>
public class EarlyBirdRangeBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _entryPrice;
public EarlyBirdRangeBreakoutStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0; _prevLow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = 0; _prevLow = 0; _entryPrice = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (_prevHigh == 0 || atrVal <= 0) { _prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; return; }
if (Position > 0)
{
if (close <= _entryPrice - atrVal * 1.5m || close >= _entryPrice + atrVal * 2.5m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close >= _entryPrice + atrVal * 1.5m || close <= _entryPrice - atrVal * 2.5m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (close > _prevHigh && close > emaVal) { _entryPrice = close; BuyMarket(); }
else if (close < _prevLow && close < emaVal) { _entryPrice = close; SellMarket(); }
}
_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class early_bird_range_breakout_strategy(Strategy):
def __init__(self):
super(early_bird_range_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(early_bird_range_breakout_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
close = float(candle.ClosePrice)
if self._prev_high == 0 or av <= 0:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
return
if self.Position > 0:
if close <= self._entry_price - av * 1.5 or close >= self._entry_price + av * 2.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close >= self._entry_price + av * 1.5 or close <= self._entry_price - av * 2.5:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
return
if self.Position == 0:
if close > self._prev_high and close > ev:
self._entry_price = close
self.BuyMarket()
elif close < self._prev_low and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
def OnReseted(self):
super(early_bird_range_breakout_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
def CreateClone(self):
return early_bird_range_breakout_strategy()