This strategy is a direct port of the MetaTrader 4 expert adviser up3x1_Investor. It trades a single instrument using completed candles from a configurable timeframe (H1 by default). The port replicates the original logic with StockSharp high-level APIs and adds clear risk-management parameters.
Trading Logic
The strategy evaluates the last fully closed candle and checks that:
The candle range (high minus low) exceeds 0.0060 price units.
The candle body (absolute difference between open and close) exceeds 0.0050 price units.
If the candle closed bullish and the above conditions are met, the strategy opens a long market position.
If the candle closed bearish and the conditions are met, the strategy opens a short market position.
Trading is completely disabled on Mondays (to mirror the DayOfWeek()==1 guard from the MQL code).
Position Management
Upon entry, the strategy sets internal targets using the configured step-based distances:
TakeProfitPoints → distance to the profit target.
StopLossPoints → protective stop distance.
TrailingStopPoints → distance used to trail the stop once price moves in favor.
Stops and targets are evaluated on every finished candle:
If price reaches the target, the position is closed at the target price.
If price reaches the stop, the position is closed to limit the loss.
Once price advances beyond the trailing distance, the stop is moved closer to the market price to lock in profit.
Additionally, if the 24-period and 60-period simple moving averages calculated on the same candles become equal (within one price step), the position is closed immediately. This mimics the MQL logic where the order is closed when both averages match exactly.
Volume and Risk Management
BaseVolume defines the fallback lot size when no account-based adjustment can be computed.
MaximumRisk replicates the original AccountFreeMargin()*MaximumRisk/1000 formula. If the portfolio value is available, the strategy sizes the position as value * MaximumRisk / 1000, rounded to one decimal place.
DecreaseFactor imitates the loss-streak reduction: after more than one consecutive loss the volume is decreased proportionally to losses / DecreaseFactor.
MinimumVolume ensures that volume never drops below the smallest tradable size used in the MQL script (0.1 lots).
Parameters
Name
Default
Description
BaseVolume
0.1
Base position size in lots when no risk adjustment is applied.
MaximumRisk
0.2
Risk factor used to derive volume from account equity (same as the original EA).
DecreaseFactor
3
Reduces position size after consecutive losses.
MinimumVolume
0.1
Smallest allowed volume.
TakeProfitPoints
20
Profit target distance measured in price steps.
StopLossPoints
50
Stop-loss distance measured in price steps.
TrailingStopPoints
10
Trailing stop distance measured in price steps.
SkipMondays
true
Disable all trading activity on Mondays.
CandleType
1 hour
Timeframe for candle subscription.
Notes
The strategy only keeps one position open at a time, matching the original CalculateCurrentOrders guard.
Consecutive loss tracking is purely internal because StockSharp brokers do not expose MetaTrader order history.
No pending orders are used; all trades are sent as market orders via BuyMarket and SellMarket.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Up3x1InvestorRangeFilterStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose; private decimal _prevEma; private bool _hasPrev;
private int _cooldown;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Up3x1InvestorRangeFilterStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 14).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14).SetDisplay("ATR Period", "ATR lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevEma = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevClose = close; _prevEma = ema;
return;
}
if (_prevClose <= _prevEma && close > ema && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 6;
}
else if (_prevClose >= _prevEma && close < ema && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 6;
}
_prevClose = close; _prevEma = ema;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class up3x1_investor_range_filter_strategy(Strategy):
def __init__(self):
super(up3x1_investor_range_filter_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 14).SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0; self._prev_ema = 0.0; self._has_prev = False; self._cooldown = 0
@property
def ema_period(self): return self._ema_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(up3x1_investor_range_filter_strategy, self).OnReseted()
self._prev_close = 0.0; self._prev_ema = 0.0; self._has_prev = False; self._cooldown = 0
def OnStarted2(self, time):
super(up3x1_investor_range_filter_strategy, self).OnStarted2(time)
self._has_prev = False; self._cooldown = 0
ema = ExponentialMovingAverage(); ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ema):
if candle.State != CandleStates.Finished: return
if not self.IsFormedAndOnlineAndAllowTrading(): return
close = float(candle.ClosePrice); ema_val = float(ema)
if not self._has_prev:
self._prev_close = close; self._prev_ema = ema_val; self._has_prev = True; return
if self._cooldown > 0:
self._cooldown -= 1; self._prev_close = close; self._prev_ema = ema_val; return
if self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume); self._cooldown = 6
elif self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume); self._cooldown = 6
self._prev_close = close; self._prev_ema = ema_val
def CreateClone(self): return up3x1_investor_range_filter_strategy()