DeepSeek印度股票数据源 Java 对接文档

📋 文档概述

本文档详细介绍如何使用 Java 语言对接 StockTV 印度股票数据源,包含完整的代码示例、数据模型、异常处理等。

🚀 快速开始

环境要求

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

项目依赖

xml 复制代码
<!-- 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>
    
    <!-- 日志框架 -->
    <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>

🏗️ 核心架构

项目结构

css 复制代码
src/main/java/com/stocktv/india/
├── config/
│   └── StockTVConfig.java
├── model/
│   ├── Stock.java
│   ├── Index.java
│   ├── KLine.java
│   └── ApiResponse.java
├── client/
│   ├── StockTVHttpClient.java
│   └── StockTVWebSocketClient.java
├── service/
│   └── IndiaStockService.java
└── demo/
    └── IndiaStockDemo.java

📦 核心代码实现

1. 配置类

java 复制代码
package com.stocktv.india.config;

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

/**
 * StockTV API 配置类
 */
public class StockTVConfig {
    
    // 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 int INDIA_COUNTRY_ID = 14;
    public static final int NSE_EXCHANGE_ID = 46;
    public static final int BSE_EXCHANGE_ID = 74;
    
    // API Key
    private final String apiKey;
    
    // HTTP 客户端和JSON处理器
    private final CloseableHttpClient httpClient;
    private final ObjectMapper objectMapper;
    
    public StockTVConfig(String apiKey) {
        this.apiKey = apiKey;
        this.httpClient = HttpClients.createDefault();
        this.objectMapper = new ObjectMapper();
        // 配置ObjectMapper
        this.objectMapper.findAndRegisterModules();
    }
    
    // Getter方法
    public String getApiKey() { return apiKey; }
    public CloseableHttpClient getHttpClient() { return httpClient; }
    public ObjectMapper getObjectMapper() { return objectMapper; }
}

2. 数据模型类

股票数据模型

java 复制代码
package com.stocktv.india.model;

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

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

/**
 * 印度股票数据模型
 */
@Data
public class Stock {
    @JsonProperty("id")
    private Long id;
    
    @JsonProperty("symbol")
    private String symbol;
    
    @JsonProperty("name")
    private String name;
    
    @JsonProperty("last")
    private BigDecimal lastPrice;
    
    @JsonProperty("chg")
    private BigDecimal change;
    
    @JsonProperty("chgPct")
    private BigDecimal changePercent;
    
    @JsonProperty("high")
    private BigDecimal high;
    
    @JsonProperty("low")
    private BigDecimal low;
    
    @JsonProperty("volume")
    private Long volume;
    
    @JsonProperty("open")
    private Boolean isOpen;
    
    @JsonProperty("exchangeId")
    private Integer exchangeId;
    
    @JsonProperty("countryId")
    private Integer countryId;
    
    @JsonProperty("time")
    private Long timestamp;
    
    @JsonProperty("fundamentalMarketCap")
    private BigDecimal marketCap;
    
    @JsonProperty("fundamentalRevenue")
    private String revenue;
    
    // 技术指标
    @JsonProperty("technicalDay")
    private String technicalDay;
    
    @JsonProperty("technicalHour")
    private String technicalHour;
    
    @JsonProperty("technicalWeek")
    private String technicalWeek;
    
    @JsonProperty("technicalMonth")
    private String technicalMonth;
    
    // 性能指标
    @JsonProperty("performanceDay")
    private BigDecimal performanceDay;
    
    @JsonProperty("performanceWeek")
    private BigDecimal performanceWeek;
    
    @JsonProperty("performanceMonth")
    private BigDecimal performanceMonth;
    
    @JsonProperty("performanceYtd")
    private BigDecimal performanceYtd;
    
    /**
     * 获取格式化的时间
     */
    public LocalDateTime getFormattedTime() {
        return LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault());
    }
    
    /**
     * 获取交易所名称
     */
    public String getExchangeName() {
        if (NSE_EXCHANGE_ID == exchangeId) {
            return "NSE";
        } else if (BSE_EXCHANGE_ID == exchangeId) {
            return "BSE";
        }
        return "Unknown";
    }
}

// 常量接口
interface ExchangeConstants {
    int NSE_EXCHANGE_ID = 46;
    int BSE_EXCHANGE_ID = 74;
}

