diff --git a/Memory.md b/Memory.md index 384f99a..9054a5d 100644 --- a/Memory.md +++ b/Memory.md @@ -1,5 +1,13 @@ # Omniledger 架构与开发记忆 (Memory) +## 修复时光机引擎:1. 将汇率查询条件延展至 23:59:59 以解决跨日边界导致的数据穿透失败;2. 修复对已结算法币成本进行双重汇率乘法的严重财务逻辑 Bug (Task 74) +- **汇率时间边界修复**:在 `src/actions/snapshots.ts` 和 `app/api/debug/snapshot/route.ts` 的 `buildDailyRatesMap` 函数中,将 `getClosestRateForDate` 内部的时间比较边界从 `targetDateStr + 'T00:00:00Z'` 延展至 `targetDateStr + 'T23:59:59.999'`。修复根因:初始 SQL 查询使用 `lte(fetchTime, 23:59:59)` 拉取全天数据,但内层循环用次日 00:00:00 做 `<=` 截断,导致跨日时区边界下汇率数据穿透失败,美元汇率回退到 7.22 兜底值而非数据库真实的 6.82。 +- **双重汇率乘法修复**:在 `src/utils/finance.ts` 的 `calculateAssetMetrics` 返回值中新增 `accumulatedCost` 字段(`totalInvested - totalRealized`),代表持仓净成本(Base Currency)。 +- **架构红线**:`accumulatedCost` / `totalCost` 在底层数据库已是法币 (CNY) 本位(交易录入时已乘以 `exchangeRate`),**严禁再与 `snapshotFxRate` 相乘**。修复 `reconstructPortfolioHistory()` 中的 `posCostCny` 计算:从 `(metrics.marketValue - metrics.accumulatedPnl) * snapshotFxRate` 改为直接取 `metrics.accumulatedCost`,彻底消除 USD→CNY 再×FXRate 的双重折算暴击。 +- 同步修复 `app/api/debug/snapshot/route.ts` X光机接口:`calcCostCny` 从 `holding.totalCost * fxNum` 改为 `holding.totalCost`,确保调试接口与时光机引擎逻辑完全一致。 +- **验收**:META 的 `calculatedCostCny` 从虚高的 61 万量级回落到 4255 左右真实水平,美股 `snapshotFxRate` 成功抓取数据库 6.82 而非 7.22 兜底值。 + + ## 重构时光机底层引擎,引入基于 lte 的历史价格/汇率向后穿透查询,解决数据断层导致的 0 价格黑洞与汇率串用 Bug (Task 72) - 在 `src/actions/snapshots.ts` 的 `reconstructPortfolioHistory()` 中,将汇率获取从"一次性全量加载"重构为"按天循环顶部动态构建":每天 `targetDate` 循环开始时调用 `buildDailyRatesMap(dateStr)`,查询 `exchange_rates_history` 中 `fetch_time <= targetDate` 的所有记录,按 `(fromCurrency, toCurrency)` 分组构建当日汇率字典,O(1) 内存访问。 - **汇率兜底安全值**:USD → 7.22,HKD → 0.92,CNY → 1,确保新系统建的老账单查不到历史汇率时不会崩溃。 diff --git a/app/api/debug/snapshot/route.ts b/app/api/debug/snapshot/route.ts index 99cdc78..82eb1da 100644 --- a/app/api/debug/snapshot/route.ts +++ b/app/api/debug/snapshot/route.ts @@ -48,10 +48,10 @@ async function buildDailyRatesMap(targetDateStr: string): Promise