diff --git a/src/position/PositionManager.js b/src/position/PositionManager.js index 2371b7d..e5b93db 100644 --- a/src/position/PositionManager.js +++ b/src/position/PositionManager.js @@ -84,6 +84,14 @@ export class PositionManager { const shareMap = new Map(dataset.map((d) => [d.code, d.shares])); const holdingMap = new Map(holdings.map((h) => [h.code, h.holdingId])); // code → holdingId const valuesMap = new Map(holdings.map((h) => [h.code, h.values ?? null])); // R-013:code → 自定义字段值 + // R-013 防御(2026-09-02 小修):展示层按该策略当前 configSchema 过滤 values 键 —— + // 字段定义收窄/删除后,行内残留的旧键(如曾与网格同款的 grid_*)不随持仓返回、不在表格显示; + // 编辑保存以过滤后值为基底 → 覆盖写回时残留键自然被清除(数据层收敛,不动写路径语义)。 + const schemaKeys = new Set((this.getStrategySchema(strategyId) ?? []).map((f) => f.key)); + const cleanValues = (raw) => { + if (!raw || typeof raw !== 'object' || schemaKeys.size === 0) return null; // 无字段定义 → 无值 + return Object.fromEntries(Object.entries(raw).filter(([k]) => schemaKeys.has(k))); + }; // R-010 Q4:批量取各 holding 的关联委托(查最后一笔成交价) const holdingIds = [...new Set(holdings.map((h) => h.holdingId).filter((x) => x != null))]; const tradesByHolding = new Map(); @@ -108,7 +116,7 @@ export class PositionManager { // getOrdersByHolding 已按 insert_ts DESC,第一条即最新 lastTradePrice = +filled[0].tradedPrice; } - return { ...p, shares, holdingId, avgPrice, lastTradePrice, values: valuesMap.get(p.code) ?? null }; // R-013 + return { ...p, shares, holdingId, avgPrice, lastTradePrice, values: cleanValues(valuesMap.get(p.code)) }; // R-013(展示层按 schema 过滤) }) .filter(Boolean); }