Sistema de Rompimento convertido del asesor experto de MetaTrader 5 "Cidomo". La estrategia espera una nueva vela en el marco temporal configurado, mide el rango de trading reciente y coloca órdenes stop pareadas por encima y por debajo de ese rango. Gestiona el riesgo con niveles clásicos de stop-loss/take-profit, un trailing stop opcional y dos modos de gestión de capital (volumen fijo o riesgo porcentual).
Cómo funciona
En cada vela terminada de CandleType, recolectar los últimos máximos y mínimos de BarsCount para definir el canal de corto plazo.
Colocar una orden buy stop en highest + IndentPips y una sell stop en lowest - IndentPips (ambos valores expresados en pips y convertidos a precios absolutos).
Cuando se activa una orden stop, la orden pendiente opuesta se cancela inmediatamente.
Para una posición abierta, la estrategia realiza un seguimiento de:
Stop-loss inicial (StopLossPips) y take-profit (TakeProfitPips).
Un trailing stop escalonado (TrailingStopPips / TrailingStepPips). El stop se mueve solo después de que el precio avanza al menos TrailingStop + TrailingStep, imitando el EA original.
Se usan salidas de mercado para emular las llamadas PositionModify de MetaTrader cuando el stop o take-profit es tocado.
Cuando UseTimeFilter está habilitado, las nuevas órdenes se envían solo dentro de ±30 segundos de StartHour:StartMinute (hora del servidor), replicando la estrecha ventana de trading del script fuente.
Gestión de capital
FixedVolume: siempre opera el TradeVolume exacto especificado por el usuario.
RiskPercent: calcula el tamaño de la orden de modo que una operación perdedora a la distancia de stop-loss configurada reduzca el capital en RiskPercent. Los volúmenes se redondean al VolumeStep del instrumento y se limitan entre MinVolume / MaxVolume.
Controles de riesgo
Los niveles iniciales de stop-loss y take-profit se almacenan localmente y se ejecutan mediante órdenes de mercado cuando el precio cruza el objetivo durante la siguiente vela.
El trailing stop solo se mueve en una dirección y respeta la distancia de paso del EA original, evitando pequeños ajustes constantes.
Si no se configura stop-loss, el dimensionamiento de posición basado en riesgo vuelve automáticamente al TradeVolume fijo.
Parámetros
Nombre
Tipo
Por defecto
Descripción
CandleType
DataType
H4
Marco temporal usado para construir el rango de ruptura.
BarsCount
int
15
Número de velas completadas consideradas al calcular el máximo más alto y mínimo más bajo.
IndentPips
decimal
3
Offset (en pips) añadido por encima/debajo del rango antes de enviar órdenes stop.
StopLossPips
decimal
50
Distancia protectora del stop en pips. Un valor de 0 deshabilita el stop.
TakeProfitPips
decimal
50
Distancia del objetivo de ganancia en pips. Un valor de 0 deshabilita el objetivo.
TrailingStopPips
decimal
35
Distancia del trailing stop en pips. Establecer en 0 para deshabilitar el trailing.
TrailingStepPips
decimal
5
Ganancia extra mínima requerida antes de ajustar el trailing stop.
MoneyManagement
CidomoMoneyManagementMode
RiskPercent
Elige entre tamaño de posición fijo y dimensionamiento basado en riesgo.
RiskPercent
decimal
1
Porcentaje de capital arriesgado por operación cuando MoneyManagement = RiskPercent.
TradeVolume
decimal
0.1
Volumen de orden fijo usado en modo FixedVolume o cuando el dimensionamiento basado en riesgo no puede calcularse.
UseTimeFilter
bool
false
Habilita el filtro de ventana de tiempo de ±30 segundos.
StartHour
int
9
Hora (0-23) del centro de la ventana de trading.
StartMinute
int
58
Minuto (0-59) del centro de la ventana de trading.
Notas
Todos los parámetros basados en pips se adaptan automáticamente a cotizaciones de 3 o 5 dígitos multiplicando el PriceStep del instrumento por 10, exactamente como la implementación de MetaTrader.
Dado que StockSharp gestiona los stops del lado del cliente en este port, asegúrate de que la estrategia permanezca conectada para que las salidas de mercado puedan emitirse cuando los niveles de protección sean superados.
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 CidomoStrategy : 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 CidomoStrategy()
{
_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 cidomo_strategy(Strategy):
def __init__(self):
super(cidomo_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(cidomo_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(cidomo_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 cidomo_strategy()