|
|
""" |
|
|
Report Service - 财务报告数据服务 |
|
|
集成 EasyFinancialAgent 的 MCP 工具,提供完整的数据查询方法 |
|
|
""" |
|
|
|
|
|
from EasyFinancialAgent.chat_direct import ( |
|
|
analyze_company_with_llm, |
|
|
chatbot_response, |
|
|
search_company_direct, |
|
|
get_company_info_direct, |
|
|
extract_financial_metrics_direct, |
|
|
get_company_filings_direct, |
|
|
get_latest_financial_data_direct, |
|
|
query_company_direct |
|
|
) |
|
|
from datetime import datetime |
|
|
from typing import Optional |
|
|
|
|
|
|
|
|
def get_report_data(symbol_or_name: str, years: int = 5): |
|
|
""" |
|
|
获取公司完整财务报告数据 |
|
|
|
|
|
Args: |
|
|
symbol_or_name: 公司代码或名称 (e.g., "Apple", "AAPL", "0000320193") |
|
|
years: 财务年限 (默认 5 年) |
|
|
|
|
|
Returns: |
|
|
完整的报告数据字典,包含公司信息、财务指标、最新数据等 |
|
|
|
|
|
Example: |
|
|
report = get_report_data("Apple", years=5) |
|
|
""" |
|
|
result = { |
|
|
"timestamp": datetime.now().isoformat(), |
|
|
"query_input": symbol_or_name, |
|
|
"status": "success", |
|
|
"data": { |
|
|
"company_search": None, |
|
|
"company_info": None, |
|
|
"latest_financial": None, |
|
|
"financial_metrics": None, |
|
|
"filings": None |
|
|
}, |
|
|
"errors": [] |
|
|
} |
|
|
|
|
|
try: |
|
|
|
|
|
print(f"[Report Service] 搜索公司: {symbol_or_name}") |
|
|
search_result = search_company_direct(symbol_or_name) |
|
|
print(f"[Report Service] 搜索结果: {search_result}") |
|
|
if "error" in search_result: |
|
|
result["errors"].append(f"Search failed: {search_result['error']}") |
|
|
result["status"] = "error" |
|
|
return result |
|
|
|
|
|
result["data"]["company_search"] = search_result |
|
|
|
|
|
|
|
|
cik = None |
|
|
if isinstance(search_result, dict): |
|
|
cik = search_result.get("cik") |
|
|
elif isinstance(search_result, (list, tuple)) and len(search_result) > 0: |
|
|
try: |
|
|
first = search_result[0] if isinstance(search_result, (list, tuple)) else None |
|
|
if isinstance(first, dict): |
|
|
cik = first.get("cik") |
|
|
except (IndexError, TypeError): |
|
|
pass |
|
|
|
|
|
if not cik: |
|
|
result["errors"].append("Could not extract CIK from search result") |
|
|
result["status"] = "error" |
|
|
return result |
|
|
|
|
|
print(f"[Report Service] 找到公司 CIK: {cik}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print(f"[Report Service] 获取 {years} 年财务指标") |
|
|
metrics = extract_financial_metrics_direct(cik, years=years) |
|
|
if "error" not in metrics: |
|
|
result["data"]["financial_metrics"] = metrics |
|
|
else: |
|
|
result["errors"].append(f"Failed to get financial metrics: {metrics.get('error')}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print(f"[Report Service] 报告数据获取完成") |
|
|
|
|
|
except Exception as e: |
|
|
result["status"] = "error" |
|
|
result["errors"].append(f"Exception: {str(e)}") |
|
|
import traceback |
|
|
result["errors"].append(traceback.format_exc()) |
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
def get_company_summary(symbol_or_name: str): |
|
|
""" |
|
|
获取公司摘要信息(轻量级查询) |
|
|
|
|
|
Args: |
|
|
symbol_or_name: 公司代码或名称 |
|
|
|
|
|
Returns: |
|
|
公司摘要数据字典 |
|
|
|
|
|
Example: |
|
|
summary = get_company_summary("Tesla") |
|
|
""" |
|
|
result = { |
|
|
"timestamp": datetime.now().isoformat(), |
|
|
"query_input": symbol_or_name, |
|
|
"status": "success", |
|
|
"data": { |
|
|
"company_search": None, |
|
|
"company_info": None, |
|
|
"latest_financial": None |
|
|
}, |
|
|
"errors": [] |
|
|
} |
|
|
|
|
|
try: |
|
|
|
|
|
search_result = search_company_direct(symbol_or_name) |
|
|
if "error" in search_result: |
|
|
result["errors"].append(f"Search failed: {search_result['error']}") |
|
|
result["status"] = "error" |
|
|
return result |
|
|
|
|
|
result["data"]["company_search"] = search_result |
|
|
|
|
|
|
|
|
cik = None |
|
|
if isinstance(search_result, dict): |
|
|
cik = search_result.get("cik") |
|
|
elif isinstance(search_result, (list, tuple)) and len(search_result) > 0: |
|
|
try: |
|
|
first = search_result[0] if isinstance(search_result, (list, tuple)) else None |
|
|
if isinstance(first, dict): |
|
|
cik = first.get("cik") |
|
|
except (IndexError, TypeError): |
|
|
pass |
|
|
|
|
|
if not cik: |
|
|
result["errors"].append("Could not extract CIK") |
|
|
result["status"] = "error" |
|
|
return result |
|
|
|
|
|
|
|
|
company_info = get_company_info_direct(cik) |
|
|
if "error" not in company_info: |
|
|
result["data"]["company_info"] = company_info |
|
|
|
|
|
|
|
|
latest_data = get_latest_financial_data_direct(cik) |
|
|
if "error" not in latest_data: |
|
|
result["data"]["latest_financial"] = latest_data |
|
|
|
|
|
except Exception as e: |
|
|
result["status"] = "error" |
|
|
result["errors"].append(str(e)) |
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
def get_financial_metrics(symbol_or_name: str, years: int = 5): |
|
|
""" |
|
|
获取财务指标趋势数据 |
|
|
|
|
|
Args: |
|
|
symbol_or_name: 公司代码或名称 |
|
|
years: 年数(默认 5 年) |
|
|
|
|
|
Returns: |
|
|
财务指标数据字典 |
|
|
|
|
|
Example: |
|
|
metrics = get_financial_metrics("Microsoft", years=10) |
|
|
""" |
|
|
result = { |
|
|
"timestamp": datetime.now().isoformat(), |
|
|
"query_input": symbol_or_name, |
|
|
"years": years, |
|
|
"status": "success", |
|
|
"data": None, |
|
|
"errors": [] |
|
|
} |
|
|
|
|
|
try: |
|
|
|
|
|
search_result = search_company_direct(symbol_or_name) |
|
|
if "error" in search_result: |
|
|
result["errors"].append(f"Search failed: {search_result['error']}") |
|
|
result["status"] = "error" |
|
|
return result |
|
|
|
|
|
|
|
|
cik = None |
|
|
if isinstance(search_result, dict): |
|
|
cik = search_result.get("cik") |
|
|
elif isinstance(search_result, (list, tuple)) and len(search_result) > 0: |
|
|
try: |
|
|
first = search_result[0] if isinstance(search_result, (list, tuple)) else None |
|
|
if isinstance(first, dict): |
|
|
cik = first.get("cik") |
|
|
except (IndexError, TypeError): |
|
|
pass |
|
|
|
|
|
if not cik: |
|
|
result["errors"].append("Could not extract CIK") |
|
|
result["status"] = "error" |
|
|
return result |
|
|
|
|
|
|
|
|
metrics = extract_financial_metrics_direct(cik, years=years) |
|
|
if "error" in metrics: |
|
|
result["errors"].append(f"Failed to get metrics: {metrics['error']}") |
|
|
result["status"] = "error" |
|
|
else: |
|
|
result["data"] = metrics |
|
|
|
|
|
except Exception as e: |
|
|
result["status"] = "error" |
|
|
result["errors"].append(str(e)) |
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
def get_service_health(): |
|
|
""" |
|
|
检查财务数据服务健康状态 |
|
|
|
|
|
Returns: |
|
|
服务状态字典 |
|
|
|
|
|
Example: |
|
|
health = get_service_health() |
|
|
""" |
|
|
return { |
|
|
"status": "ok", |
|
|
"message": "Using direct MCP functions (no HTTP service required)" |
|
|
} |
|
|
|
|
|
|
|
|
def query_company_advanced(company_input: str, type: str): |
|
|
""" |
|
|
综合查询公司信息 (直接调用 chat_direct 的高级方法) |
|
|
包括搜索、基本信息、文件列表和财务指标 |
|
|
|
|
|
Args: |
|
|
company_input: 公司名称或代码 |
|
|
get_filings: 是否获取文件列表 |
|
|
get_metrics: 是否获取财务指标 |
|
|
|
|
|
Returns: |
|
|
综合结果字典,包含 company_search, company_info, filings, metrics |
|
|
|
|
|
Example: |
|
|
result = query_company_advanced("Apple", get_filings=True, get_metrics=True) |
|
|
""" |
|
|
prompt_suggestion = f""" |
|
|
Role: |
|
|
|
|
|
You are an expert investment advisor who provides data-driven recommendations based on a company’s financials, news, and market data. |
|
|
|
|
|
Task: |
|
|
|
|
|
Analyze the following for {company_input}: |
|
|
|
|
|
Financial metrics – revenue, profit, debt, cash flow, etc. |
|
|
Recent news – assess risks and opportunities. |
|
|
Stock data – price trend, volume, etc. |
|
|
Output Restriction: |
|
|
|
|
|
Your response must contain only the following two elements and nothing else: |
|
|
|
|
|
The exact block titled "Investment Recommendation:" with the four specified fields filled in. |
|
|
A single concluding sentence summarizing how the recommendation integrates financial, news, and market data. |
|
|
Final Output Format: |
|
|
|
|
|
Investment Recommendation: |
|
|
|
|
|
Recommendation: [Buy / Hold / Avoid] |
|
|
|
|
|
Entry & Exit Price: [Specify] |
|
|
|
|
|
Stop-Loss & Take-Profit Levels: [Specify] |
|
|
|
|
|
Holding Horizon: Short-term (<1 month) or Long-term (>1 month) |
|
|
|
|
|
This recommendation is based on an objective synthesis of the company’s latest financial performance, material news developments, and current market price action. |
|
|
|
|
|
Rules: |
|
|
|
|
|
Base all advice strictly on factual, verifiable data—no speculation or opinion. |
|
|
Be concise, professional, and actionable. |
|
|
Always ground entry/exit and stop/take-profit levels in recent price behavior (e.g., support/resistance, volatility). |
|
|
If insufficient reliable data exists, default to "Avoid". |
|
|
Output must be in English only. |
|
|
|
|
|
""" |
|
|
prompt_report = f""" |
|
|
Analyze the following for {company_input}: |
|
|
3 Years Financial metrics – revenue, profit, debt, cash flow, etc. |
|
|
Recent news – assess risks and opportunities. |
|
|
Stock data – price trend, volume, etc. |
|
|
Role: |
|
|
You are a professional financial statement analyst specializing in deep, data-backed evaluations of public companies. Your task is to produce a comprehensive, clear, and actionable financial analysis report in English, strictly following the structure below. |
|
|
Instructions: |
|
|
Base all analysis on real financial data (e.g., Total Revenue, Net Income, Gross Profit Margin, Current Ratio) and current market trends. |
|
|
Incorporate visual insights: Describe or reference charts for Revenue Performance and Earnings Growth in the Summary, and use clear chart-based representations of key metrics in the Financial Statements section. |
|
|
If needed, simulate access to up-to-date financial databases (e.g., via Finnhub, SEC filings, or Bloomberg) to ensure data accuracy. |
|
|
Be objective, concise, and professional—avoid speculation or unsupported claims. |
|
|
Output Format (Markdown Only): |
|
|
Markdown |
|
|
# Financial Analysis Report: {company_input} |
|
|
## Executive Summary |
|
|
- Analyze the company’s current financial health using **Total Revenue**, **Net Income**, **Gross Profit Margin**, and **Current Ratio**. |
|
|
- Include narrative + chart descriptions for **Revenue Performance** and **Earnings Growth** (e.g., YoY/QoQ trends). |
|
|
- Summarize the company’s current status, historical performance, and forward-looking outlook based on data and sector dynamics. |
|
|
## Financial Statements |
|
|
- Present key financial metrics visually (e.g., time-series charts or comparative tables) for: |
|
|
- Total Revenue |
|
|
- Net Income |
|
|
- Gross Profit Margin (%) |
|
|
- Current Ratio |
|
|
- Ensure all data is clearly labeled, accurate, and easy to interpret. |
|
|
## Investment Recommendation |
|
|
- **Recommendation**: [Buy / Hold / Avoid] |
|
|
- **Entry Price**: $XX.XX |
|
|
- **Target Exit Price**: $XX.XX |
|
|
- **Stop-Loss Level**: $XX.XX |
|
|
- **Take-Profit Level**: $XX.XX |
|
|
- **Holding Horizon**: Short-term (<1 month) or Long-term (>1 month) |
|
|
> *Disclaimer: This recommendation is based on factual financial analysis. Market conditions may change rapidly—invest at your own risk.* |
|
|
## Industry Overview |
|
|
### Company Snapshot |
|
|
Brief overview of the company’s business model, market position, and core operations. |
|
|
### Three Investment Theses |
|
|
1. **Thesis 1**: [e.g., Strong revenue growth driven by product innovation] |
|
|
2. **Thesis 2**: [e.g., Expanding margins due to cost optimization] |
|
|
3. **Thesis 3**: [e.g., Attractive valuation relative to industry peers] |
|
|
### Key Risks & Mitigation Strategies |
|
|
- **Risk 1**: [e.g., Regulatory uncertainty] → *Mitigation*: [e.g., Geographic diversification] |
|
|
- **Risk 2**: [e.g., Rising input costs] → *Mitigation*: [e.g., Long-term supplier contracts] |
|
|
- **Risk 3**: [e.g., Intensifying competition] → *Mitigation*: [e.g., R&D investment and brand loyalty] |
|
|
Ensure the final output is fully self-contained, professionally written, and ready for investor review. Do not include any content outside the specified sections. |
|
|
|
|
|
|
|
|
""" |
|
|
|
|
|
prompt = prompt_suggestion if type == "suggestion" else prompt_report |
|
|
|
|
|
|
|
|
responses = list(chatbot_response(prompt)) |
|
|
final_response = responses[-1] |
|
|
print(f"最终分析答案---------: {final_response}") |
|
|
return final_response |
|
|
|