在 GitHub 上查看

Connect Disconnect Sound Alert 策略

概述

Connect Disconnect Sound Alert Strategy 持续检测策略连接器的在线状态,并在每次连接或断开时记录详细日志。原始的 MQL5 专家在 MetaTrader 终端连接状态变化时播放声音。C# 版本保留了这一核心思想——发现连接变化——并利用 StockSharp 的日志系统提供更加丰富的诊断信息,因此可以作为监控连接稳定性的轻量级工具。

主要特点

  • 以可配置的周期轮询连接器状态。
  • 捕获连接与断开事件,并写入详细日志。
  • 可选记录每次在线或离线持续的时间长度。
  • 首次检测时不触发声音提醒,以保持与 MQL 版本一致的行为。

参数

名称 默认值 说明
CheckIntervalSeconds 1 两次状态检查之间的秒数,必须大于零。
LogDurations true 若启用,则在每次状态切换后记录上一状态持续的时间。

所有参数均通过 StrategyParam<T> 暴露,可在界面或优化器中调整。

工作流程

  1. 启动时保存当前连接状态,并输出初始状态日志。
  2. 使用 System.Threading.Timer 定期调用处理函数,对比当前状态与上一次状态。
  3. 一旦发现变化,就写入日志。第一次通知会标记为“initial”,仅用于同步状态,不表示真正的报警。
  4. 如果启用了持续时间记录,则会指出上一次在线或离线持续了多久,方便评估连接质量。
  5. 在停止或重置时自动释放计时器,避免后台任务遗留。

使用建议

  • 将策略附加到任何支持连接器的 StockSharp 终端,它不会订阅行情或发送订单。
  • 默认 1 秒的轮询间隔适合实时监控,如需降低负载可适当增加间隔。
  • 日志通过 LogInfo 输出,可将其接入日志查看器或通知系统;需要声音提醒时,可在宿主程序中监听这些日志并播放音频。

安全性提示

  • 策略会验证轮询间隔的合法性,若不符合要求会抛出异常。
  • 使用 CurrentTime 生成时间戳,兼容实时和历史回放环境。
  • 在停止或重置时会正确释放计时器,避免出现悬挂线程。
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Connection alert strategy with SMA crossover trading.
/// Buys when fast SMA crosses above slow SMA, sells on cross below.
/// </summary>
public class ConnectDisconnectSoundAlertStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public ConnectDisconnectSoundAlertStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var fast = new SimpleMovingAverage { Length = FastPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };

		decimal? prevFast = null;
		decimal? prevSlow = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, (candle, fastVal, slowVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (prevFast.HasValue && prevSlow.HasValue)
				{
					var crossUp = prevFast.Value <= prevSlow.Value && fastVal > slowVal;
					var crossDown = prevFast.Value >= prevSlow.Value && fastVal < slowVal;

					if (crossUp && Position <= 0)
						BuyMarket();
					else if (crossDown && Position >= 0)
						SellMarket();
				}

				prevFast = fastVal;
				prevSlow = slowVal;
			})
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fast);
			DrawIndicator(area, slow);
			DrawOwnTrades(area);
		}
	}
}