GitHub で見る

自動取引スケジューラ戦略

概要

AutoTrading Scheduler 戦略は、MetaTrader の「AutoTrading」スイッチを切り替える EarnForex MetaTrader エキスパート アドバイザーを複製します。 StockSharp ポートは、ユーザー定義の時間枠の外でアカウントをフラットに保ち、時計が許容間隔内に戻ると取引を再開します。すべての構成は、曜日ごとに 1 つずつ、読み取り可能な文字列を通じて実行されます。

このモジュールは意図的にシグナルに依存せず、独自に新しい取引を開始しません。代わりに、ホスト戦略の取引状態を監視します。スケジューラが自動取引を無効にすると、すべてのアクティブな注文がキャンセルされ、オプションで現在のポジションが平坦化され、ホスト アプリケーションが反応できるように AddInfoLog を通じてイベントが記録されます。

オリジナルロジック

  • 平日ごとに複数の期間を含む永続的なタイムテーブルをロードします。
  • ローカルまたはブローカー/サーバーのタイムベースをサポートします。
  • 内部タイマーにより毎秒スケジュールを確認します。
  • 時計が現在の平日のすべての範囲外にある場合、自動取引が無効になり、オプションですべての開いている取引と保留中の注文を閉じることができます。
  • クロックが再び許可されたスパンに入ると、自動取引が再び有効になります。

実装メモ

  • StockSharp バージョンは、解析されたスケジュールをメモリに保存し、ユーザーがいずれかのテキスト パラメータを編集するたびにスケジュールを再計算します。
  • 期間は複数の形式を受け入れます: 9-1209:30-16:0021.15-23.45。分はオプションで、省略するとデフォルトで 00 になります。複数のスパンはカンマで区切ります。
  • 終了が 00:00 に等しい範囲は、午前 0 時までアクティブのままです (例: 22-0 は、22:00:00 から 23:59:59 までを意味します)。 0-0 を使用すると、一日中取引が可能になります。
  • 終了が開始よりも短い期間は、元のエキスパート アドバイザからのヘルパー ロジックを反映して、自動的に翌日に折り返されます。
  • 応答性とリソース使用量のバランスをとるために、タイマーは 5 秒ごとに実行されます。

パラメーター

名前 種類 デフォルト 説明
SchedulerEnabled bool false タイムテーブルを起動するマスタースイッチ。無効にすると、戦略が取引に干渉することはありません。
ReferenceClock TimeReference Local ローカル マシンの時計と、コネクタによって提供される交換/サーバー時間のどちらかを選択します。
ClosePositionsBeforeDisable bool true スケジューラが自動取引を無効にすると、まずすべてのアクティブな注文がキャンセルされ、現在のポジションがフラットになります。
MondaySchedule string "" 月曜日の取引間隔のカンマ区切りリスト。
TuesdaySchedule string "" 火曜日の取引間隔のカンマ区切りリスト。
WednesdaySchedule string "" 水曜日の取引間隔のカンマ区切りリスト。
ThursdaySchedule string "" 木曜日の取引間隔のカンマ区切りリスト。
FridaySchedule string "" 金曜日の取引間隔のカンマ区切りリスト。
SaturdaySchedule string "" 土曜日の取引間隔のカンマ区切りリスト。
SundaySchedule string "" 日曜日の取引間隔のカンマ区切りリスト。

すべてのスケジュール パラメータは同じ構文を受け入れます。例: "09-12, 13:30-17:45, 22-0"

使用法

  1. 戦略を目的の証券またはポートフォリオに添付します。
  2. 取引したい日の時間範囲を 1 つ以上入力します。一日中取引を禁止するには、1 日を空けておきます。
  3. SchedulerEnabled = true を設定してスケジューラを有効にします。
  4. ClosePositionsBeforeDisable を使用してポジションを自動的にフラット化するかどうかを決定します。
  5. ログ出力を監視します。各トグルは、理由 (ウィンドウが開いているか閉じているか) を含むメッセージを書き込みます。

現在時刻が許容範囲内にある場合、戦略は IsAutoTradingEnabled = true を設定します。すべての範囲外では、プロパティは false になり、モジュールは約定待ち注文をキャンセルし、設定されている場合はポジションを平坦化し、アクションを記録します。

既知の制限事項

  • この戦略は、それに付随する単一の証券のみを監視します。マルチシンボル ポートフォリオには、複数のスケジューラ インスタンスまたはカスタム コーディネーターが必要です。
  • 異なる粒度が必要な場合は、ソース コード (TimeSpan.FromSeconds(5)) 内でタイマー間隔を調整できます。
  • この戦略では、スケジュールをディスクに保存しません。永続性が必要な場合は、ホスト アプリケーションのパラメータ ストレージ メカニズムを使用します。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// AutoTrading Scheduler strategy: Momentum indicator crossover.
/// Buys when Momentum crosses above 100, sells when crosses below 100.
/// </summary>
public class AutoTradingSchedulerStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<decimal> _momentumLevel;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _prevMom;
	private int _candlesSinceTrade;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
	public decimal MomentumLevel { get => _momentumLevel.Value; set => _momentumLevel.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public AutoTradingSchedulerStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_momentumPeriod = Param(nameof(MomentumPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Momentum Period", "Momentum period", "Indicators");
		_momentumLevel = Param(nameof(MomentumLevel), 101m)
			.SetDisplay("Momentum Level", "Momentum threshold", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevMom = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevMom = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
		var momentum = new Momentum { Length = MomentumPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(momentum, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (_hasPrev)
		{
			if (_prevMom < MomentumLevel && momValue >= MomentumLevel && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (_prevMom > 200m - MomentumLevel && momValue <= 200m - MomentumLevel && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevMom = momValue;
		_hasPrev = true;
	}
}