La Estrategia Demarker Martingale recrea el asesor experto de MetaTrader "Demarker Martingale" usando la API de alto nivel de StockSharp. El sistema combina una señal del oscilador DeMarker de medio plazo con un filtro de tendencia MACD de marco temporal superior. Las entradas son seguidas por un dimensionamiento de posición al estilo martingala, niveles fijos de stop-loss y take-profit, protección de break-even y un trailing stop que imita el conjunto de herramientas de gestión monetaria del experto original.
Lógica de operación principal
Feeds de datos – la estrategia se suscribe a un marco temporal de trading definido por el usuario (velas de 15 minutos por defecto) para la generación de señales y una serie de marco temporal superior (velas mensuales por defecto) para calcular el filtro MACD.
Disparador DeMarker – cuando el valor de DeMarker supera el límite neutral DemarkerThreshold (por defecto 0.5) y la acción de precio reciente forma una superposición alcista (Low[2] < High[1]), se considera una configuración larga. A la inversa, una superposición bajista con DeMarker por debajo del umbral prepara un corto.
Confirmación MACD – el MACD de marco temporal superior debe coincidir con la dirección. Una señal alcista requiere que la línea principal de MACD esté por encima de su línea de señal, mientras que una señal bajista espera la relación opuesta. Esto reproduce el filtro MACD mensual del experto MQL.
Ejecución de órdenes – las señales válidas colocan órdenes de mercado con el volumen ajustado por martingala. Solo se mantiene una posición direccional a la vez.
Monitoreo de posición – mientras hay una posición abierta, la estrategia evalúa cada vela finalizada para detectar disparadores de stop-loss, take-profit, break-even o trailing stop. Los eventos de brecha cierran la posición completa mediante órdenes de mercado.
Gestión monetaria
Dimensionamiento inicial – las órdenes comienzan con InitialVolume alineado al VolumeStep del instrumento y acotado por VolumeMin/VolumeMax.
Escalada martingala – después de un trade perdedor, el siguiente volumen se multiplica por MartingaleMultiplier (DoubleLotSize = true) o se incrementa por LotIncrement. Los trades rentables restablecen la escalera al volumen base. La profundidad de escalada está limitada por MaxMartingaleSteps para evitar una exposición descontrolada.
Stop-loss y take-profit – las distancias se expresan en pips al estilo MetaTrader. El tamaño del pip se adapta automáticamente a las cotizaciones Forex de 3/5 dígitos, coincidiendo con la lógica ticksize original.
Break-even – una vez que la ganancia no realizada alcanza BreakEvenTriggerPips, el stop-loss se desplaza a la entrada más BreakEvenOffsetPips (largo) o menos el offset (corto).
Trailing stop – las ganancias más allá de TrailingStopPips mueven un umbral de trailing interno que se ajusta con cada vela, replicando el comportamiento TrailingStop del EA.
Parámetros
Nombre
Descripción
CandleType
Marco temporal de trading usado para señales DeMarker.
MacdCandleType
Marco temporal superior usado para calcular el filtro de tendencia MACD.
DemarkerPeriod
Período de lookback de DeMarker.
DemarkerThreshold
Límite neutral entre configuraciones alcistas y bajistas.
MacdFast / MacdSlow / MacdSignal
Longitudes EMA del MACD.
InitialVolume
Tamaño de orden base antes de los ajustes martingala.
MartingaleMultiplier
Factor de multiplicación cuando DoubleLotSize está habilitado.
LotIncrement
Incremento aditivo cuando el doblado está deshabilitado.
DoubleLotSize
Alternar entre martingala multiplicativa y aditiva.
MaxMartingaleSteps
Número máximo de escalaciones consecutivas.
StopLossPips
Distancia de stop-loss en pips.
TakeProfitPips
Distancia de take-profit en pips.
TrailingStopPips
Distancia de trailing stop en pips.
UseBreakEven
Habilitar o deshabilitar la lógica de break-even.
BreakEvenTriggerPips
Umbral de ganancia (en pips) antes de cambiar a break-even.
BreakEvenOffsetPips
Buffer aplicado al stop de break-even.
Notas de conversión
La conversión de pip refleja el EA MQL (ticksize == 0.00001 o 0.001 implica una escala de pip 10x). Esto preserva distancias de riesgo consistentes en cotizaciones de 3/5 dígitos.
El filtro de tendencia MACD usa MovingAverageConvergenceDivergenceSignal con las longitudes EMA originales y procesa una serie de velas separada para emular la lógica del gráfico mensual.
El seguimiento de la contabilidad martingala rastrea los precios de entrada ponderados promedio y el PnL realizado para decidir si el siguiente trade debe escalar o reiniciar.
Todas las acciones protectoras (stop-loss, take-profit, break-even, trailing) se ejecutan mediante salidas a mercado porque la API de alto nivel desaconseja las modificaciones directas de órdenes bajo la guardia StartProtection.
Consejos de uso
Asegúrese de que el instrumento asignado exponga PriceStep, VolumeStep, VolumeMin y VolumeMax para alinear los cálculos de pip y el redondeo de volumen con las restricciones del exchange.
Experimente con MacdCandleType (p. ej., velas semanales) para ajustar el filtro de tendencia para mercados más rápidos.
Al optimizar, ajuste conjuntamente DemarkerThreshold, TrailingStopPips y los parámetros martingala para mantener los drawdowns bajo control.
Combine la estrategia con controles de riesgo a nivel de cartera o filtros de sesión de trading al desplegar en vivo, ya que las secuencias martingala inherentemente aumentan la exposición después de las pérdidas.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class DemarkerMartingaleStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public DemarkerMartingaleStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class demarker_martingale_strategy(Strategy):
def __init__(self):
super(demarker_martingale_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(demarker_martingale_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(demarker_martingale_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return demarker_martingale_strategy()