GitHub で見る

Hoop Master Breakout戦略

概要

  • Vladimir KarputovによるMetaTrader 5エキスパートアドバイザー**「Hoop master 2」**から変換。
  • 現在の価格周辺にブレイクアウトボックスを構築し、新しいローソク足がクローズするたびに買いと売りの両方のストップ注文を準備します。
  • 負けトレード後にロットサイズを倍増させ、利益サイクル後にリセットするMT5の動作を自動的に再現します。

トレードロジック

  1. 設定されたローソク足シリーズをサブスクライブし、完成したローソク足のみを待ちます。新しいローソク足は保留注文を再準備する「ティック」として機能します。
  2. 戦略がフラットの場合:
    • 最後のクローズよりIndentPipsポイント上にbuy stopを配置します。
    • 最後のクローズよりIndentPipsポイント下にsell stopを配置します。
    • インストゥルメントのPriceStepと小数点桁数調整(小数点3桁または5桁の見積もりでは×10)を使用してMetaTraderのpipsを絶対価格単位に変換します。
  3. 各保留注文は独自のストップロスとテイクプロフィットのレベルを保存します。注文が約定すると、反対の注文がキャンセルされ、保存された保護がネイティブの取引所注文で再作成されます(ロングにはSellStop/SellLimit、ショートにはBuyStop/BuyLimit)。
  4. 保護注文がポジションをクローズする場合、重複したエグジットを避けるために残りの添付注文がキャンセルされます。
  5. オプションのトレーリングストップロジックは、価格が少なくともTrailingStopPips進んで改善がTrailingStepPipsを超えると、保護ストップを取引の有利な方向に移動させます。
  6. フラットからフラットへの各取引サイクル後、実現PnLが評価されます。マイナスのサイクルは作業ボリュームをLossMultiplierで乗算します;それ以外の場合、ボリュームはベースVolumeにリセットされます。

パラメーター

パラメーター 説明 デフォルト 備考
Volume 新規保留注文を準備する際に使用するベース注文サイズ。 戦略のVolumeプロパティ LossMultiplierに従って負けサイクル後に倍増します。
StopLossPips MetaTraderのpipsでのストップロス距離。 25 pipサイズヘルパーを使用して価格に変換されます。0はストップを無効にします。
TakeProfitPips MetaTraderのpipsでのテイクプロフィット距離。 70 価格に変換されます。0はターゲットを無効にします。
TrailingStopPips 価格とトレーリングストップの間の距離。 0 トレーリング動作を無効にするには0に設定。
TrailingStepPips トレーリングストップを移動する前の最小改善。 5 TrailingStopPipsがゼロより大きい場合のみ使用されます。
IndentPips 保留注文を準備する際に最後のクローズに追加されるオフセット。 15 ストップ注文が即時の価格ノイズの外に座ることを保証します。
LossMultiplier 損失後の次のサイクルに適用される乗数。 2 MT5 EAからのマーチンゲールスタイルのポジションサイジングを実装します。
CandleType 再準備をトリガーするローソク足タイプ/時間軸。 1時間足時間軸 テストで使用するチャートに合わせて変更します。

資金管理と保護

  • 各約定エントリーは、戦略が切断されても保護が機能するように、ストップロスとテイクプロフィットを実際の取引所注文として即座に再構築します。
  • StartProtection()は起動時に呼び出され、前回の実行から残ったストレイポジションを精算します。
  • トレーリングロジックは市場エグジットを送信する代わりに既存のストップ注文を調整し、MT5の変更と一貫した動作を維持します。

実装ノート

  • StockSharpの高レベルAPIに従います:ローソク足サブスクリプション、エントリーのためのBuyStop/SellStop、テイクプロフィット注文のためのBuyLimit/SellLimit
  • コード内のすべてのテキストコメントは英語であり、外部ドキュメント(このREADMEと翻訳)はユーザーに詳細な説明を提供します。
  • MetaTraderのpip変換はブローカーステップを10倍にすることで小数点桁数シンボル(小数点3桁または5桁)を尊重し、元のEAのm_adjusted_pointロジックと一致します。
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;

public class HoopMasterBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public HoopMasterBreakoutStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}