This strategy ports the MetaTrader expert advisor Trickerless RHMP to StockSharp's high level API. It keeps the multi-stage
entry logic of the original robot – combining Average Directional Index confirmation, smoothed moving average structure and
volatility driven position management – while following the framework conventions documented in AGENTS.md.
Trading Logic
Indicators
Average True Range (ATR) with configurable period for volatility sizing.
Average Directional Index (ADX) with full +DI/-DI components to qualify trend strength.
Two smoothed moving averages (SMMA) representing the fast and slow trend filters.
Trend evaluation
The slow SMMA slope must be within the MinSlopePips…MaxSlopePips corridor (measured in instrument pips).
ADX must exceed AdxThreshold and rise compared with the previous candle.
Price has to stay at least TrendSpacePips away from the fast SMMA to avoid congestion.
A bullish bias requires the fast SMMA above the slow SMMA, +DI ≥ -DI and a rising fast average. Bearish bias mirrors these
checks.
Primary entries
When the bullish (or bearish) bias is active, the strategy opens a long (or short) order with volume OrderVolume, respecting
MaxNetPositions and waiting at least SleepInterval between entries.
If an opposite net position exists it is flattened first to keep hedging disabled.
Spike entries
If the current candle range exceeds CandleSpikeMultiplier times the previous range, the strategy can fire an auxiliary
position in the direction of the candle body when the ADX components agree. The position uses OrderVolume * SpikeVolumeMultiplier.
Risk Management
ATR-based stop-loss, take-profit and optional trailing-stop (StopLossAtrMultiplier, TakeProfitAtrMultiplier, TrailingAtrMultiplier).
Session-wide protection: once realized PnL reaches DailyProfitTarget (fraction of starting equity), new entries are blocked.
Global emergency switch EmergencyExit closes all positions immediately when toggled.
Parameters
Name
Description
Default
CandleType
Timeframe used for all calculations.
5-minute candles
OrderVolume
Base volume for each entry.
0.03
AtrPeriod
ATR lookback length.
14
AdxPeriod
ADX lookback length.
14
AdxThreshold
Minimum ADX value to enable trading.
10
FastMaPeriod
Fast smoothed moving average period.
60
SlowMaPeriod
Slow smoothed moving average period.
120
MinSlopePips / MaxSlopePips
Allowed slope corridor for the slow SMMA.
2 / 9
TrendSpacePips
Minimal price distance from the fast SMMA (in pips).
5
CandleSpikeMultiplier
How much larger the candle range must be to trigger spike entries.
7
TakeProfitAtrMultiplier
ATR multiples for take-profit.
1.0
StopLossAtrMultiplier
ATR multiples for stop-loss.
1.5
TrailingAtrMultiplier
ATR multiples for trailing-stop (0 disables).
0
MaxNetPositions
Maximum number of simultaneous net position units.
1
SleepInterval
Minimum time between consecutive entries.
24 minutes
DailyProfitTarget
Fraction of initial equity that blocks trading once reached.
0.045
AllowNewEntries
Master switch to enable/disable entries.
true
SpikeVolumeMultiplier
Volume multiplier for spike entries.
1.0
EmergencyExit
Closes all positions immediately when true.
false
Notes
The StockSharp port focuses on the clean high level API instead of the ticket-by-ticket micro-management from MetaTrader. All
money management logic is implemented through Volume and ATR based levels.
The original EA had several balance and margin checks. These are approximated with the DailyProfitTarget, MaxNetPositions
and ATR sizing parameters so that the behaviour stays aligned without direct MT4 account calls.
Because the strategy uses smoothed averages, make sure a sufficient warm-up period is present before evaluating trades.
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 "Trickerless RHMP" MetaTrader expert.
/// Uses fast/slow EMA crossover for entries.
/// Fast EMA above slow = buy, below = sell.
/// </summary>
public class TrickerlessRhmpStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private ExponentialMovingAverage _fastMa;
private ExponentialMovingAverage _slowMa;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastMaPeriod
{
get => _fastMaPeriod.Value;
set => _fastMaPeriod.Value = value;
}
public int SlowMaPeriod
{
get => _slowMaPeriod.Value;
set => _slowMaPeriod.Value = value;
}
public TrickerlessRhmpStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe", "General");
_fastMaPeriod = Param(nameof(FastMaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast MA", "Fast EMA period", "Indicators");
_slowMaPeriod = Param(nameof(SlowMaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow MA", "Slow SMA period (computed manually)", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
_fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
_slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastMa, _slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fastMa.IsFormed || !_slowMa.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_prevFast is null || _prevSlow is null)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
var crossUp = _prevFast.Value <= _prevSlow.Value && fastValue > slowValue;
var crossDown = _prevFast.Value >= _prevSlow.Value && fastValue < slowValue;
if (crossUp)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (crossDown)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_fastMa = null;
_slowMa = null;
_prevFast = null;
_prevSlow = null;
base.OnReseted();
}
}