在 GitHub 上查看

Nina EA 策略

概述

Nina EA 策略是从 MetaTrader 4 专家顾问 “NinaEA” 转换而来的趋势跟随系统。原始版本依赖自定义指标 NINA,通过比较指标的多头与空头缓冲区,并在差值穿越零轴时进出场。StockSharp 版本使用内置 SuperTrend 指标替换该自定义指标,因为 SuperTrend 同样提供多头与空头缓冲。SuperTrend 方向的翻转即被视作原策略中的零轴穿越:趋势转多即买入,趋势转空则卖出。

策略始终只持有一笔仓位。出现反向信号时会立即平掉当前仓位,并在相反方向重新入场。可以按价格点数设置可选的止损,复现原输入参数 StopLoss 的作用。

交易流程

  1. 订阅所选蜡烛序列,并使用给定的 ATR 周期和倍数计算 SuperTrend。
  2. 等待策略与指标形成后再处理信号,避免使用未完成的数据。
  3. 每根已完成的蜡烛都会触发以下检查:
    • 若价格触及保护性止损,则立刻市价平仓。
    • 若 SuperTrend 从空头翻转为多头,先平掉任何空单,再按照设定手数买入。
    • 若 SuperTrend 从多头翻转为空头,先平掉多单,再按照设定手数卖出。
    • 记录当前的 SuperTrend 方向,以便识别下一次翻转。

这种流程忠实再现了原专家中 nina = Buffer0 - Buffer1 的符号判断方式。

仓位与风控

  • 策略任何时刻都只持有一笔仓位,信号到来时通过反向开仓实现持仓翻转,不会加仓。
  • 可选止损以价格点数表示,按成交价计算:做多时止损设置在下方,做空时设置在上方。设为 0 时表示不启用止损,与原始 MQL 代码一致。
  • 调用了 StartProtection(),如需进一步的内置风控可以基于此进行扩展。

参数说明

参数 默认值 说明
Volume 0.1 每次入场的下单数量。
AtrPeriod 10 SuperTrend 所用的 ATR 周期,对应原参数 PeriodWATR
AtrMultiplier 1 SuperTrend 使用的 ATR 倍数,对应原参数 Kwatr
StopLossPoints 0 止损距离(价格点)。为 0 时不启用止损,与原始脚本的行为一致。
CandleType TimeFrame(1 minute) 用于驱动策略逻辑的蜡烛类型。

转换说明

  • 由于原策略只使用 NINA 指标两个缓冲区的符号差异,因此可以直接用 SuperTrend 的趋势方向 (IsUpTrend) 作为替代,无需访问底层缓冲。
  • 平仓逻辑完全模仿 MQL 中的 OrdersTotal() 循环:趋势翻转时先平仓,再在相反方向建立新仓。
  • 原脚本中未参与决策的输入参数(如 highlowcbarsfrommaPSMAspreadSlippage)在转换后被省略。

使用建议

  1. 将策略附加到目标品种,选择与 MetaTrader 测试相同的蜡烛周期。
  2. 根据原策略的表现调整 ATR 周期和倍数。
  3. 如需硬性风险控制,可设置 StopLossPoints;若希望完全依靠信号进出场,保持为 0 即可。
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// SuperTrend-based strategy from NinaEA.
/// Trades on SuperTrend direction flips - buy on up trend, sell on down trend.
/// </summary>
public class NinaEaStrategy : Strategy
{
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMultiplier;
	private readonly StrategyParam<DataType> _candleType;

	private bool? _previousTrendUp;

	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public NinaEaStrategy()
	{
		_atrPeriod = Param(nameof(AtrPeriod), 10)
			.SetDisplay("ATR Period", "ATR length for SuperTrend", "Indicators");

		_atrMultiplier = Param(nameof(AtrMultiplier), 1m)
			.SetDisplay("ATR Multiplier", "SuperTrend multiplier", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_previousTrendUp = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_previousTrendUp = null;

		var superTrend = new SuperTrend
		{
			Length = AtrPeriod,
			Multiplier = AtrMultiplier
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(superTrend, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue superTrendValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!superTrendValue.IsFinal)
			return;

		var stValue = (SuperTrendIndicatorValue)superTrendValue;
		var isUpTrend = stValue.IsUpTrend;

		if (_previousTrendUp is bool prevUp)
		{
			// Trend flipped to up - go long
			if (isUpTrend && !prevUp)
			{
				if (Position < 0)
					BuyMarket();
				if (Position <= 0)
					BuyMarket();
			}
			// Trend flipped to down - go short
			else if (!isUpTrend && prevUp)
			{
				if (Position > 0)
					SellMarket();
				if (Position >= 0)
					SellMarket();
			}
		}

		_previousTrendUp = isUpTrend;
	}
}