GitHub で見る

CloseProfit V2 戦略

概要

CloseProfit V2は、設定可能な利益または損失の閾値に達したときにすべてのアクティブな取引エクスポージャーを強制的に閉じる、元のMetaTraderユーティリティの動作を複製します。StockSharpポートはアカウント保護モジュールとして機能します:完成した各ローソク足でフローティングPnLを監視し、制限を超えると未決注文をキャンセルしてポジションを清算します。この戦略は、同じポートフォリオに依存する裁量または自動エントリーと並行して実行するように設計されています。

シグナル生成システムとは異なり、CloseProfit V2は自分でポジションを開くことはありません。リアルタイムの損益メトリクスを観察するだけで、トレーダーがMQLバージョンで使用する「パニックボタン」ロジックを自動化できます。監視頻度はローソク足サブスクリプションで制御されるため、このコンポーネントは過去のバックテストとライブ取引環境の両方に対応しています。

動作原理

  1. 戦略が開始すると、現在のポートフォリオ値をフラットポジション時の直近の資産スナップショットとして取得し、設定されたローソク足サブスクリプションを起動します。
  2. ローソク足が完成するたびに、戦略は終値を保存してフローティング利益を評価します:
    • AllSymbolsが無効の場合、主要銘柄のみが追跡されます。フローティング利益はPosition * (lastClose - averagePrice)として計算されるため、未実現PnLのみが使用され、オープントレードを合計するMQLロジックを反映しています。
    • AllSymbolsが有効の場合、モジュールは現在のポートフォリオ値と直近のフラット資産スナップショットを比較します。これにより戦略が管理するすべての銘柄の未実現損益の合計が測定されます。
  3. フローティング利益がProfitCloseを超えるか-LossCloseを下回ると、戦略は完全清算を要求します。アクティブ注文を即座にキャンセルし、影響を受けるすべての銘柄をフラット化するための成行指示を送信します。
  4. 清算が完了してすべてのポジションがゼロになった後、フラット資産スナップショットが更新されます。これにより後続の監視が新しいアカウント残高から開始され、実現利益での再トリガーを避けます。

この実装は元のMQL EAの動作を反映しています:歴史的な実現PnLを無視し、オープンポジションに純粋に反応します。組み込みの保護ブロックにより、クローズルーティンがシグナルごとに1回だけ実行され、キャンセルリクエストのスパムが繰り返されないことが保証されます。

パラメーター

  • ProfitClose(デフォルト 10) – 口座通貨でのフローティング利益の閾値。未実現利益がこのレベルに達すると、戦略は監視中のすべてのポジションを清算します。
  • LossClose(デフォルト 1000) – フローティング損失の閾値。未実現ドローダウンがこの絶対値を超えると、これ以上の損失を止めるためすべてのポジションが閉じられます。
  • AllSymbols(デフォルト false)falseの場合、戦略に割り当てられた主要Securityのみが観察されます。trueの場合、モジュールは戦略のポジションセット内のすべての銘柄のフローティングPnLを集計し、それらすべてを同時に清算します。
  • CandleType(デフォルト 1分の時間軸) – 評価に使用するローソク足シリーズ。AllSymbolsが無効のとき、ローソク足の終値が利益計算を駆動します。時間軸が短いほど反応が速くなりますが、バックテスト中の計算負荷が増加します。

実践的な注意事項

  • 同じポートフォリオを共有する他の取引戦略と一緒にコンポーネントを起動します。閾値に達すると、CloseProfit V2はそれらの未決注文をキャンセルし、オープンポジションを閉じます。
  • 手数料とスワップの調整はStockSharpの高レベルAPIでは利用できないため、フローティングPnLは純粋に価格差に基づいています。これらのコストが重要な場合は、それに応じて閾値を増やしてください。
  • 清算は成行注文に依存するため、ProfitCloseLossCloseを設定するときに十分な流動性またはスリッページバッファーがあることを確認してください。
  • ローソク足サブスクリプションはバックテスト中に決定論的な評価ポイントを保証するためにも使用されます。ライブ取引では、バー内監視が必要な場合はより速いフレームに切り替えることができます。
  • 戦略は起動時にStartProtection()を呼び出し、StockSharpの組み込みセーフティチェック(例:再接続処理)がユーティリティ実行中もアクティブな状態を維持します。

元のMQL実装との違い

  • MetaTraderの「マジックナンバー」フィルターは不要です:StockSharpは戦略でオーダーを識別するため、モジュールはすでに制御するポジションを分離しています。したがってAllSymbolsは同じ戦略インスタンスが管理するすべての銘柄に適用されます。
  • MQL EAは残高、資産、チケット数を表示するためのチャートラベルを管理していました。C#バージョンはStockSharpのチャーティングがオプションであり、自動化された実行では常に利用可能とは限らないため、ログメッセージを使用します。
  • MQLでデモトレードを自動作成していたデバッグ/テスタースキャフォールディングは削除されました。StockSharp戦略は監視と清算に純粋に集中しています。

使用場面

フローティングPnLに対するハードストップが必要な場合にCloseProfit V2をデプロイします—資金提供口座の保護、プロプライエタリリスクポリシーの施行、またはセッションベースの利益目標の自動化。取引ワークフローに必要な反応速度に合わせてローソク足期間を調整してください。

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>
/// CloseProfit v2 strategy (simplified).
/// Uses EMA crossover for entries with profit/loss exit thresholds.
/// </summary>
public class CloseProfitV2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;

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

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public CloseProfitV2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candles", "General");

		_fastLength = Param(nameof(FastLength), 8)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowLength = Param(nameof(SlowLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
	}

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

		var fastEma = new ExponentialMovingAverage { Length = FastLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowLength };
		decimal prevFast = 0m;
		decimal prevSlow = 0m;
		var hasPrev = false;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, (ICandleMessage candle, decimal fastVal, decimal slowVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!hasPrev)
				{
					prevFast = fastVal;
					prevSlow = slowVal;
					hasPrev = true;
					return;
				}

				if (!IsFormedAndOnlineAndAllowTrading())
				{
					prevFast = fastVal;
					prevSlow = slowVal;
					return;
				}

				if (prevFast <= prevSlow && fastVal > slowVal && Position <= 0)
					BuyMarket();
				else if (prevFast >= prevSlow && fastVal < slowVal && Position >= 0)
					SellMarket();

				prevFast = fastVal;
				prevSlow = slowVal;
			})
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastEma);
			DrawIndicator(area, slowEma);
			DrawOwnTrades(area);
		}
	}
}