A Estratégia de Hedge Bidir MO é uma versão StockSharp do MetaTrader 4 consultor especialista mo_bidir_v0_1. O robô original foi projetado para o gráfico de cinco minutos e sempre manteve uma exposição de mercado protegida: cada nova barra abria uma posição longa e curta com distâncias de stop-loss e take-profit predefinidas. A versão StockSharp reproduz esse comportamento usando velas finalizadas, auxiliares de pedidos de alto nível e parâmetros de risco explícitos medidos em pontos de instrumento.
Lógica de negociação
Assine o tipo de vela configurado (período de cinco minutos por padrão) e processe apenas velas concluídas.
Assim que uma vela se fechar, inspecione as pernas internas da sebe. Se alguma perna permanecer aberta, a estratégia aguarda o acionamento de ordens de proteção e não abre posições adicionais.
Quando nenhuma perna estiver ativa, envie uma ordem de compra no mercado e uma ordem de venda no mercado de tamanho igual. Cada ordem executada torna-se uma perna de hedge independente monitorada pela estratégia.
Após o preenchimento de cada entrada, os limites de stop-loss e take-profit são calculados multiplicando as distâncias dos pontos configuradas pela etapa de preço do instrumento (ou incremento mínimo de preço quando a etapa não está disponível).
Em cada vela finalizada subsequente, a estratégia verifica os máximos e mínimos das velas:
As pernas longas fecham através de uma venda no mercado quando a mínima ultrapassa o nível de stop; se não for interrompido, uma alta que atinge a meta fecha a perna com lucro.
As pernas curtas fecham por meio de uma compra de mercado quando a máxima atinge o stop; caso contrário, uma baixa que atinge a meta gera o lucro.
Quando ambos os limites caem dentro da mesma vela, o stop loss é priorizado porque seu toque teria fechado a posição primeiro na implementação MetaTrader.
Assim que todas as pernas estiverem fechadas em seus níveis de proteção, a estratégia prepara imediatamente o próximo par coberto no fechamento da vela seguinte.
Este fluxo de trabalho mantém a paridade com a lógica MT4 enquanto depende exclusivamente de APIs StockSharp de alto nível (BuyMarket/SellMarket) e do processamento baseado em velas exigido pelas diretrizes de conversão.
Parâmetros
Parâmetro
Descrição
TradeVolume
Tamanho do pedido aplicado a ambos os lados da cobertura. Deve ser positivo.
StopLossPoints
Distância do preço de entrada até o stop de proteção medido em pontos do instrumento. Use 0 para desativar a parada.
TakeProfitPoints
Distância alvo do preço de entrada em pontos do instrumento. Use 0 para desativar a meta de lucro.
CandleType
Prazo usado para detectar novas barras. O padrão é um período de cinco minutos.
Todas as distâncias baseadas em pontos são convertidas em preços absolutos multiplicando o valor configurado pelo instrumento PriceStep. Se a etapa for indefinida, será utilizado o incremento mínimo de preço; quando nenhum valor está disponível, os níveis de proteção permanecem inativos.
Gestão de risco
Ambos os lados da cobertura utilizam o mesmo volume fixo e dependem de ordens de proteção simétricas.
As distâncias de stop-loss e take-profit refletem os padrões MetaTrader (80 e 750 pontos respectivamente), preservando a relação "8 pips vs. 75 pips" em um símbolo forex de 5 dígitos.
Cada etapa é fechada com uma ordem de mercado, liberando margem instantaneamente e permitindo que a etapa restante continue sem gerenciamento até que seu próprio nível de proteção seja atingido.
Notas de implementação
A estratégia processa estritamente velas acabadas para cumprir as regras de conversão de todo o projeto. O stop intrabarra ou os toques no alvo são inferidos a partir dos extremos da vela, portanto, os backtests sem dados de tick assumirão o stop acionado antes do alvo quando ambos os preços apareceram dentro da mesma barra.
O livro-razão de hedge interno acompanha os preenchimentos independentemente da posição líquida do portfólio. Isso reflete o comportamento MetaTrader onde posições longas e curtas coexistem simultaneamente.
Nenhuma lógica de rastreamento automatizada, filtros de sessão ou indicadores adicionais são introduzidos — a versão StockSharp permanece intencionalmente tão minimalista quanto o consultor especialista original.
Dicas de uso
Ajuste TradeVolume para corresponder aos tamanhos dos contratos do corretor e garantir que o instrumento suporte cobertura simultânea de compra/venda se o ambiente exigir.
Se você precisar de valores baseados em pip (por exemplo, 8 pips), multiplique pelo número de pontos que representam um pip para o símbolo atual antes de atribuir o parâmetro.
Combine a estratégia com StockSharp módulos de risco ou StartProtection se forem necessárias salvaguardas extras no nível do portfólio.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Mo Bidir: EMA crossover with ATR stops.
/// </summary>
public class MoBidirStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public MoBidirStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 10)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 25)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fastEma, slowEma, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow) { _entryPrice = close; BuyMarket(); }
else if (fastVal < slowVal && _prevFast >= _prevSlow) { _entryPrice = close; SellMarket(); }
}
_prevFast = fastVal; _prevSlow = slowVal;
}
}
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 mo_bidir_strategy(Strategy):
def __init__(self):
super(mo_bidir_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 10) \
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 25) \
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastEmaLength(self):
return self._fast_ema_length.Value
@property
def SlowEmaLength(self):
return self._slow_ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(mo_bidir_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastEmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowEmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if (fv < sv and self._prev_fast >= self._prev_slow) or close <= self._entry_price - av * 2.0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if (fv > sv and self._prev_fast <= self._prev_slow) or close >= self._entry_price + av * 2.0:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(mo_bidir_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return mo_bidir_strategy()