1use ringkernel_core::message::{CorrelationId, MessageId};
15use ringkernel_derive::RingMessage;
16use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
17
18#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
26#[message(type_id = 400)]
27#[archive(check_bytes)]
28pub struct UpdateVolatilityRing {
29 #[message(id)]
31 pub id: MessageId,
32 #[message(correlation)]
34 pub correlation_id: CorrelationId,
35 pub asset_id: u64,
37 pub return_value: i64,
39 pub timestamp: u64,
41}
42
43impl UpdateVolatilityRing {
44 pub fn new(asset_id: u64, return_value: f64, timestamp: u64) -> Self {
46 Self {
47 id: MessageId::generate(),
48 correlation_id: CorrelationId::generate(),
49 asset_id,
50 return_value: (return_value * 100_000_000.0) as i64,
51 timestamp,
52 }
53 }
54
55 pub fn return_f64(&self) -> f64 {
57 self.return_value as f64 / 100_000_000.0
58 }
59}
60
61#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
65#[message(type_id = 401)]
66#[archive(check_bytes)]
67pub struct UpdateVolatilityResponse {
68 #[message(correlation)]
70 pub correlation_id: CorrelationId,
71 pub asset_id: u64,
73 pub current_volatility: i64,
75 pub current_variance: i64,
77 pub observation_count: u32,
79}
80
81impl UpdateVolatilityResponse {
82 pub fn volatility_f64(&self) -> f64 {
84 self.current_volatility as f64 / 100_000_000.0
85 }
86
87 pub fn variance_f64(&self) -> f64 {
89 self.current_variance as f64 / 100_000_000.0
90 }
91}
92
93#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
97#[message(type_id = 402)]
98#[archive(check_bytes)]
99pub struct QueryVolatilityRing {
100 #[message(id)]
102 pub id: MessageId,
103 #[message(correlation)]
105 pub correlation_id: CorrelationId,
106 pub asset_id: u64,
108 pub horizon: u32,
110}
111
112impl QueryVolatilityRing {
113 pub fn new(asset_id: u64, horizon: u32) -> Self {
115 Self {
116 id: MessageId::generate(),
117 correlation_id: CorrelationId::generate(),
118 asset_id,
119 horizon,
120 }
121 }
122}
123
124#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
128#[message(type_id = 403)]
129#[archive(check_bytes)]
130pub struct QueryVolatilityResponse {
131 #[message(correlation)]
133 pub correlation_id: CorrelationId,
134 pub asset_id: u64,
136 pub current_volatility: i64,
138 pub forecast: [i64; 10],
140 pub forecast_count: u8,
142 pub persistence: i32, }
145
146impl QueryVolatilityResponse {
147 pub fn forecast_f64(&self) -> Vec<f64> {
149 self.forecast[..self.forecast_count as usize]
150 .iter()
151 .map(|&v| v as f64 / 100_000_000.0)
152 .collect()
153 }
154}
155
156#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
160#[message(type_id = 404)]
161#[archive(check_bytes)]
162pub struct VolatilitySpikeAlert {
163 #[message(id)]
165 pub id: MessageId,
166 pub asset_id: u64,
168 pub current_volatility: i64,
170 pub previous_volatility: i64,
172 pub spike_ratio: i32,
174 pub timestamp: u64,
176 pub severity: u8,
178}
179
180#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
188#[message(type_id = 405)]
189#[archive(check_bytes)]
190pub struct UpdateEWMAVolatilityRing {
191 #[message(id)]
193 pub id: MessageId,
194 #[message(correlation)]
196 pub correlation_id: CorrelationId,
197 pub asset_id: u64,
199 pub return_value: i64,
201 pub lambda: u16,
204 pub timestamp: u64,
206}
207
208impl UpdateEWMAVolatilityRing {
209 pub fn new(asset_id: u64, return_value: f64, timestamp: u64) -> Self {
211 Self {
212 id: MessageId::generate(),
213 correlation_id: CorrelationId::generate(),
214 asset_id,
215 return_value: (return_value * 100_000_000.0) as i64,
216 lambda: 9400, timestamp,
218 }
219 }
220
221 pub fn with_lambda(asset_id: u64, return_value: f64, lambda: f64, timestamp: u64) -> Self {
223 Self {
224 id: MessageId::generate(),
225 correlation_id: CorrelationId::generate(),
226 asset_id,
227 return_value: (return_value * 100_000_000.0) as i64,
228 lambda: (lambda * 10000.0) as u16,
229 timestamp,
230 }
231 }
232
233 pub fn lambda_f64(&self) -> f64 {
235 self.lambda as f64 / 10000.0
236 }
237}
238
239#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
243#[message(type_id = 406)]
244#[archive(check_bytes)]
245pub struct UpdateEWMAVolatilityResponse {
246 #[message(correlation)]
248 pub correlation_id: CorrelationId,
249 pub asset_id: u64,
251 pub ewma_variance: i64,
253 pub ewma_volatility: i64,
255}
256
257#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
265#[message(type_id = 407)]
266#[archive(check_bytes)]
267pub struct SetGARCHCoefficientsRing {
268 #[message(id)]
270 pub id: MessageId,
271 #[message(correlation)]
273 pub correlation_id: CorrelationId,
274 pub asset_id: u64,
276 pub omega: i64,
278 pub alpha: i32,
280 pub beta: i32,
282}
283
284impl SetGARCHCoefficientsRing {
285 pub fn new(asset_id: u64, omega: f64, alpha: f64, beta: f64) -> Self {
287 Self {
288 id: MessageId::generate(),
289 correlation_id: CorrelationId::generate(),
290 asset_id,
291 omega: (omega * 100_000_000.0) as i64,
292 alpha: (alpha * 10000.0) as i32,
293 beta: (beta * 10000.0) as i32,
294 }
295 }
296}
297
298#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
302#[message(type_id = 408)]
303#[archive(check_bytes)]
304pub struct SetGARCHCoefficientsResponse {
305 #[message(correlation)]
307 pub correlation_id: CorrelationId,
308 pub asset_id: u64,
310 pub success: bool,
312 pub long_run_variance: i64,
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn test_update_volatility_ring() {
322 let msg = UpdateVolatilityRing::new(1, 0.015, 1234567890);
323 assert_eq!(msg.asset_id, 1);
324 assert_eq!(msg.return_value, 1_500_000); assert!((msg.return_f64() - 0.015).abs() < 1e-10);
326 }
327
328 #[test]
329 fn test_query_volatility_ring() {
330 let msg = QueryVolatilityRing::new(42, 10);
331 assert_eq!(msg.asset_id, 42);
332 assert_eq!(msg.horizon, 10);
333 }
334
335 #[test]
336 fn test_ewma_with_lambda() {
337 let msg = UpdateEWMAVolatilityRing::with_lambda(1, 0.02, 0.97, 1234567890);
338 assert_eq!(msg.lambda, 9700);
339 assert!((msg.lambda_f64() - 0.97).abs() < 1e-4);
340 }
341
342 #[test]
343 fn test_garch_coefficients() {
344 let msg = SetGARCHCoefficientsRing::new(1, 0.00001, 0.1, 0.85);
345 assert_eq!(msg.asset_id, 1);
346 assert_eq!(msg.alpha, 1000); assert_eq!(msg.beta, 8500); }
349}