GitHub で見る

聖杯エキスパートMA

概要

Grail Expert MA は、MetaTrader 4 エキスパート アドバイザー _GrailExpertMAV1_0 の StockSharp ポートです。システムは、最近の高値/安値チャネルを超えた新たなブレイクアウトを検索し、動きに加わる前に反発を待ちます。典型的な価格の指数移動平均は、方向性のバイアスを提供します。取引は、EMA が、完了した最後の 2 つのローソク足で設定可能な数のピップを獲得または損失した場合にのみ許可されます。リスク管理は、ピップベースのストップロスとテイクプロフィットディスタンスを備えたオリジナルのエキスパートを反映しており、ポジションがアクティブである間は新しいエントリーを無視します。

戦略ロジック

EMA スロープ トレンド フィルター

  • 典型的な価格 ((高値 + 安値 + 終値)/3) に基づいて計算された EMA は、すべてのバーの終値で評価されます。
  • 最後の 2 つの EMA 値の差は、EMA Slope (pips) のしきい値 (シンボルのピップ サイズを使用して価格に変換) を超える必要があります。
  • 正の傾きは長い引き戻しを許可し、負の傾きは短い引き戻しを許可し、平坦な傾きは取引をブロックします。

ブレイクアウトレンジの追跡

  • この戦略は、最後に完了した Range Period バー全体で最高値と最低値を維持します。
  • これらのレベルはチャネルを形成し、その高さはプルバック ロジックに十分な距離を作らない浅い動きを拒否するために使用されます。

エントリー準備

  • 現在の足が保存された範囲を超えて新たな高値を記録すると、潜在的なロングエントリー価格が High - Breakout Buffer - Take Profit ピップで計算されます。
  • 現在の足が保存された範囲を下回る新安値を記録すると、潜在的なショートエントリー価格が Low + Breakout Buffer + Take Profit ピップで計算されます。
  • 元の EA では、新しい極値と範囲の反対側の間の距離が少なくとも 2 * Breakout Buffer + Take Profit である必要がありました。ポートは同じ検証を維持し、スプレッドが小さすぎる場合はエントリを破棄します。

エントリートリガー

  • 準備された価格はバーの残りの部分ではアクティブのままです。 EMA の傾きが正の場合、足内安値が保存されたロングエントリー価格に達するか、それを下回るとロングが実行されます。
  • EMA の傾きが負の場合に、足内高値が保存されたショートエントリー価格に達するかそれを超えると、ショートが実行されます。
  • 一度にオープンできる取引は 1 つだけです。 MQL の動作に一致するように、注文が送信されるとすぐに、ポートは両方の保留中のエントリ価格をクリアします。

出口管理

  • ロングポジションでは、Entry - Stop Loss ピップでストップを使用し、Entry + Take Profit ピップで利益目標を使用します (ゼロはそれぞれのレベルを無効にします)。
  • ショートポジションは計算を反映します(ストップは上、ターゲットは下)。
  • 元のティックロジックのバーベースの近似と一致し、ローソク足の極値が保護レベルに達すると出口がトリガーされます。

追加の安全対策

  • 保留中のエントリは、新しいローソク足が閉じるときに更新された範囲の外に出るたびにクリアされます。
  • すべてのピップ距離は、機器のティック サイズに自動的に適応します (5 桁の FX シンボルは 1 ピップを 10 ティックにマッピングします)。
  • EMA がまだ形成されていない場合、または範囲バッファーに十分な履歴がない場合、十分なデータが利用可能になるまでストラテジーはアイドル状態のままになります。

パラメーター

  • 注文量 – 成行注文のロット/契約の取引量。
  • テイクプロフィット (pips) – 固定利益目標までの距離。無効にするには、0 に設定します。
  • ストップロス (pips) – 保護ストップまでの距離。無効にするには、0 に設定します。
  • レンジ期間 – ブレイクアウトチャネルの測定に使用される完了したローソク足の数。
  • EMA 期間 – 典型的な価格に適用される指数移動平均の長さ。
  • EMA 勾配 (pips) – エントリを有効にするために必要な、連続する EMA 値間の最小ピップ上昇/下降。
  • ブレイクアウトバッファ (pips) – プルバックエントリーを準備する前に、フレッシュエクストリームからさらに距離を置きます。
  • ローソク足タイプ – データ フィードから要求された時間枠 (デフォルト: 1 時間ローソク足)。

実装メモ

  • この戦略は、生のローソク足の更新 (部分的な状態を含む) を使用して、元のバー内高値/安値監視をエミュレートします。
  • EMA 値は、終了したローソク足でのみ処理され、1 バーと 2 バーのシフトで MQL iMA 呼び出しを複製します。
  • 履歴範囲は、ロジックをソースに忠実に保ちながら、コストのかかる再スキャンを回避するために、インジケーターのルックアップではなく、有界キューを使用して追跡されます。
  • Python のバージョンは提供されていません。 API パッケージには C# 実装のみが含まれています。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Grail Expert MA: SMA crossover with ATR stops.
/// </summary>
public class GrailExpertMaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastSmaLength;
	private readonly StrategyParam<int> _slowSmaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public GrailExpertMaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastSmaLength = Param(nameof(FastSmaLength), 10)
			.SetDisplay("Fast SMA", "Fast SMA period.", "Indicators");

		_slowSmaLength = Param(nameof(SlowSmaLength), 40)
			.SetDisplay("Slow SMA", "Slow SMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastSmaLength
	{
		get => _fastSmaLength.Value;
		set => _fastSmaLength.Value = value;
	}

	public int SlowSmaLength
	{
		get => _slowSmaLength.Value;
		set => _slowSmaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;

		var fastSma = new SimpleMovingAverage { Length = FastSmaLength };
		var slowSma = new SimpleMovingAverage { Length = SlowSmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastSma, slowSma, atr, ProcessCandle)
			.Start();

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

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

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (fastVal < slowVal && _prevFast >= _prevSlow)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (close <= _entryPrice - atrVal * 2m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (close >= _entryPrice + atrVal * 2m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (fastVal < slowVal && _prevFast >= _prevSlow)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}