Good, let’s wire it in. This patch adds: 1) A mul...
Criado em: 11 de março de 2026
Respondido usando GPT-5.4 Thinking por Chat01
Criado em: 11 de março de 2026
Respondido usando GPT-5.4 Thinking por Chat01
Good, let’s wire it in. This patch adds:
runMatchSimulationExactregime flag stored on totCtxgetTotalsConfLabel so FRAGILE/SWINGY regimes cap confidence instead of flipping sidesAll patches target Tennislocks_v17.10.0_line_mass_refactor.txt.[1]
Before editing, duplicate your current script tab in Apps Script (so you can roll back):
Tennislocks_v17.10.0_line_mass_refactor.txt to e.g. Tennislocks_v17.10.0_line_mass_refactor_multichain_test.All edits below are inside MATCHPREVIEW(...) in the totals section that already calls runMatchSimulationExact for totals.[1]
Search for this exact block (you already have it):[1]
jsvar simTotals = null; try { simTotals = runMatchSimulationExact( simHA, simHB, bestOf, tlTotalsIters, marketLine, tourLabel, { totalsOnly: true, mmcLite: false, volA: volA, volB: volB, winProbA: taperSeedA, winProbB: taperSeedB, sgWinRateA: statsA ? (Number.isFinite(statsA.sgWinRate) ? statsA.sgWinRate : 0.5) : 0.5, sgWinRateB: statsB ? (Number.isFinite(statsB.sgWinRate) ? statsB.sgWinRate : 0.5) : 0.5, timeBudgetMs: Math.max(5000, tlRem() - 8000), timeCheckEvery: 200, minRuns: 2500 } ); } catch (e) { simTotals = null; } if (!simTotals || !simTotals.gamesList || !simTotals.gamesList.length) { // Fallback still totals-only - keeps preview running if the high-N pass errors simTotals = runMatchSimulationExact( simHA, simHB, bestOf, tlPropIters, marketLine, tourLabel, { totalsOnly: true, mmcLite: false, volA: volA, volB: volB, winProbA: taperSeedA, winProbB: taperSeedB, sgWinRateA: statsA ? (Number.isFinite(statsA.sgWinRate) ? statsA.sgWinRate : 0.5) : 0.5, sgWinRateB: statsB ? (Number.isFinite(statsB.sgWinRate) ? statsB.sgWinRate : 0.5) : 0.5, timeBudgetMs: Math.max(4000, tlRem() - 5000), timeCheckEvery: 200, minRuns: 1500 } ); } var totSim = simTotals;
Do not change this block.
var totSim = simTotals;, insert the multi‑chain diagnosticPaste this block right after var totSim = simTotals;:[1]
js// MONTE CARLO MULTI-CHAIN DIAGNOSTIC v1.0 // Runs several shorter independent totals sims to gauge stability of the totals distribution. // Results are stored on totCtx later as mcChains, mcAvgSpread, mcOverSpread, mcP3Spread, mcDirFlip. var mcDiag = { chains: 0, avgSpread: 0, overSpread: 0, p3Spread: 0, dirFlip: false }; try { // Only run when we have a valid primary sim and a sensible market line var mLineMC = Number(marketLine); if (totSim && totSim.gamesList && totSim.gamesList.length && Number.isFinite(mLineMC)) { var chainCount = 3; // Keep total work under the main totals budget: split tlTotalsIters across chains var perChainIters = Math.max(900, Math.floor(tlTotalsIters / 4)); var chains = []; for (var ci = 0; ci < chainCount; ci++) { // Stop early if time is nearly exhausted if (typeof tlRem === 'function' && tlRem() < 6000) break; var simC = runMatchSimulationExact( simHA, simHB, bestOf, perChainIters, marketLine, tourLabel, { totalsOnly: true, mmcLite: false, volA: volA, volB: volB, winProbA: taperSeedA, winProbB: taperSeedB, sgWinRateA: statsA ? (Number.isFinite(statsA.sgWinRate) ? statsA.sgWinRate : 0.5) : 0.5, sgWinRateB: statsB ? (Number.isFinite(statsB.sgWinRate) ? statsB.sgWinRate : 0.5) : 0.5, timeBudgetMs: Math.max(1500, typeof tlRem === 'function' ? tlRem() - 3000 : 1500), timeCheckEvery: 200, minRuns: Math.min(perChainIters, 1800) } ); if (!simC || !simC.gamesList || !simC.gamesList.length) continue; var glC = simC.gamesList.slice(); glC.sort(function (a, b) { return a - b; }); var coreC = totalsCoreFromSortedList(glC); var avgC = coreC && Number.isFinite(coreC.mean) ? coreC.mean : (Number.isFinite(simC.avgTotalGames) ? simC.avgTotalGames : NaN); var medC = coreC && Number.isFinite(coreC.median) ? coreC.median : (Number.isFinite(simC.medianGames) ? simC.medianGames : NaN); var ouC = probOUFromSortedTotals(glC, mLineMC); var pOverC = ouC && Number.isFinite(ouC.pOver) ? ouC.pOver : 0.5; var p3C = NaN; if (Number.isFinite(simC.pDeciderAdj)) p3C = simC.pDeciderAdj; else if (Number.isFinite(simC.pDeciderRaw)) p3C = simC.pDeciderRaw; else if (Number.isFinite(simC.pDecider)) p3C = simC.pDecider; chains.push({ avg: avgC, med: medC, pOver: pOverC, p3: p3C }); } mcDiag.chains = chains.length; if (chains.length >= 2) { var minAvg = 1e9, maxAvg = -1e9; var minPO = 1.0, maxPO = 0.0; var minP3 = 1.0, maxP3 = 0.0; var above = 0, below = 0; for (var j = 0; j < chains.length; j++) { var r = chains[j]; if (Number.isFinite(r.avg)) { if (r.avg < minAvg) minAvg = r.avg; if (r.avg > maxAvg) maxAvg = r.avg; } if (Number.isFinite(r.pOver)) { if (r.pOver < minPO) minPO = r.pOver; if (r.pOver > maxPO) maxPO = r.pOver; if (r.pOver > 0.5005) above++; else if (r.pOver < 0.4995) below++; } if (Number.isFinite(r.p3)) { if (r.p3 < minP3) minP3 = r.p3; if (r.p3 > maxP3) maxP3 = r.p3; } } if (maxAvg < minAvg) { minAvg = maxAvg = 0; } if (maxPO < minPO) { minPO = maxPO = 0.5; } if (maxP3 < minP3) { minP3 = maxP3 = 0; } mcDiag.avgSpread = maxAvg - minAvg; // games spread across chains mcDiag.overSpread = maxPO - minPO; // P(Over) spread across chains mcDiag.p3Spread = maxP3 - minP3; // decider-prob spread across chains mcDiag.dirFlip = (above > 0 && below > 0); // some chains Over, some Under } } } catch (mcE) { mcDiag.chains = 0; }
This does not change totSim or any existing totals math; it only measures between‑chain disagreement.[1]
Now we attach a volatility / regime label using:
matchVolatility (you already compute this from volA, volB).[1]tail31, tail35).[1]p3setAdj/p3setRaw).[1]mcDiag above.[1]Search for this block:[1]
js// DEBUG Total Games Tail Visibility var tail25 = NaN, tail30 = NaN, tail31 = NaN, tail35 = NaN; if (totSim && gamesListAdj && gamesListAdj.length) { var over25 = 0, over30 = 0, over31 = 0, over35 = 0, maxT = -1, minT = 999; for (var i = 0; i < gamesListAdj.length; i++) { var t = gamesListAdj[i]; if (t >= 25) over25++; if (t >= 30) over30++; if (t >= 31) over31++; if (t >= 35) over35++; if (t > maxT) maxT = t; if (t < minT) minT = t; } tail25 = over25 / totSim.totalRuns; tail30 = over30 / totSim.totalRuns; tail31 = over31 / totSim.totalRuns; tail35 = over35 / totSim.totalRuns; var p25 = 100 * tail25; var p30 = 100 * tail30; var p31 = 100 * tail31; var p35 = 100 * tail35; } else { // Raw sim summary do not use to flip side. }
(Your exact comments may differ slightly; use the DEBUG Total Games Tail Visibility anchor.)
Right after the tail block (before the “Raw sim summary do not use to flip side” / totRawMedian logic), insert:[1]
js// REGIME CLASSIFICATION v1.0 (STABLE / SWINGY / FRAGILE) // Combines intrinsic volatility, tails, and multi-chain disagreement into a single regime label. var regimeLabel = 'STABLE'; var regimeIndex = 0; try { // Intrinsic volatility from pre-sim inputs (volA, volB -> matchVolatility) var baseVol = Number.isFinite(matchVolatility) ? matchVolatility : 0; // Decider-driven swinginess var p3setUse = NaN; var p3setRaw = Number.isFinite(deciderPct) ? deciderPct : NaN; if (Number.isFinite(p3setAdj)) p3setUse = p3setAdj; else if (Number.isFinite(p3setRaw)) p3setUse = p3setRaw; else p3setUse = 0; // Right-tail weight from totals var tailStrong = 0; if (Number.isFinite(tail31)) tailStrong = Math.max(tailStrong, tail31); if (Number.isFinite(tail35)) tailStrong = Math.max(tailStrong, tail35); // Base regime index: 0..~0.7 regimeIndex = 0.45 * baseVol + 0.30 * p3setUse + 0.25 * tailStrong; // Multi-chain instability from mcDiag (if available) var avgSpread = Number.isFinite(mcDiag.avgSpread) ? mcDiag.avgSpread : 0; var overSpread = Number.isFinite(mcDiag.overSpread) ? mcDiag.overSpread : 0; var p3Spread = Number.isFinite(mcDiag.p3Spread) ? mcDiag.p3Spread : 0; var dirFlip = !!mcDiag.dirFlip; // Default thresholds: // - STABLE: low intrinsic volatility and tight multi-chain agreement // - SWINGY: mid volatility or mild between-chain spread // - FRAGILE: large spread in EV / OU / p3 or direction flips between chains if (mcDiag.chains >= 2) { var strongInstability = avgSpread > 1.8 || // games spread > 1.8 overSpread > 0.06 || // P(Over) spread > 6pp p3Spread > 0.10 || // decider spread > 10pp dirFlip; // some chains Over, some Under var mildInstability = avgSpread > 1.0 || overSpread > 0.035 || p3Spread > 0.06; if (strongInstability) { regimeLabel = 'FRAGILE'; } else if (regimeIndex >= 0.28 || mildInstability) { regimeLabel = 'SWINGY'; } else { regimeLabel = 'STABLE'; } } else { // Single-chain fallback: classify from intrinsic volatility only if (regimeIndex >= 0.32) regimeLabel = 'FRAGILE'; else if (regimeIndex >= 0.22) regimeLabel = 'SWINGY'; else regimeLabel = 'STABLE'; } } catch (regE) { regimeLabel = 'STABLE'; regimeIndex = 0; } // Attach regime diagnostics to totals context later (after totCtx is created).
We’ll actually attach this to totCtx in the next tiny patch.
totCtxLater in the totals block you already build totCtx like this:[1]
jsvar totCtx = { line: marketLine, mean: totAdjMean, median: totAdjMedian, mode: totMode, tourLabel: tourLabel, pDecider: totSim.pDecider, pDeciderRaw: Number.isFinite(totSim.pDeciderRaw) ? totSim.pDeciderRaw : NaN, pDeciderAdj: Number.isFinite(totSim.pDeciderAdj) ? totSim.pDeciderAdj : NaN, ... };
(Your actual object has many more fields; use the var totCtx = { anchor that immediately follows var totMode ... in the snippet.)[1]
Inside that object literal, add these four fields anywhere convenient (style-wise, I’d place them near other risk/volatility fields, but order does not matter):[1]
jsregime: regimeLabel, regimeIndex: regimeIndex, mcChains: mcDiag.chains, mcAvgSpread: mcDiag.avgSpread, mcOverSpread: mcDiag.overSpread, mcP3Spread: mcDiag.p3Spread, mcDirFlip: !!mcDiag.dirFlip,
Be sure commas are correct (no trailing comma after the last property).
This makes the regime and multi‑chain metrics available to both the pick engine and your audit output.[1]
Now we let the regime hook into the existing totals confidence ladder, only by downgrading confidence, never by flipping the side.
getTotalsConfLabelSearch for function getTotalsConfLabel(prob, pick, ctx).[1]
Inside it, you’ll see a block of tier logic with comments like:
js// ---- Bimodal totals Stats vs Elo cap ---- if (bimodalTotals) { if (tier === HIGH) tier = MID; else if (tier !== PASS) tier = LOW; } ... // ---- LEAN pick caps ---- if (pick.indexOf('LEAN') === 0) { ... }
Right after the “Bimodal totals Stats vs Elo cap” / adverse-tail / moderate-disagree section (before the // ---- LEAN pick caps ---- block), insert:[1]
js// ---- Regime caps (multi-chain + volatility) ---- if (ctx && typeof ctx.regime === 'string') { var r = String(ctx.regime).toUpperCase(); if (r === 'FRAGILE') { // Fragile regimes are never HIGH confidence. if (tier === HIGH) tier = MID; // If risk is already high, force LOW. if (risk >= 0.80 && tier !== PASS) tier = LOW; } else if (r === 'SWINGY') { // Swingy regimes can be MID, but not HIGH unless everything else is very clean. if (tier === HIGH && risk >= 0.62) tier = MID; } }
This means:
No side is ever flipped; only confidence tier is constrained, which is consistent with the role of multi‑chain diagnostics in the physics paper (improve exploration / trustworthiness, not change the central estimate).[2][3]
If you want the preview text itself to mention the regime, you can add a single line where you already push totals diagnostics.
Near the bottom of MATCHPREVIEW, after you build totDisplayMeta and totPriceText, add:[1]
jsif (totCtx && typeof totCtx.regime === 'string') { lines.push( 'Totals regime ' + String(totCtx.regime).toUpperCase() + ' (mc chains ' + (Number.isFinite(totCtx.mcChains) ? totCtx.mcChains : 0) + ', avg spread ' + (Number.isFinite(totCtx.mcAvgSpread) ? totCtx.mcAvgSpread.toFixed(2) : 'na') + ' g)' ); }
This is purely cosmetic and safe to skip if you’d rather keep output minimal.[1]
If you paste these patches in exactly those spots, you’ll have:
If you want, next step I can tighten thresholds (e.g. for ITF vs ATP) based on your logs once you’ve run a few dozen matches.
Update my script then send it as a downloadable txt link
pythonprint("Inspecting the uploaded script and patch anchors...")
Done — I patched a duplicated copy and left your original upload untouched.
Download the updated script here: Tennislocks_v17.10.0_line_mass_refactor_multichain_test.txt
Included:
regime / regimeIndex + MC spread fields on _totCtxgetTotalsConfLabel