StockTV美股实时数据对接与前端展示
下面是一个完整的StockTV美国股票实时数据对接方案,包含可直接运行的前端代码。
接口对接概述
StockTV提供全面的美国股票市场数据API,支持实时行情、历史数据、指数信息等核心功能。
主要特性
- 实时行情:毫秒级延迟的实时股价数据
- 多交易所支持:NYSE、NASDAQ、AMEX等主要交易所
- 丰富数据类型:实时价格、涨跌幅、成交量、K线数据等
- WebSocket支持:实时数据推送
前端实现代码
以下是一个简单但功能完整的股票行情展示页面,使用纯HTML/CSS/JavaScript实现:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>美股实时行情 - StockTV API对接</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a3a, #0d1b2a);
color: #e0e0e0;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 30px;
padding: 20px;
background: rgba(255, 255, 255, 0.05);
border-radius: 10px;
backdrop-filter: blur(10px);
}
h1 {
color: #4fc3f7;
margin-bottom: 10px;
font-size: 2.5rem;
}
.subtitle {
color: #90a4ae;
font-size: 1.1rem;
}
.controls {
display: flex;
justify-content: center;
gap: 15px;
margin-bottom: 30px;
flex-wrap: wrap;
}
button {
background: #1565c0;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
font-size: 1rem;
}
button:hover {
background: #1976d2;
transform: translateY(-2px);
}
button:disabled {
background: #546e7a;
cursor: not-allowed;
transform: none;
}
input {
padding: 10px 15px;
border: 1px solid #37474f;
border-radius: 5px;
background: rgba(255, 255, 255, 0.1);
color: white;
width: 200px;
font-size: 1rem;
}
input::placeholder {
color: #90a4ae;
}
.status {
text-align: center;
padding: 10px;
margin: 15px 0;
border-radius: 5px;
font-weight: bold;
}
.status.connected {
background: rgba(76, 175, 80, 0.2);
color: #4caf50;
}
.status.disconnected {
background: rgba(244, 67, 54, 0.2);
color: #f44336;
}
.data-section {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.card {
background: rgba(255, 255, 255, 0.05);
border-radius: 10px;
padding: 20px;
transition: transform 0.3s, box-shadow 0.3s;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
}
.card-header {
display: flex;
justify-content: between;
align-items: center;
margin-bottom: 15px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding-bottom: 10px;
}
.symbol {
font-size: 1.5rem;
font-weight: bold;
color: #4fc3f7;
}
.price {
font-size: 1.8rem;
font-weight: bold;
margin: 10px 0;
}
.change.positive {
color: #4caf50;
}
.change.negative {
color: #f44336;
}
.details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 15px;
}
.detail-item {
display: flex;
justify-content: space-between;
padding: 5px 0;
border-bottom: 1px dashed rgba(255, 255, 255, 0.05);
}
.detail-label {
color: #90a4ae;
}
.index-section {
background: rgba(255, 255, 255, 0.05);
border-radius: 10px;
padding: 20px;
margin-top: 30px;
}
.index-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
margin-top: 15px;
}
.index-item {
padding: 15px;
background: rgba(255, 255, 255, 0.03);
border-radius: 8px;
text-align: center;
}
.last-update {
text-align: center;
margin-top: 20px;
color: #90a4ae;
font-size: 0.9rem;
}
@media (max-width: 768px) {
.data-section {
grid-template-columns: 1fr;
}
.index-grid {
grid-template-columns: 1fr 1fr;
}
h1 {
font-size: 2rem;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>美股实时行情</h1>
<p class="subtitle">基于StockTV API的实时数据对接演示</p>
</header>
<div class="controls">
<input type="text" id="symbolInput" placeholder="输入股票代码,如:AAPL">
<button id="addStockBtn">添加股票</button>
<button id="connectBtn">连接实时数据</button>
<button id="disconnectBtn" disabled>断开连接</button>
<button id="resetBtn">重置</button>
</div>
<div id="status" class="status disconnected">未连接</div>
<div class="data-section" id="stocksContainer">
<!-- 股票卡片将通过JavaScript动态生成 -->
</div>
<div class="index-section">
<h2>美国主要指数</h2>
<div class="index-grid" id="indicesContainer">
<!-- 指数数据将通过JavaScript动态生成 -->
</div>
</div>
<div class="last-update" id="lastUpdate">
最后更新: <span id="updateTime">--</span>
</div>
</div>
<script>
// StockTV API配置
const API_CONFIG = {
baseUrl: 'https://api.stocktv.top',
// 请替换为您的实际API密钥
apiKey: 'YOUR_API_KEY_HERE',
// 默认跟踪的股票
defaultSymbols: ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA'],
// 美国主要指数
indices: [
{ symbol: 'DJI', name: '道琼斯指数' },
{ symbol: 'SPX', name: '标普500' },
{ symbol: 'IXIC', name: '纳斯达克' }
]
};
// 应用状态
let appState = {
connected: false,
stocks: [],
indices: [],
ws: null,
updateInterval: null
};
// DOM元素
const elements = {
stocksContainer: document.getElementById('stocksContainer'),
indicesContainer: document.getElementById('indicesContainer'),
status: document.getElementById('status'),
connectBtn: document.getElementById('connectBtn'),
disconnectBtn: document.getElementById('disconnectBtn'),
addStockBtn: document.getElementById('addStockBtn'),
resetBtn: document.getElementById('resetBtn'),
symbolInput: document.getElementById('symbolInput'),
updateTime: document.getElementById('updateTime')
};
// 初始化应用
function initApp() {
// 加载默认股票
API_CONFIG.defaultSymbols.forEach(symbol => {
addStock(symbol);
});
// 加载指数数据
loadIndices();
// 设置事件监听
setupEventListeners();
// 初始数据加载
refreshAllData();
}
// 设置事件监听器
function setupEventListeners() {
elements.connectBtn.addEventListener('click', connectWebSocket);
elements.disconnectBtn.addEventListener('click', disconnectWebSocket);
elements.addStockBtn.addEventListener('click', addStockFromInput);
elements.resetBtn.addEventListener('click', resetApp);
elements.symbolInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') addStockFromInput();
});
}
// 从输入框添加股票
function addStockFromInput() {
const symbol = elements.symbolInput.value.trim().toUpperCase();
if (symbol) {
addStock(symbol);
elements.symbolInput.value = '';
}
}
// 添加股票到跟踪列表
function addStock(symbol) {
if (!appState.stocks.find(stock => stock.symbol === symbol)) {
appState.stocks.push({
symbol: symbol,
data: null,
lastUpdated: null
});
// 立即获取数据
fetchStockData(symbol);
// 更新UI
renderStocks();
}
}
// 获取股票数据
async function fetchStockData(symbol) {
try {
// 使用StockTV的股票查询接口
const response = await fetch(
`${API_CONFIG.baseUrl}/stock/queryStocks?symbol=${symbol}&key=${API_CONFIG.apiKey}`
);
if (!response.ok) throw new Error('网络响应不正常');
const data = await response.json();
if (data.code === 200 && data.data && data.data.length > 0) {
const stockData = data.data[0];
updateStockData(symbol, stockData);
} else {
console.error(`获取 ${symbol} 数据失败:`, data.message);
}
} catch (error) {
console.error(`获取 ${symbol} 数据时出错:`, error);
}
}
// 更新股票数据
function updateStockData(symbol, newData) {
const stockIndex = appState.stocks.findIndex(stock => stock.symbol === symbol);
if (stockIndex !== -1) {
appState.stocks[stockIndex].data = newData;
appState.stocks[stockIndex].lastUpdated = new Date();
renderStocks();
updateTimestamp();
}
}
// 加载指数数据
async function loadIndices() {
try {
// 使用StockTV的指数接口
const response = await fetch(
`${API_CONFIG.baseUrl}/stock/indices?countryId=1&key=${API_CONFIG.apiKey}`
);
if (!response.ok) throw new Error('网络响应不正常');
const data = await response.json();
if (data.code === 200 && data.data) {
appState.indices = data.data;
renderIndices();
}
} catch (error) {
console.error('获取指数数据时出错:', error);
}
}
// 渲染股票卡片
function renderStocks() {
elements.stocksContainer.innerHTML = '';
appState.stocks.forEach(stock => {
if (!stock.data) return;
const change = stock.data.last - stock.data.prevClose;
const changePercent = (change / stock.data.prevClose) * 100;
const isPositive = change >= 0;
const stockCard = document.createElement('div');
stockCard.className = 'card';
stockCard.innerHTML = `
<div class="card-header">
<div class="symbol">${stock.symbol}</div>
<div class="name">${stock.data.name || 'N/A'}</div>
</div>
<div class="price">$${stock.data.last.toFixed(2)}</div>
<div class="change ${isPositive ? 'positive' : 'negative'}">
${isPositive ? '+' : ''}${change.toFixed(2)} (${isPositive ? '+' : ''}${changePercent.toFixed(2)}%)
</div>
<div class="details">
<div class="detail-item">
<span class="detail-label">开盘:</span>
<span>$${stock.data.open?.toFixed(2) || 'N/A'}</span>
</div>
<div class="detail-item">
<span class="detail-label">最高:</span>
<span>$${stock.data.high?.toFixed(2) || 'N/A'}</span>
</div>
<div class="detail-item">
<span class="detail-label">最低:</span>
<span>$${stock.data.low?.toFixed(2) || 'N/A'}</span>
</div>
<div class="detail-item">
<span class="detail-label">成交量:</span>
<span>${formatNumber(stock.data.volume)}</span>
</div>
</div>
`;
elements.stocksContainer.appendChild(stockCard);
});
}
// 渲染指数数据
function renderIndices() {
elements.indicesContainer.innerHTML = '';
appState.indices.forEach(index => {
const change = index.change || index.last - index.prevClose;
const changePercent = index.changePct || (change / index.prevClose) * 100;
const isPositive = change >= 0;
const indexItem = document.createElement('div');
indexItem.className = 'index-item';
indexItem.innerHTML = `
<div style="font-weight: bold; margin-bottom: 5px;">${index.symbol}</div>
<div style="font-size: 1.2rem; margin-bottom: 5px;">${index.last.toFixed(2)}</div>
<div class="${isPositive ? 'positive' : 'negative'}" style="font-size: 0.9rem;">
${isPositive ? '+' : ''}${change.toFixed(2)} (${isPositive ? '+' : ''}${changePercent.toFixed(2)}%)
</div>
<div style="font-size: 0.8rem; color: #90a4ae; margin-top: 5px;">${index.name}</div>
`;
elements.indicesContainer.appendChild(indexItem);
});
}
// 连接WebSocket实时数据
function connectWebSocket() {
if (appState.connected) return;
try {
// 创建WebSocket连接
appState.ws = new WebSocket(`wss://ws-api.stocktv.top/connect?key=${API_CONFIG.apiKey}`);
appState.ws.onopen = () => {
appState.connected = true;
updateStatus('已连接实时数据', true);
// 订阅当前跟踪的股票
const symbols = appState.stocks.map(stock => stock.symbol);
if (symbols.length > 0) {
const subscribeMsg = {
action: 'subscribe',
symbols: symbols
};
appState.ws.send(JSON.stringify(subscribeMsg));
}
};
appState.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
// 处理实时股票数据
if (data.symbol && data.price !== undefined) {
updateStockData(data.symbol, {
last: data.price,
change: data.change,
changePercent: data.changePercent,
// 其他字段可以根据需要补充
});
}
} catch (error) {
console.error('解析WebSocket消息错误:', error);
}
};
appState.ws.onclose = () => {
appState.connected = false;
updateStatus('连接已断开', false);
};
appState.ws.onerror = (error) => {
console.error('WebSocket错误:', error);
updateStatus('连接错误', false);
};
} catch (error) {
console.error('创建WebSocket连接失败:', error);
updateStatus('连接失败', false);
}
}
// 断开WebSocket连接
function disconnectWebSocket() {
if (appState.ws) {
appState.ws.close();
appState.ws = null;
}
appState.connected = false;
updateStatus('未连接', false);
}
// 更新状态显示
function updateStatus(message, isConnected) {
elements.status.textContent = message;
elements.status.className = `status ${isConnected ? 'connected' : 'disconnected'}`;
elements.connectBtn.disabled = isConnected;
elements.disconnectBtn.disabled = !isConnected;
}
// 刷新所有数据
function refreshAllData() {
appState.stocks.forEach(stock => {
fetchStockData(stock.symbol);
});
loadIndices();
}
// 更新最后刷新时间
function updateTimestamp() {
const now = new Date();
elements.updateTime.textContent = now.toLocaleTimeString();
}
// 格式化大数字
function formatNumber(num) {
if (num >= 1000000) {
return (num / 1000000).toFixed(2) + 'M';
} else if (num >= 1000) {
return (num / 1000).toFixed(2) + 'K';
}
return num.toString();
}
// 重置应用
function resetApp() {
disconnectWebSocket();
appState.stocks = [];
API_CONFIG.defaultSymbols.forEach(symbol => {
addStock(symbol);
});
renderStocks();
}
// 页面加载完成后初始化应用
document.addEventListener('DOMContentLoaded', initApp);
// 设置定时刷新(每30秒刷新一次非实时数据)
setInterval(() => {
if (!appState.connected) {
refreshAllData();
}
}, 30000);
</script>
</body>
</html>
使用说明
1. 获取API密钥
在使用此代码前,您需要从StockTV获取有效的API密钥。将代码中的YOUR_API_KEY_HERE替换为您的实际API密钥。
2. 功能特点
- 实时数据展示:显示股票实时价格、涨跌幅、成交量等信息
- WebSocket支持:可连接实时数据流,获取毫秒级更新
- 多股票跟踪:支持同时查看多只股票行情
- 主要指数显示:展示道琼斯、标普500、纳斯达克等主要指数
- 响应式设计:适配不同屏幕尺寸
3. 操作方式
- 在输入框中输入股票代码(如AAPL、MSFT)并点击"添加股票"按钮
- 点击"连接实时数据"启用WebSocket实时更新
- 点击"断开连接"切换回定期轮询模式
- 点击"重置"恢复默认股票列表
4. 数据说明
此演示使用了StockTV提供的以下API接口:
/stock/queryStocks- 获取股票实时数据/stock/indices- 获取指数数据- WebSocket连接 - 实时数据推送
注意事项
- API限制:请注意StockTV的API调用频率限制,基础版为100次/分钟
- 数据延迟:实时行情延迟≤200ms,历史数据无延迟
- 错误处理:代码包含了基本的错误处理,但实际应用中可能需要更完善的异常处理
- API密钥安全:在实际生产环境中,不应将API密钥直接暴露在前端代码中
将上述代码保存为HTML文件并在浏览器中打开即可运行这个美股实时行情展示应用。记得替换为您自己的StockTV API密钥以获得完整功能。

被折叠的 条评论
为什么被折叠?



