feat: 暴露fuyao SDK全部A股接口 + AI分析轮次上限提至30

This commit is contained in:
Sakurasan
2026-09-01 17:04:38 +08:00
parent 8b43b57bb2
commit 86429f43c2
3 changed files with 145 additions and 4 deletions
+118
View File
@@ -7,6 +7,7 @@
上游错误转 HTTPException,密钥永不出现在响应中。
"""
from datetime import datetime
from fastapi import APIRouter, Query, HTTPException
from fastapi.responses import JSONResponse
from services import fuyao_client
@@ -80,6 +81,16 @@ async def v2_prices_historical(
return JSONResponse({"data": items, "count": len(items)}, headers=_NO_CACHE_HEADERS)
@router.get("/corporate-actions/adjustment-factors", summary="v2 复权因子事件流")
async def v2_adjustment_factors(
thscode: str = Query(..., description="标的 thscode,单只"),
from_date: str = Query(None, description="起始日 yyyy-MM-dd"),
to_date: str = Query(None, description="截止日 yyyy-MM-dd"),
):
data = await _guard(fuyao_client.corp_actions_adjustment_factors(thscode, from_date, to_date))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/calendar/trading-days", summary="v2 近一年交易日序列")
async def v2_calendar():
items = await _guard(fuyao_client.calendar_trading_days())
@@ -163,3 +174,110 @@ async def v2_index_prices_historical(
):
items = await _guard(fuyao_client.index_prices_historical(thscode, start, end))
return JSONResponse({"data": items, "count": len(items)}, headers=_NO_CACHE_HEADERS)
# ---------------------------------------------------------------
# 特殊数据(涨停/跌停/炸板/连板/热股/龙虎/异动/飙升)
# ---------------------------------------------------------------
@router.get("/special/limit-up-pool", summary="v2 涨停池")
async def v2_limit_up_pool(
date: str = Query(None, description="日期 yyyy-MM-dd"),
page: int = Query(1, ge=1),
size: int = Query(50, ge=1, le=200),
sort_field: str = Query("seal_money", description="排序字段"),
sort_dir: str = Query("desc", description="asc/desc"),
):
date_ms = int(datetime.strptime(date, "%Y-%m-%d").timestamp() * 1000) if date else None
data = await _guard(fuyao_client.limit_up_pool(date_ms, page, size, sort_field, sort_dir))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/special/limit-down-pool", summary="v2 跌停池")
async def v2_limit_down_pool(
date: str = Query(None, description="日期 yyyy-MM-dd"),
page: int = Query(1, ge=1),
size: int = Query(50, ge=1, le=200),
sort_field: str = Query("last_limit_time"),
sort_dir: str = Query("desc"),
):
date_ms = int(datetime.strptime(date, "%Y-%m-%d").timestamp() * 1000) if date else None
data = await _guard(fuyao_client.limit_down_pool(date_ms, page, size, sort_field, sort_dir))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/special/limit-break-pool", summary="v2 炸板池")
async def v2_limit_break_pool(
date: str = Query(None, description="日期 yyyy-MM-dd"),
page: int = Query(1, ge=1),
size: int = Query(50, ge=1, le=200),
sort_field: str = Query("price_change_ratio_pct"),
sort_dir: str = Query("desc"),
):
date_ms = int(datetime.strptime(date, "%Y-%m-%d").timestamp() * 1000) if date else None
data = await _guard(fuyao_client.limit_break_pool(date_ms, page, size, sort_field, sort_dir))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/special/limit-up-ladder", summary="v2 连板梯队")
async def v2_limit_up_ladder():
data = await _guard(fuyao_client.limit_up_ladder())
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/special/hot-stock-list", summary="v2 热股榜")
async def v2_hot_stock_list(
period: str = Query("day", description="day/week/month"),
):
data = await _guard(fuyao_client.hot_stock_list(period))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/special/hot-stock-history", summary="v2 热股榜历史")
async def v2_hot_stock_history(date: str = Query(..., description="日期 yyyy-MM-dd")):
data = await _guard(fuyao_client.hot_stock_list_history(date))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/special/hot-stock-rank-trend", summary="v2 热股排名趋势")
async def v2_hot_stock_rank_trend(
thscode: str = Query(..., description="标的 thscode"),
start_date: str = Query(..., description="起始日期 yyyy-MM-dd"),
end_date: str = Query(..., description="结束日期 yyyy-MM-dd"),
):
data = await _guard(fuyao_client.hot_stock_rank_trend(thscode, start_date, end_date))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/special/dragon-tiger-list", summary="v2 龙虎榜")
async def v2_dragon_tiger_list(
board_type: str = Query("all", description="all/sh/sz"),
date: str = Query(None, description="日期 yyyy-MM-dd"),
):
data = await _guard(fuyao_client.dragon_tiger_list(board_type, date))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/special/anomaly-list", summary="v2 异动分析列表")
async def v2_anomaly_list(
tag_codes: str = Query(None, description="逗号分隔异动类型编码"),
):
codes = [c.strip() for c in tag_codes.split(",") if c.strip()] if tag_codes else None
data = await _guard(fuyao_client.anomaly_analysis_list(codes))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/special/anomaly-stock", summary="v2 个股异动明细")
async def v2_anomaly_stock(
thscodes: str = Query(..., description="逗号分隔 thscode,最多50只"),
):
data = await _guard(fuyao_client.anomaly_analysis_stock(thscodes))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
@router.get("/special/skyrocket-list", summary="v2 飙升榜")
async def v2_skyrocket_list(
period: str = Query("day", description="day/week/month"),
):
data = await _guard(fuyao_client.skyrocket_list(period))
return JSONResponse({"data": data}, headers=_NO_CACHE_HEADERS)
+2 -3
View File
@@ -122,8 +122,9 @@ async def collect_ai_analysis(trade_date: str) -> dict:
total_tokens = 0
final_content = ""
was_truncated = False
max_rounds = 30 # 安全上限,正常分析约 3-8 轮
for i in range(8):
for i in range(max_rounds):
response = await call_llm(messages, tools=TOOLS)
total_tokens += response.get("usage", {}).get("total_tokens", 0)
@@ -138,11 +139,9 @@ async def collect_ai_analysis(trade_date: str) -> dict:
break
if finish_reason == "length":
# 响应被截断,保存已生成的内容
was_truncated = True
final_content = message.get("content") or ""
if not final_content:
# 如果当前消息没有内容,尝试从之前的消息中提取
for msg in reversed(messages):
if msg.get("role") == "assistant" and msg.get("content"):
final_content = msg["content"]
+24
View File
@@ -135,6 +135,14 @@ async def prices_historical(thscode: str, start_ms: int, end_ms: int,
interval="1d", adjust=adjust)
async def corp_actions_adjustment_factors(thscode: str, from_date: str = None,
to_date: str = None) -> dict:
data = await _run(_sdk.corp_actions_adjustment_factors, thscode,
**({"from": from_date} if from_date else {}),
**({"to": to_date} if to_date else {}))
return data or {}
async def calendar_trading_days() -> list[dict]:
return await _run(_sdk.calendar_trading_days)
@@ -285,3 +293,19 @@ async def limit_break_pool(date_ms=None, page=1, size=50,
data = await _run(_sdk.special_data_limit_break_pool, date_ms=date_ms,
page=page, size=size, sort_field=sort_field, sort_dir=sort_dir)
return data or {}
async def hot_stock_list_history(date: str) -> dict:
data = await _run(_sdk.special_data_hot_stock_list_history, date)
return data or {}
async def hot_stock_rank_trend(thscode: str, start_date: str, end_date: str) -> dict:
data = await _run(_sdk.special_data_hot_stock_rank_trend, thscode, start_date, end_date)
return data or {}
async def anomaly_analysis_stock(thscodes: str) -> dict:
codes = [c.strip() for c in thscodes.split(",") if c.strip()]
data = await _run(_sdk.special_data_anomaly_analysis_stock, codes)
return data or {}