Skip to main content
Glama

FinanceMCP

MIT License
19
251
  • Apple
  • Linux
moneyFlow.js15.9 kB
import { TUSHARE_CONFIG } from '../config.js'; export const moneyFlow = { name: "money_flow", description: "获取个股、大盘和板块资金流向数据,包括主力资金、超大单、大单、中单、小单的净流入净额和净占比数据", parameters: { type: "object", properties: { query_type: { type: "string", description: "查询类型:stock=个股,market=大盘,sector=板块。默认根据ts_code自动判断" }, ts_code: { type: "string", description: "股票代码或板块代码。个股如'000001.SZ',板块如'BK0447'(东财板块代码)。不填写则查询大盘资金流向" }, start_date: { type: "string", description: "起始日期,格式为YYYYMMDD,如'20240901'" }, end_date: { type: "string", description: "结束日期,格式为YYYYMMDD,如'20240930'" }, content_type: { type: "string", description: "板块资金类型,仅在查询板块时有效。可选:行业、概念、地域" }, trade_date: { type: "string", description: "单独查询某个交易日的数据,格式为YYYYMMDD。如填写则忽略start_date和end_date" } }, required: ["start_date", "end_date"] }, async run(args) { try { console.log('资金流向数据查询参数:', args); const TUSHARE_API_KEY = TUSHARE_CONFIG.API_TOKEN; const TUSHARE_API_URL = TUSHARE_CONFIG.API_URL; if (!TUSHARE_API_KEY) { throw new Error('请配置TUSHARE_TOKEN环境变量'); } // 判断查询类型 let queryType = args.query_type; if (!queryType) { // 自动判断:没有ts_code=大盘,有BK开头=板块,否则=个股 if (!args.ts_code || args.ts_code.trim() === '') { queryType = 'market'; } else if (args.ts_code.startsWith('BK')) { queryType = 'sector'; } else { queryType = 'stock'; } } let result; let targetName = ''; if (queryType === 'market') { // 查询大盘资金流向 targetName = '大盘'; result = await fetchMarketMoneyFlow(args.trade_date || args.start_date, args.trade_date || args.end_date, TUSHARE_API_KEY, TUSHARE_API_URL); } else if (queryType === 'sector') { // 查询板块资金流向 targetName = `板块${args.ts_code || ''}`; result = await fetchSectorMoneyFlow(args.ts_code, args.trade_date, args.start_date, args.end_date, args.content_type, TUSHARE_API_KEY, TUSHARE_API_URL); } else { // 查询个股资金流向 targetName = `股票${args.ts_code}`; result = await fetchStockMoneyFlow(args.ts_code, args.trade_date || args.start_date, args.trade_date || args.end_date, TUSHARE_API_KEY, TUSHARE_API_URL); } if (!result.data || result.data.length === 0) { throw new Error(`未找到${targetName}在指定时间范围内的资金流向数据`); } // 格式化输出 const formattedOutput = formatMoneyFlowData(result.data, result.fields, queryType, args.ts_code); return { content: [{ type: "text", text: formattedOutput }] }; } catch (error) { console.error('资金流向数据查询错误:', error); return { content: [{ type: "text", text: `查询资金流向数据时发生错误: ${error instanceof Error ? error.message : '未知错误'}` }] }; } } }; // 获取大盘资金流向数据 async function fetchMarketMoneyFlow(startDate, endDate, apiKey, apiUrl) { const params = { api_name: "moneyflow_mkt_dc", token: apiKey, params: { start_date: startDate, end_date: endDate }, fields: "trade_date,close_sh,pct_change_sh,close_sz,pct_change_sz,net_amount,net_amount_rate,buy_elg_amount,buy_elg_amount_rate,buy_lg_amount,buy_lg_amount_rate,buy_md_amount,buy_md_amount_rate,buy_sm_amount,buy_sm_amount_rate" }; return await callTushareAPI(params, apiUrl); } // 获取个股资金流向数据 async function fetchStockMoneyFlow(tsCode, startDate, endDate, apiKey, apiUrl) { const params = { api_name: "moneyflow_dc", token: apiKey, params: { ts_code: tsCode, start_date: startDate, end_date: endDate }, fields: "ts_code,trade_date,close,pct_change,net_amount,net_amount_rate,buy_elg_amount,buy_elg_amount_rate,buy_lg_amount,buy_lg_amount_rate,buy_md_amount,buy_md_amount_rate,buy_sm_amount,buy_sm_amount_rate" }; return await callTushareAPI(params, apiUrl); } // 获取板块资金流向数据(东财) async function fetchSectorMoneyFlow(tsCode, tradeDate, startDate, endDate, contentType, apiKey, apiUrl) { const params = { api_name: "moneyflow_ind_dc", token: apiKey, params: {}, fields: "trade_date,content_type,ts_code,name,pct_change,close,net_amount,net_amount_rate,buy_elg_amount,buy_elg_amount_rate,buy_lg_amount,buy_lg_amount_rate,buy_md_amount,buy_md_amount_rate,buy_sm_amount,buy_sm_amount_rate,rank" }; // 根据参数动态构建查询条件 if (tsCode) { params.params.ts_code = tsCode; } if (tradeDate) { params.params.trade_date = tradeDate; } else { params.params.start_date = startDate; params.params.end_date = endDate; } if (contentType) { params.params.content_type = contentType; } return await callTushareAPI(params, apiUrl); } // 调用Tushare API的通用函数 async function callTushareAPI(params, apiUrl) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), TUSHARE_CONFIG.TIMEOUT); try { console.log(`请求Tushare API: ${params.api_name},参数:`, params.params); const response = await fetch(apiUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params), signal: controller.signal }); if (!response.ok) { throw new Error(`Tushare API请求失败: ${response.status}`); } const data = await response.json(); if (data.code !== 0) { throw new Error(`Tushare API错误: ${data.msg}`); } if (!data.data || !data.data.items) { throw new Error(`未找到资金流向数据`); } // 获取字段名 const fields = data.data.fields; // 将数据转换为对象数组 const convertedData = data.data.items.map((item) => { const result = {}; fields.forEach((field, index) => { result[field] = item[index]; }); return result; }); console.log(`成功获取到${convertedData.length}条资金流向数据记录`); return { data: convertedData, fields: fields }; } finally { clearTimeout(timeoutId); } } // 格式化资金流向数据输出 function formatMoneyFlowData(data, fields, queryType, tsCode) { // 按交易日期倒序排列(最新在前) const sortedData = data.sort((a, b) => (b.trade_date || '').localeCompare(a.trade_date || '')); let target = ''; if (queryType === 'market') { target = '大盘'; } else if (queryType === 'sector') { target = sortedData[0]?.name ? `板块【${sortedData[0].name}】` : `板块 ${tsCode || ''}`; } else { target = `个股 ${tsCode}`; } let output = `# 💰 ${target}资金流向数据\n\n`; // 板块查询显示特殊信息 if (queryType === 'sector' && sortedData[0]) { output += `## 📋 板块基本信息\n\n`; output += `- 板块代码: ${sortedData[0].ts_code || 'N/A'}\n`; output += `- 板块名称: ${sortedData[0].name || 'N/A'}\n`; output += `- 板块类型: ${sortedData[0].content_type || 'N/A'}\n`; if (sortedData[0].rank) { output += `- 资金流入排名: 第 ${sortedData[0].rank} 名\n`; } output += `\n`; } // 数据统计摘要 const totalDays = sortedData.length; const netInflowDays = sortedData.filter(item => (parseFloat(item.net_amount) || 0) > 0).length; const netOutflowDays = totalDays - netInflowDays; // 计算累计净流入金额 const totalNetAmount = sortedData.reduce((sum, item) => sum + (parseFloat(item.net_amount) || 0), 0); output += `## 📊 统计摘要\n\n`; output += `- 查询时间范围: ${sortedData[sortedData.length - 1]?.trade_date} 至 ${sortedData[0]?.trade_date}\n`; output += `- 交易天数: ${totalDays} 天\n`; output += `- 净流入天数: ${netInflowDays} 天 (${((netInflowDays / totalDays) * 100).toFixed(1)}%)\n`; output += `- 净流出天数: ${netOutflowDays} 天 (${((netOutflowDays / totalDays) * 100).toFixed(1)}%)\n`; output += `- 累计净流入: ${formatMoney(totalNetAmount)}\n\n`; // 构建数据表格 if (queryType === 'market') { output += formatMarketFlowTable(sortedData); } else if (queryType === 'sector') { output += formatSectorFlowTable(sortedData); } else { output += formatStockFlowTable(sortedData); } // 最近5个交易日资金流向趋势 const recentData = sortedData.slice(0, Math.min(5, sortedData.length)); output += `\n## 📈 最近资金流向趋势\n\n`; recentData.forEach(item => { const netAmount = parseFloat(item.net_amount) || 0; const netAmountRate = parseFloat(item.net_amount_rate) || 0; const trend = netAmount > 0 ? '🟢' : '🔴'; const direction = netAmount > 0 ? '净流入' : '净流出'; output += `${item.trade_date} ${trend} 主力${direction} ${formatMoney(Math.abs(netAmount))} (${Math.abs(netAmountRate).toFixed(2)}%)\n`; }); output += `\n---\n*数据来源: [Tushare](https://tushare.pro) - 东方财富(DC)*`; return output; } // 格式化大盘资金流向表格 function formatMarketFlowTable(data) { let output = `## 📋 大盘资金流向明细\n\n`; output += `| 交易日期 | 上证收盘 | 上证涨跌% | 深证收盘 | 深证涨跌% | 主力净流入(万元) | 净占比% | 超大单净流入(万元) | 大单净流入(万元) |\n`; output += `|---------|---------|---------|---------|---------|------------|--------|------------|----------|\n`; data.forEach(item => { const netAmount = parseFloat(item.net_amount) || 0; const netAmountRate = parseFloat(item.net_amount_rate) || 0; const elgAmount = parseFloat(item.buy_elg_amount) || 0; const lgAmount = parseFloat(item.buy_lg_amount) || 0; const netFlowIcon = netAmount > 0 ? '🟢' : '🔴'; output += `| ${item.trade_date} `; output += `| ${formatNumber(item.close_sh)} `; output += `| ${formatPercent(item.pct_change_sh)} `; output += `| ${formatNumber(item.close_sz)} `; output += `| ${formatPercent(item.pct_change_sz)} `; output += `| ${netFlowIcon} ${formatMoney(netAmount)} `; output += `| ${formatPercent(netAmountRate)} `; output += `| ${formatMoney(elgAmount)} `; output += `| ${formatMoney(lgAmount)} |\n`; }); return output; } // 格式化个股资金流向表格 function formatStockFlowTable(data) { let output = `## 📋 个股资金流向明细\n\n`; output += `| 交易日期 | 收盘价 | 涨跌% | 主力净流入(万元) | 净占比% | 超大单净流入(万元) | 大单净流入(万元) | 中单净流入(万元) | 小单净流入(万元) |\n`; output += `|---------|--------|------|------------|--------|------------|------------|------------|------------|\n`; data.forEach(item => { const netAmount = parseFloat(item.net_amount) || 0; const netAmountRate = parseFloat(item.net_amount_rate) || 0; const elgAmount = parseFloat(item.buy_elg_amount) || 0; const lgAmount = parseFloat(item.buy_lg_amount) || 0; const mdAmount = parseFloat(item.buy_md_amount) || 0; const smAmount = parseFloat(item.buy_sm_amount) || 0; const netFlowIcon = netAmount > 0 ? '🟢' : '🔴'; output += `| ${item.trade_date} `; output += `| ${formatNumber(item.close)} `; output += `| ${formatPercent(item.pct_change)} `; output += `| ${netFlowIcon} ${formatMoney(netAmount)} `; output += `| ${formatPercent(netAmountRate)} `; output += `| ${formatMoney(elgAmount)} `; output += `| ${formatMoney(lgAmount)} `; output += `| ${formatMoney(mdAmount)} `; output += `| ${formatMoney(smAmount)} |\n`; }); return output; } // 格式化板块资金流向表格 function formatSectorFlowTable(data) { let output = `## 📋 板块资金流向明细\n\n`; output += `| 交易日期 | 板块涨跌% | 板块指数 | 主力净流入(万元) | 净占比% | 超大单净流入(万元) | 大单净流入(万元) | 中单净流入(万元) | 小单净流入(万元) | 排名 |\n`; output += `|---------|----------|---------|------------|--------|------------|------------|------------|------------|------|\n`; data.forEach(item => { const netAmount = parseFloat(item.net_amount) || 0; const netAmountRate = parseFloat(item.net_amount_rate) || 0; const elgAmount = parseFloat(item.buy_elg_amount) || 0; const lgAmount = parseFloat(item.buy_lg_amount) || 0; const mdAmount = parseFloat(item.buy_md_amount) || 0; const smAmount = parseFloat(item.buy_sm_amount) || 0; const netFlowIcon = netAmount > 0 ? '🟢' : '🔴'; output += `| ${item.trade_date} `; output += `| ${formatPercent(item.pct_change)} `; output += `| ${formatNumber(item.close)} `; output += `| ${netFlowIcon} ${formatMoney(netAmount)} `; output += `| ${formatPercent(netAmountRate)} `; output += `| ${formatMoney(elgAmount)} `; output += `| ${formatMoney(lgAmount)} `; output += `| ${formatMoney(mdAmount)} `; output += `| ${formatMoney(smAmount)} `; output += `| ${item.rank || 'N/A'} |\n`; }); return output; } // 格式化金额(万元) function formatMoney(amount) { if (amount === 0) return '0.00万'; const amountInWan = amount / 10000; if (Math.abs(amountInWan) >= 100000) { return (amountInWan / 10000).toFixed(2) + '亿'; } return amountInWan.toFixed(2) + '万'; } // 格式化数字 function formatNumber(num) { if (num === null || num === undefined || num === '' || isNaN(parseFloat(num))) { return 'N/A'; } const number = parseFloat(num); return number.toLocaleString('zh-CN', { maximumFractionDigits: 2 }); } // 格式化百分比 function formatPercent(num) { if (num === null || num === undefined || num === '' || isNaN(parseFloat(num))) { return 'N/A'; } const number = parseFloat(num); const sign = number > 0 ? '+' : ''; return `${sign}${number.toFixed(2)}%`; }

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/guangxiangdebizi/FinanceMCP'

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