Ver en GitHub

Reversal Catcher Strategy

Reversal Catcher enters when price pushes beyond a Bollinger band and then re-enters while momentum shifts. It relies on a fast and slow EMA to define trend direction and uses RSI crosses of overbought or oversold levels to time entries. Targets and stops are derived from Bollinger band levels and the prior candle's extreme. Positions may optionally close at a specified end-of-day time.

Details

  • Entry Criteria: Price re-enters Bollinger Bands with higher high/low structure and RSI crossing extremes.
  • Long/Short: Both
  • Exit Criteria: Stop loss, target, or end-of-day flat
  • Stops: Previous candle extreme
  • Default Values:
    • BollingerPeriod = 20
    • BollingerDeviation = 1.5
    • FastEmaPeriod = 21
    • SlowEmaPeriod = 50
    • RsiPeriod = 14
    • Overbought = 70
    • Oversold = 30
    • EndOfDay = 1500
    • CandleType = 5 minute
  • Filters:
    • Category: Reversal
    • Direction: Both
    • Indicators: Bollinger Bands, EMA, RSI
    • Stops: Yes
    • Complexity: Intermediate
    • Timeframe: Intraday
    • Seasonality: No
    • Neural networks: No
    • Divergence: No
    • Risk level: Medium
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>
/// Reversal Catcher strategy using EMA crossover.
/// </summary>
public class ReversalCatcherStrategy : Strategy
{
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<DataType> _candleType;

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

	public ReversalCatcherStrategy()
	{
		_slowLength = Param(nameof(SlowLength), 40)
			.SetGreaterThanZero()
			.SetDisplay("Slow Length", "Slow EMA period", "General");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle type", "General");
	}

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		var fast = new ExponentialMovingAverage { Length = 14 };
		var slow = new ExponentialMovingAverage { Length = SlowLength };
		var prevF = 0m; var prevS = 0m; var init = false;
		var lastSignal = DateTimeOffset.MinValue;
		var cooldown = TimeSpan.FromMinutes(360);
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, (candle, f, s) =>
		{
			if (candle.State != CandleStates.Finished) return;
			if (!fast.IsFormed || !slow.IsFormed) return;
			if (!init) { prevF = f; prevS = s; init = true; return; }
			if (candle.OpenTime - lastSignal >= cooldown)
			{
				if (prevF <= prevS && f > s && Position <= 0) { BuyMarket(); lastSignal = candle.OpenTime; }
				else if (prevF >= prevS && f < s && Position >= 0) { SellMarket(); lastSignal = candle.OpenTime; }
			}
			prevF = f; prevS = s;
		}).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
	}
}