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
+119 -1
View File
@@ -7,6 +7,7 @@
上游错误转 HTTPException,密钥永不出现在响应中。 上游错误转 HTTPException,密钥永不出现在响应中。
""" """
from datetime import datetime
from fastapi import APIRouter, Query, HTTPException from fastapi import APIRouter, Query, HTTPException
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from services import fuyao_client 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) 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 近一年交易日序列") @router.get("/calendar/trading-days", summary="v2 近一年交易日序列")
async def v2_calendar(): async def v2_calendar():
items = await _guard(fuyao_client.calendar_trading_days()) items = await _guard(fuyao_client.calendar_trading_days())
@@ -162,4 +173,111 @@ async def v2_index_prices_historical(
end: int = Query(..., description="结束时间,毫秒 Unix 时间戳"), end: int = Query(..., description="结束时间,毫秒 Unix 时间戳"),
): ):
items = await _guard(fuyao_client.index_prices_historical(thscode, start, end)) items = await _guard(fuyao_client.index_prices_historical(thscode, start, end))
return JSONResponse({"data": items, "count": len(items)}, headers=_NO_CACHE_HEADERS) 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 total_tokens = 0
final_content = "" final_content = ""
was_truncated = False 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) response = await call_llm(messages, tools=TOOLS)
total_tokens += response.get("usage", {}).get("total_tokens", 0) total_tokens += response.get("usage", {}).get("total_tokens", 0)
@@ -138,11 +139,9 @@ async def collect_ai_analysis(trade_date: str) -> dict:
break break
if finish_reason == "length": if finish_reason == "length":
# 响应被截断,保存已生成的内容
was_truncated = True was_truncated = True
final_content = message.get("content") or "" final_content = message.get("content") or ""
if not final_content: if not final_content:
# 如果当前消息没有内容,尝试从之前的消息中提取
for msg in reversed(messages): for msg in reversed(messages):
if msg.get("role") == "assistant" and msg.get("content"): if msg.get("role") == "assistant" and msg.get("content"):
final_content = msg["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) 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]: async def calendar_trading_days() -> list[dict]:
return await _run(_sdk.calendar_trading_days) 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, 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) page=page, size=size, sort_field=sort_field, sort_dir=sort_dir)
return data or {} 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 {}