GitHub で見る

Larry Connors RSI-2戦略

クラシックなLarry Connors RSI-2システムのStockSharp忠実なポートです。この戦略は、2期間の高速RSIオシレーターと時間足のMoving Averageフィルターを組み合わせ、より上位の時間軸のトレンドに沿いながら短期的な平均回帰のセットアップを捉えます。pipsで表されるオプションのストップロスとテイクプロフィットレベルは、オリジナルのMetaTraderの資金管理ルールを再現します。

概念の概要

  • タイプ: トレンドフィルター付き平均回帰。
  • 市場: H1チャートで取引されるFX通貨ペア向け。
  • 方向: ロングとショートの両方を取引しますが、低速SMAフィルターの方向のみ。
  • コアインジケーター: 5期間SMA(エグジットタイミング)、200期間SMA(トレンドフィルター)、2期間RSI(シグナルトリガー)。

取引ルール

ロングエントリー

  • RSI値がRSI Long Entry(デフォルト6)を下回る。
  • 完成したローソク足の終値がSlow SMA(デフォルト200期間)を上回っている。
  • オープンポジションがない。

ショートエントリー

  • RSI値がRSI Short Entry(デフォルト95)を上回る。
  • 終値がSlow SMAを下回っている。
  • オープンポジションがない。

エグジット条件

  • ロングポジションは終値がFast SMA(デフォルト5)を上回ったときに決済。有効化されている場合、pipsで計測されるオプションのストップロスとテイクプロフィットレベルも取引を決済できます。
  • ショートポジションは終値がFast SMAを下回ったときに決済。オプションのストップロスとテイクプロフィットレベルをpipsで対称的に適用します。

リスク管理

  • Use Stop Lossはエントリー価格から相対的なpipsでの固定ストップ距離を切り替えます。
  • Use Take Profitはpipsでの対称的な利益目標を有効にします。
  • pip距離は、4/5桁の相場に対するMT5ロジックを反映して、インストゥルメントのPriceStepと小数精度を通じて絶対価格に変換されます。

デフォルト値

パラメーター デフォルト 説明
Trade Volume 1 各エントリーのベース注文量。
Fast SMA Period 5 エグジットタイミング平均。
Slow SMA Period 200 トレンド方向フィルター。
RSI Period 2 RSIオシレーターのルックバック。
RSI Long Entry 6 ロング取引の売られすぎ閾値。
RSI Short Entry 95 ショート取引の買われすぎ閾値。
Use Stop Loss true 保護ストップを有効/無効にする。
Stop Loss (pips) 30 pipsでのストップロス距離。
Use Take Profit true 固定利益目標を有効/無効にする。
Take Profit (pips) 60 pipsでの利益目標距離。
Candle Type 1時間 作業ローソク足の時間軸。

すべての調整可能なパラメーターは.SetCanOptimize(true)を公開し、Designer/Tester内でのバッチ最適化を可能にします。

実行ノート

  • シグナルはオリジナルのMetaTrader実装に合わせて、クローズしたローソク足で評価されます。
  • 保護レベルは内部で追跡され、違反時に成行注文でポジション全体を決済します。
  • 戦略は再現可能なバックテストを保証するために、各再起動時に内部状態(pipSize、エントリーアンカー)をリセットします。
  • 公開されているパフォーマンス結果を再現するために、信頼性の高いFXデータとともに戦略をプロジェクトに追加してください。

推奨される使用方法

  1. 1時間ローソク足を供給するFXデータフィードを接続する。
  2. 戦略をDesignerに追加するか、StockSharp APIを通じてプログラム的に実行する。
  3. 必要に応じてブローカーの契約仕様に合わせてpipベースのリスクパラメーターを調整する。
  4. オプションでRSI閾値や移動平均の長さを最適化して、モデルを他のシンボルに適応させる。

