GitHub で見る

EMA WMA リスク戦略

概要

  • Vladimir Hlystov による MetaTrader 4 "EMA WMA" 専門アドバイザーの転換。
  • 指数移動平均 (EMA) とローソク足 始値に基づいて計算された加重移動平均 (WMA) との関係から検出された取引トレンドの反転。
  • StockSharp の保護ヘルパーを使用して、MT4 ロボットと同じストップロス注文とテイクプロフィット注文を自動的に添付します。
  • 固定量取引のオプションを維持しながら、元の「リスク」入力を反映するリスクベースのポジションサイジングをサポートします。

オリジナルの Expert Advisor ロジック

  • MT4 バージョンは任意のシンボルとタイムフレームで機能し、新しいバー (TimeBar で保護されている) でシグナルを 1 回評価します。
  • インジケーターは PRICE_OPEN を使用するため、平均はバーの開始ティックに反応します。
  • EMA が以前は WMA を上回っていたにもかかわらず WMA を下回った場合、すべてのショート ポジションがクローズされ、事前定義されたストップロスとテイクプロフィットの距離でロング取引が開始されます。
  • EMA が WMA を下回った後、WMA を上回ると、すべてのロング ポジションがクローズされ、新しいショート ポジションがオープンされます。
  • risk 入力は、利用可能なマージンとストップロス距離からロットサイズを計算します。

StockSharpの取引ルール

  1. 設定されたローソク足シリーズ (CandleType、デフォルトは 30 分) を購読します。再塗装を避けるため、完成したキャンドルのみが加工されます。
  2. ローソク足の始値を EMA および WMA インジケーターにフィードします。両方のインジケーターが形成されるまで待ちます。
  3. 以前の EMA > 以前の WMA、および現在の EMA < 現在の WMA の場合に強気のクロスオーバーを検出します。
    • ショートを閉じて、リスクルールに従ってサイズを決めたロングポジションを入力します。
  4. 以前の EMA < 以前の WMA、および現在の EMA > 現在の WMA の場合に弱気クロスオーバーを検出します。
    • ロングポジションをすべてクローズし、リスクルールに従ってサイズを決めたショートポジションを入力します。
  5. StartProtection は市場保護注文を作成するため、すべての新しい取引は価格ステップで表されるストップロスとテイクプロフィットのレベルをすぐに受け取ります。

ポジションサイジングとリスク管理

  • RiskPercent は MT4 risk パラメータをエミュレートします。出来高は、ポートフォリオの資産、ストップロス距離、およびセキュリティのステップ/ステップ価格の値から計算されます。
  • 取引所のメタデータが欠落している (価格ステップまたはステップ価格がない) 場合、アルゴリズムは絶対停止距離の使用に戻ります。
  • RiskPercent がゼロに設定されている場合、戦略には正の OrderVolume (固定量オーバーライド) が必要です。
  • 既存の逆エクスポージャは、新しい注文が送信される前にクローズされ、MT4 の CLOSEORDER の次に OPENORDER の動作と一致します。

パラメーター

名前 説明
EmaPeriod 指数移動平均の期間 (デフォルトは 28)。
WmaPeriod 加重移動平均の期間 (デフォルトは 8)。
StopLossPoints 機器ステップ単位のストップロス距離 (デフォルトは 50)。
TakeProfitPoints 商品ステップでのテイクプロフィット距離 (デフォルトは 50)。
RiskPercent 取引ごとのリスクに対する資本の割合 (デフォルトは 10%)。
OrderVolume 固定注文量。リスクベースのサイジングを有効にするには 0 を使用します。
CandleType 計算に使用されるローソク足のデータ型/タイムフレーム。

実装メモ

  • EMA と WMA 値は、オープン価格が MT4 インジケーター設定とまったく同じように使用されるようにするために、DecimalIndicatorValue を介して手動でプッシュされます。
  • この戦略は、シグナルを確認するために閉じたローソク足に依存しています。これにより、MT4 と比較してエントリーが 1 バー遅れる可能性がありますが、先読みバイアスは防止されます。
  • 保護注文は、MetaTrader からの Point の乗数に一致する価格ステップで表されます。
  • チャート領域が使用可能な場合、チャートは移動平均と取引マーカーの両方のローソク足を自動的にプロットします。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// EMA WMA Crossover Risk: Dual EMA crossover with ATR stops.
/// </summary>
public class EmaWmaRiskStrategy : 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 EmaWmaRiskStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastEmaLength = Param(nameof(FastEmaLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowEmaLength = Param(nameof(SlowEmaLength), 21)
			.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;
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		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)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (close <= _entryPrice - atrVal * 2m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (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;
	}
}