GitHub で見る

エキスパート RSI Stochastic MA 戦略

概要

エキスパート RSI Stochastic MA 戦略は、MetaTrader 5 のエキスパートアドバイザー Expert_RSI_Stochastic_MA.mq5 の変換版です。C# 実装は StockSharp の高レベル戦略 API を活用しながら、元のロジックを再現します:設定可能な移動平均に基づくトレンドフィルター、RSI からのモメンタム確認、正確なタイミングのためのデュアルライン Stochastic オシレーター。保護動作は、オプションの固定損失閾値と Stochastic 駆動のトレーリング出口でソースアルゴリズムを複製します。

インジケーターとパラメーター

戦略は MetaTrader バージョンと同じ入力を公開し、そのデフォルト値を維持します。すべてのパラメーターは StockSharp UI を通じて最適化に利用できます。

カテゴリ パラメーター デフォルト 説明
一般 CandleType 15 分時間軸 インジケーター計算に使用されるローソク足の集計。
トレーディング TradeVolume 0.01 ロット/契約のベース注文サイズ。
RSI RsiPeriod 3 RSI の計算に使用されるバーの数。
RSI RsiPriceType 終値 RSI に適用される価格(終値、始値、高値、安値、中間、典型、加重)。
RSI RsiUpperLevel 80 ショート条件を引き起こす買われ過ぎ閾値。
RSI RsiLowerLevel 20 ロング条件を引き起こす売られ過ぎ閾値。
Stochastic StochKPeriod 6 %K ラインの期間。
Stochastic StochDPeriod 3 %D 平滑化ラインの期間。
Stochastic StochSlowing 3 %K に適用される追加の遅延係数。
Stochastic StochUpperLevel 70 両方の Stochastic ラインで共有される買われ過ぎレベル。
Stochastic StochLowerLevel 30 両方の Stochastic ラインで共有される売られ過ぎレベル。
移動平均 MaMethod 単純 移動平均のタイプ(単純、指数、平滑、加重)。
移動平均 MaPriceType 終値 移動平均に適用される価格。
移動平均 MaPeriod 150 移動平均の長さ。
移動平均 MaShift 0 移動平均値を後方にシフトするために使用される完成したバーの数。
リスク AllowLossPoints 30 損失取引を終了する前の最大不利エクスカーションのポイント数(0 で無効)。
リスク TrailingStopPoints 30 Stochastic ベースのトレーリングストップのポイント単位の距離(0 はトレーリングなしで Stochastic 時にクローズ)。

ポイント計算 – 実装は AllowLossTrailingStop パラメーターを Security.PriceStep を使用して絶対価格に変換します。商品が小数点以下 3 桁または 5 桁の場合、MetaTrader の pip 処理をエミュレートするために値に 10 を掛けます。

取引ロジック

ロング セットアップ

  1. トレンドフィルター – ローソク足の終値はシフトされた移動平均の上に留まる必要があります。
  2. モメンタム確認 – RSI は RsiLowerLevel を下回る必要があります。
  3. タイミング – 両方の Stochastic ライン(%K と %D)は StochLowerLevel を下回る必要があります。
  4. ポジションフィルター – ロング注文はロングエクスポージャーが存在しない場合にのみ出されます(Position <= 0)。注文サイズは TradeVolume に既存のショートポジションをクローズするために必要な量を加えたものです。

ショート セットアップ

  1. トレンドフィルター – ローソク足の終値はシフトされた移動平均の下にある必要があります。
  2. モメンタム確認 – RSI は RsiUpperLevel を超える必要があります。
  3. タイミング – 両方の Stochastic ラインは StochUpperLevel を上回る必要があります。
  4. ポジションフィルター – 新しいショートポジションは Position >= 0 を必要とします。戦略は必要に応じて既存のロングを自動的にオフセットします。

エグジット管理

  • 損失取引
    • AllowLossPoints がゼロの場合、戦略は Stochastic メインラインが反対の極値(ロングは StochUpperLevel、ショートは StochLowerLevel)に移動するまで待ってから、マイナスの取引をクローズします。
    • AllowLossPoints が正の場合、戦略は値を価格オフセットに変換し、損失がこの閾値を超え かつ Stochastic が中立ゾーンに戻った時点(ロングは stochMain > StochLowerLevel、ショートは < StochUpperLevel)で取引をクローズします。
  • トレーリング出口
    • TrailingStopPoints > 0 の場合、取引が利益を出し Stochastic がその極値ゾーンに達すると、完了したローソク足ごとにトレーリングストップが設定されます。ロング取引ではストップは価格の下を追い、ショート取引では上を追います。
    • TrailingStopPoints = 0 の場合、利益が出ている取引は Stochastic が極値レベルに達すると即座にクローズされます(元の EA の動作に一致)。
  • トレーリングトリガー – トレーリングの更新は完成したローソク足でのみ発生し、更新をバーごとに1回に制限した MQL 実装を反映します。

実装上の注意

  • 移動平均のシフトは最近の値を保存し MaShift バー前の値を読み取ることで処理され、MetaTrader の shift パラメーターを再現します。
  • RSI と移動平均の入力は MetaTrader オプションに対応するために複数の適用価格をサポートします。Stochastic 計算は StockSharp の組み込みオシレーター(Low/High モード)に依存し、設定された平滑化長を尊重します。
  • トレーリングと損失の閾値はポイントで測定されます。ヘルパーは典型的な FX ティックサイズ(3 または 5 桁)の値を自動的にスケーリングし、それ以外の場合はデフォルトで 1 PriceStep を使用します。
  • チャート出力にはローソク足、移動平均、RSI、Stochastic インジケーターが含まれ、元のテンプレートに似た視覚的検証が可能です。
  • リクエストにより Python 版は付属しません;C# 実装のみ提供されます。

