GitHub で見る

Colibri グリッド マネージャー戦略

概要

Colibri Grid Manager は、MetaTrader 4 Expert Advisor Colibri.mq4 (元のフォルダ MQL/9713) の StockSharp ポートです。この戦略は裁量グリッド取引に焦点を当てています。オンデマンドで階層化された未決注文を準備し、設定されたリスクバジェットを使用して各注文のサイズを設定し、保護エグジットを付加し、さらなる取引を無効にする前に毎日のドローダウン制限を適用します。

取引ロジック

  1. この戦略が開始されると、選択したローソク足シリーズとオーダーブックをサブスクライブして参照価格を追跡し、毎日の利益ベースをリセットし、以前の注文をクリアします。
  2. EnableGrid が true で、位置またはアクティブなグリッド順序が存在しない場合、ストラテジーは許可された方向ごとに新しいグリッドを構築します (AllowBuyAllowSell)。注文は、手動の中心価格を中心に、または明示的な買い/売りエントリーアンカーに相対して配分できます。
  3. 注文タイプ (OrderType) は、グリッドが指値、ストップ、または即時市場エントリーを使用するかどうかを制御します。レベル間の距離は、LevelSpacingPoints を介してポイント単位で設定され、商品ティック サイズを使用して価格増分に変換されます。
  4. 音量は固定 (FixedOrderVolume) または RiskPercent から派生します。リスクベースのサイジングでは、現在のポートフォリオ資本の設定された割合をすべてのレベルにわたって一方向に割り当て、それを保護ストップによって暗示される金銭的リスクで割ります。
  5. エントリー注文が約定すると、戦略はペアの保護注文を自動的に配置します。ストップは StopLossPrice または StopDistancePoints から導出されますが、テイクプロフィットは TakeProfitDistancePoints に依存するか、デフォルトで 1 グリッド ステップ離れたものになります。保留中の注文は ExpirationHours 時間後に期限切れになる場合があります。
  6. この戦略は、実現損益と変動損益を継続的に監視します。現在の取引日の損失が DailyLossLimitPercent に違反すると、すべての注文がキャンセルされ、オープンポジションが閉じられ、翌日が始まるまで新しいグリッドの作成が一時停止されます。
  7. 手動切り替え (CloseAllPositionsCloseLongPositionsCloseShortPositionsCancelOrders) を使用すると、トレーダーはコードに触れることなく、ブックを即座に平らにしたりクリーニングしたりできます。

パラメーター

  • EnableGrid – 自動グリッド メンテナンスを有効または無効にするマスター スイッチ。
  • OrderType – レベルの作成時に使用されるエントリ順序タイプ (LimitStopMarket)。
  • AllowBuy/AllowSell – グリッドに参加できるサイドを選択します。
  • UseCenterLine / CenterPrice – 有効にすると、中心価格を中心に買い/売りレベルを対称的に分配します。ゼロセンターは中間価格を使用します。
  • LevelSpacingPoints – 連続するレベル間の間隔。ポイント単位で測定され、商品ティック サイズを介して絶対価格差に変換されます。
  • LevelsCount – 方向ごとのレベルの数。マーケット モードの場合、この値に関係なく 1 つの注文のみが送信されます。
  • BuyEntryPrice / SellEntryPrice – センター モードが無効な場合のロング グリッドとショート グリッドの明示的なアンカー (ゼロのデフォルトは現在のビッド/アスク)。
  • StopLossPrice – すべての注文に適用される絶対ストップレベル。 StopDistancePoints から停留所を取得するには、0 のままにします。
  • StopDistancePoints – 絶対的なストップ価格が指定されていない場合のフォールバックストップ距離 (ポイント単位)。
  • TakeProfitDistancePoints – ポイント単位のオプションのテイクプロフィット距離。ゼロの場合、ストラテジーは 1 つのグリッド ステップをデフォルトのターゲットとして使用します。
  • UseRiskSizing / RiskPercent – パーセンテージベースのサイジングを有効にし、各方向グリッドに割り当てられる資本の部分を定義します。値は、その方向のすべてのレベルに均等に分割されます。
  • FixedOrderVolume – リスクベースのサイジングが無効になっている場合、または有効な数量を生成できない場合に使用される注文サイズ。
  • ExpirationHours – 保留中のグリッド注文のオプションの有効期間。
  • DailyLossLimitPercent – 取引日の開始時に取得されたポートフォリオ株式の割合として表される取引停止しきい値。
  • CloseAllPositions / CloseLongPositions / CloseShortPositions / CancelOrders – UI からアクセスできる手動メンテナンス コマンド。
  • CandleType – 毎日のリセットなどのメンテナンス イベントに使用されるキャンドル シリーズ。

