GitHub で見る

スターター V6 モッド E

Starter V6 Mod E は、MetaTrader 4 Expert Advisor Starter_v6mod_e_www_forex-instruments_info.mq4 の高レベルの StockSharp 変換です。このポートは、ラゲール発振器の極値、デュアル EMA 運動量確認、CCI フィルタリング、および EMA 角度ゲートの元の組み合わせを維持しながら、実行を StockSharp のイベント駆動型アーキテクチャに適応させます。

取引ロジック

  • トレンド ゲート: 34 期間の EMA の傾きが、構成可能な開始/終了シフト間で測定されます。傾きはピップ単位で表されます。正の傾きのみロング取引が許可され、負の傾きのみショートが許可され、フラットな読み取り値は新規エントリーをブロックします。
  • ラゲールの極値: 手作りのラゲール RSI (デフォルトではガンマ = 0.7) は、売られすぎ/買われすぎの状態を 0 ~ 1 のスケールで追跡します。ロングの場合は、現在値と以前の値の両方が Laguerre Oversold レベル未満に留まる必要があり、ショートの場合は両方の値が Laguerre Overbought を超える必要があります。
  • EMA モメンタム フィルター: 120 および 40 期間の EMA (中央価格) は、元の MA フィルターを反映して、ロングの場合は両方とも上昇し、ショートの場合は両方とも下落する必要があります。
  • CCI 確認: 14 期間の CCI は、ロングの場合は -CCI Threshold より下、ショートの場合は +CCI Threshold より上でなければならず、MQL からの Alpha フィルターを複製します。
  • 金曜日の安全性: 新しい取引は Friday Block Hour 以降ブロックされ、残りのポジションは Friday Exit Hour に達すると清算されます。

リスク管理

  • 構成可能なストップロス、テイクプロフィット、およびトレーリングストップの距離 (ピップ単位) は、エキスパートの資金管理ブロックをエミュレートします。
  • トレーリングストップはエントリー後に最も有利な価格に従い、リトレースメントが設定された距離を超えたときに取引を終了します。
  • 手動ポジション決済は SellMarket/BuyMarket を通じて実行され、高レベルの API コンプライアンスが保証されます。

パラメーター

パラメータ 説明
Volume 各市場参入の注文量。
StopLossPips ピップ単位の保護停止距離。
TakeProfitPips 利益目標(pips)。
TrailingStopPips トレーリングストップの距離 (ピップ単位) (0 はトレーリングを無効にします)。
SlowEmaPeriod PRICE_MEDIAN に計算された遅い EMA の期間。
FastEmaPeriod PRICE_MEDIAN で計算された高速 EMA の期間。
AngleEmaPeriod EMA 周期が角度検出器に使用されます。
AngleStartShift / AngleEndShift EMA の傾きを計算するために使用されるバー シフト。
AngleThreshold 取引を可能にするために必要な最小勾配 (ピップ単位)。
CciPeriod / CciThreshold CCI フィルターの期間と絶対しきい値。
LaguerreGamma ラゲール発振器のガンマパラメータ。
LaguerreOversold / LaguerreOverbought 0 ~ 1 ラゲール スケールのエントリしきい値。
CandleType ローソク足のデータ型 (デフォルトは 1 分)。
FridayBlockHour / FridayExitHour 金曜日のリスク制限を制御する時間 (現地の機器時間)。

変換メモ

  • ラゲール発振器は、元の再帰式から直接実装され、0 ~ 1 の出力範囲とガンマ スムージングを維持します。
  • EMA の傾きは、過去の EMA ポイント間の pip 正規化された差分を計算することにより、MQL 角度ヘルパーを置き換えます。
  • 株式カットオフやグリッド スタッキングなどの資金管理機能は、変換される MT4 バリアントによってデフォルトで無効になっており、StockSharp が明示的なポートフォリオ管理を奨励しているため、意図的に省略されています。
  • 注文は BuyMarket/SellMarket を通じて送信され、OnNewMyTrade を利用してトレーリング ロジックの約定価格を追跡します。
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

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

