The NNFX Auto Trade Strategy replicates the risk-sizing and management workflow of the original NNFX MetaTrader 4 panel inside StockSharp. Instead of a graphical interface, the strategy exposes manual commands through parameters. Traders can request long or short entries, instantly flatten exposure, or apply breakeven and trailing logic that mirrors the expert advisor.
Key characteristics:
ATR-driven volatility sizing with an optional override for manual stop and take-profit distances.
Position entries are split into two parts: one with a projected target and a runner that is left open for discretionary management.
Breakeven and trailing commands operate on demand, updating the stored stop levels without automatically firing on every bar.
Additional capital can be included when computing the monetary risk, matching the MQL script behaviour.
Trading Logic
ATR collection – The strategy subscribes to the configured candle type and processes an Average True Range indicator. When UsePreviousDailyAtr is enabled it copies the previous day's ATR value during the first 12 hours of the new trading day, imitating the original script.
Risk-based sizing – On a manual Buy or Sell command the engine calculates the per-unit monetary risk using the protective stop distance and converts the desired risk percentage into an executable volume.
Position split – The entry volume is divided into two halves. The first half is liquidated automatically when the projected target is touched, while the second half remains until the trader issues further commands.
Stop handling – Initial stops are stored internally and evaluated on every finished candle. Manual commands can push the stop to breakeven or advance it according to the NNFX trailing formula.
Exit controls – CloseAll immediately flattens the book, while stop breaches or partial targets trigger market exits that respect the calculated volumes.
Parameters
Parameter
Default
Description
RiskPercent
2.0
Percentage of account equity (plus AdditionalCapital) risked per trade.
AdditionalCapital
0
Extra capital added to the equity base when sizing positions.
UseAdvancedTargets
false
Switches risk distances from ATR multiples to manual pip values.
AdvancedStopPips
0
Stop distance in pips when advanced mode is active.
AdvancedTakeProfitPips
0
Target distance in pips for the partial exit when advanced mode is active.
UsePreviousDailyAtr
true
Copies the previous daily ATR during the first 12 hours of a new day.
AtrPeriod
14
ATR lookback length.
AtrStopMultiplier
1.5
Multiplier applied to ATR when computing the stop distance.
AtrTakeProfitMultiplier
1.0
Multiplier applied to ATR when computing the take-profit distance.
CandleType
1 Minute
Candle type used for ATR and price monitoring.
BuyCommand
false
Manual flag – set to true to request a long entry. Resets automatically.
SellCommand
false
Manual flag – set to true to request a short entry. Resets automatically.
BreakevenCommand
false
Manual flag – move the protective stop to the entry price. Resets automatically.
TrailingCommand
false
Manual flag – apply the NNFX trailing formula once. Resets automatically.
CloseAllCommand
false
Manual flag – close all open positions instantly. Resets automatically.
Usage Notes
The strategy requires a connected portfolio and security with valid Step, StepPrice, and VolumeStep metadata for accurate risk calculations.
Commands are evaluated on finished candles, so a new bar (or candle update) must be received after toggling a manual parameter.
When using advanced distances ensure both AdvancedStopPips and AdvancedTakeProfitPips are populated; otherwise the ATR-based defaults will remain in effect.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// NNFX Auto Trade strategy: ATR-based trend following with EMA filter.
/// Enters on EMA direction with ATR-based trailing stop management.
/// </summary>
public class NnfxAutoTradeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _entryPrice;
private decimal _bestPrice;
private bool _wasBullish;
private bool _hasPrevSignal;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public NnfxAutoTradeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(120).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend filter period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period", "Indicators");
_atrMultiplier = Param(nameof(AtrMultiplier), 2.5m)
.SetDisplay("ATR Multiplier", "ATR multiplier for stop", "Risk");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 12)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_bestPrice = 0m;
_wasBullish = false;
_hasPrevSignal = false;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_bestPrice = 0;
_hasPrevSignal = false;
_candlesSinceTrade = SignalCooldownCandles;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var stopDist = atrValue * AtrMultiplier;
var isBullish = close > emaValue;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
// Trailing stop check
if (Position > 0)
{
if (close > _bestPrice) _bestPrice = close;
if (_bestPrice - close > stopDist)
{
SellMarket();
_entryPrice = 0;
_bestPrice = 0;
_candlesSinceTrade = 0;
return;
}
}
else if (Position < 0)
{
if (close < _bestPrice) _bestPrice = close;
if (close - _bestPrice > stopDist)
{
BuyMarket();
_entryPrice = 0;
_bestPrice = 0;
_candlesSinceTrade = 0;
return;
}
}
// Entry signals
if (_hasPrevSignal && isBullish != _wasBullish && _candlesSinceTrade >= SignalCooldownCandles)
{
if (isBullish && Position <= 0)
{
BuyMarket();
_entryPrice = close;
_bestPrice = close;
_candlesSinceTrade = 0;
}
else if (!isBullish && Position >= 0)
{
SellMarket();
_entryPrice = close;
_bestPrice = close;
_candlesSinceTrade = 0;
}
}
_wasBullish = isBullish;
_hasPrevSignal = 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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class nnfx_auto_trade_strategy(Strategy):
def __init__(self):
super(nnfx_auto_trade_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(120)))
self._ema_period = self.Param("EmaPeriod", 100)
self._atr_period = self.Param("AtrPeriod", 14)
self._atr_multiplier = self.Param("AtrMultiplier", 2.5)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 12)
self._entry_price = 0.0
self._best_price = 0.0
self._was_bullish = False
self._has_prev_signal = False
self._candles_since_trade = 12
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def AtrMultiplier(self):
return self._atr_multiplier.Value
@AtrMultiplier.setter
def AtrMultiplier(self, value):
self._atr_multiplier.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(nnfx_auto_trade_strategy, self).OnReseted()
self._entry_price = 0.0
self._best_price = 0.0
self._was_bullish = False
self._has_prev_signal = False
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(nnfx_auto_trade_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._best_price = 0.0
self._has_prev_signal = False
self._candles_since_trade = self.SignalCooldownCandles
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, atr, self._process_candle).Start()
def _process_candle(self, candle, ema_value, atr_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
stop_dist = float(atr_value) * float(self.AtrMultiplier)
is_bullish = close > float(ema_value)
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
# Trailing stop check
if self.Position > 0:
if close > self._best_price:
self._best_price = close
if self._best_price - close > stop_dist:
self.SellMarket()
self._entry_price = 0.0
self._best_price = 0.0
self._candles_since_trade = 0
return
elif self.Position < 0:
if close < self._best_price:
self._best_price = close
if close - self._best_price > stop_dist:
self.BuyMarket()
self._entry_price = 0.0
self._best_price = 0.0
self._candles_since_trade = 0
return
# Entry signals
if self._has_prev_signal and is_bullish != self._was_bullish and self._candles_since_trade >= self.SignalCooldownCandles:
if is_bullish and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._best_price = close
self._candles_since_trade = 0
elif not is_bullish and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._best_price = close
self._candles_since_trade = 0
self._was_bullish = is_bullish
self._has_prev_signal = True
def CreateClone(self):
return nnfx_auto_trade_strategy()