Skip to content

ui: 行情价格展示层硬编码「$」,港股/A 股/新加坡标的币种显示错误(4 个调用点) #258

Description

@wxrbyte

现象

行情价格展示层仍有 4 处 $ 硬编码,任何非美元标的都会把本币金额当成美元显示:

文件 硬编码位置 影响
packages/ui/src/components/workspace/SecurityHeader.tsx formatPrice = (v) => \$${v.toFixed(2)}`` 顶栏最新价 + 开/高/低/昨收 5 个数字
packages/ui/src/components/workspace/OverviewView.tsx 同名 fmtPrice 行情块 + 表现块 5 个数字
packages/ui/src/components/stock/Watchlist.tsx formatPrice 自选列表每一行的最新价
packages/ui/src/components/stock/StockCard.tsx 3 处内联模板 卡片最新价 + H:/L: 行

这些都是错误断言:

  • 0700.HK(港股)显示 $350.00,正确应为 HK$350.00;
  • 600519.SH / 000001.SZ(A 股)显示 $350.00,正确应为 CN¥350.00;
  • D05.SG 显示 $350.00,正确应为 SGD 350.00。

并且在 zh-CN 下即使标的是美股也会错:Intl 的 zh-CN 货币格式是 US$1,234.56,硬编码 $ 与该 locale 约定不符。

这直接违反仓库自身的约定 —— packages/i18n/src/format.ts 与 packages/ui/src/lib/money.ts 的 docstring 都写明「NEVER hardcode $」,lib/money.ts 更明确要求「一个视图不能同时出现 US$1,234.56 与 $1,234.56」。

OverviewView 是这条约束被破坏的最直观证据:它已经用 formatMoney(holding.marketValue, holding.currency) 渲染持仓块,却在同一个视图的行情块里用硬编码 $。

复现

新增 5 个测试文件(详见下方验证段),在未修复的代码上运行:

(fail) prices a Hong Kong instrument in HKD, never a bare $        Expected: 1  Received: 0
(fail) falls back to the routed market when the provider reports no currency
                                                                   Expected: 5  Received: 0
(fail) formats every quote cell in the instrument currency          Expected: 5  Received: 0
(fail) prices a Hong Kong tile in HKD instead of a hardcoded $      Expected: 3  Received: 1
(fail) prices each row in that symbol currency
        Received: "0700.HKTencent Holdings$350.00+0.43%"
 1 pass / 6 fail  +  SyntaxError: Export named 'currencyForSymbol' not found

Watchlist 那条失败信息把问题完整暴露出来:Tencent Holdings 的最新价被渲染成 $350.00。

根因

已关闭的 issue #86「Unify currency formatting onto the i18n locale policy (lib/money vs @finagent/i18n)」把货币格式化统一到了 lib/money.ts → @finagent/i18n,但迁移时漏掉了这 4 个只显示行情价格、不显示持仓金额的调用点。它们各自保留了设计刷新时期的本地 formatPrice/fmtPrice 辅助函数。

SecurityHeader / OverviewView 其实已经通过 market.getStaticInfo() 拿到了 StaticInfo.currency(packages/core/src/index.ts:81),只是没有用它格式化价格。

修复方案

  1. 在 packages/ui/src/lib/money.ts 补一个 currencyForSymbol(symbol),作为 @finagent/shared/src/agent/answer-block-emitter.ts 中同名函数的渲染进程镜像(UI 包因会把 node/executor 代码带进 renderer 而不能 import @finagent/shared,仓库中已有多个同类镜像先例)。映射规则与上游完全一致:.HK→HKD、.SH/.SZ→CNY、.SG→SGD、其余→USD。
  2. 4 个调用点改用 formatMoney(value, currency):
    • SecurityHeader / OverviewView 优先使用数据源上报的货币(info?.currency / staticInfo?.currency),缺失时回落到符号路由推断;
    • Watchlist / StockCard 只持有 symbol,直接使用符号路由推断。
  3. 删除这 4 处本地硬编码辅助函数。

不改动任何 i18n 文案、不新增 key,展示形态完全由既有 @finagent/i18n 的 locale 策略决定。

影响面

仅涉及「行情价格」的展示;formatNumber(quote.volume) 等非货币数字不变。本次未纳入提醒规则的 ruleSummary(packages/ui/src/atoms/alertAtoms.ts:97-99 同样硬编码 $,而本地化的 AlertCard 走的是 formatCurrency(value) 无币种参数、渲染成不带币种的裸数字)——那是另一条数据通路(告警规则描述,且该 helper 尚未 i18n),如需处理建议单独开 issue,避免把这个 PR 扩大成两类改动。

验证

TDD 顺序:先写测试 → 在未修复代码上确认失败 → 修复 → 全部通过。

# 修复前
 1 pass / 6 fail   (+ SyntaxError: Export named 'currencyForSymbol' not found)

# 修复后
 9 pass / 0 fail

新增测试覆盖:

文件 覆盖内容
packages/ui/src/lib/money.test.ts currencyForSymbol 的 5 种后缀映射、大小写、未知/空值回落,以及与 formatMoney 的组合输出
packages/ui/src/components/workspace/SecurityHeader.test.tsx 0700.HK + provider 上报 HKD → 5 处 HK$;600519.SH 且 provider 未上报币种 → 5 处 CN¥(验证回落分支)
packages/ui/src/components/workspace/OverviewView.test.tsx 行情块 5 个数字全部使用 HK$
packages/ui/src/components/stock/Watchlist.test.tsx 自选行按该行 symbol 的币种渲染
packages/ui/src/components/stock/StockCard.test.tsx 港股卡片 3 处 HK$;美股卡片仍为 $(防止过度修正)

回归对照:

命令 修复前 修复后
bun test packages/ui --isolate 321 pass / 0 fail 332 pass / 0 fail
bun test apps/electron --isolate 32 pass / 0 fail 32 pass / 0 fail
bun test packages/core --isolate 53 pass / 0 fail 53 pass / 0 fail
bun run typecheck 5/5 exit 0 5/5 exit 0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

claimedClaimed by a contributor and currently in progress

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions