Auf GitHub ansehen

Twenty-Pips-Preiskanalstrategie

Überblick

Die Twenty-Pips-Preiskanalstrategie ist eine Umsetzung des ursprünglichen MetaTrader-Expertenberaters 20 Pips, die einen Preiskanal im Donchian-Stil mit kurzfristigen gleitenden Durchschnittsfiltern kombiniert. Der Algorithmus öffnet Trades nur, wenn die aktuelle Kerze entgegengesetzt zur vorherigen öffnet, filtert die Richtung mit gleitenden Durchschnitten, die auf typischen Preisen berechnet werden, und verwaltet Ausstiege über ein festes Zwanzig-Pip-Ziel, das durch einen dynamischen kanalbasierten Trailing Stop unterstützt wird.

Die StockSharp-Version behält den Geist des ursprünglichen Ansatzes bei und passt die Auftragsverwaltung gleichzeitig an die übergeordnete API-Version an. Marktaufträge werden für Ein- und Ausstiege verwendet, Gewinnziele werden intern überwacht und Stop-Levels werden mit Preiskanalbedingungen nachgebildet.

Handelslogik

  1. Indikatorstapel

    • Ein einfacher gleitender Durchschnitt einer Periode des typischen Preises (H+L+C)/3 dient als schnelle Basislinie, die den typischen Preis der vorherigen Kerze widerspiegelt.
    • Ein konfigurierbarer langsamer einfacher gleitender Durchschnitt (Standard 20), der auf Schlusskursen berechnet wird, spielt die Rolle des MA_Low-Filters aus dem EA.
    • Höchste und niedrigste Indikatoren mit demselben Zeitraum wie der Preiskanal (Standard 20) emulieren die ursprünglichen benutzerdefinierten Indikatorpuffer.
  2. Eintrittsbedingungen

    • Long-Setup: Der vorherige schnelle typische Preis liegt über dem vorherigen langsamen gleitenden Durchschnitt und die aktuelle Kerze öffnet sich unter der vorherigen Eröffnung. Nach einem Verlustgeschäft wird das Volumen mit dem Erholungsfaktor multipliziert (Standard 2). Der Einstiegspreis wird erfasst, um Gewinn und Verlust zu verfolgen.
    • Short-Setup: Der vorherige schnelle typische Preis liegt unter dem vorherigen langsamen gleitenden Durchschnitt und die aktuelle Kerze öffnet sich über der vorherigen Eröffnung. Die Volumenskalierung folgt der gleichen Erholungslogik wie bei Long-Trades.
  3. Exit-Management

    • Bei der Eröffnung der Position wird ein festes Take-Profit-Ziel in Höhe von TakeProfitPips multipliziert mit der Preisstufe des Instruments festgelegt.
    • Ein kanalgesteuerter Trailing Stop ahmt den ursprünglichen OrderModify-Aufruf nach. Wenn der vorherige Balken den Preiskanal durchbricht (Verschiebung um zwei Balken aus der MT4-Logik), wird der Schutzstopp zum vorherigen Extrem minus/plus dem nachlaufenden Offset in Pips verschoben. Wenn die nächste Kerze über dieses Extrem hinausgeht, wird die Position sofort zum Eröffnungspreis geschlossen.
    • Take-Profit-, Trailing-Stop- und Gap-Exits werden alle über Marktaufträge ausgeführt, während der tatsächliche Exit-Preis verfolgt wird, um die Gewinn-/Verlust-Flagge für die Martingal-Skalierung zu aktualisieren.
  4. Martingale Wiederherstellung

    • Nach jeder geschlossenen Verlustposition wird die nächste Einstiegsgröße mit RecoveryMultiplier multipliziert. Bei gewinnbringenden Geschäften wird die Flagge zurückgesetzt und auf das Basisvolumen zurückgesetzt.

Parameter

Name Beschreibung Standard
CandleType Primärer Zeitrahmen, der für Berechnungen verwendet wird. 1 Stunde Kerzen
ChannelPeriod Lookback-Zeitraum für den Kanal im Donchian-Stil. 20
SlowMaPeriod Länge des langsam gleitenden Durchschnittsfilters. 20
TakeProfitPips Abstand in Pips für das festgelegte Gewinnziel. 20
TrailingOffsetPips Offset, der beim Anziehen des Anschlags auf das vorherige Extrem verwendet wird. 10
RecoveryMultiplier Nach einem Verlust angewendeter Volumenmultiplikator. 2
Volume Basishandelsvolumen vor Erholungsskalierung. 0,1