K线数据模型

java 复制代码
package com.stocktv.india.model;

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

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

/**
 * K线数据模型
 */
@Data
public class KLine {
    @JsonProperty("time")
    private Long timestamp;
    
    @JsonProperty("open")
    private BigDecimal open;
    
    @JsonProperty("high")
    private BigDecimal high;
    
    @JsonProperty("low")
    private BigDecimal low;
    
    @JsonProperty("close")
    private BigDecimal close;
    
    @JsonProperty("volume")
    private Long volume;
    
    @JsonProperty("vo")
    private BigDecimal turnover;
    
    /**
     * 获取格式化的时间
     */
    public LocalDateTime getFormattedTime() {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
    }
    
    /**
     * 计算振幅
     */
    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);
    }
}

指数数据模型

java 复制代码
package com.stocktv.india.model;

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

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

/**
 * 指数数据模型
 */
@Data
public class Index {
    @JsonProperty("id")
    private Long id;
    
    @JsonProperty("name")
    private String name;
    
    @JsonProperty("symbol")
    private String symbol;
    
    @JsonProperty("last")
    private BigDecimal lastPrice;
    
    @JsonProperty("chg")
    private BigDecimal change;
    
    @JsonProperty("chgPct")
    private BigDecimal changePercent;
    
    @JsonProperty("high")
    private BigDecimal high;
    
    @JsonProperty("low")
    private BigDecimal low;
    
    @JsonProperty("isOpen")
    private Boolean isOpen;
    
    @JsonProperty("time")
    private Long timestamp;
    
    @JsonProperty("flag")
    private String countryFlag;
    
    /**
     * 获取格式化的时间
     */
    public LocalDateTime getFormattedTime() {
        return LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault());
    }
}

API响应包装类

java 复制代码
package com.stocktv.india.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 StockListResponse {
    @JsonProperty("records")
    private List<Stock> records;
    
    @JsonProperty("total")
    private Integer total;
    
    @JsonProperty("current")
    private Integer current;
    
    @JsonProperty("pages")
    private Integer pages;
    
    @JsonProperty("size")
    private Integer size;
}

3. HTTP客户端实现

java 复制代码
package com.stocktv.india.client;

import com.fasterxml.jackson.core.type.TypeReference;
import com.stocktv.india.config.StockTVConfig;
import com.stocktv.india.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;

/**
 * StockTV HTTP API客户端
 */
public class StockTVHttpClient {
    
    private static final Logger logger = LoggerFactory.getLogger(StockTVHttpClient.class);
    
    private final StockTVConfig config;
    private final CloseableHttpClient httpClient;
    
    public StockTVHttpClient(StockTVConfig config) {
        this.config = config;
        this.httpClient = config.getHttpClient();
    }
    
