Files
group_fqcd_jr/app/core/fund_contracts.py
T
张胜宇 da1d14cb31 chore(sync): zsy_developcc 全量同步至 qyqy_develop(W27 口径)
- 分支内容对齐 qyqy_develop 3e24033,树完全一致(同步后 git diff 为空)
- 覆盖本轮 W27 交付:L0 表层判定层 + 出口 E6 行情 + 收益过滤槽位白名单
  + 免责声明分档 + 金标扩容至 55 条(全绿)+ 配置版本 244 已发布
- 新增 app/core/exit_codes.py、app/service/fund_trend_service.py 及 3 个测试文件
- 新增 开发文档\D3.9-客服Agent智能路由与行情出口设计-2026-09-21.md
- 基线:e239eb7(2026-09-17 品牌口径统一快照),本提交为其直接后继
2026-09-21 22:33:09 +08:00

77 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""基金行情公共契约。内部使用稳定英文字段,不暴露供应商字段名。"""
from datetime import date, datetime
from decimal import Decimal
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
class FundQuoteQuery(BaseModel):
model_config = ConfigDict(extra="forbid", frozen=True)
fund_codes: tuple[str, ...] = ()
fund_type: str | None = None
target_date: date | None = None
limit: int = Field(default=50, ge=1, le=1000)
@field_validator("fund_codes")
@classmethod
def validate_codes(cls, value: tuple[str, ...]) -> tuple[str, ...]:
for code in value:
if len(code) != 6 or not code.isdigit():
raise ValueError("fund code must be a six-digit number")
return tuple(dict.fromkeys(value))
class FundQuote(BaseModel):
model_config = ConfigDict(extra="forbid", frozen=True)
fund_code: str
fund_name: str
fund_type: str | None = None
nav: Decimal | None = None
nav_date: date | None = None
daily_change: Decimal | None = None
half_year_return: Decimal | None = None
one_year_return: Decimal | None = None
year_to_date_return: Decimal | None = None
since_inception_return: Decimal | None = None
quote_time: datetime
quote_source: Literal["eastmoney", "cache", "degraded"]
is_intraday: bool
degraded: bool = False
class FundTrendQuery(BaseModel):
"""`E6` 行情走势出口的入参(`W27` / `D3.9` §4.2)。
**只收实体,不收问句**:`fund_code` 与 `fund_name` 至少给一个 —— 由 Agent 侧
「表层判定层」抽好再传,工具自己不做自然语言理解。这样"抽错实体"与"查不到数据"
是两类可分开归因、分开回归的失败。
"""
model_config = ConfigDict(extra="forbid", frozen=True)
#: 6 位场内代码(最确定的实体)
fund_code: str | None = None
#: 产品名(需在 `fin_product.product_name` 里**唯一**命中,否则判 `ambiguous`)
fund_name: str | None = None
#: 取多少个**净值日**(不是自然日,见 `fund_trend_service` 的口径说明)
days: int = Field(default=120, ge=5, le=365)
@field_validator("fund_code")
@classmethod
def validate_code(cls, value: str | None) -> str | None:
if value is None:
return None
text = value.strip()
if len(text) != 6 or not text.isdigit():
raise ValueError("fund code must be a six-digit number")
return text
@model_validator(mode="after")
def require_entity(self) -> "FundTrendQuery":
if not (self.fund_code or (self.fund_name or "").strip()):
raise ValueError("fund_code or fund_name is required")
return self