摘要

韩国 KOSPI 主板、KOSDAQ 创业板是亚太市场重要组成,三星电子、SK 海力士等龙头股是量化监控重点。本文基于 Java OkHttp 封装韩股行情工具类,支持指数与多只个股批量查询,包含连接池复用、Gzip 压缩、异常重试,可直接嵌入 SpringBoot 项目做定时采集。

一、业务背景

韩股市场数据经常和 A 股、台股做横向对比分析。Java 后端项目对接时,常见几个问题:

  1. OkHttpClient 没有做成全局单例,频繁新建导致端口耗尽;
  2. 单股循环请求,没有利用批量查询能力;
  3. 缺少超时和重试,网络抖动直接丢数据;
  4. 没有统一日志,出问题难定位。

二、品种编码说明

脉动行情数据 API 韩股市场覆盖:

  • 指数:IDX_KOSPI(KOSPI 指数)、IDX_KOSDAQ(科斯达克指数)、IDX_KOSPI200
  • 个股:005930 三星电子、000660SK 海力士、005380 现代汽车、035420NAVER、035720Kakao
  • 接口地址:http://39.107.99.235:1008/getQuote.php
  • K 线接口:http://39.107.99.235:1008/redis.php

三、Maven 依赖

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.12.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.32</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.9</version>
</dependency>

四、完整 Java 工具类

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * 韩股行情批量采集工具
 * 支持KOSPI/KOSDAQ指数、个股
 */
public class KrStockQuoteUtil {

    private static final Logger log = LoggerFactory.getLogger(KrStockQuoteUtil.class);
    private static final int TIMEOUT_SEC = 10;
    private static final int MAX_RETRY = 3;

    // 接口地址,仅代码内配置
    private static final String QUOTE_URL = "http://39.107.99.235:1008/getQuote.php";
    private static final String KLINE_URL = "http://39.107.99.235:1008/redis.php";

    // 全局单例连接池
    private static final OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder()
            .connectTimeout(TIMEOUT_SEC, TimeUnit.SECONDS)
            .readTimeout(TIMEOUT_SEC, TimeUnit.SECONDS)
            .writeTimeout(TIMEOUT_SEC, TimeUnit.SECONDS)
            .retryOnConnectionFailure(true)
            .build();

    /**
     * 批量获取韩股实时行情
     * @param codes 多个编码英文逗号分隔
     * @return 行情数组
     */
    public static JSONArray getBatchQuote(String codes) {
        String url = QUOTE_URL + "?code=" + codes;
        for (int i = 0; i < MAX_RETRY; i++) {
            try {
                Request request = new Request.Builder()
                        .url(url)
                        .header("Accept-Encoding", "gzip")
                        .get()
                        .build();
                try (Response resp = HTTP_CLIENT.newCall(request).execute()) {
                    if (!resp.isSuccessful()) {
                        log.warn("请求失败 status={}", resp.code());
                        continue;
                    }
                    String body = resp.body().string();
                    JSONObject root = JSON.parseObject(body);
                    if (root.getIntValue("code") == 200) {
                        return root.getJSONObject("data").getJSONArray("body");
                    }
                }
            } catch (Exception e) {
                log.error("第{}次请求异常: {}", i + 1, e.getMessage());
            }
        }
        return new JSONArray();
    }

    /**
     * 获取单标的K线
     */
    public static String getKline(String code, String cycle, int rows) {
        String url = String.format("%s?code=%s&time=%s&rows=%d",
                KLINE_URL, code, cycle, rows);
        try {
            Request request = new Request.Builder()
                    .url(url)
                    .header("Accept-Encoding", "gzip")
                    .get()
                    .build();
            try (Response resp = HTTP_CLIENT.newCall(request).execute()) {
                if (resp.isSuccessful()) {
                    return resp.body().string();
                }
            }
        } catch (Exception e) {
            log.error("K线请求异常: {}", e.getMessage());
        }
        return "";
    }

    public static void main(String[] args) {
        // 批量采集:指数 + 核心个股
        String codes = "IDX_KOSPI,IDX_KOSDAQ,005930,000660,005380,035420";
        JSONArray items = getBatchQuote(codes);
        for (int i = 0; i < items.size(); i++) {
            JSONObject item = items.getJSONObject(i);
            System.out.printf(
                "[%s] %s 最新价:%s 涨跌:%s 涨跌幅:%s%%%n",
                item.getString("Time"),
                item.getString("StockCode"),
                item.getString("Price"),
                item.getString("Diff"),
                item.getString("DiffRate")
            );
        }

        // 获取三星电子日K
        String kline = getKline("005930", "1d", 50);
        System.out.println("\n三星电子005930日K线:");
        System.out.println(kline);
    }
}

五、SpringBoot 定时任务示例

import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;

@Component
@EnableScheduling
public class KrStockSyncTask {

    private final String batchCodes = "IDX_KOSPI,IDX_KOSDAQ,005930,000660,035420";

    @Scheduled(fixedRate = 30000)
    public void syncKrStock() {
        JSONArray items = KrStockQuoteUtil.getBatchQuote(batchCodes);
        for (int i = 0; i < items.size(); i++) {
            JSONObject item = items.getJSONObject(i);
            // 业务扩展:解析入库、日志归档、价格预警
            System.out.println("采集: " + item.getString("StockCode")
                    + " 价格:" + item.getString("Price"));
        }
    }
}

六、核心字段说明

表格

字段说明
StockCode标的编码
Price最新价
Open开盘价
LastClose昨收价
High日内最高
Low日内最低
Diff涨跌额
DiffRate涨跌幅
TotalVol成交量
Time更新时间

七、开发注意事项

  1. OkHttpClient 必须全局单例,不要在方法里 new;
  2. 批量请求一次拼接多个编码,比单只循环请求效率高很多;
  3. 韩股交易时段为韩国标准时间,比国内快 1 小时,定时任务注意时区;
  4. 开启 Gzip 压缩,国际线路带宽省一半;
  5. 生产环境增加日志和异常告警,连续失败及时通知。

八、总结

Java OkHttp 工具类封装好后,嵌入 SpringBoot 做定时采集非常方便。本文实现韩股 KOSPI/KOSDAQ 指数与核心个股的批量采集,修改标的编码即可扩展更多韩股上市公司,适合亚太行情监控、量化分析类项目。