GitHub で見る

Back Kick戦略

Back Kick戦略は、MetaTrader 5のエキスパートアドバイザー Back kick.mq5 から変換されたヘッジ型ブレイクアウトシステムです。すべてのバーのクローズ時に買いと売りの両方の成行ポジションを開くことで、継続的に両サイドのエクスポージャーを維持します。各レッグはpipで表現された対称的なストップロスとテイクプロフィット距離で保護されています。StockSharpポートは、集計されたネットポジションに依存するのではなく、状態を手動で追跡することで、ペアリングされたポジションを独立して保持します。

トレードロジック

  1. 設定された時間軸のロウソク足を購読します。ロウソク足が確定してアクティブなヘッジレッグがない場合、新しいエントリーペアを要求します。
  2. 同じボリュームを使用して成行買いと成行売りの注文を即座に送信します。各レッグはpip距離から変換された独自のストップロスとテイクプロフィットオフセットを保持します。
  3. レベル1データから最良の買値/売値を監視します。レッグが保護価格に達した場合、成行注文で閉じられ、反対のレッグは独自の出口がトリガーされるまでアクティブのままです。
  4. 両方のレッグがフラットになった後、戦略はヘッジを再作成する前に次の確定ロウソク足を待ちます。

この動作は、急激な価格「キック」を捉えるために両方向に常に再エントリーする元のエキスパートを反映しています。

パラメーター

名前 説明 デフォルト値 注意事項
OrderVolume 各ヘッジレッグに使用するボリューム。 0.1 楽器の VolumeStep に正規化され、MinVolume/MaxVolume を尊重する必要があります。
StopLossPips pip単位のストップロス距離。 50 両方のレッグの保護ストップを無効化するには 0 に設定。
TakeProfitPips pip単位のテイクプロフィット距離。 140 保護テイクプロフィットを無効化するには 0 に設定。
CandleType 新しいヘッジペアをトリガーする時間軸。 15m 選択した証券がサポートする任意の TimeFrame を受け入れます。
LogDiagnostics エントリーと出口に関する詳細なログを有効にします。 false フィルシーケンスのデバッグに役立ちます。

実装上の注意事項

  • pip変換 – 元のEAは3/5桁のシンボルのpipサイズを調整します。StockSharpポートは必要に応じて価格ステップを 10 で乗算することでこれを複製します。
  • 手動ヘッジモデル – StockSharpはネットポジションを使用するため、戦略はレッグごとの状態(PositionState)を保持し、出口のために明示的な成行注文を送信します。これにより、ポジションをネットする証券会社と互換性を保ちながら、MT5のヘッジアカウントモードに似た動作が可能になります。
  • リスク管理 – ストップロスとテイクプロフィットレベルはオプションです。いずれかが無効になっている場合、そのレッグは反対の保護レベルに達したときか、外部管理によってのみ閉じられます。
  • 保護サービス – カスタム出口ロジックが実装されていても、予期しない切断を監視するためにフレームワークが StartProtection() を呼び出します。

使用方法

  1. 信頼性の高いレベル1データ(買値/売値)と希望の時間軸ロウソク足を持つ証券に戦略をアタッチします。
  2. リスクプロファイルに従ってpip距離と取引ボリュームを設定します。
  3. 戦略を開始します。ヘッジペアを送信する前に次のロウソク足確定を待ちます。
  4. ログまたはチャートを監視して、各レッグが独立してどのように出口するかを観察します。

MT5バージョンとの違い

  • リスクパーセンテージに基づくマネーマネジメントは移植されていません。取引サイズの制御には OrderVolume を使用してください。
  • StockSharpはポートフォリオポジションを集計するため、戦略は内部のブックキーピングを通じてヘッジを模倣します。これにより、ポジションをネットする証券会社と互換性を保ちながら、元のエキスパートに近い動作が確保されます。
  • 証券会社固有のフリーズ/ストップレベルチェックは省略されています。代わりに、ボリューム正規化ルーティンは取引所の制限が違反された場合に説明的な例外をスローします。

ファイル

  • CS/BackKickStrategy.cs – StockSharpの高レベルAPIを使用した戦略実装。
  • README.md – 英語のドキュメント(このファイル)。
  • README_ru.md – ロシア語のドキュメント。
  • README_zh.md – 中国語のドキュメント。
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 BackKickStrategy : 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 BackKickStrategy()
	{
		_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;
	}
}