Die Trade-Channel-Strategie repliziert den ursprünglichen MetaTrader-Expertenberater, der Preiskanäle mit ATR-basierten Stopps handelte. Es wartet darauf, dass die Kanalgrenzen unverändert bleiben und dass die letzte Kerze diese Niveaus berührt oder ablehnt. Wenn das Setup angezeigt wird, eröffnet die Strategie eine Position in der entgegengesetzten Richtung der Berührung und wendet einen adaptiven Trailing Stop an, der in Punkten gemessen wird.
Der Ansatz zielt darauf ab, die Mean-Reversion um einen stabilen Preiskanal herum auszunutzen. Es filtert Signale, sodass der Kanal flach sein muss (keine neuen Hochs oder Tiefs), bevor er eintritt. Schutzstopps werden jenseits des Kanals mithilfe der Average True Range platziert, und ein optionaler Trailing Stop sichert Gewinne, sobald sich die Bewegung entwickelt.
Einzelheiten
Eintrittskriterien:
Short: Das Kanalhoch entspricht dem vorherigen Kanalhoch und die letzte Kerze durchbricht entweder dieses Hoch oder schließt zwischen dem Hoch und dem Pivot (high + low + close) / 3.
Long: Das Tief des Kanals entspricht dem Tief des vorherigen Kanals und die letzte Kerze durchbricht entweder dieses Tief oder schließt zwischen dem Tief und dem Pivot.
Long/Short: Beide Richtungen, aber jeweils nur eine Position.
Ausstiegskriterien:
Long: Der Preis berührt das Kanalhoch, während das Hoch unverändert blieb.
Kurz: Der Preis berührt das Kanaltief, während das Tief unverändert blieb.
Der optionale Trailing Stop verschärft sich hinter dem Markt, sobald der Gewinn TrailingDistance Punkte übersteigt.
Stops: Anfänglicher Stop-Loss bei channel boundary ± ATR. Trailing Stop ersetzt ihn, wenn er aktiviert wird.
Standardwerte:
Volume = 0,1 m
ChannelPeriod = 20
AtrPeriod = 4
TrailingDistance = 30
CandleType = 30-Minuten-Kerzen
Filter:
Kategorie: Mean Reversion
Richtung: Beide
Indikatoren: Höchster, niedrigster, durchschnittlicher wahrer Bereich
Stopps: ATR Stopp, Trailing
Komplexität: Mittelschwer
Zeitrahmen: Intraday (30 Minuten)
Saisonalität: Nein
Neuronale Netze: Nein
Divergenz: Nein
Risikostufe: Mittel
Notizen
Volume steuert die Bestellgröße; Es kann jeweils nur eine Position existieren.
TrailingDistance wird in Punkten (Preisschritten) angegeben. Auf Null setzen, um den Trailing Stop zu deaktivieren.
Die Strategie erfordert historische Kerzen, um die Höchst-/Tiefst- und ATR-Indikatoren vor dem Handel aufzuwärmen.
Stop-Orders werden automatisch gelöscht, wenn die Position geschlossen oder die Strategie zurückgesetzt wird.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class TradeChannelAtrStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevMid;
private bool _hasPrev;
private int _cooldownRemaining;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TradeChannelAtrStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 48).SetDisplay("Channel Period", "Channel lookback", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14).SetDisplay("ATR Period", "ATR lookback", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 150).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevMid = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevMid = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(highest, lowest, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var mid = (highest + lowest) / 2;
if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
_prevMid = mid;
return;
}
if (_prevClose <= _prevMid && close > mid && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevClose >= _prevMid && close < mid && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevClose = close;
_prevMid = mid;
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class trade_channel_atr_strategy(Strategy):
def __init__(self):
super(trade_channel_atr_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 48) \
.SetDisplay("Channel Period", "Channel lookback", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 150) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def channel_period(self):
return self._channel_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(trade_channel_atr_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(trade_channel_atr_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
highest = Highest()
highest.Length = self.channel_period
lowest = Lowest()
lowest.Length = self.channel_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.process_candle).Start()
def process_candle(self, candle, highest, lowest):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
mid = (float(highest) + float(lowest)) / 2.0
if not self._has_prev:
self._prev_close = close
self._prev_mid = mid
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_mid = mid
return
if self._prev_close <= self._prev_mid and close > mid and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_close >= self._prev_mid and close < mid and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_close = close
self._prev_mid = mid
def CreateClone(self):
return trade_channel_atr_strategy()