本文档详细介绍如何使用 Java 语言通过 StockTV API 对接币安交易所数据,包含实时行情、K线数据、交易对信息等完整功能。

🚀 快速开始

环境要求

  • 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/binance/
├── config/
│   └── BinanceConfig.java
├── model/
│   ├── CryptoPair.java
│   ├── TickerPrice.java
│   ├── KLine.java
│   ├── Trade.java
│   └── ApiResponse.java
├── client/
│   ├── BinanceHttpClient.java
│   └── BinanceWebSocketClient.java
├── service/
│   └── BinanceDataService.java
└── demo/
    └── BinanceDemo.java

📦 核心代码实现

1. 配置类

package com.stocktv.binance.config;

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

/**
 * 币安数据配置类
 */
public class BinanceConfig {
    
    // 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 CRYPTO_PAIR_LIST = "/crypto/pairlist";
    public static final String CRYPTO_TICKER_PRICE = "/crypto/tickerPrice";
    public static final String CRYPTO_LAST_PRICE = "/crypto/lastPrice";
    public static final String CRYPTO_GET_KLINES = "/crypto/getKlines";
    public static final String CRYPTO_GET_TRADES = "/crypto/getTrades";
    
    // 币安市场ID
    public static final int BINANCE_MARKET_ID = 338;
    
    // 主要交易对
    public static final String BTC_USDT = "BTCUSDT";
    public static final String ETH_USDT = "ETHUSDT";
    public static final String BNB_USDT = "BNBUSDT";
    public static final String ADA_USDT = "ADAUSDT";
    public static final String DOT_USDT = "DOTUSDT";
    public static final String XRP_USDT = "XRPUSDT";
    public static final String LTC_USDT = "LTCUSDT";
    public static final String LINK_USDT = "LINKUSDT";
    public static final String BCH_USDT = "BCHUSDT";
    public static final String XLM_USDT = "XLMUSDT";
    
    // API Key
    private final String apiKey;
    
    // HTTP 客户端和JSON处理器
    private final CloseableHttpClient httpClient;
    private final ObjectMapper objectMapper;
    
