GitHub で見る

保留中のリミットグリッド戦略(MQL/8147 コンバージョン)

概要

保留制限グリッド戦略は、MetaTrader エキスパートの動作を再現します。 MQL/8147 に保存されています。この戦略は、保留中の指値注文の対称グリッドを構築します。 現在の買値/買値付近。利益を浮かせながらグリッドをアクティブに保ちます 設定された利益目標とドローダウンしきい値の範囲内に留まります。そのうちの 1 つが、 しきい値に違反すると、すべての注文がキャンセルされ、オープンポジションがフラット化され、 新しいアカウント資本をベースラインとして使用してグリッドが再構築されます。

取引ロジック

  1. レベル 1 データを購読して、最良の買値と売値を追跡します。
  2. 初めてライブデータを受信したときにアカウントの資産を取得し、次のように保存します。 セッションのベースライン。
  3. LevelsPerSide の売り制限を市場より上に設定し、同じ数の買いを追加します 市場を下回る限界。グリッド レベル間の距離は次によって制御されます。 GridStepPoints が商品価格ステップに変換されました。
  4. 未決注文が約定されたときに新しい注文を再発行することなく、保留中の注文を保持します。の グリッドは完全にリセットされた後にのみ再作成されます。
  5. 変動損益を継続的に監視します。
    • 利益が ProfitTargetCurrency に達したら、すべてのエクスポージャを閉じてリセットします。
    • ドローダウンが MaxDrawdownCurrency を超えた場合は、ブックを平らにしてリセットしてください。
  6. リセットされるたびに、ベースライン エクイティが再度取得され、グリッドが再構築されます。 最新の買値/売値スナップショットを使用します。

パラメーター

パラメータ 説明
ProfitTargetCurrency グリッドの完全なリセットをトリガーする純利益 (アカウント通貨)。
MaxDrawdownCurrency すべての露光が終了する前の最大許容浮遊損失。
GridStepPoints ブローカー ポイントで表される連続するグリッド レベル間の距離。
LevelsPerSide 市場の上下で作成された未決注文の数。
OrderVolume 未決の各指値注文に割り当てられたボリューム。

リスク管理

この戦略では、オーダーごとのストップやターゲットは付加されません。代わりに、それを監督します。 合計された損益。 RequestFlatten ヘルパーは保留中の注文をキャンセルし、 成行注文 (ClosePosition 経由) を使用して、オープンなエクスポージャーを削除します。その後 平坦化が完了すると、グリッド状態とベースライン エクイティが配置前にリセットされます。 新しい注文。

注意事項

  • 価格は為替を考慮して Security.ShrinkPrice を通じて正規化されます 価格ステップ。
  • MetaTrader の「ポイント」値は、機器 PriceStep を分析することによってエミュレートされます。 4 桁と 5 桁の引用符と一致します。
  • この戦略では、グリッド注文の再送信を回避し、 元の専門家は、フラグ変数に依存して、すべてのレベルを一意に保つようにしました。 手動または自動リセットが発生します。
using System;

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

namespace StockSharp.Samples.Strategies;

public class PendingLimitGridStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cooldownBars;

	private decimal _prevClose;
	private decimal _prevMid;
	private bool _hasPrev;
	private int _barsSinceLastTrade;

	public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }

	public PendingLimitGridStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 24).SetDisplay("Channel Period", "Grid channel lookback", "Indicators");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
		_cooldownBars = Param(nameof(CooldownBars), 200).SetDisplay("Cooldown Bars", "Minimum bars between trades", "Risk");
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = 0;
		_prevMid = 0;
		_hasPrev = false;
		_barsSinceLastTrade = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		_barsSinceLastTrade = 0;
		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(highest, lowest, ProcessCandle).Start();
	}

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

		var close = candle.ClosePrice;
		var mid = (highest + lowest) / 2;

		if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }

		_barsSinceLastTrade++;

		if (_barsSinceLastTrade >= CooldownBars)
		{
			if (_prevClose <= _prevMid && close > mid && Position <= 0)
			{
				if (Position < 0) BuyMarket();
				BuyMarket();
				_barsSinceLastTrade = 0;
			}
			else if (_prevClose >= _prevMid && close < mid && Position >= 0)
			{
				if (Position > 0) SellMarket();
				SellMarket();
				_barsSinceLastTrade = 0;
			}
		}

		_prevClose = close;
		_prevMid = mid;
	}
}