rustkernel_temporal/
messages.rs

1//! Message types for temporal analysis kernels.
2//!
3//! Input/output message types for the `BatchKernel` trait implementations
4//! and Ring kernel messages for K2K communication.
5
6use rustkernel_derive::KernelMessage;
7use serde::{Deserialize, Serialize};
8
9use crate::types::{
10    ARIMAParams, ARIMAResult, AnomalyMethod, ChangePointMethod, ChangePointResult,
11    DecompositionResult, GARCHParams, ProphetResult, TimeSeries, TimeSeriesAnomalyResult,
12    TrendMethod, TrendResult, VolatilityResult,
13};
14
15// ============================================================================
16// ARIMA Forecast Messages
17// ============================================================================
18
19/// Input for ARIMA forecasting.
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct ARIMAForecastInput {
22    /// Input time series.
23    pub series: TimeSeries,
24    /// ARIMA(p,d,q) parameters.
25    pub params: ARIMAParams,
26    /// Forecast horizon.
27    pub horizon: usize,
28}
29
30impl ARIMAForecastInput {
31    /// Create a new ARIMA forecast input.
32    pub fn new(series: TimeSeries, params: ARIMAParams, horizon: usize) -> Self {
33        Self {
34            series,
35            params,
36            horizon,
37        }
38    }
39
40    /// Create with default ARIMA(1,1,1) parameters.
41    pub fn with_defaults(series: TimeSeries, horizon: usize) -> Self {
42        Self {
43            series,
44            params: ARIMAParams::new(1, 1, 1),
45            horizon,
46        }
47    }
48}
49
50/// Output from ARIMA forecasting.
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct ARIMAForecastOutput {
53    /// ARIMA result with coefficients, fitted values, and forecasts.
54    pub result: ARIMAResult,
55    /// Computation time in microseconds.
56    pub compute_time_us: u64,
57}
58
59// ============================================================================
60// Prophet Decomposition Messages
61// ============================================================================
62
63/// Input for Prophet-style decomposition.
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct ProphetDecompositionInput {
66    /// Input time series.
67    pub series: TimeSeries,
68    /// Seasonal period (e.g., 12 for monthly, 7 for daily).
69    pub period: Option<usize>,
70    /// Forecast horizon.
71    pub horizon: usize,
72}
73
74impl ProphetDecompositionInput {
75    /// Create a new Prophet decomposition input.
76    pub fn new(series: TimeSeries, period: Option<usize>, horizon: usize) -> Self {
77        Self {
78            series,
79            period,
80            horizon,
81        }
82    }
83
84    /// Create with a specified period.
85    pub fn with_period(series: TimeSeries, period: usize, horizon: usize) -> Self {
86        Self {
87            series,
88            period: Some(period),
89            horizon,
90        }
91    }
92
93    /// Create without seasonality.
94    pub fn without_seasonality(series: TimeSeries, horizon: usize) -> Self {
95        Self {
96            series,
97            period: None,
98            horizon,
99        }
100    }
101}
102
103/// Output from Prophet decomposition.
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct ProphetDecompositionOutput {
106    /// Prophet result with trend, seasonal, and forecast components.
107    pub result: ProphetResult,
108    /// Computation time in microseconds.
109    pub compute_time_us: u64,
110}
111
112// ============================================================================
113// Change Point Detection Messages
114// ============================================================================
115
116/// Input for change point detection.
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct ChangePointDetectionInput {
119    /// Input time series.
120    pub series: TimeSeries,
121    /// Detection method (PELT, BinarySegmentation, CUSUM).
122    pub method: ChangePointMethod,
123    /// Penalty for adding change points.
124    pub penalty: f64,
125    /// Minimum segment length.
126    pub min_segment: usize,
127}
128
129impl ChangePointDetectionInput {
130    /// Create a new change point detection input.
131    pub fn new(
132        series: TimeSeries,
133        method: ChangePointMethod,
134        penalty: f64,
135        min_segment: usize,
136    ) -> Self {
137        Self {
138            series,
139            method,
140            penalty,
141            min_segment,
142        }
143    }
144
145    /// Create with PELT method and default parameters.
146    pub fn pelt(series: TimeSeries, penalty: f64) -> Self {
147        Self {
148            series,
149            method: ChangePointMethod::PELT,
150            penalty,
151            min_segment: 10,
152        }
153    }
154
155    /// Create with Binary Segmentation method.
156    pub fn binary_segmentation(series: TimeSeries, penalty: f64) -> Self {
157        Self {
158            series,
159            method: ChangePointMethod::BinarySegmentation,
160            penalty,
161            min_segment: 10,
162        }
163    }
164
165    /// Create with CUSUM method.
166    pub fn cusum(series: TimeSeries, threshold: f64) -> Self {
167        Self {
168            series,
169            method: ChangePointMethod::CUSUM,
170            penalty: threshold,
171            min_segment: 10,
172        }
173    }
174}
175
176/// Output from change point detection.
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct ChangePointDetectionOutput {
179    /// Change point detection result.
180    pub result: ChangePointResult,
181    /// Computation time in microseconds.
182    pub compute_time_us: u64,
183}
184
185// ============================================================================
186// Time Series Anomaly Detection Messages
187// ============================================================================
188
189/// Input for time series anomaly detection.
190///
191/// Ring message type_id: 2020 (TemporalAnalysis domain)
192#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
193#[message(type_id = 2020, domain = "TemporalAnalysis")]
194pub struct TimeSeriesAnomalyDetectionInput {
195    /// Input time series.
196    pub series: TimeSeries,
197    /// Detection method.
198    pub method: AnomalyMethod,
199    /// Anomaly threshold.
200    pub threshold: f64,
201    /// Window size for moving statistics.
202    pub window: Option<usize>,
203}
204
205impl TimeSeriesAnomalyDetectionInput {
206    /// Create a new anomaly detection input.
207    pub fn new(
208        series: TimeSeries,
209        method: AnomalyMethod,
210        threshold: f64,
211        window: Option<usize>,
212    ) -> Self {
213        Self {
214            series,
215            method,
216            threshold,
217            window,
218        }
219    }
220
221    /// Create with Z-score method.
222    pub fn zscore(series: TimeSeries, threshold: f64) -> Self {
223        Self {
224            series,
225            method: AnomalyMethod::ZScore,
226            threshold,
227            window: None,
228        }
229    }
230
231    /// Create with rolling Z-score.
232    pub fn rolling_zscore(series: TimeSeries, threshold: f64, window: usize) -> Self {
233        Self {
234            series,
235            method: AnomalyMethod::ZScore,
236            threshold,
237            window: Some(window),
238        }
239    }
240
241    /// Create with IQR method.
242    pub fn iqr(series: TimeSeries, multiplier: f64) -> Self {
243        Self {
244            series,
245            method: AnomalyMethod::IQR,
246            threshold: multiplier,
247            window: None,
248        }
249    }
250
251    /// Create with moving average deviation method.
252    pub fn moving_average(series: TimeSeries, threshold: f64, window: usize) -> Self {
253        Self {
254            series,
255            method: AnomalyMethod::MovingAverageDeviation,
256            threshold,
257            window: Some(window),
258        }
259    }
260
261    /// Create with seasonal ESD method.
262    pub fn seasonal_esd(series: TimeSeries, threshold: f64, period: usize) -> Self {
263        Self {
264            series,
265            method: AnomalyMethod::SeasonalESD,
266            threshold,
267            window: Some(period),
268        }
269    }
270}
271
272/// Output from time series anomaly detection.
273///
274/// Ring message type_id: 2021 (TemporalAnalysis domain)
275#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
276#[message(type_id = 2021, domain = "TemporalAnalysis")]
277pub struct TimeSeriesAnomalyDetectionOutput {
278    /// Anomaly detection result.
279    pub result: TimeSeriesAnomalyResult,
280    /// Computation time in microseconds.
281    pub compute_time_us: u64,
282}
283
284// ============================================================================
285// Seasonal Decomposition Messages
286// ============================================================================
287
288/// Input for seasonal decomposition.
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct SeasonalDecompositionInput {
291    /// Input time series.
292    pub series: TimeSeries,
293    /// Seasonal period.
294    pub period: usize,
295    /// Use robust (median-based) estimation.
296    pub robust: bool,
297}
298
299impl SeasonalDecompositionInput {
300    /// Create a new seasonal decomposition input.
301    pub fn new(series: TimeSeries, period: usize, robust: bool) -> Self {
302        Self {
303            series,
304            period,
305            robust,
306        }
307    }
308
309    /// Create with standard (mean-based) decomposition.
310    pub fn standard(series: TimeSeries, period: usize) -> Self {
311        Self {
312            series,
313            period,
314            robust: false,
315        }
316    }
317
318    /// Create with robust (median-based) decomposition.
319    pub fn robust(series: TimeSeries, period: usize) -> Self {
320        Self {
321            series,
322            period,
323            robust: true,
324        }
325    }
326}
327
328/// Output from seasonal decomposition.
329#[derive(Debug, Clone, Serialize, Deserialize)]
330pub struct SeasonalDecompositionOutput {
331    /// Decomposition result with trend, seasonal, and residual.
332    pub result: DecompositionResult,
333    /// Computation time in microseconds.
334    pub compute_time_us: u64,
335}
336
337// ============================================================================
338// Trend Extraction Messages
339// ============================================================================
340
341/// Input for trend extraction.
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct TrendExtractionInput {
344    /// Input time series.
345    pub series: TimeSeries,
346    /// Trend extraction method.
347    pub method: TrendMethod,
348    /// Window size for moving average.
349    pub window: usize,
350}
351
352impl TrendExtractionInput {
353    /// Create a new trend extraction input.
354    pub fn new(series: TimeSeries, method: TrendMethod, window: usize) -> Self {
355        Self {
356            series,
357            method,
358            window,
359        }
360    }
361
362    /// Create with simple moving average.
363    pub fn simple_ma(series: TimeSeries, window: usize) -> Self {
364        Self {
365            series,
366            method: TrendMethod::SimpleMovingAverage,
367            window,
368        }
369    }
370
371    /// Create with exponential moving average.
372    pub fn ema(series: TimeSeries, span: usize) -> Self {
373        Self {
374            series,
375            method: TrendMethod::ExponentialMovingAverage,
376            window: span,
377        }
378    }
379
380    /// Create with centered moving average.
381    pub fn centered_ma(series: TimeSeries, window: usize) -> Self {
382        Self {
383            series,
384            method: TrendMethod::CenteredMovingAverage,
385            window,
386        }
387    }
388
389    /// Create with Lowess smoothing.
390    pub fn lowess(series: TimeSeries, bandwidth: usize) -> Self {
391        Self {
392            series,
393            method: TrendMethod::Lowess,
394            window: bandwidth,
395        }
396    }
397}
398
399/// Output from trend extraction.
400#[derive(Debug, Clone, Serialize, Deserialize)]
401pub struct TrendExtractionOutput {
402    /// Trend extraction result.
403    pub result: TrendResult,
404    /// Computation time in microseconds.
405    pub compute_time_us: u64,
406}
407
408// ============================================================================
409// Volatility Analysis Messages
410// ============================================================================
411
412/// Input for volatility analysis.
413///
414/// Ring message type_id: 2050 (TemporalAnalysis domain)
415#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
416#[message(type_id = 2050, domain = "TemporalAnalysis")]
417pub struct VolatilityAnalysisInput {
418    /// Time series of returns.
419    pub returns: TimeSeries,
420    /// GARCH(p,q) parameters.
421    pub params: GARCHParams,
422    /// Forecast horizon.
423    pub forecast_horizon: usize,
424}
425
426impl VolatilityAnalysisInput {
427    /// Create a new volatility analysis input.
428    pub fn new(returns: TimeSeries, params: GARCHParams, forecast_horizon: usize) -> Self {
429        Self {
430            returns,
431            params,
432            forecast_horizon,
433        }
434    }
435
436    /// Create with GARCH(1,1) model.
437    pub fn garch_1_1(returns: TimeSeries, forecast_horizon: usize) -> Self {
438        Self {
439            returns,
440            params: GARCHParams::new(1, 1),
441            forecast_horizon,
442        }
443    }
444
445    /// Create with custom GARCH(p,q).
446    pub fn garch(returns: TimeSeries, p: usize, q: usize, forecast_horizon: usize) -> Self {
447        Self {
448            returns,
449            params: GARCHParams::new(p, q),
450            forecast_horizon,
451        }
452    }
453}
454
455/// Output from volatility analysis.
456///
457/// Ring message type_id: 2051 (TemporalAnalysis domain)
458#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
459#[message(type_id = 2051, domain = "TemporalAnalysis")]
460pub struct VolatilityAnalysisOutput {
461    /// Volatility result with variance, volatility, and forecasts.
462    pub result: VolatilityResult,
463    /// Computation time in microseconds.
464    pub compute_time_us: u64,
465}
466
467/// Input for EWMA volatility analysis.
468///
469/// Ring message type_id: 2052 (TemporalAnalysis domain)
470#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
471#[message(type_id = 2052, domain = "TemporalAnalysis")]
472pub struct EWMAVolatilityInput {
473    /// Time series of returns.
474    pub returns: TimeSeries,
475    /// Decay factor (lambda), typically 0.94 for daily data.
476    pub lambda: f64,
477    /// Forecast horizon.
478    pub forecast_horizon: usize,
479}
480
481impl EWMAVolatilityInput {
482    /// Create a new EWMA volatility input.
483    pub fn new(returns: TimeSeries, lambda: f64, forecast_horizon: usize) -> Self {
484        Self {
485            returns,
486            lambda,
487            forecast_horizon,
488        }
489    }
490
491    /// Create with RiskMetrics lambda (0.94).
492    pub fn riskmetrics(returns: TimeSeries, forecast_horizon: usize) -> Self {
493        Self {
494            returns,
495            lambda: 0.94,
496            forecast_horizon,
497        }
498    }
499}
500
501/// Output from EWMA volatility analysis.
502///
503/// Ring message type_id: 2053 (TemporalAnalysis domain)
504#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
505#[message(type_id = 2053, domain = "TemporalAnalysis")]
506pub struct EWMAVolatilityOutput {
507    /// Volatility result.
508    pub result: VolatilityResult,
509    /// Computation time in microseconds.
510    pub compute_time_us: u64,
511}