GitHub で見る

TenPointThree MACD グリッド戦略

概要

この戦略は、MetaTrader エキスパート アドバイザ 10p3v003 (10point3.mq4) の C# ポートです。 MACD クロスオーバー トリガーとマーチンゲール グリッド エンジンを組み合わせています。元のロジックは、次の主要な動作を持つ StockSharp の高レベルの API を使用して複製されました。

  • MACD シグナル ロジック – MACD のメイン ラインがシフトされたバー (SignalShift) のシグナル ラインと交差すると、取引方向が決定されます。ロングエントリーの場合は、前のシグナル値が -TradingRangePips 未満であること、現在の MACD の値がゼロ未満であることが必要です。ショートの場合はその逆です。信号はオプションで ReverseSignal を介して反転できます。
  • グリッド階層化 – 最初のポジションがオープンされた後、価格が最後の約定に対して少なくとも GridStepPips だけ動いた場合にのみ、同じ方向への追加エントリーが許可されます。新しいレッグごとにボリュームが LotMultiplier (または、MaxTrades > 12 の場合は 1.5) 倍になり、MQL4 のマーチンゲール スケーリングが模倣されます。
  • リスク保護OrdersToProtect 以上の取引がアクティブで、変動利益が金額しきい値を超えている場合、最新のレッグはクローズされ、それ以上のエントリーは追加されません。しきい値は、設定されたリスクパーセント (資金管理が有効) または契約サイズのヒューリスティック (資金管理が無効) に基づきます。
  • レッグごとのエグジット – 各レッグは、独自のテイクプロフィット、仮想ストップロス、およびトレーリングストップを追跡します。停止距離は元の式 InitialStopPips + (MaxTrades - existingOrders) * GridStepPips と一致します。トレーリングは、価格がポジションに有利に TrailingStopPips + GridStepPips だけ動いた後にのみ有効になり、価格が TrailingStopPips までリトレースしたときにレッグを閉じます。
  • セッション フィルターUseTimeFilter が有効な場合、ローソク足の時刻が厳密に StopHourStartHour の間にある間は新しいグリッドは開始されず、スクリプトの「危険タイム ゾーン」ガードが再現されます。

すべての金銭換算では、証券の PriceStep/StepPrice メタデータが使用されます。取引所が契約サイズを公開しない場合、元の外国為替の想定と一致するフォールバック値 100000 が適用されます。

パラメーター

名前 説明
CandleType MACD の処理に使用される Candle サブスクリプション (デフォルト: 30 分の時間枠)。
Volume 最初のグリッドオーダーの基本ロットサイズ。
TakeProfitPips 各レッグのテイクプロフィットの距離 (0 無効)。
InitialStopPips ベースストップ距離 (pips)。実際の停止時間は、空きグリッド スロットの数に応じて増加します。
TrailingStopPips レッグが十分に利益を上げた後に適用されるピップ単位のトレーリング ストップ距離 (0 無効)。
MaxTrades 同時マーチンゲール エントリの最大数。
LotMultiplier 追加の各グリッド レッグのボリュームに適用される乗数 (MaxTrades > 12 の場合は 1.5 にオーバーライドされます)。
GridStepPips 次のグリッドエントリーを開く前に必要な最小の逆方向の価格変動(ピップ単位)。
OrdersToProtect 変動利益保護が最新の取引を終了するまでのアクティブなレッグの最小数。
UseMoneyManagement 口座資本に基づいた動的なロット計算を可能にします。
AccountType リスクの式を選択します: 0 – 標準 (資本 / 10,000)。 1 – 通常 (資本 / 100,000); 2 – ナノ (資本 / 1,000)。
RiskPercent 資金管理が有効な場合に使用される資本の割合。
ReverseSignal ロング/ショート MACD 信号を反転します。
FastEmaLength, SlowEmaLength, SignalLength MACD 期間(デフォルトでは 12/26/9)。
SignalShift クロスオーバー チェックに使用される閉じたバー バックの数 (デフォルト: 1)。
TradingRangePips クロスオーバーが受け入れられる前に突破する必要がある MACD 信号帯域 (ピップ単位)。
UseTimeFilter StopHour/StartHour に基づいてセッション ガードを有効にします。
StopHour, StartHour UseTimeFilter が true の場合に新しいグリッドの作成をブロックする排他的な範囲。

お金の管理メモ

UseMoneyManagement が無効な場合、基本ロット (Volume) が直接使用されます。それ以外の場合、EA は元の EA と同じ式を使用して現在の株式からロットサイズを計算します。

  • アカウントの種類 0: Ceil(risk% * equity / 10,000) / 10
  • アカウントの種類 1: risk% * equity / 100,000
  • アカウントの種類 2: risk% * equity / 1,000

ボリュームは Security.VolumeStep で正規化され、その後、Security.MinVolume/MaxVolume で上限が設定されます。

実行ワークフロー

  1. 設定されたローソク足ストリームをサブスクライブし、BindEx を介して MACD インジケーターをフィードします。
  2. 終了したローソクごとに、アクティブなレッグのトレーリング/ストップ ロジックを更新します。
  3. MACD クロスオーバー ルールが発動したら、セッション フィルターで取引が許可されていること、グリッドの方向が既存のポジションと一致していること、価格が最後の約定に対して GridStepPips だけ移動していることを確認してください。
  4. マーチンゲール乗数を使用して次のレッグのボリュームを計算し、成行注文を送信します。
  5. 変動利益を監視します。保護しきい値に達したら、最新のレッグを閉じて、次のローソク足まで一時停止します。

変換メモ

  • すべてのコメントは必要に応じて英語で書き直されました。
  • 高レベルの StockSharp API (キャンドル + BindEx) が使用されます。インジケータ値への直接アクセスは回避されます。
  • 変動利益の計算は、PriceStep/StepPrice に依存します。エキゾチックな楽器の場合は、これらのフィールドが入力されていることを確認してください。
  • StockSharp はデフォルトでポジションを集約するため、この戦略は、MQL4 の注文管理をエミュレートするために内部的にレッグごとの状態を維持します。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// 10point3 MACD Grid: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class TenPointThreeMacdGridStrategy : 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 TenPointThreeMacdGridStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastEmaLength = Param(nameof(FastEmaLength), 12)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
		_slowEmaLength = Param(nameof(SlowEmaLength), 26)
			.SetDisplay("Slow EMA", "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;
	}
}