GitHub で見る

Three EMA戦略

概要

この戦略は、3つの指数移動平均(EMA)を積み重ねることでMetaTraderの「ThreeEMA」エキスパートアドバイザーを再現します。同じ時間軸で高速、中速、低速EMAの間の方向的整列を探します。平均が厳密に昇順に整列している(高速が中速より上、中速が低速より上)場合、ストラテジーはロングポジションを開くか維持します。順序が逆転(高速が中速より下、中速が低速より下)すると、ショートポジションを開くか維持します。保護的なストップロスとテイクプロフィットのオフセットは元のMQLパラメーターを反映し、インストゥルメントのティクサイズに対する価格ポイントで表されます。

元のMQLの動作

MQLバージョンは3つのEMAインジケーター(FastPeriodMediumPeriodSlowPeriod)をインスタンス化し、最後にクローズされたバーでの相対的な順序に基づいて取引シグナルを生成しました:

  • FastEMA > MediumEMA > SlowEMAのときロングを開く / ショートを閉じる
  • FastEMA < MediumEMA < SlowEMAのときショートを開く / ロングを閉じる
  • ストップロスとテイクプロフィットはエントリー価格からの固定点数として適用されました。

注文はマーケット執行で送信され、マネーマネジメントブロックは固定ロットサイズを使用しました。トレーリングモジュールは無効でした。

StockSharp実装の詳細

  • 高水準ローソク足サブスクリプションAPIを使用します。3つのExponentialMovingAverageインジケーターがメイン時間軸サブスクリプションにバインドされ、各完成したローソク足がすべてのEMA値を同時に配信します。
  • 取引決定はインバーバーノイズを避けるために完全に形成されたローソク足でのみ評価されます。
  • 方向的なスタックが現れると、ストラテジーは進行中の注文をキャンセルし、必要に応じて反対のエクスポージャーをクローズし、必要な方向に新しいマーケットポジションを開きます。
  • StartProtectionはインストゥルメントのPriceStepを使用して、設定されたポイントベースのストップロスとテイクプロフィット距離を実際の価格オフセットに変換します。これは元のEAの保護動作を反映します。
  • チャート統合はチャートエリアが利用可能な場合にローソク足と3つすべてのEMAを描画し、シグナルの視覚的な検証を容易にします。

パラメーター

名前 デフォルト 説明
CandleType 1分足時間軸 EMAに使用するローソク足サブスクリプションの時間軸。
FastPeriod 5 高速EMAの長さ。MediumPeriodより小さくなければなりません。
MediumPeriod 12 中速EMAの長さ。高速と低速の期間の間でなければなりません。
SlowPeriod 24 低速EMAの長さ。最も高い期間値でなければなりません。
StopLossPoints 400 インストゥルメントポイントで表現された保護的ストップロス距離(PriceStepを使用して価格に変換)。無効にするにはゼロ。
TakeProfitPoints 900 インストゥルメントポイントでのテイクプロフィット距離(PriceStepを使用して価格に変換)。無効にするにはゼロ。

使用上の注意

  1. ストラテジーを開始する前にVolumeを設定して、希望の注文サイズを反映させます(元のEAは固定ロットを使用していました)。
  2. EMA期間が厳密に増加し続けるようにしてください;そうでなければ、MQLソースで見つかる検証に一致するようにOnStarted中に例外がスローされます。
  3. ロジックはEMAスタックが逆転するたびにポジションを常に反転させるため、条件が強気と弱気の整列の間で交互になるたびにストラテジーは継続的に市場にさらされます。
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;

public class ThreeEmaStrategy : 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 ThreeEmaStrategy()
	{
		_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;
	}
}