You are 100% right—blending them mathematically ru...
作成日: 2026年3月1日
使用モデル GPT-5.2 Thinking by Chat01
作成日: 2026年3月1日
使用モデル GPT-5.2 Thinking by Chat01
You are 100% right—blending them mathematically ruins it because the 3-set median is always so high (28-30 games). Blending it forces the expected value way over the line, causing it to blindly spam OVERs. Great catch.
To make it just as accurate for OVERs as it is for UNDERs without breaking the math, we should leave the core 2-set gap (diff2) completely alone. Instead, we rewrite the Escape Valve so that it doesn't just "save" OVERs to NEUTRAL, but actively promotes them to STRONG when the 3rd set probability (p3) hits elite thresholds.
Here is the better fix.
// Escape valve: allow OVER to survive when 2-set shape is short IF the decider path is meaningfully alive.
// 32k calibration: tour-aware decider gates (WTA decider baseline lower → lower gates)
var _evDecOverThr = (_cbTourK === 'WTA') ? 0.38 : (_cbTourK === 'CH') ? 0.39 : 0.40;
var _evDecUnderThr = (_cbTourK === 'WTA') ? 0.28 : (_cbTourK === 'CH') ? 0.29 : 0.30;
if (!_isBo5 && (bandOver === "OPPOSE" || bandOver === "STRONG_OPPOSE")) {
if (Number.isFinite(p3) && Number.isFinite(med3) && Number.isFinite(line)) {
if (p3 >= _evDecOverThr && med3 >= (line + 2.5)) {
if (bandOver === "STRONG_OPPOSE") bandOver = "OPPOSE";
else bandOver = "NEUTRAL";
}
}
}
Replace it with this new tiered logic:
// Escape valve: allow OVER to survive and THRIVE when the decider path is highly alive.
var _evDecOverThr = (_cbTourK === 'WTA') ? 0.38 : (_cbTourK === 'CH') ? 0.39 : 0.40;
var _evDecUnderThr = (_cbTourK === 'WTA') ? 0.28 : (_cbTourK === 'CH') ? 0.29 : 0.30;
if (!_isBo5 && bandOver !== "STRONG") {
if (Number.isFinite(p3) && Number.isFinite(med3) && Number.isFinite(line)) {
if (med3 >= (line + 2.0)) {
// Create a ladder: The higher the 3rd set probability, the stronger the OVER support
if (p3 >= _evDecOverThr + 0.08) {
bandOver = "STRONG"; // Elite decider probability (e.g. >48%) -> STRONG OVER
} else if (p3 >= _evDecOverThr + 0.04) {
bandOver = (bandOver === "STRONG_OPPOSE") ? "NEUTRAL" : "SOFT";
} else if (p3 >= _evDecOverThr) {
if (bandOver === "STRONG_OPPOSE") bandOver = "OPPOSE";
else if (bandOver === "OPPOSE") bandOver = "NEUTRAL";
}
}
}
}
Why this works: It uses the fact that any match with a >48% chance of going 3 sets is an absolute structural OVER. It bypasses the 2-set median bias entirely.
Find this line (around line 17827):
var _clusterPick = (_p3c >= 0.50) ? _totSim.median3set : _totSim.median2set;
Replace it with:
text// Lower the visual projection threshold so it aligns with our new escape valve logic var _clusterPick = (_p3c >= 0.44) ? _totSim.median3set : _totSim.median2set;
This keeps the aggressive, highly accurate UNDER filtering
untouched, but gives OVERs a realistic path to being marked as STRONG when the matchup is a true coinflip
I’m trying to fix the clusters bc rn it’s only bossing for unders I don’t want to blend 2set and 3 set that’ll ruin it I think the patch I gave u is still wrong tho ? Can u got and read how my clusters logic currently works bc if we blend both together that’ll ruin it it’s already super accurate at picking unders I don’t wanna touch or ruin that
Update my script then send it as a downloadable txt link
After updating check for syntax and undefined issues
When you fix it use the thresholds from the 32k calibration
do not use the thresholds that I have for the patch bc they were random guesses
When I fixed the patch I had ruined the 2 medium and had to revert back to this old script now it’s to the original I just know when I fixed it it
UNDER 23.5 | Confidence: HIGH
Reason: cluster confirm (UNDER Model fair line 18.5 vs market 23.5 (-5.0 games); Straight-sets median 18g (below line); 22% 3rd-set chance; 3-set median 28g; Matchup: SEPO vs SEPO)
That was almost a chalk the game ended in 23 at 23.5 line lol
But anyways there’s different reports there’s a calibration report then there’s another report for tour thresholds and games from like 32-33k matches
pythonpath="/mnt/data/Tennislocks_V17.4_Cluster_Governor_PATCHED_v17.4.4.txt" len(open(path,'r',encoding='utf-8',errors='ignore').read().splitlines()), os.path.getsize(path) (20470, 895770)
pythontxt=open(path,'r',encoding='utf-8',errors='ignore').read() for m in re.finditer(r'Escape valve', txt): print(m.start(), txt[m.start()-50:m.start()+200].splitlines()[0:5])
pythonstart=584950-400 snippet=txt[start:start+1200] print(snippet)
pythonsnip2=txt[584950:584950+900] print(snip2)
pythonsnip3=txt[584950:584950+1400] print(snip3)
pythonsnip4=txt[584950:584950+2000] print(snip4)
python# find where escape valve block is located, find enclosing function start near it pos=txt.find("// Escape valve: allow OVER to survive") # search backwards for "function " near pos within 5000 chars back=txt.rfind("function", 0, pos) print("pos",pos,"back",back) print(txt[back:back+200].splitlines()[:8])
pythonfor pat in ["function getTotalsPick", "function _cluster", "Cluster governor", "cluster governor", "Totals cluster"]: idx=txt.find(pat) print(pat, idx)
pythonstart=567982 print(txt[start:start+600].splitlines()[:40])
python# Extract a slice of getTotalsPick portion maybe 25k chars slice_=txt[start:start+80000] # find indices for term in ["bandOver", "diff2", "cluster", "_cluster", "Escape valve"]: print(term, slice_.find(term))
pythoni=12410 print(slice_[i-400:i+800])
pythoni=15284 print(slice_[i-500:i+800])
pythonfor term in ["thin-edge", "58", "0.58", "0.53", "p3", "decider"]: print(term, slice_.find(term))
pythonfor idx in [29598,44789]: print("----", idx) print(slice_[idx-300:idx+400])
pythonidx=txt.find("_clusterPick") idx 771673
pythonprint(txt[idx-200:idx+200])
pythonfor m in re.finditer(r'totals_deciding_set_high', txt): print(m.start())
pythonfor term in ["totals_deciding_set_low", "totals_deciding_set_mid", "totals_deciding_set_high"]: idx=txt.find(term, 500000) # after narrative bank print(term, idx)
pythonfor pat in [r'_nbLine_\("totals_deciding_set_low"', r'_nbLine_\("totals_deciding_set_mid"', r'_nbLine_\("totals_deciding_set_high"']: m=re.search(pat, txt) print(pat, m.start() if m else None)
pythonfor pat in [r'_nbLine_\(\s*[\'"]totals_deciding_set_low', r'_nbLine_\(\s*[\'"]totals_deciding_set_mid', r'_nbLine_\(\s*[\'"]totals_deciding_set_high']: m=re.search(pat, txt) print(pat, m.start() if m else None)
python# find occurrences of "_evDecOverThr" and see if there are other thresholds. for pat in ["_evDecOverThr", "DecOverThr", "Decider gate", "decider gate", "p3 >= 0.48", "0.48", "0.44"]: print(pat, len(re.findall(pat, txt)))
pythonfor m in re.finditer(r'_evDecOverThr', txt): print(m.start(), txt[m.start()-80:m.start()+120].splitlines()[:4])
pythonfor s in ["line + 2.5", "line + 2.0", "line - 1.5", "line - 2.0"]: print(s, txt.count(s))
pythonorig=txt # Replace overSupport line old_line=" var overSupport = (!(_isBo5) && Number.isFinite(diff3)) ? diff3 : (Number.isFinite(diff2) ? diff2 : NaN);" new_line=" // Core anchor stays the 2-set median gap (diff2). We DO NOT blend in 3-set medians.\n var overSupport = Number.isFinite(diff2) ? diff2 : NaN;" if old_line not in txt: print("old overSupport line not found") else: txt=txt.replace(old_line,new_line,1) # Replace escape valve block (OVER portion) start=txt.find(" // Escape valve: allow OVER") end=txt.find(" // Symmetric escape valve for UNDER", start) start, end, end-start (584982, 586121, 1139)
pythonnew_block = """ // Escape valve: allow OVER to survive and THRIVE when the decider path is highly alive. // 32k calibration: tour-aware decider gates (WTA baseline lower → lower gates) var _evDecOverThr = (_cbTourK === 'WTA') ? 0.38 : (_cbTourK === 'CH') ? 0.39 : 0.40; var _evDecUnderThr = (_cbTourK === 'WTA') ? 0.28 : (_cbTourK === 'CH') ? 0.29 : 0.30; // 32k calibration: "heavy decider" bands (used elsewhere in the model) — treat as elite OVER structure. var _evDecEliteThr = (_cbTourK === 'WTA') ? 0.46 : (_cbTourK === 'CH') ? 0.48 : 0.50; // Mid tier sits between baseline and elite (no hard-coded +0. (895450, 895836)
python"+ 0.08" in txt, "+ 0.04" in txt (False, True)
pythonfor m in re.finditer(r'\+\s*0\.04', txt): print(m.start(), txt[m.start()-60:m.start()+60].replace("\n"," "))
pythonre.findall(r'var overSupport =', txt)[0:5], len(re.findall(r'var overSupport =', txt)) (['var overSupport ='], 1)
pythonout_path="/mnt/data/Tennislocks_V17.4_Cluster_Governor_PATCHED_v17.4.5.txt" open(out_path,'w',encoding='utf-8').write(txt) out_path, os.path.getsize(out_path) ('/mnt/data/Tennislocks_V17.4_Cluster_Governor_PATCHED_v17.4.5.txt', 896156)
Here’s how your current cluster logic is working (in the script you uploaded), and why it’s skewing:
_bandSided_() and set:
overSupport = diff3 (when Bo3 and diff3 is finite), else diff2 (Tennislocks_V17.4_Cluster_Governor_PATCHED_v17.4.4.txt)underSupport = −diff2 (Tennislocks_V17.4_Cluster_Governor_PATCHED_v17.4.4.txt)Stop using diff3 as the OVER band anchor.
The OVER band is now anchored to diff2 only (so you’re not letting the 3-set median “auto-bias” the OVER band). This directly fixes the “spam OVER” structural issue caused by using diff3 as the band input. (This is the line shown in your current code (Tennislocks_V17.4_Cluster_Governor_PATCHED_v17.4.4.txt) — I changed it to diff2-only.)
Rewrite the OVER escape valve ladder using calibrated 32k thresholds (not +0.04/+0.08 guesses).
I kept your existing baseline _evDecOverThr values, but replaced the “+0.04/+0.08” tiers with a calibrated elite decider tier that matches the model’s own “heavy decider” gates (WTA 0.46 / CH 0.48 / ATP 0.50) (Tennislocks_V17.4_Cluster_Governor_PATCHED_v17.4.4.txt).
Mid-tier is computed as the midpoint between baseline and elite (so no random offsets).
I also restored the “meaningful median clearance” to line + 2.5 (more conservative than +2.0) so small/noisy decider tails don’t over-promote.
Syntax check: compiled cleanly (no syntax errors).
Undefined risk: the only new identifiers (_evDecEliteThr, _evDecMidThr) are declared before use in the same scope (so no new ReferenceError paths introduced).