摘要
跨境量化系统、大宗商品监控平台需要对接多种国际期货品种,涵盖能源、贵金属、指数等多个品类。手动逐个对接效率低,稳定性差。本文基于 OkHttp 封装国际期货行情工具类,支持批量拉取美原油、纽约金、小纳指等主力合约行情,结合 SpringBoot 定时任务实现结构化解析与 MySQL 落库,包含连接池复用、异常重试、Gzip 压缩优化,数据源采用脉动行情数据 API。
关键词:Java;SpringBoot;国际期货;美原油;行情采集
一、业务背景
国际期货市场品种丰富,主流的能源类、贵金属类、指数类期货都是量化监控、资产配置的重要标的。 很多项目对接国际期货时,容易出现几个共性问题:
- 每个品种单独写请求逻辑,代码冗余,维护成本高;
- 没有全局连接池,频繁创建 HTTP 连接导致资源浪费;
- 缺少超时与异常处理,接口波动直接影响业务;
- 数据解析后缺少标准化落库方案,不便后续查询分析。
示例品种编码: CL:WTI 纽约原油期货 GC:COMEX 黄金期货 SI:COMEX 白银期货 NQ:纳斯达克 100 指数期货(小纳指) YM:道琼斯指数期货(小道指)
二、Maven 依赖
XML
<dependencies>
<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.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
</dependencies>
三、国际期货行情工具类
java
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import com.alibaba.fastjson2.JSONObject;
import java.util.concurrent.TimeUnit;
/**
* 国际期货行情采集工具类
* 支持批量查询能源、贵金属、指数类期货
*/
public class IntlFuturesQuoteUtil {
private static final OkHttpClient HTTP_CLIENT;
// 实时行情接口,仅代码内配置
private static final String QUOTE_URL = "http://39.107.99.235:1008/getQuote.php";
// K线接口,仅代码内配置
private static final String KLINE_URL = "http://39.107.99.235:1008/redis.php";
// 批量测试品种
private static final String BATCH_CODES = "CL,GC,SI,NQ,YM";
static {
HTTP_CLIENT = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
}
/**
* 批量获取国际期货实时行情
* @param codes 多个品种编码,英文逗号分隔
*/
public static JSONObject getBatchQuote(String codes) {
Request request = new Request.Builder()
.url(QUOTE_URL + "?code=" + codes)
.header("Accept-Encoding", "gzip")
.get()
.build();
try (Response resp = HTTP_CLIENT.newCall(request).execute()) {
if (!resp.isSuccessful()) return null;
return JSONObject.parseObject(resp.body().string());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取单品种K线数据
* @param code 品种编码
* @param cycle 周期 1m/5m/1h/1d
* @param rows 返回条数
*/
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);
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 null;
return resp.body().string();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
JSONObject quote = getBatchQuote(BATCH_CODES);
System.out.println("国际期货批量行情:\n" + quote);
String kline = getKline("CL", "1h", 200);
System.out.println("\n美原油1小时K线:\n" + kline);
}
}
四、SpringBoot 定时同步任务
java
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;
import javax.annotation.Resource;
@Component
@EnableScheduling
public class IntlFuturesSyncTask {
private final String batchCodes = "CL,GC,SI,NQ,YM";
@Resource
private IntlFuturesQuoteMapper intlFuturesQuoteMapper;
@Scheduled(fixedRate = 30000)
public void syncFuturesQuote() {
JSONObject result = IntlFuturesQuoteUtil.getBatchQuote(batchCodes);
if (result == null || !"200".equals(result.getString("code"))) {
return;
}
JSONArray bodyArray = result.getJSONObject("data").getJSONArray("body");
for (int i = 0; i < bodyArray.size(); i++) {
JSONObject item = bodyArray.getJSONObject(i);
IntlFuturesQuoteEntity entity = new IntlFuturesQuoteEntity();
entity.setStockCode(item.getString("StockCode"));
entity.setPrice(item.getBigDecimal("Price"));
entity.setOpen(item.getBigDecimal("Open"));
entity.setHigh(item.getBigDecimal("High"));
entity.setLow(item.getBigDecimal("Low"));
entity.setDiff(item.getBigDecimal("Diff"));
entity.setDiffRate(item.getBigDecimal("DiffRate"));
entity.setVolume(item.getLong("TotalVol"));
entity.setUpdateTime(item.getString("Time"));
// 幂等写入:存在更新,不存在插入
intlFuturesQuoteMapper.insertOrUpdate(entity);
}
}
}
五、数据库表结构参考
sql
CREATE TABLE `intl_futures_quote` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`stock_code` varchar(32) NOT NULL COMMENT '品种编码',
`price` decimal(10,2) DEFAULT NULL COMMENT '最新价',
`open` decimal(10,2) DEFAULT NULL COMMENT '开盘价',
`high` decimal(10,2) DEFAULT NULL COMMENT '最高价',
`low` decimal(10,2) DEFAULT NULL COMMENT '最低价',
`diff` decimal(10,2) DEFAULT NULL COMMENT '涨跌额',
`diff_rate` decimal(5,2) DEFAULT NULL COMMENT '涨跌幅',
`volume` bigint DEFAULT NULL COMMENT '成交量',
`update_time` varchar(32) DEFAULT NULL COMMENT '行情时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_stock_code` (`stock_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='国际期货行情表';
六、核心返回字段说明
| 字段 | 说明 |
|---|---|
| StockCode | 期货品种编码 |
| Price | 最新成交价 |
| Open | 开盘价 |
| LastClose | 昨结算价 |
| High | 日内最高价 |
| Low | 日内最低价 |
| Diff | 涨跌额 |
| DiffRate | 涨跌幅百分比 |
| TotalVol | 成交量 |
| Time | 行情更新时间 |
七、开发注意事项
- 请求头开启 Gzip 压缩,大幅降低国际线路传输体积;
- OkHttpClient 全局单例,复用连接池,减少握手开销;
- 支持批量查询,多品种一次请求完成,降低接口调用频次;
- 国际期货交易时段覆盖美东时区,业务层注意时区转换;
- 数据库唯一键实现幂等写入,避免重复数据。
八、总结
本文实现多品类国际期货的批量采集、解析与落库,基于 SpringBoot 可快速部署为后台定时服务。修改品种编码列表,即可扩展到更多能源、农产品、股指期货品种,适合跨境量化、大宗商品监控类项目。