Merge pull request #405 from zelongji/fix/fuyao-income-null-schema

fix(fuyao): 财务报表构建改为全量推断 schema, 修复利润表同步必现失败
This commit is contained in:
wshy
2026-09-24 12:22:40 +08:00
committed by GitHub
2 changed files with 38 additions and 1 deletions
+3 -1
View File
@@ -934,7 +934,9 @@ class FuyaoProvider:
if src not in field_map and src not in row and isinstance(value, (int, float)):
row[src] = value
rows_out.append(row)
return pl.DataFrame(rows_out) if rows_out else pl.DataFrame()
# 稀疏列(如仅银行有值的 financial_expense)前 100 行可能全空, 默认采样会
# 推断成 Null 列, 后续数值 append 失败 → 全量扫描推断
return pl.DataFrame(rows_out, infer_schema_length=None) if rows_out else pl.DataFrame()
def _financial_metrics(self, symbols: list[str]) -> pl.DataFrame:
client = self._get_client()
+35
View File
@@ -137,6 +137,41 @@ def test_balance_and_cashflow_mapping(monkeypatch):
assert cf["pay_dividends_profits_interest_cash"] == pytest.approx(64000000000.0)
class _PerSymbolIncomeClient(_FakeFinClient):
"""按标的返回不同利润表行, 模拟全市场同步的逐股请求。"""
def __init__(self, rows_by_symbol: dict[str, dict]):
super().__init__()
self.rows_by_symbol = rows_by_symbol
def financial_statements(self, stmt, thscode, limit=1):
self.stmt_calls.append((stmt, thscode, limit))
return [dict(self.rows_by_symbol[thscode], thscode=thscode)]
def test_income_sparse_column_after_100_null_rows(monkeypatch):
"""前 100 只标的某映射列全空、之后才出现数值时, 整表仍须构建成功。
真实场景: interest_expenses→financial_expense 仅银行等少数标的有值,
全市场按代码顺序同步时前 100 行全为 None, Polars 默认按前 100 行推断出
Null 列, 遇到中信银行的 6.1199e10 即 append 失败, income 整表从未写入。
"""
rows_by_symbol = {
f"{600000 + i:06d}.SH": dict(_INCOME_ROW, interest_expenses=None)
for i in range(100)
}
rows_by_symbol["601998.SH"] = dict(_INCOME_ROW, interest_expenses=6.1199e10)
provider = _provider_with(monkeypatch, _PerSymbolIncomeClient(rows_by_symbol))
df = provider.get_financials("income", list(rows_by_symbol), latest_only=True)
assert df.height == 101
assert df.schema["financial_expense"] == pl.Float64
bank = df.filter(pl.col("symbol") == "601998.SH").row(0, named=True)
assert bank["financial_expense"] == pytest.approx(6.1199e10)
assert df.filter(pl.col("symbol") != "601998.SH")["financial_expense"].null_count() == 100
_METRICS_ABILITIES = [
{
"ability": "profitability",