博客 / 詳情

返回

國際期貨、黃金、石油數據 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. 聯繫支持: 通過官方渠道獲取技術支持
user avatar esunr 頭像 mulander 頭像 fehaha 頭像 anygraphanywhere 頭像 niaonao 頭像 hailongwang_od 頭像 yubaolee 頭像 tinyang 頭像 yancy_5f300f5cc4004 頭像 best-doraemon 頭像 camille_5f9b7f6b8693f 頭像 kumendejianpan 頭像
28 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.