Skip to main content
Glama

MCP Stock Assistant

stock-test.cjs10.8 kB
#!/usr/bin/env node // 股票API测试工具 - CommonJS版本,可以直接运行 // 测试 get-stock-history 和 get-market-index 两个接口 // API endpoints const SEARCH_API = "http://searchapi.eastmoney.com/api/suggest/get"; const QUOTE_API = "http://push2.eastmoney.com/api/qt/stock/get"; const HISTORY_API = "http://push2his.eastmoney.com/api/qt/stock/kline/get"; const INDEX_API = "http://push2.eastmoney.com/api/qt/ulist.np/get"; // Common headers const USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"; const COMMON_HEADERS = { "User-Agent": USER_AGENT, "Referer": "http://www.eastmoney.com/", "Accept": "*/*", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", }; // 测试接口连通性 async function testApiConnectivity() { console.log("🔍 测试API接口连通性...\n"); const apis = [ { name: "股票历史数据接口", url: HISTORY_API }, { name: "大盘指数接口", url: INDEX_API }, { name: "股票搜索接口", url: SEARCH_API, params: "?input=茅台&type=14&token=D43BF722C8E33BDC906FB84D85E326E8&count=5" }, { name: "股票行情接口", url: QUOTE_API } ]; for (const api of apis) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const testUrl = api.params ? api.url + api.params : api.url; const response = await fetch(testUrl, { method: 'GET', headers: COMMON_HEADERS, signal: controller.signal }); clearTimeout(timeoutId); if (response.ok) { console.log(`✅ ${api.name}: 连通正常 (状态码: ${response.status})`); } else { console.log(`⚠️ ${api.name}: 响应异常 (状态码: ${response.status})`); } } catch (error) { if (error.name === 'AbortError') { console.log(`❌ ${api.name}: 连接超时 (5秒)`); } else { console.log(`❌ ${api.name}: 连接失败 - ${error.message}`); } } } console.log(""); } // 搜索股票 async function searchStock(keyword) { try { const params = new URLSearchParams({ input: keyword, type: "14", token: "D43BF722C8E33BDC906FB84D85E326E8", count: "5", }); const response = await fetch(`${SEARCH_API}?${params}`, { headers: COMMON_HEADERS, }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); if (data.QuotationCodeTable.Status === 0 && data.QuotationCodeTable.Data) { for (const item of data.QuotationCodeTable.Data) { if (item.SecurityTypeName === "沪A" || item.SecurityTypeName === "深A") { return [item.Code, item.Name]; } } } return null; } catch (error) { console.error("搜索股票失败:", error); return null; } } // 获取股票市场代码 function getStockMarket(code) { if (code.match(/^(000|002|300|301)/)) { return [0, `0.${code}`]; } else if (code.match(/^(600|601|603|605|688)/)) { return [1, `1.${code}`]; } throw new Error(`不支持的股票代码格式:${code}`); } // 获取股票历史数据 (对应 get-stock-history) async function getStockHistory(stockInput, period = "101", days = 30) { try { let stockCode = stockInput; // 如果输入不是6位数字,尝试按名称搜索 if (!stockInput.match(/^\d{6}$/)) { const searchResult = await searchStock(stockInput); if (!searchResult) { console.error(`未找到股票:${stockInput}`); return null; } [stockCode] = searchResult; console.log(`找到股票:${searchResult[1]}(${searchResult[0]})`); } // 获取市场代码和完整代码 const [market, fullCode] = getStockMarket(stockCode); // 获取历史数据 const params = new URLSearchParams({ secid: fullCode, klt: period, // 101=日K, 102=周K, 103=月K fqt: "1", // 前复权 lmt: days.toString(), end: "20500101", iscca: "1", fields1: "f1,f2,f3,f4,f5,f6", fields2: "f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61", }); console.log(`📊 正在获取股票历史数据: ${stockCode} (${fullCode})`); const response = await fetch(`${HISTORY_API}?${params}`, { headers: COMMON_HEADERS, }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); if (data.rc !== 0 || !data.data || !data.data.klines) { throw new Error(data.msg || "获取历史数据失败"); } const historyData = data.data.klines.map((kline) => { const parts = kline.split(","); return { date: parts[0], open: parseFloat(parts[1]), close: parseFloat(parts[2]), high: parseFloat(parts[3]), low: parseFloat(parts[4]), volume: parseInt(parts[5]) / 100, // 转换为手 amount: parseFloat(parts[6]) / 10000, // 转换为万元 changePercent: parseFloat(parts[8]), }; }); return { stockName: data.data.name, stockCode: data.data.code, data: historyData.reverse() // 最新的在前面 }; } catch (error) { console.error("获取股票历史数据失败:", error); return null; } } // 获取大盘指数 (对应 get-market-index) async function getMarketIndex() { try { const params = new URLSearchParams({ fltt: "2", invt: "2", fields: "f1,f2,f3,f4,f12,f13,f14", secids: "1.000001,0.399001", // 上证指数和深证成指 }); console.log("📈 正在获取大盘指数数据..."); const response = await fetch(`${INDEX_API}?${params}`, { headers: COMMON_HEADERS, }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); if (data.rc !== 0 || !data.data || !data.data.diff) { throw new Error(data.msg || "获取指数数据失败"); } const indexData = data.data.diff.map((item) => ({ code: item.f12, name: item.f14, price: item.f2, changePercent: item.f3, change: item.f4, volume: 0, // 新接口不返回成交量 amount: 0, // 新接口不返回成交额 time: new Date().toLocaleTimeString(), })); return indexData; } catch (error) { console.error("获取大盘指数失败:", error); return null; } } // 格式化历史数据 function formatHistoryData(result) { if (!result || !result.data) { return "无法获取历史数据"; } const { stockName, stockCode, data } = result; const formatPrice = (price) => price.toFixed(2); const formatVolume = (volume) => volume.toFixed(2); let output = `📈 ${stockName}(${stockCode}) 历史数据 (最近${data.length}个交易日)\n\n`; // 显示最近10天的数据 const recentData = data.slice(0, Math.min(10, data.length)); output += "日期 开盘 收盘 最高 最低 成交量(手) 涨跌幅\n"; output += "─".repeat(65) + "\n"; for (const point of recentData) { const changeColor = point.changePercent >= 0 ? "📈" : "📉"; output += `${point.date} ${formatPrice(point.open).padStart(6)} ${formatPrice(point.close).padStart(6)} ${formatPrice(point.high).padStart(6)} ${formatPrice(point.low).padStart(6)} ${formatVolume(point.volume).padStart(8)} ${changeColor}${point.changePercent.toFixed(2)}%\n`; } // 统计信息 const prices = data.map(d => d.close); const maxPrice = Math.max(...prices); const minPrice = Math.min(...prices); const avgPrice = prices.reduce((a, b) => a + b, 0) / prices.length; const totalVolume = data.reduce((sum, d) => sum + d.volume, 0); output += `\n📊 统计信息: 最高价: ${formatPrice(maxPrice)} 最低价: ${formatPrice(minPrice)} 平均价: ${formatPrice(avgPrice)} 总成交量: ${formatVolume(totalVolume)}手 `; return output; } // 格式化指数数据 function formatIndexData(data) { if (!data || data.length === 0) { return "无法获取指数数据"; } const formatPrice = (price) => price.toFixed(2); const formatVolume = (volume) => volume.toFixed(2); let output = `📊 大盘指数信息\n\n`; output += "指数名称 当前点位 涨跌幅 涨跌点 成交量(手) 成交额(万)\n"; output += "─".repeat(70) + "\n"; for (const index of data) { const changeColor = index.changePercent >= 0 ? "📈" : "📉"; const changeSign = index.change >= 0 ? "+" : ""; output += `${index.name.padEnd(10)} ${formatPrice(index.price).padStart(8)} ${changeColor}${index.changePercent.toFixed(2)}%`.padEnd(20); output += ` ${changeSign}${formatPrice(index.change).padStart(7)} ${formatVolume(index.volume).padStart(10)} ${formatVolume(index.amount).padStart(10)}\n`; } output += `\n🕐 更新时间: ${data[0]?.time || new Date().toLocaleTimeString()}`; return output; } // 主函数 - 测试和示例 async function main() { console.log("🚀 股票API测试工具启动\n"); // 1. 测试接口连通性 await testApiConnectivity(); // 2. 测试获取大盘指数 console.log("📈 测试获取大盘指数..."); try { const indexData = await getMarketIndex(); if (indexData) { console.log(formatIndexData(indexData)); } else { console.log("❌ 获取大盘指数失败"); } } catch (error) { console.log("❌ 获取大盘指数出错:", error.message); } console.log("\n" + "=".repeat(80) + "\n"); // 3. 测试获取股票历史数据 console.log("📊 测试获取股票历史数据..."); const testStocks = ["600519", "000001", "贵州茅台"]; // 测试不同的输入格式 for (const stock of testStocks) { console.log(`\n🔍 测试股票: ${stock}`); try { const historyResult = await getStockHistory(stock, "101", 10); // 获取最近10天数据 if (historyResult) { console.log(formatHistoryData(historyResult)); } else { console.log(`❌ 获取股票 ${stock} 历史数据失败`); } } catch (error) { console.log(`❌ 获取股票 ${stock} 历史数据出错:`, error.message); } // 避免请求过于频繁 await new Promise(resolve => setTimeout(resolve, 1000)); } console.log("\n✅ 测试完成!"); } // 如果直接运行此文件,则执行测试 if (require.main === module) { main().catch(console.error); } // 导出函数供其他模块使用 module.exports = { testApiConnectivity, getStockHistory, getMarketIndex, searchStock, formatHistoryData, formatIndexData };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/qqzhangyanhua/mcp-stock'

If you have feedback or need assistance with the MCP directory API, please join our Discord server