fix(ledger): 修复汇率转换的双标幻觉,统一大盘与快照的财务聚合基准

This commit is contained in:
2026-05-01 05:52:09 +08:00
parent 52a94a9ffa
commit b4f21e7cd6
4 changed files with 46 additions and 33 deletions
+13 -12
View File
@@ -26,13 +26,19 @@ function getTodayInShanghai(): string {
export async function recordDailySnapshot() {
const positions = await getPortfolioPositions();
// 统一使用 engine 输出的 marketValueCny / accumulatedPnlCny
const totalValueCny = positions.reduce(
(sum, pos) => sum.plus(pos.cnyValue || '0'),
(sum, pos) => sum.plus(new Big(pos.marketValueCny || '0')),
new Big(0)
).toString();
// 推导真实投入本金 CNY = 市值 - 累计盈亏
const totalCostCny = positions.reduce(
(sum, pos) => sum.plus(pos.totalCostCny || '0'),
(sum, pos) => {
const mv = new Big(pos.marketValueCny || '0');
const ap = new Big(pos.accumulatedPnlCny || '0');
return sum.plus(mv.minus(ap));
},
new Big(0)
).toString();
@@ -324,21 +330,16 @@ export async function reconstructPortfolioHistory() {
const priceStr = await getEffectivePrice(assetId, currentDate);
const baseCurrency = assetBaseCurrencyMap.get(assetId) || 'USD';
let cnyPrice: string;
if (!priceStr) {
cnyPrice = convertPriceToCny(assetLatestPriceMap.get(assetId) || '0', baseCurrency);
} else {
cnyPrice = convertPriceToCny(priceStr, baseCurrency);
}
const priceStrForMetrics = priceStr || assetLatestPriceMap.get(assetId) || '0';
const metrics = calculateAssetMetrics(assetTxs, cnyPrice);
const metrics = calculateAssetMetrics(assetTxs, priceStrForMetrics);
// 获取资产对人民币的汇率
const assetFxRate = new Big(getRate(baseCurrency, 'CNY') || '1');
// marketValue 已使用 CNY 价格计算,直接取用
// totalInvested 基于原始币种价格,需乘以汇率折算为 CNY
const posValueCny = new Big(metrics.marketValue);
// 统一汇率折算边界:calculateAssetMetrics 输出全部为 Base Currency
// 必须无例外地将所有金额字段乘以 fxRate 得到 CNY
const posValueCny = new Big(metrics.marketValue).times(assetFxRate);
const posCostCny = new Big(metrics.totalInvested).times(assetFxRate);
totalValueCny = totalValueCny.plus(posValueCny);