oxidiviner_core/
lib.rs

1/*!
2# OxiDiviner Core
3
4The foundation of the OxiDiviner time series forecasting ecosystem, providing data structures,
5interfaces, and utility traits used across all forecasting models.
6
7[![Crates.io](https://img.shields.io/crates/v/oxidiviner-core.svg)](https://crates.io/crates/oxidiviner-core)
8[![Documentation](https://docs.rs/oxidiviner-core/badge.svg)](https://docs.rs/oxidiviner-core)
9[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
11## Core Components
12
13### Data Structures
14
15* [`TimeSeriesData`] - A flexible container for time series data with timestamps
16* [`OHLCVData`] - A specialized container for financial time series (Open-High-Low-Close-Volume)
17
18### Interfaces
19
20* [`Forecaster`] - The central trait implemented by all forecasting models
21* [`ModelEvaluation`] - Common metrics for evaluating forecast accuracy 
22* [`ModelOutput`] - Standard output format for forecasts and evaluations
23
24## Usage Example
25
26```rust
27use oxidiviner_core::{TimeSeriesData, Forecaster, Result};
28use chrono::{Utc, TimeZone};
29
30// Create a custom forecasting model
31struct SimpleAverageForecast {
32    name: String,
33    values: Vec<f64>,
34}
35
36impl SimpleAverageForecast {
37    fn new() -> Self {
38        Self {
39            name: "Simple Average".to_string(),
40            values: Vec::new(),
41        }
42    }
43}
44
45impl Forecaster for SimpleAverageForecast {
46    fn name(&self) -> &str {
47        &self.name
48    }
49    
50    fn fit(&mut self, data: &TimeSeriesData) -> Result<()> {
51        self.values = data.values().to_vec();
52        Ok(())
53    }
54    
55    fn forecast(&self, horizon: usize) -> Result<Vec<f64>> {
56        if self.values.is_empty() {
57            return Err(oxidiviner_core::OxiError::NotFitted);
58        }
59        
60        // Calculate the average of all values
61        let avg = self.values.iter().sum::<f64>() / self.values.len() as f64;
62        
63        // Return the average for each point in the forecast horizon
64        Ok(vec![avg; horizon])
65    }
66    
67    fn evaluate(&self, test_data: &TimeSeriesData) -> Result<oxidiviner_core::ModelEvaluation> {
68        // Implementation would calculate various error metrics
69        // between forecasts and actual test data
70        unimplemented!()
71    }
72}
73
74fn example() -> Result<()> {
75    // Create sample time series data
76    let dates = vec![
77        Utc.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap(),
78        Utc.with_ymd_and_hms(2023, 1, 2, 0, 0, 0).unwrap(),
79        Utc.with_ymd_and_hms(2023, 1, 3, 0, 0, 0).unwrap(),
80    ];
81    let values = vec![1.0, 2.0, 3.0];
82    
83    let data = TimeSeriesData::new(dates, values)?;
84    
85    // Create our model
86    let mut model = SimpleAverageForecast::new();
87    
88    // Fit and forecast
89    model.fit(&data)?;
90    let forecast = model.forecast(2)?;
91    
92    println!("Forecast: {:?}", forecast);
93    Ok(())
94}
95*/
96
97use chrono::{DateTime, Utc};
98use serde::{Deserialize, Serialize};
99use thiserror::Error;
100
101mod data;
102mod error;
103
104// Re-export the main components
105pub use data::{OHLCVData, TimeSeriesData};
106pub use error::{OxiError, Result};
107
108/// Central trait that all forecasting models must implement
109///
110/// The Forecaster trait provides a common interface for time series models.
111/// It includes methods for fitting models to data, generating forecasts,
112/// and evaluating model performance.
113pub trait Forecaster {
114    /// Get the name of the model
115    fn name(&self) -> &str;
116    
117    /// Fit the model to training data
118    ///
119    /// # Arguments
120    ///
121    /// * `data` - The time series data to fit the model to
122    ///
123    /// # Returns
124    ///
125    /// * `Result<()>` - Success or an error if fitting fails
126    fn fit(&mut self, data: &TimeSeriesData) -> Result<()>;
127    
128    /// Generate forecasts for the specified horizon
129    ///
130    /// # Arguments
131    ///
132    /// * `horizon` - The number of future time steps to forecast
133    ///
134    /// # Returns
135    ///
136    /// * `Result<Vec<f64>>` - The forecasted values or an error
137    fn forecast(&self, horizon: usize) -> Result<Vec<f64>>;
138    
139    /// Evaluate the model on test data
140    ///
141    /// # Arguments
142    ///
143    /// * `test_data` - The time series data to evaluate against
144    ///
145    /// # Returns
146    ///
147    /// * `Result<ModelEvaluation>` - Evaluation metrics or an error
148    fn evaluate(&self, test_data: &TimeSeriesData) -> Result<ModelEvaluation>;
149    
150    /// Generate forecasts and evaluation in a standardized output format
151    ///
152    /// # Arguments
153    ///
154    /// * `horizon` - The number of future time steps to forecast
155    /// * `test_data` - Optional test data for evaluation
156    ///
157    /// # Returns
158    ///
159    /// * `Result<ModelOutput>` - Standardized output or an error
160    fn predict(&self, horizon: usize, test_data: Option<&TimeSeriesData>) -> Result<ModelOutput> {
161        // Generate forecasts
162        let forecasts = self.forecast(horizon)?;
163        
164        // If test data is provided, evaluate the model
165        let evaluation = if let Some(test_data) = test_data {
166            Some(self.evaluate(test_data)?)
167        } else {
168            None
169        };
170        
171        Ok(ModelOutput {
172            model_name: self.name().to_string(),
173            forecasts,
174            evaluation,
175        })
176    }
177}
178
179/// Model evaluation metrics
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct ModelEvaluation {
182    /// Name of the model
183    pub model_name: String,
184    /// Mean Absolute Error
185    pub mae: f64,
186    /// Mean Squared Error
187    pub mse: f64,
188    /// Root Mean Squared Error
189    pub rmse: f64,
190    /// Mean Absolute Percentage Error
191    pub mape: f64,
192    /// Symmetric Mean Absolute Percentage Error
193    pub smape: f64,
194}
195
196/// Standardized output from a forecasting model
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct ModelOutput {
199    /// Name of the model
200    pub model_name: String,
201    /// Forecasted values
202    pub forecasts: Vec<f64>,
203    /// Optional evaluation metrics (if test data was provided)
204    pub evaluation: Option<ModelEvaluation>,
205}