在 GitHub 上查看

Smart Forex System 策略

概述

Smart Forex System 策略是 MetaTrader 智能交易系统在 StockSharp 平台上的移植版本。策略结合了上一根 K 线的动量过滤器与带有加仓倍数的网格加仓机制。当上一根 K 线出现显著的趋势收盘且当前价格相对于参考收盘价偏离足够多时,会启动第一笔交易。价格逆向移动时,会按照固定的点差间距继续加仓,并使用可配置的倍数放大手数。平仓逻辑通过网格平均价格设置的分级止盈,以及跟随最近一次加仓价格的止损来控制风险。

交易逻辑

  • 信号判定
    • 仅使用选定周期内最新完成的 K 线。
    • 计算动量值 (当前收盘价 − 上一收盘价) / 上一收盘价 * 10,000
    • 若上一根 K 线为阴线且动量低于负阈值,则允许开启多头网格。
    • 若上一根 K 线为阳线且动量高于正阈值,则允许开启空头网格。
    • 通过 Trading Mode 参数可以限制仅做多、仅做空、双向或完全关闭交易。
  • 网格加仓
    • 当存在未平仓网格时,如果价格相对于最后一次进场价逆向运行至少 Grid Step 点,则触发下一笔加仓。
    • 每次加仓的手数按 Lot Multiplier 放大,同时受到证券最小/最大手数以及 Max Volume 的限制。
    • 网格中的订单数量达到 Max Trades 时停止继续加仓。
  • 退出机制
    • 从最近一次进场价起,设置距离为 Stop Loss 点的强制止损,突破该水平时立即清空整个网格。
    • 依据网格规模设置不同的止盈目标:
      • 单一持仓使用 First Take Profit 点的目标。
      • 多笔持仓使用 Grid Take Profit 点的目标,以提高获利概率。
    • 所有判断在 K 线收盘时进行,确保数据最终确认。

风险提示

  • 加倍网格会在趋势行情中迅速放大头寸,请谨慎设置加仓倍数与最大订单数,尤其是在波动性较大的品种上。
  • 默认 400 点的止损源自原始 EA,幅度较大,如需更紧的风险控制,可结合 ATR 等指标重新调整。
  • 网格交易对保证金要求较高,务必确认账户杠杆、合约大小与 Start Volume 的组合在经纪商处可行。

参数一览

参数 说明 默认值
Trading Mode 允许的交易方向(仅多、仅空、双向、关闭)。 Long & Short
Momentum Threshold 触发信号所需的最小动量(伪点)。 1
Start Volume 新网格第一笔订单的手数。 0.01
Max Volume 单笔订单的最大手数。 2
Lot Multiplier 后续加仓的手数倍数。 1.5
Grid Step 连续加仓之间的最小点差。 26
Max Trades 单方向允许的最大订单数量。 12
First Take Profit 仅有一笔持仓时的止盈距离。 30
Grid Take Profit 多笔持仓时的止盈距离。 7
Stop Loss 最近一笔订单的止损距离。 400
Candle Type 用于计算信号的 K 线周期。 1 小时 K 线

使用建议

  1. 建议选择流动性充足、点差稳定的外汇品种运行策略。
  2. Candle Type 默认设置为 1 小时,可根据交易风格调整为其他周期。
  3. 在真实环境运行前,请先在历史数据上优化网格间距、加仓倍数和动量阈值。
  4. 时刻关注保证金占用,必要时配合账户级别的资金保护机制。
  5. 避免在同一品种上同时运行多套网格策略,以减少风险叠加。

与 MetaTrader 版本的差异

  • 本移植版本使用收盘 K 线进行决策,减少噪音并提高回测与实盘之间的一致性。
  • 下单手数会自动适配 StockSharp 证券对象的最小值、最大值和步长要求,兼容更多经纪商。
  • 止盈与止损逻辑在策略内部统一处理,无需对每笔订单频繁修改参数。
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;
		}
	}
}