Nutzungshinweise

  • Die Strategie geht davon aus, dass Security.PriceStep den Pip-Wert des gehandelten Instruments widerspiegelt. Passen Sie TakeProfitPips und TrailingOffsetPips an, wenn das Symbol eine andere Pip-Definition verwendet.
  • Da StockSharp Marktaufträge für Ausstiege verwendet, können Backtests im Vergleich zu den ursprünglichen MT4-Stop- und Limit-Aufträgen einen Slippage aufweisen. Die Logik reproduziert immer noch die gleichen Preisschwellen.
  • Die Kanalwerte werden verschoben, um die iCustom(..., shift=2)-Aufrufe zu emulieren; Beachten Sie dies, wenn Sie das Nachlaufverhalten ändern.
  • Der Wiederherstellungsmultiplikator kann auf 1 gesetzt werden, um die Martingal-Skalierung zu deaktivieren.
using System;
using System.Linq;
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;

/// <summary>
/// Converted "20 pips" price channel strategy.
/// Uses Donchian channel breakouts with MA filter, trailing stop, and recovery multiplier.
/// </summary>
public class TwentyPipsPriceChannelStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;

	private readonly List<decimal> _highs = new();
	private readonly List<decimal> _lows = new();
	private decimal? _prevChannelUpper;
	private decimal? _prevChannelLower;

	/// <summary>
	/// Candle type to process.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Donchian channel lookback period.
	/// </summary>
	public int ChannelPeriod
	{
		get => _channelPeriod.Value;
		set => _channelPeriod.Value = value;
	}

	/// <summary>
	/// Slow moving average length.
	/// </summary>
	public int SlowMaPeriod
	{
		get => _slowMaPeriod.Value;
		set => _slowMaPeriod.Value = value;
	}

	/// <summary>
	/// Stop loss distance in absolute price units.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	/// <summary>
	/// Take profit distance in absolute price units.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="TwentyPipsPriceChannelStrategy"/>.
	/// </summary>
	public TwentyPipsPriceChannelStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Primary candle type", "General");

		_channelPeriod = Param(nameof(ChannelPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Channel Period", "Donchian channel lookback", "Parameters");

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA Period", "Slow moving average length", "Parameters");

		_stopLoss = Param(nameof(StopLoss), 500m)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop loss distance", "Risk");

		_takeProfit = Param(nameof(TakeProfit), 500m)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take profit distance", "Risk");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return new[] { (Security, CandleType) };
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_highs.Clear();
		_lows.Clear();
		_prevChannelUpper = null;
		_prevChannelLower = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		var slowMa = new SMA { Length = SlowMaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(slowMa, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, slowMa);
			DrawOwnTrades(area);
		}

		// Use StartProtection for SL/TP
		var tp = TakeProfit > 0 ? new Unit(TakeProfit, UnitTypes.Absolute) : null;
		var sl = StopLoss > 0 ? new Unit(StopLoss, UnitTypes.Absolute) : null;
		StartProtection(tp, sl);

		base.OnStarted2(time);
	}

	private void ProcessCandle(ICandleMessage candle, decimal slowMaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		// Track highs and lows for manual Donchian channel
		_highs.Add(candle.HighPrice);
		_lows.Add(candle.LowPrice);

		while (_highs.Count > ChannelPeriod)
			_highs.RemoveAt(0);
		while (_lows.Count > ChannelPeriod)
			_lows.RemoveAt(0);

		if (_highs.Count < ChannelPeriod)
		{
			_prevChannelUpper = null;
			_prevChannelLower = null;
			return;
		}

		var channelUpper = _highs.Max();
		var channelLower = _lows.Min();

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevChannelUpper = channelUpper;
			_prevChannelLower = channelLower;
			return;
		}

		// Channel breakout with MA filter
		if (_prevChannelUpper.HasValue && _prevChannelLower.HasValue)
		{
			// Breakout above the previous channel high -> buy signal
			if (candle.ClosePrice > _prevChannelUpper.Value && candle.ClosePrice > slowMaValue && Position <= 0)
			{
				if (Position < 0)
					BuyMarket(Math.Abs(Position));
				BuyMarket(Volume);
			}
			// Breakout below the previous channel low -> sell signal
			else if (candle.ClosePrice < _prevChannelLower.Value && candle.ClosePrice < slowMaValue && Position >= 0)
			{
				if (Position > 0)
					SellMarket(Position);
				SellMarket(Volume);
			}
		}

		_prevChannelUpper = channelUpper;
		_prevChannelLower = channelLower;
	}
}