View on GitHub

Mad Trader Strategy

Mad Trader is a trend-following strategy converted from the original MQL expert "madtrader-8.7". It combines ATR and RSI indicators to identify low-volatility pullbacks during an emerging trend. The system waits for ATR to be below a specified threshold but still rising and for RSI to increase within an overall bullish or bearish trend. When these conditions align and the candle body is within defined limits, the strategy opens a market order in the direction suggested by RSI. Positions are protected by a trailing stop and a basket-profit mechanism that closes all trades once the account equity reaches target growth.

Details

  • Entry Criteria:
    • ATR is below MaxAtr and greater than previous ATR value.
    • Candle body size is between MinCandle and MaxCandle.
    • Trading time is within [StartHour, EndHour).
    • RSI trend above 50 and current RSI rising but below RsiLowerLevel → buy.
    • RSI trend below 50 and current RSI falling but above RsiUpperLevel → sell.
    • Enforces a minimum delay of TradeInterval between trades.
  • Long/Short: Both.
  • Exit Criteria:
    • Trailing stop hit.
    • Basket profit target met (BasketProfit or BasketProfit * BasketBoost).
  • Stops: Trailing stop measured in price points.
  • Default Values:
    • AtrPeriod = 14
    • RsiPeriod = 14
    • TrendBars = 60
    • MinCandle = 5
    • MaxCandle = 10
    • MaxAtr = 10
    • RsiUpperLevel = 50
    • RsiLowerLevel = 50
    • StartHour = 0
    • EndHour = 23
    • TradeInterval = 30 minutes
    • TrailingStop = 7
    • BasketProfit = 1.05
    • BasketBoost = 1.1
    • RefreshHours = 24
    • ExponentialGrowth = 0.01
  • Filters:
    • Category: Trend following
    • Direction: Both
    • Indicators: ATR, RSI
    • Stops: Trailing stop
    • Complexity: Moderate
    • Timeframe: Short-term (5 minute candles)
    • 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>
/// RSI-based trading strategy.
/// </summary>
public class MadTraderStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiUpper;
	private readonly StrategyParam<decimal> _rsiLower;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevRsi;
	private bool _hasPrev;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal RsiUpper { get => _rsiUpper.Value; set => _rsiUpper.Value = value; }
	public decimal RsiLower { get => _rsiLower.Value; set => _rsiLower.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public MadTraderStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_rsiUpper = Param(nameof(RsiUpper), 70m)
			.SetDisplay("RSI Upper", "Overbought level", "Indicators");
		_rsiLower = Param(nameof(RsiLower), 30m)
			.SetDisplay("RSI Lower", "Oversold level", "Indicators");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle type", "General");
	}

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

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

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

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		SubscribeCandles(CandleType)
			.Bind(rsi, ProcessCandle)
			.Start();
	}

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

		if (!_hasPrev)
		{
			_prevRsi = rsiVal;
			_hasPrev = true;
			return;
		}

		// Buy when RSI crosses above lower level (oversold exit)
		if (_prevRsi <= RsiLower && rsiVal > RsiLower && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
		// Sell when RSI crosses below upper level (overbought exit)
		else if (_prevRsi >= RsiUpper && rsiVal < RsiUpper && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}

		_prevRsi = rsiVal;
	}
}