Auf GitHub ansehen

Ein-Minuten-Scalper-Strategie

Diese Strategie portiert den 1 MINUTE SCALPER MetaTrader 4 Expert Advisor in die StockSharp High-Level-API. Sie behält die mehrschichtige Trendbestätigung, das Multi-Timeframe-Momentum und den Langzeit-MACD-Filter des Original-Roboters bei, während die Risikokontrollen an StockSharp's positionszentriertes Modell angepasst werden.

Kernlogik

  1. Trend-Stack – dreizehn linear gewichtete gleitende Durchschnitte (LWMA 3/5/8/10/12/15/30/35/40/45/50/55/200) müssen in strenger Reihenfolge ausgerichtet sein. Long-Trades erfordern, dass jeder kürzere Durchschnitt über dem nächsten liegt, während Shorts die Bedingung umkehren.
  2. Primäres Trend-Gate – ein zusätzlicher schneller LWMA (Standard 6) muss für Longs über dem langsamen LWMA (Standard 85) bleiben und für Shorts darunter, was die schnell-vs-langsam-Prüfung des EA widerspiegelt.
  3. Kerzenstruktur – Einträge werden nur ausgelöst, wenn die Überlappungsmuster aus dem Skript vorhanden sind: Für Longs muss das Tief vor zwei Bars unter dem vorherigen Hoch liegen, für Shorts muss das vorherige Tief unter dem Hoch von vor zwei Bars fallen.
  4. Momentum-Filter – ein 14-Perioden-Momentum-Indikator, berechnet auf einem höheren Zeitrahmen (Standard 15-Minuten- Kerzen), muss bei mindestens einem der letzten drei Werte um mindestens die konfigurierten Schwellenwerte von 100 abweichen. Dies reproduziert die MomLevelB/MomLevelS-Vergleiche.
  5. Monatlicher MACD-Bias – ein MACD, der auf dem ausgewählten MACD-Zeitrahmen aufgebaut ist (standardmäßig 30-Tage-Kerzen als Proxy für monatliche Daten), muss die Hauptlinie über der Signallinie für Longs oder darunter für Shorts zeigen.

Trade-Management

  • Anfangsschutz – Stop-Loss- und Take-Profit-Abstände werden in Instrument-Schritten (Punkten) ausgedrückt. Wenn eine Position öffnet, wandelt die Strategie diese Schrittzählungen in absolute Preise um, indem sie Security.PriceStep verwendet.
  • Break-Even-Bewegung – nachdem sich der Preis um BreakEvenTriggerSteps zugunsten bewegt hat, wird der Stop auf den Einstieg plus BreakEvenOffsetSteps verschoben (für Shorts gilt die gespiegelte Logik). Das Flag wird einmal pro Position ausgelöst.
  • Schritt-Trailing – wenn TrailingStopSteps positiv ist, folgt der Stop dem höchsten (oder niedrigsten) Preis seit dem Einstieg um die angegebene Anzahl von Schritten.
  • Geld-Trailing – sobald der schwebende Gewinn MoneyTrailTarget (Währung) überschreitet, verfolgt die Strategie den Spitzen-PnL und schließt die Position, wenn der Rückgang MoneyTrailStop entspricht.
  • Geld-/Prozentziele – optionale absolute oder prozentuale Take-Profit-Ziele schließen alle Positionen, wenn der schwebende PnL die konfigurierten Schwellenwerte überschreitet. Das Prozentziel verwendet den beim Start der Strategie erfassten Anfangs-Portfoliowert.
  • Eigenkapital-Stop – die Strategie überwacht das maximale Eigenkapital (Portfoliowert plus offener PnL). Wenn der Drawdown von diesem Höchststand EquityRiskPercent überschreitet, werden alle Positionen abgebaut und die AccountEquityHigh()- Schutzmaßnahme des EA repliziert.

Parameter

