Ver en GitHub

Estrategia de comercio de automóviles de NNFX

Descripción general

La Estrategia de comercio automático de NNFX replica el flujo de trabajo de gestión y dimensionamiento de riesgos del panel original NNFX MetaTrader 4 dentro de StockSharp. En lugar de una interfaz gráfica, la estrategia expone comandos manuales a través de parámetros. Los operadores pueden solicitar entradas largas o cortas, reducir instantáneamente la exposición o aplicar una lógica de equilibrio y seguimiento que refleje al asesor experto.

Características clave:

  • Tamaño de volatilidad impulsado por ATR con una anulación opcional para distancias de parada y toma de ganancias manuales.
  • Las entradas de posiciones se dividen en dos partes: una con un objetivo proyectado y un corredor que se deja abierto para gestión discrecional.
  • Los comandos de equilibrio y seguimiento funcionan según demanda, actualizando los niveles de parada almacenados sin activarse automáticamente en cada barra.
  • Se puede incluir capital adicional al calcular el riesgo monetario, coincidiendo con el comportamiento del script MQL.

Lógica de trading

  1. Colección ATR: la estrategia se suscribe al tipo de vela configurado y procesa un indicador de rango verdadero promedio. Cuando UsePreviousDailyAtr está habilitado, copia el valor ATR del día anterior durante las primeras 12 horas del nuevo día de negociación, imitando el script original.
  2. Dimensionamiento basado en el riesgo: con un comando manual Buy o Sell, el motor calcula el riesgo monetario por unidad utilizando la distancia de parada protectora y convierte el porcentaje de riesgo deseado en un volumen ejecutable.
  3. División de posición: el volumen de entrada se divide en dos mitades. La primera mitad se liquida automáticamente cuando se toca el objetivo proyectado, mientras que la segunda mitad permanece hasta que el comerciante da más órdenes.
  4. Manejo de paradas: las paradas iniciales se almacenan internamente y se evalúan en cada vela terminada. Los comandos manuales pueden empujar el tope hasta el punto de equilibrio o avanzarlo de acuerdo con la fórmula final de NNFX.
  5. Controles de salida: CloseAll aplana inmediatamente el libro, mientras que las violaciones de los límites o los objetivos parciales desencadenan salidas del mercado que respetan los volúmenes calculados.

Parámetros

Parámetro Predeterminado Descripción
RiskPercent 2.0 Porcentaje del capital de la cuenta (más AdditionalCapital) arriesgado por operación.
AdditionalCapital 0 Capital adicional añadido a la base patrimonial al dimensionar las posiciones.
UseAdvancedTargets false Cambia las distancias de riesgo de múltiplos de ATR a valores de pips manuales.
AdvancedStopPips 0 Distancia de parada en pips cuando el modo avanzado está activo.
AdvancedTakeProfitPips 0 Distancia objetivo en pips para la salida parcial cuando el modo avanzado está activo.
UsePreviousDailyAtr true Copia el ATR diario anterior durante las primeras 12 horas de un nuevo día.
AtrPeriod 14 ATR longitud retrospectiva.
AtrStopMultiplier 1.5 Multiplicador aplicado a ATR al calcular la distancia de parada.
AtrTakeProfitMultiplier 1.0 Multiplicador aplicado a ATR al calcular la distancia de obtención de beneficios.
CandleType 1 Minute Tipo de vela utilizado para ATR y seguimiento de precios.
BuyCommand false Bandera manual: configurada en true para solicitar una entrada larga. Se reinicia automáticamente.
SellCommand false Bandera manual: configurada en true para solicitar una entrada breve. Se reinicia automáticamente.
BreakevenCommand false Bandera manual: mueve el stop de protección al precio de entrada. Se reinicia automáticamente.
TrailingCommand false Bandera manual: aplique la fórmula final NNFX una vez. Se reinicia automáticamente.
CloseAllCommand false Bandera manual: cierre todas las posiciones abiertas al instante. Se reinicia automáticamente.

Notas de uso

  • La estrategia requiere una cartera conectada y seguridad con metadatos Step, StepPrice y VolumeStep válidos para realizar cálculos de riesgo precisos.
  • Los comandos se evalúan en velas terminadas, por lo que se debe recibir una nueva barra (o actualización de vela) después de alternar un parámetro manual.
  • Cuando utilice distancias avanzadas, asegúrese de que tanto AdvancedStopPips como AdvancedTakeProfitPips estén completos; de lo contrario, los valores predeterminados basados ​​en ATR seguirán vigentes.
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;
	}
}