    /**
     * 获取印度股票列表
     * 
     * @param pageSize 每页数量
     * @param page 页码
     * @return 股票列表
     */
    public List<Stock> getIndiaStocks(Integer pageSize, Integer page) throws IOException, URISyntaxException {
        URI uri = new URIBuilder(config.BASE_URL + "/stock/stocks")
                .addParameter("countryId", String.valueOf(StockTVConfig.INDIA_COUNTRY_ID))
                .addParameter("pageSize", String.valueOf(pageSize))
                .addParameter("page", String.valueOf(page))
                .addParameter("key", config.getApiKey())
                .build();
        
        ApiResponse<StockListResponse> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<StockListResponse>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取 {} 条印度股票数据", response.getData().getRecords().size());
            return response.getData().getRecords();
        } else {
            throw new RuntimeException("API请求失败: " + response.getMessage());
        }
    }
    
    /**
     * 查询单个股票
     * 
     * @param pid 股票PID
     * @param symbol 股票代码
     * @param name 股票名称
     * @return 股票列表
     */
    public List<Stock> queryStock(Long pid, String symbol, String name) throws IOException, URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder(config.BASE_URL + "/stock/queryStocks")
                .addParameter("key", config.getApiKey());
        
        if (pid != null) {
            uriBuilder.addParameter("id", String.valueOf(pid));
        }
        if (symbol != null) {
            uriBuilder.addParameter("symbol", symbol);
        }
        if (name != null) {
            uriBuilder.addParameter("name", name);
        }
        
        URI uri = uriBuilder.build();
        
        ApiResponse<List<Stock>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<Stock>>>() {});
        
        if (response.isSuccess()) {
            logger.info("股票查询成功,返回 {} 条记录", response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("股票查询失败: " + response.getMessage());
        }
    }
    
    /**
     * 批量查询多个股票
     * 
     * @param pids 股票PID列表
     * @return 股票列表
     */
    public List<Stock> getStocksByPids(List<Long> pids) throws IOException, URISyntaxException {
        if (pids == null || pids.isEmpty()) {
            throw new IllegalArgumentException("股票PID列表不能为空");
        }
        
        String pidsStr = String.join(",", pids.stream().map(String::valueOf).toArray(String[]::new));
        
        URI uri = new URIBuilder(config.BASE_URL + "/stock/stocksByPids")
                .addParameter("key", config.getApiKey())
                .addParameter("pids", pidsStr)
                .build();
        
        ApiResponse<List<Stock>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<Stock>>>() {});
        
        if (response.isSuccess()) {
            logger.info("批量查询成功,返回 {} 条记录", response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("批量查询失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取印度主要指数
     * 
     * @return 指数列表
     */
    public List<Index> getIndiaIndices() throws IOException, URISyntaxException {
        URI uri = new URIBuilder(config.BASE_URL + "/stock/indices")
                .addParameter("countryId", String.valueOf(StockTVConfig.INDIA_COUNTRY_ID))
                .addParameter("key", config.getApiKey())
                .build();
        
        ApiResponse<List<Index>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<Index>>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取 {} 个印度指数", response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("获取指数失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取K线数据
     * 
     * @param pid 股票PID
     * @param interval 时间间隔
     * @return K线数据列表
     */
    public List<KLine> getKLineData(Long pid, String interval) throws IOException, URISyntaxException {
        if (pid == null) {
            throw new IllegalArgumentException("股票PID不能为空");
        }
        
        URI uri = new URIBuilder(config.BASE_URL + "/stock/kline")
                .addParameter("pid", String.valueOf(pid))
                .addParameter("interval", interval)
                .addParameter("key", config.getApiKey())
                .build();
        
        ApiResponse<List<KLine>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<KLine>>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取股票 {} 的K线数据,共 {} 条", pid, response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("获取K线数据失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取涨跌排行榜
     * 
     * @param type 排行榜类型
     * @return 股票列表
     */
    public List<Stock> getUpDownList(Integer type) throws IOException, URISyntaxException {
        URI uri = new URIBuilder(config.BASE_URL + "/stock/updownList")
                .addParameter("countryId", String.valueOf(StockTVConfig.INDIA_COUNTRY_ID))
                .addParameter("type", String.valueOf(type))
                .addParameter("key", config.getApiKey())
                .build();
        
        ApiResponse<List<Stock>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<Stock>>>() {});
        
        if (response.isSuccess()) {
            String typeName = getRankingTypeName(type);
            logger.info("成功获取{},共 {} 条记录", typeName, response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("获取排行榜失败: " + response.getMessage());
        }
    }
    
    /**
     * 获取IPO新股日历
     * 
     * @param type IPO类型
     * @return IPO列表
     */
    public List<Object> getIpoList(Integer type) throws IOException, URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder(config.BASE_URL + "/stock/getIpo")
                .addParameter("countryId", String.valueOf(StockTVConfig.INDIA_COUNTRY_ID))
                .addParameter("key", config.getApiKey());
        
        if (type != null) {
            uriBuilder.addParameter("type", String.valueOf(type));
        }
        
        URI uri = uriBuilder.build();
        
        ApiResponse<List<Object>> response = executeGetRequest(uri, 
            new TypeReference<ApiResponse<List<Object>>>() {});
        
        if (response.isSuccess()) {
            logger.info("成功获取IPO数据,共 {} 条", response.getData().size());
            return response.getData();
        } else {
            throw new RuntimeException("获取IPO数据失败: " + 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);
        }
    }
    
    /**
     * 获取排行榜类型名称
     */
    private String getRankingTypeName(Integer type) {
        switch (type) {
            case 1: return "涨幅榜";
            case 2: return "跌幅榜";
            case 3: return "涨停榜";
            case 4: return "跌停榜";
            default: return "排行榜";
        }
    }
    
    /**
     * 关闭HTTP客户端
     */
    public void close() throws IOException {
        if (httpClient != null) {
            httpClient.close();
        }
    }
}

4. 服务层封装

java 复制代码
package com.stocktv.india.service;

import com.stocktv.india.client.StockTVHttpClient;
import com.stocktv.india.config.StockTVConfig;
import com.stocktv.india.model.Index;
import com.stocktv.india.model.KLine;
import com.stocktv.india.model.Stock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * 印度股票数据服务
 */
public class IndiaStockService {
    
    private static final Logger logger = LoggerFactory.getLogger(IndiaStockService.class);
    
    private final StockTVHttpClient httpClient;
    private final StockTVConfig config;
    
    public IndiaStockService(String apiKey) {
        this.config = new StockTVConfig(apiKey);
        this.httpClient = new StockTVHttpClient(config);
    }
    
    /**
     * 获取Nifty 50成分股
     */
    public List<Stock> getNifty50Stocks() {
        try {
            // 实际应用中应该根据Nifty 50成分股列表查询
            List<Stock> stocks = httpClient.getIndiaStocks(50, 1);
            logger.info("获取Nifty 50成分股成功,共 {} 只股票", stocks.size());
            return stocks;
        } catch (Exception e) {
            logger.error("获取Nifty 50成分股失败", e);
            throw new RuntimeException("获取Nifty 50成分股失败", e);
        }
    }
    
    /**
     * 获取印度主要指数
     */
    public List<Index> getMajorIndices() {
        try {
            List<Index> indices = httpClient.getIndiaIndices();
            logger.info("获取印度主要指数成功,共 {} 个指数", indices.size());
            return indices;
        } catch (Exception e) {
            logger.error("获取印度指数失败", e);
            throw new RuntimeException("获取印度指数失败", e);
        }
    }
    
    /**
     * 查询特定股票
     */
    public Stock getStockBySymbol(String symbol) {
        try {
            List<Stock> stocks = httpClient.queryStock(null, symbol, null);
            if (stocks.isEmpty()) {
                throw new RuntimeException("未找到股票: " + symbol);
            }
            logger.info("查询股票 {} 成功", symbol);
            return stocks.get(0);
        } catch (Exception e) {
            logger.error("查询股票失败: " + symbol, e);
            throw new RuntimeException("查询股票失败: " + symbol, e);
        }
    }
    
    /**
     * 批量查询股票
     */
    public List<Stock> getStocksBySymbols(List<String> symbols) {
        try {
            // 这里需要先将symbol转换为pid,简化处理直接查询
            List<Stock> result = symbols.stream()
                    .map(this::getStockBySymbol)
                    .collect(Collectors.toList());
            logger.info("批量查询 {} 只股票成功", symbols.size());
            return result;
        } catch (Exception e) {
            logger.error("批量查询股票失败", e);
            throw new RuntimeException("批量查询股票失败", e);
        }
    }
    
    /**
     * 获取股票K线数据
     */
    public List<KLine> getStockKLine(Long pid, String interval) {
        try {
            List<KLine> klines = httpClient.getKLineData(pid, interval);
            logger.info("获取股票 {} 的K线数据成功,共 {} 条", pid, klines.size());
            return klines;
        } catch (Exception e) {
            logger.error("获取K线数据失败: pid=" + pid, e);
            throw new RuntimeException("获取K线数据失败: pid=" + pid, e);
        }
    }
    
    /**
     * 获取涨幅榜
     */
    public List<Stock> getGainers() {
        try {
            List<Stock> gainers = httpClient.getUpDownList(1);
            logger.info("获取涨幅榜成功,共 {} 只股票", gainers.size());
            return gainers;
        } catch (Exception e) {
            logger.error("获取涨幅榜失败", e);
            throw new RuntimeException("获取涨幅榜失败", e);
        }
    }
    
    /**
     * 获取跌幅榜
     */
    public List<Stock> getLosers() {
        try {
            List<Stock> losers = httpClient.getUpDownList(2);
            logger.info("获取跌幅榜成功,共 {} 只股票", losers.size());
            return losers;
        } catch (Exception e) {
            logger.error("获取跌幅榜失败", e);
            throw new RuntimeException("获取跌幅榜失败", e);
        }
    }
    
    /**
     * 获取IPO数据
     */
    public List<Object> getUpcomingIPOs() {
        try {
            List<Object> ipos = httpClient.getIpoList(1); // 1表示未上市
            logger.info("获取IPO数据成功,共 {} 个", ipos.size());
            return ipos;
        } catch (Exception e) {
            logger.error("获取IPO数据失败", e);
            throw new RuntimeException("获取IPO数据失败", e);
        }
    }
    
    /**
     * 关闭服务
     */
    public void close() {
        try {
            httpClient.close();
            logger.info("IndiaStockService已关闭");
        } catch (Exception e) {
            logger.error("关闭服务时发生错误", e);
        }
    }
}

5. 使用示例

java 复制代码
package com.stocktv.india.demo;

import com.stocktv.india.model.Index;
import com.stocktv.india.model.KLine;
import com.stocktv.india.model.Stock;
import com.stocktv.india.service.IndiaStockService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * 印度股票数据使用示例
 */
public class IndiaStockDemo {
    
    private static final Logger logger = LoggerFactory.getLogger(IndiaStockDemo.class);
    
    public static void main(String[] args) {
        // 替换为您的实际 API Key
        String apiKey = "您的API_KEY";
        
        IndiaStockService stockService = new IndiaStockService(apiKey);
        
        try {
            logger.info("=== StockTV 印度股票数据演示程序开始 ===");
            
            // 1. 获取印度主要指数
            demonstrateIndices(stockService);
            
            // 2. 查询特定股票
            demonstrateStockQuery(stockService);
            
            // 3. 获取Nifty 50成分股示例
            demonstrateNifty50(stockService);
            
            // 4. 获取K线数据
            demonstrateKLineData(stockService);
            
            // 5. 获取排行榜
            demonstrateRankings(stockService);
            
            logger.info("=== 演示程序执行完成 ===");
            
        } catch (Exception e) {
            logger.error("演示程序执行失败", e);
        } finally {
            // 关闭服务
            stockService.close();
        }
    }
    
    /**
     * 演示指数数据获取
     */
    private static void demonstrateIndices(IndiaStockService stockService) {
        logger.info("\n1. 印度主要指数");
        List<Index> indices = stockService.getMajorIndices();
        
        indices.forEach(index -> {
            String trend = index.getChange().doubleValue() >= 0 ? "📈" : "📉";
            logger.info("{} {}: {}{} ({}{}%)", 
                trend, index.getName(), index.getLastPrice(),
                index.getChange().doubleValue() >= 0 ? "↑" : "↓",
                index.getChange().doubleValue() >= 0 ? "+" : "",
                index.getChangePercent());
        });
    }
    
    /**
     * 演示股票查询
     */
    private static void demonstrateStockQuery(IndiaStockService stockService) {
        logger.info("\n2. 查询特定股票");
        
        // 查询Reliance Industries
        Stock reliance = stockService.getStockBySymbol("RELIANCE");
        printStockInfo(reliance, "Reliance Industries");
        
        // 查询TCS
        Stock tcs = stockService.getStockBySymbol("TCS");
        printStockInfo(tcs, "Tata Consultancy Services");
    }
    
    /**
     * 演示Nifty 50成分股
     */
    private static void demonstrateNifty50(IndiaStockService stockService) {
        logger.info("\n3. Nifty 50成分股(示例)");
        List<Stock> niftyStocks = stockService.getNifty50Stocks();
        
        // 显示前10只股票
        niftyStocks.stream().limit(10).forEach(stock -> {
            String trend = stock.getChangePercent().doubleValue() >= 0 ? "🟢" : "🔴";
            logger.info("{} {}: ₹{} ({}{}%) - {}", 
                trend, stock.getSymbol(), stock.getLastPrice(),
                stock.getChangePercent().doubleValue() >= 0 ? "+" : "",
                stock.getChangePercent(), stock.getName());
        });
    }
    
    /**
     * 演示K线数据获取
     */
    private static void demonstrateKLineData(IndiaStockService stockService) {
        logger.info("\n4. K线数据示例");
        
        Stock reliance = stockService.getStockBySymbol("RELIANCE");
        if (reliance != null) {
            List<KLine> kLines = stockService.getStockKLine(reliance.getId(), "P1D");
            
            logger.info("Reliance Industries 近期日K线数据:");
            kLines.stream().limit(5).forEach(kLine -> {
                logger.info("时间: {}, 开: ₹{}, 高: ₹{}, 低: ₹{}, 收: ₹{}, 成交量: {}", 
                    kLine.getFormattedTime(), kLine.getOpen(), 
                    kLine.getHigh(), kLine.getLow(), kLine.getClose(),
                    kLine.getVolume());
            });
        }
    }
    
    /**
     * 演示排行榜功能
     */
    private static void demonstrateRankings(IndiaStockService stockService) {
        logger.info("\n5. 市场排行榜");
        
        // 获取涨幅榜
        List<Stock> gainers = stockService.getGainers();
        logger.info("📈 今日涨幅榜(前5):");
        gainers.stream().limit(5).forEach(stock -> {
            logger.info("   {}: ₹{} (+{}%) - {}", 
                stock.getSymbol(), stock.getLastPrice(), 
                stock.getChangePercent(), stock.getName());
        });
        
        // 获取跌幅榜
        List<Stock> losers = stockService.getLosers();
        logger.info("📉 今日跌幅榜(前5):");
        losers.stream().limit(5).forEach(stock -> {
            logger.info("   {}: ₹{} ({}%) - {}", 
                stock.getSymbol(), stock.getLastPrice(), 
                stock.getChangePercent(), stock.getName());
        });
    }
    
    /**
     * 打印股票信息
     */
    private static void printStockInfo(Stock stock, String description) {
        if (stock != null) {
            String status = stock.getIsOpen() ? "🟢 交易中" : "🔴 已收盘";
            String trend = stock.getChangePercent().doubleValue() >= 0 ? "📈" : "📉";
            
            logger.info("{} {} - {}", trend, description, status);
            logger.info("   代码: {} | 价格: ₹{}", stock.getSymbol(), stock.getLastPrice());
            logger.info("   涨跌: ₹{} ({}{}%)", stock.getChange(), 
                stock.getChangePercent().doubleValue() >= 0 ? "+" : "",
                stock.getChangePercent());
            logger.info("   最高: ₹{} | 最低: ₹{} | 成交量: {}", 
                stock.getHigh(), stock.getLow(), stock.getVolume());
            
            if (stock.getTechnicalDay() != null) {
                logger.info("   技术指标: {}", getTechnicalIndicatorName(stock.getTechnicalDay()));
            }
        }
    }
    
    /**
     * 获取技术指标中文名称
     */
    private static String getTechnicalIndicatorName(String indicator) {
        switch (indicator) {
            case "strong_buy": return "强烈买入";
            case "buy": return "买入";
            case "neutral": return "中性";
            case "sell": return "卖出";
            case "strong_sell": return "强烈卖出";
            default: return indicator;
        }
    }
}

🎯 高级功能

自定义股票监控器

java 复制代码
package com.stocktv.india.advanced;

import com.stocktv.india.model.Stock;
import com.stocktv.india.service.IndiaStockService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * 股票价格监控器
 */
public class StockPriceMonitor {
    
    private static final Logger logger = LoggerFactory.getLogger(StockPriceMonitor.class);
    
    private final IndiaStockService stockService;
    private final ScheduledExecutorService scheduler;
    private final Map<String, Stock> lastPrices;
    private final Set<String> monitoredSymbols;
    
    public StockPriceMonitor(String apiKey) {
        this.stockService = new IndiaStockService(apiKey);
        this.scheduler = Executors.newScheduledThreadPool(1);
        this.lastPrices = new HashMap<>();
        this.monitoredSymbols = new HashSet<>();
    }
    
    /**
     * 添加监控股票
     */
    public void addStock(String symbol) {
        monitoredSymbols.add(symbol);
        logger.info("添加监控股票: {}", symbol);
    }
    
    /**
     * 开始监控
     */
    public void startMonitoring() {
        logger.info("开始股票价格监控...");
        scheduler.scheduleAtFixedRate(this::checkPrices, 0, 60, TimeUnit.SECONDS);
    }
    
    /**
     * 停止监控
     */
    public void stopMonitoring() {
        scheduler.shutdown();
        stockService.close();
        logger.info("股票价格监控已停止");
    }
    
    /**
     * 检查价格变化
     */
    private void checkPrices() {
        try {
            List<Stock> currentStocks = stockService.getStocksBySymbols(
                new ArrayList<>(monitoredSymbols));
            
            for (Stock currentStock : currentStocks) {
                String symbol = currentStock.getSymbol();
                Stock lastStock = lastPrices.get(symbol);
                
                if (lastStock != null) {
                    // 检查价格变化
                    double priceChange = currentStock.getLastPrice().subtract(lastStock.getLastPrice()).doubleValue();
                    double percentChange = currentStock.getChangePercent().doubleValue();
                    
                    // 价格预警逻辑
                    if (Math.abs(percentChange) > 2.0) {
                        logger.warn("🚨 股票 {} 价格波动超过2%: {}%", 
                            symbol, percentChange);
                    }
                    
                    // 技术指标变化
                    if (!Objects.equals(currentStock.getTechnicalDay(), lastStock.getTechnicalDay())) {
                        logger.info("📊 股票 {} 技术指标变化: {} → {}", 
                            symbol, lastStock.getTechnicalDay(), currentStock.getTechnicalDay());
                    }
                }
                
                // 更新最后价格
                lastPrices.put(symbol, currentStock);
            }
            
        } catch (Exception e) {
            logger.error("价格监控执行失败", e);
        }
    }
    
    /**
     * 获取监控报告
     */
    public void printMonitoringReport() {
        logger.info("=== 股票监控报告 ===");
        lastPrices.values().forEach(stock -> {
            String trend = stock.getChangePercent().doubleValue() >= 0 ? "🟢" : "🔴";
            logger.info("{} {}: ₹{} ({}{}%)", 
                trend, stock.getSymbol(), stock.getLastPrice(),
                stock.getChangePercent().doubleValue() >= 0 ? "+" : "",
                stock.getChangePercent());
        });
    }
}

⚠️ 注意事项

1. 错误处理最佳实践

java 复制代码
/**
 * 自定义异常类
 */
class StockTVException extends RuntimeException {
    public StockTVException(String message) {
        super(message);
    }
    
    public StockTVException(String message, Throwable cause) {
        super(message, cause);
    }
}

/**
 * 重试机制
 */
class RetryableStockService {
    private static final int MAX_RETRIES = 3;
    private static final long RETRY_DELAY_MS = 1000;
    
    public Stock getStockWithRetry(String symbol) {
        int retries = 0;
        while (retries < MAX_RETRIES) {
            try {
                IndiaStockService service = new IndiaStockService("API_KEY");
                return service.getStockBySymbol(symbol);
            } catch (Exception e) {
                retries++;
                if (retries == MAX_RETRIES) {
                    throw new StockTVException("获取股票数据失败,已达到最大重试次数", e);
                }
                try {
                    Thread.sleep(RETRY_DELAY_MS);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new StockTVException("重试过程被中断", ie);
                }
            }
        }
        throw new StockTVException("未知错误");
    }
}

2. 性能优化建议

java 复制代码
/**
 * 数据缓存示例
 */
class StockDataCache {
    private final Map<String, Stock> stockCache = new ConcurrentHashMap<>();
    private final Map<Long, List<KLine>> klineCache = new ConcurrentHashMap<>();
    private final long cacheTimeoutMs = 5 * 60 * 1000; // 5分钟缓存
    
    public Stock getCachedStock(String symbol, IndiaStockService service) {
        Stock cached = stockCache.get(symbol);
        if (cached != null && 
            System.currentTimeMillis() - cached.getTimestamp() * 1000 < cacheTimeoutMs) {
            return cached;
        }
        
        Stock fresh = service.getStockBySymbol(symbol);
        stockCache.put(symbol, fresh);
        return fresh;
    }
}

📞 技术支持

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

  1. 查看日志: 启用DEBUG级别日志查看详细请求信息
  2. 检查网络 : 确保可以正常访问 api.stocktv.top
  3. 验证API Key: 确认API Key有效且具有相应权限
  4. 联系支持: 通过官方渠道获取技术支持
相关推荐
恋猫de小郭3 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
牛奔1 小时前
Go 如何避免频繁抢占?
开发语言·后端·golang
想用offer打牌6 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
KYGALYX7 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法8 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate