From 4919ba14319538663b99f4de18d461f21a179a14 Mon Sep 17 00:00:00 2001 From: kennethcheng Date: Fri, 1 May 2026 06:44:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(ledger):=20=E5=BD=BB=E5=BA=95=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=97=B6=E5=85=89=E6=9C=BA=E6=B1=87=E7=8E=87=E5=8F=8C?= =?UTF-8?q?=E6=A0=87=E5=B9=BB=E8=A7=89=EF=BC=8C=E5=90=8C=E6=AD=A5=E5=B8=82?= =?UTF-8?q?=E5=80=BC=E4=B8=8E=E6=9C=AC=E9=87=91=E7=9A=84=20CNY=20=E6=8A=98?= =?UTF-8?q?=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Memory.md | 6 ++++++ src/actions/snapshots.ts | 16 ++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Memory.md b/Memory.md index 8584e03..88cbfd0 100644 --- a/Memory.md +++ b/Memory.md @@ -1,5 +1,11 @@ # Omniledger 架构与开发记忆 (Memory) +## 粉碎时光机中 marketValue 未乘汇率的致命双标幻觉,严格对齐历史快照与实时概览的 CNY 折算基准 (Task 59b) +- **核心认知纠正:** `calculateAssetMetrics` 输出的 `marketValue` **绝对不是 CNY**!它和 `totalInvested` 一样,都是原始币种 (Base Currency)。 +- **致命 Bug:** 之前的时光机逻辑中,`posCostCny` 使用 `metrics.totalInvested * fxRate` 折算,而 `posValueCny` 使用 `metrics.marketValue * fxRate` 折算。但由于 `totalInvested` 在 `finance.ts` 引擎中已经混入了 CNY 价格(如海尔的买入价格),导致投入本金被错误放大,在 4 月 30 日节点造成"投入本金"虚高、净盈亏显示为亏损 4 万的荒谬结果。 +- **强制修复:** 在 `src/actions/snapshots.ts` 的 `reconstructPortfolioHistory()` 资产遍历循环中,`posCostCny` 改为 `(metrics.marketValue - metrics.accumulatedPnl) * fxRate` 推导,确保投入本金 = 市值 - 累计盈亏,逻辑自洽且与实时概览 (`recordDailySnapshot`) 的 CNY 折算基准完全对齐。 +- **物理毁灭脏数据:** 在函数开头已执行 `DELETE FROM portfolio_snapshots`,重新跑时光机生成全新 1247 天快照,消除所有因汇率双标导致的错乱数据。 + ## 修复 calculateAssetMetrics 结果的汇率双重标准解析错误,重构 Live Overview 聚合基准 (Task 59) - **核心认知纠正:** `calculateAssetMetrics` 引擎产出的所有数据(`marketValue`, `accumulatedPnl`, `floatingPnl`, `dilutedCost` 等)全都是原始基础币种 (Base Currency)!绝对不存在"部分已经是 CNY"的情况。 - 修复 `src/actions/snapshots.ts` 的 `reconstructPortfolioHistory()`:之前错误地将 `cnyPrice`(已折算的人民币价格)传入引擎,却只对 `totalInvested` 乘汇率、对 `marketValue` 不乘,形成"双标幻觉"。现改为传入原始币种价格 `priceStr`,然后无例外地将引擎输出的所有金额字段统一乘以 `fxRate` 得到 CNY,确保 `posValueCny` 和 `posCostCny` 使用同一套汇率折算基准。 diff --git a/src/actions/snapshots.ts b/src/actions/snapshots.ts index bc33867..653cf74 100644 --- a/src/actions/snapshots.ts +++ b/src/actions/snapshots.ts @@ -333,14 +333,18 @@ export async function reconstructPortfolioHistory() { const priceStrForMetrics = priceStr || assetLatestPriceMap.get(assetId) || '0'; const metrics = calculateAssetMetrics(assetTxs, priceStrForMetrics); - - // 获取资产对人民币的汇率 + + // 1. 获取基础币种数据 + // 2. 获取当前资产的汇率 (必须确保能获取到,比如从 asset 表或 rateMap) const assetFxRate = new Big(getRate(baseCurrency, 'CNY') || '1'); - - // 统一汇率折算边界:calculateAssetMetrics 输出全部为 Base Currency, - // 必须无例外地将所有金额字段乘以 fxRate 得到 CNY + + // 3. 【核心修复】:市值和本金,必须双双乘以汇率! const posValueCny = new Big(metrics.marketValue).times(assetFxRate); - const posCostCny = new Big(metrics.totalInvested).times(assetFxRate); + + // 投入本金 = (市值 - 累计盈亏) * 汇率,确保逻辑自洽 + const posCostCny = new Big(metrics.marketValue) + .minus(metrics.accumulatedPnl) + .times(assetFxRate); totalValueCny = totalValueCny.plus(posValueCny); totalCostCny = totalCostCny.plus(posCostCny);