GitHub で見る

Extreme EA(StockSharp変換版)

Extreme EA 戦略は、もともとMetaTrader用に書かれたトレンドフォローのエキスパートアドバイザーです。2つの移動平均を商品チャンネル指数(CCI)フィルターと適応的な資金管理モジュールと組み合わせています。このポートはStockSharpの高レベルAPIを通じてすべての重要なパラメーターを公開しながら、取引ロジックをそのまま維持しています。この戦略は完成したローソク足のみで動作し、独立したローソク足サブスクリプションで移動平均とCCIを実行することで複数の時間軸と互換性があります。

戦略概要

  1. トレンドフィルター: 2つの移動平均が設定可能な MaCandleType で計算されます。高速平均は短期モメンタムを追跡し、低速平均は支配的なトレンドの傾きを定義します。戦略はMQLコードの元の CopyBuffer 配列オフセットを模倣するために前の2つの値を使用して低速平均の傾きを確認します。
  2. モメンタムフィルター: CCIは独自の時間軸(CciCandleType)と価格ソースで評価されます。最後に完成した値はキャッシュされ、新しいCCIローソク足が現れるまで再利用されます(MetaTraderのバッファの動作に対応)。
  3. エントリールール:
    • 低速MAが上昇し、高速MAが上昇し、CCIが下位レベルを下回るとロングに入ります。
    • 低速MAが下落し、高速MAが下落し、CCIが上位レベルを上回るとショートに入ります。
  4. エグジットルール:
    • 低速MAの上昇が止まったらすべてのロングをクローズします。
    • 低速MAの下落が止まったらすべてのショートをクローズします。

リスク管理

  • MaximumRisk は現在のポートフォリオ資本と最新価格に基づいてターゲットポジションサイズを制御します。計算されたボリュームがゼロであるか、ポートフォリオ値が利用できない場合、戦略は設定された Volume または取引所の最小値にフォールバックします。
  • DecreaseFactor は2つ以上の連続する損失取引の後に計算されたボリュームを削減します。削減は元の計算式 lot = lot - lot * losses / DecreaseFactor を反映します。
  • HistoryDays は損失連続がどのくらい長く記憶されるかを制限します。指定された日数の後に決済取引が発生した場合、削減を適用する前に連続がリセットされます。
  • MaxPositions は方向ごとのネットエクスポージャーを制限することでピラミッティングを制限します。上限に達すると、エクスポージャーが低下するまで新しいエントリーが抑制されます。

パラメーター

名前 デフォルト 説明
MaximumRisk decimal 0.05 各新しい取引のサイジングに使用される資本の割合。
DecreaseFactor decimal 6 損失連続削減係数。無効にするには 0 に設定する。
HistoryDays int 60 連続損失を数える際に保持される日数。
MaxPositions int 3 方向ごとの最大同時エントリー数。
FastMaPeriod int 15 高速移動平均の期間。
SlowMaPeriod int 75 低速移動平均の期間。
CciPeriod int 12 CCIのルックバック長。
CciUpperLevel decimal 50 ショートに使用される上位CCIの閾値。
CciLowerLevel decimal -50 ロングに使用される下位CCIの閾値。
MaCandleType DataType 15m 両方の移動平均と執行の時間軸。
CciCandleType DataType 30m CCIフィルターの時間軸。
MaMethod MaMethod Exponential 平滑化方法(Simple, Exponential, Smoothed, LinearWeighted)。
MaPriceMode AppliedPriceMode Median 移動平均の価格入力。
CciPriceMode AppliedPriceMode Typical CCIの価格入力。

実装上の注意

  • 戦略は移動平均の時間軸を1回サブスクライブし、オプションでCCIに対して2番目のサブスクリプションを行います。両方の時間軸が一致する場合、単一のサブスクリプションが両方のコンポーネントに入力し、元の単一チャートのワークフローを再現します。
  • 前のインジケーター値はプライベートフィールドにキャッシュされ、手動のインジケーターバッファを使わずに ma_slow_array[1]ma_slow_array[2]ma_fast_array[0] の比較をエミュレートします。
  • ポジションサイジングは拒否された注文を避けるためにインストルメントのボリュームステップ、最小値、最大値に対して正規化されます。
  • リスクモジュールは完成したポジションごとの実現PnLを推定するためにエントリーと出口価格を記録し、MetaTraderで使用される HistoryDealGet ループを置き換えます。

MQLバージョンとの違い

  • MetaTrader固有の関数 FreeMarginCheckMarginCheckHistorySelect はStockSharpのポートフォリオメトリクスと内部損失連続トラッカーで近似されます。
  • StockSharpポートはネットポジションで動作します。クローズ注文は関連する方向のエクスポージャー全体をフラット化し、統合されたポジションモデルに合わせます。
  • 元のEAのロギングルーティンはStockSharpの組み込み診断を優先して省略されました。
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>
/// Extreme EA strategy using fast/slow EMA crossover with CCI filter.
/// Buys when both EMAs rising and CCI below lower level (oversold bounce).
/// Sells when both EMAs falling and CCI above upper level (overbought reversal).
/// </summary>
public class ExtremeEaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _cciUpperLevel;
	private readonly StrategyParam<decimal> _cciLowerLevel;

	private ExponentialMovingAverage _fastMa;
	private ExponentialMovingAverage _slowMa;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _prevFast2;
	private decimal _prevSlow2;
	private bool _hasPrev;

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

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

	/// <summary>
	/// CCI indicator length.
	/// </summary>
	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	/// <summary>
	/// Upper CCI threshold for sell entries.
	/// </summary>
	public decimal CciUpperLevel
	{
		get => _cciUpperLevel.Value;
		set => _cciUpperLevel.Value = value;
	}

	/// <summary>
	/// Lower CCI threshold for buy entries.
	/// </summary>
	public decimal CciLowerLevel
	{
		get => _cciLowerLevel.Value;
		set => _cciLowerLevel.Value = value;
	}

	/// <summary>
	/// Initialize strategy parameters.
	/// </summary>
	public ExtremeEaStrategy()
	{
		_fastMaPeriod = Param(nameof(FastMaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA", "Fast EMA period", "Indicator");

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 200)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA", "Slow EMA period", "Indicator");

		_cciPeriod = Param(nameof(CciPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI lookback period", "Indicator");

		_cciUpperLevel = Param(nameof(CciUpperLevel), 50m)
			.SetDisplay("CCI Upper", "Upper CCI threshold for sell", "Levels");

		_cciLowerLevel = Param(nameof(CciLowerLevel), -50m)
			.SetDisplay("CCI Lower", "Lower CCI threshold for buy", "Levels");
	}

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

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

		_fastMa = null;
		_slowMa = null;
		_prevFast = 0;
		_prevSlow = 0;
		_prevFast2 = 0;
		_prevSlow2 = 0;
		_hasPrev = false;
	}

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

		_fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
		_slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fastMa, _slowMa, ProcessCandle);
		subscription.Start();
	}

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

		if (!_fastMa.IsFormed || !_slowMa.IsFormed)
		{
			_prevFast2 = _prevFast;
			_prevSlow2 = _prevSlow;
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_hasPrev = true;
			return;
		}

		if (!_hasPrev)
		{
			_prevFast2 = _prevFast;
			_prevSlow2 = _prevSlow;
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_hasPrev = true;
			return;
		}

		var slowIsRising = _prevSlow > _prevSlow2;
		var slowIsFalling = _prevSlow < _prevSlow2;
		var fastIsRising = fastValue > _prevFast;
		var fastIsFalling = fastValue < _prevFast;

		// Buy: fast crosses above slow
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
		}
		// Sell: fast crosses below slow
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
		}

		_prevFast2 = _prevFast;
		_prevSlow2 = _prevSlow;
		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}