mirror of
https://github.com/shy3130/tick-stock-panel.git
synced 2026-11-02 10:26:13 +00:00
fix(assistant): get_financials 按 period_end 排序后取最近 N 期, 不再依赖文件行序
排序候选是 report_date / end_date / ann_date / date, 本地财务表一个都没有 (报告期列是 period_end, 公告日是 announce_date), 于是不排序直接 tail(N)。 首次全量同步按数据源返回顺序原样落盘, 行序为新→旧时「最近 4 期」拿到的是最早的 4 期。 把 period_end 加为首选排序列, 与 financial_analyzer 按 period_end 取最新 N 期同口径。 Closes #355 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5eafc0ad0e
commit
ad320adfa1
@@ -339,7 +339,8 @@ def _get_financials(args: dict[str, Any], ctx: ToolContext) -> dict[str, Any]:
|
||||
sub = df.filter(pl.col("symbol") == symbol)
|
||||
if sub.is_empty():
|
||||
return {"symbol": symbol, "table": table, "rows": [], "note": "本地财务数据中没有该标的。"}
|
||||
sort_col = next((c for c in ("report_date", "end_date", "ann_date", "date") if c in sub.columns), None)
|
||||
# 本地财务表的报告期列是 period_end; 物理行序取决于同步路径, 须显式排序后再取最近 N 期
|
||||
sort_col = next((c for c in ("period_end", "report_date", "end_date", "ann_date", "date") if c in sub.columns), None)
|
||||
if sort_col:
|
||||
sub = sub.sort(sort_col)
|
||||
sub = sub.tail(periods)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"""AI 助手 get_financials 必须返回「最近 N 个报告期」, 不依赖 parquet 的物理行序。
|
||||
|
||||
财务表的报告期列是 period_end (与 financial_sync 的合并键、financial_analyzer 的
|
||||
「按 period_end 降序取最新 N 期」一致)。_get_financials 的排序候选却是
|
||||
report_date / end_date / ann_date / date —— 本地财务表一个都没有, 于是不排序,
|
||||
直接 tail(N) 取物理行序的最后 N 行。首次全量同步 (_sync_table) 按数据源返回顺序
|
||||
原样落盘, 数据源按新→旧返回时, 助手答「最近 4 期」拿到的是最早的 4 期。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
|
||||
import polars as pl
|
||||
|
||||
from app.custom.assistant import tools as assistant_tools
|
||||
|
||||
_PERIODS = [
|
||||
date(2024, 9, 30), date(2024, 12, 31), date(2025, 3, 31),
|
||||
date(2025, 6, 30), date(2025, 9, 30), date(2025, 12, 31),
|
||||
]
|
||||
|
||||
|
||||
def _write_metrics(data_dir, periods: list[date]) -> None:
|
||||
out = data_dir / "financials" / "metrics"
|
||||
out.mkdir(parents=True)
|
||||
pl.DataFrame({
|
||||
"symbol": ["600519.SH"] * len(periods) + ["000001.SZ"],
|
||||
"period_end": [*periods, date(2025, 12, 31)],
|
||||
"announce_date": [date(p.year + (p.month == 12), (p.month % 12) + 1, 28) for p in periods] + [date(2026, 3, 20)],
|
||||
"roe": [float(i) for i in range(len(periods))] + [9.9],
|
||||
}).write_parquet(out / "part.parquet")
|
||||
|
||||
|
||||
async def _periods_returned(data_dir, periods: int) -> list[str]:
|
||||
ctx = assistant_tools.ToolContext.build(data_dir=data_dir)
|
||||
payload = await assistant_tools.execute_assistant_tool(
|
||||
"get_financials", {"symbol": "600519.SH", "table": "metrics", "periods": periods}, ctx,
|
||||
)
|
||||
assert payload["ok"] is True
|
||||
return [row["period_end"] for row in payload["result"]["rows"]]
|
||||
|
||||
|
||||
async def test_latest_periods_when_file_is_newest_first(tmp_path) -> None:
|
||||
"""数据源按新→旧返回、首次同步原样落盘。"""
|
||||
_write_metrics(tmp_path, list(reversed(_PERIODS)))
|
||||
|
||||
assert await _periods_returned(tmp_path, 4) == [
|
||||
"2025-03-31", "2025-06-30", "2025-09-30", "2025-12-31",
|
||||
]
|
||||
|
||||
|
||||
async def test_latest_periods_when_file_is_oldest_first(tmp_path) -> None:
|
||||
"""累积合并路径 (_merge_report_history) 已按 period_end 升序重写: 结果不变。"""
|
||||
_write_metrics(tmp_path, _PERIODS)
|
||||
|
||||
assert await _periods_returned(tmp_path, 2) == ["2025-09-30", "2025-12-31"]
|
||||
Reference in New Issue
Block a user