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

相关推荐
Wang's Blog2 小时前
Java 项目实战: 交友项目 - FastDFS:入门案例
java·开发语言·交友
apcipot_rain3 小时前
C++转Java日记(03)
java·开发语言·c++
布莱克6053 小时前
C++ 中 map 的 insert 与 emplace:key 已存在时会覆盖吗?
开发语言·数据结构·c++·算法·map
xys_6784 小时前
深挖C++:类和对象(中)
开发语言·c++
灿飞屋4 小时前
Rust 编程其实很简单
开发语言·rust
MOONICK6 小时前
QT元数据
开发语言·qt
D e re k ZH6 小时前
C++ 系统学习手记:从语法细节到工程实践
开发语言·数据结构·c++·算法
灿飞屋7 小时前
Kotlin与Java虚拟机三:控制流
android·java·开发语言·后端·python·kotlin·安卓
xiaoye-duck7 小时前
《C++ 项目:手写大模型接入 SDK》Ollama接口设计(上):初始化、全量响应实现和测试
开发语言·网络·c++·语言模型
总要学点什么8 小时前
第98篇:三维特效-材质线效果——给一条线换十种材质,虚线的图案只填了一半
开发语言·前端·javascript·ecmascript·gis·webgl·材质