Coverage for src / monte_neo / monte_carlo / cscv.py: 100%
19 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-28 16:27 +0200
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-28 16:27 +0200
1"""Combinatorial Symmetric Cross-Validation (CSCV) module.
3Used to detect backtest overfitting and calculate Probability of Overfitting (PBO).
4"""
6from __future__ import annotations
8from typing import TYPE_CHECKING, Any
10import pandas as pd
12from monte_neo.utils.logger import get_logger
14if TYPE_CHECKING:
15 from monte_neo.indicators.base import BaseIndicator
16 from monte_neo.metrics.calculator import MetricsCalculator
18logger = get_logger(__name__)
20class CSCVAnalyzer:
21 """Analyzer for Combinatorial Symmetric Cross-Validation."""
23 def analyze(
24 self,
25 indicator: BaseIndicator,
26 data: pd.DataFrame,
27 metrics_calc: MetricsCalculator,
28 n_segments: int = 10,
29 ) -> dict[str, Any]:
30 """Run simplified CSCV analysis.
32 Args:
33 indicator: Indicator to test.
34 data: OHLCV data.
35 metrics_calc: Metrics calculator.
36 n_segments: Number of segments to split data into.
38 Returns:
39 Dictionary with PBO and robustness metrics.
40 """
41 if len(data) < n_segments:
42 return {"error": "Insufficient data for CSCV"}
44 segment_size = len(data) // n_segments
45 segments = [data.iloc[i*segment_size : (i+1)*segment_size] for i in range(n_segments)]
47 results = []
49 for i in range(n_segments):
50 test_data = segments[i]
51 # signals = indicator.generate_signals(test_data)
52 # metrics = metrics_calc.calculate_all(test_data, signals)
53 # results.append(metrics.get("sharpe_ratio", 0))
55 # Use indicator's own signal generation which might be accelerated
56 signals = indicator.generate_signals(test_data)
57 metrics = metrics_calc.calculate_all(test_data, signals)
58 results.append(metrics.get("sharpe_ratio", 0))
60 pbo = sum(1 for r in results if r < 0) / len(results) if results else 1.0
62 return {
63 "pbo": pbo,
64 "segment_scores": results,
65 "is_robust": pbo < 0.2
66 }