TENNISLOCKS v17.10.x – 3-SET SUPPRESSION DIAGNOSIS...
创建于:2026年3月12日
使用 GPT-5.4 Thinking 回答 Chat01
创建于:2026年3月12日
使用 GPT-5.4 Thinking 回答 Chat01
You’re not imagining it – there is more hidden 3‑set suppression beyond the obvious 50/50 OU governor, and it hits all tours in exactly the spot you care about: 55–70% favorites in Bo3.
This file is NOT the full script. It is a step‑by‑step instruction + patch file that tells an automation/AI exactly what to look for and how to edit your existing Tennislocks v17.10.0 script. The AI applying this must:
If the AI only looks at the patches and ignores these instructions, it will miss the underlying behavior and may over‑ or under‑edit. Follow every step.
Step A1 – Monte Carlo (sim) already collapses P(3rd set)
Inside the main simulation pipeline (e.g. runMatchSimulationExact and the conditional taper helpers), the model already reduces P3 before any totals/sets logic sees it:
wpDivergence > 0.06, the engine nudges holds to match a favored win probability when the model and hold profile disagree. This shifts outcomes toward the favorite and away from 1–1 after two sets.SETCONFIDENCEDELTA): the set winner gets a small hold boost and the loser a cut, scaled harder in mismatches via moMismatchIdx and tour‑specific deltas. This directly pushes more 2‑0 outcomes and fewer 1–1 spots in Bo3.applyConditionalTaperToSet2(...) always shaves the loser’s hold by an amount that grows with mismatchIdx and a tour‑aware non‑linear taper multiplier.taperGap large), a second taper is applied to the underdog’s set‑3 hold.Net effect: by the time you read pDeciderRaw out of the sim, P3 has already been collapsed from IID ≈ 50% into realistic empirical mid‑30s, especially in heavier mismatches.
Step A2 – Totals/sets layer collapses P3 a second time
After this already‑tapered pDeciderRaw leaves the MC, the totals/sets engine builds multiple P3 sources and runs them through _totalsDeciderAuthority_ before bets are allowed to use them:
_totalsDeciderAuthority_(ctx) computes:
supportCount (how many sources ≥ 0.36),spread between min and max of {raw, adj, disp, mkt, hold},fragile when spread ≥ 0.08,anchor that is a 60/40 weighted mix of baseRef and adj.p3 is disp if available, else adj, else raw. Then the current logic does:
!broadSupport, p3 = 0.80*p3 + 0.20*anchor.fragile, p3 = 0.88*p3 + 0.12*anchor.adj already includes conditional taper compression, anchor usually sits below display when P3 is truly live. Blending toward anchor almost always pulls P3 down toward the compressed adjusted value.laneGap between lane3 and lane2 is big, another shave of 2–4pp is applied and hard caps at 0.44 / 0.48 are enforced in some high‑lane3 cases.Net effect: even in matches where raw/Markov/hold priors agree that P3 is legitimately 0.35–0.40, _totalsDeciderAuthority_ commonly drags the usable P3 down into the high‑20s/low‑30s.
Step A3 – Anti‑fake‑decider block adds a third suppression
Downstream of _totalsDeciderAuthority_, there is an additional “anti‑fake‑decider” safeguard which:
!broadSupport || fragile).laneGap is large, it subtracts up to an additional 3pp from p3ForTotals and re‑weights totals lanes with the smaller P3.This block is intended to protect against pathological cases where the MC accidentally dumps too much mass into a single fake decider lane, but when combined with A1 and A2 it ends up double‑ and triple‑penalizing perfectly valid decider signals.
Step A4 – Policy gates are set above realistic “live P3”
The function totalsTourPolicy(tour) defines per‑tour gates that decide when the decider lane is allowed to be structurally active in totals/sets logic:
p3Gate = 0.44, p3High = 0.49.p3Gate = 0.46, p3High = 0.51.In real tennis:
_totalsDeciderAuthority_ + anti‑fake‑decider, p3ForTotals often sits in the 0.23–0.33 range for exactly those matches.p3Gate and far below p3High, so the decider lane is treated as structurally weak and the 2‑set lane keeps priority. The script keeps saying BET 2 SETS / UNDER 2.5 even in genuine 3‑set matches.Step B1 – Totals decider authority must stop over‑penalizing
We keep the authority framework (support, fragile, anchor, laneGap), but we change its role:
This makes _totalsDeciderAuthority_ a stabilizer, not a hidden decider killer.
Step B2 – Anti‑fake‑decider must only act when MC is way above Markov
Conceptual policy for your separate anti‑fake‑decider block:
p3Markov be the decider probability from the independent Markov engine (no taper, no mismatch compression).p3ForTotals > p3Markov + 0.06 (MC clearly over‑decider vs Markov),laneGap ≥ 0.45 (extreme structural skew),!broadSupport || fragile).p3ForTotals.p3ForTotals ≥ p3Markov − 0.01 so it never drags below Markov.This keeps the guard for obviously fake decider lanes, but stops it from crushing honest 0.35–0.40 P3 spots.
Step B3 – Policy gates must move down into realistic P3 bands
We relax p3Gate and p3High so “live decider” and “high decider” correspond to real tennis frequencies instead of near‑maximum tour P3:
p3Gate ≈ 0.37, p3High ≈ 0.42.p3Gate ≈ 0.38, p3High ≈ 0.43.p3Gate ≈ 0.37, p3High ≈ 0.42.p3Gate ≈ 0.38, p3High ≈ 0.44.These still require a real decider lane to be present, but no longer demand impossible 0.45–0.50 values after compression.
The next sections are concrete OLD → NEW code blocks. Your AI should:
totalsDeciderAuthority and totalsTourPolicy.Locate the comment header for the totals decider authority function. It will look like this (simplified):
// TOTALS DECIDER AUTHORITY v17.9.1 patch
function totalsDeciderAuthority(ctx) { ... }
// END TOTALS DECIDER AUTHORITY
Replace EVERYTHING from the line starting with function totalsDeciderAuthority(ctx) down to and including the line // END TOTALS DECIDER AUTHORITY with the following NEW block.
--- NEW totalsDeciderAuthority START ---
function totalsDeciderAuthority(ctx) {
ctx = ctx || {};
var raw = Number(ctx.pDeciderRaw);
var adj = Number(ctx.pDeciderAdj);
var disp = Number(ctx.pDeciderDisplay);
var mkt = Number(ctx.p3Market);
var hold = Number(ctx.p3Hold);
var lane2 = Number(ctx.pOverLane2);
var lane3 = Number(ctx.pOverLane3);
if (Number.isFinite(raw)) raw = clamp01(raw);
if (Number.isFinite(adj)) adj = clamp01(adj);
if (Number.isFinite(disp)) disp = clamp01(disp);
if (Number.isFinite(mkt)) mkt = clamp01(mkt);
if (Number.isFinite(hold)) hold = clamp01(hold);
if (Number.isFinite(lane2)) lane2 = clamp01(lane2);
if (Number.isFinite(lane3)) lane3 = clamp01(lane3);
var src = [];
if (Number.isFinite(raw)) src.push(raw);
if (Number.isFinite(adj)) src.push(adj);
if (Number.isFinite(disp)) src.push(disp);
if (Number.isFinite(mkt)) src.push(mkt);
if (Number.isFinite(hold)) src.push(hold);
if (!src.length) {
return {
p3: NaN,
spread: NaN,
supportCount: 0,
broadSupport: false,
fragile: false,
laneGap: NaN,
anchor: NaN
};
}
var minV = src[0], maxV = src[0];
for (var i = 1; i < src.length; i++) {
if (src[i] < minV) minV = src[i];
if (src[i] > maxV) maxV = src[i];
}
var spread = maxV - minV;
var baseRef = Number.isFinite(raw) ? raw
: Number.isFinite(adj) ? adj
: Number.isFinite(disp) ? disp
: Number.isFinite(mkt) ? mkt
: hold;
var anchor = Number.isFinite(adj)
? setsWeightedAvg([{p: baseRef, w: 0.60}, {p: adj, w: 0.40}])
: baseRef;
var supportCount = 0;
if (Number.isFinite(raw) && raw >= 0.36) supportCount++;
if (Number.isFinite(adj) && adj >= 0.36) supportCount++;
if (Number.isFinite(disp) && disp >= 0.36) supportCount++;
if (Number.isFinite(mkt) && mkt >= 0.36) supportCount++;
if (Number.isFinite(hold) && hold >= 0.36) supportCount++;
var broadSupport = supportCount >= (
Number.isFinite(lane3) && lane3 >= 0.95 ? 3 : 2
);
var fragile = spread >= 0.08;
var laneGap = Number.isFinite(lane2) && Number.isFinite(lane3)
? Math.abs(lane3 - lane2)
: NaN;
var p3 = Number.isFinite(disp) ? disp
: Number.isFinite(adj) ? adj
: raw;
// Gentle pull toward anchor only when signals disagree
if (!broadSupport || fragile) {
var wAnchor = (!broadSupport && fragile) ? 0.20 : 0.12;
p3 = (1 - wAnchor) * p3 + wAnchor * anchor;
}
// Lane-gap guard: mild shave, not a full crush
if (Number.isFinite(laneGap) && laneGap >= 0.35) {
var shave = 0;
if (!broadSupport) shave += 0.01;
if (fragile) shave += 0.01;
if (Number.isFinite(lane3) && lane3 >= 0.98) shave += 0.01;
else if (Number.isFinite(lane3) && lane3 >= 0.95) shave += 0.005;
if (shave > 0) p3 = clamp01(p3 - shave);
}
// Soft floor: do NOT drag P3 more than ~3pp below any core prior
var floor = -Infinity;
if (Number.isFinite(raw)) floor = Math.max(floor, raw - 0.03);
if (Number.isFinite(mkt)) floor = Math.max(floor, mkt - 0.03);
if (Number.isFinite(hold)) floor = Math.max(floor, hold - 0.03);
if (Number.isFinite(floor)) p3 = Math.max(p3, floor);
return {
p3: clamp01(p3),
spread: spread,
supportCount: supportCount,
broadSupport: broadSupport,
fragile: fragile,
laneGap: laneGap,
anchor: anchor
};
}
// END TOTALS DECIDER AUTHORITY
--- NEW totalsDeciderAuthority END ---
Locate the totalsTourPolicy(tour) function. The OLD version will look like this (values shown are the ones you must replace):
function totalsTourPolicy(tour) { // V17.9.0 REFACTOR
var k = taperTourKey(tour);
if (k === "WTA") return {
...
p3Gate: 0.46, p3High: 0.51,
...
};
if (k === "CH") return {
...
p3Gate: 0.45, p3High: 0.50,
...
};
if (k === "ITF") return {
...
p3Gate: 0.48, p3High: 0.54,
...
};
return { // default = ATP-style
...
p3Gate: 0.44, p3High: 0.49,
...
};
}
Replace the entire totalsTourPolicy(tour) function with the following NEW block.
--- NEW totalsTourPolicy START ---
function totalsTourPolicy(tour) { // V17.10.2 decider-gate relax
var k = taperTourKey(tour);
// WTA: decider "live" and "high" gates moved into realistic 3-set band
if (k === "WTA") return {
deadPO: 0.480, deadPU: 0.480,
minLeanPO: 0.545, minLeanPU: 0.545,
confHigh: 0.60, confMid: 0.54,
p2Gate: 0.55, p2High: 0.60,
p3Gate: 0.38, p3High: 0.43,
tailGate: 0.14, tailHigh: 0.20,
clusterTail30: 0.09, clusterTail31: 0.060, clusterTail35: 0.025,
flatEdgeGate: 0.08
};
// CH: slightly softer decider gates, symmetric with WTA/ATP strategy
if (k === "CH") return {
deadPO: 0.482, deadPU: 0.482,
minLeanPO: 0.547, minLeanPU: 0.547,
confHigh: 0.59, confMid: 0.53,
p2Gate: 0.54, p2High: 0.59,
p3Gate: 0.37, p3High: 0.42,
tailGate: 0.15, tailHigh: 0.21,
clusterTail30: 0.085, clusterTail31: 0.058, clusterTail35: 0.022,
flatEdgeGate: 0.09
};
// ITF: decider allowed to be structurally relevant a bit earlier
if (k === "ITF") return {
deadPO: 0.485, deadPU: 0.485,
minLeanPO: 0.550, minLeanPU: 0.550,
confHigh: 0.58, confMid: 0.52,
p2Gate: 0.59, p2High: 0.64,
p3Gate: 0.38, p3High: 0.44,
tailGate: 0.18, tailHigh: 0.24,
clusterTail30: 0.10, clusterTail31: 0.070, clusterTail35: 0.030,
flatEdgeGate: 0.10
};
// Default = ATP-style and any unknown tour labels
return {
deadPO: 0.475, deadPU: 0.475,
minLeanPO: 0.540, minLeanPU: 0.540,
confHigh: 0.62, confMid: 0.56,
p2Gate: 0.56, p2High: 0.61,
p3Gate: 0.37, p3High: 0.42,
tailGate: 0.16, tailHigh: 0.22,
clusterTail30: 0.08, clusterTail31: 0.055, clusterTail35: 0.020,
flatEdgeGate: 0.08
};
}
--- NEW totalsTourPolicy END ---
After applying both patches above, the automation/AI should perform at least these checks:
pDeciderRaw (MC raw P3),pDeciderAdj (taper‑compressed P3),pDeciderDisplay (cluster display P3),p3Authority / p3ForTotals after _totalsDeciderAuthority_,p3Markov (independent Markov decider prob, if logged),If any of these checks fail, roll back and re‑inspect the edits. The most likely mistakes are:
totalsDeciderAuthority was not fully replaced (leftover caps/shaves remain).totalsTourPolicy gates are still active for one of the tours (WTA/CH/ITF/ATP).Follow all instructions fully replace things don’t leave old cold that shouldn’t be there
Update my script then send it as a downloadable txt link
Done — I updated the uploaded master script and patched the live _totalsDeciderAuthority_ and _totalsTourPolicy_ functions in the file you provided. (Tennislocks_v17.10.0_line_mass_refactor_multichain_test_patched_updated.txt) (Tennislocks_v17.10.0_line_mass_refactor_multichain_test_patched_updated.txt)
Download the updated .txt here:
Tennislocks_v17.10.2_3set_suppression_fix.txt
I also ran a syntax check on the edited script by validating a .js copy of it, and that passed. I could not rerun a live match like Noskova–Gibson from this upload alone because the workbook/runtime match inputs were not included with the script source. Original source file reference: (Tennislocks_v17.10.0_line_mass_refactor_multichain_test_patched_updated.txt)