GitHub で見る

Gann Grid戦略

この戦略は、MQL/25065/Gann Grid.mq4から元のGann GridエキスパートアドバイザーをStockSharpの高水準APIにポートします。元のスクリプトは手動チャートオブジェクトと複数の時間軸フィルターを混在させていましたが、C#バージョンは全体的なワークフローを維持しながら、チャート由来のデータをインジケーター駆動のロジックに置き換え、無人で実行できるようにします。

取引ロジック

  1. 合成GannグリッドAnchorPeriodローソク足にわたる最高値と最安値が、MetaTraderで手動に描画されていた価格レベルを近似します。最高値を超えたブレイクアウトはロングセットアップを、最安値を下回るブレイクダウンはショートをトリガーします。
  2. トレンド確認 – 上位時間軸(TrendCandleType)の高速・低速線形加重移動平均がブレイクアウトの方向に一致している必要があります。
  3. モメンタムフィルター – モメンタムインジケーターと現在の価格(同じく上位時間軸)の間のパーセント距離がMomentumThresholdを超える必要があり、十分な加速があることを確保します。
  4. MACD確認 – 別のローソク足ストリーム(MacdCandleType)がMACD(デフォルト12/26/9)を駆動します。MACDラインはトレード方向と同じゼロとシグナルラインの側にある必要があります。
  5. リスク管理 – 対称的なストップロスとテイクプロフィットのオフセットがエントリー価格から適用されます。オプションのブレイクイーブンとトレーリングモジュールがMQL実装の資本保護ブロックを再現します。

元の「新しいバー」チェックと一致するように完成したローソク足のみが処理されます。

MQLバージョンとの違い

  • MetaTraderのコードは手動で描画したGANNGRIDオブジェクトを期待していました。ポートはこれをローリングの最高/最安値インジケーターに置き換え、自動テストのためにロジックを決定論的にします。
  • MetaTraderのMomentumは100を中心としています。StockSharpのMomentumは価格差を出力するため、ストラテジーはMomentumThresholdと比較する前に現在のクローズのパーセンテージに変換します。
  • MQLスクリプトの通知(メール、プッシュ)とグラフィック操作は省略されます。
  • StockSharpストラテジーはターミナルレベルの注文ではなくポジションを管理するため、リスク管理は既存注文の変更ではなくマーケットエグジットを使用します。

パラメーター

名前 デフォルト 説明
CandleType DataType 5分足時間軸 ブレイクアウトを定義するプライマリローソク足。
TrendCandleType DataType 15分足時間軸 LWMAとモメンタムフィルターに使用する上位時間軸。
MacdCandleType DataType 1日足時間軸 MACD確認フィルターを駆動するローソク足ストリーム。
FastMaPeriod int 6 上位時間軸の高速LWMA長。
SlowMaPeriod int 85 上位時間軸の低速LWMA長。
MomentumPeriod int 14 モメンタムルックバック長。
MomentumThreshold decimal 0.3 取引に必要な最小モメンタム偏差(パーセント)。
AnchorPeriod int 100 合成Gannグリッドを形成するプライマリローソク足の数。
TakeProfitOffset decimal 0.005 エントリー価格からの絶対テイクプロフィット距離。
StopLossOffset decimal 0.002 エントリー価格からの絶対ストップロス距離。
EnableTrailing bool true トレーリングストップ管理を有効にします。
TrailingActivation decimal 0.003 トレーリングストップが価格を追跡し始める前に必要な利益。
TrailingStep decimal 0.0015 ローカル高値とトレーリングストップの間の距離。
EnableBreakEven bool true ブレイクイーブンへの移動ロジックを有効にします。
BreakEvenTrigger decimal 0.0025 ブレイクイーブンが起動される前に必要な利益。
BreakEvenOffset decimal 0.0 ブレイクイーブンでクローズするときにエントリー価格に適用されるオフセット。
MacdFastPeriod int 12 MACD内の高速EMA長。
MacdSlowPeriod int 26 MACD内の低速EMA長。
MacdSignalPeriod int 9 MACD内のシグナルEMA長。

すべてのオフセットは絶対価格距離です。シンボルのティクサイズに合わせて調整してください(例:0.001 ≈ 5桁のFX相場で10ポイント)。

使用方法

  1. ストラテジーをセキュリティにアタッチし、ローソク足のタイプを設定します。単一の時間軸が必要な場合は、複数のフィルターに同じローソク足タイプを使用することが可能です。
  2. インストゥルメントのボラティリティに合わせてAnchorPeriodと価格オフセットを調整します。
  3. リスクポリシーに従ってブレイクイーブン/トレーリングを有効または無効にします。
  4. ストラテジーを起動します;必要なローソク足ストリームに自動的にサブスクライブし、マーケット注文でポジションを管理します。

ファイル

  • CS/GannGridStrategy.cs – ストラテジーの実装。
  • 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 GannGridStrategy : 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 GannGridStrategy()
	{
		_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;
	}
}