Auf GitHub ansehen

Handelskanal ATR-Strategie

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;
	}
}