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 量化系统、行情大屏中。修改品种编码即可快速适配其他欧洲期货品种。

相关推荐
yexianglunbai12 分钟前
中间件(Middleware)详解:概念、作用与实战
java·开发语言·中间件
叶总没有会14 分钟前
OpenFeign
java·开发语言·springcloud·openfeign
海天鹰38 分钟前
C#入门:显示窗口
c#
淡海水43 分钟前
16-02-C#常用数据结构源码-附录B-源码索引速查
unity·c#·游戏引擎·il2cpp
geovindu1 小时前
rust: tree
开发语言·后端·rust
程序员老陆1 小时前
C++ 继承方式:公有、保护、私有,到底在控制什么
开发语言·c++
AI备忘录1 小时前
(二十二)华为华三锐捷迈普思科 802.1X 端口认证配置命令(网络准入五厂商对照)
运维·服务器·开发语言·网络·安全·华为
free-elcmacom1 小时前
手撕 memcpy 和 memmove:从字节级拷贝到内存重叠的终极较量
c语言·开发语言·青少年编程·visual studio code·visual studio
白杨尚青1 小时前
C++入门篇(十一):string(中)——容量与增删改查:一篇吃透所有常用接口(万字详解)
java·开发语言·c++
老歌老听老掉牙1 小时前
旅行商问题:从数学模型到 Python 实战
开发语言·python