Estrategia de identificador de ciclo cíclope
Descripción general
Esta estrategia traslada el asesor experto de MetaTrader Cyclops v1.2 junto con su indicador propietario CycleIdentifier al nivel alto de StockSharp API. El algoritmo suaviza los precios de cierre con un promedio móvil suavizado (SMMA), mide la volatilidad reciente a través de un rango verdadero promedio retrospectivo largo y marca puntos de inflexión del ciclo cuando el precio se aleja lo suficiente de la oscilación más reciente. Las reversiones de ciclo importantes generan nuevas entradas, mientras que las reversiones menores ofrecen señales de salida opcionales.
Un filtro de retraso cero configurable valida la pendiente de la serie suavizada. El filtro puede funcionar directamente en datos de precios suavizados o en un RSI estilo Wilder derivado de la misma serie. Hay confirmación adicional disponible a través de un indicador Momentum clásico, y las operaciones se pueden limitar a una ventana de día/hora específica.
Lógica de señal
- Detección de ciclo: la máquina de estado interna rastrea los últimos máximos y mínimos del precio suavizado. Cuando el precio supera el umbral adaptativo (rango promedio × Longitud), la estrategia marca un ciclo menor. Se requiere un múltiplo mayor (MajorCycleStrength) para marcar un ciclo importante.
- Entradas – Los principales ciclos alcistas (
MajorBuy) abren posiciones largas; Los principales ciclos bajistas (MajorSell) abren cortos. Las posiciones activas se cierran automáticamente antes de retroceder al lado opuesto.
- Salidas opcionales: cuando UseExitSignal está habilitado, las operaciones rentables pueden cerrarse en la señal de ciclo menor correspondiente (
MinorSellExit para largos, MinorBuyExit para cortos) si no hay ningún ciclo principal opuesto presente.
- Filtro de retardo cero: si UseCycleFilter está habilitado, un filtro de suavizado de retardo cero debe confirmar la pendiente (aumentando para las posiciones largas, bajando para las posiciones cortas). La fuente del filtro se selecciona mediante CycleFilterMode (precio suavizado o RSI).
- Filtro de impulso: con UseMomentumFilter habilitado, las entradas requieren
Momentum ≥ MomentumTriggerLong para largos y Momentum ≤ MomentumTriggerShort para cortos.
Gestión comercial
- Objetivos fijos – TakeProfitPips y StopLossPips definen salidas fijas opcionales en pips de instrumentos.
- ** Punto de equilibrio **: cuando se alcanzan los pips de ganancia BreakEvenTrigger, el stop se coloca en la entrada ± un pip.
- Trailing – TrailingStopTrigger activa un trailing stop que sigue el precio en TrailingStopPips una vez que se alcanza la distancia de activación.
- Control de sesión: si UseTimeRestriction es verdadero, se permiten nuevas posiciones solo antes del
DayEnd (0=domingo) y hasta el HourEnd (inclusive) de ese día. Las operaciones existentes se siguen gestionando posteriormente.
Parámetros
| Parámetro |
Descripción |
Volume |
Volumen de pedidos utilizado para las entradas. |
PriceActionFilter |
Longitud de la media móvil suavizada aplicada al precio de cierre. |
Length |
Multiplicador aplicado al rango promedio para detectar ciclos menores. |
MajorCycleStrength |
Multiplicador que separa las oscilaciones mayores de las menores. |
UseCycleFilter |
Habilita la confirmación de pendiente con retraso cero. |
CycleFilterMode |
Selecciona entrada sin retraso: precio suavizado (Sma) o RSI (Rsi). |
FilterStrengthSma |
Longitud del filtro de retraso cero cuando se utiliza el precio suavizado. |
FilterStrengthRsi |
Duración y período RSI cuando el filtro se basa en valores RSI. |
UseMomentumFilter |
Activa o desactiva la confirmación de impulso. |
MomentumPeriod |
Longitud del indicador de momento. |
MomentumTriggerLong |
Impulso mínimo requerido para entradas largas. |
MomentumTriggerShort |
Máximo impulso permitido para entradas cortas. |
UseExitSignal |
Permite salidas basadas en ciclos menores cuando son rentables. |
UseTimeRestriction |
Limita el comercio a la ventana configurada de día/hora de la semana. |
DayEnd |
Último día de la semana en el que se permiten nuevas entradas. |
HourEnd |
Última hora del último día de negociación para nuevas entradas. |
BreakEvenTrigger |
Beneficio en pips necesarios para activar el punto de equilibrio. |
TrailingStopTrigger |
Beneficio en pips necesarios para comenzar a seguir. |
TrailingStopPips |
Distancia en pips mantenida por el trailing stop. |
TakeProfitPips |
Distancia de toma de ganancias fija en pips. |
StopLossPips |
Distancia fija de stop-loss en pips. |
CandleType |
Cronograma primario que alimenta la estrategia. |
Diferencias respecto al EA original
- El rango promedio se estima con un rango verdadero promedio de 250 períodos multiplicado por Longitud, lo que proporciona un comportamiento equivalente al intervalo alto/bajo usado en MQL.
- La confirmación de impulso utiliza el valor real del indicador (el script MQL se compara con el multiplicador de pips
bm, lo que desactiva efectivamente el filtro).
- El suavizado de retraso cero se implementa con los mismos coeficientes recursivos pero expresados en aritmética decimal. El modo RSI utiliza un Wilder RSI cuyo período es igual a FilterStrengthRsi.
Notas de uso
- Seleccione el instrumento y vincule el parámetro
CandleType al período de tiempo deseado.
- Configure los ajustes de riesgo y sesión para que coincidan con el entorno de su corredor.
- Habilite UseCycleFilter o UseMomentumFilter cuando se requiera una confirmación más estricta; deshabilítelos para entradas más rápidas pero más ruidosas.
- La estrategia mantiene como máximo una posición abierta. Las señales de ciclo opuesto cierran la posición actual antes de que se evalúe una nueva.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Cyclops CycleIdentifier: EMA crossover with RSI confirmation and ATR stops.
/// </summary>
public class CyclopsCycleIdentifierStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public CyclopsCycleIdentifierStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 8)
.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 21)
.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 50) { _entryPrice = close; SellMarket(); }
}
_prevFast = fastVal; _prevSlow = slowVal;
}
}
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
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, AverageTrueRange
class cyclops_cycle_identifier_strategy(Strategy):
def __init__(self):
super(cyclops_cycle_identifier_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(2))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 8) \
.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 21) \
.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastEmaLength(self):
return self._fast_ema_length.Value
@property
def SlowEmaLength(self):
return self._slow_ema_length.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(cyclops_cycle_identifier_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastEmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowEmaLength
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._rsi, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
rv = float(rsi_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if (fv < sv and self._prev_fast >= self._prev_slow) or close <= self._entry_price - av * 2.0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if (fv > sv and self._prev_fast <= self._prev_slow) or close >= self._entry_price + av * 2.0:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow and rv > 50:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow and rv < 50:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(cyclops_cycle_identifier_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return cyclops_cycle_identifier_strategy()