← back to Ken
Ken: fix resolver 270x portfolio over-credit — settle idempotently (UPDATE ... WHERE status='open' RETURNING, credit only on winning the flip) + re-entrancy guard so duplicate intervals (5121/9388) + slow passes can't double-count. Real bets follow paper winners, so clean rollups matter.
e7e3c8c22537fe134faec5a2df20c6651fc3226c · 2026-08-03 09:16:02 -0700 · steve@designerwallcoverings.com
Files touched
Diff
commit e7e3c8c22537fe134faec5a2df20c6651fc3226c
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Mon Aug 3 09:16:02 2026 -0700
Ken: fix resolver 270x portfolio over-credit — settle idempotently (UPDATE ... WHERE status='open' RETURNING, credit only on winning the flip) + re-entrancy guard so duplicate intervals (5121/9388) + slow passes can't double-count. Real bets follow paper winners, so clean rollups matter.
---
kalshi-dash/server.js | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/kalshi-dash/server.js b/kalshi-dash/server.js
index d2a2692..e092fed 100644
--- a/kalshi-dash/server.js
+++ b/kalshi-dash/server.js
@@ -8912,7 +8912,10 @@ async function capturePortfolioSnapshots() {
}
// Resolve portfolio trades — HYPER MODE: quick take-profit/stop-loss + aging out
+let _resolveInFlight = false;
async function resolvePortfolioTrades() {
+ if (_resolveInFlight) return; // guard: a slow pass + duplicate intervals must not overlap
+ _resolveInFlight = true;
try {
// Check ALL open trades (no minimum age for hyper-trading)
const { rows: openTrades } = await kenQ(`
@@ -8968,10 +8971,17 @@ async function resolvePortfolioTrades() {
if (!status) continue; // Hold position
- await kenQ(`
+ // IDEMPOTENT SETTLE (fix 2026-08-03): only THIS pass may credit the portfolio,
+ // and only if it is the one that flips the trade open→settled. `AND status='open'`
+ // + RETURNING makes the flip atomic; if a concurrent/re-entrant pass already
+ // settled this trade, rowCount is 0 and we skip crediting — preventing the
+ // ~270x portfolio over-credit that came from the same trade being counted many times.
+ const flip = await kenQ(`
UPDATE ken_portfolio_trades SET status = $1, exit_price_cents = $2, pnl_cents = $3, resolved_at = NOW()
- WHERE id = $4
+ WHERE id = $4 AND status = 'open'
+ RETURNING id
`, [status, currentPrice, pnl, trade.id]);
+ if (flip.rowCount === 0) continue; // lost the race — already settled elsewhere, do NOT double-credit
// Update portfolio running totals
const streakVal = status === 'won' ? 1 : status === 'lost' ? -1 : 0;
@@ -9017,6 +9027,8 @@ async function resolvePortfolioTrades() {
if (openTrades.length > 0) console.log(`[Ken/Portfolio] Resolved ${openTrades.length} trades across portfolios`);
} catch (e) {
console.error('[Ken/Portfolio] Resolution error:', e.message);
+ } finally {
+ _resolveInFlight = false;
}
}
← b9768ef Ken v2 GO-LIVE wiring: live orders follow profitable-winner
·
back to Ken
·
auto-save: 2026-08-03T09:22:36 (1 files) — kalshi-dash/src/L fc471b8 →