Ver no GitHub

Tops Bottoms Trend RSI Strategy

Overview

This strategy is a StockSharp port of the MetaTrader expert advisor "Tops bottoms trend and rsi ea". It monitors finished candles of the selected timeframe, searches for emerging trend tops or bottoms within a configurable lookback window, and confirms each opportunity with a Relative Strength Index (RSI) filter. When the criteria are met the strategy opens a single market order and immediately assigns protective stop-loss and take-profit levels derived from pip-based distances.

Trading Logic

  • Data source – the algorithm subscribes to the configured candle type and evaluates only finished candles to avoid using incomplete data.
  • Bottom detection (long setup) – the close of the latest candle must be at least BuyTrendPips pips below the high of the candle BuyTrendCandles bars ago. All intermediate lows must stay above the current close, and the quality filter (BuyTrendQuality) requires that recent highs do not deviate too much from the reference high. When this structure forms and the previous candle's RSI value is below BuyRsiThreshold, the strategy opens a long position with volume BuyVolume.
  • Top detection (short setup) – the close of the latest candle must be at least SellTrendPips pips above the low of the candle SellTrendCandles bars ago. Intermediate highs must remain below the current close while the quality filter (SellTrendQuality) keeps recent lows close to the reference low. If the previous candle's RSI value exceeds SellRsiThreshold, the strategy opens a short position with volume SellVolume.
  • Risk management – after each entry the strategy stores the fill price and calculates pip-based protective levels. Stop-loss offsets use BuyStopLossPips or SellStopLossPips. Take-profit distances are primarily derived from the stop via BuyTakeProfitPercentOfStop or SellTakeProfitPercentOfStop. If the long take-profit percentage is disabled (0) the fixed BuyTakeProfitPips distance is used instead. Whenever subsequent candles touch the corresponding stop or take-profit levels the position is closed with a market order.
  • Position control – the system keeps at most one open position. New signals are ignored while a position or active order exists. RSI confirmation always relies on the previous candle (one-bar shift), mirroring the original EA.

Parameters

Name Description Default
BuyVolume Order volume used for long positions. 0.01
BuyStopLossPips Stop-loss distance for long trades in pips. 20
BuyTakeProfitPips Fixed take-profit distance in pips for longs when percentage mode is disabled. 5
BuyTakeProfitPercentOfStop Take-profit as a percentage of the long stop-loss distance. 100
SellVolume Order volume used for short positions. 0.01
SellStopLossPips Stop-loss distance for short trades in pips. 20
SellTakeProfitPercentOfStop Take-profit as a percentage of the short stop-loss distance. 100
SellTrendCandles Number of candles inspected when searching for new tops. 10
SellTrendPips Minimum advance above the reference low required for a short setup (pips). 20
SellTrendQuality Trend-quality filter for short setups (clamped to the 1–9 range). 5
BuyTrendCandles Number of candles inspected when searching for new bottoms. 10
BuyTrendPips Minimum decline below the reference high required for a long setup (pips). 20
BuyTrendQuality Trend-quality filter for long setups (clamped to the 1–9 range). 5
BuyRsiPeriod RSI period used for long confirmations. 14
BuyRsiThreshold RSI oversold threshold that must be crossed from above to enable long entries. 40
SellRsiPeriod RSI period used for short confirmations. 14
SellRsiThreshold RSI overbought threshold that must be crossed from below to enable short entries. 60
CandleType Timeframe of the candles processed by the strategy. 30-minute time frame

Notes

  • Pip distances are converted to prices using the security's PriceStep. Five-digit and fractional-pip forex quotes are normalised to the classic pip size, replicating the conversion rules from the original EA.
  • Because the RSI confirmation uses the previous candle (shift = 1), the strategy needs at least one fully formed RSI value before it can trade. The first few candles after startup are therefore ignored.
  • The logic cancels all protective levels whenever a position is fully closed, ensuring that the next entry starts with fresh risk parameters.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Tops Bottoms Trend RSI strategy: RSI trend direction with EMA filter.
/// Buys when RSI above 50 and close above EMA, sells when RSI below 50 and close below EMA.
/// </summary>
public class TopsBottomsTrendRsiStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _emaPeriod;

	private decimal _prevRsi;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }

	public TopsBottomsTrendRsiStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_rsiPeriod = Param(nameof(RsiPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA trend filter period", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevRsi = 0;
		_hasPrev = false;
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ema, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			if (_prevRsi <= 45 && rsiValue > 45 && candle.ClosePrice > emaValue && Position <= 0)
				BuyMarket();
			else if (_prevRsi >= 55 && rsiValue < 55 && candle.ClosePrice < emaValue && Position >= 0)
				SellMarket();
		}

		_prevRsi = rsiValue;
		_hasPrev = true;
	}
}