Esta carpeta contiene el port de StockSharp del asesor experto de MetaTrader "Fractals Martingale". La estrategia mezcla fractales de Bill Williams, un filtro de tendencia basado en Ichimoku y una confirmación mensual de MACD. El dimensionamiento de posición sigue una secuencia martingala clásica que multiplica el volumen de operación después de cada ciclo perdedor, mientras que un enfriamiento opcional previene exposiciones descontroladas.
Lógica de trading
Detección de fractales en el marco temporal de trabajo – las velas finalizadas se almacenan en búfer para detectar máximos y mínimos locales separados por FractalDepth vecinos. Se registra una configuración alcista cuando la siguiente vela abre por encima del máximo fractal, mientras que una configuración bajista requiere la siguiente apertura por debajo del mínimo fractal. Los niveles detectados permanecen válidos durante FractalLookback velas procesadas.
Filtro de tendencia Ichimoku – el fractal debe alinearse con la tendencia de Ichimoku calculada en el marco temporal superior definido por IchimokuCandleType. Las operaciones long requieren que Tenkan-sen esté por encima de Kijun-sen; las operaciones short requieren que Tenkan-sen esté por debajo de Kijun-sen.
Confirmación mensual de MACD – el EA original usaba un MACD mensual para decidir si dominan compradores o vendedores. El port se suscribe a la serie MacdCandleType (velas de 30 días por defecto) y solo acepta señales long cuando la línea MACD está por encima de la línea de señal; las señales short necesitan la condición opuesta.
Filtro de sesión – las órdenes se colocan solo entre StartHour (inclusive) y EndHour (exclusive). Se admite una ventana nocturna para sesiones de trading nocturnas.
Escala de volumen martingala – el tamaño base de la orden proviene de TradeVolume. Después de cada ronda perdedora, el siguiente volumen de orden se multiplica por Multiplier y se alinea al paso de volumen del instrumento. Las operaciones ganadoras restablecen la secuencia. Cuando se supera MaxConsecutiveLosses, el algoritmo hace una pausa de PauseMinutes antes de reanudarse con el volumen base.
Cambio de dirección – siempre que se envía una nueva operación, la estrategia compensa automáticamente cualquier posición opuesta antes de abrir exposición en la dirección solicitada.
Gestión de riesgos
StopLossPips y TakeProfitPips se convierten a distancias de precio absolutas usando el tamaño de pip detectado y se aplican a través de StartProtection. Esto refleja el EA original donde ambos stops se definían en pips.
La implementación original exponía trailing stops opcionales basados en dinero. El port de StockSharp depende del bloque de protección incorporado porque el manejo de la moneda del portfolio real es específico del broker.
Parámetros
Parámetro
Descripción
TradeVolume
Tamaño base de la orden usado para la primera entrada de una secuencia.
Multiplier
Factor aplicado al próximo volumen de operación después de una pérdida.
StopLossPips, TakeProfitPips
Distancias de stop de protección y objetivo medidas en pips.
FractalDepth
Número de velas en cada lado requeridas para confirmar un máximo/mínimo fractal.
FractalLookback
Número máximo de velas procesadas para las cuales un fractal detectado permanece válido.
StartHour, EndHour
Ventana de trading expresada en horas del intercambio. Cuando ambos valores coinciden el filtro se deshabilita.
MaxConsecutiveLosses
Número de operaciones perdedoras antes de que la estrategia haga una pausa.
PauseMinutes
Duración del período de enfriamiento activado después de superar el límite de pérdidas.
TenkanPeriod, KijunPeriod, SenkouPeriod
Longitudes de Ichimoku Kinko Hyo usadas en el marco temporal superior.
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod
Longitudes de EMA para la confirmación MACD del marco temporal superior.
CandleType
Serie de velas primaria donde se evalúan los fractales y las ejecuciones.
IchimokuCandleType
Marco temporal superior utilizado para calcular las líneas Tenkan y Kijun.
MacdCandleType
Marco temporal utilizado para calcular el filtro MACD (mensual por defecto).
Notas de uso
Cálculo del tamaño de pip – el valor del pip se deriva de Security.PriceStep. Las cotizaciones forex de cinco dígitos se escalan automáticamente para coincidir con la definición de MetaTrader usada en el EA fuente.
Suscripciones de indicadores – la estrategia consume hasta tres series de velas. Asegúrese de que el feed de datos pueda suministrar todos los marcos temporales solicitados para mantener los filtros sincronizados.
Precauciones de martingala – doblar el volumen aumenta rápidamente la exposición. Use los parámetros de enfriamiento o reduzca el multiplicador si la cuenta no puede soportar rachas de pérdidas prolongadas.
Diferencias vs. el EA de MT4 – las alertas de correo/notificación, los trailing stops basados en balance y las verificaciones de margen explícitas fueron eliminadas porque StockSharp ya maneja la conectividad, la seguridad del portfolio y la ejecución de órdenes. La lógica de entrada/salida central coincide con la implementación MQL.
Archivos
CS/FractalsMartingaleStrategy.cs – implementación en C# usando la API de estrategia de alto nivel.
README.md – documentación en inglés (este archivo).
README_zh.md – traducción al chino simplificado.
README_ru.md – traducción al ruso.
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 FractalsMartingaleStrategy : 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 FractalsMartingaleStrategy()
{
_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 fractals_martingale_strategy(Strategy):
def __init__(self):
super(fractals_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(fractals_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(fractals_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 fractals_martingale_strategy()