File size: 2,351 Bytes
c40c447
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
"""
DTOs para casos de uso de Backtesting.
"""

from dataclasses import dataclass
from typing import List, Optional, Dict


@dataclass
class BacktestMetricsDTO:
    """DTO para métricas de backtest."""
    
    mae: float
    mape: float
    rmse: float
    mse: float
    
    def to_dict(self) -> Dict:
        """Convierte a diccionario."""
        return {
            "mae": round(self.mae, 4),
            "mape": round(self.mape, 4),
            "rmse": round(self.rmse, 4),
            "mse": round(self.mse, 4)
        }


@dataclass
class BacktestInputDTO:
    """DTO de entrada para backtest."""
    
    values: List[float]
    test_size: int
    quantile_levels: List[float]
    timestamps: Optional[List[str]] = None
    freq: str = "D"
    
    def validate(self) -> None:
        """Valida los datos de entrada."""
        if not self.values:
            raise ValueError("values no puede estar vacío")
        
        if self.test_size < 1:
            raise ValueError("test_size debe ser >= 1")
        
        if self.test_size >= len(self.values):
            raise ValueError("test_size debe ser menor que la longitud de values")
        
        train_size = len(self.values) - self.test_size
        if train_size < 3:
            raise ValueError("train_size debe ser al menos 3 puntos")
        
        if not all(0 <= q <= 1 for q in self.quantile_levels):
            raise ValueError("quantile_levels debe estar en [0, 1]")
        
        if self.timestamps and len(self.timestamps) != len(self.values):
            raise ValueError("timestamps y values deben tener la misma longitud")


@dataclass
class BacktestOutputDTO:
    """DTO de salida para backtest."""
    
    forecast_values: List[float]
    actual_values: List[float]
    errors: List[float]
    metrics: BacktestMetricsDTO
    timestamps: List[str]
    quantiles: Optional[Dict[str, List[float]]] = None
    
    def to_dict(self) -> Dict:
        """Convierte a diccionario."""
        result = {
            "forecast_values": self.forecast_values,
            "actual_values": self.actual_values,
            "errors": self.errors,
            "metrics": self.metrics.to_dict(),
            "timestamps": self.timestamps
        }
        
        if self.quantiles:
            result["quantiles"] = self.quantiles
        
        return result