stock-ai-agent/scripts/analyze_stock.py
2026-02-19 21:20:20 +08:00

214 lines
7.1 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
美股手动分析脚本
用法:
python3 scripts/analyze_stock.py AAPL # 分析单只股票
python3 scripts/analyze_stock.py AAPL TSLA # 分析多只股票
python3 scripts/analyze_stock.py # 分析配置的所有股票
环境:
需要在 backend 目录下运行,或设置 PYTHONPATH
"""
import sys
import os
import asyncio
from datetime import datetime
# 添加项目路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'backend'))
from app.services.yfinance_service import get_yfinance_service
from app.crypto_agent.llm_signal_analyzer import LLMSignalAnalyzer
from app.config import get_settings
from app.utils.logger import logger
async def analyze_stock(symbol: str):
"""
分析单只股票
Args:
symbol: 股票代码
"""
print("\n" + "=" * 80)
print(f"📊 分析 {symbol}")
print("=" * 80)
try:
# 1. 获取服务
yf_service = get_yfinance_service()
llm_analyzer = LLMSignalAnalyzer()
settings = get_settings()
# 2. 获取当前行情
print(f"\n📈 获取当前行情...")
ticker = yf_service.get_ticker(symbol)
if not ticker:
print(f"❌ 无法获取 {symbol} 的行情数据")
return
current_price = ticker['lastPrice']
price_change = ticker['priceChange']
price_change_percent = ticker['priceChangePercent']
print(f" 最新价格: ${current_price:,.2f}")
print(f" 涨跌额: ${price_change:+,.2f}")
print(f" 涨跌幅: {price_change_percent:+.2f}%")
print(f" 成交量: {ticker['volume']:,}")
# 3. 获取 K 线数据
print(f"\n📊 获取 K 线数据...")
data = yf_service.get_multi_timeframe_data(symbol)
if not data:
print(f"❌ 无法获取 {symbol} 的 K 线数据")
return
for tf, df in data.items():
print(f" {tf}: {len(df)} 条数据")
# 4. LLM 分析
print(f"\n🤖 LLM 分析中...")
print("-" * 80)
result = await llm_analyzer.analyze(
symbol,
data,
symbols=[symbol],
position_info=None
)
# 5. 输出分析结果
print("\n📋 分析结果:")
print("-" * 80)
# 市场状态
summary = result.get('analysis_summary', '')
print(f"市场状态: {summary}")
# 新闻情绪
news_sentiment = result.get('news_sentiment', '')
if news_sentiment:
sentiment_map = {'positive': '📈 积极', 'negative': '📉 消极', 'neutral': ' 中性'}
print(f"新闻情绪: {sentiment_map.get(news_sentiment, news_sentiment)}")
news_impact = result.get('news_impact', '')
if news_impact:
print(f"消息影响: {news_impact}")
# 关键价位
levels = result.get('key_levels', {})
if levels.get('support') or levels.get('resistance'):
print(f"\n关键价位:")
if levels.get('support'):
support_str = ', '.join([f"${s:,.2f}" for s in levels.get('support', [])[:3]])
print(f" 支撑位: {support_str}")
if levels.get('resistance'):
resistance_str = ', '.join([f"${r:,.2f}" for r in levels.get('resistance', [])[:3]])
print(f" 阻力位: {resistance_str}")
# 信号
signals = result.get('signals', [])
if not signals:
print(f"\n⏸️ 结论: 无交易信号,继续观望")
else:
print(f"\n🎯 发现 {len(signals)} 个信号:")
print("-" * 80)
for sig in signals:
signal_type = sig.get('type', 'unknown')
type_map = {'short_term': '短线', 'medium_term': '中线', 'long_term': '长线'}
type_text = type_map.get(signal_type, signal_type)
action = sig.get('action', 'wait')
action_map = {'buy': '🟢 做多', 'sell': '🔴 做空', 'wait': '⏸️ 观望'}
action_text = action_map.get(action, action)
grade = sig.get('grade', 'D')
confidence = sig.get('confidence', 0)
grade_icon = {'A': '⭐⭐⭐', 'B': '⭐⭐', 'C': '', 'D': ''}.get(grade, '')
print(f"\n{type_text} {action_text} [{grade}{grade_icon}] {confidence}%")
entry = sig.get('entry_price', 0)
sl = sig.get('stop_loss', 0)
tp = sig.get('take_profit', 0)
if entry and sl and tp:
print(f" 入场: ${entry:,.2f}")
print(f" 止损: ${sl:,.2f} ({((sl - entry) / entry * 100):+.1f}%)")
print(f" 止盈: ${tp:,.2f} ({((tp - entry) / entry * 100):+.1f}%)")
reason = sig.get('reason', '')
if reason:
print(f" 理由: {reason}")
risk_warning = sig.get('risk_warning', '')
if risk_warning:
print(f" 风险提示: {risk_warning}")
# 6. 格式化通知
if signals:
print("\n" + "=" * 80)
print("📱 通知预览:")
print("-" * 80)
for sig in signals:
if sig.get('grade', 'D') != 'D':
formatted = llm_analyzer.format_signal_for_notification(symbol, sig)
print(f"\n{formatted['title']}")
print(formatted['content'])
except Exception as e:
print(f"\n❌ 分析 {symbol} 失败: {e}")
import traceback
traceback.print_exc()
async def main():
"""主函数"""
import argparse
parser = argparse.ArgumentParser(description='美股手动分析脚本')
parser.add_argument('symbols', nargs='*', help='股票代码(留空则分析配置的所有股票)')
args = parser.parse_args()
# 确定要分析的股票
if args.symbols:
symbols = [s.upper() for s in args.symbols]
else:
settings = get_settings()
symbols = settings.stock_symbols.split(',') if settings.stock_symbols else []
if not symbols or not symbols[0]:
print("❌ 未指定股票代码,且配置文件中未配置 STOCK_SYMBOLS")
print("\n用法:")
print(" python3 scripts/analyze_stock.py AAPL")
print(" python3 scripts/analyze_stock.py AAPL TSLA NVDA")
print(" python3 scripts/analyze_stock.py # 分析配置的所有股票")
sys.exit(1)
print("\n" + "=" * 80)
print("🤖 美股手动分析脚本")
print("=" * 80)
print(f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"股票: {', '.join(symbols)}")
print("=" * 80)
# 逐个分析
for symbol in symbols:
await analyze_stock(symbol)
print("\n" + "=" * 80)
print("✅ 分析完成!")
print("=" * 80)
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\n\n⚠️ 用户中断")
sys.exit(0)