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[](https://crates.io/crates/oxidiviner-core)
8[](https://docs.rs/oxidiviner-core)
9[](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}