正確なRSIと移動平均ロジックを維持することで、このポートによりMT5ユーザーはStockSharpエコシステム内でLarry Connors RSI-2の方法論を評価することができます。

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Larry Connors RSI-2 strategy with a 200-period SMA filter and optional stop management.
/// </summary>
public class LarryConnersRsi2Strategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<int> _fastSmaPeriod;
	private readonly StrategyParam<int> _slowSmaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiLongEntry;
	private readonly StrategyParam<decimal> _rsiShortEntry;
	private readonly StrategyParam<bool> _useStopLoss;
	private readonly StrategyParam<decimal> _stopLossPips;
	private readonly StrategyParam<bool> _useTakeProfit;
	private readonly StrategyParam<decimal> _takeProfitPips;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _pipSize;
	private decimal? _longEntryPrice;
	private decimal? _shortEntryPrice;

	/// <summary>
	/// Order volume for entries.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set
		{
			_tradeVolume.Value = value;
			Volume = value;
		}
	}

	/// <summary>
	/// Fast SMA period used for timing exits.
	/// </summary>
	public int FastSmaPeriod
	{
		get => _fastSmaPeriod.Value;
		set => _fastSmaPeriod.Value = value;
	}

	/// <summary>
	/// Slow SMA period used as a trend filter.
	/// </summary>
	public int SlowSmaPeriod
	{
		get => _slowSmaPeriod.Value;
		set => _slowSmaPeriod.Value = value;
	}

	/// <summary>
	/// RSI lookback length.
	/// </summary>
	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	/// <summary>
	/// RSI threshold for long entries.
	/// </summary>
	public decimal RsiLongEntry
	{
		get => _rsiLongEntry.Value;
		set => _rsiLongEntry.Value = value;
	}

	/// <summary>
	/// RSI threshold for short entries.
	/// </summary>
	public decimal RsiShortEntry
	{
		get => _rsiShortEntry.Value;
		set => _rsiShortEntry.Value = value;
	}

	/// <summary>
	/// Enables stop-loss handling in price pips.
	/// </summary>
	public bool UseStopLoss
	{
		get => _useStopLoss.Value;
		set => _useStopLoss.Value = value;
	}

	/// <summary>
	/// Stop-loss size expressed in pips.
	/// </summary>
	public decimal StopLossPips
	{
		get => _stopLossPips.Value;
		set => _stopLossPips.Value = value;
	}

	/// <summary>
	/// Enables take-profit handling in price pips.
	/// </summary>
	public bool UseTakeProfit
	{
		get => _useTakeProfit.Value;
		set => _useTakeProfit.Value = value;
	}

	/// <summary>
	/// Take-profit size expressed in pips.
	/// </summary>
	public decimal TakeProfitPips
	{
		get => _takeProfitPips.Value;
		set => _takeProfitPips.Value = value;
	}

	/// <summary>
	/// Timeframe used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes <see cref="LarryConnersRsi2Strategy"/>.
	/// </summary>
	public LarryConnersRsi2Strategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Order volume", "Trading")
			;

		_fastSmaPeriod = Param(nameof(FastSmaPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast SMA Period", "Fast SMA length", "Indicators")
			;

		_slowSmaPeriod = Param(nameof(SlowSmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Slow SMA Period", "Slow SMA length", "Indicators")
			;

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

		_rsiLongEntry = Param(nameof(RsiLongEntry), 6m)
			.SetDisplay("RSI Long Entry", "RSI threshold for longs", "Signals")
			;

		_rsiShortEntry = Param(nameof(RsiShortEntry), 95m)
			.SetDisplay("RSI Short Entry", "RSI threshold for shorts", "Signals")
			;

		_useStopLoss = Param(nameof(UseStopLoss), true)
			.SetDisplay("Use Stop Loss", "Enable stop-loss management", "Risk");

		_stopLossPips = Param(nameof(StopLossPips), 30m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss (pips)", "Stop-loss distance in pips", "Risk")
			;

		_useTakeProfit = Param(nameof(UseTakeProfit), true)
			.SetDisplay("Use Take Profit", "Enable take-profit management", "Risk");

		_takeProfitPips = Param(nameof(TakeProfitPips), 60m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit (pips)", "Take-profit distance in pips", "Risk")
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for candles", "General");
	}

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

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

		// Clear internal state between runs.
		_pipSize = 0m;
		_longEntryPrice = null;
		_shortEntryPrice = null;
	}

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

		// Use configured trade volume for default order size.
		Volume = TradeVolume;

		// Pre-compute pip size multiplier for risk management calculations.
		var priceStep = Security?.PriceStep ?? 1m;
		var decimals = Security?.Decimals ?? 0;
		var pipMultiplier = decimals is 1 or 3 or 5 ? 10m : 1m;
		_pipSize = priceStep * pipMultiplier;
		if (_pipSize <= 0m)
			_pipSize = priceStep;

		// Prepare technical indicators.
		var fastSma = new SMA { Length = FastSmaPeriod };
		var slowSma = new SMA { Length = SlowSmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		// Subscribe to candles and bind indicators for combined processing.
		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastSma, slowSma, rsi, ProcessCandle)
			.Start();

		// Build optional chart visuals to monitor the strategy.
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastSma);
			DrawIndicator(area, slowSma);
			DrawIndicator(area, rsi);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastSma, decimal slowSma, decimal rsi)
	{
		// Act only on fully formed candles to mimic MQL bar-close execution.
		if (candle.State != CandleStates.Finished)
			return;

		// Manage open long position exits before generating new signals.
		if (Position > 0)
		{
			if (UseStopLoss && _longEntryPrice.HasValue)
			{
				var stopPrice = _longEntryPrice.Value - StopLossPips * _pipSize;
				if (candle.LowPrice <= stopPrice)
				{
					SellMarket();
					ResetLongState();
					return;
				}
			}

			if (UseTakeProfit && _longEntryPrice.HasValue)
			{
				var targetPrice = _longEntryPrice.Value + TakeProfitPips * _pipSize;
				if (candle.HighPrice >= targetPrice)
				{
					SellMarket();
					ResetLongState();
					return;
				}
			}

			if (candle.ClosePrice > fastSma)
			{
				SellMarket();
				ResetLongState();
				return;
			}
		}
		else if (Position < 0)
		{
			if (UseStopLoss && _shortEntryPrice.HasValue)
			{
				var stopPrice = _shortEntryPrice.Value + StopLossPips * _pipSize;
				if (candle.HighPrice >= stopPrice)
				{
					BuyMarket();
					ResetShortState();
					return;
				}
			}

			if (UseTakeProfit && _shortEntryPrice.HasValue)
			{
				var targetPrice = _shortEntryPrice.Value - TakeProfitPips * _pipSize;
				if (candle.LowPrice <= targetPrice)
				{
					BuyMarket();
					ResetShortState();
					return;
				}
			}

			if (candle.ClosePrice < fastSma)
			{
				BuyMarket();
				ResetShortState();
				return;
			}
		}

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		// Generate new entries only when flat to match the MQL logic.
		if (Position == 0)
		{
			var canGoLong = rsi < RsiLongEntry && candle.ClosePrice > slowSma;
			if (canGoLong)
			{
				BuyMarket();
				_longEntryPrice = candle.ClosePrice;
				_shortEntryPrice = null;
				return;
			}

			var canGoShort = rsi > RsiShortEntry && candle.ClosePrice < slowSma;
			if (canGoShort)
			{
				SellMarket();
				_shortEntryPrice = candle.ClosePrice;
				_longEntryPrice = null;
			}
		}
	}

	private void ResetLongState()
	{
		// Drop long tracking data after an exit.
		_longEntryPrice = null;
	}

	private void ResetShortState()
	{
		// Drop short tracking data after an exit.
		_shortEntryPrice = null;
	}
}