A Martingale Estratégia de Breakout é uma versão StockSharp do MetaTrader consultor especialista MartinGaleBreakout.mq5. O sistema
espera por velas de rompimento anormalmente grandes e coloca uma única ordem de mercado na direção do rompimento. Embora o EA original
rastreia um "número mágico" para gerenciar suas posições, a implementação do StockSharp depende do contexto da estratégia, portanto o comportamento
é efetivamente o mesmo quando a estratégia é executada isoladamente.
O algoritmo se concentra em duas ideias principais:
Detecção de ruptura – a estratégia examina o tamanho de cada vela finalizada e compara-a com o intervalo médio da
dez velas anteriores. Quando o intervalo atual é três vezes maior que a média e a vela fecha fortemente no
direção do rompimento, um sinal de negociação é produzido.
Martingalerecuperação estilo – a estratégia monitora lucros e perdas flutuantes. Sempre que o PnL não realizado atingir o
limite de perda configurado, ele fecha imediatamente todas as posições abertas e aumenta a próxima meta de lucro para que a negociação seguinte
tenta recuperar a perda. Assim que a meta aumentada for atingida, os limites serão redefinidos para os valores originais.
A porta mantém todos os parâmetros de gerenciamento de dinheiro do código MQL5, incluindo a porcentagem do saldo reservada para margem, o
metas de lucros e perdas baseadas em porcentagem e o multiplicador que expande a distância de obtenção de lucro durante a fase de recuperação.
Lógica de negociação
Assine a série de velas configuradas e aguarde as velas finalizadas.
Calcule o intervalo da vela (High - Low) e mantenha um buffer de tamanho fixo com os dez intervalos anteriores para determinar o
média de referência usada para detecção de fuga.
Calcule o PnL flutuante acompanhando os preços médios de entrada para os lados comprado e vendido. Se o PnL não realizado exceder o
meta de lucro ou violar o limite de stop-loss, fechar imediatamente todas as posições e redefinir o estado de recuperação como no
consultor especialista original.
Ignore a colocação de ordens enquanto a estratégia já mantém uma posição ou quando a negociação não é permitida pelo estado da conexão.
Quando uma vela de alta aparecer, dimensione o pedido para que o lucro esperado corresponda à meta atual. O lucro
a distância nas etapas de preço é multiplicada durante a recuperação, exatamente como o parâmetro TP_Points_Multiplier do EA.
Valide o volume calculado em relação aos limites do instrumento (mínimo, máximo e passo) e certifique-se de que a margem necessária
não excede a alocação de saldo configurada ou os fundos livres disponíveis. Se as restrições forem respeitadas, envie um
ordem de compra de mercado.
Repita o mesmo processo para rompimentos de baixa, enviando uma ordem de venda a mercado.
A combinação dessas regras recria o comportamento do sistema MetaTrader original, incluindo a transição de entrada e saída
do modo de recuperação após um evento de stop-loss.
Parâmetros
Parâmetro
Descrição
Padrão
TakeProfitPoints
Distância entre o preço de entrada e o preço de realização de lucro expresso em etapas de preço.
50
BalancePercentAvailable
Porcentagem máxima do saldo da conta que pode ser reservada para margem em uma única negociação.
50
TakeProfitPercentOfBalance
Lucro alvo expresso como uma percentagem do saldo atual.
0.1
StopLossPercentOfBalance
Tamanho do stop loss expresso como uma porcentagem do saldo atual.
10
RecoveryStartFraction
Fração do stop loss usada antes de mudar para o modo de recuperação.
0.1
RecoveryPointsMultiplier
Multiplicador aplicado à distância de lucro durante a recuperação.
1
CandleType
Fonte de dados de velas usada pela estratégia (período de tempo, velas de tick, etc.).
15-minute time frame
Notas adicionais
O cálculo do volume replica o auxiliar MetaTrader CalcLotWithTP. Ele deriva o tamanho do lote necessário para atingir o atual
meta de lucro para um determinado movimento de preço e, em seguida, normaliza o resultado para a etapa de volume do instrumento.
As verificações de margem são realizadas com o mesmo espírito de CheckVolumeValue e o filtro de porcentagem de saldo usado em MQL
versão. As ordens são rejeitadas quando a margem exigida excede a parcela permitida do saldo ou os fundos livres informados pelo
o portfólio.
A estratégia cancela todos os pedidos ativos antes de nivelar as posições para que o comportamento corresponda ao auxiliar CloseAllOrders de
o consultor especialista original.
O buffer de intervalo interno armazena apenas dez valores e é equivalente à iteração sobre iHigh/iLow na origem EA. Não
são necessários dados históricos além das últimas dez velas.
using System;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Breakout strategy that detects abnormally large candles and enters in the breakout direction.
/// Uses a simple martingale recovery: after a losing trade, the next entry is taken more aggressively.
/// </summary>
public class MartingaleBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<decimal> _breakoutMultiplier;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<DataType> _candleType;
private readonly decimal[] _rangeBuffer = new decimal[10];
private int _rangeBufferCount;
private int _rangeBufferIndex;
private decimal _rangeBufferSum;
private decimal _entryPrice;
private Sides? _entrySide;
private bool _lastWasLoss;
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
public decimal BreakoutMultiplier
{
get => _breakoutMultiplier.Value;
set => _breakoutMultiplier.Value = value;
}
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public MartingaleBreakoutStrategy()
{
_lookback = Param(nameof(Lookback), 10)
.SetDisplay("Lookback", "Number of candles for average range", "General");
_breakoutMultiplier = Param(nameof(BreakoutMultiplier), 3m)
.SetDisplay("Breakout Mult", "Multiplier above avg range for breakout", "General");
_takeProfitPct = Param(nameof(TakeProfitPct), 1m)
.SetDisplay("Take Profit %", "Take profit as percentage of entry price", "Trading");
_stopLossPct = Param(nameof(StopLossPct), 0.5m)
.SetDisplay("Stop Loss %", "Stop loss as percentage of entry price", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "General");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rangeBufferCount = 0;
_rangeBufferIndex = 0;
_rangeBufferSum = 0m;
_entryPrice = 0m;
_entrySide = null;
_lastWasLoss = false;
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;
var closePrice = candle.ClosePrice;
// Check exit conditions first
if (Position != 0 && _entryPrice > 0)
{
var tp = _lastWasLoss ? TakeProfitPct * 1.5m : TakeProfitPct;
var sl = StopLossPct;
if (_entrySide == Sides.Buy)
{
var pnlPct = (closePrice - _entryPrice) / _entryPrice * 100m;
if (pnlPct >= tp || pnlPct <= -sl)
{
_lastWasLoss = pnlPct < 0;
SellMarket();
_entryPrice = 0;
_entrySide = null;
UpdateRangeStatistics(candle);
return;
}
}
else if (_entrySide == Sides.Sell)
{
var pnlPct = (_entryPrice - closePrice) / _entryPrice * 100m;
if (pnlPct >= tp || pnlPct <= -sl)
{
_lastWasLoss = pnlPct < 0;
BuyMarket();
_entryPrice = 0;
_entrySide = null;
UpdateRangeStatistics(candle);
return;
}
}
}
// Entry logic - only when flat
if (Position == 0)
{
var range = candle.HighPrice - candle.LowPrice;
if (_rangeBufferCount >= _rangeBuffer.Length)
{
var avgRange = _rangeBufferSum / _rangeBuffer.Length;
if (range > avgRange * BreakoutMultiplier)
{
var body = candle.ClosePrice - candle.OpenPrice;
if (body > 0 && body > range * 0.4m)
{
// Bullish breakout
BuyMarket();
_entryPrice = closePrice;
_entrySide = Sides.Buy;
}
else if (body < 0 && Math.Abs(body) > range * 0.4m)
{
// Bearish breakout
SellMarket();
_entryPrice = closePrice;
_entrySide = Sides.Sell;
}
}
}
}
UpdateRangeStatistics(candle);
}
private void UpdateRangeStatistics(ICandleMessage candle)
{
var range = candle.HighPrice - candle.LowPrice;
if (_rangeBufferCount < _rangeBuffer.Length)
{
_rangeBuffer[_rangeBufferIndex] = range;
_rangeBufferSum += range;
_rangeBufferCount++;
_rangeBufferIndex = (_rangeBufferIndex + 1) % _rangeBuffer.Length;
return;
}
_rangeBufferSum -= _rangeBuffer[_rangeBufferIndex];
_rangeBuffer[_rangeBufferIndex] = range;
_rangeBufferSum += range;
_rangeBufferIndex = (_rangeBufferIndex + 1) % _rangeBuffer.Length;
}
/// <inheritdoc />
protected override void OnReseted()
{
Array.Clear(_rangeBuffer);
_rangeBufferCount = 0;
_rangeBufferIndex = 0;
_rangeBufferSum = 0m;
_entryPrice = 0m;
_entrySide = null;
_lastWasLoss = false;
base.OnReseted();
}
}
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
class martingale_breakout_strategy(Strategy):
def __init__(self):
super(martingale_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._lookback = self.Param("Lookback", 10)
self._breakout_multiplier = self.Param("BreakoutMultiplier", 3.0)
self._take_profit_pct = self.Param("TakeProfitPct", 1.0)
self._stop_loss_pct = self.Param("StopLossPct", 0.5)
self._range_buffer = [0.0] * 10
self._range_buffer_count = 0
self._range_buffer_index = 0
self._range_buffer_sum = 0.0
self._entry_price = 0.0
self._entry_side = 0
self._last_was_loss = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def Lookback(self):
return self._lookback.Value
@Lookback.setter
def Lookback(self, value):
self._lookback.Value = value
@property
def BreakoutMultiplier(self):
return self._breakout_multiplier.Value
@BreakoutMultiplier.setter
def BreakoutMultiplier(self, value):
self._breakout_multiplier.Value = value
@property
def TakeProfitPct(self):
return self._take_profit_pct.Value
@TakeProfitPct.setter
def TakeProfitPct(self, value):
self._take_profit_pct.Value = value
@property
def StopLossPct(self):
return self._stop_loss_pct.Value
@StopLossPct.setter
def StopLossPct(self, value):
self._stop_loss_pct.Value = value
def OnReseted(self):
super(martingale_breakout_strategy, self).OnReseted()
self._range_buffer = [0.0] * 10
self._range_buffer_count = 0
self._range_buffer_index = 0
self._range_buffer_sum = 0.0
self._entry_price = 0.0
self._entry_side = 0
self._last_was_loss = False
def OnStarted2(self, time):
super(martingale_breakout_strategy, self).OnStarted2(time)
self._range_buffer = [0.0] * 10
self._range_buffer_count = 0
self._range_buffer_index = 0
self._range_buffer_sum = 0.0
self._entry_price = 0.0
self._entry_side = 0
self._last_was_loss = False
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _update_range_statistics(self, candle):
r = float(candle.HighPrice) - float(candle.LowPrice)
buf_len = len(self._range_buffer)
if self._range_buffer_count < buf_len:
self._range_buffer[self._range_buffer_index] = r
self._range_buffer_sum += r
self._range_buffer_count += 1
self._range_buffer_index = (self._range_buffer_index + 1) % buf_len
return
self._range_buffer_sum -= self._range_buffer[self._range_buffer_index]
self._range_buffer[self._range_buffer_index] = r
self._range_buffer_sum += r
self._range_buffer_index = (self._range_buffer_index + 1) % buf_len
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
buf_len = len(self._range_buffer)
# Check exit conditions first
if self.Position != 0 and self._entry_price > 0:
tp = float(self.TakeProfitPct) * 1.5 if self._last_was_loss else float(self.TakeProfitPct)
sl = float(self.StopLossPct)
if self._entry_side == 1:
pnl_pct = (close - self._entry_price) / self._entry_price * 100.0
if pnl_pct >= tp or pnl_pct <= -sl:
self._last_was_loss = pnl_pct < 0
self.SellMarket()
self._entry_price = 0.0
self._entry_side = 0
self._update_range_statistics(candle)
return
elif self._entry_side == -1:
pnl_pct = (self._entry_price - close) / self._entry_price * 100.0
if pnl_pct >= tp or pnl_pct <= -sl:
self._last_was_loss = pnl_pct < 0
self.BuyMarket()
self._entry_price = 0.0
self._entry_side = 0
self._update_range_statistics(candle)
return
# Entry logic - only when flat
if self.Position == 0:
r = float(candle.HighPrice) - float(candle.LowPrice)
if self._range_buffer_count >= buf_len:
avg_range = self._range_buffer_sum / buf_len
if r > avg_range * float(self.BreakoutMultiplier):
body = float(candle.ClosePrice) - float(candle.OpenPrice)
if body > 0 and body > r * 0.4:
self.BuyMarket()
self._entry_price = close
self._entry_side = 1
elif body < 0 and abs(body) > r * 0.4:
self.SellMarket()
self._entry_price = close
self._entry_side = -1
self._update_range_statistics(candle)
def CreateClone(self):
return martingale_breakout_strategy()