在 GitHub 上查看

Alert MACD Slow 策略对应 MetaTrader 4 顾问 Alert_MACD_Slow.mq4。它监控 MACD 主线与两条指数均线,在出现潜在突破时输出文字提醒。本移植不会发送任何订单,与原版仅弹出提示框的行为完全一致。

核心思路

  1. 订阅所选 K 线序列,同时驱动 MACD(3, 20, 9)、快慢 EMA(20 与 65 周期)。
  2. 缓存最近四根已完成 K 线的 MACD 值,用于重现 MQL 代码中的斜率判断。
  3. 保存最近两根 K 线的最高价与最低价,对应 High[1]/High[2]Low[1]/Low[2] 的突破过滤条件。
  4. 当快 EMA 位于慢 EMA 上方(或下方),且收盘价突破记忆的高点(或低点),同时 MACD 在零线下方向上(或向下)转折时,在日志中写入相应的提醒信息。

参数

名称 默认值 说明
MacdFastPeriod 3 MACD 快速 EMA 周期。
MacdSlowPeriod 20 MACD 慢速 EMA 周期。
MacdSignalPeriod 9 MACD 信号线平滑周期。
QuickEmaPeriod 20 快速趋势 EMA(原代码中的 Ma_Quick)。
SlowEmaPeriod 65 慢速趋势 EMA(原代码中的 Ma_Slow)。
CandleType TimeFrame(30m) 传递给指标链的 K 线类型,可根据需要调整。

提醒逻辑细节

  • MACD 斜率缓存:策略在内部移动历史值,而不是调用 GetValue,既符合转换规范,也保留了原策略对 Macd_1Macd_2 等值的比较。
  • 突破判断:使用当前收盘价与先前高低点比对,作为 MQL 中 Ask > High[1] / Bid < Low[1] 判定的替代实现。
  • 趋势过滤:仅当快慢 EMA 排列方向正确时才触发提醒,与原顾问中的多空过滤保持一致。
  • 日志输出:通过 AddInfoLog 打印提醒,内容包含四个缓存的 MACD 数值与突破水平,方便回测与分析。
  • 无交易:因为原策略不下单,本移植同样只提供信号,不会开仓。

使用步骤

  1. 将策略附加到品种上,设置合适的 CandleType 与指标周期(可使用默认值)。
  2. 启动策略,等待 MACD 与 EMA 指标形成(需要数根 K 线历史)。
  3. 观察日志:出现看多条件时会打印 SET UP LONG,看空条件时会打印 SET UP SHORT_VALUE,与原始提示文字一致。
  4. 根据输出的诊断信息手动决策,或进一步接入自动化逻辑。

分类

  • 类别:提醒 / 趋势突破确认
  • 交易方向:无(仅信号)
  • 执行方式:处理已完成的 K 线事件
  • 数据需求:与 CandleType 匹配的 K 线序列
  • 复杂度:中等(多个过滤器,但状态管理简单)
  • 风险管理:不适用(不建仓)

该移植充分利用 StockSharp 的订阅、指标绑定与日志功能,同时保持 MQL 顾问以提醒为中心的设计。

using System;

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

namespace StockSharp.Samples.Strategies;

public class AlertMacdSlowStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public AlertMacdSlowStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 26).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = default;
		_prevSlow = default;
		_hasPrev = default;
		_cooldown = default;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) return;

		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
		if (_cooldown > 0)
		{
			_cooldown--;
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
		{
			var volume = Volume + Math.Abs(Position);
			BuyMarket(volume);
			_cooldown = 2;
		}
		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
		{
			var volume = Volume + Math.Abs(Position);
			SellMarket(volume);
			_cooldown = 2;
		}

		_prevFast = fast; _prevSlow = slow;
	}
}