The Smart Forex System Strategy is a StockSharp port of the MetaTrader expert advisor "Smart Forex System". The robot blends a single-candle momentum filter with a martingale-style averaging grid. The first trade is opened when the previous candle shows a strong directional close and the current price has moved sufficiently away from the reference close. Additional entries are added at fixed pip intervals in the adverse direction, with the position size increasing by a configurable multiplier. The strategy manages exits through averaged take-profit levels and a safety stop-loss linked to the latest grid order.
Trading Logic
Signal generation
Evaluate the last completed candle on the selected timeframe.
Calculate a momentum ratio: (current close - previous close) / previous close * 10,000.
If the previous candle is bearish and the momentum is lower than the negative threshold, a long basket may start.
If the previous candle is bullish and the momentum exceeds the positive threshold, a short basket may start.
Trading can be limited to long-only, short-only, both directions, or disabled entirely via the Trading Mode parameter.
Grid expansion
Once a basket exists, new entries are added whenever price moves against the position by at least Grid Step pips from the price of the last order.
Each new order volume is multiplied by Lot Multiplier. Volumes are clamped to broker limits and the configured Max Volume.
The basket stops growing when the number of orders reaches Max Trades.
Exit management
A hard stop-loss is placed Stop Loss pips away from the price of the latest order. Breaching that distance closes the entire basket.
Take-profit levels depend on the basket size:
A single order uses First Take Profit pips from the volume-weighted average entry price.
Multiple orders use Grid Take Profit pips from the same average entry price to capture smaller rebounds.
Exits are processed on finished candles to ensure the indicators have final values.
Risk Management Notes
The martingale-like position sizing dramatically increases exposure in adverse trends. Use conservative multipliers and basket sizes on highly volatile instruments.
The default stop-loss (400 pips) is intentionally wide to mirror the original EA. Consider aligning it with the instrument's ATR if smaller losses are required.
Grid trading consumes margin quickly. Ensure the account leverage, contract size and Start Volume parameters are consistent with the broker specifications.
Parameters
Name
Description
Default
Trading Mode
Allowed trade direction (long-only, short-only, both, or disabled).
Long & Short
Momentum Threshold
Minimum momentum in pseudo-pips required to trigger a signal.
1
Start Volume
Volume of the very first order in a new basket.
0.01
Max Volume
Hard cap applied to any single order volume.
2
Lot Multiplier
Multiplier used when sizing subsequent grid orders.
1.5
Grid Step
Minimum distance in pips before adding the next order.
26
Max Trades
Maximum number of orders allowed per direction.
12
First Take Profit
Take-profit distance in pips when only one order is open.
30
Grid Take Profit
Take-profit distance in pips once the basket holds multiple orders.
7
Stop Loss
Stop distance in pips from the latest order price.
400
Candle Type
Timeframe used for signal evaluation.
1-hour candles
Recommended Usage
Attach the strategy to a forex symbol with sufficient liquidity and a predictable spread.
Set the Candle Type to match the original EA's operating timeframe (H1 by default) or adapt it to your preferred horizon.
Optimise the grid spacing, multiplier, and momentum filter on historical data before live deployment.
Monitor margin usage closely. The basket can grow rapidly, so consider coupling the strategy with account-wide equity protection.
Avoid overlapping with other grid-based systems on the same instrument to reduce the risk of compounding drawdowns.
Differences Compared to the MetaTrader Version
The StockSharp port works with finished candles instead of tick-by-tick updates, which reduces noise and makes the logic deterministic.
Order volumes are adjusted using StockSharp security metadata (min, max, and step), ensuring compatibility with a wide range of brokers.
Take-profit and stop-loss checks are handled inside the strategy logic instead of submitting individual order modifications for every grid level.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Smart Forex System strategy: Triple EMA alignment.
/// Enters when fast > mid > slow (buy) or fast < mid < slow (sell).
/// </summary>
public class SmartForexSystemStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _midPeriod;
private readonly StrategyParam<int> _slowPeriod;
private bool _wasBullishAlignment;
private bool _hasPrevAlignment;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int MidPeriod { get => _midPeriod.Value; set => _midPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public SmartForexSystemStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_midPeriod = Param(nameof(MidPeriod), 25)
.SetGreaterThanZero()
.SetDisplay("Mid EMA", "Mid EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_wasBullishAlignment = false;
_hasPrevAlignment = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_wasBullishAlignment = false;
_hasPrevAlignment = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var mid = new ExponentialMovingAverage { Length = MidPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, mid, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal midValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
var bullishAlignment = fastValue > midValue && midValue > slowValue;
var bearishAlignment = fastValue < midValue && midValue < slowValue;
var crossedUp = bullishAlignment && (!_hasPrevAlignment || !_wasBullishAlignment);
var crossedDown = bearishAlignment && (!_hasPrevAlignment || _wasBullishAlignment);
if (crossedUp && Position <= 0)
BuyMarket();
else if (crossedDown && Position >= 0)
SellMarket();
if (bullishAlignment || bearishAlignment)
{
_wasBullishAlignment = bullishAlignment;
_hasPrevAlignment = true;
}
}
}
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 smart_forex_system_strategy(Strategy):
def __init__(self):
super(smart_forex_system_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._fast_period = self.Param("FastPeriod", 10)
self._mid_period = self.Param("MidPeriod", 25)
self._slow_period = self.Param("SlowPeriod", 50)
self._was_bullish_alignment = False
self._has_prev_alignment = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def MidPeriod(self):
return self._mid_period.Value
@MidPeriod.setter
def MidPeriod(self, value):
self._mid_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
def OnReseted(self):
super(smart_forex_system_strategy, self).OnReseted()
self._was_bullish_alignment = False
self._has_prev_alignment = False
def OnStarted2(self, time):
super(smart_forex_system_strategy, self).OnStarted2(time)
self._was_bullish_alignment = False
self._has_prev_alignment = False
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
mid = ExponentialMovingAverage()
mid.Length = self.MidPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, mid, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_value, mid_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
mid_val = float(mid_value)
slow_val = float(slow_value)
bullish_alignment = fast_val > mid_val and mid_val > slow_val
bearish_alignment = fast_val < mid_val and mid_val < slow_val
crossed_up = bullish_alignment and (not self._has_prev_alignment or not self._was_bullish_alignment)
crossed_down = bearish_alignment and (not self._has_prev_alignment or self._was_bullish_alignment)
if crossed_up and self.Position <= 0:
self.BuyMarket()
elif crossed_down and self.Position >= 0:
self.SellMarket()
if bullish_alignment or bearish_alignment:
self._was_bullish_alignment = bullish_alignment
self._has_prev_alignment = True
def CreateClone(self):
return smart_forex_system_strategy()