La Martingale estrategia de ruptura es una StockSharp adaptación del MetaTrader asesor experto MartinGaleBreakout.mq5. el sistema
espera velas de ruptura anormalmente grandes y coloca una orden de mercado única en la dirección de ruptura. Mientras que el EA original
rastrea un "número mágico" para gestionar sus posiciones, la implementación de StockSharp depende del contexto de la estrategia, por lo que el comportamiento
es efectivamente el mismo cuando la estrategia se ejecuta de forma aislada.
El algoritmo se centra en dos ideas centrales:
Detección de ruptura: la estrategia examina el tamaño de cada vela terminada y la compara con el rango promedio de la
diez velas anteriores. Cuando el rango actual es tres veces mayor que el promedio y la vela cierra con fuerza en el
dirección de la ruptura, se produce una señal comercial.
Recuperación al estilo Martingale: la estrategia realiza un seguimiento de las pérdidas y ganancias flotantes. Siempre que el PnL no realizado alcance el
umbral de pérdida configurado, cierra inmediatamente todas las posiciones abiertas y aumenta el siguiente objetivo de ganancias para que la siguiente operación
intenta recuperar la pérdida. Una vez que se alcanza el objetivo aumentado, los umbrales se restablecen a los valores originales.
El puerto mantiene todos los parámetros de administración de dinero del código MQL5, incluido el porcentaje de saldo reservado para margen, el
objetivos de pérdidas y ganancias basados en porcentajes, y el multiplicador que amplía la distancia de obtención de beneficios durante la fase de recuperación.
Lógica comercial
Suscríbase a la serie de velas configuradas y espere las velas terminadas.
Calcule el rango de la vela (High - Low) y mantenga un búfer de tamaño fijo con los diez rangos anteriores para determinar el
promedio de referencia utilizado para la detección de rupturas.
Calcule el PnL flotante siguiendo los precios de entrada promedio para los lados largo y corto. Si el PnL no realizado excede el
objetivo de ganancias o supera el umbral de stop-loss, cierre inmediatamente todas las posiciones y restablezca el estado de recuperación como en el
asesor experto original.
Omita la colocación de órdenes mientras la estrategia ya tenga una posición o cuando el estado de conexión no permita operar.
Cuando aparezca una vela de ruptura alcista, dimensione la orden para que la ganancia esperada coincida con el objetivo actual. la toma de ganancias
La distancia en los pasos de precio se multiplica durante la recuperación, exactamente como el parámetro TP_Points_Multiplier del EA.
Valide el volumen calculado con los límites del instrumento (mínimo, máximo y paso) y asegúrese de que se cumpla el margen requerido.
no excede la asignación de saldo configurada o los fondos gratuitos disponibles. Si se respetan las restricciones, envíe un
orden de compra de mercado.
Repita el mismo proceso para rupturas bajistas, enviando en su lugar una orden de venta de mercado.
La combinación de estas reglas recrea el comportamiento del sistema MetaTrader original, incluida la transición hacia y desde
del modo de recuperación después de un evento de stop-loss.
Parámetros
Parámetro
Descripción
Predeterminado
TakeProfitPoints
Distancia entre el precio de entrada y el precio de obtención de beneficios expresado en incrementos de precio.
50
BalancePercentAvailable
Porcentaje máximo del saldo de la cuenta que se puede reservar para margen en una sola operación.
50
TakeProfitPercentOfBalance
Beneficio objetivo expresado como porcentaje del saldo actual.
0.1
StopLossPercentOfBalance
Tamaño del stop-loss expresado como porcentaje del saldo actual.
10
RecoveryStartFraction
Fracción del stop-loss utilizado antes de cambiar al modo de recuperación.
0.1
RecoveryPointsMultiplier
Multiplicador aplicado a la distancia de obtención de beneficios durante la recuperación.
1
CandleType
Fuente de datos de velas utilizada por la estrategia (período de tiempo, velas de tick, etc.).
15-minute time frame
Notas adicionales
El cálculo del volumen replica el MetaTrader ayudante CalcLotWithTP. Deduce el tamaño del lote necesario para alcanzar el precio actual.
objetivo de ganancias para un movimiento de precio determinado y luego normaliza el resultado al paso de volumen del instrumento.
Las comprobaciones de márgenes se realizan con el mismo espíritu que CheckVolumeValue y el filtro de porcentaje de saldo utilizado en MQL
versión. Las órdenes se rechazan cuando el margen requerido excede la parte permitida del saldo o los fondos libres informados por
la cartera.
La estrategia cancela todas las órdenes activas antes de aplanar las posiciones para que el comportamiento coincida con el ayudante CloseAllOrders de
el asesor experto original.
El búfer de rango interno almacena solo diez valores y equivale a iterar sobre iHigh/iLow en la fuente EA. No
Se requieren datos históricos más allá de las últimas diez 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()