Good, let’s wire it in. This patch adds: 1) A mul...

Créé le : 11 mars 2026

Répondu en utilisant GPT-5.4 Thinking par Chat01

Question

Tennislocks_v17.10.0_line_mass_refactor.txt

Good, let’s wire it in. This patch adds:

  1. A multi‑chain totals diagnostic around runMatchSimulationExact
  2. A regime flag stored on totCtx
  3. A small hook in getTotalsConfLabel so FRAGILE/SWINGY regimes cap confidence instead of flipping sides

All patches target Tennislocks_v17.10.0_line_mass_refactor.txt.[1]


Patch 0 – Safety

Before editing, duplicate your current script tab in Apps Script (so you can roll back):

  • Copy 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]


Patch 1 – Multi‑chain totals diagnostic

1A. Find the main totals sim block

Search for this exact block (you already have it):[1]

js
var 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.

1B. Immediately after var totSim = simTotals;, insert the multi‑chain diagnostic

Paste 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]


Patch 2 – Regime flag (stable / swingy / fragile)

Now we attach a volatility / regime label using:

  • Pre‑sim match volatility matchVolatility (you already compute this from volA, volB).[1]
  • Totals tails (tail31, tail35).[1]
  • Decider probability (p3setAdj/p3setRaw).[1]
  • Multi‑chain disagreement from mcDiag above.[1]

2A. Find the tail-visibility block

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.)

2B. Immediately after that block, insert the regime calculation

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.


Patch 3 – Store regime info on totCtx

Later in the totals block you already build totCtx like this:[1]

js
var 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]

js
regime: 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]


Patch 4 – Use regime to cap totals confidence

Now we let the regime hook into the existing totals confidence ladder, only by downgrading confidence, never by flipping the side.

4A. Find getTotalsConfLabel

Search 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) { ... }

4B. Insert regime caps near the other volatility/tail caps

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:

  • STABLE: unchanged behavior.
  • SWINGY: HIGH gets knocked to MID when overall risk is already elevated.
  • FRAGILE: HIGH → MID, and very high‑risk FRAGILE cases are forced to LOW tier (which your existing policy already interprets conservatively).[1]

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]


Patch 5 – (Optional) print regime in the preview text

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]

js
if (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:

  • Multi‑chain sampling around the existing totals engine
  • A stable/swingy/fragile regime flag grounded in both pre‑sim volatility and between‑chain disagreement
  • Confidence tiers that automatically back off when the sim is noisy, without touching your central Monte Carlo totals distribution

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

Réflexion

I’m opening the uploaded script, locating the totals section and getTotalsConfLabel, then I’ll wire the four patches in and return the edited .txt file.

Thought for 11s

Python

python
print("Inspecting the uploaded script and patch anchors...")

Réflexion

bash -lc cp /mnt/data/Tennislocks_v17.10.0_line_mass_refactor_multichain_test.txt /tmp/tl_patch_check.js && node --check /tmp/tl_patch_check.js >/tmp/nodecheck2.out 2>&1; ec=?;cat/tmp/nodecheck2.out;exit?; cat /tmp/nodecheck2.out; exit ec

Thought for 3m 58s

Réponse

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:

  • Multi-chain totals diagnostic around the existing totals sim
  • regime / regimeIndex + MC spread fields on _totCtx
  • Regime-based confidence caps in getTotalsConfLabel
  • Optional preview line printing totals regime summary

Partager cette Q&R