Auf GitHub ansehen

Backbone-Basket-Strategie (StockSharp)

Überblick

Die Backbone Basket-Strategie portiert den ursprünglichen Expertenberater MetaTrader 4 „Backbone.mq4“ auf die hohe Ebene StockSharp API. Das System sammelt Geld-/Brief-Extremwerte, um die anfängliche Handelsrichtung zu bestimmen, und wechselt dann zwischen Long- und Short-Körben. Jeder Korb wird schrittweise aufgebaut, wobei pro abgeschlossener Kerze eine Marktorder hinzugefügt wird, bis entweder die konfigurierte MaxTrades-Anzahl erreicht ist oder Schutzaufträge die Position schließen. Die Risikokontrolle wird durch ein fraktioniertes Risikomodell gewährleistet, das das Handelsvolumen anhand des Kontowerts und der Stop-Loss-Distanz skaliert.

Marktdatenfluss

  • Kerzen (CandleType) – abgeschlossene Kerzen beschleunigen die Entscheidungsfindung; Pro fertigem Barren kann nur eine Order erteilt werden, genau wie im MT4-Skript.
  • Orderbuch-Snapshots – die besten Geld- und Briefwerte werden verfolgt, um Trailing-Stop-Berechnungen und die anfängliche „extreme“ Erkennungslogik zu reproduzieren.
  • Strategiestatus – die Basis StockSharp Strategy behält die laufende Position, den durchschnittlichen Einstiegspreis und den PnL bei, der zur Verwaltung von Schutzaufträgen verwendet wird.

Handelslogik

  1. Anfangskalibrierung – obwohl keine Richtung definiert ist, zeichnet die Strategie den höchsten gesehenen Geld- und den niedrigsten Briefkurs auf. Wenn der Preis von diesen Extremwerten um TrailingStopPoints * PriceStep zurückgeht, wird die erste Korbrichtung gewählt.
  2. Auftragsreihenfolge
    • Wenn der letzte abgeschlossene Trade Short war (_lastPositionDirection == -1) und es keine offenen Trades gibt, wird ein neuer Kaufmarkt-Auftrag übermittelt.
    • Wenn der vorherige Trade long war (_lastPositionDirection == 1) und der Korb noch Kapazität hat, werden bei nachfolgenden Kerzen zusätzliche Kaufaufträge übermittelt.
    • Für Verkaufsaufträge gelten symmetrische Regeln, wenn der letzte Trade lang war.
  3. Volumengröße – jede neue Bestellung ruft das MT4-inspirierte Vol()-Analogon auf. Der verfügbare Kontowert (aktueller Wert → Saldo → Startsaldo) wird mit MaxRisk multipliziert und durch die mit PriceStepCost in Geld umgerechnete Stop-Loss-Distanz dividiert. Das Ergebnis wird mit VolumeStep abgeglichen, durch MinVolume/MaxVolume begrenzt und abgelehnt, wenn es unter die Mindesthandelsgröße fällt.
  4. Schutzaufträge – Sobald ein Trade ausgeführt wird, platziert die Strategie einen einzelnen Stop-Loss- und Take-Profit-Auftrag, der den gesamten Korb abdeckt. Entfernungen werden wie bei der MQL-Version in „Punkten“ (Preisschritten) ausgedrückt.
  5. Trailing Stop – wenn sowohl StopLossPoints als auch TrailingStopPoints positiv sind, wird die Stop-Order erneut ausgegeben, um Gewinne zu sichern, wann immer sich der Preis um mehr als die Trailing-Distanz über den aufgezeichneten Einstiegspreis hinaus bewegt. Lange Körbe verwenden das beste Gebot als Referenz; Kurze Körbe verwenden die beste Frage.
  6. Korbabschluss – wenn entweder die Stop-Loss- oder Take-Profit-Order ausgeführt wird, werden alle internen Zähler zurückgesetzt, sodass LastPosition unverändert bleibt, sodass die nächste Kerze einen Korb in die entgegengesetzte Richtung startet, was das ursprüngliche EA-Verhalten widerspiegelt.

Money-Management

  • Verwendet die gleiche Bruchformel 1 / (MaxTrades / MaxRisk - openTrades) wie der MQL-Experte.
  • Das Risikokapital wird ab Portfolio.CurrentValue geschätzt und fällt auf CurrentBalance oder BeginBalance zurück.
  • Das Volumen wird verworfen, wenn die berechnete Größe nach der Ausrichtung an VolumeStep unter dem MinVolume des Instruments liegt.
  • Stop-Loss- und Take-Profit-Orders werden bei jeder Volumenänderung neu erstellt, sodass der Schutz immer den gesamten Korb abdeckt.

Parameter