/// <summary>
/// Starter V6 Mod E strategy using dual EMA crossover with Laguerre RSI filter.
/// Buy when fast EMA crosses above slow EMA and Laguerre is oversold.
/// Sell when fast EMA crosses below slow EMA and Laguerre is overbought.
/// </summary>
public class StarterV6ModEStrategy : Strategy
{
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<decimal> _laguerreGamma;
	private readonly StrategyParam<decimal> _laguerreOversold;
	private readonly StrategyParam<decimal> _laguerreOverbought;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _lagL0;
	private decimal _lagL1;
	private decimal _lagL2;
	private decimal _lagL3;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _prevLaguerre;
	private bool _hasPrev;

	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	public decimal LaguerreGamma
	{
		get => _laguerreGamma.Value;
		set => _laguerreGamma.Value = value;
	}

	public decimal LaguerreOversold
	{
		get => _laguerreOversold.Value;
		set => _laguerreOversold.Value = value;
	}

	public decimal LaguerreOverbought
	{
		get => _laguerreOverbought.Value;
		set => _laguerreOverbought.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public StarterV6ModEStrategy()
	{
		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 26)
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 12)
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_laguerreGamma = Param(nameof(LaguerreGamma), 0.7m)
			.SetDisplay("Laguerre Gamma", "Smoothing factor for Laguerre RSI", "Indicators");

		_laguerreOversold = Param(nameof(LaguerreOversold), 0.5m)
			.SetDisplay("Laguerre Oversold", "Oversold level (0-1)", "Indicators");

		_laguerreOverbought = Param(nameof(LaguerreOverbought), 0.5m)
			.SetDisplay("Laguerre Overbought", "Overbought level (0-1)", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

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

		_lagL0 = 0m;
		_lagL1 = 0m;
		_lagL2 = 0m;
		_lagL3 = 0m;
		_prevFast = 0m;
		_prevSlow = 0m;
		_prevLaguerre = 0m;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;
		_lagL0 = _lagL1 = _lagL2 = _lagL3 = 0m;

		var fastEma = new ExponentialMovingAverage { Length = FastEmaPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var laguerre = CalculateLaguerre(candle.ClosePrice);

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_prevLaguerre = laguerre;
			_hasPrev = true;
			return;
		}

		// EMA crossover signals
		var bullishCross = _prevFast <= _prevSlow && fast > slow;
		var bearishCross = _prevFast >= _prevSlow && fast < slow;

		// Long: fast EMA crosses above slow + Laguerre was oversold
		if (Position <= 0 && bullishCross && _prevLaguerre <= LaguerreOversold)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Short: fast EMA crosses below slow + Laguerre was overbought
		else if (Position >= 0 && bearishCross && _prevLaguerre >= LaguerreOverbought)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
		_prevLaguerre = laguerre;
	}

	private decimal CalculateLaguerre(decimal price)
	{
		var gamma = LaguerreGamma;

		var l0Prev = _lagL0;
		var l1Prev = _lagL1;
		var l2Prev = _lagL2;
		var l3Prev = _lagL3;

		_lagL0 = (1m - gamma) * price + gamma * l0Prev;
		_lagL1 = -gamma * _lagL0 + l0Prev + gamma * l1Prev;
		_lagL2 = -gamma * _lagL1 + l1Prev + gamma * l2Prev;
		_lagL3 = -gamma * _lagL2 + l2Prev + gamma * l3Prev;

		decimal cu = 0m;
		decimal cd = 0m;

		if (_lagL0 >= _lagL1) cu = _lagL0 - _lagL1; else cd = _lagL1 - _lagL0;
		if (_lagL1 >= _lagL2) cu += _lagL1 - _lagL2; else cd += _lagL2 - _lagL1;
		if (_lagL2 >= _lagL3) cu += _lagL2 - _lagL3; else cd += _lagL3 - _lagL2;

		var denom = cu + cd;
		return denom == 0m ? 0m : cu / denom;
	}
}