Files
group_fqcd_jr/docs/42-场内基金知识条目草稿.md
T
lzf_0626 f6bfcc26b3 fix(demo): 把未上市的 160129 换成真实挂牌的 515450,并修掉种子假行情盖住真行情
## 1. 160129 本就不该在场内清单里

`160129`(南方金利定开债券C)是 `160128`(金利定开债券 **A** 类)的 C 类份额,
而 C 类份额只在场外销售、**不在交易所挂牌** —— 行情源对它永远返回空,下单只能走
净值降级(`source=eastmoney_nav_fallback`),既掩盖了"该产品并不交易"这一事实,
又让成交价带上折溢价偏差。

选型依据:把南方基金全部 **882 个代码**逐个问过腾讯行情源(该源只对交易所上市证券
返回数据),确认真实上市交易的只有 **109 个**;其中 R1/R2 的场内产品
(159700、160128、511070、511810)**原先已在清单内**,所以替换品只能来自 R3 及以上。
选定 **`515450` 红利低波50ETF南方**:仍是南方基金旗下、成交额约 1.3 亿元流动性充足、
红利低波定位偏稳健,与它替换掉的债券 LOF 定位最接近;客户风险等级已覆盖 R3
(原有 `510300` 即 R3)。

改动:`hq.py` 的 `FUND_TYPE_GROUPS`、`tools/import_hq_test_products.py` 的场内映射,
两处都留了注释防止被加回来;`docs/42` / `docs/43` 的产品表与费率表同步
(净值 1.4027、管理费 0.50%、托管费 0.10%);`docs/42` 另加了一条决策记录。

库内用**复用 `fin_product.id = 9100005`** 的方式替换,使 `fin_holding` /
`fin_sim_order` / `fin_transaction` / `fin_market_price` 的既有引用自动跟随,
不产生孤儿数据。那笔历史成交保留 `quote_source='eastmoney_nav_fallback'` ——
它确实是当时的事实,不该被粉饰。同时清掉了 `fin_market_price` 里那条净值降级行,
全库净值降级行情行数归零。

## 2. 种子假行情会盖住真实行情(更严重,且会反复出现)

`seed_sim_account_demo.py` 原来按"今天"upsert 一条假行情(510300=4.50、
510500=6.20,`source='eastmoney_demo_seed'`),而真实行情来自行情源、日期是
**最近交易日**。下单按 `trade_date DESC` 取行情,这条种子行**永远排在真实行情前面**;
它的 `source_updated_at` 只是 seed 运行时刻,过了 `MAX_QUOTE_AGE`(15 分钟)就让整个
产品变成 `503 行情已过期` —— 而库里明明躺着一条刚同步好的真实行情。周末尤其明显:
`trade_date` 落在**非交易日**,价格还是编的。

实测表现:`tools/seed_demo_data.py` 跑完第 4 步刚同步过真实行情,冒烟脚本的下单仍报
`503`,且本该 4.579 的成交价被查到 4.50。

改为:**已有任何行情行就绝不插手**(真实行情优先,种子不参与竞争);
一条都没有时才补,且 `trade_date` 退到最近交易日。

验证:`tools/e2e_smoke_test.py` → **40/40**(下单成交价 4.579 = 真实行情);
ruff 通过;mypy 250 文件 0 错;unit+contract 1381 passed / 0 failed。
2026-09-13 22:28:42 +08:00

17 KiB
Raw Blame History

场内基金知识条目草稿(待审核,尚未入库)

状态:草稿。尚未写入知识库 —— 审核通过后再走 POST /api/v1/knowledge/documents 入库。

数据来源:净值取自东方财富真实行情 —— 走项目自带的 app/infrastructure/fund_market_adapter.py(与下单链路、MarketQuoteSyncService 同源), 取数时间 2026-09-13;产品属性(代码/名称/交易所/风险等级/交易单位)取自 fin_product 表。 共 20 只(ETF 13 + LOF 7)。复现命令:python tools/fetch_live_quotes.py。 原则:只写表里确实有值的字段;缺失的字段一律写"以交易页面为准", 不补编数字(编费率是最容易出事的一类错误)。

为什么需要它:知识库现有的产品素材是一套"南方科技有限公司 个人理财产品手册", 里面没有本项目的场内基金条目。所以客户问"南方沪深300ETF 的起投金额"时, 检索到的是"南方结构性存款:起投金额 20万"这类别的产品,分数 0.6818、 与次优间隙只有 0.0034 —— 客服按置信度规则转人工。转人工是对的(硬答会答错产品), 缺的是本项目的产品知识。