    public BinanceConfig(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.binance.model;

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

import java.math.BigDecimal;

/**
 * 加密货币交易对数据模型
 */
@Data
public class CryptoPair {
    @JsonProperty("symbol")
    private String symbol;
    
    @JsonProperty("legal_currency_price")
    private BigDecimal legalCurrencyPrice;
    
    @JsonProperty("market_cap_display")
    private String marketCapDisplay;
    
    @JsonProperty("percent_change_utc0_3d")
    private BigDecimal percentChangeUtc03d;
    
    @JsonProperty("price")
    private String price;
    
    @JsonProperty("price_change_today")
    private String priceChangeToday;
    
    @JsonProperty("rank")
    private Integer rank;
    
    @JsonProperty("logo")
    private String logo;
    
    @JsonProperty("id")
    private Long id;
    
    @JsonProperty("price_display")
    private String priceDisplay;
    
    @JsonProperty("price_display_cny")
    private BigDecimal priceDisplayCny;
    
    @JsonProperty("market_id")
    private Integer marketId;
    
    @JsonProperty("market_name")
    private String marketName;
    
    @JsonProperty("market_cap_usd")
    private String marketCapUsd;
    
    @JsonProperty("exchange_time")
    private String exchangeTime;
    
    @JsonProperty("percent_change_1h")
    private BigDecimal percentChange1h;
    
    @JsonProperty("percent_change_24h")
    private BigDecimal percentChange24h;
    
    @JsonProperty("name")
    private String name;
    
    @JsonProperty("volume_24h")
    private String volume24h;
    
    @JsonProperty("price_usd")
    private String priceUsd;
    
    @JsonProperty("available_supply")
    private Long availableSupply;
    
    @JsonProperty("alias")
    private String alias;
    
    @JsonProperty("currency")
    private String currency;
    
    @JsonProperty("pair")
    private String pair;
    
    @JsonProperty("percent_change_7d")
    private BigDecimal percentChange7d;
    
    @JsonProperty("category")
    private String category;
    
    @JsonProperty("search_field")
    private String searchField;
    
    /**
     * 获取数值形式的当前价格
     */
    public BigDecimal getNumericPrice() {
        try {
            return new BigDecimal(price);
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }
    
    /**
     * 获取交易对基础货币
     */
    public String getBaseAsset() {
        if (pair != null && pair.contains("/")) {
            return pair.split("/")[0];
        }
        return symbol.replace("USDT", "");
    }
    
    /**
     * 获取交易对报价货币
     */
    public String getQuoteAsset() {
        if (pair != null && pair.contains("/")) {
            return pair.split("/")[1];
        }
        return "USDT";
    }
}
价格行情数据模型
package com.stocktv.binance.model;

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

import java.math.BigDecimal;

/**
 * 价格行情数据模型
 */
@Data
public class TickerPrice {
    @JsonProperty("priceChange")
    private String priceChange;
    
    @JsonProperty("symbol")
    private String symbol;
    
    @JsonProperty("count")
    private Long count;
    
    @JsonProperty("openPrice")
    private String openPrice;
    
    @JsonProperty("lastId")
    private Long lastId;
    
    @JsonProperty("quoteVolume")
    private String quoteVolume;
    
    @JsonProperty("firstId")
    private Long firstId;
    
    @JsonProperty("volume")
    private String volume;
    
    @JsonProperty("weightedAvgPrice")
    private String weightedAvgPrice;
    
    @JsonProperty("lowPrice")
    private String lowPrice;
    
    @JsonProperty("highPrice")
    private String highPrice;
    
    @JsonProperty("closeTime")
    private Long closeTime;
    
    @JsonProperty("openTime")
    private Long openTime;
    
    @JsonProperty("priceChangePercent")
    private String priceChangePercent;
    
    @JsonProperty("lastPrice")
    private String lastPrice;
    
    /**
     * 获取数值形式的最后价格
     */
    public BigDecimal getNumericLastPrice() {
        try {
            return new BigDecimal(lastPrice);
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }
    
    /**
     * 获取数值形式的涨跌幅
     */
    public BigDecimal getNumericPriceChangePercent() {
        try {
            return new BigDecimal(priceChangePercent);
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }
    
    /**
     * 获取数值形式的交易量
     */
    public BigDecimal getNumericVolume() {
        try {
            return new BigDecimal(volume);
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }
}
最新价格数据模型
package com.stocktv.binance.model;

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

import java.math.BigDecimal;

/**
 * 最新价格数据模型
 */
@Data
public class LastPrice {
    @JsonProperty("symbol")
    private String symbol;
    
    @JsonProperty("price")
    private String price;
    
    /**
     * 获取数值形式的价格
     */
    public BigDecimal getNumericPrice() {
        try {
            return new BigDecimal(price);
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }
}
K线数据模型
package com.stocktv.binance.model;

import lombok.Data;

import java.math.BigDecimal;

/**
 * K线数据模型
 */
@Data
public class KLine {
    private Long openTime;
    private BigDecimal openPrice;
    private BigDecimal highPrice;
    private BigDecimal lowPrice;
    private BigDecimal closePrice;
    private BigDecimal volume;
    private Long closeTime;
    private BigDecimal quoteAssetVolume;
    private Integer numberOfTrades;
    private BigDecimal takerBuyBaseAssetVolume;
    private BigDecimal takerBuyQuoteAssetVolume;
    private String ignore;
    
    /**
     * 构造函数 - 从数组解析
     */
    public KLine(Object[] data) {
        if (data != null && data.length >= 11) {
            this.openTime = ((Number) data[0]).longValue();
            this.openPrice = new BigDecimal(data[1].toString());
            this.highPrice = new BigDecimal(data[2].toString());
            this.lowPrice = new BigDecimal(data[3].toString());
            this.closePrice = new BigDecimal(data[4].toString());
            this.volume = new BigDecimal(data[5].toString());
            this.closeTime = ((Number) data[6]).longValue();
            this.quoteAssetVolume = new BigDecimal(data[7].toString());
            this.numberOfTrades = ((Number) data[8]).intValue();
            this.takerBuyBaseAssetVolume = new BigDecimal(data[9].toString());
            this.takerBuyQuoteAssetVolume = new BigDecimal(data[10].toString());
            if (data.length > 11) {
                this.ignore = data[11].toString();
            }
        }
    }
    
    /**
     * 计算振幅百分比
     */
    public BigDecimal getAmplitudePercent() {
        if (openPrice.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return highPrice.subtract(lowPrice)
                .divide(openPrice, 4, BigDecimal.ROUND_HALF_UP)
                .multiply(BigDecimal.valueOf(100));
    }
    
    /**
     * 计算涨跌幅百分比
     */
    public BigDecimal getChangePercent() {
        if (openPrice.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return closePrice.subtract(openPrice)
                .divide(openPrice, 4, BigDecimal.ROUND_HALF_UP)
                .multiply(BigDecimal.valueOf(100));
    }
}
交易数据模型
package com.stocktv.binance.model;

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

import java.math.BigDecimal;

/**
 * 交易数据模型
 */
@Data
public class Trade {
    @JsonProperty("quoteQty")
    private String quoteQty;
    
    @JsonProperty("price")
    private String price;
    
    @JsonProperty("qty")
    private String qty;
    
    @JsonProperty("isBestMatch")
    private Boolean isBestMatch;
    
    @JsonProperty("id")
    private Long id;
    
    @JsonProperty("time")
    private Long time;
    
    @JsonProperty("isBuyerMaker")
    private Boolean isBuyerMaker;
    
    /**
     * 获取数值形式的价格
     */
    public BigDecimal getNumericPrice() {
        try {
            return new BigDecimal(price);
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }
    
    /**
     * 获取数值形式的数量
     */
    public BigDecimal getNumericQty() {
        try {
            return new BigDecimal(qty);
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }
    
    /**
     * 获取交易方向
     */
    public String getTradeDirection() {
        return Boolean.TRUE.equals(isBuyerMaker) ? "SELL" : "BUY";
    }
}
API响应包装类
package com.stocktv.binance.model;

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

import java.util.List;

/**
 * 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 PairListResponse {
    @JsonProperty("total_count")
    private Integer totalCount;
    
    @JsonProperty("total_page")
    private Integer totalPage;
    
    @JsonProperty("list")
    private List<CryptoPair> list;
    
    @JsonProperty("fields")
    private List<String> fields;
}

3. HTTP客户端实现

package com.stocktv.binance.client;

import com.fasterxml.jackson.core.type.TypeReference;
import com.stocktv.binance.config.BinanceConfig;
import com.stocktv.binance.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.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 币安数据HTTP客户端
 */
public class BinanceHttpClient {
    
    private static final Logger logger = LoggerFactory.getLogger(BinanceHttpClient.class);
    
    private final BinanceConfig config;
    private final CloseableHttpClient httpClient;
    
    public BinanceHttpClient(BinanceConfig config) {
        this.config = config;
        this.httpClient = config.getHttpClient();
    }
    
    /**
     * 获取交易对列表
     */
    public List<CryptoPair> getPairList(Integer page, Integer size) throws IOException, URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder(config.BASE_URL + config.CRYPTO_PAIR_LIST)
                .addParameter("key", config.getApiKey())
                .addParameter("marketId", String.valueOf(config.BINANCE_MARKET_ID));
        
        if (page != null) {
            uriBuilder.addParameter("page", String.valueOf(page));
        }
        if (size != null) {
            uriBuilder.addParameter("size", String.valueOf(size));
        }
        
        URI uri = uriBuilder.build();
        
        ApiResponse<PairListResponse> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<PairListResponse>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取 {} 个交易对", response.getData().getList().size());
            return response.getData().getList();
        } else {
            throw new RuntimeException("获取交易对列表失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取最新行情
     */
    public List<TickerPrice> getTickerPrice(List<String> symbols) throws IOException, URISyntaxException {
        if (symbols == null || symbols.isEmpty()) {
            throw new IllegalArgumentException("交易对列表不能为空");
        }
        
        // 限制最大数量
        if (symbols.size() > 100) {
            symbols = symbols.subList(0, 100);
            logger.warn("交易对数量超过限制,只取前100个");
        }
        
        String symbolsStr = String.join(",", symbols);
        
        URI uri = new URIBuilder(config.BASE_URL + config.CRYPTO_TICKER_PRICE)
                .addParameter("key", config.getApiKey())
                .addParameter("symbols", symbolsStr)
                .build();
        
        ApiResponse<List<TickerPrice>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<TickerPrice>>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取 {} 个交易对的最新行情", response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("获取最新行情失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取最新价格
     */
    public List<LastPrice> getLastPrice(List<String> symbols) throws IOException, URISyntaxException {
        if (symbols == null || symbols.isEmpty()) {
            throw new IllegalArgumentException("交易对列表不能为空");
        }
        
        // 限制最大数量
        if (symbols.size() > 100) {
            symbols = symbols.subList(0, 100);
            logger.warn("交易对数量超过限制,只取前100个");
        }
        
        String symbolsStr = String.join(",", symbols);
        
        URI uri = new URIBuilder(config.BASE_URL + config.CRYPTO_LAST_PRICE)
                .addParameter("key", config.getApiKey())
                .addParameter("symbols", symbolsStr)
                .build();
        
        ApiResponse<List<LastPrice>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<LastPrice>>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取 {} 个交易对的最新价格", response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("获取最新价格失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取K线数据
     */
    public List<KLine> getKlines(String symbol, String interval) throws IOException, URISyntaxException {
        if (symbol == null || symbol.trim().isEmpty()) {
            throw new IllegalArgumentException("交易对不能为空");
        }
        
        URI uri = new URIBuilder(config.BASE_URL + config.CRYPTO_GET_KLINES)
                .addParameter("key", config.getApiKey())
                .addParameter("symbol", symbol)
                .addParameter("interval", interval)
                .build();
        
        // K线数据返回的是数组的数组
        ApiResponse<List<Object[]>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<Object[]>>>() {});
        
        if (response.isSuccess()) {
            List<KLine> klines = response.getData().stream()
                    .map(KLine::new)
                    .collect(Collectors.toList());
            logger.info("成功获取交易对 {} 的K线数据,共 {} 条", symbol, klines.size());
            return klines;
        } else {
            throw new RuntimeException("获取K线数据失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取近期成交
     */
    public List<Trade> getTrades(String symbol) throws IOException, URISyntaxException {
        if (symbol == null || symbol.trim().isEmpty()) {
            throw new IllegalArgumentException("交易对不能为空");
        }
        
        URI uri = new URIBuilder(config.BASE_URL + config.CRYPTO_GET_TRADES)
                .addParameter("key", config.getApiKey())
                .addParameter("symbol", symbol)
                .build();
        
        ApiResponse<List<Trade>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<Trade>>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取交易对 {} 的成交数据,共 {} 条", symbol, response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("获取成交数据失败: " + response.getMessage());
        }
    }
    
    /**
     * 搜索交易对
     */
    public List<CryptoPair> searchPairs(String keyword, Integer limit) throws IOException, URISyntaxException {
        List<CryptoPair> allPairs = getPairList(1, 1000); // 获取较多数据用于搜索
        
        return allPairs.stream()
                .filter(pair -> 
                    pair.getSymbol().toLowerCase().contains(keyword.toLowerCase()) ||
                    pair.getName().toLowerCase().contains(keyword.toLowerCase()) ||
                    (pair.getSearchField() != null && 
                     pair.getSearchField().toLowerCase().contains(keyword.toLowerCase())))
                .limit(limit != null ? limit : 10)
                .collect(Collectors.toList());
    }
    
    /**
     * 获取Top N市值的加密货币
     */
    public List<CryptoPair> getTopMarketCapPairs(int topN) throws IOException, URISyntaxException {
        List<CryptoPair> allPairs = getPairList(1, topN);
        
        // 按排名排序
        return allPairs.stream()
                .sorted((p1, p2) -> {
                    if (p1.getRank() == null) return 1;
                    if (p2.getRank() == null) return -1;
                    return p1.getRank().compareTo(p2.getRank());
                })
                .limit(topN)
                .collect(Collectors.toList());
    }
    
    /**
     * 通用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. WebSocket客户端实现

package com.stocktv.binance.client;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.stocktv.binance.config.BinanceConfig;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 币安WebSocket实时数据客户端
 */
public class BinanceWebSocketClient {
    
    private static final Logger logger = LoggerFactory.getLogger(BinanceWebSocketClient.class);
    
    private final BinanceConfig config;
    private final ObjectMapper objectMapper;
    private WebSocketClient webSocketClient;
    private CountDownLatch connectionLatch;
    
    public BinanceWebSocketClient(BinanceConfig config) {
        this.config = config;
        this.objectMapper = config.getObjectMapper();
    }
    
    /**
     * 连接WebSocket服务器
     */
    public void connect() throws Exception {
        String wsUrl = config.WS_URL + "?key=" + config.getApiKey();
        URI serverUri = URI.create(wsUrl);
        
        connectionLatch = new CountDownLatch(1);
        
        webSocketClient = new WebSocketClient(serverUri) {
            @Override
            public void onOpen(ServerHandshake handshake) {
                logger.info("币安WebSocket连接已建立");
                connectionLatch.countDown();
            }
            
            @Override
            public void onMessage(String message) {
                try {
                    handleRealTimeMessage(message);
                } catch (Exception e) {
                    logger.error("处理WebSocket消息时出错", e);
                }
            }
            
            @Override
            public void onClose(int code, String reason, boolean remote) {
                logger.info("币安WebSocket连接已关闭: code={}, reason={}, remote={}", code, reason, remote);
            }
            
            @Override
            public void onError(Exception ex) {
                logger.error("币安WebSocket连接错误", ex);
            }
        };
        
        webSocketClient.connect();
        
        // 等待连接建立
        if (!connectionLatch.await(10, TimeUnit.SECONDS)) {
            throw new RuntimeException("WebSocket连接超时");
        }
    }
    
    /**
     * 处理实时消息
     */
    private void handleRealTimeMessage(String message) throws Exception {
        JsonNode jsonNode = objectMapper.readTree(message);
        
        // 解析实时行情数据
        if (jsonNode.has("symbol") || jsonNode.has("pid")) {
            RealTimeData realTimeData = parseRealTimeData(jsonNode);
            onRealTimeData(realTimeData);
        } else {
            logger.debug("收到其他消息: {}", message);
        }
    }
    
    /**
     * 解析实时数据
     */
    private RealTimeData parseRealTimeData(JsonNode jsonNode) {
        RealTimeData data = new RealTimeData();
        
        if (jsonNode.has("symbol")) {
            data.setSymbol(jsonNode.get("symbol").asText());
        }
        if (jsonNode.has("pid")) {
            data.setPid(jsonNode.get("pid").asText());
        }
        if (jsonNode.has("last_numeric")) {
            data.setLastPrice(jsonNode.get("last_numeric").asText());
        }
        if (jsonNode.has("bid")) {
            data.setBid(jsonNode.get("bid").asText());
        }
        if (jsonNode.has("ask")) {
            data.setAsk(jsonNode.get("ask").asText());
        }
        if (jsonNode.has("high")) {
            data.setHigh(jsonNode.get("high").asText());
        }
        if (jsonNode.has("low")) {
            data.setLow(jsonNode.get("low").asText());
        }
        if (jsonNode.has("pc")) {
            data.setPriceChange(jsonNode.get("pc").asText());
        }
        if (jsonNode.has("pcp")) {
            data.setChangePercent(jsonNode.get("pcp").asText());
        }
        if (jsonNode.has("turnover_numeric")) {
            data.setVolume(jsonNode.get("turnover_numeric").asText());
        }
        if (jsonNode.has("time")) {
            data.setTime(jsonNode.get("time").asText());
        }
        if (jsonNode.has("timestamp")) {
            data.setTimestamp(jsonNode.get("timestamp").asText());
        }
        
        return data;
    }
    
    /**
     * 处理实时数据
     */
    protected void onRealTimeData(RealTimeData data) {
        String symbol = data.getSymbol() != null ? data.getSymbol() : data.getPid();
        
        // 记录基础信息
        logger.debug("实时行情: {} - 价格: {}, 涨跌幅: {}%", 
            symbol, data.getLastPrice(), data.getChangePercent());
        
        // 价格预警逻辑
        try {
            double changePercent = Double.parseDouble(data.getChangePercent());
            if (Math.abs(changePercent) > 5.0) {
                logger.warn("🚨 加密货币价格波动预警: {} 波动 {}%", symbol, changePercent);
            }
            
            // 大额交易监控
            if (data.getVolume() != null) {
                double volume = Double.parseDouble(data.getVolume());
                if (volume > 1000000) { // 100万美元以上
                    logger.info("💰 大额交易: {} 成交额 ${}", symbol, volume);
                }
            }
        } catch (NumberFormatException e) {
            // 忽略转换错误
        }
    }
    
    /**
     * 发送消息
     */
    public void sendMessage(String message) {
        if (webSocketClient != null && webSocketClient.isOpen()) {
            webSocketClient.send(message);
        }
    }
    
    /**
     * 关闭连接
     */
    public void close() {
        if (webSocketClient != null) {
            webSocketClient.close();
        }
    }
    
    /**
     * 实时数据模型
     */
    public static class RealTimeData {
        private String pid;
        private String symbol;
        private String lastPrice;
        private String bid;
        private String ask;
        private String high;
        private String low;
        private String priceChange;
        private String changePercent;
        private String volume;
        private String time;
        private String timestamp;
        
        // Getters and Setters
        public String getPid() { return pid; }
        public void setPid(String pid) { this.pid = pid; }
        public String getSymbol() { return symbol; }
        public void setSymbol(String symbol) { this.symbol = symbol; }
        public String getLastPrice() { return lastPrice; }
        public void setLastPrice(String lastPrice) { this.lastPrice = lastPrice; }
        public String getBid() { return bid; }
        public void setBid(String bid) { this.bid = bid; }
        public String getAsk() { return ask; }
        public void setAsk(String ask) { this.ask = ask; }
        public String getHigh() { return high; }
        public void setHigh(String high) { this.high = high; }
        public String getLow() { return low; }
        public void setLow(String low) { this.low = low; }
        public String getPriceChange() { return priceChange; }
        public void setPriceChange(String priceChange) { this.priceChange = priceChange; }
        public String getChangePercent() { return changePercent; }
        public void setChangePercent(String changePercent) { this.changePercent = changePercent; }
        public String getVolume() { return volume; }
        public void setVolume(String volume) { this.volume = volume; }
        public String getTime() { return time; }
        public void setTime(String time) { this.time = time; }
        public String getTimestamp() { return timestamp; }
        public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
    }
}
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