Auf GitHub ansehen

Bronze Warrioir-Strategie

Übersicht

  • Konvertierung des MetaTrader 5-Experten Bronze Warrioir.mq5 in die StockSharp High-Level-API.
  • Handelt ein einzelnes Symbol mit fertigen Kerzen und kombiniert CCI, Williams %R und einen proprietären „DayImpuls"-Oszillator.
  • Fokussiert auf das Erfassen von Momentum-Ausbrüchen, wenn DayImpuls-Anstieg, Williams-%R-Extreme und CCI-Werte übereinstimmen.

Indikator-Stack

  • Commodity Channel Index (CCI) – klassischer CCI mit dem konfigurierten IndicatorPeriod. Long-Signale erfordern einen Wert unter -CciLevel; Short-Signale benötigen einen Wert über CciLevel.
  • Williams %R – auf demselben Zeitraum angewendet. Ein Wert über WilliamsLevelUp bestätigt überkauftes Terrain, Werte unter WilliamsLevelDown bestätigen überverkaufte Niveaus.
  • DayImpuls Oszillator – Replik des enthaltenen benutzerdefinierten Indikators. Wandelt jeden Kerzenkörper in Punkte um (Schlusskurs minus Eröffnung geteilt durch den Instrument-Punktwert) und wendet zwei aufeinanderfolgende exponentielle gleitende Durchschnitte mit demselben Zeitraum an. Steigende Werte zeigen wachsenden Bullen-Druck; fallende Werte zeigen Bären-Druck.

Handelslogik

  1. Eigenkapitalschutz – bevor Signale generiert werden, akkumuliert die Strategie den schwebenden PnL der aktuellen Exposition. Steigt er über ProfitTarget oder fällt unter LossTarget, werden alle offenen Positionen sofort geschlossen.
  2. Eintriegsfilter – fertige Kerzen sind obligatorisch. Der Algorithmus benötigt einen gespeicherten DayImpuls-Wert der vorherigen Kerze, um den ursprünglichen Look-Back mit custom[1] zu emulieren.
  3. Short-Setup – ausgelöst wenn:
    • Keine aktive Short-Exposition vorhanden.
    • DayImpuls liegt über DayImpulsLevel und ist größer als sein vorheriger Wert (positives Momentum).
    • Williams %R liegt über WilliamsLevelUp (überkauft) und CCI ist größer als CciLevel.
    • Orders verwenden TradeVolume plus offenes Long-Volumen für eine Umkehrung in einer einzigen Transaktion im StockSharp-Netting-Modell.
  4. Long-Setup – symmetrische Bedingungen:
    • Keine aktive Long-Exposition.
    • DayImpuls liegt unter DayImpulsLevel und ist kleiner als sein vorheriger Wert (fallendes Momentum).
    • Williams %R liegt unter WilliamsLevelDown und CCI ist kleiner als -CciLevel.
    • Verwendet TradeVolume plus ausstehende Short-Volumen für eine vollständige Umkehrung wenn nötig.
  5. Hedge-artige Umkehrungen – wenn nur eine gerichtete Exposition vorhanden ist und der schwebende PnL das Band [-PredTarget / 2, PredTarget] verlässt, validiert der EA den Martingale-Schritt über den LotCoefficient-Parameter. Im StockSharp-Port bleibt die Validierung erhalten, aber die tatsächliche Ausführung führt eine Schließen-und-Umkehren-Order durch, da die Plattform Nettopositionen statt unabhängiger Hedge-Tickets führt.

Risikomanagement

  • StopLossPips und TakeProfitPips werden mithilfe des PriceStep des Instruments in Preisabstände umgewandelt. Für 3- oder 5-stellige Forex-Symbole wird ein zusätzlicher Faktor von 10 angewendet, um MetaTrader-„Pips" zu emulieren.
  • Beide Werte werden an den High-Level-StartProtection-Helper übergeben, der automatische Stop-Loss- und Take-Profit-Niveaus an die aktive Position anfügt.
  • Die Strategie führt intern Long/Short-Volumen-Tracking, damit GetOpenPnL mit der MetaTrader-Berechnung übereinstimmt, die Commission + Swap + Profit für jedes Ticket summiert.

Parameter

Name Beschreibung Standard
TradeVolume Basis-Ordervolumen in Lots. 1
StopLossPips Schutz-Stop in Pips, umgerechnet in Preisabstand. 50
TakeProfitPips Gewinnziel in Pips, umgerechnet in Preisabstand. 50
IndicatorPeriod Zeitraum für CCI, Williams %R und DayImpuls. 14
CciLevel Absoluter CCI-Schwellenwert für Trades. 150
WilliamsLevelUp Williams %R Überkauft-Niveau (negativer Wert). -15
WilliamsLevelDown Williams %R Überverkauft-Niveau (negativer Wert). -85
DayImpulsLevel DayImpuls-Schwellenwert zur Trennung bullischer/bärischer Regime. 50
ProfitTarget Schwebender Gewinnziel in Kontowährung. 100
LossTarget Schwebende Verlustgrenze in Kontowährung. -100
PredTarget Band zur Auslösung von Mittelungskehrungen. 40
LotCoefficient Vom EA geerbter Validierungskoeffizient. 2
CandleType Für alle Indikatoren verwendeter Zeitrahmen. 15m-Kerzen

Implementierungshinweise

  • Der DayImpuls Oszillator ist als innere Indikatorklasse eingebettet und spiegelt die ursprüngliche doppelte EMA-Glättungslogik wider.
  • Da StockSharp-Strategien Nettopositionen verwalten, werden simultane Long/Short-Hedges der MQL-Version emuliert, indem Schließ- und Eröffnungsvolumen innerhalb derselben Marktorder kombiniert werden.
  • Die Strategie funktioniert nur mit fertigen Kerzen und verwendet IsFormedAndOnlineAndAllowTrading(), um den globalen Strategie-Lebenszyklus zu respektieren.
  • Long/Short-Durchschnittspreise werden über OnOwnTradeReceived verfolgt, damit Teilschließungen und Umkehrungen den schwebenden PnL korrekt aktualisieren.
using System;
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>
/// Bronze Warrior strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class BronzeWarrioirStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public BronzeWarrioirStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}