GitHub で見る

MACD シグナル ATR 戦略

概要

MACD シグナル戦略 は、MetaTrader エキスパート MACD_signal.mq4 を StockSharp に移植します。オリジナルのロボットが計測したのは、 ATR ベースのボラティリティ バンドに対する MACD のヒストグラムを作成し、ヒストグラムがそれを横切るたびに単一の成行注文をオープンします バンド。この C# バージョンは、StockSharp の高レベルの API を使用して同じモメンタム ブレイクアウト ロジックを再作成し、以前の ヒストグラムと ATR の読み取り値を明示的に示し、名前付きパラメータと英語を使用してすべての資金管理ルールを文書化します。 ソースコード内のコメント。

チケットを直接変更した MetaTrader 実装とは異なり、StockSharp ポートはネット ポジションで動作します。それ したがって、方向を反転する前に現在の露出を閉じ、トレーリングストップに依存するのではなく内部的に更新します。 ブローカー側の OrderModify 呼び出し。

取引ロジック

  1. 構成されたローソク足シリーズ (CandleType) をサブスクライブし、部分的なバーを避けるために完成したローソク足のみを処理します。 騒音。
  2. 選択した高速、低速、およびシグナル EMA の長さを MovingAverageConvergenceDivergenceSignal インジケーターにフィードします。の ヒストグラム値 (MACD - signal) はバーが閉じるたびに保存されます。
  3. 同じローソク足で AverageTrueRange を計算します。 前のバーの値に次の値が乗算されます。 ThresholdMultiplier は、MQL から rr = ATR * LEVEL のしきい値を再作成します。
  4. 以前のヒストグラムがまだ下回っている間に現在のヒストグラムが +threshold を超えた場合に強気のブレイクアウトを検出します それ。口座がフラットまたはショートで、Direction によってロング取引が許可されている場合は、次のサイズの成行買い注文を送信します。 TradeVolume
  5. ヒストグラムが前のローソク足で -threshold を上回った後、その下を横切ったときに弱気のブレイクアウトを検出します。もし 戦略がフラットまたはロングで、ショート取引が有効になっている場合は、TradeVolume のサイズの成行売り注文を発行します。
  6. バーごとにオープンポジションを管理:
    • ヒストグラムが負になるとすぐにロングを閉じます。プラスに転じたらショートを閉じます。
    • 元の値をエミュレートするために、ローソク足の高値または安値に対する固定テイクプロフィットディスタンス (TakeProfitPoints) を監視します MetaTrader テイクプロフィットパラメータ;
    • 価格がエントリーからTrailingStopPoints以上離れたらトレーリングストップを更新し、ローソク足が再訪した場合は終了します トレーリングレベル。ロングストップは入札価格の代理として終値を追跡し、ショートストップは次のように終値を追跡します。 言い値の代理。
  7. EA は、TakeProfitPoints が過去の最小ポイント 10 ポイントを下回り、保護チェックに一致する場合、取引を拒否します。 MQL コードに存在します。

リスク管理

  • 一度に 1 つの注文 この戦略は、新しいポジションを開く前に常にネットフラットであり、元の注文を反映しています。 OrdersTotal() < 1 の要件。
  • 固定音量。 TradeVolumeLots 入力を置き換え、Strategy.Volume にもコピーされるため、手動 UI アクションでは 同じサイズです。
  • 固定テイクプロフィット。 TakeProfitPoints は、次を使用して MQL ポイント距離を商品ティック サイズに変換します。 Security.PriceStep
  • インジケーターベースの撤退。 ヒストグラムの符号反転が即座に市場撤退をトリガーし、EA が市場に留まらないことが保証されます。 勢いが反転する市場。
  • トレーリングストップ 価格が設定されたステップ数を超えて取引に有利に動くと、ストップが引かれます プロフィットゾーン内で終値に従い、決して後退することはありません。

パラメーター

名前 種類 デフォルト 説明
TradeVolume decimal 10 注文サイズ(ロット)はすべての市場エントリーに使用され、Strategy.Volume にコピーされます。
TakeProfitPoints int 10 価格ステップで表される固定利食いターゲットまでの距離。 10未満の値
取引を無効にします。
TrailingStopPoints int 25 トレーリングストップの価格ステップの距離。末尾を無効にするには、0 に設定します。
FastPeriod int 9 MACD インジケーター内の高速な EMA の長さ。
SlowPeriod int 15 MACD インジケーター内の低速な EMA の長さ。
SignalPeriod int 8 MACD 信号線を平滑化するために使用される EMA の長さ。
ThresholdMultiplier decimal 0.004 ブレイクアウトバンドを構築するために前のバー ATR に適用される乗数。
AtrPeriod int 200 ATR ボラティリティ フィルターの計算に使用されるローソク足の数。
CandleType DataType 30分の時間枠 戦略によって処理される主な時間枠。

元のエキスパートアドバイザーとの違い

  • MetaTrader は AccountFreeMargin() を公開し、値が小さすぎる場合は取引を拒否しました。 StockSharp 戦略はそうではありません には同じマージン スナップショットがあるため、ポートはそのチェックを省略します。ポートフォリオレベルのリスク管理は外部で処理する必要があります。 必要に応じて戦略を立てます。
  • MQL バージョンは、OrderModify を使用して逆指値注文を調整しました。 StockSharp はネット ポジションで動作するため、コンバージョンは管理されます。 ローソク足の高値/安値とトレーリングストップ変数を監視することで内部的に終了します。
  • MetaTrader は手動で「バー」を数え、使用可能なローソク足が 100 個未満になった場合に警告を出力しました。 StockSharp が依存しているのは インジケーターの準備状況 (BindEx) により、MACD と ATR に十分なデータが得られるまで戦略は自動的にアイドル状態になります。
  • ポートは、以前の ATR とヒストグラム値を明示的に保存して、Delta/Delta1 のしきい値比較を再現します。 ランダムなインジケーターのインデックス作成に対する StockSharp のルールに違反することはありません。

使い方のヒント

  • Security.PriceStepSecurity.MinVolumeSecurity.VolumeStep の正確さを維持して、大量のコンバージョンと収益を確保します 計算は取引所と一致したままになります。
  • 不安定な市場で戦略の取引が頻繁すぎる場合は、ThresholdMultiplier または AtrPeriod を増やします。それらを減らす システムがボラティリティのブレイクアウトに対してより敏感になるようにします。
  • 元のスクリプトは大きな値を想定しているため、レバレッジがかかった商品やボラティリティの高い商品で実行する場合は、TradeVolume が低くなります。 外国為替シンボルのロットサイズ。
  • のみを許可したい場合は、組み込みの Direction プロパティを使用して戦略をより高いタイムフレーム フィルターと組み合わせます。 特定の市場体制中のロングまたはショート。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// MACD Signal ATR: EMA crossover with ATR stops.
/// </summary>
public class MacdSignalAtrStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _atrLength;

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

	public MacdSignalAtrStrategy()
	{
		_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");
		_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 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 atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fastEma, slowEma, 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 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) { _entryPrice = close; BuyMarket(); }
			else if (fastVal < slowVal && _prevFast >= _prevSlow) { _entryPrice = close; SellMarket(); }
		}
		_prevFast = fastVal; _prevSlow = slowVal;
	}
}