238 lines
7.6 KiB
Python
238 lines
7.6 KiB
Python
"""
|
||
A股交易分析系统 - FastAPI 后端服务
|
||
基于现代 Web 架构的全新实现
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
from datetime import datetime
|
||
from typing import Any, Dict, List, Optional
|
||
|
||
from fastapi import FastAPI, HTTPException
|
||
from fastapi.middleware.cors import CORSMiddleware
|
||
from pydantic import BaseModel
|
||
|
||
# 添加 TradingShared 路径
|
||
SHARED_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'TradingShared')
|
||
if SHARED_PATH not in sys.path:
|
||
sys.path.insert(0, SHARED_PATH)
|
||
if os.path.join(SHARED_PATH, 'api') not in sys.path:
|
||
sys.path.insert(0, os.path.join(SHARED_PATH, 'api'))
|
||
|
||
# 创建 FastAPI 应用
|
||
app = FastAPI(
|
||
title="A股交易分析系统 API",
|
||
description="提供股票分析、智能推荐、竞价排行等功能",
|
||
version="2.0.0"
|
||
)
|
||
|
||
# 配置 CORS
|
||
app.add_middleware(
|
||
CORSMiddleware,
|
||
allow_origins=["http://localhost:3000", "http://localhost:5173", "*"],
|
||
allow_credentials=True,
|
||
allow_methods=["*"],
|
||
allow_headers=["*"],
|
||
)
|
||
|
||
|
||
# ==================== 数据模型 ====================
|
||
|
||
class StockInfo(BaseModel):
|
||
code: str
|
||
name: str
|
||
price: Optional[float] = None
|
||
change_percent: Optional[float] = None
|
||
score: Optional[int] = None
|
||
recommendation: Optional[str] = None
|
||
|
||
|
||
class StockAnalysisRequest(BaseModel):
|
||
stock_code: str
|
||
llm_model: str = "none"
|
||
period: str = "short" # short, medium, long
|
||
|
||
|
||
class StockAnalysisResponse(BaseModel):
|
||
stock_code: str
|
||
stock_name: str
|
||
technical_score: int
|
||
fundamental_score: int
|
||
combined_score: int
|
||
recommendation: str
|
||
analysis_detail: str
|
||
timestamp: str
|
||
|
||
|
||
class MarketOverview(BaseModel):
|
||
total_stocks: int
|
||
rising_stocks: int
|
||
falling_stocks: int
|
||
ai_recommendations: int
|
||
last_update: str
|
||
|
||
|
||
class SystemStatus(BaseModel):
|
||
data_source: str
|
||
kline_data: str
|
||
choice_terminal: str
|
||
ai_model: str
|
||
|
||
|
||
# ==================== 模拟数据 ====================
|
||
|
||
MOCK_STOCKS = [
|
||
{"code": "600519", "name": "贵州茅台", "price": 1856.00, "change_percent": 5.23, "score": 92},
|
||
{"code": "000858", "name": "五粮液", "price": 168.50, "change_percent": 4.87, "score": 88},
|
||
{"code": "601318", "name": "中国平安", "price": 48.20, "change_percent": 3.56, "score": 85},
|
||
{"code": "600036", "name": "招商银行", "price": 35.80, "change_percent": -1.24, "score": 72},
|
||
{"code": "600000", "name": "浦发银行", "price": 8.65, "change_percent": 2.18, "score": 78},
|
||
{"code": "300750", "name": "宁德时代", "price": 215.00, "change_percent": 6.32, "score": 89},
|
||
{"code": "002594", "name": "比亚迪", "price": 268.00, "change_percent": 4.15, "score": 87},
|
||
{"code": "600276", "name": "恒瑞医药", "price": 42.30, "change_percent": -0.85, "score": 75},
|
||
]
|
||
|
||
|
||
# ==================== API 路由 ====================
|
||
|
||
@app.get("/")
|
||
async def root():
|
||
"""API 根路径"""
|
||
return {
|
||
"message": "A股交易分析系统 API",
|
||
"version": "2.0.0",
|
||
"docs": "/docs"
|
||
}
|
||
|
||
|
||
@app.get("/api/market/overview", response_model=MarketOverview)
|
||
async def get_market_overview():
|
||
"""获取市场概览数据"""
|
||
rising = len([s for s in MOCK_STOCKS if s["change_percent"] > 0])
|
||
falling = len([s for s in MOCK_STOCKS if s["change_percent"] < 0])
|
||
high_score = len([s for s in MOCK_STOCKS if s["score"] >= 80])
|
||
|
||
return MarketOverview(
|
||
total_stocks=4396,
|
||
rising_stocks=2847,
|
||
falling_stocks=1243,
|
||
ai_recommendations=86,
|
||
last_update=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
)
|
||
|
||
|
||
@app.get("/api/system/status", response_model=SystemStatus)
|
||
async def get_system_status():
|
||
"""获取系统状态"""
|
||
return SystemStatus(
|
||
data_source="正常",
|
||
kline_data="已更新",
|
||
choice_terminal="待连接",
|
||
ai_model="DeepSeek"
|
||
)
|
||
|
||
|
||
@app.get("/api/stocks/ranking", response_model=List[StockInfo])
|
||
async def get_stock_ranking(limit: int = 10):
|
||
"""获取竞价排行榜"""
|
||
sorted_stocks = sorted(MOCK_STOCKS, key=lambda x: x["change_percent"], reverse=True)
|
||
return [
|
||
StockInfo(
|
||
code=s["code"],
|
||
name=s["name"],
|
||
price=s["price"],
|
||
change_percent=s["change_percent"],
|
||
score=s["score"]
|
||
)
|
||
for s in sorted_stocks[:limit]
|
||
]
|
||
|
||
|
||
@app.get("/api/stocks/recommendations", response_model=List[StockInfo])
|
||
async def get_recommendations(limit: int = 5):
|
||
"""获取 AI 智能推荐"""
|
||
sorted_stocks = sorted(MOCK_STOCKS, key=lambda x: x["score"], reverse=True)
|
||
recommendations = ["短期强势", "技术突破", "成长潜力", "价值低估", "趋势向好"]
|
||
|
||
return [
|
||
StockInfo(
|
||
code=s["code"],
|
||
name=s["name"],
|
||
price=s["price"],
|
||
change_percent=s["change_percent"],
|
||
score=s["score"],
|
||
recommendation=recommendations[i] if i < len(recommendations) else "潜力股"
|
||
)
|
||
for i, s in enumerate(sorted_stocks[:limit])
|
||
]
|
||
|
||
|
||
@app.get("/api/stocks/{stock_code}", response_model=StockInfo)
|
||
async def get_stock_info(stock_code: str):
|
||
"""获取单只股票信息"""
|
||
stock = next((s for s in MOCK_STOCKS if s["code"] == stock_code), None)
|
||
if not stock:
|
||
raise HTTPException(status_code=404, detail=f"股票 {stock_code} 未找到")
|
||
|
||
return StockInfo(
|
||
code=stock["code"],
|
||
name=stock["name"],
|
||
price=stock["price"],
|
||
change_percent=stock["change_percent"],
|
||
score=stock["score"]
|
||
)
|
||
|
||
|
||
@app.post("/api/stocks/analyze", response_model=StockAnalysisResponse)
|
||
async def analyze_stock(request: StockAnalysisRequest):
|
||
"""分析单只股票"""
|
||
stock = next((s for s in MOCK_STOCKS if s["code"] == request.stock_code), None)
|
||
|
||
if not stock:
|
||
# 如果不在模拟数据中,返回一个默认分析
|
||
stock = {"code": request.stock_code, "name": f"股票{request.stock_code}", "score": 75}
|
||
|
||
# 模拟分析结果
|
||
technical_score = min(100, stock.get("score", 75) + 5)
|
||
fundamental_score = max(0, stock.get("score", 75) - 5)
|
||
combined_score = stock.get("score", 75)
|
||
|
||
period_map = {
|
||
"short": "短期(1-5天)",
|
||
"medium": "中期(1-4周)",
|
||
"long": "长期(1-6个月)"
|
||
}
|
||
|
||
return StockAnalysisResponse(
|
||
stock_code=request.stock_code,
|
||
stock_name=stock["name"],
|
||
technical_score=technical_score,
|
||
fundamental_score=fundamental_score,
|
||
combined_score=combined_score,
|
||
recommendation="买入" if combined_score >= 80 else "持有" if combined_score >= 60 else "观望",
|
||
analysis_detail=f"基于{period_map.get(request.period, '短期')}分析,该股票技术面评分{technical_score}分,"
|
||
f"基本面评分{fundamental_score}分,综合评分{combined_score}分。"
|
||
f"当前市场表现{'强势' if combined_score >= 80 else '稳健' if combined_score >= 60 else '偏弱'}。",
|
||
timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
)
|
||
|
||
|
||
@app.get("/api/llm/models")
|
||
async def get_llm_models():
|
||
"""获取可用的 LLM 模型列表"""
|
||
return {
|
||
"models": [
|
||
{"id": "none", "name": "无 AI 分析", "status": "available"},
|
||
{"id": "deepseek", "name": "DeepSeek", "status": "available"},
|
||
{"id": "minimax", "name": "MiniMax", "status": "available"},
|
||
{"id": "openrouter", "name": "OpenRouter", "status": "available"},
|
||
{"id": "gemini", "name": "Gemini", "status": "available"},
|
||
],
|
||
"default": "deepseek"
|
||
}
|
||
|
||
|
||
if __name__ == "__main__":
|
||
import uvicorn
|
||
uvicorn.run(app, host="0.0.0.0", port=8000)
|