feat(data): 取真实费率并回填 fin_product;发现 510300 的身份问题
## 取费率
项目原有的行情适配器只有 names/quotes/history,没有费率。东财也没有可用的 JSON 接口
(`FundArchivesDatas.aspx?type=jjfl` 实测正文只有一句 `var apidata=`),
所以给 EastmoneyFundAdapter 加了 fetch_fees:抓 f10 基金概况页
(fundf10.eastmoney.com/jbgk_{code}.html)去标签后匹配字段名,取管理费/托管费/
销售服务费/申赎费,并顺带带回基金全称。取不到的基金不进返回(而不是给全 None),
免得调用方把"没抓到"当成"该基金确实没有费率"。
实测 20/20 全部取到。
## 回填
新增 tools/backfill_product_fees.py,默认 dry-run、默认只补 NULL:
- 只补空值:写入 38 个字段(19 只 × 管理费/托管费),已执行
- 已有值一律不动 —— `510300` 库里的 0.5000/0.1000 保持原样,要改需显式 --overwrite
- 写入走单事务
回填前 grep 确认:`fin_product.management_fee_rate` / `custodian_fee_rate`
**目前没有任何业务代码读取**(advisor 域那两个 fee 字段属于另一张表
advisor_product_contract),所以这次回填不改变任何现有行为。
要让客户真的看到费率,还需要接口与前端读它 —— 那不在本次范围。
## 两个数据问题(都指向 510300)
1) **它不是南方基金的产品**。数据源返回的基金全称是
「华泰柏瑞沪深300交易型开放式指数证券投资基金」,而库里 fund_manager 写「南方基金」。
其余 19 只全称都是"南方…"开头,只有它例外。
影响面不只是知识库:MarketQuoteSyncService 按 fund_manager='南方基金' 筛选同步对象,
所以它会被当成自家产品一起同步、一起展示。
2) **它的费率是照抄的**。库里 0.5000/0.1000,真实 0.15/0.05;而 0.50/0.10 恰好是
159329/159382/159511/588890 这四只的真实费率。结合它的净值快照时间是当天
(其余 19 只停在 09-11)、净值取整数 4.500000,判断是演示用途的人为设定。
这两条都由用户决定怎么处理,本次只记录、不擅自改(510300 的 fund_manager、净值、费率
三处原值都没动)。
## 其他
- tools/fetch_live_quotes.py 增加费率核对段落与 --no-fees(费率要串行抓 45KB/只,较慢)
- docs/42 用真实费率重写 1.2 节与新增 4.3 真实费率清单,3.1 节标出 510300 的身份问题
- docs/42 的数据缺口表更新:费率不再是缺口
门禁:ruff 通过 / mypy 249 文件 0 错 / 单元+契约 1377 passed 2 skipped。
This commit is contained in:
+84
-27
@@ -33,10 +33,18 @@
|
||||
|
||||
问:买场内基金要交哪些费用?费率是多少?
|
||||
|
||||
答:场内基金的交易费用主要是**券商佣金**,费率由您开户的券商决定,本平台不代收、
|
||||
也不设定该费率。基金本身的**管理费**与**托管费**从基金资产中按日计提、
|
||||
体现在净值里,不向您单独收取。
|
||||
各产品的管理费与托管费**以产品详情页披露为准**;本平台产品库目前只有部分产品登记了这两项。
|
||||
答:场内基金涉及两类费用 ——
|
||||
|
||||
**(1)基金自身的费用**,从基金资产中按日计提、体现在净值里,不向您单独收取:
|
||||
本平台 20 只场内基金的管理费率为 **0.15%~1.20%/年**、托管费率为 **0.05%~0.20%/年**,
|
||||
每只产品的具体费率见下一节的产品清单。**ETF 通常较低**(如沪深300ETF 管理费 0.15%/年、
|
||||
托管费 0.05%/年),**LOF 与主动管理型较高**(如南方积极配置混合 1.20%/年、托管 0.20%/年)。
|
||||
货币 ETF 另收销售服务费(如货币ETF南方 0.25%/年)。
|
||||
|
||||
**(2)交易费用**,即您买卖时产生的**券商佣金**,费率由您开户的券商决定,
|
||||
本平台不代收、也不设定该费率。
|
||||
|
||||
具体以产品详情页披露与交易页面展示为准。
|
||||
|
||||
### 1.3 价格最小变动单位
|
||||
|
||||
@@ -110,7 +118,7 @@
|
||||
|
||||
## 三、重点产品条目
|
||||
|
||||
### 3.1 沪深300ETF(510300)—— 字段最全,建议作为演示主产品
|
||||
### 3.1 沪深300ETF(510300)—— ⚠️ 这只的身份存疑,见下方红字
|
||||
|
||||
问:沪深300ETF 的起投金额是多少?南方沪深300ETF 怎么买?
|
||||
|
||||
@@ -118,32 +126,47 @@
|
||||
风险等级 **R3**。它按"手"交易,**1 手 = 100 份**,
|
||||
按最新净值 **4.5794 元**(2026-09-11)估算,买入 1 手约需 **458 元**;
|
||||
产品库中登记的最小交易金额为 **100.00 元**。
|
||||
该产品的管理费率为 **0.50%/年**、托管费率为 **0.10%/年**(从基金资产中计提,不单独向您收取)。
|
||||
该产品的管理费率为 **0.15%/年**、托管费率为 **0.05%/年**(从基金资产中计提,不单独向您收取)。
|
||||
实际成交金额、可买数量与费用请以交易页面的实时报价为准。
|
||||
|
||||
> ⚠️ **两处待你确认**:
|
||||
> 🔴 **这只不是南方基金的产品**。数据源返回的基金全称是
|
||||
> 「**华泰柏瑞**沪深300交易型开放式指数证券投资基金」,而库里 `fund_manager` 写的是
|
||||
> **「南方基金」**。其余 19 只的全称都带"南方"前缀,只有它例外。
|
||||
>
|
||||
> 1. **净值口径**:这条用的是数据源真实值 **4.5794**,而库里 `fin_product.current_nav` 是
|
||||
> **4.500000**,且快照时间是 **2026-09-13 08:19:37**(就是今天)—— 其余 19 只的快照
|
||||
> 都停在 `2026-09-11 15:00:00`。**说明这条是被人为改过的演示值**(整数好算"450 元")。
|
||||
> 知识条目该用真实值还是演示值,请你定;如果交易链路读的是库里那个值,
|
||||
> 那么"知识说 4.5794、下单按 4.5 成交"会不一致。
|
||||
> 2. **"1 手约 458 元"与"最小交易金额 100.00 元"仍不相等** —— 前者是 `lot_size=100 × 真实净值`,
|
||||
> 后者是 `fin_product.min_amount` 原值。请确认该以哪个为准,或说明 `min_amount`
|
||||
> 对场内产品不参与校验。**未确认前建议回答里只保留"1 手 = 100 份、金额随市价变动"。**
|
||||
> 这解释了为什么**唯独它**的数据处处特殊:
|
||||
>
|
||||
> | 字段 | 库里 | 真实(东方财富) |
|
||||
> |---|---|---|
|
||||
> | `fund_manager` | 南方基金 | **华泰柏瑞** |
|
||||
> | `current_nav` | 4.500000(快照时间**当天**) | 4.5794 |
|
||||
> | `management_fee_rate` | 0.5000 | **0.15** |
|
||||
> | `custodian_fee_rate` | 0.1000 | **0.05** |
|
||||
>
|
||||
> 而且 `0.50/0.10` 恰好是另外几只 ETF(`159329`/`159382`/`159511`/`588890`)的真实费率,
|
||||
> 看起来是**照抄过来的值**。
|
||||
>
|
||||
> 影响面比"知识库答不准"更大:`MarketQuoteSyncService` 按 `fund_manager = '南方基金'`
|
||||
> 筛选同步对象,所以这只**会被当成南方产品一起同步、一起显示**。
|
||||
> **请你决定**:是把它的 `fund_manager` 改回"华泰柏瑞"(那它就会退出同步范围、
|
||||
> 投顾与知识条目也不该再把它算作自家产品),还是保留现状作为演示数据 ——
|
||||
> 若保留,建议在演示口径里说清楚"这只是同指数产品、不是本公司产品"。
|
||||
>
|
||||
> ⚠️ 另外两点仍需你定:**(a)**净值该用真实值 4.5794 还是库里的演示值 4.500000;
|
||||
> **(b)**"1 手约 458 元"与"最小交易金额 100.00 元"仍不相等,请确认以哪个为准。
|
||||
|
||||
### 3.2 其他产品(简版模板)
|
||||
|
||||
其余 19 只按同一模板生成,字段取自第二节的表格:
|
||||
其余 19 只按同一模板生成,净值取第二节的表、费率取 4.3 的表:
|
||||
|
||||
问:<产品名> 是什么?代码是多少?风险等级?
|
||||
问:<产品名> 是什么?代码是多少?风险等级?费率多少?
|
||||
|
||||
答:<产品名>(代码 <product_code>)在<上交所/深交所>挂牌,属于 <ETF/LOF>,
|
||||
风险等级 <R?,由平台产品库登记>。按"手"交易,1 手 = 100 份。
|
||||
最新净值 <current_nav>(<净值日期>)。交易费用与费率以产品详情页和交易页面为准。
|
||||
风险等级 <R?>。按"手"交易,**1 手 = 100 份**。最新净值 <净值>(<净值日期>)。
|
||||
管理费率 <x>%/年、托管费率 <y>%/年,从基金资产中计提、不单独向您收取;
|
||||
买卖时的券商佣金由您开户的券商决定,本平台不代收。
|
||||
具体以产品详情页与交易页面为准。
|
||||
|
||||
> ⚠️ 这 19 只**只有 `510300` 登记了管理费与托管费**,其余全部为空
|
||||
> (`transaction_fee_rate` 更是 20 只全空),所以模板里不写任何费率数字。
|
||||
> 费率数值直接取 4.3 —— **20 只都有真实值**,不必再写"以页面为准"这种模糊话术。
|
||||
|
||||
---
|
||||
|
||||
@@ -151,9 +174,8 @@
|
||||
|
||||
| 字段 | 现状 | 影响 |
|
||||
|---|---|---|
|
||||
| `transaction_fee_rate` | **20/20 全空** | 无法回答"交易费率是多少",只能指向券商 |
|
||||
| `management_fee_rate` | 仅 `510300` 有(0.5000) | 其余 19 只无法回答管理费 |
|
||||
| `custodian_fee_rate` | 仅 `510300` 有(0.1000) | 同上 |
|
||||
| ~~`management_fee_rate`~~ / ~~`custodian_fee_rate`~~ | **已补齐**:20 只真实费率全部取到,见 4.3 | 库里 19 只为空;`510300` 的值与真实冲突(0.5000/0.1000 vs 0.15/0.05) |
|
||||
| `transaction_fee_rate` | **20/20 全空** | 场内交易佣金由券商收取,本就不该由平台登记,**保留为空是合理的** |
|
||||
| `min_amount` | 19 只为 `0.00`,`510300` 为 `100.00` | 场内产品的"最小金额"语义待确认 |
|
||||
| `risk_disclosure_required` 等三项 | 全部为 `0` | 与"严格风险等级匹配"的业务口径是否需要区分产品,待确认 |
|
||||
| 产品"起投金额" | **表里没有这个字段** | 场内按手交易,本就没有固定起投金额;建议统一按 1.1 的口径回答 |
|
||||
@@ -182,9 +204,44 @@
|
||||
**差三个数量级** —— 两个接口对这只的含义不同,**需要人工核实该用哪个口径**
|
||||
- 建议:核实之前,知识条目**不要写这只的具体净值**
|
||||
|
||||
> 复现:`python tools/fetch_live_quotes.py`(默认表格对比,`--json` 输出结构化结果)。
|
||||
> 该脚本只报告差异、**不写库** —— `fin_product.current_nav` 的刷新属于
|
||||
> `ProductHistorySyncService` / `MarketQuoteSyncService` 的职责。
|
||||
> 复现:`python tools/fetch_live_quotes.py`(默认表格对比,`--json` 输出结构化结果,
|
||||
> `--no-fees` 可跳过费率抓取)。该脚本只报告差异、**不写库** —— `fin_product.current_nav`
|
||||
> 的刷新属于 `ProductHistorySyncService` / `MarketQuoteSyncService` 的职责。
|
||||
|
||||
### 4.3 真实费率(2026-09-13 取自东方财富 f10 基金概况页)
|
||||
|
||||
**20 只全部取到**,库里 19 只为空、1 只与真实冲突。费率是**长期稳定**字段,
|
||||
下列数值可放心写进知识条目:
|
||||
|
||||
| 代码 | 名称 | 管理费 %/年 | 托管费 %/年 | 销售服务费 %/年 |
|
||||
|---|---|---|---|---|
|
||||
| 159329 | 沙特ETF南方 | 0.50 | 0.10 | — |
|
||||
| 159382 | 创业板人工智能ETF南方 | 0.50 | 0.10 | — |
|
||||
| 159511 | 通信ETF南方 | 0.50 | 0.10 | — |
|
||||
| 159615 | 恒生生物科技ETF南方 | 0.50 | 0.15 | — |
|
||||
| 159687 | 亚太精选ETF南方 | 0.20 | 0.05 | — |
|
||||
| 159700 | 科创债ETF南方 | 0.15 | 0.05 | — |
|
||||
| 159948 | 创业板ETF南方 | 0.15 | 0.05 | — |
|
||||
| 160105 | 南方积极配置混合(LOF) | 1.20 | 0.20 | — |
|
||||
| 160127 | 南方新兴消费增长股票(LOF)A | 1.20 | 0.20 | — |
|
||||
| 160128 | 南方金利定开债券A | 0.50 | 0.15 | — |
|
||||
| 160129 | 南方金利定开债券C | 0.50 | 0.15 | — |
|
||||
| 160142 | 南方优势产业(LOF) | 1.20 | 0.20 | — |
|
||||
| 160143 | 南方创业板2年定期开放混合 | 1.20 | 0.20 | — |
|
||||
| 501018 | 南方原油A | 1.00 | 0.20 | — |
|
||||
| 501062 | 南方瑞合定开混合(LOF) | 1.20 | 0.20 | — |
|
||||
| **510300** | 沪深300ETF | **0.15** | **0.05** | — |
|
||||
| 510500 | 中证500ETF南方 | 0.15 | 0.05 | — |
|
||||
| 511070 | 公司债ETF南方 | 0.15 | 0.05 | — |
|
||||
| 511810 | 货币ETF南方 | 0.30 | 0.05 | **0.25** |
|
||||
| 588890 | 科创芯片ETF南方 | 0.50 | 0.10 | — |
|
||||
|
||||
**规律**:宽基/债券 ETF 多为 0.15% 管理费,行业主题 ETF 多为 0.50%,
|
||||
主动管理型 LOF 为 1.00%~1.20%。只有货币 ETF 收销售服务费。
|
||||
|
||||
> 📌 **一处提醒**:数据源没有给出**场内交易的佣金费率**(`transaction_fee_rate` 20 只全空),
|
||||
> 这是**正常的** —— 佣金由客户开户的券商决定,平台不该也不能登记它。
|
||||
> 所以"交易费率是多少"这个问题,标准答法是引到券商,而不是从产品库取值。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user