使用上のヒント

  • 非従来のティックサイズの有価証券に展開する場合、Security.PriceStep が入力されていることを確認してください;そうでなければデフォルト変換が使用されます(1 ポイント = 1 価格単位)。
  • さらなるストップロスやテイクプロフィット管理が必要な場合は、組み込みの StartProtection または追加のリスクモジュールを組み合わせてください。
  • インジケーターの長さとリスク閾値を一緒に最適化してください — 戦略は意図的に MetaTrader エキスパートのすべての主要なコントロールを公開しています。
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>
/// Combines an SMA trend filter with RSI and Stochastic oscillators.
/// Enters long when price is above SMA, RSI is oversold and Stochastic is oversold.
/// Enters short when price is below SMA, RSI is overbought and Stochastic is overbought.
/// </summary>
public class ExpertRsiStochasticMaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiUpperLevel;
	private readonly StrategyParam<decimal> _rsiLowerLevel;
	private readonly StrategyParam<int> _stochKPeriod;
	private readonly StrategyParam<int> _stochDPeriod;
	private readonly StrategyParam<decimal> _stochUpperLevel;
	private readonly StrategyParam<decimal> _stochLowerLevel;
	private readonly StrategyParam<int> _maPeriod;

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

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

	public decimal RsiUpperLevel
	{
		get => _rsiUpperLevel.Value;
		set => _rsiUpperLevel.Value = value;
	}

	public decimal RsiLowerLevel
	{
		get => _rsiLowerLevel.Value;
		set => _rsiLowerLevel.Value = value;
	}

	public int StochKPeriod
	{
		get => _stochKPeriod.Value;
		set => _stochKPeriod.Value = value;
	}

	public int StochDPeriod
	{
		get => _stochDPeriod.Value;
		set => _stochDPeriod.Value = value;
	}

	public decimal StochUpperLevel
	{
		get => _stochUpperLevel.Value;
		set => _stochUpperLevel.Value = value;
	}

	public decimal StochLowerLevel
	{
		get => _stochLowerLevel.Value;
		set => _stochLowerLevel.Value = value;
	}

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	public ExpertRsiStochasticMaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Time frame used for calculations", "General");

		_rsiPeriod = Param(nameof(RsiPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "Number of bars for RSI", "RSI");

		_rsiUpperLevel = Param(nameof(RsiUpperLevel), 80m)
			.SetDisplay("RSI Overbought", "Upper RSI threshold", "RSI");

		_rsiLowerLevel = Param(nameof(RsiLowerLevel), 20m)
			.SetDisplay("RSI Oversold", "Lower RSI threshold", "RSI");

		_stochKPeriod = Param(nameof(StochKPeriod), 6)
			.SetGreaterThanZero()
			.SetDisplay("%K Period", "Length of Stochastic %K", "Stochastic");

		_stochDPeriod = Param(nameof(StochDPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("%D Period", "Length of Stochastic %D", "Stochastic");

		_stochUpperLevel = Param(nameof(StochUpperLevel), 70m)
			.SetDisplay("Stoch Overbought", "Upper Stochastic threshold", "Stochastic");

		_stochLowerLevel = Param(nameof(StochLowerLevel), 30m)
			.SetDisplay("Stoch Oversold", "Lower Stochastic threshold", "Stochastic");

		_maPeriod = Param(nameof(MaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Length of moving average", "Moving Average");
	}

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

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

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

		var sma = new SMA { Length = MaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var stoch = new StochasticOscillator();
		stoch.K.Length = StochKPeriod;
		stoch.D.Length = StochDPeriod;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(sma, rsi, stoch, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue smaValue, IIndicatorValue rsiValue, IIndicatorValue stochValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!smaValue.IsFinal || !rsiValue.IsFinal || !stochValue.IsFinal)
			return;

		var sma = smaValue.IsEmpty ? (decimal?)null : smaValue.GetValue<decimal>();
		var rsiVal = rsiValue.IsEmpty ? (decimal?)null : rsiValue.GetValue<decimal>();

		if (sma is not decimal smaDecimal || rsiVal is not decimal rsiDecimal)
			return;

		var stoch = (StochasticOscillatorValue)stochValue;
		if (stoch.K is not decimal stochK || stoch.D is not decimal stochD)
			return;

		var price = candle.ClosePrice;

		// Long entry: price above SMA, RSI oversold, Stochastic oversold
		if (price > smaDecimal && rsiDecimal < RsiLowerLevel && stochK < StochLowerLevel && stochD < StochLowerLevel)
		{
			if (Position <= 0)
				BuyMarket();
		}
		// Short entry: price below SMA, RSI overbought, Stochastic overbought
		else if (price < smaDecimal && rsiDecimal > RsiUpperLevel && stochK > StochUpperLevel && stochD > StochUpperLevel)
		{
			if (Position >= 0)
				SellMarket();
		}
		// Exit long: Stochastic overbought
		else if (Position > 0 && stochK > StochUpperLevel)
		{
			SellMarket();
		}
		// Exit short: Stochastic oversold
		else if (Position < 0 && stochK < StochLowerLevel)
		{
			BuyMarket();
		}
	}
}