GitHub で見る

手動 EA 戦略

手動 EA 戦略は、MetaTrader 4 エキスパート アドバイザー Manual_EA.mq4 (フォルダー MQL/8159) の 1 対 1 の StockSharp 高レベル API 変換です。元のシステムは、Stochastic オシレーターが極端なゾーンを離れるたびに、任意の買い注文または売り注文を発行します。 StockSharp ポートは同じ 5-3-3 オシレーター構成を維持し、次の成行注文を行う前に既存のエクスポージャーを自動的にネッティングし、戦略パラメーターを通じて共通の資金管理オプションを公開します。

取引ロジック

  1. この戦略は、CandleType シリーズ(デフォルト: 15 分足ローソク足)をサブスクライブし、終値を次のように構成された Stochastic オシレーターに供給します。
    • %K ルックバック = KPeriod (デフォルトは 5 バー)
    • %K の速度低下 = Slowing (デフォルトは 3 バー)
    • %D 平滑化 = DPeriod (デフォルトは 3 バー)
  2. シグナルは、完成した各ローソク足の %D (シグナル) ラインの最終値で評価されます。 2 つの連続した読み取り値が比較され、レベルクロスが検出されます。
  3. ロングエントリ – 以前の %D 値が OversoldLevel (デフォルトは 10) 以下で、最新の値がそのしきい値を超えた場合。この戦略では、まず短期エクスポージャーを無効にしてから、成行注文で Volume + |short position| を購入します。
  4. 簡単なエントリ – 以前の %D 値が OverboughtLevel (デフォルトは 90) 以上で、最新の値がそのしきい値を下回った場合。既存のロングポジションは、市場で Volume + |long position| を売却する前にクローズされます。
  5. 秘密保持命令は StartProtection を通じて処理されます。プラスの StopLoss および/または TakeProfit (価格ポイントで測定) は、自動リスク管理を有効にします。パラメータを 0 に設定すると、対応する保護が無効になります。

このポートは、インジケーター バッファーのアクセス パターンと未完成のキャンドル ロジックを意図的に回避し、StockSharp の高レベルの API ベスト プラクティスに準拠しています。

パラメーター

パラメータ 説明 デフォルト
CandleType ローソクを構築し、オシレーターを駆動するために使用されるタイムフレーム (DataType として)。 15分の時間枠
KPeriod Stochastic %K ラインのルックバック長。 5
DPeriod Stochastic %D 信号線の長さの平滑化。 3
Slowing %D が計算される前に、%K に追加の平滑化が適用されます。 3
OverboughtLevel %D によって下方向にクロスされたときにショートエントリーをトリガーする上限。 90
OversoldLevel %D を上方に超えたときにロングエントリーをトリガーする下限。 10
StopLoss 保護ストップロスの価格ポイントの距離 (0 = 無効)。 100
TakeProfit テイクプロフィットターゲットの価格ポイントの距離 (0 = 無効)。 100
Volume 新しいシグナルごとに送信される注文サイズ (ロット)。既存の反対のポジションが最初にネットされます。 0.1

追加の注意事項

  • この戦略は、SubscribeCandlesBindEx を併用して、StochasticOscillatorValue の更新をストリーミングし、取引の決定が行われる前にインジケーターの値が最終的なものであることを保証します。
  • チャートの視覚化は、チャート領域が使用可能な場合、選択したローソク足シリーズ、Stochastic オシレーター、および独自の取引を自動的にプロットします。
  • %D クロッシングは連続した終了ローソク足で評価されるため、動作はシフト 1 と 2 で MODE_SIGNAL 値を比較した MQL 実装と一致します。
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 ManualEaStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevRsi;
	private bool _hasPrev;
	private int _cooldown;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public ManualEaStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 9).SetDisplay("RSI Period", "RSI lookback", "Indicators");
		_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA filter", "Indicators");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
		_prevRsi = default;
		_hasPrev = default;
		_cooldown = default;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ema, ProcessCandle).Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsi, decimal ema)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (!_hasPrev) { _prevRsi = rsi; _hasPrev = true; return; }
		if (_cooldown > 0)
		{
			_cooldown--;
			_prevRsi = rsi;
			return;
		}

		if (_prevRsi <= 20 && rsi > 20 && close > ema && Position == 0)
		{
			BuyMarket();
			_cooldown = 2;
		}
		else if (_prevRsi >= 80 && rsi < 80 && close < ema && Position == 0)
		{
			SellMarket();
			_cooldown = 2;
		}
		_prevRsi = rsi;
	}
}