The TrendScalperStrategy is a C# conversion of the MetaTrader 4 expert advisor Currencyprofits_01_1.mq4. The original robot is a lightweight trend-following scalper that combines a short-term EMA/SMA crossover filter with breakout entries around the most recent swing highs and lows. The StockSharp port keeps the same decision rules while embracing the framework's high-level candle subscription and indicator pipeline.
Trading Logic
Indicators
Fast EMA (default 6) on close prices.
Slow SMA (default 12) on close prices.
Highest High (default window 6) and Lowest Low (default window 6) calculated from the candle highs and lows.
Entry Conditions
Long: price sweeps into the recent low band (Lowest Low) while the fast EMA is above the slow SMA. The strategy sends a market buy order with the volume defined by the money-management rule.
Short: price touches the recent high band (Highest High) while the fast EMA is below the slow SMA. A market sell order is placed using the same volume calculation.
The system stays flat while a position is open, mirroring the single-order behaviour of the MQL version.
Exit Conditions
Long exit: when an open long position sees the candle high break the recorded Highest High, the position is closed at market.
Short exit: when an open short position observes the candle low fall through the Lowest Low, the short is covered at market.
A protective stop-loss managed by StartProtection is attached to every trade when StopLossPoints is greater than zero.
Money Management
The lot sizing logic reproduces the three modes exposed in the MQL script:
Mode
Description
Behaviour in the port
0
Fixed lots (LotsIfNoMM).
Returns the configured FixedVolume.
<0
Fractional lots computed from the account balance and risk factor.
Uses the same base formula, but the result is rounded up to the next integer, floored at 1 lot and capped at 100.
The balance is taken from Portfolio.CurrentValue (falling back to BeginValue). If the portfolio value is unavailable, the strategy reverts to the fixed volume so orders are still emitted during backtests.
Risk Management
Stop-loss: the StopLossPoints parameter is expressed in price points (pips). During OnStarted the distance is multiplied by Security.PriceStep and passed to StartProtection, letting StockSharp maintain the protective order.
Single position: the logic enforces Position == 0 before opening a new trade, preventing overlapping positions exactly like the MT4 expert.
Parameters
Name
Default
Description
CandleType
15-minute time frame
Candle series used for indicator calculations and signals.
FastLength
6
Period of the fast EMA.
SlowLength
12
Period of the slow SMA.
BreakoutWindow
6
Number of candles inspected for the highest high / lowest low breakout filter.
FixedVolume
0.1 lots
Volume when money management is disabled or fallback is required.
MoneyManagementMode
0
Selects between fixed, fractional, or rounded lot sizing.
MoneyManagementRisk
40
Risk factor multiplier used in balance-based lot sizing.
StopLossPoints
50
Stop-loss distance in price points (converted to absolute price before calling StartProtection).
Implementation Notes
Indicator chaining relies on the high-level SubscribeCandles().Bind(...) workflow; no manual series buffering is required.
Comments in the code were added in English to match the repository guidelines.
No unit tests were modified; the focus of this conversion is the strategy and its accompanying documentation.
Usage Tips
Select a candle interval that matches the original trading environment (e.g., short intraday time frames for scalping).
Ensure the portfolio has a valid PriceStep so the stop-loss conversion to absolute price works correctly.
Adjust MoneyManagementRisk carefully: higher values lead to larger positions because of the ceil(balance * risk / 10000) calculation inherited from the MQL expert.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trend Scalper strategy - EMA crossover with Highest/Lowest breakout confirmation.
/// Buys when fast EMA crosses above slow EMA and close is near highest.
/// Sells when fast EMA crosses below slow EMA and close is near lowest.
/// </summary>
public class TrendScalperStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TrendScalperStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 8)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_channelPeriod = Param(nameof(ChannelPeriod), 20)
.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevFast = fast;
_prevSlow = slow;
}
}
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.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class trend_scalper_strategy(Strategy):
def __init__(self):
super(trend_scalper_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 8).SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21).SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._channel_period = self.Param("ChannelPeriod", 20).SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
@property
def fast_period(self): return self._fast_period.Value
@property
def slow_period(self): return self._slow_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(trend_scalper_strategy, self).OnReseted()
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
def OnStarted2(self, time):
super(trend_scalper_strategy, self).OnStarted2(time)
self._has_prev = False
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.process_candle).Start()
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished: return
f = float(fast); s = float(slow)
if not self._has_prev:
self._prev_fast = f; self._prev_slow = s; self._has_prev = True; return
if self._prev_fast <= self._prev_slow and f > s and self.Position <= 0:
if self.Position < 0: self.BuyMarket()
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and f < s and self.Position >= 0:
if self.Position > 0: self.SellMarket()
self.SellMarket()
self._prev_fast = f; self._prev_slow = s
def CreateClone(self): return trend_scalper_strategy()