実装メモ

  • この戦略は、高レベルの StockSharp API: SubscribeCandlesSubscribeOrderBookBuyLimitSellStop など) にのみ依存します。直接コネクタ ロジックやインジケーターへのアクセスは必要ありません。
  • 注文サイジングでは、Security.PriceStepSecurity.StepPrice を使用して、MQL スクリプトからのポイントベースの距離を金銭リスクに変換します。
  • プロテクション・イグジットは、元のエントリー注文を変更するのではなく、個別のストップ/リミット注文によって実装されます。これは、リンクされたプロテクション注文を処理する StockSharp の方法と一致します。
  • 日次損失フィルターは暦日が変わるとリセットされ、ポートフォリオ値が再度記録されます。トレーダーは、安全ロックをオーバーライドしたい場合は、EnableGrid を切り替えることで手動で取引を再開できます。
  • MT4 グローバル変数、緊急終了フラグ、ソース スクリプトのグラフィカル クリーンアップ ルーチンは、厳密に型指定されたパラメーターと手動切り替えに置き換えられました。

使用のヒント

  1. グリッドを有効にする前に、グリッドを中央に配置するか、特定の価格に固定するかを定義します。中央に配置されたグリッドの場合は、意味のある CenterPrice を指定します。アンカーグリッドの場合は無効のままにして、売買エントリー価格を入力します。
  2. 金融商品のボラティリティに合わせて、LevelSpacingPointsStopDistancePoints、および TakeProfitDistancePoints を調整します。 3 つはすべてポイントベースの値であることに注意してください。
  3. リスクベースのサイジングを使用する場合は、商品に有効な PriceStepStepPrice があることを確認してください。そうしないと、戦略は固定ボリュームに戻ります。
  4. 設定パラメータを変更する前に、手動制御パラメータを使用してポジションをすばやくキャンセルまたはフラット化します。
  5. 複数の戦略が同じポートフォリオを共有している場合は、1 日あたりの損失限度額と外部リスク管理を組み合わせます。

元のExpert Advisorとの違い

  • StockSharp バージョンは、MT4 グローバル変数やコメントベースのマジックナンバー ロジックではなく、クリーンなパラメーター インターフェイスに焦点を当てています。
  • 緊急閉鎖フラグ、グリッド サイズの自動調整、および元のコードからのグラフィカル オブジェクトのクリーンアップは、手動の切り替えと簡単なパラメーターの検証に削減されます。
  • MQL スクリプトのトレーリング ストップ ヘルパーは複製されません。必要に応じて、StockSharp の既存の末尾モジュールを使用します。
  • オーダー間の MQL 依存関係ロジック (「マザー」オーダーに基づいて実行/キャンセル) は再現されません。各レベルは、独自の保護命令に従って独立して動作します。

これらの調整は、元の Colibri エキスパート アドバイザーの精神 (厳格な資金管理を備えた構造化されたマルチレベル エントリ) を維持しながら、実装を慣用的な StockSharp パターンに合わせています。

using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Colibri Grid Manager: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class ColibriGridManagerStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

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

	public ColibriGridManagerStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastEmaLength = Param(nameof(FastEmaLength), 10)
			.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators");
		_slowEmaLength = Param(nameof(SlowEmaLength), 30)
			.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators");
		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI 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 FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
	public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.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 fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, 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) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
		}

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