Strategie zur Identifizierung des Zyklopenzyklus
Überblick
Diese Strategie portiert den Expert Advisor Cyclops v1.2 von MetaTrader zusammen mit seinem proprietären Indikator CycleIdentifier auf das hohe Niveau von StockSharp API. Der Algorithmus glättet die Schlusskurse mit einem geglätteten gleitenden Durchschnitt (SMMA), misst die jüngste Volatilität anhand eines langen Lookback-Durchschnitts-True-Ranges und markiert Zykluswendepunkte, wenn der Preis weit genug vom letzten Schwung entfernt ist. Große Zyklusumkehrungen generieren neue Einstiege, während kleinere Umkehrungen optionale Ausstiegssignale bieten.
Ein konfigurierbarer Zero-Lag-Filter validiert die Steigung der geglätteten Reihe. Der Filter kann direkt auf geglättete Preisdaten oder auf ein aus derselben Serie abgeleitetes RSI im Wilder-Stil wirken. Eine zusätzliche Bestätigung ist über einen klassischen Momentum-Indikator verfügbar, und der Handel kann auf ein bestimmtes Wochentags-/Stundenfenster beschränkt werden.
Signallogik
- Zykluserkennung – Die interne Zustandsmaschine verfolgt die letzten Hochs und Tiefs des geglätteten Preises. Wenn der Preis den adaptiven Schwellenwert (durchschnittliche Spanne × Länge) überschreitet, markiert die Strategie einen kleinen Zyklus. Zur Kennzeichnung eines Hauptzyklus ist ein größeres Vielfaches (MajorCycleStrength) erforderlich.
- Einträge – Große Aufwärtszyklen (
MajorBuy) eröffnen Long-Positionen; Große rückläufige Zyklen (MajorSell) eröffnen Shorts. Aktive Positionen werden vor dem Rückwärtsfahren auf die Gegenseite automatisch geschlossen.
- Optionale Exits – Wenn UseExitSignal aktiviert ist, können profitable Trades beim entsprechenden Nebenzyklussignal (
MinorSellExit für Long-Positionen, MinorBuyExit für Shorts) geschlossen werden, wenn kein entgegengesetzter Hauptzyklus vorhanden ist.
- Zero-Lag-Filter – Wenn UseCycleFilter aktiviert ist, muss ein Zero-Lag-Glättungsfilter die Steigung bestätigen (steigend bei Long-Positionen, fallend bei Short-Positionen). Die Filterquelle wird durch CycleFilterMode ausgewählt (geglätteter Preis oder RSI).
- Momentum-Filter – Wenn UseMomentumFilter aktiviert ist, erfordern Einträge
Momentum ≥ MomentumTriggerLong für Long-Positionen und Momentum ≤ MomentumTriggerShort für Short-Positionen.
Handelsmanagement
- Feste Ziele – TakeProfitPips und StopLossPips definieren optionale feste Exits in Instrumenten-Pips.
- Break-Even – Wenn BreakEvenTrigger Gewinnpips erreicht werden, wird der Stop auf den Einstiegspunkt ± einen Pip gezogen.
- Trailing – TrailingStopTrigger aktiviert einen Trailing Stop, der dem Preis bei TrailingStopPips folgt, sobald die Triggerdistanz erreicht ist.
- Sitzungssteuerung – Wenn UseTimeRestriction wahr ist, sind neue Positionen nur vor
DayEnd (0=Sonntag) und bis zu HourEnd (einschließlich) an diesem Tag zulässig. Bestehende Trades werden auch im Anschluss weiterhin verwaltet.
Parameter
| Parameter |
Beschreibung |
Volume |
Auftragsvolumen, das für Einträge verwendet wird. |
PriceActionFilter |
Länge des geglätteten gleitenden Durchschnitts, der auf den Schlusskurs angewendet wird. |
Length |
Auf den Durchschnittsbereich angewendeter Multiplikator zur Erkennung kleinerer Zyklen. |
MajorCycleStrength |
Multiplikator, der große von kleinen Schwankungen trennt. |
UseCycleFilter |
Aktiviert die Bestätigung der verzögerungsfreien Steigung. |
CycleFilterMode |
Wählt die Eingabe ohne Verzögerung aus: geglätteter Preis (Sma) oder RSI (Rsi). |
FilterStrengthSma |
Länge des Zero-Lag-Filters bei Verwendung des geglätteten Preises. |
FilterStrengthRsi |
Länge und RSI Zeitraum, wenn der Filter auf RSI Werten basiert. |
UseMomentumFilter |
Schaltet die Impulsbestätigung ein oder aus. |
MomentumPeriod |
Länge des Momentum-Indikators. |
MomentumTriggerLong |
Minimaler Schwung für lange Einstiege erforderlich. |
MomentumTriggerShort |
Maximal zulässiger Schwung für kurze Einstiege. |
UseExitSignal |
Ermöglicht Exits auf Basis kleinerer Zyklen, wenn sie profitabel sind. |
UseTimeRestriction |
Beschränkt den Handel auf das konfigurierte Wochentags-/Stundenfenster. |
DayEnd |
Letzter Tag der Woche, an dem neue Einträge zulässig sind. |
HourEnd |
Letzte Stunde am letzten Handelstag für Neuzugänge. |
BreakEvenTrigger |
Gewinn in Pips, der zur Aktivierung des Break-Even-Stopps erforderlich ist. |
TrailingStopTrigger |
Der Gewinn in Pips ist erforderlich, um mit dem Trailing zu beginnen. |
TrailingStopPips |
Vom Trailing Stop aufrechterhaltener Abstand in Pips. |
TakeProfitPips |
Feste Take-Profit-Distanz in Pips. |
StopLossPips |
Stop-Loss-Distanz in Pips korrigiert. |
CandleType |
Primärer Zeitrahmen, der die Strategie speist. |
Unterschiede zum Original EA
- Der durchschnittliche Bereich wird mit einem 250-Perioden-Durchschnitt der wahren Reichweite multipliziert mit Länge geschätzt und liefert ein Verhalten, das dem in MQL verwendeten gleitenden Hoch-/Tief-Bereich entspricht.
- Die Momentum-Bestätigung verwendet den tatsächlichen Indikatorwert (das Skript MQL wird mit dem Pip-Multiplikator
bm verglichen, wodurch der Filter effektiv deaktiviert wird).
- Die verzögerungsfreie Glättung wird mit denselben rekursiven Koeffizienten implementiert, jedoch in Dezimalarithmetik ausgedrückt. Der RSI-Modus verwendet einen Wilder RSI, dessen Periode FilterStrengthRsi entspricht.
Nutzungshinweise
- Wählen Sie das Instrument aus und binden Sie den Parameter
CandleType an den gewünschten Zeitrahmen.
- Konfigurieren Sie die Risiko- und Sitzungseinstellungen passend zu Ihrer Broker-Umgebung.
- Aktivieren Sie UseCycleFilter oder UseMomentumFilter, wenn eine strengere Bestätigung erforderlich ist; Deaktivieren Sie sie für schnellere, aber lautere Eingaben.
- Die Strategie behält höchstens eine offene Position bei. Gegenläufige Taktsignale schließen die aktuelle Position, bevor eine neue ausgewertet wird.
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()