The RobotPowerM5 Meta4 V12 strategy is a C# port of the MetaTrader 4 expert advisor RobotPowerM5_meta4V12.mq4. The original EA
was designed for five-minute Forex charts and evaluates the balance between Bulls Power and Bears Power to decide whether a new
long or short position should be opened. The StockSharp version keeps the one-position-at-a-time behaviour, reproduces the point-
based stop-loss / take-profit settings, and reimplements the trailing-stop logic that gradually locks in profits once the market
moves in the trade's favour.
Trading Logic
Indicator engine
Five-minute candles are subscribed by default (the timeframe is configurable through the CandleType parameter).
A pair of StockSharp indicators, BullsPower and BearsPower, are updated on every finished candle using the configured
averaging period.
The combined value BullsPower + BearsPower is stored with a one-bar delay in order to mimic the shift=1 calls from the
MQL code, which always operate on the last fully closed bar.
Entry rules
When no position is open and the delayed sum of Bulls/Bears Power is positive, a market buy order is issued.
When no position is open and the delayed sum is negative, a market sell order is issued.
Signals are ignored while a position is active; the trade is managed exclusively through protective exits.
Volume handling
The Volume parameter represents the requested lot size. It is passed directly to BuyMarket / SellMarket, allowing the
connector to round to the instrument's lot step if necessary.
Risk Management
Stop-loss – The initial stop is placed StopLossPoints MetaTrader points away from the average fill price. The level is
monitored with candle lows (for longs) or highs (for shorts); once touched the strategy exits at market.
Take-profit – The profit target is TakeProfitPoints points from the entry and is evaluated on candle highs/lows, matching
how MT4 closes positions when a target is hit intrabar.
Trailing stop – After the price moves in the trade's favour by more than TrailingStopPoints, a trailing stop is activated.
For long positions the stop is shifted to referencePrice - trailingDistance, where the reference is the maximum of the candle's
close and high. For shorts the stop follows referencePrice + trailingDistance, using the minimum of the candle's close and low.
This reproduces the EA's trailing behaviour that was originally implemented with OrderModify.
Parameters
Name
Description
Default
Notes
BullBearPeriod
Averaging period supplied to both Bulls Power and Bears Power indicators.
5
Increasing the value smooths the momentum filter.
Volume
Requested lot size for every entry.
1
The actual traded volume depends on the broker's lot step and limits.
StopLossPoints
Initial protective stop distance in MetaTrader points.
45
Set to 0 to disable the hard stop-loss.
TakeProfitPoints
Take-profit distance in MetaTrader points.
150
Set to 0 to trade without a fixed profit target.
TrailingStopPoints
Distance used by the trailing stop once the trade is profitable.
15
Set to 0 to disable trailing.
CandleType
Timeframe used for indicator calculations.
5m time frame
Any other DataType can be selected if needed.
Implementation Notes
The strategy stores all risk levels (stop-loss, take-profit, trailing stop) internally and issues market exits when candles
confirm that a price threshold was breached. This mirrors the MT4 approach where orders were modified tick-by-tick.
Indicator subscriptions are wired via Subscription.Bind, which feeds both Bulls Power and Bears Power into a single callback.
The point size is derived from Security.PriceStep, keeping the parameters compatible with instruments that quote in ticks,
pips, or cents.
Entry checks always use the previous indicator values, ensuring that partially formed candles never trigger orders.
Differences vs. the MQL Version
Trade management uses explicit market exits instead of modifying the stop-loss order in place; this is more robust across
different StockSharp connectors while producing the same outcome.
Parameter ranges are validated through StrategyParam helpers so that invalid values (for example, negative trailing stops) are
rejected at configuration time.
Detailed logging hooks, chart output, and candle subscriptions leverage StockSharp's high-level API instead of manual tick loops.
The expert identifier string present in the MT4 script is not required in StockSharp and has therefore been omitted.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// RobotPowerM5: RSI momentum with EMA filter and ATR stops.
/// </summary>
public class RobotPowerM5Meta4V12Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevRsi;
private decimal _entryPrice;
private int _cooldown;
public RobotPowerM5Meta4V12Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_emaLength = Param(nameof(EmaLength), 50)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0;
_entryPrice = 0;
_cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_entryPrice = 0;
_cooldown = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevRsi == 0 || atrVal <= 0)
{
_prevRsi = rsiVal;
return;
}
if (_cooldown > 0) { _cooldown--; _prevRsi = rsiVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 80)
{
SellMarket();
_entryPrice = 0;
_cooldown = 10;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 20)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 10;
}
}
if (Position == 0)
{
if (rsiVal > 65 && _prevRsi <= 65 && close > emaVal)
{
_entryPrice = close;
BuyMarket();
_cooldown = 10;
}
else if (rsiVal < 35 && _prevRsi >= 35 && close < emaVal)
{
_entryPrice = close;
SellMarket();
_cooldown = 10;
}
}
_prevRsi = rsiVal;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage, AverageTrueRange
class robot_power_m5_meta4_v12_strategy(Strategy):
def __init__(self):
super(robot_power_m5_meta4_v12_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_rsi = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(robot_power_m5_meta4_v12_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._entry_price = 0.0
self._cooldown = 0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
ev = float(ema_val)
av = float(atr_val)
if self._prev_rsi == 0 or av <= 0:
self._prev_rsi = rv
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_rsi = rv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if close >= self._entry_price + av * 2.5 or close <= self._entry_price - av * 1.5 or rv > 80:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 10
elif self.Position < 0:
if close <= self._entry_price - av * 2.5 or close >= self._entry_price + av * 1.5 or rv < 20:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 10
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
if self.Position == 0:
if rv > 65 and self._prev_rsi <= 65 and close > ev:
self._entry_price = close
self.BuyMarket()
self._cooldown = 10
elif rv < 35 and self._prev_rsi >= 35 and close < ev:
self._entry_price = close
self.SellMarket()
self._cooldown = 10
self._prev_rsi = rv
def OnReseted(self):
super(robot_power_m5_meta4_v12_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._entry_price = 0.0
self._cooldown = 0
def CreateClone(self):
return robot_power_m5_meta4_v12_strategy()