GitHub で見る

TakeProfitTimeGuardStrategy 戦略

概要

TakeProfitTimeGuardStrategy は MetaTrader のエキスパート Exp_GTakeProfit_Tm の動作をエミュレートし、アカウントレベルの利益を監視し、設定可能な取引スケジュール外ではフラットなポジション状態を強制する。この戦略は独自にポジションを開かない。代わりに、利益目標が達成されたとき、または許可された時間範囲外で取引を停止しなければならないときに、既存のエクスポージャーを自動的に決済するリスク管理オーバーレイとして機能する。

コアロジック

  • 設定可能なローソク足ストリーム(デフォルト1分)にサブスクライブし、最新の終値を使用して実現済みおよび未実現 PnL を評価する。
  • 総利益を実現済み PnL(Strategy.PnL)と現在の平均ポジション価格から導出される浮動 PnL の合計として計算する。
  • 取引ウィンドウが開いている間は損失を無視し、オリジナルのエキスパートアドバイザーの動作を反映する。
  • テイクプロフィット目標が達成されると、内部ストップフラグを設定し、アカウントがフラットになるまで残りのポジションを繰り返し清算する。ストップフラグはポートフォリオがゼロポジションに戻った後にリセットされる。
  • オプションの取引ウィンドウが有効な場合、現在時刻が許可された範囲外になるたびに戦略はすべてのポジションを決済し、取引を再有効化する前にブックがフラットになるまで待機する。

パラメーター

パラメーター デフォルト 説明
CandleType DataType 1分足時間軸 利益とスケジュールロジックを評価するために使用するローソク足シリーズ。
TargetMode ProfitTargetModes (Percent/Currency) Percent TakeProfitValue をアカウント資本のパーセンテージとして解釈するか、絶対通貨額として解釈するかを選択する。
TakeProfitValue decimal 100 利益目標の閾値。TargetMode に従って解釈される。ゼロより大きくなければならない。
UseTradingWindow bool true 時間フィルターを有効または無効にする。
StartTime TimeSpan 00:00:00 許可された取引ウィンドウの開始(含む)。
EndTime TimeSpan 23:59:00 許可された取引ウィンドウの終了。開始時刻が終了時刻より大きい場合、ウィンドウは深夜をまたぐ。

動作上の注意

  1. ポートフォリオの初期値は戦略の起動時(または値がゼロだった場合は最初の更新時)に取得され、パーセンテージ目標の参照として使用される。
  2. 戦略は最新のローソク足終値を使用して浮動 PnL を計算する。結果は選択されたローソク足の粒度に依存する。
  3. 利益目標が達成されると、戦略はブックが空になるまでポジションをフラット化するための成行注文を送り続ける。ブックを決済する理由をログに記録する。
  4. UseTradingWindow が有効でクロックがウィンドウ外にある場合、利益目標が達成されていなくても同じフラット化ルーティンが実行される。
  5. ストップフラグ(_stop)はポジションがゼロに戻った後にのみクリアされ、条件が許すときに取引が再開できるようになる。

オリジナル MQL 戦略との違い

  • ティックハンドラーの代わりに StockSharp 高レベル API(SubscribeCandles)を使用する。
  • Strategy.PositionPrice によって公開される平均ポジション価格から浮動利益を計算する。
  • より簡単な監視のためにテイクプロフィットイベントをログに記録する。
  • 時刻比較はサブスクライブされたローソク足の DateTimeOffset.CloseTime に基づいている。

使用上のヒント

  • ガードレイヤーとして機能させるために、既に別の取引戦略を実行しているポートフォリオに戦略を添付する。
  • 利益評価に必要な応答性に合ったローソク足の時間軸を選択する(例:迅速な制御のための1分)。
  • ポートフォリオ情報(特に CurrentValue)が利用可能であることを確認する。そうでない場合は、パーセンテージ目標を実行する前に明示的な開始残高を設定する。
  • 戦略は別の主要戦略内で 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;

/// <summary>
/// Take Profit Time Guard strategy (simplified). Uses CCI momentum
/// with session time awareness for entries and profit management.
/// </summary>
public class TakeProfitTimeGuardStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciLength;
	private readonly StrategyParam<decimal> _upperLevel;
	private readonly StrategyParam<decimal> _lowerLevel;

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

	public int CciLength
	{
		get => _cciLength.Value;
		set => _cciLength.Value = value;
	}

	public decimal UpperLevel
	{
		get => _upperLevel.Value;
		set => _upperLevel.Value = value;
	}

	public decimal LowerLevel
	{
		get => _lowerLevel.Value;
		set => _lowerLevel.Value = value;
	}

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

		_cciLength = Param(nameof(CciLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("CCI Length", "CCI period", "Indicators");

		_upperLevel = Param(nameof(UpperLevel), 100m)
			.SetDisplay("Upper Level", "CCI level for sell signal", "Logic");

		_lowerLevel = Param(nameof(LowerLevel), -100m)
			.SetDisplay("Lower Level", "CCI level for buy signal", "Logic");
	}

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

		var cci = new CommodityChannelIndex { Length = CciLength };

		decimal prevCci = 0;
		var hasPrev = false;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(cci, (ICandleMessage candle, decimal cciVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!hasPrev)
				{
					prevCci = cciVal;
					hasPrev = true;
					return;
				}

				if (!IsFormedAndOnlineAndAllowTrading())
				{
					prevCci = cciVal;
					return;
				}

				// CCI crosses up from below lower level
				if (prevCci < LowerLevel && cciVal >= LowerLevel && Position <= 0)
					BuyMarket();
				// CCI crosses down from above upper level
				else if (prevCci > UpperLevel && cciVal <= UpperLevel && Position >= 0)
					SellMarket();

				prevCci = cciVal;
			})
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);

			var cciArea = CreateChartArea();
			if (cciArea != null)
				DrawIndicator(cciArea, cci);
		}
	}
}