use ringkernel_core::message::{CorrelationId, MessageId};
use ringkernel_derive::RingMessage;
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
#[message(type_id = 1100)]
#[archive(check_bytes)]
pub struct UpdateVolatilityRing {
#[message(id)]
pub id: MessageId,
#[message(correlation)]
pub correlation_id: CorrelationId,
pub asset_id: u64,
pub return_value: i64,
pub timestamp: u64,
}
impl UpdateVolatilityRing {
pub fn new(asset_id: u64, return_value: f64, timestamp: u64) -> Self {
Self {
id: MessageId::generate(),
correlation_id: CorrelationId::generate(),
asset_id,
return_value: (return_value * 100_000_000.0) as i64,
timestamp,
}
}
pub fn return_f64(&self) -> f64 {
self.return_value as f64 / 100_000_000.0
}
}
#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
#[message(type_id = 1101)]
#[archive(check_bytes)]
pub struct UpdateVolatilityResponse {
#[message(correlation)]
pub correlation_id: CorrelationId,
pub asset_id: u64,
pub current_volatility: i64,
pub current_variance: i64,
pub observation_count: u32,
}
impl UpdateVolatilityResponse {
pub fn volatility_f64(&self) -> f64 {
self.current_volatility as f64 / 100_000_000.0
}
pub fn variance_f64(&self) -> f64 {
self.current_variance as f64 / 100_000_000.0
}
}
#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
#[message(type_id = 1102)]
#[archive(check_bytes)]
pub struct QueryVolatilityRing {
#[message(id)]
pub id: MessageId,
#[message(correlation)]
pub correlation_id: CorrelationId,
pub asset_id: u64,
pub horizon: u32,
}
impl QueryVolatilityRing {
pub fn new(asset_id: u64, horizon: u32) -> Self {
Self {
id: MessageId::generate(),
correlation_id: CorrelationId::generate(),
asset_id,
horizon,
}
}
}
#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
#[message(type_id = 1103)]
#[archive(check_bytes)]
pub struct QueryVolatilityResponse {
#[message(correlation)]
pub correlation_id: CorrelationId,
pub asset_id: u64,
pub current_volatility: i64,
pub forecast: [i64; 10],
pub forecast_count: u8,
pub persistence: i32, }
impl QueryVolatilityResponse {
pub fn forecast_f64(&self) -> Vec<f64> {
self.forecast[..self.forecast_count as usize]
.iter()
.map(|&v| v as f64 / 100_000_000.0)
.collect()
}
}
#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
#[message(type_id = 1104)]
#[archive(check_bytes)]
pub struct VolatilitySpikeAlert {
#[message(id)]
pub id: MessageId,
pub asset_id: u64,
pub current_volatility: i64,
pub previous_volatility: i64,
pub spike_ratio: i32,
pub timestamp: u64,
pub severity: u8,
}
#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
#[message(type_id = 1105)]
#[archive(check_bytes)]
pub struct UpdateEWMAVolatilityRing {
#[message(id)]
pub id: MessageId,
#[message(correlation)]
pub correlation_id: CorrelationId,
pub asset_id: u64,
pub return_value: i64,
pub lambda: u16,
pub timestamp: u64,
}
impl UpdateEWMAVolatilityRing {
pub fn new(asset_id: u64, return_value: f64, timestamp: u64) -> Self {
Self {
id: MessageId::generate(),
correlation_id: CorrelationId::generate(),
asset_id,
return_value: (return_value * 100_000_000.0) as i64,
lambda: 9400, timestamp,
}
}
pub fn with_lambda(asset_id: u64, return_value: f64, lambda: f64, timestamp: u64) -> Self {
Self {
id: MessageId::generate(),
correlation_id: CorrelationId::generate(),
asset_id,
return_value: (return_value * 100_000_000.0) as i64,
lambda: (lambda * 10000.0) as u16,
timestamp,
}
}
pub fn lambda_f64(&self) -> f64 {
self.lambda as f64 / 10000.0
}
}
#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
#[message(type_id = 1106)]
#[archive(check_bytes)]
pub struct UpdateEWMAVolatilityResponse {
#[message(correlation)]
pub correlation_id: CorrelationId,
pub asset_id: u64,
pub ewma_variance: i64,
pub ewma_volatility: i64,
}
#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
#[message(type_id = 1107)]
#[archive(check_bytes)]
pub struct SetGARCHCoefficientsRing {
#[message(id)]
pub id: MessageId,
#[message(correlation)]
pub correlation_id: CorrelationId,
pub asset_id: u64,
pub omega: i64,
pub alpha: i32,
pub beta: i32,
}
impl SetGARCHCoefficientsRing {
pub fn new(asset_id: u64, omega: f64, alpha: f64, beta: f64) -> Self {
Self {
id: MessageId::generate(),
correlation_id: CorrelationId::generate(),
asset_id,
omega: (omega * 100_000_000.0) as i64,
alpha: (alpha * 10000.0) as i32,
beta: (beta * 10000.0) as i32,
}
}
}
#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
#[message(type_id = 1108)]
#[archive(check_bytes)]
pub struct SetGARCHCoefficientsResponse {
#[message(correlation)]
pub correlation_id: CorrelationId,
pub asset_id: u64,
pub success: bool,
pub long_run_variance: i64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_update_volatility_ring() {
let msg = UpdateVolatilityRing::new(1, 0.015, 1234567890);
assert_eq!(msg.asset_id, 1);
assert_eq!(msg.return_value, 1_500_000); assert!((msg.return_f64() - 0.015).abs() < 1e-10);
}
#[test]
fn test_query_volatility_ring() {
let msg = QueryVolatilityRing::new(42, 10);
assert_eq!(msg.asset_id, 42);
assert_eq!(msg.horizon, 10);
}
#[test]
fn test_ewma_with_lambda() {
let msg = UpdateEWMAVolatilityRing::with_lambda(1, 0.02, 0.97, 1234567890);
assert_eq!(msg.lambda, 9700);
assert!((msg.lambda_f64() - 0.97).abs() < 1e-4);
}
#[test]
fn test_garch_coefficients() {
let msg = SetGARCHCoefficientsRing::new(1, 0.00001, 0.1, 0.85);
assert_eq!(msg.asset_id, 1);
assert_eq!(msg.alpha, 1000); assert_eq!(msg.beta, 8500); }
}