Java WebSocket 实现恒指期货实时行情长连接(通用金融方案)

摘要

在量化程序、行情监控平台开发过程中,金融类 WebSocket 长连接普遍存在无心跳保活、断线不会自动重连、行情数据解析容错差等问题。本文以恒指期货为演示标的,使用 java-websocket 框架搭建一套可直接复用的客户端,内置 10 秒定时心跳、3 秒延迟重连、完整 JSON 异常捕获逻辑,适配股指、贵金属、外汇等各类行情场景。 关键词:Java WebSocket;金融行情;恒指期货;心跳重连;实时数据推送

一、开发痛点梳理

  1. 空闲无心跳,链路被服务端主动断开,无人知晓;
  2. 网络波动断连后无重试机制,需要手动重启服务;
  3. 行情 JSON 字段较多,未做异常判断,非法报文直接造成程序崩溃;
  4. 多品种订阅逻辑未封装,切换标的重复编写代码。

二、项目依赖 Maven

<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>java-websocket</artifactId>
    <version>1.5.4</version>
</dependency>
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.32</version>
</dependency>

三、完整可运行客户端代码

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;

import java.net.URI;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 恒指期货实时行情订阅客户端
 */
public class HsiMarketClient {
    // 行情推送真实地址(仅代码内配置使用)
    private static final String WS_URL = "ws://39.107.99.235/ws";
    // 恒指期货订阅编码
    private static final String SYMBOL = "HX_HSI";
    private static WebSocketClient webSocketClient;
    private static Timer heartTimer;
    // 心跳间隔10秒
    private static final int HEART_CYCLE = 10000;
    // 断连重连等待3秒
    private static final int RETRY_WAIT = 3000;

    public static void main(String[] args) {
        connect();
    }

    public static void connect() {
        try {
            webSocketClient = new WebSocketClient(new URI(WS_URL)) {
                @Override
                public void onOpen(ServerHandshake handshake) {
                    System.out.println("行情长连接建立完成,执行品种订阅");
                    // 订阅报文标准格式
                    String subJson = "{\"Key\":\"" + SYMBOL + "\"}";
                    send(subJson);
                    startHeartBeat();
                }

                @Override
                public void onMessage(String message) {
                    try {
                        JSONObject root = JSON.parseObject(message);
                        if (!root.containsKey("body")) {
                            return;
                        }
                        JSONObject data = root.getJSONObject("body");
                        System.out.printf(
                                "品种:%s 现价:%s 涨跌:%s 涨跌幅:%s%% 更新时间:%s%n",
                                data.getString("StockCode"),
                                data.get("Price"),
                                data.get("Diff"),
                                data.get("DiffRate"),
                                data.getString("Time")
                        );
                    } catch (Exception e) {
                        System.err.println("行情数据解析异常:" + e.getMessage());
                    }
                }

                @Override
                public void onClose(int code, String reason, boolean remote) {
                    System.out.printf("连接断开,等待%d毫秒后自动重试%n", RETRY_WAIT);
                    if (heartTimer != null) heartTimer.cancel();
                    new Thread(() -> {
                        try {
                            Thread.sleep(RETRY_WAIT);
                            connect();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }).start();
                }

                @Override
                public void onError(Exception ex) {
                    System.err.println("WebSocket通信异常:" + ex.getMessage());
                }
            };
            webSocketClient.connect();
        } catch (Exception e) {
            System.err.println("连接初始化失败:" + e.getMessage());
        }
    }

    // 启动定时心跳任务,每10s发送一次ping报文
    private static void startHeartBeat() {
        heartTimer = new Timer();
        heartTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if (webSocketClient != null && webSocketClient.isOpen()) {
                    long timestamp = System.currentTimeMillis() / 1000;
                    String pingMsg = "{\"ping\":" + timestamp + "}";
                    webSocketClient.send(pingMsg);
                }
            }
        }, 0, HEART_CYCLE);
    }
}

四、行情返回核心字段说明

字段释义
StockCode交易品种唯一编码
Price实时最新成交价
Open当日开盘价格
LastClose上一交易日收盘价
High日内最高价格
Low日内最低价格
Diff涨跌数值
DiffRate涨跌幅百分比
BP1一档买盘价格
SP1一档卖盘价格
Time行情数据更新时间

五、补充:HTTP 同步拉取行情方式

如果业务不需要毫秒级实时推送,可采用 HTTP 接口定时拉取行情数据,请求头必须携带Accept-Encoding: gzip压缩参数,降低网络传输消耗。

import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class MarketHttpDemo {
    public static void main(String[] args) throws Exception {
        // 实时行情接口(仅代码内填写完整地址)
        URL url = new URL("http://39.107.99.235:1008/getQuote.php?code=HX_HSI");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestProperty("Accept-Encoding", "gzip");
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
        conn.disconnect();
    }
}

六、历史 K 线数据获取示例代码

需要历史行情做指标计算、走势分析时,调用 K 线接口获取多周期数据,支持 1 分钟、5 分钟、1 小时、日线等周期。

import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class KlineDemo {
    public static void main(String[] args) throws Exception {
        // K线接口完整地址仅写在代码中
        URL url = new URL("http://39.107.99.235:1008/redis.php?code=HX_HSI&time=1h&rows=200");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestProperty("Accept-Encoding", "gzip");
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
        conn.disconnect();
    }
}

七、线上生产环境优化建议

  1. 增加重连次数阈值,避免极端网络环境无限循环创建连接;
  2. 对接缓存或数据库,将实时行情持久化存储,用于历史复盘;
  3. 增加价格波动告警逻辑,当涨跌幅度超过阈值输出日志告警;
  4. 多品种并行订阅场景,封装工具类统一管理多个 WebSocket 连接;
  5. 服务部署使用守护进程托管,防止终端关闭导致程序退出。

八、总结

本文完整实现了适配恒指期货的行情 WebSocket 客户端,解决长连接心跳保活、自动断线重连、行情报文安全解析三大核心开发难点。这套代码逻辑通用,只需修改品种编码,即可快速迁移至外汇、贵金属、其他股指期货等各类行情业务场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值