Diese Strategie repliziert die Kernideen des MetaTrader 4-Expertenberaters "Gann Line" (Quell-ID 24877) unter Verwendung der High-Level-API von StockSharp. Sie behält dieselben Trend-, Momentum- und langfristigen MACD-Filter bei, während alle Geldmanagement-Tools in Preisschritten ausgedrückt werden, was die Logik broker-unabhängig macht.
Handelslogik
Trendfilter (primärer Zeitrahmen)
Zwei linear gewichtete gleitende Durchschnitte (LWMA) werden auf den typischen Kerzenpreis (high + low + close) / 3 angewendet.
Ein Long-Bias erfordert, dass die schnelle LWMA über der langsamen LWMA schließt; ein Short-Bias erfordert das Gegenteil.
Momentum-Bestätigung (höherer Zeitrahmen)
Ein auf einem konfigurierbaren höheren Zeitrahmen berechneter Momentum-Oszillator prüft, wie weit der Oszillator vom Gleichgewichtsniveau (100) abweicht.
Mindestens einer der letzten drei abgeschlossenen Momentum-Werte muss den konfigurierten Abweichungsschwellenwert überschreiten, bevor ein Trade erlaubt ist.
Langsamer MACD-Filter (sehr hoher Zeitrahmen)
Ein auf einem langsamen Zeitrahmen (standardmäßig monatlich) berechneter MACD-Filter muss die Richtung bestätigen: MACD-Hauptlinie über Signal für Longs, unter für Shorts.
Positionsmanagement
Feste Stop-Loss- und Take-Profit-Ziele werden von Preisschritten in absolute Preise umgerechnet, wenn ein Trade eröffnet wird.
Optionale Break-even-Logik verschiebt den Stop zum Einstiegspreis plus einem Offset, sobald der Trade eine bestimmte Anzahl von Schritten in Gewinn geht.
Optionale Trailing-Logik verschiebt den Stop hinter das höchste Hoch (für Longs) oder niedrigste Tief (für Shorts), sobald der Preis eine konfigurierbare Anzahl von Schritten zurückgelegt hat.
Risikomanagement
Alle Abstände (Stop-Loss, Take-Profit, Break-even und Trailing) werden in Preis-Schritten eingegeben. Der Helper konvertiert sie in Preise mit dem Instrument-PriceStep.
Die Strategie arbeitet mit der Basis-Volume-Eigenschaft. Wenn sie null ist, wird standardmäßig ein Kontrakt/Lot verwendet.
Nur eine einzige Nettoposition wird verwaltet. Entgegengesetzte Signale schließen den aktuellen Trade, bevor ein neuer geöffnet wird.
Unterschiede zur MQL4-Version
Der ursprüngliche Expertenberater verwendete eine manuell gezeichnete Gann-Trendlinie. StockSharp exponiert keine Diagrammobjekte, daher ersetzt der Port diese Prüfung durch die LWMA-Steigungsbestätigung.
Geldbasiertes Trailing, Teilschließungen und kontoweite Eigenkapitalprüfungen aus dem Skript werden in deterministische schrittbasierte Berechnungen vereinfacht.
Benachrichtigungen (Alerts, E-Mails, mobile Pushes) werden nicht generiert, da StockSharp-Strategien typischerweise auf der Plattformausgabe protokollieren.
Parameter
Name
Beschreibung
Fast LWMA
Länge der schnellen LWMA für den Trendfilter.
Slow LWMA
Länge der langsamen LWMA für den Trendfilter.
Momentum Period
Rückblick des Momentum-Oszillators auf dem sekundären Zeitrahmen.
Momentum Threshold
Minimale Abweichung von 100 für einen der letzten drei Momentum-Werte.
MACD Fast / Slow / Signal
EMA-Längen des langsamen MACD-Filters.
Take Profit (steps)
Take-Profit-Abstand in Preisschritten.
Stop Loss (steps)
Stop-Loss-Abstand in Preisschritten.
Use Trailing, Trail Activation, Trail Distance
Trailing aktivieren, Gewinn vor Trailing-Start, Abstand zwischen Preisextrem und Trailing-Stop.
Use BreakEven, BreakEven Activation, BreakEven Offset
Break-even aktivieren, Gewinn vor Stop-Verschiebung, danach gesicherter Zusatzgewinn.
Primary Timeframe
Kerzentyp für den LWMA-Crossover.
Momentum Timeframe
Kerzentyp für den Momentum-Oszillator.
MACD Timeframe
Kerzentyp für den langsamen MACD-Filter.
Verwendungstipps
Wählen Sie ein Instrument und stellen Sie den gewünschten Primary Timeframe ein. Die anderen Zeitrahmen haben standardmäßig 1 Stunde (Momentum) und 30 Tage (MACD), können aber angepasst werden.
Konfigurieren Sie Volume und die schrittbasierten Risikoparameter entsprechend Ihrer Broker-Kontraktspezifikationen.
Führen Sie die Strategie in Designer oder über Code aus. Überwachen Sie das Protokoll, um zu verifizieren, dass Filter, Break-even-Bewegungen und Trailing-Anpassungen wie erwartet erscheinen.
Optimieren Sie Momentum- und MACD-Schwellenwerte, um die portierte Logik an verschiedene Märkte oder Zeitrahmen anzupassen.
Weitere Verbesserungen
Einen eigenkapitalbasierten globalen Stop ähnlich dem Originalskript integrieren.
Den LWMA-Steigungsfilter durch eine benutzerdefinierte chartgezeichnete Trendlinie ersetzen, sobald StockSharp Objektereignisse exponiert.
Partielle Gewinnmitnahmen hinzufügen, um das Multi-Ziel-Verhalten der MQL4-Version nachzuahmen.
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 GannLineStrategy : 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 GannLineStrategy()
{
_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 gann_line_strategy(Strategy):
def __init__(self):
super(gann_line_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(gann_line_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(gann_line_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 gann_line_strategy()