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;
}
}
📞 技术支持
如果在使用过程中遇到问题,可以通过以下方式获取帮助:
- 查看日志: 启用DEBUG级别日志查看详细请求信息
- 检查网络: 确保可以正常访问
api.stocktv.top - 验证API Key: 确认API Key有效且具有相应权限
- 联系支持: 通过官方渠道获取技术支持

浙公网安备 33010602011771号