copy-trader-mu (СПЕКА ДЛЯ РЕВЬЮ, НЕ ЗАДЕПЛОЕНО)⚠️ MONEY-PATH. Файл
/root/poly-exec/copy-loop-mu.mjs(off-box, Node, вне репо). Менять ТОЛЬКО осознанно, с тестом (--selftest/ OBSERVE-режим) и явным «да» Rick. Этот документ — план, не правка.
if (patch.close) {
p.status = 'closed'; p.closedReason = patch.close; p.closedAt = now;
// worthless-aged = resolved $0 loser → -cost. onchain-absent = sold/redeemed → last mark.
p.realizedPnl = patch.close === 'resolved-worthless-aged' ? -(Number(p.cost)||0) : (Number(p.markPnl)||0);
}
resolved-worthless-aged (лузер) → -cost ✅ корректно.resolved-onchain-absent → банкует markPnl. Для резолвнутого победителя (токен сгорел после редима) markPnl часто stale/0 → выигрыш банкуется как 0. Это и есть массовые нули (≈2200 из них — на resolved-рынках).onchain-absent неоднозначен: токен = 0 на чейне И при продаже (mirror-sell, есть proceeds в markPnl), И при редиме победителя ($1/share), И у $0-лузера. По одному балансу не отличить.
При resolved-onchain-absent определить исход рынка и считать payout, а не markPnl:
p.marketId/p.outcome:payoutNumerators(conditionId) → выиграл ли индекс исхода позиции.screener_markets.winning_outcome (демон уже ходит в наш API). Менее «честно» (наша БД), но дёшево.won = (p.outcome === winningOutcome)realized = won ? (Number(p.size) - Number(p.cost)) : -(Number(p.cost)) // size шар × $1 payout − затратыmarkPnl (proceeds продажи).node copy-loop-mu.mjs --selftest (есть в файле, dump resolved-контекста) — сверить realized на исторических позициях.pm2 restart copy-trader-mu в тихое окно, мониторить логи банков.copy-loop-mu.mjs перед правкой.