Parameter Beschreibung
Volume Ordervolumen für neue Einträge. Wird zur absoluten aktuellen Position hinzugefügt, sodass Umkehrungen die Exposition sofort wechseln.
FastMaPeriod / SlowMaPeriod LWMA-Längen für den primären Trendfilter.
MomentumPeriod Länge des Momentum-Indikators auf höherem Zeitrahmen.
MomentumBuyThreshold / MomentumSellThreshold Minimale absolute Abweichung von 100, die für Long-/Short-Momentum-Bestätigung erforderlich ist.
MacdFastLength / MacdSlowLength / MacdSignalLength MACD-Konfiguration für MacdCandleType.
StopLossSteps / TakeProfitSteps Schutz-Stop- und Zielabstände in Preisschritten. Auf null setzen zum Deaktivieren.
TrailingStopSteps Schrittbasierter Trailing-Stop-Abstand (0 deaktiviert).
BreakEvenTriggerSteps / BreakEvenOffsetSteps Abstand zum Auslösen der Break-Even-Bewegung und der Offset beim Verschieben des Stops.
UseMoneyTakeProfit, MoneyTakeProfit Währungsbasiertes schwebendes Gewinnziel aktivieren und dimensionieren.
UsePercentTakeProfit, PercentTakeProfit Schwebendes Gewinnziel als Prozentsatz des Anfangskapitals aktivieren und dimensionieren.
EnableMoneyTrailing, MoneyTrailTarget, MoneyTrailStop Trailing-Logik für den schwebenden Gewinn konfigurieren.
UseEquityStop, EquityRiskPercent Drawdown-Stop aktivieren und den maximalen Drawdown-Prozentsatz definieren.
CandleType Primäre Arbeitskerzen (Standard 1 Minute).
MomentumCandleType Kerzen auf höherem Zeitrahmen für den Momentum-Indikator (Standard 15 Minuten).
MacdCandleType Kerzen für den MACD-Trendfilter (Standard 30 Tage ≈ monatlich).

Unterschiede zum MT4-Expert

  • StockSharp verwendet Nettopositionen, daher pflegt die Strategie immer eine einzelne aggregierte Position anstelle mehrerer Tickets bis zu Max_Trades. Umkehrungen schließen das bestehende Exposure, bevor sie in die entgegengesetzte Richtung öffnen.
  • PercentTakeProfit bezieht sich auf den beim Start erfassten Portfoliowert statt des sich ständig ändernden AccountBalance(), das MetaTrader verwendet, was geräuschvolle Ziele vermeidet, wenn externe Trades das Guthaben ändern.
  • Die geldbasierte Ausstiegslogik (Take_Profit_In_Money und TRAIL_PROFIT_IN_MONEY2) arbeitet mit dem Live-PnL, der aus dem durchschnittlichen Eintrittspreis der Strategie berechnet wird. Dies entspricht dem EA-Verhalten, aber innerhalb des StockSharp-Schutzrahmens.
  • Die Plattform muss Kerzen-Feeds für die ausgewählten Zeitrahmen liefern (CandleType, MomentumCandleType, MacdCandleType). Stellen Sie sicher, dass die verwendeten Adapter die angeforderten Auflösungen unterstützen.

Stimmen Sie die Schwellenwerte auf Ihr Instrument und Ihre Session ab. Enge Spreads oder hochvolatile Paare können breitere Schrittabstände oder größere Momentum-Schwellenwerte erfordern, um Rauschen zu reduzieren.

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>
/// One-minute scalper strategy using fast/slow WMA crossover.
/// Buys on bullish crossover, sells on bearish crossover.
/// </summary>
public class OneMinuteScalperStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private WeightedMovingAverage _fast;
	private WeightedMovingAverage _slow;

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

	/// <summary>
	/// Fast WMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow WMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="OneMinuteScalperStrategy"/> class.
	/// </summary>
	public OneMinuteScalperStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast WMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 85)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow WMA period", "Indicator");

		_stopLossPoints = Param(nameof(StopLossPoints), 200)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 500)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

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

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

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_fast = new WeightedMovingAverage { Length = FastPeriod };
		_slow = new WeightedMovingAverage { 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;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

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

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// WMA crossover
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

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

			SellMarket();
			_entryPrice = close;
			_cooldown = 60;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}