GitHub で見る

Hans123Trader Rangeブレイクアウト戦略

概要

Hans123Trader ストラテジー は、StockSharp の高レベル API を使用して、MetaTrader エキスパート アドバイザー「Hans123Trader v1」を再作成します。システムは、直近の 5 分間の取引レンジのブレイクに基づいて、ストップ注文を 1 日 2 回発行します。価格ステップが小数点以下のピップに対応する外国為替スタイルのシンボルに合わせて調整されています。未決注文は取引日ごとに更新され、カレンダーが切り替わるとオープンポジションは強制的にクローズされます。

コアワークフロー

  1. レンジトラッキング – 5分足ローソク足のローリング80バーウィンドウは、HighestおよびLowestインジケーターを介して維持されます。直近の高値と安値がブレイクアウトレベルを定義します。
  2. セッションのスケジュール – 2 つの独立した取引ウィンドウは、EndSession1EndSession2 によって制御されます。クロックが設定された時間 (分 00) に達すると、ストラテジーは新しい保留中のストップ注文を計算します。
  3. 注文の配置 – 買いストップは検出された高値より 5 ポイント上に送信され、売りストップは検出された安値より 5 ポイント下に送信されます。注文は、MetaTrader の有効期限 23:59 に倣って新しい日が始まるとすぐに削除されます。
  4. ポジション管理 – エントリー後、戦略は要求された最初のストップロス、オプションのテイクプロフィット、およびトレーリングストップを適用します。保護レベルはポイントで表され、商品の PriceStep を使用して価格に変換されます。
  5. 毎日の衛生管理 – 新しい取引日の開始時にポジションがオープンのままの場合、そのポジションは市場でクローズされます。前日の保留中の注文はすべて、新しい注文が準備される前にキャンセルされます。

取引ルール

  • エントリーシグナル
    • 1 日に 2 回のブレークアウト試行: 1 回目は EndSession1、もう 1 回目は EndSession2 (時間はブローカー/サーバー時間)。
    • 買いストップ価格 = HighestHigh + 5 points。売りストップ価格 = LowestLow − 5 points
    • どちらのオーダーも現在の Volume パラメータ (デフォルトは 1) を使用します。
    • 出来高が正でない場合、注文はスキップされます。
  • 終了ロジック
    • 最初のストップロス = エントリー価格 ± InitialStopLoss ポイント (ロングの場合は下、ショートの場合は上)。
    • テイクプロフィット = エントリー価格 ± TakeProfit ポイント (上はロング、下はショート)。
    • トレーリングストップは、終値が少なくとも TrailingStop ポイント以上利益に近づくたびに、保護レベルを強化します。
    • 翌日まで残ったポジションは市場で直ちに決済されます。
  • 注文のメンテナンス
    • 保留中のストップ注文は、暦日ごとの初めにキャンセルされます。
    • 逆指値注文がトリガーされる(またはキャンセル/失敗される)と、内部参照は自動的にクリアされます。

パラメーター

名前 説明
BeginSession1 / BeginSession2 UI の互換性 (開始時間のヒント) のために保存されています。現在の実装は終了時間トリガーに依存しています。
EndSession1 / EndSession2 新しい逆指値注文が準備される時間帯 (0 ~ 23)。分は正確にゼロでなければなりません。
TrailingStop 後続距離 (ポイント単位)。 0 は末尾を無効にします。
TakeProfit テイクプロフィット距離(ポイント単位)。 0 はテイクプロフィットを無効にします。
InitialStopLoss ポイント単位の初期ストップロス距離。 0 は、トレーリングがアクティブにならない限り、保護ストップなしで取引を終了します。
CandleType 80 バー範囲に使用されるローソク足シリーズ (デフォルトは TimeSpan.FromMinutes(5))。
Volume Strategy から継承された戦略ベース ボリューム。

変換メモ

  • MetaTrader ヘルパー関数 OrderSendExtended とグローバル変数ロックは必要ありません。 StockSharp は同時実行性を内部で管理します。
  • マジックナンバーは明示的な順序参照 (_session* フィールド) に置き換えられます。注文ライフサイクル イベントは、注文が終了するとこれらの参照をクリアします。
  • 23:59 に期限切れになる保留中の注文は、新しい日が始まるときにキャンセルすることでエミュレートされます。
  • トレーリング ストップ ロジックは、MetaTrader の買値/売値の代わりにローソク足の終値を使用します。
  • すべてのポイントベースの距離には Security.PriceStep が乗算されます。 PriceStep が設定されていない場合、生のポイント値は絶対価格距離として扱われます。

使用のヒント

  • ポイントから価格への変換と出来高の四捨五入が正確になるように、適切に設定された PriceStepStepPrice、および VolumeStep を商品に割り当てます。
  • 5 分間の履歴データが利用可能であることを確認します。ブレイクアウトレベルは、最新の 80 のローソク足に依存します。
  • EndSession1/EndSession2 を調整して、希望する市場セッション(ロンドン前やニューヨーク休憩前など)に合わせます。
  • ライブ デプロイの前に、デザイナーまたはランナーを使用して、選択した機器に合わせて InitialStopLossTakeProfit、および TrailingStop を最適化します。
  • 複数の戦略が同じポートフォリオを共有する場合は、戦略を StockSharp のリスク管理と組み合わせます。
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>
/// Hans123 breakout strategy. Builds a range from recent highest/lowest prices
/// and enters on breakout above range high or below range low.
/// </summary>
public class Hans123TraderRangeBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _rangeLength;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _entryPrice;
	private decimal _prevHighest;
	private decimal _prevLowest;

	public Hans123TraderRangeBreakoutStrategy()
	{
		_rangeLength = Param(nameof(RangeLength), 20)
			.SetDisplay("Range Length", "Number of candles used to compute the breakout range.", "Breakout");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle series for range detection.", "General");
	}

	public int RangeLength
	{
		get => _rangeLength.Value;
		set => _rangeLength.Value = value;
	}

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

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

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

		_entryPrice = 0;
		_prevHighest = 0;
		_prevLowest = 0;
	}

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

		_entryPrice = 0;
		_prevHighest = 0;
		_prevLowest = 0;

		var highest = new Highest { Length = RangeLength };
		var lowest = new Lowest { Length = RangeLength };

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

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);

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

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

		if (highestValue <= 0 || lowestValue <= 0)
		{
			_prevHighest = highestValue;
			_prevLowest = lowestValue;
			return;
		}

		// Entry on breakout using previous levels
		if (Position == 0 && _prevHighest > 0 && _prevLowest > 0)
		{
			if (candle.ClosePrice > _prevHighest)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
			}
			else if (candle.ClosePrice < _prevLowest)
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
			}
		}

		_prevHighest = highestValue;
		_prevLowest = lowestValue;
	}
}