GitHub で見る

Xバグ戦略

概要

X バグ戦略 は、同じ名前の MQL4 エキスパート アドバイザーから変換された移動平均クロスオーバー システムです。ローソク足の中央値に基づいて計算された 2 つの単純移動平均を比較します。ファースト平均がスロー平均の上または下にクロスすると、戦略はクロスオーバーの方向にポジションをオープンします。この実装では、オプションのシグナル反転、反対シグナルでの自動ポジションクローズ、pip ベースの保護命令など、オリジナルの Expert Advisor 機能が再現されています。

取引ロジック

  1. 構成されたローソク足タイプ (デフォルトでは 1 分ローソク足) をサブスクライブし、2 つの単純移動平均 (高速ラインと低速ライン) を計算します。平均では中央価格が使用され、設定されたインジケーターのシフトが尊重されます。
  2. 現在のファスト値がスロー値を上回っているが、2 バー前のファスト値がスロー値を下回っている場合に、強気のクロスオーバーを検出します。逆の条件を使用して弱気のクロスオーバーを検出します。
  3. ReverseSignals が有効になっている場合、オプションでクロスオーバー信号を反転して反対方向にトレードします。
  4. CloseOnSignal が有効な場合、新しいシグナルで新しいポジションを入力する前に、反対のポジションを直ちに閉じます。
  5. 強気シグナルでロングポジションを入力し、弱気シグナルでショートポジションを入力します。この戦略は、同じ方向にポジションを積み重ねることを避けます。現在のポジションがフラットであるか、シグナルと一致している場合にのみ取引されます。

リスク管理

  • StopLossPips – 絶対保護ストップをピップ単位で設定します。ストップはピップ全体で表現されます。端数価格設定 (5 桁または 3 桁の相場) は、証券価格ステップを使用して pip 値を変換することによって自動的に処理されます。
  • TakeProfitPips – 利益目標距離をピップ単位で設定します。
  • TrailingStopPipsUseTrailingStop が有効な場合、ポジションが利益に移行すると、設定されたピップ距離で開始するトレーリング ストップが有効になります。後続ステップは後続距離と一致し、元の MetaTrader ロジックを複製します。
  • すべての保護注文は、MQL4 の専門家と同等の状態を維持するために、市場からの撤退を伴う StartProtection を通じて管理されます。

パラメーター

パラメータ 説明 デフォルト
OrderVolume 市場エントリーに使用される基本取引量。 0.1
StopLossPips ピップス単位で測定されるストップロス距離。無効にするには、0 に設定します。 70
TakeProfitPips ピップス単位で測定されるテイクプロフィット距離。無効にするには、0 に設定します。 5000
UseTrailingStop トレーリングストップ管理を有効または無効にします。 true
TrailingStopPips ピップ単位のトレーリング距離。 90
FastPeriod 高速移動平均の期間。 1
FastShift シグナルを評価する前に高速移動平均をシフトするためのバー。 0
SlowPeriod ゆっくりとした移動平均の期間。 14
SlowShift シグナルを評価する前に低速移動平均をシフトするためのバー。 10
CloseOnSignal 新しいシグナルが現れたら、すぐに反対のポジションを閉じます。 true
ReverseSignals 信号の方向を反転して、クロスオーバーに対抗します。 false
AppliedPrice 移動平均に提供されるローソク足の価格ソース。 Median
CandleType 信号生成用のローソク足データ型。 1 minute 時間枠

注意事項

  • pip 変換では、小数点以下 5 桁または 3 桁で引用されたシンボルの価格ステップに 10 が乗算され、元の Expert Advisor の動作と一致します。
  • Python ポートは提供されません。このディレクトリには C# ストラテジーのみが含まれます。
  • トレーリング ストップ、ストップ、ターゲットはオプションです。無効にするには、対応する pip 値をゼロに設定します。
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

using Ecng.Common;

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

/// <summary>
/// Moving average crossover strategy converted from the MQL4 expert "X bug".
/// Uses fast and slow SMA crossover for signal generation.
/// </summary>
public class XBugStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<bool> _reverseSignals;
	private readonly StrategyParam<DataType> _candleType;

	private SimpleMovingAverage _slowMa;
	private decimal? _prevFast;
	private decimal? _prevSlow;
	private int _cooldown;
	private int _candleCount;

	public XBugStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA period", "Length of the fast moving average.", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA period", "Length of the slow moving average.", "Indicators");

		_reverseSignals = Param(nameof(ReverseSignals), false)
			.SetDisplay("Reverse signals", "Invert buy and sell directions.", "Trading");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle type", "Primary timeframe used for signals.", "General");
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public bool ReverseSignals
	{
		get => _reverseSignals.Value;
		set => _reverseSignals.Value = value;
	}

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_slowMa = default;
		_prevFast = null;
		_prevSlow = null;
		_cooldown = 0;
		_candleCount = 0;
	}

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

		Volume = 0.001m;
		_prevFast = null;
		_prevSlow = null;
		_cooldown = 0;
		_candleCount = 0;

		_slowMa = new SimpleMovingAverage { Length = SlowPeriod };

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

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

		if (!_slowMa.IsFormed)
			return;

		_candleCount++;
		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		// Use close price as the "fast" value (period=1 effectively)
		var fastValue = candle.ClosePrice;

		if (_prevFast is null || _prevSlow is null)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			return;
		}

		var signal = 0;
		if (fastValue > slowValue && _prevFast.Value <= _prevSlow.Value)
			signal = 1;
		else if (fastValue < slowValue && _prevFast.Value >= _prevSlow.Value)
			signal = -1;

		_prevFast = fastValue;
		_prevSlow = slowValue;

		if (signal == 0)
			return;

		if (ReverseSignals)
			signal = -signal;

		if (signal > 0 && Position <= 0)
		{
			BuyMarket();
			_cooldown = 100;
		}
		else if (signal < 0 && Position >= 0)
		{
			SellMarket();
			_cooldown = 100;
		}
	}
}