GitHub で見る

RSI Expert ブレイクアウト戦略

概要

  • RSI閾値のブレイクアウトを取引するMetaTrader 5の「RSI_Expert」戦略を移植したものです。
  • 単一のRSIインジケーターを使用して、売られすぎ/買われすぎ領域付近のモメンタムリバーサルを検出します。
  • pipsで表現された元の固定テイクプロフィット、ストップロス、トレーリングストップ管理を実装します。

戦略ロジック

  1. 選択したローソク足シリーズでRSIを構築します(デフォルト期間: 14)。
  2. 最新の2つの完成したRSI値を追跡します。
  3. RSIが以前に下回っていた後、下限閾値(デフォルト20)を再び上回った場合にロングに入ります。
  4. RSIが以前に上回っていた後、上限閾値(デフォルト60)を再び下回った場合にショートに入ります。
  5. 純方向を維持するため、新しいポジションを開く前に反対のエクスポージャーをクローズします。
  6. pipsで計測されたオプションのストップロス、テイクプロフィット、トレーリングストップ距離でオープントレードを管理します。

パラメーター

名前 説明 デフォルト
CandleType ローソク足集計に使用する時間軸。 1時間足
TradeVolume エントリーに使用する注文サイズ。 0.1
RsiPeriod RSIのルックバック長。 14
RsiUpperLevel 弱気リバーサルを示すRSI閾値。 60
RsiLowerLevel 強気リバーサルを示すRSI閾値。 20
TakeProfitPips pipsでのテイクプロフィット距離(0で無効化)。 60
StopLossPips pipsでのストップロス距離(0で無効化)。 0
TrailingStopPips pipsでのトレーリングストップ距離(0でトレーリング無効化)。 15
TrailingStepPips トレーリングストップが再度シフトされる前の最小価格改善。 5

pipsの解釈: StockSharpポートでは「pip」は1つの Security.PriceStep に相当します。小数点以下の相場を持つFXシンボルでは、価格ステップが銘柄のpipの慣習と一致することを確認し、一致しない場合は入力距離を適切に調整してください。

リスク管理

  • テイクプロフィットとストップロスは、最新の平均ポジション価格を使用して、完成した各ローソク足で評価されます。
  • トレーリングストップは、移動が TrailingStopPips + TrailingStepPips を超えた後にのみ起動し、価格が進むにつれて TrailingStopPips だけクローズに追従します。
  • ストップチェックはローソク足の高値/安値を使用してバー内トリガーをエミュレートします。トリガーされた場合、ポジションは成行でクローズされます。

変換ノート

  • 高レベルAPI(SubscribeCandles + Bind)が使用され、RSI値は手動インジケーターバッファなしにバインディングコールバックから直接消費されます。
  • トレーリングストップロジックは、各調整前のステップ閾値を含むMQL条件を再現します。
  • 戦略は、古いレベルが新しいトレードに引き継がれるのを避けるため、エクスポージャーがフリップまたはクローズするたびにトレーリング状態をリセットします。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// RSI Expert Breakout strategy. Trades RSI threshold crossovers.
/// </summary>
public class RsiExpertBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiUpper;
	private readonly StrategyParam<decimal> _rsiLower;

	private decimal? _prevRsi;

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

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	public decimal RsiUpper
	{
		get => _rsiUpper.Value;
		set => _rsiUpper.Value = value;
	}

	public decimal RsiLower
	{
		get => _rsiLower.Value;
		set => _rsiLower.Value = value;
	}

	public RsiExpertBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI lookback period", "Indicators");

		_rsiUpper = Param(nameof(RsiUpper), 70m)
			.SetDisplay("RSI Upper", "Overbought threshold", "Indicators");

		_rsiLower = Param(nameof(RsiLower), 30m)
			.SetDisplay("RSI Lower", "Oversold threshold", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevRsi = null;
	}

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

		_prevRsi = null;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevRsi = rsiVal;
			return;
		}

		if (_prevRsi == null)
		{
			_prevRsi = rsiVal;
			return;
		}

		// RSI crosses above lower level from below → buy
		if (_prevRsi.Value < RsiLower && rsiVal >= RsiLower && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// RSI crosses below upper level from above → sell
		else if (_prevRsi.Value > RsiUpper && rsiVal <= RsiUpper && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevRsi = rsiVal;
	}
}