DeepSeek印度股票数据源 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>
    
    <!-- 日志框架 -->
    <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/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. 配置类

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. 数据模型类

股票数据模型

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线数据模型

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);
    }
}

指数数据模型

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响应包装类

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客户端实现

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. 服务层封装

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. 使用示例

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;
        }
    }
}

🎯 高级功能

自定义股票监控器

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. 错误处理最佳实践

/**
 * 自定义异常类
 */
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. 性能优化建议

/**
 * 数据缓存示例
 */
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. 联系支持: 通过官方渠道获取技术支持
posted @ 2025-11-10 16:32  CryptoRzz  阅读(8)  评论(0)    收藏  举报