GitHub で見る

JMaster RSX 戦略

概要

JMaster RSX 戦略は、MetaTrader 4 エキスパート アドバイザ jMasterRSXv1 をそのまま変換したものです。システムは、高速 (M5) および低速 (M30) の時間枠で計算された Jurik RSX オシレーターの値を調整します。より高いタイムフレームが強気または弱気の方向を指し、高速オシレーターが売られすぎ/買われすぎの領域に達すると、戦略は対応する方向のポジションに入ります。すべてのシグナルは、以前の完全に閉じたローソク足を使用して新しいバーの開始時に評価され、shift = 1 値を参照した MT4 実装と一致します。

指標とデータ

  • 高速タイムフレームの Jurik RSX (長さ = RsxLength)FastCandleType (デフォルトの 5 分足) で定義されたローソク足シリーズのオシレーターを評価します。この変換により、カスタム rsx.mq4 インジケーターで使用される元の再帰フィルターが再現されます。
  • スロータイムフレームのJurik RSXSlowCandleType(デフォルトの30分足)で定義されたローソク足シリーズの同じ長さで計算されます。最新の完了したスロー値は、MT4 のシフト動作を反映して、使用される前に 1 バー遅れます。

エントリーロジック

  1. 新しい高速キャンドルが開くまで待ちます (StockSharp で完成したキャンドルを処理するのと同じです)。
  2. 以前の高速 RSX 読み取り値と以前の低速 RSX 読み取り値 (現在の終値から 1 本の遅いローソク足) を取得します。
  3. 長いセットアップ: 遅い RSX は MidlineLevel (デフォルト 50) を超え、および 高速な RSX は OversoldLevel (デフォルト 25) を下回ります。
  4. 簡単なセットアップ: 遅い RSX は MidlineLevel 未満であり、および 速い RSX は OverboughtLevel (デフォルト 75) を超えています。
  5. 現在アクティブなポジションがないときに、ボリューム Volume で成行注文を開きます。

終了ロジック

  • ショート条件(ミッドラインを下回る遅い RSX と買われ過ぎのしきい値を上回る速い RSX)が満たされたらすぐに、開いているロング ポジションを閉じます。
  • ロング条件が満たされたらすぐにオープンショートポジションを閉じます(遅いRSXがミッドラインを超え、速いRSXが売られすぎのしきい値を下回る)。
  • この戦略はポジションを積み上げません。新しいエントリを検討する前に、常にフラットな状態に戻ります。

ポジションサイズ

  • 注文は、Volume パラメータ (デフォルトは 0.1) によって制御される固定量で行われます。
  • 適応的な資金管理やピラミッド型ロジックは実装されていません。これは、DecreaseFactor がゼロのままだったときの元の EA のデフォルトの動作を反映しています。

パラメーター

名前 説明 デフォルト
FastCandleType 高速 RSX 計算用のローソク式 M5
SlowCandleType 遅い RSX 計算用のローソク足タイプ M30
RsxLength 両方の RSX インスタンスで共有されるルックバックの長さ 14
OverboughtLevel 短いエントリの高速 RSX しきい値 75
OversoldLevel 長いエントリの高速 RSX しきい値 25
MidlineLevel 強気体制と弱気体制を分ける遅いRSX正中線 50
Volume 市場参入のための注文量 0.1

使用上の注意

  • 履歴データが、設定された両方の時間枠で完成したローソク足を提供することを確認します。この戦略はローソク足が閉じた後にのみ反応します。
  • 遅い RSX 値は意図的に 1 バー分遅れているため、より高いタイムフレームでのバー内反転は 1 バー後に表示されます。これはソース EA と一致し、先読みバイアスを防ぎます。
  • 埋め込まれた RSX インジケーターは 0 ~ 100 の範囲の値を出力し、必要に応じて他のオシレーターと直接比較できます。
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>
/// JMaster RSX: RSI-based momentum with EMA trend filter.
/// Buys when RSI exits oversold and price above EMA.
/// Sells when RSI exits overbought and price below EMA.
/// </summary>
public class JmasterRsxStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<decimal> _oversold;

	private decimal _prevRsi;
	private decimal _entryPrice;

	public JmasterRsxStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_emaLength = Param(nameof(EmaLength), 30)
			.SetDisplay("EMA Length", "EMA trend filter.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");

		_overbought = Param(nameof(Overbought), 75m)
			.SetDisplay("Overbought", "RSI overbought level.", "Signals");

		_oversold = Param(nameof(Oversold), 25m)
			.SetDisplay("Oversold", "RSI oversold level.", "Signals");
	}

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

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public decimal Overbought
	{
		get => _overbought.Value;
		set => _overbought.Value = value;
	}

	public decimal Oversold
	{
		get => _oversold.Value;
		set => _oversold.Value = value;
	}

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

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

		_prevRsi = 0;
		_entryPrice = 0;
	}

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

		_prevRsi = 0;
		_entryPrice = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, ema, atr, ProcessCandle)
			.Start();

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

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, ema);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevRsi == 0 || atrVal <= 0)
		{
			_prevRsi = rsiVal;
			return;
		}

		var close = candle.ClosePrice;

		// Entry
		if (Position == 0)
		{
			if (_prevRsi < Oversold && rsiVal >= Oversold && close > emaVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (_prevRsi > Overbought && rsiVal <= Overbought && close < emaVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevRsi = rsiVal;
	}
}