GitHub で見る

HTH Traderヘッジ戦略

概要

この戦略はMetaTraderエキスパートアドバイザー「HTH Trader」の直接変換版です。4本脚のフォレックスバスケットを取引し、EURUSDとUSDCHF、GBPUSD、AUDUSDのミラーバスケット間の日次平均回帰を捉えようとします。StockSharpポートは元のリスク管理とタイミングルールを維持しながら、マルチ証券取引に高レベルAPIを使用します。

主な特徴:

  • 端末時間の00:05から00:12の間に1日1回ヘッジバスケットをオープンします。
  • EURUSDの過去2日分の日次終値を使用してバスケットの方向を決定します。
  • 4つのインストゥルメントを同時に管理:EURUSD(主要証券)、USDCHF、GBPUSD、AUDUSD。
  • オープン利益をpipsで追跡し、バスケット全体の利益・損失ターゲットをサポートします。
  • バスケットのドローダウンが閾値を超えたときに利益が出ている脚に追加する緊急倍増機能を含みます。
  • 端末時間23:00またはバスケットが設定した利益/損失制限に達したときにすべての取引をクローズします。

データ要件

  • イントラデイロウソク足: 4つのシンボルすべてがIntradayCandleTypeで設定された時間軸(デフォルト5分)のイントラデイロウソク足を配信する必要があります。これらのロウソク足は最新の価格とセッション時計を提供します。
  • 日次ロウソク足: 戦略が最後の2つの完成した日次終値を監視できるように、各シンボルは日次ロウソク足を提供する必要があります。

トレードロジック

  1. 完成した各イントラデイロウソク足の終わりに、戦略は現在のオープン利益を確認します:
    • AllowEmergencyTradingが有効で、総オープン利益≤-EmergencyLossPipsの場合、戦略は現在利益が出ているすべての脚を倍増し、その日のそれ以上の緊急取引を無効にします。
    • UseProfitTargetが有効で、総オープン利益≥ProfitTargetPipsの場合、バスケットは即座にクローズされます。
    • UseLossLimitが有効で、総オープン利益≤-LossLimitPipsの場合、バスケットは即座にクローズされます。
  2. 時計が23:00に達すると、利益に関わらずバスケットはクローズされます。
  3. ポジションが開いておらず、時計が00:05–00:12ウィンドウ内にある場合、戦略は主要シンボル(デフォルトEURUSD)の最後の2つの完成した日次終値を確認します:
    • 日次の変化率がの場合、戦略はオープンします:ロングEURUSD、ロングUSDCHF、ショートGBPUSD、ロングAUDUSD。
    • 変化がの場合、オープンします:ショートEURUSD、ショートUSDCHF、ロングGBPUSD、ショートAUDUSD。
    • 変化がゼロか日次終値が欠落している場合、戦略はその日の取引をスキップします。
  4. すべてのポジションはClosePositionを通じて成行注文でクローズされます。

パラメーター

名前 説明 デフォルト
TradeEnabled 注文の配置を有効または無効にします。 true
ShowProfitInfo ポジションがオープンの間、各更新時にバスケット利益をpipsで記録します。 true
UseProfitTarget ProfitTargetPipsに達したときの自動クローズを有効にします。 false
UseLossLimit LossLimitPipsに達したときの自動クローズを有効にします。 false
AllowEmergencyTrading 緊急倍増機能を許可します。 true
EmergencyLossPips 緊急倍増をトリガーするバスケットドローダウン(pips単位)。 60
ProfitTargetPips UseProfitTargetが有効なときにクローズをトリガーするバスケット利益(pips単位)。 80
LossLimitPips UseLossLimitが有効なときにクローズをトリガーするバスケット損失(pips単位)。 40
TradingVolume 各脚の注文ボリューム。 0.01
Symbol2 2番目の証券(デフォルトUSDCHF)。 null
Symbol3 3番目の証券(デフォルトGBPUSD)。 null
Symbol4 4番目の証券(デフォルトAUDUSD)。 null
IntradayCandleType スケジューリングと価格更新に使用するイントラデイ時間軸。 5分ロウソク足

使用上の注記

  • 開始前に主要証券(Strategy.Security)をEURUSD(または希望するリード通貨ペア)に割り当て、Symbol2Symbol3Symbol4を相関するインストゥルメントにマッピングします。
  • 各証券に有効なPriceStepがあることを確認してください。そうでないとpipsでの利益計算ができず、緊急ロジックは非アクティブのままになります。
  • 緊急倍増機能は現在利益が出ている脚にのみ追加します。損失が出ている脚はドローダウンを増幅させないよう手つかずのままにされます。
  • 実装は成行注文が最新のロウソク足終値近くで執行されると仮定します。正確な計算のために、タイムリーなイントラデイロウソク足を配信するデータフィードに戦略を接続してください。
  • ロジックは1分(または選択した時間軸)ごとの単一バーで駆動されるため、元のMQLのティックバイティック動作は執行タイミングでわずかに異なる場合がありますが、取引のシーケンスと条件は参照エキスパートアドバイザーと一致します。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Hedge strategy simplified from HTH Trader. Trades based on daily close deviation.
/// </summary>
public class HthTraderStrategy : Strategy
{
	private readonly StrategyParam<bool> _tradeEnabled;
	private readonly StrategyParam<bool> _useProfitTarget;
	private readonly StrategyParam<bool> _useLossLimit;
	private readonly StrategyParam<int> _profitTargetPips;
	private readonly StrategyParam<int> _lossLimitPips;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose1;
	private decimal _prevClose2;
	private decimal _entryPrice;
	private decimal _priceStep;

	/// <summary>
	/// Enable automated trading.
	/// </summary>
	public bool TradeEnabled
	{
		get => _tradeEnabled.Value;
		set => _tradeEnabled.Value = value;
	}

	/// <summary>
	/// Enable closing by reaching the profit target.
	/// </summary>
	public bool UseProfitTarget
	{
		get => _useProfitTarget.Value;
		set => _useProfitTarget.Value = value;
	}

	/// <summary>
	/// Enable closing by reaching the loss limit.
	/// </summary>
	public bool UseLossLimit
	{
		get => _useLossLimit.Value;
		set => _useLossLimit.Value = value;
	}

	/// <summary>
	/// Profit target in pips.
	/// </summary>
	public int ProfitTargetPips
	{
		get => _profitTargetPips.Value;
		set => _profitTargetPips.Value = value;
	}

	/// <summary>
	/// Loss limit in pips.
	/// </summary>
	public int LossLimitPips
	{
		get => _lossLimitPips.Value;
		set => _lossLimitPips.Value = value;
	}

	/// <summary>
	/// Candle type for monitoring.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes parameters.
	/// </summary>
	public HthTraderStrategy()
	{
		_tradeEnabled = Param(nameof(TradeEnabled), true)
			.SetDisplay("Trade Enabled", "Allow the strategy to submit orders", "General");

		_useProfitTarget = Param(nameof(UseProfitTarget), true)
			.SetDisplay("Use Profit Target", "Close when profit target is reached", "Risk");

		_useLossLimit = Param(nameof(UseLossLimit), true)
			.SetDisplay("Use Loss Limit", "Close when loss limit is reached", "Risk");

		_profitTargetPips = Param(nameof(ProfitTargetPips), 80)
			.SetDisplay("Profit Target (pips)", "Profit target in pips", "Risk");

		_lossLimitPips = Param(nameof(LossLimitPips), 40)
			.SetDisplay("Loss Limit (pips)", "Loss limit in pips", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for monitoring", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose1 = 0m;
		_prevClose2 = 0m;
		_entryPrice = 0m;
		_priceStep = 0m;
	}

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

		_priceStep = Security?.PriceStep ?? 0.0001m;
		if (_priceStep <= 0m)
			_priceStep = 0.0001m;

		SubscribeCandles(CandleType)
			.Bind(ProcessCandle)
			.Start();
	}

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

		if (!TradeEnabled)
			return;

		// Check exit conditions
		if (Position != 0 && _entryPrice > 0m)
		{
			var priceDiff = Position > 0
				? candle.ClosePrice - _entryPrice
				: _entryPrice - candle.ClosePrice;

			var pipsDiff = priceDiff / _priceStep;

			if (UseProfitTarget && pipsDiff >= ProfitTargetPips)
			{
				if (Position > 0) SellMarket();
				else BuyMarket();
				_entryPrice = 0m;
				return;
			}

			if (UseLossLimit && pipsDiff <= -LossLimitPips)
			{
				if (Position > 0) SellMarket();
				else BuyMarket();
				_entryPrice = 0m;
				return;
			}
		}

		// Entry logic based on daily close deviation
		if (Position == 0 && _prevClose1 > 0m && _prevClose2 > 0m)
		{
			var deviation = (100m * _prevClose1 / _prevClose2) - 100m;

			if (deviation > 0.1m)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
			}
			else if (deviation < -0.1m)
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
			}
		}

		_prevClose2 = _prevClose1;
		_prevClose1 = candle.ClosePrice;
	}
}