{"record":{"id":"7cb9bc36162f7069","repo":"HKUDS/Vibe-Trading","slug":"performance-must-be-2-d-got-shape-matrix-shape","errorCode":null,"errorMessage":"performance must be 2-D, got shape {matrix.shape}","messagePattern":"performance must be 2-D, got shape (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/multipletesting.py","lineNumber":483,"sourceCode":"        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\"\n        )\n\n    used_rows = subset_size * n_splits\n    dropped = n_rows - used_rows\n    trimmed = matrix[:used_rows]","sourceCodeStart":465,"sourceCodeEnd":501,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/multipletesting.py#L465-L501","documentation":"probability_of_backtest_overfitting converts the performance input to a 2-D (rows x strategies) matrix via pandas and requires exactly two dimensions. Passing a flat list of returns, a 1-D array, or a 3-D structure yields a matrix whose ndim != 2, which the CSCV splitting logic cannot index, so it raises this error naming the offending shape.","triggerScenarios":"Passing a 1-D list/array of returns for a single strategy, or a stacked 3-D array (trials x strategies x metrics), or a dict whose DataFrame conversion collapses to one dimension.","commonSituations":"Running PBO for a single strategy while prototyping; passing a list-of-lists-of-lists of per-trade returns; converting from a numpy tensor or xarray object that keeps extra dimensions.","solutions":["Reshape to (n_rows, n_strategies): np.asarray(performance).reshape(len(performance), -1) or pass a wide DataFrame with one column per strategy.","If you truly have one strategy, PBO is undefined (see the >= 2 strategies error) — add competitors or skip the analysis.","Check performance.squeeze() to drop accidental singleton dimensions before calling."],"exampleFix":"# before\npbo = probability_of_backtest_overfitting(returns_1d, n_splits=16)  # raises\n\n# after\nwide = np.asarray(returns_list_of_strategies).T  # shape (rows, strategies)\npbo = probability_of_backtest_overfitting(wide, n_splits=16)","handlingStrategy":"type-guard","validationCode":"perf = np.asarray(performance)\nif perf.ndim != 2:\n    perf = perf.reshape(-1, perf.shape[-1]) if perf.ndim == 1 else perf.squeeze()","typeGuard":"def is_2d_performance(p) -> bool:\n    return np.asarray(p).ndim == 2","tryCatchPattern":"try:\n    pbo = probability_of_backtest_overfitting(performance, n_splits)\nexcept ValueError as e:\n    if 'must be 2-D' in str(e):\n        pbo = probability_of_backtest_overfitting(np.asarray(performance).reshape(-1, 1 if np.asarray(performance).ndim == 1 else -1).T, n_splits)\n    else:\n        raise","preventionTips":["Always build a (rows, strategies) wide DataFrame at the pipeline boundary.","Assert .ndim == 2 in your data loader.","Document the expected orientation in config comments."],"tags":["backtesting","cscv","shape-validation","numpy"],"backgroundTag":"invalid-array-shape","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}