交易代理需要两个数据流:实时市场新闻和结构化金融数据。本教程构建了一个 MCP 服务器,它将突发新闻和情绪的 Scavio 搜索与价格和基本面的金融数据 API 结合起来,将两者公开为任何 MCP 兼容代理都可以调用的工具调用。
前置条件
- Python 3.11+
- 来自 https://scavio.dev 的 Scavio API 密钥
- 免费的金融数据 API 密钥(Alpha Vantage 或类似)
- mcp Python 包 (pip install mcp)
操作指南
步骤 1: 设置MCP服务器骨架
使用标准协议处理程序创建 MCP 服务器。该服务器将公开三个工具:market_news、stock_quote 和 Trading_signal。
from mcp.server import Server
from mcp.types import Tool, TextContent
import httpx
import json
import os
SCAVIO_API_KEY = os.environ.get("SCAVIO_API_KEY", "your-api-key")
FINANCE_API_KEY = os.environ.get("FINANCE_API_KEY", "your-finance-key")
server = Server("trading-data")
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="market_news",
description="Search for breaking market news and analyst commentary on a stock or sector",
inputSchema={
"type": "object",
"properties": {
"query": {"type": "string", "description": "Stock ticker or sector to search"},
"num_results": {"type": "integer", "default": 5}
},
"required": ["query"]
}
),
Tool(
name="stock_quote",
description="Get current price, volume, and daily change for a stock ticker",
inputSchema={
"type": "object",
"properties": {
"ticker": {"type": "string", "description": "Stock ticker symbol"}
},
"required": ["ticker"]
}
),
Tool(
name="trading_signal",
description="Combine news sentiment with price data to generate a trading signal",
inputSchema={
"type": "object",
"properties": {
"ticker": {"type": "string"},
"timeframe": {"type": "string", "default": "1d"}
},
"required": ["ticker"]
}
)
]步骤 2: 通过 Scavio 搜索实施市场新闻工具
market_news 工具搜索最近的财经新闻,过滤市场相关内容,并返回带有源 URL 的结构化摘要。
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "market_news":
return await handle_market_news(arguments)
elif name == "stock_quote":
return await handle_stock_quote(arguments)
elif name == "trading_signal":
return await handle_trading_signal(arguments)
return [TextContent(type="text", text=f"Unknown tool: {name}")]
async def handle_market_news(args: dict) -> list[TextContent]:
query = args["query"]
num_results = args.get("num_results", 5)
async with httpx.AsyncClient(timeout=15) as client:
resp = await client.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": SCAVIO_API_KEY},
json={
"query": f"{query} stock market news 2026",
"num_results": num_results
}
)
resp.raise_for_status()
results = resp.json().get("results", [])
news = []
for r in results:
news.append({
"title": r.get("title", ""),
"url": r.get("url", ""),
"summary": r.get("description", "")[:300],
"source": r.get("url", "").split("/")[2] if r.get("url") else ""
})
return [TextContent(type="text", text=json.dumps({"news": news, "count": len(news)}, indent=2))]步骤 3: 实施股票报价和交易信号工具
stock_quote 工具从金融 API 获取价格数据。 Trading_signal 工具将新闻情绪与价格走势相结合,产生简单的看涨/看跌/中性信号。
async def handle_stock_quote(args: dict) -> list[TextContent]:
ticker = args["ticker"].upper()
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.get(
"https://www.alphavantage.co/query",
params={
"function": "GLOBAL_QUOTE",
"symbol": ticker,
"apikey": FINANCE_API_KEY
}
)
resp.raise_for_status()
data = resp.json().get("Global Quote", {})
quote = {
"ticker": ticker,
"price": data.get("05. price", "N/A"),
"change": data.get("09. change", "N/A"),
"change_pct": data.get("10. change percent", "N/A"),
"volume": data.get("06. volume", "N/A")
}
return [TextContent(type="text", text=json.dumps(quote, indent=2))]
async def handle_trading_signal(args: dict) -> list[TextContent]:
ticker = args["ticker"].upper()
# Get news
news_result = await handle_market_news({"query": ticker, "num_results": 5})
news_data = json.loads(news_result[0].text)
# Get price
quote_result = await handle_stock_quote({"ticker": ticker})
quote_data = json.loads(quote_result[0].text)
# Simple sentiment scoring from news titles
positive_words = {"surge", "jump", "rally", "beat", "upgrade", "growth", "record", "gain"}
negative_words = {"drop", "fall", "crash", "miss", "downgrade", "decline", "loss", "cut"}
sentiment_score = 0
for article in news_data.get("news", []):
title_words = set(article["title"].lower().split())
sentiment_score += len(title_words & positive_words)
sentiment_score -= len(title_words & negative_words)
# Combine with price change
change_pct = quote_data.get("change_pct", "0%").replace("%", "")
try:
price_signal = float(change_pct)
except ValueError:
price_signal = 0.0
# Generate signal
combined = sentiment_score + (1 if price_signal > 0 else -1 if price_signal < 0 else 0)
if combined >= 2:
signal = "BULLISH"
elif combined <= -2:
signal = "BEARISH"
else:
signal = "NEUTRAL"
result = {
"ticker": ticker,
"signal": signal,
"sentiment_score": sentiment_score,
"price_change": change_pct + "%",
"news_count": len(news_data.get("news", [])),
"confidence": "high" if abs(combined) >= 3 else "medium" if abs(combined) >= 2 else "low"
}
return [TextContent(type="text", text=json.dumps(result, indent=2))]步骤 4: 运行 MCP 服务器
通过 stdio 启动 MCP 服务器,以便任何 MCP 兼容客户端(Claude Desktop、Hermes、自定义代理)都可以连接并调用交易工具。
from mcp.server.stdio import stdio_server
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
# MCP client config (add to mcp_config.json):
# {
# "mcpServers": {
# "trading-data": {
# "command": "python",
# "args": ["trading_mcp_server.py"],
# "env": {
# "SCAVIO_API_KEY": "your-api-key",
# "FINANCE_API_KEY": "your-finance-key"
# }
# }
# }
# }Python 示例
import asyncio
import httpx
import json
SCAVIO_API_KEY = "your-api-key"
async def market_news(ticker: str) -> dict:
async with httpx.AsyncClient(timeout=15) as client:
resp = await client.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": SCAVIO_API_KEY},
json={"query": f"{ticker} stock market news 2026", "num_results": 5}
)
resp.raise_for_status()
results = resp.json().get("results", [])
return {
"ticker": ticker,
"articles": [{"title": r["title"], "url": r["url"]} for r in results],
"count": len(results)
}
async def main():
news = await market_news("NVDA")
print(f"Found {news['count']} articles for {news['ticker']}")
for a in news["articles"]:
print(f" {a['title']}")
asyncio.run(main())JavaScript 示例
const SCAVIO_API_KEY = "your-api-key";
async function marketNews(ticker) {
const resp = await fetch("https://api.scavio.dev/api/v1/search", {
method: "POST",
headers: { "x-api-key": SCAVIO_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ query: ticker + " stock market news 2026", num_results: 5 })
});
const data = await resp.json();
return {
ticker,
articles: (data.results || []).map(r => ({ title: r.title, url: r.url })),
count: (data.results || []).length
};
}
async function main() {
const news = await marketNews("NVDA");
console.log(`Found ${news.count} articles for ${news.ticker}`);
news.articles.forEach(a => console.log(" " + a.title));
}
main();预期输出
Found 5 articles for NVDA
NVIDIA Q2 2026 Earnings Beat Expectations...
NVDA Stock Surges on New AI Chip Announcement...