GitHub で見る

オープンタイムのデイリーウィンドウ戦略

概要

この戦略は、MetaTrader エキスパート「OpenTime」の動作を再現します。設定可能な時間帯に成行注文を出し、オプションで専用の出口ウィンドウ中にすべてのエクスポージャーをクローズし、固定ストップロス、テイクプロフィット、トレーリングプロテクションなどのシンプルな資金管理ルールを適用します。このポートは高レベルの StockSharp Strategy API を使用するため、この戦略をフレームワーク内の他のコンポーネントと組み合わせることができます。

仕組み

  1. 選択した時間枠の終了したローソク足ごとに時刻チェックがトリガーされます。
  2. 現在の時間が取引ウィンドウ内にある場合、ストラテジーは有効なすべての方向に対して成行注文を送信します。
    • 片側のみが有効な場合は、要求されたボリュームに達するまで、現在のネット ポジションが拡張または逆転されます。
    • 両方が有効な場合、買い注文と売り注文は同じウィンドウで発行されます。 StockSharp アカウントのエクスポージャは横にネットされているため、2 番目の方向を開くと、新しい方向を確立する前に反対側のエクスポージャが自動的に相殺されます。
  3. 終了ウィンドウがアクティブである間、ストラテジーは ClosePosition() を 1 回呼び出して、未処理のエクスポージャーを平準化します。
  4. オプションのストップロス、テイクプロフィット、トレーリングストップの距離は、市場出口を使用して保護注文を管理する StartProtection に委任されます。

パラメーター

  • ウィンドウを閉じるを有効にするTimeClose フラグを反映します。有効にすると、Close Position TimeWindow Length は既存の取引が終了するタイミングを定義します。
  • Close Position Time – 毎日の出口ウィンドウが始まる時刻 (デフォルトは 20:50)。
  • 取引時間 – 新しい取引が許可される毎日の時間 (デフォルトは 18:50)。
  • ウィンドウの長さ – 取引ウィンドウと終了ウィンドウの両方の期間(デフォルトは 5 分、元の Duration 入力に対応)。
  • 販売エントリを許可 – MQL Sell スイッチに対応します。短いエントリを有効にします (デフォルトは true)。
  • 購入エントリを許可 – MQL Buy スイッチに対応します。長いエントリを有効にします (デフォルトは false)。
  • 注文量 – 新しい取引ごとの目標純量 (デフォルトは 0.1 ロット)。この戦略では、反対のシグナルが現れたときに現在のポジションの絶対値を加算するため、単一の成行注文で反転が発生します。
  • Stop-Loss Points – 保護停止の距離 (ポイント単位) (デフォルト 0 は停止を無効にします)。
  • 利益確定ポイント – 利益ターゲットまでのポイント単位の距離 (デフォルトは 0 でターゲットが無効になります)。
  • トレーリング ストップを使用する – 元の SimpleTrailing ヘルパーのトレーリング ストップ ロジックを有効にします。
  • トレーリングストップポイント – ポイントで表されるトレーリング距離 (デフォルトは 300)。
  • トレーリング ステップ ポイント – トレーリング ストップに進む前に追加の進捗が必要です (デフォルトは 3)。
  • ローソク足タイプ – 時間チェックに使用される時間枠 (デフォルトは 1 分ローソク足)。

注意事項

  • ポイントサイズは証券価格ステップから導出されます。小数点 3 桁および 5 桁の引用符の場合、ステップは 10 倍され、MQL スクリプトで使用される pip 処理が再現されます。
  • StartProtection は、距離の少なくとも 1 つがゼロより大きい場合にのみ保護停止を取り付けます。固定ストップロスなしでトレーリングがアクティブな場合、トレーリング距離が初期保護値として提供されます。
  • StockSharp はすでに成行注文の自動エラー処理を提供しているため、この戦略では未決注文や繰り返しの再試行を意図的に管理しません。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Open Time Daily Window: trades during a specific time window using
/// EMA direction for entry. Closes position at the end of window.
/// </summary>
public class OpenTimeDailyWindowStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _tradeHour;
	private readonly StrategyParam<int> _windowMinutes;
	private readonly StrategyParam<int> _closeHour;

	private decimal _prevEma;
	private decimal _entryPrice;

	public OpenTimeDailyWindowStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_emaLength = Param(nameof(EmaLength), 20)
			.SetDisplay("EMA Length", "EMA period for direction.", "Indicators");

		_tradeHour = Param(nameof(TradeHour), 10)
			.SetDisplay("Trade Hour", "Hour when trading window opens (UTC).", "Schedule");

		_windowMinutes = Param(nameof(WindowMinutes), 120)
			.SetDisplay("Window Minutes", "Duration of trading window.", "Schedule");

		_closeHour = Param(nameof(CloseHour), 20)
			.SetDisplay("Close Hour", "Hour to close positions (UTC).", "Schedule");
	}

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

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int TradeHour
	{
		get => _tradeHour.Value;
		set => _tradeHour.Value = value;
	}

	public int WindowMinutes
	{
		get => _windowMinutes.Value;
		set => _windowMinutes.Value = value;
	}

	public int CloseHour
	{
		get => _closeHour.Value;
		set => _closeHour.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevEma = 0;
		_entryPrice = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ema = new ExponentialMovingAverage { Length = EmaLength };

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

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

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

		var hour = candle.OpenTime.Hour;
		var minute = candle.OpenTime.Minute;
		var totalMinutes = hour * 60 + minute;
		var tradeStart = TradeHour * 60;
		var tradeEnd = tradeStart + WindowMinutes;
		var closeStart = CloseHour * 60;

		var close = candle.ClosePrice;

		// Close position at close hour
		if (Position != 0 && totalMinutes >= closeStart && totalMinutes < closeStart + 30)
		{
			if (Position > 0)
				SellMarket();
			else
				BuyMarket();
			_entryPrice = 0;
		}

		if (_prevEma == 0)
		{
			_prevEma = emaVal;
			return;
		}

		// Trade within window
		var inWindow = totalMinutes >= tradeStart && totalMinutes < tradeEnd;

		if (Position == 0 && inWindow)
		{
			var emaRising = emaVal > _prevEma;
			var emaFalling = emaVal < _prevEma;

			if (emaRising && close > emaVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (emaFalling && close < emaVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevEma = emaVal;
	}
}