GitHub で見る

Exp ColorMETRO MMRec Duplex戦略

概要

この戦略はMetaTrader 5のエキスパートアドバイザー Exp_ColorMETRO_MMRec_Duplex をStockSharpにポートします。オリジナルのロボットは2つの独立したColorMETROインジケーターモジュール(ロング用とショート用)を実行し、繰り返し損失後にポジションサイズを縮小するMMRec(マネーマネジメント再計算)オーバーレイを適用します。C#バージョンはローソク購読と注文ルーティングにStockSharpの高レベルAPIを使用しながらその動作を反映します。

取引ロジック

  • 2つの異なるColorMETROインジケーターが設定可能なローソクタイプで動作します。ロングモジュールはロング露出のみを管理し、ショートモジュールはショート露出を制御します。
  • 各インジケーターは高速と低速のステップ付きRSIエンベロープを生成します。戦略はMQL5の CopyBuffer 呼び出しを模倣し、履歴値を保存して SignalBar で定義されたバーを検査します。
  • 検査されたバーで高速バンドが低速バンドを下からクロスする(前のバーではまだ高速バンドが低速バンドの上にあった)場合にロングエントリーが生成されます。新しいロングを開く前にオープン中のショートポジションはフラットにされます。
  • ロング決済は、前の検査バーの低速バンドが高速バンドの上にある場合、オリジナルEAの弱気レジームシグナルとして発生します。
  • ショートエントリーと決済はロングロジックを反映します(エントリーは上クロス、前バーで高速ラインが低速ラインの上にある場合が決済)。
  • 完了したローソクのみ処理され、インジケーターが両方のバンドを準備完了と報告するまで取引はブロックされ、MetaTraderのウォームアップ期間を再現します。

マネーマネジメント(MMRec)

  • Strategy.Volume は参照ロットサイズを定義します。ロングとショートモジュールは新規注文のサイジング時に各 LongMm/ShortMm 係数でそれを乗算します。
  • 各完了トレード後、戦略はその結果が損失だったかどうかを記録します(履歴ディールを検査するEAと同様にローソク終値に基づく)。
  • モジュールの最新 TotalTrigger トレードが少なくとも LossTrigger 件の損失トレードを含む場合、モジュールは縮小乗数(SmallMm)に切り替わります。損失数がしきい値を下回ると、デフォルト乗数が自動的に復元されます。
  • ポジション反転は、反対方向のサイジングと開始前に既存トレードの結果を最初に確定します(MMRecカウンターを更新)。

インジケーターの注記

  • ColorMetroMmrecIndicator はカスタムインジケーター ColorMETRO の忠実なポートです。ステップトラッキングとトレンドメモリを持つRSIコアに駆動された同じ高速・低速エンベロープを供給します。
  • インジケーターは内部RSIと準備完了フラグを公開し、戦略がMQL実装と同様に不完全な値を無視できるようにします。

パラメーター

グループ 名前 説明
ロング LongCandleType ロングColorMETROモジュールに使用するローソクタイプ。
ロング LongTotalTrigger MMRec評価時に検査する完了ロングトレード数。
ロング LongLossTrigger 縮小ロング乗数を有効化する損失数。
ロング LongSmallMm 損失連続後にロングトレードに適用される縮小乗数。
ロング LongMm ロングトレードのデフォルト乗数。
ロング LongEnableOpen ロングポジションの開始を有効化。
ロング LongEnableClose ロングポジションの決済を有効化。
ロング LongPeriodRsi ロングColorMETROインジケーター内で使用するRSI期間。
ロング LongStepSizeFast ロングモジュールの高速エンベロープステップサイズ。
ロング LongStepSizeSlow ロングモジュールの低速エンベロープステップサイズ。
ロング LongSignalBar インジケーター値を読む際の履歴シフト(終値バー単位)。
ロング LongMagic 参照用に保持されたオリジナルのMT5マジックナンバー。
ロング LongStopLossTicks EAのストップロス距離プレースホルダー(適用されない)。
ロング LongTakeProfitTicks EAのテイクプロフィット距離プレースホルダー(適用されない)。
ロング LongDeviationTicks EAの許容スリッページプレースホルダー(適用されない)。
ロング LongMarginMode 互換性のために保持されたMMモードフラグ(ロジックは生の乗数を使用)。
ショート ShortCandleType ショートColorMETROモジュールに使用するローソクタイプ。
ショート ShortTotalTrigger MMRec評価時に検査する完了ショートトレード数。
ショート ShortLossTrigger 縮小ショート乗数を有効化する損失数。
ショート ShortSmallMm 損失連続後にショートトレードに適用される縮小乗数。
ショート ShortMm ショートトレードのデフォルト乗数。
ショート ShortEnableOpen ショートポジションの開始を有効化。
ショート ShortEnableClose ショートポジションの決済を有効化。
ショート ShortPeriodRsi ショートColorMETROインジケーター内で使用するRSI期間。
ショート ShortStepSizeFast ショートモジュールの高速エンベロープステップサイズ。
ショート ShortStepSizeSlow ショートモジュールの低速エンベロープステップサイズ。
ショート ShortSignalBar インジケーター値を読む際の履歴シフト(終値バー単位)。
ショート ShortMagic 参照用に保持されたオリジナルのMT5マジックナンバー。
ショート ShortStopLossTicks EAのストップロス距離プレースホルダー(適用されない)。
ショート ShortTakeProfitTicks EAのテイクプロフィット距離プレースホルダー(適用されない)。
ショート ShortDeviationTicks EAの許容スリッページプレースホルダー(適用されない)。
ショート ShortMarginMode 互換性のために保持されたMMモードフラグ(ロジックは生の乗数を使用)。

実装上の注記

  • 戦略は SubscribeCandles(...).BindEx(...) に依存し、変換ガイドラインに沿って直接バッファアクセスを避けます。
  • EAの保護ストップはパラメーターのみとして残されています;ユーザーは必要に応じて StartProtection またはカスタムリスクモジュールを追加できます。
  • 両方のモジュールは同じ銘柄インスタンスを共有しますが、MetaTraderのデュプレックスレイアウトに合わせて独自のローソク購読とMMRecカウンターを保持します。
  • コード内のすべてのコメントは英語で提供され、ロジックは GetTrades などの禁止されたAPI呼び出しを避けています。

免責事項

このポートはオリジナルEAの論理構造を再現しますが、実行品質は接続されたブローカー、データフィード、StockSharpの設定に依存します。実際の資本で取引する前に、常に履歴データとデモデータで動作を検証してください。

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;

/// <summary>
/// ColorMETRO MMRec Duplex strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class ExpColorMetroMmrecDuplexStrategy : 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 ExpColorMetroMmrecDuplexStrategy()
	{
		_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;
	}
}