GitHub で見る

リバース戦略

概要

リバース戦略は、Bollinger バンドと相対強度指数 (RSI) を組み合わせて、使い果たされた動きを特定する平均回帰取引システムです。この戦略は、Bollinger エンベロープ付近での価格反転を探しますが、同時に RSI が売られすぎまたは買われすぎのゾーンからクロスバックする必要があります。両方の条件が満たされると、戦略は前の動きに対して開始され、固定バンドベースのストップとターゲットで取引を管理します。

取引ロジック

  1. 設定されたローソク足シリーズ (デフォルトの 5 分足ローソク足) を購読します。
  2. 構成された期間と偏差乗数を使用した単純移動平均を使用して、Bollinger バンドを計算します。
  3. 構成されたルックバック期間を使用して RSI を計算します。
  4. 以前に完了したローソク足を追跡してクロスオーバーを検出します。
    • ロングセットアップ: 前回の終値は以前の下限バンドを下回っており、RSI は売られ過ぎのしきい値を下回っています。 RSIが売られ過ぎレベルを上回る間に、現在の終値は下限バンドを上回る必要があります。
    • 短いセットアップ: 前回の終値は前の上限バンドを上回っており、RSI は買われすぎのしきい値を上回っています。 RSIが買われ過ぎレベルを下回っている間、現在の終値は上限バンドを下回る必要があります。
  5. ロングセットアップがトリガーされたら、市場で買い、エントリー終値より 1 標準偏差下に保護ストップを設定し、終値より 2 標準偏差上にテイクプロフィットを設定します。
  6. ショートセットアップがトリガーされたら、市場で売り、エントリー終値より 1 標準偏差上に保護ストップを設定し、その 2 標準偏差下にテイクプロフィットを設定します。
  7. オープンポジションを管理:
    • 価格が上限バンドに触れた場合、ストップに達した場合、またはテイクプロフィットターゲットに到達した場合は、ロング取引を終了します。
    • 価格が下限バンドに触れた場合、ストップに到達した場合、またはテイクプロフィットターゲットに到達した場合は、ショートトレードを終了します。

パラメーター

名前 説明 デフォルト
CandleType キャンドルのサブスクリプションの期間。 5分の時間枠
BollingerPeriod Bollinger 移動平均と標準偏差に使用されるバーの数。 20
BollingerWidth Bollinger バンドに適用される標準偏差乗数。 2.0
RsiPeriod RSI の計算に使用されるバーの数。 14
RsiOverbought RSI のしきい値は、ショートエントリーの買われ過ぎの状態を示します。 70
RsiOversold RSI のしきい値は、ロングエントリーの売られ過ぎ状態を示します。 30

すべてのパラメータは、StockSharp デザイナーまたはランナーによる最適化をサポートしています。売られすぎ/買われすぎのレベルを調整すると、反転検出の積極性が変わります。一方、Bollinger の幅は、シグナルが考慮される前に価格がどこまで伸びる必要があるかを制御します。

使用上の注意

  • この戦略では、自動ローソク足サブスクリプションとインジケーター バインディングを備えた高レベルの StockSharp API を使用します。
  • すべての取引操作は成行注文 (BuyMarket/SellMarket) に依存します。ストップロスとテイクプロフィットのレベルは、未決注文としてではなくコードで処理されます。
  • デフォルト設定は日中チャートでの大きな反転をターゲットにしていますが、CandleType を変更することでより高い時間枠に適応させることができます。
  • ライブ環境で実行する場合は、戦略を追加のフィルター (トレンド、ボラティリティ、セッション時間) と組み合わせることを検討してください。
using System;
using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from "Reverse" MetaTrader expert.
/// Uses Bollinger Band touches with RSI confirmation for mean-reversion entries.
/// Enters long when price crosses above lower band with RSI oversold,
/// enters short when price crosses below upper band with RSI overbought.
/// </summary>
public class ReverseStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<decimal> _bollingerWidth;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<decimal> _rsiOversold;

	private ExponentialMovingAverage _ema;
	private RelativeStrengthIndex _rsi;

	private decimal _prevClose;
	private decimal _prevRsi;
	private decimal _prevLower;
	private decimal _prevUpper;
	private bool _hasPrev;

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

	public int BollingerPeriod
	{
		get => _bollingerPeriod.Value;
		set => _bollingerPeriod.Value = value;
	}

	public decimal BollingerWidth
	{
		get => _bollingerWidth.Value;
		set => _bollingerWidth.Value = value;
	}

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

	public decimal RsiOverbought
	{
		get => _rsiOverbought.Value;
		set => _rsiOverbought.Value = value;
	}

	public decimal RsiOversold
	{
		get => _rsiOversold.Value;
		set => _rsiOversold.Value = value;
	}

	public ReverseStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for signals", "General");

		_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Period", "MA length for Bollinger Bands", "Indicators");

		_bollingerWidth = Param(nameof(BollingerWidth), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Width", "Standard deviation multiplier", "Indicators");

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

		_rsiOverbought = Param(nameof(RsiOverbought), 70m)
			.SetDisplay("RSI Overbought", "Upper threshold for short signals", "Signals");

		_rsiOversold = Param(nameof(RsiOversold), 30m)
			.SetDisplay("RSI Oversold", "Lower threshold for long signals", "Signals");
	}

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

		_ema = new ExponentialMovingAverage { Length = BollingerPeriod };
		_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		_hasPrev = false;

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

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

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

		if (!_ema.IsFormed || !_rsi.IsFormed)
			return;

		var close = candle.ClosePrice;
		var bandOffset = emaValue * (BollingerWidth / 100m);
		var upperBand = emaValue + bandOffset;
		var lowerBand = emaValue - bandOffset;

		if (!_hasPrev)
		{
			_prevClose = close;
			_prevRsi = rsiValue;
			_prevLower = lowerBand;
			_prevUpper = upperBand;
			_hasPrev = true;
			return;
		}

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// Long: price crosses up from below lower band + RSI was oversold
		var longSignal = _prevClose < _prevLower && close >= lowerBand && _prevRsi < RsiOversold;
		// Short: price crosses down from above upper band + RSI was overbought
		var shortSignal = _prevClose > _prevUpper && close <= upperBand && _prevRsi > RsiOverbought;

		if (longSignal)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (shortSignal)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		// Exit long at upper band
		if (Position > 0 && close >= upperBand)
			SellMarket(Position);

		// Exit short at lower band
		if (Position < 0 && close <= lowerBand)
			BuyMarket(Math.Abs(Position));

		_prevClose = close;
		_prevRsi = rsiValue;
		_prevLower = lowerBand;
		_prevUpper = upperBand;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_ema = null;
		_rsi = null;
		_prevClose = 0;
		_prevRsi = 0;
		_prevLower = 0;
		_prevUpper = 0;
		_hasPrev = false;

		base.OnReseted();
	}
}