国际期货、黄金、石油数据 Java 对接文档

📋 文档概述

本文档详细介绍如何使用 Java 语言对接 StockTV 国际期货、黄金、石油等大宗商品数据源,包含完整的代码示例、数据模型和实时监控功能。

🚀 快速开始

环境要求

  • JDK 8+
  • Maven 3.6+
  • 网络连接(可访问 api.stocktv.top

项目依赖

<!-- pom.xml -->
<dependencies>
    <!-- HTTP客户端 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.14</version>
    </dependency>
    
    <!-- JSON处理 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.15.2</version>
    </dependency>
    
    <!-- WebSocket客户端 -->
    <dependency>
        <groupId>org.java-websocket</groupId>
        <artifactId>Java-WebSocket</artifactId>
        <version>1.5.3</version>
    </dependency>
    
    <!-- 日志框架 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.7</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>2.0.7</version>
    </dependency>
    
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.28</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

🏗️ 核心架构

项目结构

src/main/java/com/stocktv/futures/
├── config/
│   └── FuturesConfig.java
├── model/
│   ├── FuturesContract.java
│   ├── CommodityData.java
│   ├── KLine.java
│   └── ApiResponse.java
├── client/
│   ├── FuturesHttpClient.java
│   ├── MarketHttpClient.java
│   └── FuturesWebSocketClient.java
├── service/
│   └── FuturesDataService.java
└── demo/
    └── FuturesDemo.java

📦 核心代码实现

1. 配置类

package com.stocktv.futures.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

/**
 * 期货数据配置类
 */
public class FuturesConfig {
    
    // API 基础配置
    public static final String BASE_URL = "https://api.stocktv.top";
    public static final String WS_URL = "wss://ws-api.stocktv.top/connect";
    
    // 期货接口路径
    public static final String FUTURES_LIST = "/futures/list";
    public static final String FUTURES_QUERY = "/futures/querySymbol";
    public static final String FUTURES_KLINE = "/futures/kline";
    
    // 外汇市场接口路径
    public static final String MARKET_CURRENCY_LIST = "/market/currencyList";
    public static final String MARKET_CURRENCY = "/market/currency";
    public static final String MARKET_TODAY = "/market/todayMarket";
    public static final String MARKET_CHART = "/market/chart";
    public static final String MARKET_SPARK = "/market/spark";
    
    // 主要商品代码
    public static final String GOLD_SPOT = "XAUUSD=X";
    public static final String SILVER_SPOT = "XAGUSD=X";
    public static final String CRUDE_OIL = "CL=F";
    public static final String BRENT_OIL = "BZ=F";
    public static final String NATURAL_GAS = "NG=F";
    public static final String COPPER = "HG=F";
    
    // API Key
    private final String apiKey;
    
    // HTTP 客户端和JSON处理器
    private final CloseableHttpClient httpClient;
    private final ObjectMapper objectMapper;
    
    public FuturesConfig(String apiKey) {
        this.apiKey = apiKey;
        this.httpClient = HttpClients.createDefault();
        this.objectMapper = new ObjectMapper();
        this.objectMapper.findAndRegisterModules();
    }
    
    // Getter方法
    public String getApiKey() { return apiKey; }
    public CloseableHttpClient getHttpClient() { return httpClient; }
    public ObjectMapper getObjectMapper() { return objectMapper; }
}

2. 数据模型类

期货合约数据模型

package com.stocktv.futures.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

import java.math.BigDecimal;

/**
 * 期货合约数据模型
 */
@Data
public class FuturesContract {
    @JsonProperty("date")
    private String date;
    
    @JsonProperty("symbol")
    private String symbol;
    
    @JsonProperty("buy")
    private BigDecimal buy;
    
    @JsonProperty("sell")
    private BigDecimal sell;
    
    @JsonProperty("high_price")
    private BigDecimal highPrice;
    
    @JsonProperty("prev_price")
    private BigDecimal previousPrice;
    
    @JsonProperty("volume")
    private BigDecimal volume;
    
    @JsonProperty("name")
    private String name;
    
    @JsonProperty("time")
    private String time;
    
    @JsonProperty("low_price")
    private BigDecimal lowPrice;
    
    @JsonProperty("open_price")
    private BigDecimal openPrice;
    
    @JsonProperty("last_price")
    private BigDecimal lastPrice;
    
    @JsonProperty("chg")
    private BigDecimal change;
    
    @JsonProperty("chg_pct")
    private BigDecimal changePercent;
    
    /**
     * 获取商品类型
     */
    public CommodityType getCommodityType() {
        if (symbol.contains("XAU")) return CommodityType.GOLD;
        if (symbol.contains("XAG")) return CommodityType.SILVER;
        if (symbol.contains("CL")) return CommodityType.CRUDE_OIL;
        if (symbol.contains("NG")) return CommodityType.NATURAL_GAS;
        if (symbol.contains("HG")) return CommodityType.COPPER;
        return CommodityType.OTHER;
    }
}

/**
 * 商品类型枚举
 */
enum CommodityType {
    GOLD("黄金"),
    SILVER("白银"),
    CRUDE_OIL("原油"),
    BRENT_OIL("布伦特原油"),
    NATURAL_GAS("天然气"),
    COPPER("铜"),
    OTHER("其他");
    
    private final String chineseName;
    
    CommodityType(String chineseName) {
        this.chineseName = chineseName;
    }
    
    public String getChineseName() {
        return chineseName;
    }
}

商品现货数据模型

package com.stocktv.futures.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

import java.math.BigDecimal;

/**
 * 商品现货数据模型
 */
@Data
public class CommodityData {
    @JsonProperty("symbol")
    private String symbol;
    
    @JsonProperty("chg")
    private String change;
    
    @JsonProperty("chgPct")
    private String changePercent;
    
    @JsonProperty("name")
    private String name;
    
    @JsonProperty("lastPrice")
    private String lastPrice;
    
    // 今日市场数据
    @JsonProperty("previous_close")
    private String previousClose;
    
    @JsonProperty("ask")
    private String ask;
    
    @JsonProperty("52week_range")
    private String week52Range;
    
    @JsonProperty("bid")
    private String bid;
    
    @JsonProperty("open")
    private String open;
    
    @JsonProperty("day_trange")
    private String dayRange;
    
    /**
     * 获取数值形式的最后价格
     */
    public BigDecimal getNumericLastPrice() {
        try {
            return new BigDecimal(lastPrice.replace("+", "").replace("%", ""));
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }
    
    /**
     * 获取数值形式的涨跌幅
     */
    public BigDecimal getNumericChangePercent() {
        try {
            return new BigDecimal(changePercent.replace("+", "").replace("%", ""));
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }
}

K线数据模型

package com.stocktv.futures.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

import java.math.BigDecimal;

/**
 * K线数据模型(期货专用)
 */
@Data
public class FuturesKLine {
    @JsonProperty("date")
    private String date;
    
    @JsonProperty("volume")
    private Integer volume;
    
    @JsonProperty("high")
    private BigDecimal high;
    
    @JsonProperty("s")
    private String s;
    
    @JsonProperty("low")
    private BigDecimal low;
    
    @JsonProperty("position")
    private Integer position;
    
    @JsonProperty("close")
    private BigDecimal close;
    
    @JsonProperty("open")
    private BigDecimal open;
    
    @JsonProperty("timestamp")
    private Double timestamp;
    
    /**
     * 计算振幅
     */
    public BigDecimal getAmplitude() {
        if (open == null || open.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return high.subtract(low).divide(open, 4, BigDecimal.ROUND_HALF_UP)
                .multiply(BigDecimal.valueOf(100));
    }
}

API响应包装类

package com.stocktv.futures.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

import java.util.List;
import java.util.Map;

/**
 * API通用响应包装类
 */
@Data
public class ApiResponse<T> {
    @JsonProperty("code")
    private Integer code;
    
    @JsonProperty("message")
    private String message;
    
    @JsonProperty("data")
    private T data;
    
    /**
     * 判断请求是否成功
     */
    public boolean isSuccess() {
        return code != null && code == 200;
    }
}

/**
 * 汇率转换数据
 */
@Data
class CurrencyConversionData {
    @JsonProperty("conversions")
    private Map<String, Map<String, BigDecimal>> conversions;
    
    @JsonProperty("generatedAt")
    private String generatedAt;
    
    @JsonProperty("dataAsOf")
    private String dataAsOf;
}

3. HTTP客户端实现

期货HTTP客户端

package com.stocktv.futures.client;

import com.fasterxml.jackson.core.type.TypeReference;
import com.stocktv.futures.config.FuturesConfig;
import com.stocktv.futures.model.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

/**
 * 期货数据HTTP客户端
 */
public class FuturesHttpClient {
    
    private static final Logger logger = LoggerFactory.getLogger(FuturesHttpClient.class);
    
    private final FuturesConfig config;
    private final CloseableHttpClient httpClient;
    
    public FuturesHttpClient(FuturesConfig config) {
        this.config = config;
        this.httpClient = config.getHttpClient();
    }
    
    /**
     * 获取期货市场列表
     */
    public List<FuturesContract> getFuturesList() throws IOException, URISyntaxException {
        URI uri = new URIBuilder(config.BASE_URL + config.FUTURES_LIST)
                .addParameter("key", config.getApiKey())
                .build();
        
        ApiResponse<List<FuturesContract>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<FuturesContract>>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取 {} 个期货合约", response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("获取期货列表失败: " + response.getMessage());
        }
    }
    
    /**
     * 查询特定期货品种
     */
    public List<FuturesContract> queryFuturesSymbol(String symbol) throws IOException, URISyntaxException {
        URI uri = new URIBuilder(config.BASE_URL + config.FUTURES_QUERY)
                .addParameter("key", config.getApiKey())
                .addParameter("symbol", symbol)
                .build();
        
        ApiResponse<List<FuturesContract>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<FuturesContract>>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功查询期货品种: {}", symbol);
            return response.getData();
        } else {
            throw new RuntimeException("查询期货品种失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取期货K线数据
     */
    public List<FuturesKLine> getFuturesKLine(String symbol, String interval) throws IOException, URISyntaxException {
        URI uri = new URIBuilder(config.BASE_URL + config.FUTURES_KLINE)
                .addParameter("key", config.getApiKey())
                .addParameter("symbol", symbol)
                .addParameter("interval", interval)
                .build();
        
        ApiResponse<List<FuturesKLine>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<FuturesKLine>>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取期货 {} 的K线数据,共 {} 条", symbol, response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("获取期货K线数据失败: " + response.getMessage());
        }
    }
    
    /**
     * 通用GET请求执行方法
     */
    private <T> T executeGetRequest(URI uri, TypeReference<T> typeReference) throws IOException {
        HttpGet request = new HttpGet(uri);
        logger.debug("执行期货API请求: {}", uri);
        
        try (CloseableHttpResponse response = httpClient.execute(request)) {
            int statusCode = response.getStatusLine().getStatusCode();
            String responseBody = EntityUtils.toString(response.getEntity());
            
            if (statusCode != 200) {
                throw new IOException("HTTP请求失败,状态码: " + statusCode);
            }
            
            logger.debug("期货API响应: {}", responseBody);
            return config.getObjectMapper().readValue(responseBody, typeReference);
        }
    }
    
    /**
     * 关闭HTTP客户端
     */
    public void close() throws IOException {
        if (httpClient != null) {
            httpClient.close();
        }
    }
}

外汇市场HTTP客户端

package com.stocktv.futures.client;

import com.fasterxml.jackson.core.type.TypeReference;
import com.stocktv.futures.config.FuturesConfig;
import com.stocktv.futures.model.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;

/**
 * 外汇市场数据HTTP客户端
 */
public class MarketHttpClient {
    
    private static final Logger logger = LoggerFactory.getLogger(MarketHttpClient.class);
    
    private final FuturesConfig config;
    private final CloseableHttpClient httpClient;
    
    public MarketHttpClient(FuturesConfig config) {
        this.config = config;
        this.httpClient = config.getHttpClient();
    }
    
    /**
     * 获取全球汇率列表
     */
    public CurrencyConversionData getCurrencyList() throws IOException, URISyntaxException {
        URI uri = new URIBuilder(config.BASE_URL + config.MARKET_CURRENCY_LIST)
                .addParameter("key", config.getApiKey())
                .build();
        
        ApiResponse<CurrencyConversionData> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<CurrencyConversionData>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取汇率数据");
            return response.getData();
        } else {
            throw new RuntimeException("获取汇率列表失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取实时汇率列表
     */
    public List<CommodityData> getCurrencyRates(String countryType) throws IOException, URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder(config.BASE_URL + config.MARKET_CURRENCY)
                .addParameter("key", config.getApiKey());
        
        if (countryType != null) {
            uriBuilder.addParameter("countryType", countryType);
        }
        
        URI uri = uriBuilder.build();
        
        ApiResponse<List<CommodityData>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<CommodityData>>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取 {} 个汇率数据", response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("获取实时汇率失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取当前商品信息(黄金、原油等)
     */
    public CommodityData getTodayMarket(String symbol) throws IOException, URISyntaxException {
        URI uri = new URIBuilder(config.BASE_URL + config.MARKET_TODAY)
                .addParameter("key", config.getApiKey())
                .addParameter("symbol", symbol)
                .build();
        
        ApiResponse<CommodityData> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<CommodityData>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取商品数据: {}", symbol);
            return response.getData();
        } else {
            throw new RuntimeException("获取商品信息失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取K线图表数据
     */
    public Object getChartData(String symbol, String interval) throws IOException, URISyntaxException {
        URI uri = new URIBuilder(config.BASE_URL + config.MARKET_CHART)
                .addParameter("key", config.getApiKey())
                .addParameter("symbol", symbol)
                .addParameter("interval", interval)
                .build();
        
        // 由于chart接口返回复杂结构,直接返回Object
        ApiResponse<Object> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<Object>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取图表数据: {}", symbol);
            return response.getData();
        } else {
            throw new RuntimeException("获取图表数据失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取汇率信息详情
     */
    public Object getSparkData(String symbol, String interval) throws IOException, URISyntaxException {
        URI uri = new URIBuilder(config.BASE_URL + config.MARKET_SPARK)
                .addParameter("key", config.getApiKey())
                .addParameter("symbol", symbol)
                .addParameter("interval", interval)
                .build();
        
        ApiResponse<Object> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<Object>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取汇率详情: {}", symbol);
            return response.getData();
        } else {
            throw new RuntimeException("获取汇率详情失败: " + response.getMessage());
        }
    }
    
    /**
     * 通用GET请求执行方法
     */
    private <T> T executeGetRequest(URI uri, TypeReference<T> typeReference) throws IOException {
        HttpGet request = new HttpGet(uri);
        logger.debug("执行市场API请求: {}", uri);
        
        try (CloseableHttpResponse response = httpClient.execute(request)) {
            int statusCode = response.getStatusLine().getStatusCode();
            String responseBody = EntityUtils.toString(response.getEntity());
            
            if (statusCode != 200) {
                throw new IOException("HTTP请求失败,状态码: " + statusCode);
            }
            
            logger.debug("市场API响应: {}", responseBody);
            return config.getObjectMapper().readValue(responseBody, typeReference);
        }
    }
    
    /**
     * 关闭HTTP客户端
     */
    public void close() throws IOException {
        if (httpClient != null) {
            httpClient.close();
        }
    }
}

4. 服务层封装

package com.stocktv.futures.service;

import com.stocktv.futures.client.FuturesHttpClient;
import com.stocktv.futures.client.FuturesWebSocketClient;
import com.stocktv.futures.client.MarketHttpClient;
import com.stocktv.futures.config.FuturesConfig;
import com.stocktv.futures.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 期货数据服务
 */
public class FuturesDataService {
    
    private static final Logger logger = LoggerFactory.getLogger(FuturesDataService.class);
    
    private final FuturesHttpClient futuresClient;
    private final MarketHttpClient marketClient;
    private final FuturesWebSocketClient wsClient;
    private final FuturesConfig config;
    
    public FuturesDataService(String apiKey) {
        this.config = new FuturesConfig(apiKey);
        this.futuresClient = new FuturesHttpClient(config);
        this.marketClient = new MarketHttpClient(config);
        this.wsClient = new FuturesWebSocketClient(config);
    }
    
    /**
     * 获取所有期货合约列表
     */
    public List<FuturesContract> getAllFutures() {
        try {
            List<FuturesContract> futures = futuresClient.getFuturesList();
            logger.info("成功获取 {} 个期货合约", futures.size());
            return futures;
        } catch (Exception e) {
            logger.error("获取期货列表失败", e);
            throw new RuntimeException("获取期货列表失败", e);
        }
    }
    
    /**
     * 获取贵金属期货数据
     */
    public List<FuturesContract> getPreciousMetalsFutures() {
        try {
            List<FuturesContract> allFutures = getAllFutures();
            return allFutures.stream()
                    .filter(f -> f.getCommodityType() == CommodityType.GOLD || 
                                f.getCommodityType() == CommodityType.SILVER)
                    .collect(Collectors.toList());
        } catch (Exception e) {
            logger.error("获取贵金属期货失败", e);
            throw new RuntimeException("获取贵金属期货失败", e);
        }
    }
    
    /**
     * 获取能源期货数据
     */
    public List<FuturesContract> getEnergyFutures() {
        try {
            List<FuturesContract> allFutures = getAllFutures();
            return allFutures.stream()
                    .filter(f -> f.getCommodityType() == CommodityType.CRUDE_OIL || 
                                f.getCommodityType() == CommodityType.NATURAL_GAS)
                    .collect(Collectors.toList());
        } catch (Exception e) {
            logger.error("获取能源期货失败", e);
            throw new RuntimeException("获取能源期货失败", e);
        }
    }
    
    /**
     * 获取黄金现货价格
     */
    public CommodityData getGoldSpotPrice() {
        try {
            CommodityData goldData = marketClient.getTodayMarket(FuturesConfig.GOLD_SPOT);
            logger.info("成功获取黄金现货价格: {}", goldData.getLastPrice());
            return goldData;
        } catch (Exception e) {
            logger.error("获取黄金现货价格失败", e);
            throw new RuntimeException("获取黄金现货价格失败", e);
        }
    }
    
    /**
     * 获取原油现货价格
     */
    public CommodityData getCrudeOilPrice() {
        try {
            CommodityData oilData = marketClient.getTodayMarket(FuturesConfig.CRUDE_OIL);
            logger.info("成功获取原油价格: {}", oilData.getLastPrice());
            return oilData;
        } catch (Exception e) {
            logger.error("获取原油价格失败", e);
            throw new RuntimeException("获取原油价格失败", e);
        }
    }
    
    /**
     * 获取主要商品价格
     */
    public void getMajorCommoditiesPrices() {
        try {
            List<CommodityData> commodities = marketClient.getCurrencyRates(null);
            List<CommodityData> majorCommodities = commodities.stream()
                    .filter(c -> c.getSymbol().contains("XAU") || 
                                c.getSymbol().contains("XAG") || 
                                c.getSymbol().contains("CL") ||
                                c.getSymbol().contains("NG"))
                    .collect(Collectors.toList());
            
            logger.info("获取 {} 个主要商品价格", majorCommodities.size());
            majorCommodities.forEach(this::logCommodityPrice);
            
        } catch (Exception e) {
            logger.error("获取主要商品价格失败", e);
            throw new RuntimeException("获取主要商品价格失败", e);
        }
    }
    
    /**
     * 获取期货K线数据
     */
    public List<FuturesKLine> getFuturesKLineData(String symbol, String interval) {
        try {
            List<FuturesKLine> klines = futuresClient.getFuturesKLine(symbol, interval);
            logger.info("成功获取 {} 的K线数据,共 {} 条", symbol, klines.size());
            return klines;
        } catch (Exception e) {
            logger.error("获取期货K线数据失败: {}", symbol, e);
            throw new RuntimeException("获取期货K线数据失败: " + symbol, e);
        }
    }
    
    /**
     * 启动实时数据监控
     */
    public void startRealTimeMonitoring() {
        try {
            wsClient.connect();
            logger.info("期货实时数据监控已启动");
        } catch (Exception e) {
            logger.error("启动实时数据监控失败", e);
            throw new RuntimeException("启动实时数据监控失败", e);
        }
    }
    
    /**
     * 停止实时数据监控
     */
    public void stopRealTimeMonitoring() {
        wsClient.close();
        logger.info("期货实时数据监控已停止");
    }
    
    /**
     * 记录商品价格
     */
    private void logCommodityPrice(CommodityData commodity) {
        String trend = commodity.getChangePercent().contains("+") ? "📈" : "📉";
        logger.info("{} {}: {} ({})", 
            trend, commodity.getName(), commodity.getLastPrice(), commodity.getChangePercent());
    }
    
    /**
     * 关闭服务
     */
    public void close() {
        try {
            futuresClient.close();
            marketClient.close();
            wsClient.close();
            logger.info("FuturesDataService已关闭");
        } catch (Exception e) {
            logger.error("关闭服务时发生错误", e);
        }
    }
}

6. 使用示例

package com.stocktv.futures.demo;

import com.stocktv.futures.model.CommodityData;
import com.stocktv.futures.model.FuturesContract;
import com.stocktv.futures.model.FuturesKLine;
import com.stocktv.futures.service.FuturesDataService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * 期货数据使用示例
 */
public class FuturesDemo {
    
    private static final Logger logger = LoggerFactory.getLogger(FuturesDemo.class);
    
    public static void main(String[] args) {
        // 替换为您的实际 API Key
        String apiKey = "您的API_KEY";
        
        FuturesDataService futuresService = new FuturesDataService(apiKey);
        
        try {
            logger.info("=== StockTV 期货数据演示程序开始 ===");
            
            // 1. 获取期货合约列表
            demonstrateFuturesList(futuresService);
            
            // 2. 获取贵金属数据
            demonstratePreciousMetals(futuresService);
            
            // 3. 获取能源数据
            demonstrateEnergyFutures(futuresService);
            
            // 4. 获取现货价格
            demonstrateSpotPrices(futuresService);
            
            // 5. 获取K线数据
            demonstrateKLineData(futuresService);
            
            // 6. 启动实时监控(可选)
            // demonstrateRealTimeMonitoring(futuresService);
            
            logger.info("=== 演示程序执行完成 ===");
            
        } catch (Exception e) {
            logger.error("演示程序执行失败", e);
        } finally {
            // 关闭服务
            futuresService.close();
        }
    }
    
    /**
     * 演示期货合约列表
     */
    private static void demonstrateFuturesList(FuturesDataService service) {
        logger.info("\n1. 期货合约列表");
        List<FuturesContract> futures = service.getAllFutures();
        
        // 显示前10个合约
        futures.stream().limit(10).forEach(contract -> {
            String trend = contract.getChangePercent().doubleValue() >= 0 ? "🟢" : "🔴";
            logger.info("{} {}: {}{} ({}{}%) - {}", 
                trend, contract.getSymbol(), contract.getLastPrice(),
                contract.getChange().doubleValue() >= 0 ? "↑" : "↓",
                contract.getChangePercent().doubleValue() >= 0 ? "+" : "",
                contract.getChangePercent(), contract.getName());
        });
    }
    
    /**
     * 演示贵金属数据
     */
    private static void demonstratePreciousMetals(FuturesDataService service) {
        logger.info("\n2. 贵金属期货");
        List<FuturesContract> metals = service.getPreciousMetalsFutures();
        
        metals.forEach(contract -> {
            String trend = contract.getChangePercent().doubleValue() >= 0 ? "🟢" : "🔴";
            logger.info("{} {}: {}{} ({}{}%)", 
                trend, contract.getName(), contract.getLastPrice(),
                contract.getChange().doubleValue() >= 0 ? "↑" : "↓",
                contract.getChangePercent().doubleValue() >= 0 ? "+" : "",
                contract.getChangePercent());
        });
    }
    
    /**
     * 演示能源期货
     */
    private static void demonstrateEnergyFutures(FuturesDataService service) {
        logger.info("\n3. 能源期货");
        List<FuturesContract> energy = service.getEnergyFutures();
        
        energy.forEach(contract -> {
            String trend = contract.getChangePercent().doubleValue() >= 0 ? "🟢" : "🔴";
            logger.info("{} {}: {}{} ({}{}%) - 成交量: {}", 
                trend, contract.getName(), contract.getLastPrice(),
                contract.getChange().doubleValue() >= 0 ? "↑" : "↓",
                contract.getChangePercent().doubleValue() >= 0 ? "+" : "",
                contract.getChangePercent(), contract.getVolume());
        });
    }
    
    /**
     * 演示现货价格
     */
    private static void demonstrateSpotPrices(FuturesDataService service) {
        logger.info("\n4. 现货价格");
        
        // 获取黄金现货
        CommodityData gold = service.getGoldSpotPrice();
        printCommodityInfo(gold, "黄金现货");
        
        // 获取原油现货
        CommodityData oil = service.getCrudeOilPrice();
        printCommodityInfo(oil, "原油现货");
        
        // 获取主要商品
        service.getMajorCommoditiesPrices();
    }
    
    /**
     * 演示K线数据
     */
    private static void demonstrateKLineData(FuturesDataService service) {
        logger.info("\n5. K线数据示例");
        
        // 获取黄金K线数据
        List<FuturesKLine> goldKlines = service.getFuturesKLineData("XAU", "1");
        if (!goldKlines.isEmpty()) {
            logger.info("黄金期货近期K线数据:");
            goldKlines.stream().limit(5).forEach(kline -> {
                logger.info("时间: {}, 开: {}, 高: {}, 低: {}, 收: {}, 振幅: {}%", 
                    kline.getDate(), kline.getOpen(), kline.getHigh(), 
                    kline.getLow(), kline.getClose(), kline.getAmplitude());
            });
        }
    }
    
    /**
     * 演示实时监控
     */
    private static void demonstrateRealTimeMonitoring(FuturesDataService service) {
        logger.info("\n6. 启动实时监控");
        service.startRealTimeMonitoring();
        
        // 监控30秒
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        service.stopRealTimeMonitoring();
    }
    
    /**
     * 打印商品信息
     */
    private static void printCommodityInfo(CommodityData commodity, String description) {
        if (commodity != null) {
            String trend = commodity.getChangePercent().contains("+") ? "📈" : "📉";
            logger.info("{} {}: {}", trend, description, commodity.getLastPrice());
            logger.info("   涨跌: {} ({})", commodity.getChange(), commodity.getChangePercent());
            
            if (commodity.getBid() != null) {
                logger.info("   买卖盘: {} / {}", commodity.getBid(), commodity.getAsk());
            }
            if (commodity.getDayRange() != null) {
                logger.info("   当日区间: {}", commodity.getDayRange());
            }
        }
    }
}

🎯 高级功能

商品价格监控器

package com.stocktv.futures.advanced;

import com.stocktv.futures.model.CommodityData;
import com.stocktv.futures.model.FuturesContract;
import com.stocktv.futures.service.FuturesDataService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 商品价格监控器
 */
public class CommodityPriceMonitor {
    
    private static final Logger logger = LoggerFactory.getLogger(CommodityPriceMonitor.class);
    
    private final FuturesDataService futuresService;
    private final ScheduledExecutorService scheduler;
    private final Map<String, BigDecimal> priceAlerts;
    private final Set<String> monitoredSymbols;
    
    // 监控配置
    private final long checkIntervalSeconds = 60;
    private final double alertThresholdPercent = 2.0;
    
    public CommodityPriceMonitor(String apiKey) {
        this.futuresService = new FuturesDataService(apiKey);
        this.scheduler = Executors.newScheduledThreadPool(1);
        this.priceAlerts = new HashMap<>();
        this.monitoredSymbols = new HashSet<>();
        
        // 默认监控主要商品
        initializeDefaultMonitors();
    }
    
    /**
     * 初始化默认监控列表
     */
    private void initializeDefaultMonitors() {
        monitoredSymbols.add("XAU"); // 黄金
        monitoredSymbols.add("XAG"); // 白银
        monitoredSymbols.add("CL");  // 原油
        monitoredSymbols.add("NG");  // 天然气
        monitoredSymbols.add("HG");  // 铜
        
        logger.info("初始化监控 {} 个商品", monitoredSymbols.size());
    }
    
    /**
     * 添加价格预警
     */
    public void addPriceAlert(String symbol, BigDecimal targetPrice) {
        priceAlerts.put(symbol, targetPrice);
        logger.info("添加价格预警: {} - {}", symbol, targetPrice);
    }
    
    /**
     * 开始监控
     */
    public void startMonitoring() {
        logger.info("开始商品价格监控,检查间隔: {}秒", checkIntervalSeconds);
        scheduler.scheduleAtFixedRate(this::checkPrices, 0, checkIntervalSeconds, TimeUnit.SECONDS);
    }
    
    /**
     * 停止监控
     */
    public void stopMonitoring() {
        scheduler.shutdown();
        futuresService.close();
        logger.info("商品价格监控已停止");
    }
    
    /**
     * 检查价格变化
     */
    private void checkPrices() {
        try {
            List<FuturesContract> currentFutures = futuresService.getAllFutures();
            
            for (FuturesContract futures : currentFutures) {
                String symbol = futures.getSymbol();
                
                if (monitoredSymbols.contains(symbol)) {
                    checkPriceAlert(futures);
                    checkVolatilityAlert(futures);
                }
            }
            
            // 检查现货价格
            checkSpotPrices();
            
        } catch (Exception e) {
            logger.error("价格监控执行失败", e);
        }
    }
    
    /**
     * 检查价格预警
     */
    private void checkPriceAlert(FuturesContract futures) {
        BigDecimal targetPrice = priceAlerts.get(futures.getSymbol());
        if (targetPrice != null) {
            BigDecimal currentPrice = futures.getLastPrice();
            BigDecimal difference = currentPrice.subtract(targetPrice).abs();
            BigDecimal differencePercent = difference.divide(targetPrice, 4, BigDecimal.ROUND_HALF_UP)
                    .multiply(BigDecimal.valueOf(100));
            
            if (differencePercent.compareTo(BigDecimal.valueOf(alertThresholdPercent)) <= 0) {
                logger.warn("🚨 价格接近预警: {} 当前价 {} vs 目标价 {} (相差 {}%)", 
                    futures.getSymbol(), currentPrice, targetPrice, differencePercent);
            }
        }
    }
    
    /**
     * 检查波动率预警
     */
    private void checkVolatilityAlert(FuturesContract futures) {
        double changePercent = Math.abs(futures.getChangePercent().doubleValue());
        
        if (changePercent > alertThresholdPercent) {
            String direction = futures.getChangePercent().doubleValue() > 0 ? "上涨" : "下跌";
            logger.warn("🚨 价格波动预警: {} {} {}%", 
                futures.getSymbol(), direction, changePercent);
        }
    }
    
    /**
     * 检查现货价格
     */
    private void checkSpotPrices() {
        try {
            CommodityData gold = futuresService.getGoldSpotPrice();
            CommodityData oil = futuresService.getCrudeOilPrice();
            
            // 检查现货价格异常波动
            checkSpotPriceVolatility(gold, "黄金现货");
            checkSpotPriceVolatility(oil, "原油现货");
            
        } catch (Exception e) {
            logger.debug("获取现货价格失败: {}", e.getMessage());
        }
    }
    
    /**
     * 检查现货价格波动
     */
    private void checkSpotPriceVolatility(CommodityData commodity, String name) {
        if (commodity != null) {
            try {
                BigDecimal changePercent = commodity.getNumericChangePercent().abs();
                if (changePercent.compareTo(BigDecimal.valueOf(alertThresholdPercent)) > 0) {
                    String direction = commodity.getChangePercent().contains("+") ? "上涨" : "下跌";
                    logger.warn("🚨 现货价格波动: {} {} {}%", 
                        name, direction, changePercent);
                }
            } catch (Exception e) {
                // 忽略转换错误
            }
        }
    }
    
    /**
     * 获取监控报告
     */
    public void printMonitoringReport() {
        logger.info("=== 商品监控报告 ===");
        logger.info("监控商品数量: {}", monitoredSymbols.size());
        logger.info("价格预警数量: {}", priceAlerts.size());
        logger.info("波动预警阈值: {}%", alertThresholdPercent);
    }
}

⚡ 主要商品代码参考

商品类型 代码 说明
黄金 XAU 黄金期货
黄金现货 XAUUSD=X 黄金兑美元现货
白银 XAG 白银期货
白银现货 XAGUSD=X 白银兑美元现货
原油 CL WTI原油期货
布伦特原油 BZ 布伦特原油期货
天然气 NG 天然气期货
HG 铜期货
大豆 ZS 大豆期货
玉米 ZC 玉米期货

📊 时间间隔参数

期货时间间隔

  • 1: 1分钟
  • 5: 5分钟
  • 15: 15分钟
  • 30: 30分钟
  • 60: 60分钟
  • 1d: 1天

外汇时间间隔

  • 1m: 1分钟
  • 5m: 5分钟
  • 15m: 15分钟
  • 30m: 30分钟
  • 60m: 60分钟
  • 1h: 1小时
  • 1d: 1天
  • 1wk: 1周
  • 1mo: 1月

📞 技术支持

如果在使用过程中遇到问题,可以通过以下方式获取帮助:

  1. 查看日志: 启用DEBUG级别日志查看详细请求信息
  2. 检查网络: 确保可以正常访问 api.stocktv.top
  3. 验证API Key: 确认API Key有效且具有相应权限
  4. 联系支持: 通过官方渠道获取技术支持
posted @ 2025-11-12 17:34  CryptoRzz  阅读(7)  评论(0)    收藏  举报