一、通用交易规则(建议优先入库)

1.1 场内基金怎么买、最小单位是多少

问:场内基金怎么买?最小买多少?

答:本平台的场内基金按"手"撮合,1 手 = 100 份,委托数量必须是 100 份的整数倍。 最小交易金额以产品详情页披露的字段为准(本平台产品库中登记为 100.00 元); 实际所需金额按成交价 × 份数计算,随市价变动。 实际可买数量与所需金额请以交易页面的实时报价为准。

1.2 场内基金的费用有哪些

问:买场内基金要交哪些费用?费率是多少?

答:场内基金涉及两类费用 ——

(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 价格最小变动单位

问:场内基金报价最小变动多少?

答:本平台场内基金的最小价格变动单位为 0.001 元。

1.4 风险等级与适当性

问:我能买什么风险等级的产品?

答:产品风险等级从 R1 到 R5,由低到高。平台按您的有效风险测评结果做匹配校验: 测评等级与产品等级不匹配时,下单会被拦截。请先完成风险承受能力测评, 并以交易页面的校验结果为准。客服不能修改您的测评结果,也不能代为开通跨级权限。

1.5 场内基金与场外基金的区别

问:场内基金和场外基金有什么不一样?

答:场内基金(ETF/LOF)在交易所挂牌,按市价、以"手"为单位实时撮合成交; 场外基金按当日净值申赎,通常 T+1 确认。本平台的模拟交易针对场内基金, 下单即按市价全额成交;场外基金的业务流程与本模块相互独立。

口径已定(项目方 2026-09-13):「最小交易金额」一律以产品字段为准, 不用「1 手 × 净值」推算金额 —— 后者会与字段值不一致,客户会看到两套数字。


二、产品清单(20 只,数据来自 fin_product)

代码 名称 交易所 类型 风险等级 真实净值 当日涨跌 净值日期
159329 沙特ETF南方 SZSE ETF R5 0.9167 -0.01% 2026-09-10
159382 创业板人工智能ETF南方 SZSE ETF R4 2.5841 -0.49% 2026-09-11
159511 通信ETF南方 SZSE ETF R4 2.3755 +0.61% 2026-09-11
159615 恒生生物科技ETF南方 SZSE ETF R5 1.1027 -1.39% 2026-09-11
159687 亚太精选ETF南方 SZSE ETF R5 1.8891 -0.38% 2026-09-10
159700 科创债ETF南方 SZSE ETF R2 101.8009 +0.01% 2026-09-11
159948 创业板ETF南方 SZSE ETF R4 3.6895 -0.49% 2026-09-11
510300 沪深300ETF SSE ETF R3 4.5794 -0.83% 2026-09-11
510500 中证500ETF南方 SSE ETF R4 7.6027 -1.79% 2026-09-11
511070 公司债ETF南方 SSE ETF R2 103.0145 +0.01% 2026-09-11
511810 货币ETF南方 SSE ETF R1 0.2332 ⚠️ 0.00% 2026-09-13
515450 红利低波50ETF南方 SSE ETF R3 1.4027 -0.43% 2026-09-11
588890 科创芯片ETF南方 SSE ETF R4 1.2327 -1.36% 2026-09-11
160105 南方积极配置混合(LOF) SZSE LOF R3 1.2514 -1.34% 2026-09-11
160127 南方新兴消费增长股票(LOF)A SZSE LOF R4 0.8364 -0.64% 2026-09-11
160128 南方金利定开债券A SZSE LOF R2 1.0260 0.00% 2026-09-11
160142 南方优势产业(LOF) SZSE LOF R3 1.0623 -2.09% 2026-09-11
160143 南方创业板2年定期开放混合 SZSE LOF R3 1.6105 -1.14% 2026-09-11
501018 南方原油A SSE LOF R5 2.0750 +3.86% 2026-09-10
501062 南方瑞合定开混合(LOF) SSE LOF R3 2.1048 -0.49% 2026-09-11

本表净值是真实数据,取数时间 2026-09-13,来源为项目自带的东方财富适配器 (app/infrastructure/fund_market_adapter.py:push2.eastmoney.com 行情 + api.fund.eastmoney.com 历史净值)。复现命令:python tools/fetch_live_quotes.py。

核对结果:20 只全部取到真实数据;其中 18 只与库里 fin_product.current_nav 完全一致 (库里存 6 位小数,如 0.916700,数据源给 4 位 0.9167,是同一笔数), 2 只不一致 —— 510300 与 511810,见下方 3.1 与第四节。

2026-09-13 变更:160129 → 515450

原 160129(南方金利定开债券C)已从清单移除,替换为 515450 红利低波50ETF南方 (SSE / ETF / R3,管理费 0.50%、托管费 0.10%,净值 1.4027)。

为什么必须换:160129 是 160128(金利定开债券 A 类)的 C 类份额, 而 C 类份额只在场外销售、不在交易所挂牌 —— 行情源对它永远返回空,下单只能走 净值降级(source=eastmoney_nav_fallback)。这既掩盖了"该产品并不交易"这一事实, 又让成交价带上折溢价偏差。它本就不该出现在场内清单里。

选型依据:把南方基金全部 882 个代码逐个问过腾讯行情源(该源只对交易所上市 证券返回数据),确认真实上市交易的只有 109 个;其中 R1/R2 的场内产品 (159700、160128、511070、511810)原先已在清单内,所以替换品只能来自 R3 及以上。 选 515450 是因为它仍属南方基金、成交额约 1.3 亿元流动性充足、红利低波定位偏 稳健,与它替换掉的债券 LOF 定位最接近;客户风险等级已覆盖 R3(原有 510300 即 R3)。

处理方式:复用 fin_product.id = 9100005 而非删除重建,使 fin_holding / fin_sim_order / fin_transaction / fin_market_price 的既有引用自动跟随, 不产生孤儿数据。那笔历史成交保留 quote_source='eastmoney_nav_fallback' —— 它确实是当时的事实,不该被粉饰。

同时移出的还有 hq.py 的 FUND_TYPE_GROUPS(160129 原在"债券型"组) 与 tools/import_hq_test_products.py 的场内映射,两处都留了注释防止被加回来。

问:平台上有哪些场内基金?产品代码是什么?

答:本平台模拟交易范围内共有 20 只场内基金,包含 13 只 ETF 和 7 只 LOF, 基金管理人均为南方基金。代表性的有:沪深300ETF(510300,R3)、 中证500ETF南方(510500,R4)、创业板ETF南方(159948,R4)、 货币ETF南方(511810,R1)、科创债ETF南方(159700,R2)等。 完整清单与实时净值请以产品列表页为准。


三、重点产品条目

3.1 沪深300ETF(510300)—— ⚠️ 这只的身份存疑,见下方红字

问:沪深300ETF 的起投金额是多少?南方沪深300ETF 怎么买?

答:沪深300ETF 的产品代码为 510300,在**上交所(SSE)**挂牌,属于 ETF, 风险等级 R3。它按"手"交易,1 手 = 100 份, 按最新净值 4.5794 元(2026-09-11)估算,买入 1 手约需 458 元; 产品库中登记的最小交易金额为 100.00 元。 该产品的管理费率为 0.15%/年、托管费率为 0.05%/年(从基金资产中计提,不单独向您收取)。 实际成交金额、可买数量与费用请以交易页面的实时报价为准。

✅ 已按项目方决定处理(2026-09-13):保留 fund_manager = "南方基金" 作为演示口径 —— 不改成"华泰柏瑞",因为改了它就会退出 MarketQuoteSyncService 的同步范围, 投顾与知识条目也不该再把它算作自家产品;改为在产品页显式披露: common/mock-data.js 给这只加了 product_note,详情页会显示 「本产品为同指数参考产品,非本公司发行的基金,仅用于功能演示」。

同时净值与费率已回填为真实值:

python tools/backfill_product_snapshot.py --codes 510300 --apply --overwrite
字段 原值 现值(真实)
current_nav 4.500000 4.5794
current_nav_at 2026-09-13 08:19:37 2026-09-11 15:00:00
management_fee_rate 0.5000 0.15
custodian_fee_rate 0.1000 0.05

上面词条里的数字已按真实值书写。保留一条披露备查:这只的真实全称是 「华泰柏瑞沪深300交易型开放式指数证券投资基金」,其余 19 只全称都以"南方"开头; 且 0.50/0.10 恰好是另外四只 ETF(159329/159382/159511/588890)的真实费率, 原先那组值像是照抄来的。

3.2 其他产品(简版模板)

其余 19 只按同一模板生成,净值取第二节的表、费率取 4.3 的表:

问:<产品名> 是什么?代码是多少?风险等级?费率多少?

答:<产品名>(代码 <product_code>)在<上交所/深交所>挂牌,属于 <ETF/LOF>, 风险等级 <R?>。按"手"交易,1 手 = 100 份。最新净值 <净值>(<净值日期>)。 管理费率 %/年、托管费率 %/年,从基金资产中计提、不单独向您收取; 买卖时的券商佣金由您开户的券商决定,本平台不代收。 具体以产品详情页与交易页面为准。

费率数值直接取 4.3 —— 20 只都有真实值,不必再写"以页面为准"这种模糊话术。


四、数据缺口(需要业务或产品侧补,我不会代填)

字段 现状 影响
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 的口径回答

4.2 真实数据核对结果(2026-09-13)

用 python tools/fetch_live_quotes.py 拉东方财富真实行情,与库里 fin_product.current_nav 逐只比对:

结果 只数 说明
完全一致 18 库里存 6 位小数(0.916700)、数据源给 4 位(0.9167),是同一笔数
不一致 2 见下

510300 沪深300ETF:库里 4.500000 vs 真实 4.5794

  • 库里快照时间是 2026-09-13 08:19:37(就是今天),其余 19 只都停在 2026-09-11 15:00:00
  • ⇒ 这一条是被人为改过的演示值(4.5 是整数,正好凑"1 手 = 450 元")
  • 要决定的:知识条目用真实值还是演示值。若交易链路按库里的 4.5 成交, 而知识说 4.5794,两处口径会打架 —— 这比"知识库有没有这条"更要紧

511810 货币ETF南方:不是脏数据,是数据已更新 —— 而且我一开始比错了接口

查证结果(2026-09-13):

日期 DWJZ(单位净值) LJJZ(累计净值)
2026-09-13 0.2332 0.8740
2026-09-11 0.2661 0.8740
  • 库里存的 0.266100 正是 09-11 的真实 DWJZ,快照时间 2026-09-11 15:00:00 也对得上 ⇒ 它不是脏数据,只是旧值
  • 我先前说的"差三个数量级"是我自己用错了接口:QUOTE_API 返的是场内交易价格 (货币 ETF 约 100 元/份,所以 100.012),而 current_nav 的口径是 DWJZ 单位净值。 两者不是一回事,不能混比
  • 口径由既有数据确定:其余 19 只的 current_nav 与 fetch_history 的 DWJZ 逐只一致

⇒ 这只若要刷新,按 DWJZ 口径刷成 0.2332 即可:

python tools/backfill_product_snapshot.py --codes 511810 --apply --overwrite

本次未执行,等你确认 —— 因为 0.2661 本身也是真实值(只是旧一天), 刷新属于"要不要把快照推进到最新",是运维节奏问题,不是修错。

复现: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 —
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
515450 红利低波50ETF南方 0.50 0.10 —
588890 科创芯片ETF南方 0.50 0.10 —

规律:宽基/债券 ETF 多为 0.15% 管理费,行业主题 ETF 多为 0.50%, 主动管理型 LOF 为 1.00%~1.20%。只有货币 ETF 收销售服务费。

📌 一处提醒:数据源没有给出场内交易的佣金费率(transaction_fee_rate 20 只全空), 这是正常的 —— 佣金由客户开户的券商决定,平台不该也不能登记它。 所以"交易费率是多少"这个问题,标准答法是引到券商,而不是从产品库取值。


五、入库方式(审核通过后我执行)

# 走知识库管理端点上传,服务端自动切分并投向量同步事件
POST /api/v1/knowledge/documents
  { "filename": "场内基金产品手册_v1.md", "knowledge_type": "product",
    "content_base64": "<本文件的 base64>" }

入库后建议复核:

  1. 用访客浮窗问「南方沪深300ETF 的起投金额是多少」,看是否从"转人工"变为正常作答;
  2. 问「平台上有哪些场内基金」,应命中第二节的清单条目;
  3. 复查分数是否越过阈值(HIGH_SCORE=0.75 直接答;0.55~0.75 之间还要求 top1 领先次优 ≥ MIN_GAP=0.07)。

⚠️ 另外提醒:fin_knowledge_meta 表当前是空的,而 Milvus 里有知识且检索正常 —— 说明现有知识是直接灌进 Milvus 的,没写 MySQL 元数据。 走上面这个端点入库会正常写元数据,但两批数据的元数据状态会不一致 (旧的仍旧查不到、删不掉)。要不要把旧数据一并纳入治理,请你定。