claude强化功能

This commit is contained in:
2026-06-14 11:54:45 +08:00
parent cc8dff4e57
commit e524a3589a
43 changed files with 13421 additions and 73 deletions

View File

@@ -1,4 +1,4 @@
"""智策股票终端 — FastAPI 后端入口。
"""Blackdata股票终端 — FastAPI 后端入口。
- /api/* : 数据接口(基于 AkShare带缓存与降级
- / : 托管前端原型prototype 目录)
@@ -7,6 +7,7 @@ import os
import json
import datetime as dt
from contextlib import asynccontextmanager
from typing import List, Dict, Any, Optional
from fastapi import FastAPI, Query
from fastapi.middleware.cors import CORSMiddleware
@@ -19,6 +20,7 @@ import akshare_service as svc
import config
import scheduler
import backtest as bt
import backtest_advanced as bta
import ai
import signals as sig
import report as rpt
@@ -26,10 +28,19 @@ import portfolio as pf
import llm
import alerts as al
import notifier
import intraday_radar as radar
import sector_rotation as sector
import smart_selector as selector
import attribution_analysis as attrib
import ai_chat
import sentiment_monitor as sentiment
import event_driven as events
import financial_analysis as fin
import limit_analysis as limit_up
from db import init_db, get_session
from models import (DailyQuote, IndexDaily, SectorDaily, FundFlowDaily,
SentimentDaily, DragonTiger, Security, JobRun, StockMetric, Trade,
AlertRule, AlertEvent)
AlertRule, AlertEvent, SelectorStrategy, SelectorAlert)
@asynccontextmanager
@@ -43,7 +54,7 @@ async def lifespan(app: FastAPI):
yield
app = FastAPI(title="智策股票终端 API", version="0.2.0", lifespan=lifespan)
app = FastAPI(title="Blackdata股票终端 API", version="0.2.0", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
@@ -253,6 +264,98 @@ def backtest_api(symbol: str = Query("600519"), fast: int = Query(5, ge=2, le=60
return bt.run_backtest(symbol, fast, slow)
# ============ 增强回测 ============
class BacktestParams(BaseModel):
symbol: str
strategy: str = "ma" # ma, multi_factor
fast: int = 5
slow: int = 20
position_size: float = 1.0
stop_loss: float = 0.0
take_profit: float = 0.0
initial_capital: float = 100000.0
commission: float = 0.0005
@app.post("/api/backtest/advanced")
def backtest_advanced(params: BacktestParams):
"""增强回测"""
if params.strategy == "ma":
strategy = bta.MAStrategy(
fast=params.fast,
slow=params.slow,
position_size=params.position_size,
stop_loss=params.stop_loss,
take_profit=params.take_profit
)
elif params.strategy == "multi_factor":
strategy = bta.MultiFactorStrategy(position_size=params.position_size)
else:
return {"ok": False, "msg": "不支持的策略类型"}
return bta.run_advanced_backtest(
symbol=params.symbol,
strategy=strategy,
initial_capital=params.initial_capital,
commission=params.commission
)
class OptimizeParams(BaseModel):
symbol: str
strategy: str = "ma"
fast_range: List[int] = [3, 5, 10, 15]
slow_range: List[int] = [20, 30, 60]
metric: str = "sharpe_ratio"
@app.post("/api/backtest/optimize")
def backtest_optimize(params: OptimizeParams):
"""参数优化"""
param_grid = {
"fast": params.fast_range,
"slow": params.slow_range
}
results = bta.optimize_parameters(
symbol=params.symbol,
param_grid=param_grid,
strategy_class=bta.MAStrategy,
metric=params.metric
)
return {
"ok": True,
"symbol": params.symbol,
"metric": params.metric,
"results": results[:20] # 返回前20个最优结果
}
class CompareParams(BaseModel):
symbol: str
strategies: List[Dict[str, Any]]
@app.post("/api/backtest/compare")
def backtest_compare(params: CompareParams):
"""策略对比"""
strategies = []
for s in params.strategies:
if s["type"] == "ma":
strategies.append(bta.MAStrategy(
fast=s.get("fast", 5),
slow=s.get("slow", 20),
stop_loss=s.get("stop_loss", 0),
take_profit=s.get("take_profit", 0)
))
elif s["type"] == "multi_factor":
strategies.append(bta.MultiFactorStrategy())
return bta.compare_strategies(params.symbol, strategies)
# ============ 全市场选股 ============
STRATEGIES = {
"surge": "最近暴涨5日涨幅≥20%",
@@ -479,6 +582,186 @@ def portfolio_equity():
return pf.equity_curve()
@app.get("/api/portfolio/attribution")
def portfolio_attribution():
"""持仓归因分析"""
return attrib.analyze_attribution()
# ============ AI 对话式分析 ============
class ChatRequest(BaseModel):
session_id: str
message: str
@app.post("/api/chat")
def chat(req: ChatRequest):
"""AI对话"""
return ai_chat.chat(req.session_id, req.message)
@app.delete("/api/chat/{session_id}")
def clear_chat(session_id: str):
"""清空会话"""
ai_chat.clear_session(session_id)
return {"ok": True}
@app.get("/api/chat/{session_id}/history")
def chat_history(session_id: str):
"""获取会话历史"""
return {"ok": True, "messages": ai_chat.get_session_history(session_id)}
# ============ 社区情绪监控 ============
@app.post("/api/sentiment/collect")
def sentiment_collect(limit: int = Query(50, ge=10, le=200)):
"""采集社区帖子"""
return sentiment.collect_posts(limit)
@app.get("/api/sentiment/index")
def sentiment_index(date: Optional[str] = None):
"""获取情绪指数"""
d = dt.date.fromisoformat(date) if date else None
return sentiment.calculate_sentiment_index(d)
@app.get("/api/sentiment/hot_stocks")
def sentiment_hot_stocks(days: int = Query(1, ge=1, le=7), limit: int = Query(20, le=50)):
"""热议股票排行"""
return sentiment.get_hot_stocks(days, limit)
@app.get("/api/sentiment/history")
def sentiment_history(days: int = Query(30, ge=7, le=90)):
"""情绪指数历史"""
return sentiment.get_sentiment_history(days)
@app.get("/api/sentiment/correlation")
def sentiment_correlation(code: str = Query(...), days: int = Query(60, ge=20, le=180)):
"""情绪与股价相关性"""
return sentiment.analyze_sentiment_correlation(code, days)
@app.get("/api/sentiment/wordcloud")
def sentiment_wordcloud(days: int = Query(7, ge=1, le=30), top_n: int = Query(50, le=100)):
"""关键词云"""
return sentiment.get_keyword_cloud(days, top_n)
# ============ 事件驱动策略 ============
@app.post("/api/events/seed")
def events_seed():
"""生成示例事件数据"""
return events.seed_sample_events()
@app.get("/api/events/earnings/pattern")
def earnings_pattern(days_before: int = Query(5, ge=1, le=10), days_after: int = Query(10, ge=5, le=30)):
"""财报发布前后统计规律"""
return events.analyze_earnings_pattern(days_before, days_after)
@app.get("/api/events/insider")
def insider_trading(code: Optional[str] = None, days: int = Query(180, ge=30, le=365)):
"""高管增减持跟踪"""
return events.track_insider_trading(code, days)
@app.get("/api/events/unlock")
def unlock_impact(days: int = Query(90, ge=30, le=180)):
"""限售解禁影响分析"""
return events.analyze_unlock_impact(days)
@app.get("/api/events/policy")
def policy_events(sector: Optional[str] = None, days: int = Query(180, ge=30, le=365)):
"""行业政策事件"""
return events.get_policy_events(sector, days)
class EventSelectorRequest(BaseModel):
event_types: List[str]
days: int = 30
@app.post("/api/events/selector")
def event_selector(req: EventSelectorRequest):
"""事件驱动选股"""
return events.event_driven_selector(req.event_types, req.days)
# ============ 财报深度解读 ============
@app.post("/api/financial/seed")
def financial_seed():
"""生成示例财报数据"""
return fin.seed_sample_reports()
@app.get("/api/financial/trend")
def financial_trend(code: str = Query(...), periods: int = Query(8, ge=4, le=16)):
"""财报关键指标趋势"""
return fin.get_report_trend(code, periods)
@app.get("/api/financial/summary")
def financial_summary(code: str = Query(...)):
"""AI财报摘要"""
return fin.generate_ai_summary(code)
@app.get("/api/financial/compare")
def financial_compare(code: str = Query(...), sector: Optional[str] = None):
"""同行对比"""
return fin.compare_with_peers(code, sector)
@app.get("/api/financial/warnings")
def financial_warnings(code: str = Query(...)):
"""财报异常预警"""
return fin.detect_abnormalities(code)
@app.get("/api/financial/calendar")
def financial_calendar(days: int = Query(30, ge=7, le=90)):
"""财报发布日历"""
return fin.get_report_calendar(days)
@app.get("/api/financial/rankings")
def financial_rankings(metric: str = Query("roe"), limit: int = Query(20, le=50)):
"""财报排行榜"""
return fin.get_top_reports(metric, limit)
# ============ 涨跌停分析 ============
@app.get("/api/limit/stocks")
def limit_stocks(date: Optional[str] = None, limit_type: str = Query("up")):
"""获取涨停/跌停股票"""
d = dt.date.fromisoformat(date) if date else None
return limit_up.get_limit_stocks(d, limit_type)
@app.get("/api/limit/consecutive")
def consecutive_limits(days: int = Query(10, ge=5, le=30)):
"""连板股追踪"""
return limit_up.track_consecutive_limits(days)
@app.get("/api/limit/break_rate")
def limit_break_rate(days: int = Query(60, ge=30, le=180)):
"""炸板率统计"""
return limit_up.analyze_limit_break_rate(days)
@app.get("/api/limit/squad")
def limit_squad(days: int = Query(30, ge=10, le=90), min_limits: int = Query(5, ge=3, le=10)):
"""涨停敢死队排行"""
return limit_up.get_limit_squad_rankings(days, min_limits)
# ============ 推送通知 ============
@app.get("/api/notify/status")
def notify_status():
@@ -489,7 +772,7 @@ def notify_status():
def notify_test():
if not notifier.any_enabled():
return {"ok": False, "msg": "未配置任何推送渠道,请在 backend/.env 配置后重启"}
res = notifier.notify("智策】推送测试", "这是一条来自智策股票终端的测试通知,收到即表示推送通道正常。")
res = notifier.notify("Blackdata】推送测试", "这是一条来自Blackdata股票终端的测试通知,收到即表示推送通道正常。")
return {"ok": True, "result": res}
@@ -613,6 +896,252 @@ def news_ai(n: NewsAI):
"text": f"判断:{senti}(关键词:{''.join(kw) or ''})。摘要:{text_in[:80]}\n(配置大模型后可获得更深入的关联分析)"}
# ============ 盘中实时监控雷达 ============
@app.get("/api/radar/status")
def radar_status():
"""雷达状态。"""
return {"trading_time": radar._is_trading_time()}
@app.post("/api/radar/scan")
def radar_scan():
"""手动触发异动扫描。"""
return radar.scan_all()
@app.get("/api/radar/events")
def radar_events(hours: int = Query(2, ge=1, le=24), limit: int = Query(50, le=200)):
"""获取最近的异动事件。"""
return {"list": radar.get_recent_events(hours, limit)}
@app.post("/api/radar/notify")
def radar_notify():
"""推送未通知的异动。"""
return radar.notify_events()
@app.get("/api/radar/stats")
def radar_stats(date: str = Query(None)):
"""异动统计。"""
d = dt.date.fromisoformat(date) if date else None
return radar.get_statistics(d)
# ============ 板块轮动分析 ============
@app.get("/api/sector/trend")
def sector_trend(days: int = Query(20, ge=5, le=60), top_n: int = Query(15, le=30)):
"""板块强弱趋势"""
return sector.get_sector_trend(days, top_n)
@app.get("/api/sector/flow")
def sector_flow(days: int = Query(5, ge=1, le=20)):
"""资金流向分析"""
return sector.analyze_fund_flow(days)
@app.get("/api/sector/lifecycle")
def sector_lifecycle(name: str = Query(...), days: int = Query(60, ge=20, le=120)):
"""板块生命周期"""
return sector.analyze_lifecycle(name, days)
@app.get("/api/sector/leaders")
def sector_leaders(name: str = Query(...), days: int = Query(20, ge=5, le=60), limit: int = Query(10, le=30)):
"""龙头股识别"""
return sector.identify_leaders(name, days, limit)
@app.get("/api/sector/correlation")
def sector_correlation(days: int = Query(60, ge=20, le=120), top_n: int = Query(20, le=30)):
"""板块联动性分析"""
return sector.analyze_correlation(days, top_n)
@app.get("/api/sector/summary")
def sector_summary():
"""板块轮动摘要"""
return sector.get_rotation_summary()
# ============ 智能选股增强 ============
@app.get("/api/selector/fields")
def selector_fields():
"""获取可用字段"""
return {"ok": True, "fields": selector.get_available_fields()}
@app.get("/api/selector/presets")
def selector_presets():
"""获取预设策略"""
return {"ok": True, "presets": selector.get_preset_strategies()}
class SelectorRequest(BaseModel):
strategy: Dict[str, Any]
date: Optional[str] = None
@app.post("/api/selector/run")
def selector_run(req: SelectorRequest):
"""执行选股"""
try:
strategy = selector.Strategy.from_dict(req.strategy)
date = dt.date.fromisoformat(req.date) if req.date else None
return selector.run_selector(strategy, date)
except Exception as e:
return {"ok": False, "msg": str(e)}
@app.post("/api/selector/backtest")
def selector_backtest(req: SelectorRequest, days: int = Query(60, ge=20, le=250)):
"""选股策略回测"""
try:
strategy = selector.Strategy.from_dict(req.strategy)
return selector.backtest_selector(strategy, days)
except Exception as e:
return {"ok": False, "msg": str(e)}
class CompareRequest(BaseModel):
strategy: Dict[str, Any]
date1: str
date2: str
@app.post("/api/selector/compare")
def selector_compare(req: CompareRequest):
"""对比选股结果"""
try:
strategy = selector.Strategy.from_dict(req.strategy)
date1 = dt.date.fromisoformat(req.date1)
date2 = dt.date.fromisoformat(req.date2)
return selector.compare_results(date1, date2, strategy)
except Exception as e:
return {"ok": False, "msg": str(e)}
@app.get("/api/selector/strategies")
def list_strategies():
"""获取保存的策略列表"""
with get_session() as s:
rows = s.execute(
select(SelectorStrategy).order_by(SelectorStrategy.updated_at.desc())
).scalars().all()
return {
"ok": True,
"strategies": [{
"id": r.id,
"name": r.name,
"description": r.description,
"is_preset": r.is_preset,
"created_at": r.created_at.strftime("%Y-%m-%d %H:%M:%S"),
"updated_at": r.updated_at.strftime("%Y-%m-%d %H:%M:%S")
} for r in rows]
}
class SaveStrategyRequest(BaseModel):
name: str
description: str = ""
strategy: Dict[str, Any]
@app.post("/api/selector/strategies")
def save_strategy(req: SaveStrategyRequest):
"""保存策略"""
try:
strategy = selector.Strategy.from_dict(req.strategy)
with get_session() as s:
record = SelectorStrategy(
name=req.name,
description=req.description,
strategy_json=strategy.to_json()
)
s.add(record)
s.commit()
return {"ok": True, "id": record.id}
except Exception as e:
return {"ok": False, "msg": str(e)}
@app.get("/api/selector/strategies/{sid}")
def get_strategy(sid: int):
"""获取策略详情"""
with get_session() as s:
record = s.get(SelectorStrategy, sid)
if not record:
return {"ok": False, "msg": "策略不存在"}
return {
"ok": True,
"id": record.id,
"name": record.name,
"description": record.description,
"strategy": json.loads(record.strategy_json)
}
@app.delete("/api/selector/strategies/{sid}")
def delete_strategy(sid: int):
"""删除策略"""
with get_session() as s:
record = s.get(SelectorStrategy, sid)
if record:
s.delete(record)
s.commit()
return {"ok": True}
@app.get("/api/selector/alerts")
def list_selector_alerts():
"""获取选股预警列表"""
with get_session() as s:
rows = s.execute(
select(SelectorAlert).order_by(SelectorAlert.id.desc())
).scalars().all()
return {
"ok": True,
"alerts": [{
"id": r.id,
"strategy_id": r.strategy_id,
"strategy_name": r.strategy_name,
"status": r.status,
"last_checked": r.last_checked.strftime("%m-%d %H:%M") if r.last_checked else "",
"last_count": r.last_count
} for r in rows]
}
class CreateAlertRequest(BaseModel):
strategy_id: int
strategy_name: str
@app.post("/api/selector/alerts")
def create_selector_alert(req: CreateAlertRequest):
"""创建选股预警"""
with get_session() as s:
record = SelectorAlert(
strategy_id=req.strategy_id,
strategy_name=req.strategy_name
)
s.add(record)
s.commit()
return {"ok": True, "id": record.id}
@app.delete("/api/selector/alerts/{aid}")
def delete_selector_alert(aid: int):
"""删除选股预警"""
with get_session() as s:
record = s.get(SelectorAlert, aid)
if record:
s.delete(record)
s.commit()
return {"ok": True}
# ============ 静态前端 ============
FRONTEND_DIR = os.path.join(os.path.dirname(BASE_DIR), "prototype")
if os.path.isdir(FRONTEND_DIR):