GitHub で見る

Zone Recovery Button戦略

Zone Recovery Button戦略は、MetaTrader エキスパートアドバイザー "ZONE RECOVERY BUTTON VER1" (MQL/25347) を直接変換したものです。元のロボットは、ヘッジされたバスケットを開始するためにチャート上の BUY/SELL ボタンに依存していました。この StockSharp 版では、手動パネルをパラメーターに置き換えながら、回復ロジック、金額/割合 take-profit、通貨単位の trailing stop、equity-stop 保護を維持しています。

戦略が開始方向を受け取ると、初期の成行注文を開きます。価格が設定されたゾーン幅を横切るたびに、システムは増加した数量で反対方向の取引を積み上げます。バスケットは、参照 take-profit に到達したとき、含み益が設定された金額/割合目標に達したとき、trailing stop が利益を過度に戻したとき、または equity-stop しきい値に違反したときに閉じられます。

取引ルール

  1. 開始方向 - BUY または SELL ボタンの押下をエミュレートします。戦略はデータを受け取り、取引が許可されるとすぐに最初の注文を開きます。バスケットを閉じた後、同じ方向で自動的に再開できます。
  2. ゾーンリカバリー - 各回復ステップでアルゴリズムは方向を交互に変えます。ロングサイクルでは、価格が Base Price - Zone Width を下回ると売り、その後、市場が基準を上回って戻ると再び買います。ショートサイクルではロジックが反転します。
  3. 数量スケーリング - 追加の各ヘッジは、前回数量を乗算するか、固定増分を加え、EA の "Lots"/"Multiply" 設定を再現します。
  4. Take-profit制御 - バスケットは次の条件で閉じられます。
    • 参照価格から測定した pip ベースの take-profit。
    • 口座通貨での金額目標。
    • 現在のポートフォリオ値から計算した割合目標。
    • 含み益がしきい値を超えると利益を固定し、その後許容 drawdown を超えて戻すと閉じる trailing ロジック。
    • 現在の含み損をサイクル中に観測された最高 equity と比較する緊急 equity-stop。

パラメーター

パラメーター デフォルト 説明
CandleType TimeSpan.FromMinutes(5) 価格変動の監視に使用するローソク足タイプ。
StartDirection Buy 初期サイクル方向 (BUY/SELL/NONE)。
AutoRestart true 前のバスケットが閉じた後、新しいサイクルを自動的に再開します。
TakeProfitPips 200 基準価格と pip take-profit 目標の間の pip 距離。
ZoneRecoveryPips 10 反対方向の次のヘッジを発動する pip 距離。
InitialVolume 0.01 最初の取引の数量 (ロット)。
UseVolumeMultiplier true 有効な場合、各ヘッジは前回数量を乗算します。無効な場合は VolumeIncrement が追加されます。
VolumeMultiplier 2 UseVolumeMultipliertrue のときに適用される乗数。
VolumeIncrement 0.01 UseVolumeMultiplierfalse のときの数量増分。
MaxTrades 100 バスケット内の最大取引数。
UseMoneyTakeProfit false 含み益が MoneyTakeProfit を超えたときの決済を有効にします。
MoneyTakeProfit 40 口座通貨での利益目標。
UsePercentTakeProfit false 含み益が残高の PercentTakeProfit パーセントを超えたときの決済を有効にします。
PercentTakeProfit 10 現在のポートフォリオ値に対する割合利益目標。
EnableTrailing true 通貨単位の利益 trailing を有効にします。
TrailingProfitThreshold 40 trailing を起動する利益水準。
TrailingDrawdown 10 バスケットを閉じる前に許容されるピーク含み益からの drawdown。
UseEquityStop true 緊急 equity stop を有効にします。
TotalEquityRiskPercent 1 フラット化前に許容される最大含み損 (equity 高値に対する割合)。

注意事項

  • 戦略は PriceStepStepPrice の値を提供する任意の商品で動作します。これらのパラメーターは、pip 距離を価格および通貨単位へ変換するために必要です。
  • StockSharp はネットポジションモデルを使うため、ヘッジグリッドは内部でシミュレートされます。戦略は MetaTrader の利益計算を再現するため、独自の取引ステップ一覧を保持します。
  • trailing ロジックはアクティブなバスケットの含み益で動作します。注文ベースの trailing stop は使用しません。
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 ZoneRecoveryButtonStrategy : 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 ZoneRecoveryButtonStrategy()
	{
		_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;
	}
}