{"record":{"id":"5e49dba96d782d59","repo":"HKUDS/Vibe-Trading","slug":"n-splits-must-be-an-even-number-4-got-n-split","errorCode":null,"errorMessage":"n_splits must be an even number >= 4, got {n_splits}","messagePattern":"n_splits must be an even number >= 4, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/multipletesting.py","lineNumber":478,"sourceCode":"        performance: Returns per strategy, rows as observations and columns as\n            strategies. A DataFrame's column labels are preserved only for the\n            caller's convenience; this function returns no per-strategy output.\n        n_splits: Number of subsets. Must be even and at least 4; the number of\n            combinations is ``C(n_splits, n_splits/2)``, so 16 gives 12,870.\n        ddof: Delta degrees of freedom for the Sharpe standard deviation,\n            forwarded to :func:`sharpe_ratio`.\n\n    Returns:\n        A :class:`CSCVResult`.\n\n    Raises:\n        ValueError: If ``n_splits`` is odd or below 4, if fewer than 2\n            strategies are supplied (a rank needs competitors), if the sample\n            cannot give each subset at least 2 rows, or if every strategy has\n            zero variance so no Sharpe is defined.\n    \"\"\"\n    if n_splits < 4 or n_splits % 2 != 0:\n        raise ValueError(f\"n_splits must be an even number >= 4, got {n_splits}\")\n\n    frame = pd.DataFrame(performance)\n    matrix = frame.to_numpy(dtype=float)\n    if matrix.ndim != 2:\n        raise ValueError(f\"performance must be 2-D, got shape {matrix.shape}\")\n\n    n_rows, n_strategies = matrix.shape\n    if n_strategies < 2:\n        raise ValueError(\n            f\"CSCV ranks strategies against each other and needs at least 2, \"\n            f\"got {n_strategies}\"\n        )\n\n    subset_size = n_rows // n_splits\n    if subset_size < 2:\n        raise ValueError(\n            f\"{n_rows} rows split {n_splits} ways gives {subset_size} row(s) per \"\n            \"subset; each subset needs at least 2 for a Sharpe\"","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/multipletesting.py#L460-L496","documentation":"probability_of_backtest_overfitting implements CSCV, which needs the return series split into an even number n_splits >= 4 of blocks so they can be recombined into symmetric in-sample/out-of-sample halves. An odd or too-small count would break the combinatorial pairing logic and bias the PBO estimate, so it is rejected up front.","triggerScenarios":"Calling probability_of_backtest_overfitting(performance, n_splits=3) or n_splits=5 (odd), or n_splits=2 (below the minimum of 4).","commonSituations":"Copying n_splits from a K-fold cross-validation config (often 3, 5, or 10) where odd values are the norm; exposing n_splits as a user-tunable knob in a backtest UI without documenting the parity constraint.","solutions":["Use an even value >= 4, e.g. n_splits=16 as in Bailey et al.'s CSCV paper.","Validate/round the config value at load time: n_splits = max(4, 2 * (n_splits // 2)).","Document the even-and->=4 constraint next to any user-facing parameter."],"exampleFix":"# before\nresult = probability_of_backtest_overfitting(perf, n_splits=5)\n\n# after\nresult = probability_of_backtest_overfitting(perf, n_splits=16)","handlingStrategy":"validation","validationCode":"n_splits = int(n_splits)\nif n_splits < 4 or n_splits % 2:\n    n_splits = max(4, 2 * (n_splits // 2))  # snap to nearest valid even >= 4","typeGuard":"def valid_cscv_splits(n: int) -> bool:\n    return isinstance(n, int) and n >= 4 and n % 2 == 0","tryCatchPattern":"try:\n    pbo = probability_of_backtest_overfitting(perf, n_splits)\nexcept ValueError as e:\n    if 'n_splits' in str(e):\n        pbo = probability_of_backtest_overfitting(perf, n_splits=16)  # safe default\n    else:\n        raise","preventionTips":["Keep one CSCV_CONFIG with a validated n_splits (e.g. 16) reused everywhere.","Do not copy K-fold defaults (5) into CSCV code.","Assert n_rows >= 2 * n_splits (ideally much more) before calling."],"tags":["backtesting","cscv","pbo","input-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}