Java 對接印度股票數(shù)據(jù)源實現(xiàn) http+ws實時數(shù)據(jù)
以下是使用 Java 對接 StockTV 印度股票數(shù)據(jù)源的完整實現(xiàn),包括實時行情、K線數(shù)據(jù)、公司信息等功能。
1. 項目依賴
首先在 pom.xml 中添加必要的依賴:
<dependencies>
<!-- HTTP 客戶端 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<!-- JSON 處理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- WebSocket 客戶端 -->
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.3</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
</dependency>
</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 基礎(chǔ)配置
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 - 請?zhí)鎿Q為實際的 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. 數(shù)據(jù)模型類
股票基本信息
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;
// 技術(shù)指標(biāo)
@JsonProperty("technicalDay")
private String technicalDay;
@JsonProperty("technicalHour")
private String technicalHour;
@JsonProperty("technicalWeek")
private String technicalWeek;
@JsonProperty("technicalMonth")
private String technicalMonth;
}
K線數(shù)據(jù)
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;
}
指數(shù)數(shù)據(jù)
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 響應(yīng)包裝類
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<ApiResponse<StockListResponse>>() {});
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<ApiResponse<List<Stock>>>() {});
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<ApiResponse<List<Stock>>>() {});
return response.getData();
}
/**
* 獲取印度主要指數(shù)
*/
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<ApiResponse<List<Index>>>() {});
return response.getData();
}
/**
* 獲取K線數(shù)據(jù)
*/
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<ApiResponse<List<KLine>>>() {});
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<ApiResponse<List<Stock>>>() {});
return response.getData();
}
/**
* 通用GET請求執(zhí)行方法
*/
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 實時數(shù)據(jù)客戶端
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服務(wù)器
*/
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連接已關(guān)閉: 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);
// 解析實時行情數(shù)據(jù)
if (jsonNode.has("pid")) {
RealTimeData realTimeData = objectMapper.treeToValue(jsonNode, RealTimeData.class);
onRealTimeData(realTimeData);
} else {
logger.info("收到消息: {}", message);
}
}
/**
* 處理實時行情數(shù)據(jù) - 需要子類重寫
*/
protected void onRealTimeData(RealTimeData data) {
logger.info("實時行情: {} - 最新價: {}, 漲跌幅: {}%",
data.getPid(), data.getLastNumeric(), data.getPcp());
}
/**
* 發(fā)送消息
*/
public void sendMessage(String message) {
if (webSocketClient != null && webSocketClient.isOpen()) {
webSocketClient.send(message);
}
}
/**
* 關(guān)閉連接
*/
public void close() {
if (webSocketClient != null) {
webSocketClient.close();
}
}
/**
* 實時數(shù)據(jù)模型
*/
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. 服務(wù)類
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只股票(實際應(yīng)該根據(jù)Nifty 50成分股列表查詢)
return httpClient.getIndiaStocks(50, 1);
} catch (Exception e) {
logger.error("獲取Nifty 50成分股失敗", e);
throw new RuntimeException(e);
}
}
/**
* 獲取印度主要指數(shù)
*/
public List<Index> getMajorIndices() {
try {
return httpClient.getIndiaIndices();
} catch (Exception e) {
logger.error("獲取印度指數(shù)失敗", 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線數(shù)據(jù)
*/
public List<KLine> getStockKLine(Long pid, String interval) {
try {
return httpClient.getKLineData(pid, interval);
} catch (Exception e) {
logger.error("獲取K線數(shù)據(jù)失敗: 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);
}
}
/**
* 啟動實時數(shù)據(jù)監(jiān)聽
*/
public void startRealTimeMonitoring() {
try {
webSocketClient.connect();
logger.info("實時數(shù)據(jù)監(jiān)聽已啟動");
} catch (Exception e) {
logger.error("啟動實時數(shù)據(jù)監(jiān)聽失敗", e);
throw new RuntimeException(e);
}
}
/**
* 停止實時數(shù)據(jù)監(jiān)聽
*/
public void stopRealTimeMonitoring() {
webSocketClient.close();
logger.info("實時數(shù)據(jù)監(jiān)聽已停止");
}
}
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. 獲取印度主要指數(shù)
logger.info("=== 印度主要指數(shù) ===");
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線數(shù)據(jù)
logger.info("\n=== RELIANCE日K線數(shù)據(jù) ===");
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. 啟動實時數(shù)據(jù)監(jiān)聽(可選)
logger.info("\n=== 啟動實時數(shù)據(jù)監(jiān)聽 ===");
// stockService.startRealTimeMonitoring();
// 保持程序運行一段時間
Thread.sleep(30000);
// 停止實時數(shù)據(jù)監(jiān)聽
// stockService.stopRealTimeMonitoring();
} catch (Exception e) {
logger.error("演示程序執(zhí)行失敗", e);
}
}
}
8. 自定義實時數(shù)據(jù)處理
如果需要自定義實時數(shù)據(jù)處理邏輯,可以繼承 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) {
// 自定義實時數(shù)據(jù)處理邏輯
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. 配置說明
時間間隔參數(shù)
PT5M- 5分鐘K線PT15M- 15分鐘K線PT1H- 1小時K線P1D- 日K線P1W- 周K線P1M- 月K線
排行榜類型
1- 漲幅榜2- 跌幅榜3- 漲停榜4- 跌停榜
10. 注意事項
- API Key管理: 建議從配置文件或環(huán)境變量讀取API Key
- 錯誤處理: 所有API調(diào)用都需要適當(dāng)?shù)漠惓L幚?/li>
- 頻率限制: 注意API的調(diào)用頻率限制
- 連接管理: WebSocket連接需要處理重連邏輯
- 數(shù)據(jù)緩存: 對于不經(jīng)常變化的數(shù)據(jù)可以實施緩存
這個完整的Java實現(xiàn)提供了對接印度股票數(shù)據(jù)源的所有核心功能,可以根據(jù)具體需求進(jìn)行擴(kuò)展和優(yōu)化。

浙公網(wǎng)安備 33010602011771號