GitHub で見る

3103 — ADX EA (C#) 戦略

概要

オリジナルのMetaTrader「ADX EA」は、Average Directional Indexのブレイクアウトと+DI/−DIのクロスオーバー、上位タイムフレームの モメンタム確認、月次MACDフィルターを組み合わせています。C#ポートはStockSharpの高レベルAPIの上でそのマルチフィルターワーク フローを複製します。戦略は3つの足ストリームを購読します:

  1. 主要タイムフレーム(デフォルト5分) — ADX、線形加重移動平均、価格構造チェック、ボリュームフィルターを駆動します。
  2. モメンタムタイムフレーム(デフォルト15分) — エントリーを制御する100ベースライン周辺のモメンタム偏差を生成します。
  3. MACDタイムフレーム(デフォルト30日) — ポジション決済を制御する月次MACDを反映します。

取引ロジック

  • ブレイクアウトモジュール – 有効時、ロング取引には以下が必要です:

    • ADXまたは+DIがEntryLevelを上回り、+DIと−DIの差がMinDirectionalDifferenceより大きい。
    • 高速LWMAが低速LWMAを上回り、強気足構造(Low[2] < High[1])、モメンタム上昇(Momentum[1] > Momentum[2])。
    • 上位タイムフレームの直近3回のモメンタム読み取りのうち少なくとも1回が100からMomentumBuyThreshold以上乖離している。
    • 主要タイムフレームで出来高増加(Volume[1] > Volume[2]またはVolume[1] > Volume[3])。
    • 月次タイムフレームのMACDが強気(MacdMain[1] > MacdSignal[1])。
    • 全体的なトレンド強度を確認するためADXがExitLevelを上回る。

    ショートブレイクアウトは、−DI優位、弱気構造(Low[1] < High[2])、MomentumSellThreshold分の100以下のモメンタム、 弱気MACDの比較で対称的なロジックを適用します。

  • クロスオーバーモジュール – 有効時、+DIが−DIを上回るクロス(ロング)または−DIが+DIを上回るクロス(ショート)を探します。 オプションのフィルターはオリジナルのEAを反映します:

    • RequireAdxSlopeはADXが前回読み取りより高いことを要求します。
    • ConfirmCrossOnBreakoutはクロスバーに同じブレイクアウト閾値チェックを追加します。
    • MinAdxMainLineはクロス中の最小ADX強度を強制します。
    • LWMAの整列、モメンタムの傾き、出来高の拡大、MACDの極性が意図した方向に一致している必要があります。
  • ピラミッディング – 各新規注文はLotExponentに従ってボリュームを追加します。戦略はTradeVolumeをベースロットサイズ として扱い、LotExponent^nnはすでに開いているステップ数)で増やします。MaxTradesは蓄積できるネットボリュームを制限します。

リスク管理

  • 保護注文TakeProfitStepsStopLossStepsStartProtectionに渡され、銘柄の価格ステップで表現されます。
  • トレーリングストップTrailingStopStepsは最高の終値を超えた手動トレーリングバリアを維持します。
  • ブレイクイーブンUseBreakEvenが有効の場合、価格がBreakEvenTriggerステップ前進した後にストップが締め付けられ、 BreakEvenOffsetステップだけストップをオフセットできます。
  • MACD決済EnableMacdExitが真の場合、月次MACD関係はMACDがシグナルを下回ったときにロングを閉じます(ショートはその逆)、 EAのClose_BUY/Close_SELLルーティンに対応します。
  • 資本ストップUseEquityStopは浮動利益曲線を追跡し、ドローダウンがTotalEquityRiskパーセントに達するとポジションを 清算します。

口座通貨ターゲットに依存する機能(「Take Profit in Money」、「Trailing Profit in Money」など)はポートされていません。 StockSharp戦略は通常、ストップ距離と組み込み保護サービスを通じて保護ロジックを管理するためです。EAの他のすべての決定ポイントは インジケーター等価物で保持されます。

パラメーター

パラメーター デフォルト 説明
TradeVolume 0.01 最初のエントリーのベースロットサイズ。
CandleType 5mタイムフレーム ADX/LWMAロジックの主要足シリーズ。
MomentumCandleType 15mタイムフレーム モメンタム偏差フィルターの上位タイムフレーム。
MacdCandleType 30日タイムフレーム MACD決済フィルターを供給するタイムフレーム。
FastMaPeriod 6 高速線形加重移動平均の長さ。
SlowMaPeriod 85 低速線形加重移動平均の長さ。
AdxPeriod 14 Average Directional Indexの期間。
MomentumPeriod 14 上位タイムフレームのモメンタムインジケーター期間。
MacdFastPeriod 12 MACD決済フィルター内の高速EMA期間。
MacdSlowPeriod 26 MACD決済フィルター内の低速EMA期間。
MacdSignalPeriod 9 MACD決済フィルター内のシグナルSMA期間。
EnableBreakoutStrategy true ADXブレイクアウトブランチのトグル。
EnableCrossStrategy true DIクロスオーバーブランチのトグル。
UseTrendFilter true ブレイクアウト中にロングで+DI優位、ショートで−DI優位を強制。
RequireAdxSlope true DIクロスを評価する際にADXが上昇することを要求。
ConfirmCrossOnBreakout true クロスオーバーモジュールにブレイクアウト閾値を追加。
EnableMacdExit true MACDベースの決済ルーティンを有効化。
EntryLevel 10 ブレイクアウトで使用される最小ADX/+DI/−DIレベル。
ExitLevel 10 新規エントリーを許可する最小ADX強度。
MinDirectionalDifference 10 +DIと−DIの必要差。
MinAdxMainLine 10 DIクロス中の最小ADXレベル。
MomentumBuyThreshold 0.3 強気モメンタム確認に必要な100からの偏差。
MomentumSellThreshold 0.3 弱気モメンタム確認に必要な100からの偏差。
MaxTrades 10 最大ピラミッディングステップ数。
LotExponent 1.44 各追加ステップのボリューム乗数。
TakeProfitSteps 50 テイクプロフィット注文の価格ステップ単位の距離。
StopLossSteps 20 ストップロス注文の価格ステップ単位の距離。
TrailingStopSteps 40 価格ステップ単位の手動トレーリングストップ距離。
UseBreakEven true ブレイクイーブン再配置ロジックを有効化。
BreakEvenTrigger 30 ブレイクイーブンを有効にする前に必要な有利な動きのステップ数。
BreakEvenOffset 30 ストップ移動時にエントリー価格に追加されるステップ数。
UseEquityStop true ドローダウンベースの緊急決済を有効化。
TotalEquityRisk 1 すべてのポジションをフラットにする前の許容ドローダウンパーセンテージ。

使用のヒント

  • 元のタイムフレームマッピングを模倣するためにMomentumCandleTypeMacdCandleTypeを主要タイムフレームに合わせてください (例:5分チャート → 15分モメンタム → 月次MACD)。
  • EntryLevelMinDirectionalDifferenceMinAdxMainLineを一緒に調整してください;三つ全てを下げるとブレイクアウト フィルターがかなり緩くなります。
  • LotExponentを1.0より大きくするとEAのマーチンゲール式スケーリングが再現されます。ポジションサイズを一定に保つには1.0に設定。
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>
/// ADX EA strategy using EMA crossover with ADX trend strength filter.
/// Buys when fast EMA crosses above slow EMA with strong trend.
/// Sells on reverse crossover.
/// </summary>
public class AdxEaStrategy : 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;

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public AdxEaStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 200)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow EMA period", "Indicator");

		_stopLossPoints = Param(nameof(StopLossPoints), 200)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

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

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

	/// <inheritdoc />
	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;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// EMA crossover
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 60;
		}
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 60;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}