C# 实现德指 DAX 期货 WebSocket 实时行情监听

摘要

欧洲市场量化监控系统中,德国 DAX 指数期货是核心观测标的。本文基于 C# ClientWebSocket 实现完整的长连接监听客户端,包含心跳保活、断线自动重连、行情结构化解析,适合.NET 技术栈项目集成,数据源采用脉动行情数据 API。

关键词:C#;WebSocket;德指 DAX;欧洲期货;实时行情

一、业务背景

德指 DAX 期货跟踪德国 DAX30 指数,是欧洲股市的核心风向标。很多.NET 技术栈的量化系统对接长连接行情时,容易踩心跳缺失、断连无重连、同步阻塞等坑。 本文采用异步 WebSocket 实现,全程非阻塞,支持 7×24 小时稳定运行。

品种编码:ZY_DAX 德指期货

二、环境说明

基于.NET 6+ 原生 ClientWebSocket 实现,无需第三方依赖。

三、完整实现代码

vbnet 复制代码
using System;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace DaxQuoteClient
{
    public class DaxWsClient
    {
        // WebSocket服务地址,仅代码内配置
        private const string WsUrl = "ws://39.107.99.235/ws";
        private const string Symbol = "ZY_DAX";
        private const int HeartbeatInterval = 10000; // 10秒心跳
        private const int ReconnectDelay = 3000; // 重连延迟3秒
        
        private ClientWebSocket _ws;
        private CancellationTokenSource _cts;
        private Timer _heartbeatTimer;

        public async Task StartAsync()
        {
            _cts = new CancellationTokenSource();
            await ConnectAsync();
        }

        private async Task ConnectAsync()
        {
            try
            {
                _ws = new ClientWebSocket();
                await _ws.ConnectAsync(new Uri(WsUrl), _cts.Token);
                Console.WriteLine("WebSocket连接成功,订阅德指DAX行情");
                
                // 发送订阅报文
                var subMsg = $"{{\"Key\":\"{Symbol}\"}}";
                await SendAsync(subMsg);
                
                // 启动心跳定时器
                _heartbeatTimer = new Timer(HeartbeatCallback, null, HeartbeatInterval, HeartbeatInterval);
                
                // 启动接收循环
                _ = ReceiveLoopAsync();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"连接失败:{ex.Message},{ReconnectDelay}ms后重连");
                await Task.Delay(ReconnectDelay);
                await ConnectAsync();
            }
        }

        private async void HeartbeatCallback(object state)
        {
            try
            {
                var ping = $"{{\"ping\":{DateTimeOffset.Now.ToUnixTimeSeconds()}}}";
                await SendAsync(ping);
            }
            catch
            {
                // 心跳失败触发重连
                _ = ReconnectAsync();
            }
        }

        private async Task ReceiveLoopAsync()
        {
            var buffer = new byte[4096];
            while (_ws.State == WebSocketState.Open && !_cts.IsCancellationRequested)
            {
                try
                {
                    var result = await _ws.ReceiveAsync(new ArraySegment<byte>(buffer), _cts.Token);
                    if (result.MessageType == WebSocketMessageType.Text)
                    {
                        var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                        HandleMessage(message);
                    }
                    else if (result.MessageType == WebSocketMessageType.Close)
                    {
                        await _ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, _cts.Token);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"接收异常:{ex.Message}");
                    break;
                }
            }
            // 退出循环触发重连
            _ = ReconnectAsync();
        }

        private void HandleMessage(string message)
        {
            try
            {
                using var doc = JsonDocument.Parse(message);
                if (!doc.RootElement.TryGetProperty("body", out var body))
                    return;
                
                var stockCode = body.GetProperty("StockCode").GetString();
                var price = body.GetProperty("Price").GetString();
                var high = body.GetProperty("High").GetString();
                var low = body.GetProperty("Low").GetString();
                var time = body.GetProperty("Time").GetString();
                
                Console.WriteLine($"[{time}] {stockCode} 最新价:{price} 最高:{high} 最低:{low}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"解析异常:{ex.Message}");
            }
        }

        private async Task SendAsync(string message)
        {
            var bytes = Encoding.UTF8.GetBytes(message);
            await _ws.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, _cts.Token);
        }

        private async Task ReconnectAsync()
        {
            _heartbeatTimer?.Dispose();
            _ws?.Dispose();
            Console.WriteLine($"连接断开,{ReconnectDelay}ms后自动重连");
            await Task.Delay(ReconnectDelay);
            await ConnectAsync();
        }

        public void Stop()
        {
            _cts.Cancel();
            _heartbeatTimer?.Dispose();
            _ws?.Dispose();
        }
    }

    class Program
    {
        static async Task Main(string[] args)
        {
            var client = new DaxWsClient();
            await client.StartAsync();
            
            Console.WriteLine("按任意键退出");
            Console.ReadKey();
            client.Stop();
        }
    }
}

四、核心返回字段

字段 说明
StockCode 品种编码
Price 最新价格
Open 开盘价
LastClose 昨结算价
High 日内最高价
Low 日内最低价
Diff 涨跌额
DiffRate 涨跌幅
Time 行情更新时间

五、开发注意事项

  1. 采用异步接收循环,避免同步阻塞导致的界面卡死或线程浪费;
  2. 10 秒定时心跳,防止服务端主动断开空闲连接;
  3. 所有异常都会触发重连逻辑,保证 7×24 小时稳定运行;
  4. 德指交易时间对应欧洲时区,国内夜间波动较大,业务告警注意时区;
  5. 生产环境建议替换 Console 输出为日志框架,比如 Serilog。

六、总结

本文基于.NET 原生 WebSocket 实现德指 DAX 期货实时行情监听,包含完整的心跳、重连、解析逻辑,无需第三方依赖,可直接集成到.NET 量化系统、行情大屏中。修改品种编码即可快速适配其他欧洲期货品种。

相关推荐
码士集团小青1 小时前
C# 手写向量化能追上 C 吗?
c#
大圣编蚕1 小时前
Java ByteArrayInputStream 详解:从入门到实战
java·开发语言·算法
菜鸟~noob2331 小时前
【电子战】第02篇:能量检测器 vs. 相关检测器【含matlab代码】
开发语言·matlab
OpenAnolis小助手2 小时前
一句话看透 JVM,SysOM 诊断 Skill 新增 Java 应用诊断能力
java·开发语言·jvm·阿里云·操作系统控制台·sysom
小玮看世界2 小时前
[Python]OD算法在OD实际运用转化参考清单
开发语言·python·算法
ForteScarlet3 小时前
Kotlin 2.4.20 现已发布,新特性多不多?
android·java·开发语言·开源·kotlin·jetbrains
萧瑟余晖3 小时前
Java深入解析篇六十二之安全机制详解
java·开发语言
c#上位机3 小时前
tuple_max求数组中的最大值
c#·halcon·机器视觉
菜鸟~noob2334 小时前
【电子战】 第01篇:噪声、门限与虚警/检测概率(PFA/PD)【含matlab代码】
开发语言·matlab