Die Cycle-Lines-Strategie ist die StockSharp-Portierung des MetaTrader Expert Advisors "Cycle Lines". Das ursprüngliche Skript kombinierte Chart-Zeichnungen mit manuellen Trade-Schaltflächen. Diese Portierung konzentriert sich auf die automatisierte Handelslogik, die diese Bedienelemente begleitete. Die Strategie handelt Kreuzungen der MACD-Linie, hält das Risiko über absolute Stop-Loss- und Take-Profit-Grenzen eng kontrolliert und unterstützt Break-even- sowie Trailing-Stop-Management.
Wenn die MACD-Linie ihre Signallinie nach oben kreuzt, eröffnet die Strategie eine Long-Position. Wenn die Linie die Signallinie nach unten kreuzt, eröffnet sie eine Short-Position. Offene Trades werden geschlossen, wenn der Indikator in die Gegenrichtung wechselt oder eine der Schutzregeln (Stop-Loss, Take-Profit, Break-even oder Trailing Stop) ausgelöst wird.
Handelsregeln
Einstiegsbedingungen
Long: MACD kreuzt auf der ausgewählten Kerzenserie über die Signallinie.
Short: MACD kreuzt auf der ausgewählten Kerzenserie unter die Signallinie.
Einstiege werden erst bewertet, nachdem der Indikator vollständig ausgebildet ist und die Strategie verbunden sowie zum Handel zugelassen ist.
Ausstiegsbedingungen
Entgegengesetzte MACD-Kreuzung.
Stop-Loss erreicht.
Take-Profit erreicht.
Break-even-Schutzlevel berührt.
Trailing-Stop-Level berührt.
Parameter
Name
Beschreibung
Standard
Hinweise
Volume
Ordervolumen pro Trade.
1
Muss positiv sein.
MacdFastPeriod
Periode der schnellen EMA innerhalb der MACD-Berechnung.
12
Optimierbar.
MacdSlowPeriod
Periode der langsamen EMA innerhalb von MACD.
26
Optimierbar.
MacdSignalPeriod
Periode der MACD-Signallinie.
9
Optimierbar.
StopLoss
Absolute Preisdistanz für den Schutz-Stop.
0
Deaktiviert, wenn auf 0 gesetzt.
TakeProfit
Absolute Preisdistanz für das Take-Profit-Ziel.
0
Deaktiviert, wenn auf 0 gesetzt.
TrailingOffset
Abstand zwischen dem besten günstigen Preis und dem Trailing Stop.
0
Deaktiviert, wenn auf 0 gesetzt.
BreakEvenTrigger
Gewinndistanz, die vor dem Verschieben des Stops auf Break-even erforderlich ist.
0
Deaktiviert, wenn auf 0 gesetzt.
BreakEvenOffset
Zusätzlicher Offset, der auf das Break-even-Niveau angewendet wird.
0
Ermöglicht, etwas zusätzlichen Gewinn ober-/unterhalb des Einstiegs zu sichern.
CandleType
Für Indikatorberechnungen verwendete Kerzenserie.
Kerzen im Zeitrahmen von 15 Minuten
Akzeptiert jeden von StockSharp unterstützten DataType.
Positionsverwaltung
Stop-Loss / Take-Profit: Beide Prüfungen bewerten Intrabar-Extreme (Hoch/Tief) abgeschlossener Kerzen und stellen sicher, dass der Ausstieg die konfigurierte absolute Distanz zum Einstiegspreis einhält.
Break-even: Sobald sich der Preis um mindestens BreakEvenTrigger zugunsten des Trades bewegt, aktiviert die Strategie einen Stop bei entry ± BreakEvenOffset. Jede Gegenbewegung, die dieses Niveau berührt, schließt die Position.
Trailing Stop: Bei Long-Trades wird der höchste erreichte Preis überwacht. Das Stop-Niveau folgt dem Hoch abzüglich TrailingOffset. Bei Short-Trades spiegelt die Logik dieses Verhalten um den niedrigsten Preis.
Nutzungshinweise
Die Strategie handelt jeweils nur eine einzelne Position. Aufeinanderfolgende Signale pyramidisieren eine bestehende Position nicht.
Parameter werden als StrategyParam<T>-Objekte bereitgestellt und unterstützen daher die Optimierung innerhalb von StockSharp.
Um das Standardverhalten des ursprünglichen EA nachzubilden, setzen Sie die MACD-Perioden auf 12 / 26 / 9 und konfigurieren die Risikoparameter entsprechend den gewünschten Pip-Werten.
Unterschiede zur MQL-Version
Chart-Zeichnungsfunktionen und manuelle BUY/SELL-Schaltflächen wurden weggelassen, weil StockSharp Visualisierung anders handhabt.
Alle Schutzregeln wurden so umgeschrieben, dass sie auf Kerzendaten statt auf MetaTrader-Tickereignissen arbeiten; dadurch bleiben sie mit der High-Level-API von StockSharp kompatibel.
Trailing- und Break-even-Logik sind für Long- und Short-Trades symmetrisch, um Klarheit und Robustheit zu erhöhen.
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 CycleLinesStrategy : 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 CycleLinesStrategy()
{
_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 cycle_lines_strategy(Strategy):
def __init__(self):
super(cycle_lines_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(cycle_lines_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(cycle_lines_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 cycle_lines_strategy()