View on GitHub

NNFX Auto Trade Strategy

Overview

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. Exit controlsCloseAll 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;
	}
}