GitHub で見る

Bruno 戦略

Bruno エキスパートアドバイザーは MetaTrader 5 向けに元々書かれたトレンドフォローシステムです。ポートは同じ確認チェーンを維持します: 方向性動き付きの Average Directional Index (ADX)、一対の指数移動平均(EMA 8/21)、MACD (13, 34, 8)、ストキャスティクスオシレーター (21, 3, 3)、Parabolic SAR (0.055, 0.21) の傾き。方向に同意した各フィルターは注文サイズを設定可能な係数で乗算します。同じローソク足でロングとショートの両シグナルが増幅された場合、競合する注文を避けるためにトレードはスキップされます。

取引ロジック

  • 方向バイアス
    • +DI > -DI かつ +DI > 20 のとき、ロング圧力が強化されます。
    • +DI < -DI かつ +DI < 40 のとき、ショート圧力が強化されます。
  • モメンタムアライメント
    • ロング優先は EMA(8) が EMA(21) より上、ストキャスティクス %K が %D より上、%K が買われすぎ閾値(デフォルト 80)より下であることを必要とします。
    • ショート優先は EMA(8) が EMA(21) より下、ストキャスティクス %K が %D より下、%K が売られすぎ閾値(デフォルト 20)より上であることを必要とします。
  • MACD フィルター
    • ロングバイアス: MACD ヒストグラムがゼロより上、MACD メインラインがシグナルラインより上。
    • ショートバイアス: MACD ヒストグラムがゼロより下、MACD メインラインがシグナルラインより下。
  • Parabolic SAR の傾き
    • EMA(8) > EMA(21) の間に前の SAR 値が上昇しているとき、ロングバイアスが強化されます。
    • EMA(8) < EMA(21) の間に前の SAR 値が下落しているとき、ショートバイアスが強化されます。

各満たされた条件はベースロットサイズを SignalMultiplier(デフォルト 1.6)で乗算します。一度に1サイドのみアクティブにできます。最終シグナルが生成されると、ストラテジーは反対のポジションをクローズし、乗算されたボリュームで成行注文を送信し、現在のクローズを参入価格として保存します。

ポジション管理

  • ストップロス / テイクプロフィット – 調整済みピップスで表された固定距離、MetaTrader バージョンと一致。イントラバーでいずれかのレベルに達した場合、ポジションは即座にクローズされます。
  • トレーリングストップ – 浮動利益が TrailingStop + TrailingStep ピップスを超えると起動します。ストップはその後 TrailingStop ピップス価格の後ろに置かれ、利益が少なくとも TrailingStep ピップス増加した場合のみ前進します。
  • 競合処理 – 同じローソク足でロングとショートの両フィルターが発動した場合、新しいトレードは取りません。

パラメーター

グループ 名前 説明
トレーディング BaseVolume 乗数前の初期ロットサイズ。
トレーディング SignalMultiplier 各同意フィルターが適用するボリューム乗数。
リスク管理 StopLossPips / TakeProfitPips 調整済みピップスの保護距離。無効にするにはゼロに設定します。
リスク管理 TrailingStopPips / TrailingStepPips 調整済みピップスのトレーリング距離と最小ステップ。
インジケーター AdxPeriod, AdxPositiveThreshold, AdxNegativeThreshold ADX 長と DI 閾値。
インジケーター FastEmaPeriod, SlowEmaPeriod トレンド確認に使用される EMA 長。
インジケーター MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod MACD 設定。
インジケーター StochasticPeriod, StochasticKsmoothing, StochasticDsmoothing, StochasticOverbought, StochasticOversold ストキャスティクスオシレーター設定。
全般 CandleType 全シグナルチェーンに使用される時間軸(デフォルト 1 時間)。

注意事項

  • 調整済みピップサイズは MetaTrader の規則に従います: 3 または 5 桁の小数点以下の楽器は 10 で乗算されます。
  • Parabolic SAR は加速ステップ 0.055、最大 0.21 で動作し、エキスパートアドバイザーのデフォルト値を反映します。
  • ポートは元のマネー管理スタイル(ボリュームスタッキング)を維持しますが、エクスポージャーを単一の StockSharp ポジションに集約します。
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;
	}
}