GitHub で見る

アダプティブ グリッド MT4 (StockSharp ポート)

概要

この戦略は、StockSharp のハイレベル API の「アダプティブ グリッド Mt4」エキスパート アドバイザーを再作成します。対称グリッドをドロップします。 現在のローソク足の終値付近でストップ注文と売りストップ注文を行います。グリッド距離は、平均真範囲 (ATR) から導出されます。 したがって、市場の変動に適応します。各未決注文は、設定可能な数のローソク足の後に期限切れになり、 横向き市場での注文帳は整然としています。

エントリー注文が約定されると、ストラテジーは計算された価格で一致するテイクプロフィット注文とストップロス注文を即座に登録します。 グリッドを生成した ATR スナップショットから。保護命令は入力されたエントリと 1 対 1 で行われ、実行されるまで存続します。 または手動でキャンセルします。

パラメーター

パラメータ 説明
GridLevels 市場の上下のストップ注文の数。 EA の nGrid 入力に相当します。
TimerBars 保留中のエントリーがキャンセルされるまでに完了したローソク足の数 (MT4 nBars)。
PriceOffsetMultiplier 現在の価格 (Poffset) からの初期オフセットに適用される ATR 乗数。
GridStepMultiplier 連続するグリッド レベル間の間隔に使用される ATR 乗数 (Pstep)。
StopLossMultiplier 各注文に付加されたストップロスの距離を定義する ATR 乗数 (StopLoss)。
TakeProfitMultiplier ATR テイクプロフィットの距離を定義する乗数 (TakeProfit)。
AtrPeriod ATR の平均期間。スクリプトのハードコーディングされた値 14 をミラーリングします。
OrderVolume すべての未決注文に使用されるボリューム (MT4 Lot)。
CandleType グリッドの再計算を行う時間枠 (Wtf)。

取引ロジック

  1. 設定された CandleType のキャンドルを購読し、ATR をフィードします(14)。
  2. 完成したキャンドルごとに:
    • 内部バーカウンターを進め、TimerBarsを超えた保留中のグリッド注文をキャンセルします。
    • ATR が形成されていない場合、グリッド注文がまだアクティブである場合、または戦略がすでにポジションを保持している場合は、以降の処理をスキップします。
    • ブレイクアウト オフセット、グリッド間隔、ストップロス、テイクプロフィット距離を ATR * multiplier 値として計算します。
    • ローソク足の終値付近で GridLevels ペアの買いストップ注文と売りストップ注文を配置し、価格を正規化します Security.ShrinkPrice は、商品のティックサイズを尊重します。
  3. エントリがいっぱいになると、追跡されたグリッド リストからそのエントリを削除し、対応する保護命令を生成します。
    • ロングエントリーは SellStop のストップロスと SellLimit のテイクプロフィットを受け取ります。
    • ショートエントリーは BuyStop のストップロスと BuyLimit のテイクプロフィットを受け取ります。
  4. 保護命令は OnOrderChanged 経由で監視されるため、完了またはキャンセルされたエントリは追跡から削除されます。 リスト。

注意事項

  • グリッドは、オープンポジションがなく、既存のグリッド注文がすべて期限切れになった場合にのみ再構築され、What() のロジックと一致します。 オリジナルの EA。
  • 価格は生の Bid/Ask ティックではなくローソク足の終値から計算されます。これにより、実装がキャンドル駆動に保たれます 同時に、市場全体で同じ対称レイアウトを作成します。
  • グリッドに使用される ATR のスナップショットは、MetaTrader のチケットごとのストップとテイクプロフィットを模倣する保護命令にも使用されます 価値観。
  • リクエストに一致する Python 翻訳はまだありません。
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Adaptive grid strategy using ATR-based breakout levels.
/// Simplified from the "Adaptive Grid Mt4" expert advisor to use market orders.
/// </summary>
public class AdaptiveGridMt4Strategy : Strategy
{
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _breakoutMultiplier;
	private readonly StrategyParam<DataType> _candleType;

	private AverageTrueRange _atr;
	private decimal? _prevClose;
	private decimal? _prevAtr;
	private decimal _stopPrice;
	private decimal _takeProfitPrice;

	/// <summary>
	/// ATR averaging period.
	/// </summary>
	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	/// <summary>
	/// Breakout threshold in ATR multiples.
	/// </summary>
	public decimal BreakoutMultiplier
	{
		get => _breakoutMultiplier.Value;
		set => _breakoutMultiplier.Value = value;
	}

	/// <summary>
	/// Candle type used to drive calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public AdaptiveGridMt4Strategy()
	{
		_atrPeriod = Param(nameof(AtrPeriod), 20)
		.SetGreaterThanZero()
		.SetDisplay("ATR Period", "Number of candles used for ATR smoothing", "Indicators");

		_breakoutMultiplier = Param(nameof(BreakoutMultiplier), 2.5m)
		.SetGreaterThanZero()
		.SetDisplay("Breakout Multiplier", "ATR multiplier for breakout threshold", "Grid");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
		.SetDisplay("Candle Type", "Candle type used to trigger grid recalculation", "General");
	}

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

		_prevClose = null;
		_prevAtr = null;
		_stopPrice = 0;
		_takeProfitPrice = 0;

		_atr = new AverageTrueRange { Length = AtrPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
		.Bind(_atr, ProcessCandle)
		.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

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

		if (!_atr.IsFormed || atrValue <= 0)
		{
			_prevClose = candle.ClosePrice;
			_prevAtr = atrValue;
			return;
		}

		// Check protective stops
		if (Position > 0)
		{
			if (_stopPrice > 0 && candle.LowPrice <= _stopPrice)
			{
				SellMarket(Math.Abs(Position));
				_stopPrice = 0;
				_takeProfitPrice = 0;
			}
			else if (_takeProfitPrice > 0 && candle.HighPrice >= _takeProfitPrice)
			{
				SellMarket(Math.Abs(Position));
				_stopPrice = 0;
				_takeProfitPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (_stopPrice > 0 && candle.HighPrice >= _stopPrice)
			{
				BuyMarket(Math.Abs(Position));
				_stopPrice = 0;
				_takeProfitPrice = 0;
			}
			else if (_takeProfitPrice > 0 && candle.LowPrice <= _takeProfitPrice)
			{
				BuyMarket(Math.Abs(Position));
				_stopPrice = 0;
				_takeProfitPrice = 0;
			}
		}

		if (_prevClose is not decimal prevClose || _prevAtr is not decimal prevAtr || prevAtr <= 0)
		{
			_prevClose = candle.ClosePrice;
			_prevAtr = atrValue;
			return;
		}

		var threshold = prevAtr * BreakoutMultiplier;
		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// Breakout up
		if (candle.ClosePrice > prevClose + threshold && Position <= 0)
		{
			BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
			_stopPrice = candle.ClosePrice - atrValue * 3;
			_takeProfitPrice = candle.ClosePrice + atrValue * 4;
		}
		// Breakout down
		else if (candle.ClosePrice < prevClose - threshold && Position >= 0)
		{
			SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
			_stopPrice = candle.ClosePrice + atrValue * 3;
			_takeProfitPrice = candle.ClosePrice - atrValue * 4;
		}

		_prevClose = candle.ClosePrice;
		_prevAtr = atrValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_atr = null;
		_prevClose = null;
		_prevAtr = null;
		_stopPrice = 0;
		_takeProfitPrice = 0;

		base.OnReseted();
	}
}