GitHub で見る

Plan X戦略

概要

Plan X戦略は、元のMetaTrader 5エキスパートアドバイザーから変換されたブレイクアウトシステムです。各完成したローソク足のクローズを、設定可能なバー数だけシフトした参照ローソク足と比較して評価します。最新のクローズが参照クローズを指定のチャンネル高さだけ超えると、戦略はブレイクアウト方向にポジションを開きます。オプションのシグナル反転により、反対方向のブレイクアウトを取引できます。

実装はStockSharpの高水準APIを使用します。調整可能なストップロス、テイクプロフィット、トレーリングストップロジック、および取引セッションフィルターをサポートします。

動作方法

  1. ローソク足処理 – 戦略は設定されたローソク足タイプを購読し、完成したローソク足のみを処理します。シフトされた参照バーと最新値を比較するために、クローズの短い履歴が維持されます。
  2. ブレイクアウト検出 – 最新のクローズがチャンネル高さ以上で参照クローズを超えると、ロングシグナルが生成されます。同じ量だけ下回ると、ショートシグナルが生成されます。反転フラグが有効な場合、シグナルは反転します。
  3. 注文実行 – 戦略はマーケット注文を使用します。反対のポジションから反転する場合、注文ボリュームは自動的に現在のポジションの絶対値を含め、1つの操作でフラット化して再エントリーします。
  4. リスク管理 – ストップロスとテイクプロフィットレベルはエントリー直後に設定されます。価格がトレーリング距離にトレーリングステップを加えた量以上で有利に動いた場合、トレーリングストップが元のストップに取って代わることができます。
  5. タイムフィルター – 取引は開始時間と終了時間に制限できます。開始時間が終了時間より大きい場合、ウィンドウは午前0時をまたがるものとして処理されます。

パラメーター

パラメーター 説明
Stop Loss (pips) インストゥルメントの価格ステップに基づいて価格単位に変換された、pipでの保護ストップ距離。
Take Profit (pips) pipでのターゲット距離。
Trailing Stop (pips) 価格とトレーリングストップの間の距離。トレーリングを無効にするにはゼロに設定します。
Trailing Step (pips) トレーリングストップが進む前に必要な追加利益。トレーリングが有効な場合は正の値でなければなりません。
Channel Height (pips) pipで表したブレイクアウト閾値。
Candle Shift 最新のクローズと参照ローソク足の間のバー数。
Use Time Control 取引セッションフィルターを有効または無効にします。
Start Hour 取引が許可される最初の時間(0〜23)。
End Hour 取引が許可される最後の時間(0〜23)。
Reverse Signals ブレイクアウト方向を反転します。
Order Volume ロット/契約で表したマーケット注文サイズ。
Candle Type 分析に使用するローソク足のデータタイプ。

シグナルロジック

  • ロングエントリー – 最新のクローズ ≥ 参照クローズ + チャンネル高さ、反転無効。
  • ショートエントリー – 最新のクローズ ≤ 参照クローズ − チャンネル高さ、反転無効。
  • 反転が有効な場合、ロジックはロングとショートの条件を入れ替えます。

トレーリングストップロジック

  • トレーリングストップは有利な動きが価格でTrailing Stop + Trailing Stepを超えると有効になります。
  • ロングポジションでは新しい値が既存のストップより高い場合、ストップはhigh − Trailing Stopに移動されます。
  • ショートポジションでは新しい値が既存のストップより低い場合、ストップはlow + Trailing Stopに移動されます。

追加の注意事項

  • pip計算は3桁または5桁の小数点を持つインストゥルメントの価格ステップを10倍にするMQLバージョンを模倣します。
  • 許可されたセッション外での取引は新しいエントリーをスキップしますが、開いているポジションは引き続き管理します。
  • 戦略は起動時に一度StartProtection()を呼び出して組み込みポートフォリオ保護サービスを有効にします。
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;

public class PlanXStrategy : 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 PlanXStrategy()
	{
		_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;
	}
}