Diese Strategie ist eine direkte Portierung des Expertenberaters „MetaTrader 4 „Channels“, der in der öffentlichen Bibliothek von Gordago enthalten ist. Es kombiniert einen sehr schnellen exponentiellen gleitenden Durchschnitt (EMA) mit drei EMA-basierten Hüllkurven, um Momente zu erkennen, in denen der Preis aus komprimierten Zonen austritt. Sobald eine einzelne Position offen ist, stützt sich die Strategie auf Stop-Orders und optionale Trailing-Stops, um Exits zu verwalten, genau wie die ursprüngliche MQL-Implementierung.
Handelslogik
Die Strategie abonniert standardmäßig stündliche Kerzen und berechnet:
Ein schneller EMA (Länge 2) mit Schlusspreisen der Kerze.
Ein zweites schnelles EMA (Länge 2) mit Eröffnungspreisen der Kerze, erforderlich durch die Short-Einstiegsregeln des Fachberaters.
Ein langsamer EMA (Länge 220) bei Schlusskursen, der als Basis für drei Umschlagabweichungen dient: ±1,0 %, ±0,7 % und ±0,3 %.
Eine Long-Position wird eröffnet, wenn der schlussbasierte Fast-EMA eine der sechs historischen Gegenprüfungen erfüllt:
Es verläuft nach oben durch die äußere, um 1 % niedrigere Hülle.
Es kreuzt nach oben durch die untere Hüllkurve von 0,7 %.
Er verbringt zwei aufeinanderfolgende Balken unterhalb der 0,3 %-Untergrenze (überverkaufter Zustand).
Es kreuzt nach oben durch das langsame EMA selbst.
Es kreuzt nach oben durch die obere 0,3 %-Grenze.
Es durchquert die obere Grenze von 0,7 % nach oben.
Eine Short-Position wird eröffnet, wenn der auf Eröffnung basierende schnelle EMA eine der symmetrischen Short-Regeln auslöst:
Es verläuft nach unten durch die äußere 1 %-Oberhülle.
Es kreuzt nach unten durch die obere Hüllkurve von 0,7 %.
Es durchquert die obere 0,3 %-Grenze nach unten.
Es kreuzt nach unten durch den langsamen EMA.
Es kreuzt nach unten durch die 0,3 % untere Hüllkurve.
Es kreuzt nach unten durch die 0,7 % untere Hüllkurve.
Es kann immer nur eine Marktposition existieren. Ein neues Signal wird ignoriert, während ein Handel aktiv ist, was dem Verhalten des MetaTrader-Experten entspricht.
Risikomanagement
Für Long- und Short-Trades können individuelle Stop-Loss- und Take-Profit-Distanzen konfiguriert werden. Wenn dieser Wert auf Null gesetzt ist, werden diese Schutzbefehle übersprungen, wodurch der standardmäßig deaktivierte Status der ursprünglichen Quelle repliziert wird.
Optionale Trailing-Stops verschärfen die Schutzorder, sobald sich der Preis um mehr als die in Punkten gemessene Trailing-Distanz zugunsten der Position bewegt.
Alle Schutzaufträge werden automatisch gelöscht, wenn die Position abgeflacht wird oder die Strategie stoppt.
Parameter
Name
Beschreibung
Candle Type
Für die Preisanalyse verwendeter Zeitrahmen (Standard: 1 Stunde).
Volume
Für alle Einträge verwendete Bestellgröße.
Fast EMA / Slow EMA
Zeiträume für die schnellen und langsamen EMAs.
Envelope 1%, Envelope 0.7%, Envelope 0.3%
Prozentuale Breite der drei Umschlagbänder.
Buy Stop-Loss, Sell Stop-Loss
Abstand in Punkten zwischen dem Einstiegspreis und dem anfänglichen Stop-Loss für Long- oder Short-Trades.
Buy Take-Profit, Sell Take-Profit
Distanz in Punkten für die optionalen festen Take-Profit-Levels.
Buy Trailing, Sell Trailing
Trailing-Stop-Distanz in Punkten für Long- oder Short-Positionen.
Use Trading Hours
Aktiviert den Zeitfensterfilter.
From Hour, To Hour
Inklusive Stundengrenzen für die Eröffnung neuer Positionen. Das Fenster wird um Mitternacht umgebrochen, wenn From größer als To ist.
Nutzungshinweise
Da die Stoppentfernungen in Punkten definiert sind, werden sie intern mit der Sicherheit PriceStep multipliziert. Stellen Sie sicher, dass dieser Schritt mit dem für den Handel verwendeten Instrument übereinstimmt.
Die schnelle EMA-Länge ist absichtlich sehr kurz, um den MT4-Experten widerzuspiegeln. Durch Erhöhen ändert sich die Signalfrequenz dramatisch.
Der ursprüngliche Berater erlaubte auch das Whitelisting von Konten und akustische Warnungen. Diese wurden weggelassen, da sie plattformspezifisch sind und keinen Einfluss auf die Bestelllogik haben.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Channels strategy - EMA envelope breakout.
/// Buys when price breaks above fast EMA + offset, sells when it breaks below.
/// Uses slow EMA as trend filter.
/// </summary>
public class ChannelsStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ChannelsStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
var close = candle.ClosePrice;
// Buy: price crosses above fast EMA, fast EMA above slow EMA (uptrend)
if (close > fast && fast > slow && _prevFast <= _prevSlow && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Sell: price crosses below fast EMA, fast EMA below slow EMA (downtrend)
else if (close < fast && fast < slow && _prevFast >= _prevSlow && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
// Exit long when fast crosses below slow
else if (Position > 0 && fast < slow && _prevFast >= _prevSlow)
{
SellMarket();
}
// Exit short when fast crosses above slow
else if (Position < 0 && fast > slow && _prevFast <= _prevSlow)
{
BuyMarket();
}
_prevFast = fast;
_prevSlow = slow;
}
}
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 channels_strategy(Strategy):
def __init__(self):
super(channels_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(channels_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(channels_strategy, self).OnStarted2(time)
self._has_prev = False
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.process_candle).Start()
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast)
slow_val = float(slow)
if not self._has_prev:
self._prev_fast = fast_val
self._prev_slow = slow_val
self._has_prev = True
return
close = float(candle.ClosePrice)
if close > fast_val and fast_val > slow_val and self._prev_fast <= self._prev_slow and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close < fast_val and fast_val < slow_val and self._prev_fast >= self._prev_slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif self.Position > 0 and fast_val < slow_val and self._prev_fast >= self._prev_slow:
self.SellMarket()
elif self.Position < 0 and fast_val > slow_val and self._prev_fast <= self._prev_slow:
self.BuyMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return channels_strategy()