Name Standard Beschreibung
CandleType 15-minütiger Zeitrahmen Kerzenintervall, das zum Auslösen neuer Entscheidungen verwendet wird.
MaxRisk 0,5 Bruchteil des Portfolios, der bei der Größenbestimmung der nächsten Order berücksichtigt wird. Muss positiv sein.
MaxTrades 10 Maximale Anzahl an Trades, die im aktuellen Korb gesammelt werden können.
TakeProfitPoints 170 Take-Profit-Distanz gemessen in Preisschritten. Zum Deaktivieren auf 0 setzen.
StopLossPoints 40 Stop-Loss-Distanz gemessen in Preisschritten. Erforderlich für Trailing- und Positionsgrößenbestimmung.
TrailingStopPoints 300 Trailing-Stop-Distanz in Preisschritten. Auf 0 setzen, um einen statischen Stopp beizubehalten.

Konvertierungshinweise

  • Das Original EA ändert jede Bestellung einzeln; Die StockSharp-Version verwaltet einen aggregierten Stop-Loss und Take-Profit pro Korb, da StockSharp-Positionen standardmäßig saldiert werden.
  • Die Volume-Größe hängt von Security.PriceStepCost ab. Wenn der Connector diesen Wert nicht bereitstellt, greift die Strategie auf die konfigurierte Eigenschaft Volume zurück.
  • Nachlaufende Aktualisierungen werden angewendet, wenn eine neue Kerze eintrifft, was dem „Einmal pro Balken“-Verhalten des MT4-Skripts entspricht (das nur bei Bars > PrevBars funktionierte).
  • Die alternierende Logik behält die zuletzt ausgeführte Richtung in _lastPositionDirection bei, sodass sobald ein Korb geschlossen wird, die nächste Kerze automatisch einen Korb in die entgegengesetzte Richtung öffnet, genau wie der Quellcode.
  • Es wird nur die C#-Implementierung bereitgestellt. In diesem Verzeichnis gibt es keinen Python-Port.

Nutzungstipps

  • Weisen Sie Instrumente mit genauen PriceStep-, PriceStepCost- und Volumenmetadaten zu, um realistische Positionsgrößen zu erhalten.
  • Stellen Sie beim Backtesting sicher, dass der Orderbuch-Feed verfügbar ist, damit die Trailing-Stop-Logik auf die besten Geld-/Briefwerte zugreifen kann.
  • Um die aggressive Skalierung zu deaktivieren, erhöhen Sie MaxTrades oder verringern Sie MaxRisk, sodass die Ersetzung durch Vol() kleinere Volumina zurückgibt.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Backbone strategy that alternates between long and short based on retracements from recent extremes.
/// Uses ATR to detect when price has pulled back enough from its high/low to enter a new position.
/// </summary>
public class BackboneBasketStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _retraceMultiplier;

	private decimal _highestPrice;
	private decimal _lowestPrice;
	private decimal _entryPrice;
	private int _lastDirection; // 1 = long, -1 = short, 0 = none

	public BackboneBasketStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe used for analysis.", "General");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetDisplay("ATR Period", "Period for ATR indicator.", "Indicators");

		_retraceMultiplier = Param(nameof(RetraceMultiplier), 2m)
			.SetDisplay("Retrace Multiplier", "ATR multiplier for retracement threshold.", "Signals");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	public decimal RetraceMultiplier
	{
		get => _retraceMultiplier.Value;
		set => _retraceMultiplier.Value = value;
	}

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

		_highestPrice = 0;
		_lowestPrice = decimal.MaxValue;
		_entryPrice = 0;
		_lastDirection = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_highestPrice = 0;
		_lowestPrice = decimal.MaxValue;
		_entryPrice = 0;
		_lastDirection = 0;

		var atr = new AverageTrueRange { Length = AtrPeriod };

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

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

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

		if (atrValue <= 0)
			return;

		var close = candle.ClosePrice;

		// Track extremes
		if (close > _highestPrice)
			_highestPrice = close;
		if (close < _lowestPrice)
			_lowestPrice = close;

		var threshold = atrValue * RetraceMultiplier;

		// Exit existing positions on retracement
		if (Position > 0 && close < _highestPrice - threshold)
		{
			SellMarket();
			_entryPrice = 0;
			_lastDirection = 1;
			_lowestPrice = close;
		}
		else if (Position < 0 && close > _lowestPrice + threshold)
		{
			BuyMarket();
			_entryPrice = 0;
			_lastDirection = -1;
			_highestPrice = close;
		}

		// Entry logic - alternate direction
		if (Position == 0)
		{
			if (_lastDirection != 1 && close < _highestPrice - threshold)
			{
				// Price pulled back from high - sell
				SellMarket();
				_entryPrice = close;
				_lastDirection = -1;
				_lowestPrice = close;
			}
			else if (_lastDirection != -1 && close > _lowestPrice + threshold)
			{
				// Price bounced from low - buy
				BuyMarket();
				_entryPrice = close;
				_lastDirection = 1;
				_highestPrice = close;
			}
		}
	}
}