Auf GitHub ansehen

Bruno-Strategie

Der Bruno-Expertenberater ist ein Trendfolgesystem, das ursprünglich für MetaTrader 5 geschrieben wurde. Der Port behält dieselbe Bestätigungskette: Average Directional Index (ADX) mit Richtungsbewegung, ein Paar exponentieller gleitender Durchschnitte (EMA 8/21), MACD (13, 34, 8), ein Stochastischer Oszillator (21, 3, 3) und die Steigung eines Parabolic SAR (0.055, 0.21). Jeder Filter, der mit der Richtung übereinstimmt, multipliziert die Ordergröße mit einem konfigurierbaren Faktor. Wenn sowohl Long- als auch Short-Signale auf derselben Kerze verstärkt werden, wird kein Trade durchgeführt, um widersprüchliche Orders zu vermeiden.

Handelslogik

  • Richtungsbias
    • Long-Druck wird verstärkt wenn +DI > -DI und +DI > 20.
    • Short-Druck wird verstärkt wenn +DI < -DI und +DI < 40.
  • Momentum-Ausrichtung
    • Long-Präferenz erfordert EMA(8) über EMA(21), Stochastisches %K über %D und %K unter der Überkauft-Schwelle (Standard 80).
    • Short-Präferenz erfordert EMA(8) unter EMA(21), Stochastisches %K unter %D und %K über der Überverkauft-Schwelle (Standard 20).
  • MACD-Filter
    • Long-Bias: MACD-Histogramm über null und MACD-Hauptlinie über der Signallinie.
    • Short-Bias: MACD-Histogramm unter null und MACD-Hauptlinie unter der Signallinie.
  • Parabolic SAR-Steigung
    • Long-Bias wird verstärkt wenn die vorherigen SAR-Werte steigen während EMA(8) > EMA(21).
    • Short-Bias wird verstärkt wenn die vorherigen SAR-Werte fallen während EMA(8) < EMA(21).

Jede erfüllte Bedingung multipliziert die Basis-Lotgröße mit SignalMultiplier (Standard 1.6). Nur eine Seite kann gleichzeitig aktiv sein. Wenn ein abschließendes Signal generiert wird, schließt die Strategie jede entgegengesetzte Position, sendet die Marktorder mit dem multiplizierten Volumen und speichert den aktuellen Schlusskurs als Einstiegspreis.

Positionsmanagement

  • Stop-Loss / Take-Profit – feste Abstände in angepassten Pips, entsprechend der MetaTrader-Version. Wenn ein Level intrabar getroffen wird, wird die Position sofort geschlossen.
  • Trailing-Stop – aktiviert, sobald der schwebende Gewinn TrailingStop + TrailingStep Pips übersteigt. Der Stop wird dann TrailingStop Pips hinter dem Preis platziert und rückt nur vor, wenn der Gewinn um mindestens TrailingStep weitere Pips zunimmt.
  • Konfliktbehandlung – wenn sowohl Long- als auch Short-Filter auf derselben Kerze ausgelöst werden, wird kein neuer Trade eingegangen.

Parameter

Gruppe Name Beschreibung
Handel BaseVolume Anfängliche Lotgröße vor Multiplikatoren.
Handel SignalMultiplier Volumen-Multiplikator, der von jedem übereinstimmenden Filter angewendet wird.
Risikomanagement StopLossPips / TakeProfitPips Schutzabstände in angepassten Pips. Auf null setzen zum Deaktivieren.
Risikomanagement TrailingStopPips / TrailingStepPips Trailing-Abstand und Mindestschritt in angepassten Pips.
Indikatoren AdxPeriod, AdxPositiveThreshold, AdxNegativeThreshold ADX-Länge und DI-Schwellenwerte.
Indikatoren FastEmaPeriod, SlowEmaPeriod EMA-Längen für die Trendbestätigung.
Indikatoren MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod MACD-Konfiguration.
Indikatoren StochasticPeriod, StochasticKsmoothing, StochasticDsmoothing, StochasticOverbought, StochasticOversold Stochastischer Oszillator-Einstellungen.
Allgemein CandleType Zeitrahmen für die gesamte Signalkette (Standard 1 Stunde).

Hinweise

  • Die angepasste Pip-Größe folgt der MetaTrader-Konvention: Instrumente mit 3 oder 5 Dezimalstellen werden mit 10 multipliziert.
  • Parabolic SAR arbeitet mit Beschleunigungsschritt 0.055 und Maximum 0.21, entsprechend den Expertenberater-Standardwerten.
  • Der Port behält den ursprünglichen Geldmanagement-Stil (Volumen-Stacking) bei, aggregiert das Exposure jedoch in einer einzigen StockSharp-Position.
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>
/// Bruno trend-following strategy using EMA crossover with trend filter.
/// Buys when fast EMA crosses above slow EMA with price above trend EMA.
/// Sells on reverse conditions.
/// </summary>
public class BrunoStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _trendPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;
	private ExponentialMovingAverage _trend;

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

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

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

	/// <summary>
	/// Trend EMA period.
	/// </summary>
	public int TrendPeriod
	{
		get => _trendPeriod.Value;
		set => _trendPeriod.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="BrunoStrategy"/> class.
	/// </summary>
	public BrunoStrategy()
	{
		_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");

		_trendPeriod = Param(nameof(TrendPeriod), 200)
			.SetGreaterThanZero()
			.SetDisplay("Trend Period", "Trend 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");
	}

	/// <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;
		_trend = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

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

		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		_trend = new ExponentialMovingAverage { Length = TrendPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, _trend, ProcessCandle);
		subscription.Start();
	}

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

		if (!_fast.IsFormed || !_slow.IsFormed || !_trend.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 = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

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

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

		// EMA crossover with trend filter
		if (_prevFast <= _prevSlow && fastValue > slowValue && close > trendValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

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

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

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}