mishrabp's picture
Upload folder using huggingface_hub
f29cac7 verified
"""
Trading Research MCP Server
"""
import sys
import os
import yfinance as yf
from textblob import TextBlob
from mcp.server.fastmcp import FastMCP
from typing import List, Dict, Any
from core.mcp_telemetry import log_usage, log_trace, log_metric
import uuid
import time
# Initialize FastMCP Server
mcp = FastMCP("Trading Research", host="0.0.0.0")
@mcp.tool()
def get_news_sentiment(symbol: str) -> List[Dict[str, Any]]:
"""
Get recent news and analyze sentiment for a stock symbol.
"""
start_time = time.time()
trace_id = str(uuid.uuid4())
span_id = str(uuid.uuid4())
log_usage("mcp-trading-research", "get_news_sentiment")
try:
ticker = yf.Ticker(symbol)
news = ticker.news
results = []
for item in news:
title = item.get("title", "")
if not title:
continue
blob = TextBlob(title)
sentiment = blob.sentiment.polarity
sentiment_label = "NEUTRAL"
if sentiment > 0.1:
sentiment_label = "POSITIVE"
elif sentiment < -0.1:
sentiment_label = "NEGATIVE"
results.append({
"title": title,
"publisher": item.get("publisher", "Unknown"),
"link": item.get("link", ""),
"published": item.get("providerPublishTime", ""),
"sentiment_score": round(sentiment, 2),
"sentiment_label": sentiment_label
})
duration = (time.time() - start_time) * 1000
log_trace("mcp-trading-research", trace_id, span_id, "get_news_sentiment", duration, "ok")
return results
except Exception as e:
duration = (time.time() - start_time) * 1000
log_trace("mcp-trading-research", trace_id, span_id, "get_news_sentiment", duration, "error")
return [{"error": str(e)}]
@mcp.tool()
def get_insider_trades(symbol: str) -> List[Dict[str, Any]]:
"""
Get recent insider trading activity.
"""
log_usage("mcp-trading-research", "get_insider_trades")
try:
ticker = yf.Ticker(symbol)
insider = ticker.insider_transactions
if insider is None or insider.empty:
return []
# Convert top 5 recent trades to dict
recent = insider.head(5).reset_index()
# Handle potential date columns and serialization
recent = recent.astype(str) # Simplest way to ensure JSON serializable
return recent.to_dict(orient="records")
except Exception as e:
return [{"error": str(e)}]
@mcp.tool()
def get_analyst_ratings(symbol: str) -> List[Dict[str, Any]]:
"""
Get recent analyst recommendations.
"""
try:
ticker = yf.Ticker(symbol)
recs = ticker.recommendations
if recs is None or recs.empty:
return []
# Recent recommendations
recent = recs.tail(5).reset_index()
recent = recent.astype(str)
return recent.to_dict(orient="records")
except Exception as e:
return [{"error": str(e)}]
@mcp.tool()
def get_sec_filings(symbol: str) -> List[Dict[str, Any]]:
"""
Get recent SEC filing links (10-K, 10-Q).
Note: yfinance might not support this reliably, falling back to mock if needed or using news.
"""
try:
ticker = yf.Ticker(symbol)
# Some versions have .sec_filings, others don't.
# Fallback: Search news for "Filing" or check .news
# Or simple mock for now if API not available
return [{"info": "SEC Filings retrieval requires EDGAR API key or advanced scraping. Showing placeholder."}]
except Exception as e:
return [{"error": str(e)}]
if __name__ == "__main__":
import os
if os.environ.get("MCP_TRANSPORT") == "sse":
import uvicorn
port = int(os.environ.get("PORT", 7860))
uvicorn.run(mcp.sse_app(), host="0.0.0.0", port=port)
else:
mcp.run()