以下是使用 Java 对接 StockTV 印度股票数据源的完整实现,包括实时行情、K线数据、公司信息等功能。
1. 项目依赖
首先在 pom.xml 中添加必要的依赖:- <dependencies>
-
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- httpclient</artifactId>
- <version>4.5.14</version>
- </dependency>
-
-
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- jackson-databind</artifactId>
- <version>2.15.2</version>
- </dependency>
-
-
- <dependency>
- <groupId>org.java-websocket</groupId>
- Java-WebSocket</artifactId>
- <version>1.5.3</version>
- </dependency>
-
-
- <dependency>
- <groupId>org.slf4j</groupId>
- slf4j-api</artifactId>
- <version>2.0.7</version>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- slf4j-simple</artifactId>
- <version>2.0.7</version>
- </dependency>
- </dependencies>
复制代码 2. 配置类
- 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;
- 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 - 请替换为实际的 API Key
- private String apiKey;
-
- // HTTP 客户端
- private final CloseableHttpClient httpClient;
- private final ObjectMapper objectMapper;
-
- public StockTVConfig(String apiKey) {
- this.apiKey = apiKey;
- this.httpClient = HttpClients.createDefault();
- this.objectMapper = new ObjectMapper();
- }
-
- // Getters
- public String getApiKey() { return apiKey; }
- public CloseableHttpClient getHttpClient() { return httpClient; }
- public ObjectMapper getObjectMapper() { return objectMapper; }
- }
复制代码 3. 数据模型类
股票基本信息
- package com.stocktv.india.model;
- import com.fasterxml.jackson.annotation.JsonProperty;
- import lombok.Data;
- import java.math.BigDecimal;
- @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;
- }
复制代码 K线数据
- package com.stocktv.india.model;
- import com.fasterxml.jackson.annotation.JsonProperty;
- import lombok.Data;
- import java.math.BigDecimal;
- @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;
- }
复制代码 指数数据
- package com.stocktv.india.model;
- import com.fasterxml.jackson.annotation.JsonProperty;
- import lombok.Data;
- import java.math.BigDecimal;
- @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;
- }
复制代码 API 响应包装类
- package com.stocktv.india.model;
- import com.fasterxml.jackson.annotation.JsonProperty;
- import lombok.Data;
- import java.util.List;
- @Data
- public class ApiResponse<T> {
- @JsonProperty("code")
- private Integer code;
-
- @JsonProperty("message")
- private String message;
-
- @JsonProperty("data")
- private T data;
- }
- @Data
- class StockListResponse {
- @JsonProperty("records")
- private List<Stock> records;
-
- @JsonProperty("total")
- private Integer total;
-
- @JsonProperty("current")
- private Integer current;
-
- @JsonProperty("pages")
- private Integer pages;
- }
复制代码 4. HTTP API 客户端
- 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;
- 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();
- }
-
- /**
- * 获取印度股票列表
- */
- public List<Stock> getIndiaStocks(Integer pageSize, Integer page) throws IOException, URISyntaxException {
- URI uri = new URIBuilder(config.BASE_URL + "/stock/stocks")
- .addParameter("countryId", String.valueOf(config.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>() {});
-
- if (response.getCode() == 200) {
- return response.getData().getRecords();
- } else {
- throw new RuntimeException("API Error: " + response.getMessage());
- }
- }
-
- /**
- * 查询单个股票
- */
- 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>>() {});
-
- return response.getData();
- }
-
- /**
- * 批量查询多个股票
- */
- public List<Stock> getStocksByPids(List<Long> pids) throws IOException, URISyntaxException {
- 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>>() {});
-
- return response.getData();
- }
-
- /**
- * 获取印度主要指数
- */
- public List<Index> getIndiaIndices() throws IOException, URISyntaxException {
- URI uri = new URIBuilder(config.BASE_URL + "/stock/indices")
- .addParameter("countryId", String.valueOf(config.INDIA_COUNTRY_ID))
- .addParameter("key", config.getApiKey())
- .build();
-
- ApiResponse<List<Index>> response = executeGetRequest(uri,
- new TypeReference>>() {});
-
- return response.getData();
- }
-
- /**
- * 获取K线数据
- */
- public List<KLine> getKLineData(Long pid, String interval) throws IOException, URISyntaxException {
- 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>>() {});
-
- return response.getData();
- }
-
- /**
- * 获取涨跌排行榜
- */
- public List<Stock> getUpDownList(Integer type) throws IOException, URISyntaxException {
- URI uri = new URIBuilder(config.BASE_URL + "/stock/updownList")
- .addParameter("countryId", String.valueOf(config.INDIA_COUNTRY_ID))
- .addParameter("type", String.valueOf(type))
- .addParameter("key", config.getApiKey())
- .build();
-
- ApiResponse<List<Stock>> response = executeGetRequest(uri,
- new TypeReference>>() {});
-
- return response.getData();
- }
-
- /**
- * 通用GET请求执行方法
- */
- private <T> T executeGetRequest(URI uri, TypeReference<T> typeReference) throws IOException {
- HttpGet request = new HttpGet(uri);
-
- try (CloseableHttpResponse response = httpClient.execute(request)) {
- String responseBody = EntityUtils.toString(response.getEntity());
- logger.debug("API Response: {}", responseBody);
-
- return config.getObjectMapper().readValue(responseBody, typeReference);
- }
- }
- }
复制代码 5. WebSocket 实时数据客户端
- package com.stocktv.india.client;
- import com.fasterxml.jackson.databind.JsonNode;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.stocktv.india.config.StockTVConfig;
- import org.java_websocket.client.WebSocketClient;
- import org.java_websocket.handshake.ServerHandshake;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import java.net.URI;
- import java.util.concurrent.CountDownLatch;
- import java.util.concurrent.TimeUnit;
- public class StockTVWebSocketClient {
-
- private static final Logger logger = LoggerFactory.getLogger(StockTVWebSocketClient.class);
-
- private final StockTVConfig config;
- private final ObjectMapper objectMapper;
- private WebSocketClient webSocketClient;
- private CountDownLatch connectionLatch;
-
- public StockTVWebSocketClient(StockTVConfig config) {
- this.config = config;
- this.objectMapper = config.getObjectMapper();
- }
-
- /**
- * 连接WebSocket服务器
- */
- public void connect() throws Exception {
- String wsUrl = config.WS_URL + "?key=" + config.getApiKey();
- URI serverUri = URI.create(wsUrl);
-
- connectionLatch = new CountDownLatch(1);
-
- webSocketClient = new WebSocketClient(serverUri) {
- @Override
- public void onOpen(ServerHandshake handshake) {
- logger.info("WebSocket连接已建立");
- connectionLatch.countDown();
- }
-
- @Override
- public void onMessage(String message) {
- try {
- handleMessage(message);
- } catch (Exception e) {
- logger.error("处理WebSocket消息时出错", e);
- }
- }
-
- @Override
- public void onClose(int code, String reason, boolean remote) {
- logger.info("WebSocket连接已关闭: code={}, reason={}, remote={}", code, reason, remote);
- }
-
- @Override
- public void onError(Exception ex) {
- logger.error("WebSocket连接错误", ex);
- }
- };
-
- webSocketClient.connect();
-
- // 等待连接建立
- if (!connectionLatch.await(10, TimeUnit.SECONDS)) {
- throw new RuntimeException("WebSocket连接超时");
- }
- }
-
- /**
- * 处理收到的消息
- */
- private void handleMessage(String message) throws Exception {
- JsonNode jsonNode = objectMapper.readTree(message);
-
- // 解析实时行情数据
- if (jsonNode.has("pid")) {
- RealTimeData realTimeData = objectMapper.treeToValue(jsonNode, RealTimeData.class);
- onRealTimeData(realTimeData);
- } else {
- logger.info("收到消息: {}", message);
- }
- }
-
- /**
- * 处理实时行情数据 - 需要子类重写
- */
- protected void onRealTimeData(RealTimeData data) {
- logger.info("实时行情: {} - 最新价: {}, 涨跌幅: {}%",
- data.getPid(), data.getLastNumeric(), data.getPcp());
- }
-
- /**
- * 发送消息
- */
- public void sendMessage(String message) {
- if (webSocketClient != null && webSocketClient.isOpen()) {
- webSocketClient.send(message);
- }
- }
-
- /**
- * 关闭连接
- */
- public void close() {
- if (webSocketClient != null) {
- webSocketClient.close();
- }
- }
-
- /**
- * 实时数据模型
- */
- public static class RealTimeData {
- private String pid;
- private String lastNumeric;
- private String bid;
- private String ask;
- private String high;
- private String low;
- private String lastClose;
- private String pc;
- private String pcp;
- private String turnoverNumeric;
- private String time;
- private String timestamp;
- private Integer type;
-
- // Getters and Setters
- public String getPid() { return pid; }
- public void setPid(String pid) { this.pid = pid; }
- public String getLastNumeric() { return lastNumeric; }
- public void setLastNumeric(String lastNumeric) { this.lastNumeric = lastNumeric; }
- public String getBid() { return bid; }
- public void setBid(String bid) { this.bid = bid; }
- public String getAsk() { return ask; }
- public void setAsk(String ask) { this.ask = ask; }
- public String getHigh() { return high; }
- public void setHigh(String high) { this.high = high; }
- public String getLow() { return low; }
- public void setLow(String low) { this.low = low; }
- public String getLastClose() { return lastClose; }
- public void setLastClose(String lastClose) { this.lastClose = lastClose; }
- public String getPc() { return pc; }
- public void setPc(String pc) { this.pc = pc; }
- public String getPcp() { return pcp; }
- public void setPcp(String pcp) { this.pcp = pcp; }
- public String getTurnoverNumeric() { return turnoverNumeric; }
- public void setTurnoverNumeric(String turnoverNumeric) { this.turnoverNumeric = turnoverNumeric; }
- public String getTime() { return time; }
- public void setTime(String time) { this.time = time; }
- public String getTimestamp() { return timestamp; }
- public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
- public Integer getType() { return type; }
- public void setType(Integer type) { this.type = type; }
- }
- }
复制代码 6. 服务类
- package com.stocktv.india.service;
- import com.stocktv.india.client.StockTVHttpClient;
- import com.stocktv.india.client.StockTVWebSocketClient;
- 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;
- public class IndiaStockService {
-
- private static final Logger logger = LoggerFactory.getLogger(IndiaStockService.class);
-
- private final StockTVHttpClient httpClient;
- private final StockTVWebSocketClient webSocketClient;
-
- public IndiaStockService(String apiKey) {
- StockTVConfig config = new StockTVConfig(apiKey);
- this.httpClient = new StockTVHttpClient(config);
- this.webSocketClient = new StockTVWebSocketClient(config);
- }
-
- /**
- * 获取Nifty 50成分股
- */
- public List<Stock> getNifty50Stocks() {
- try {
- // 获取前50只股票(实际应该根据Nifty 50成分股列表查询)
- return httpClient.getIndiaStocks(50, 1);
- } catch (Exception e) {
- logger.error("获取Nifty 50成分股失败", e);
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 获取印度主要指数
- */
- public List<Index> getMajorIndices() {
- try {
- return httpClient.getIndiaIndices();
- } catch (Exception e) {
- logger.error("获取印度指数失败", e);
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 查询特定股票
- */
- public List<Stock> getStockBySymbol(String symbol) {
- try {
- return httpClient.queryStock(null, symbol, null);
- } catch (Exception e) {
- logger.error("查询股票失败: " + symbol, e);
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 获取股票K线数据
- */
- public List<KLine> getStockKLine(Long pid, String interval) {
- try {
- return httpClient.getKLineData(pid, interval);
- } catch (Exception e) {
- logger.error("获取K线数据失败: pid=" + pid, e);
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 获取涨幅榜
- */
- public List<Stock> getGainers() {
- try {
- return httpClient.getUpDownList(1); // 1表示涨幅榜
- } catch (Exception e) {
- logger.error("获取涨幅榜失败", e);
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 启动实时数据监听
- */
- public void startRealTimeMonitoring() {
- try {
- webSocketClient.connect();
- logger.info("实时数据监听已启动");
- } catch (Exception e) {
- logger.error("启动实时数据监听失败", e);
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 停止实时数据监听
- */
- public void stopRealTimeMonitoring() {
- webSocketClient.close();
- logger.info("实时数据监听已停止");
- }
- }
复制代码 7. 使用示例
- package com.stocktv.india.demo;
- import com.stocktv.india.service.IndiaStockService;
- import com.stocktv.india.client.StockTVWebSocketClient;
- 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;
- 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 {
- // 1. 获取印度主要指数
- logger.info("=== 印度主要指数 ===");
- List<Index> indices = stockService.getMajorIndices();
- indices.forEach(index ->
- logger.info("{}: {} ({}{}%)",
- index.getName(), index.getLastPrice(),
- index.getChange().doubleValue() > 0 ? "+" : "",
- index.getChangePercent())
- );
-
- // 2. 获取Nifty 50成分股
- logger.info("\n=== Nifty 50成分股(示例)===");
- List<Stock> niftyStocks = stockService.getNifty50Stocks();
- niftyStocks.stream().limit(10).forEach(stock ->
- logger.info("{} ({}) : {} INR, 涨跌幅: {}%",
- stock.getSymbol(), stock.getName(),
- stock.getLastPrice(), stock.getChangePercent())
- );
-
- // 3. 查询特定股票
- logger.info("\n=== 查询RELIANCE股票 ===");
- List<Stock> relianceStock = stockService.getStockBySymbol("RELIANCE");
- if (!relianceStock.isEmpty()) {
- Stock stock = relianceStock.get(0);
- logger.info("Reliance Industries: {} INR, 成交量: {}",
- stock.getLastPrice(), stock.getVolume());
- }
-
- // 4. 获取K线数据
- logger.info("\n=== RELIANCE日K线数据 ===");
- if (!relianceStock.isEmpty()) {
- Long pid = relianceStock.get(0).getId();
- List<KLine> kLines = stockService.getStockKLine(pid, "P1D");
- kLines.stream().limit(5).forEach(kLine ->
- logger.info("时间: {}, 开: {}, 高: {}, 低: {}, 收: {}",
- kLine.getTimestamp(), kLine.getOpen(),
- kLine.getHigh(), kLine.getLow(), kLine.getClose())
- );
- }
-
- // 5. 获取涨幅榜
- logger.info("\n=== 今日涨幅榜 ===");
- List<Stock> gainers = stockService.getGainers();
- gainers.stream().limit(5).forEach(stock ->
- logger.info("{}: {} INR, 涨幅: {}%",
- stock.getSymbol(), stock.getLastPrice(), stock.getChangePercent())
- );
-
- // 6. 启动实时数据监听(可选)
- logger.info("\n=== 启动实时数据监听 ===");
- // stockService.startRealTimeMonitoring();
-
- // 保持程序运行一段时间
- Thread.sleep(30000);
-
- // 停止实时数据监听
- // stockService.stopRealTimeMonitoring();
-
- } catch (Exception e) {
- logger.error("演示程序执行失败", e);
- }
- }
- }
复制代码 8. 自定义实时数据处理
如果需要自定义实时数据处理逻辑,可以继承 StockTVWebSocketClient:- package com.stocktv.india.custom;
- import com.stocktv.india.client.StockTVWebSocketClient;
- import com.stocktv.india.config.StockTVConfig;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- public class CustomWebSocketClient extends StockTVWebSocketClient {
-
- private static final Logger logger = LoggerFactory.getLogger(CustomWebSocketClient.class);
-
- public CustomWebSocketClient(StockTVConfig config) {
- super(config);
- }
-
- @Override
- protected void onRealTimeData(RealTimeData data) {
- // 自定义实时数据处理逻辑
- if ("7310".equals(data.getPid())) { // RELIANCE的PID
- logger.info("RELIANCE实时行情 - 价格: {}, 涨跌幅: {}%, 成交量: {}",
- data.getLastNumeric(), data.getPcp(), data.getTurnoverNumeric());
- }
-
- // 可以在这里添加交易逻辑、报警逻辑等
- if (Double.parseDouble(data.getPcp()) > 5.0) {
- logger.warn("股票 {} 涨幅超过5%: {}%", data.getPid(), data.getPcp());
- }
- }
- }
复制代码 9. 配置说明
时间间隔参数
- PT5M - 5分钟K线
- PT15M - 15分钟K线
- PT1H - 1小时K线
- P1D - 日K线
- P1W - 周K线
- P1M - 月K线
排行榜类型
- 1 - 涨幅榜
- 2 - 跌幅榜
- 3 - 涨停榜
- 4 - 跌停榜
10. 注意事项
- API Key管理: 建议从配置文件或环境变量读取API Key
- 错误处理: 所有API调用都需要适当的异常处理
- 频率限制: 注意API的调用频率限制
- 连接管理: WebSocket连接需要处理重连逻辑
- 数据缓存: 对于不经常变化的数据可以实施缓存
这个完整的Java实现提供了对接印度股票数据源的所有核心功能,可以根据具体需求进行扩展和优化。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |