use ringkernel_derive::RingMessage;
use rkyv::{Archive, Deserialize, Serialize};
use rustkernel_core::messages::MessageId;
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 400)]
pub struct UpdatePositionRing {
pub id: MessageId,
pub asset_id: u64,
pub value_fp: i64,
pub expected_return_fp: i64,
pub volatility_fp: i64,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 401)]
pub struct UpdatePositionResponse {
pub request_id: u64,
pub asset_id: u64,
pub var_stale: bool,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 402)]
pub struct QueryVaRRing {
pub id: MessageId,
pub confidence_fp: i64,
pub holding_period: u32,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 403)]
pub struct QueryVaRResponse {
pub request_id: u64,
pub var_fp: i64,
pub es_fp: i64,
pub confidence_fp: i64,
pub holding_period: u32,
pub is_fresh: bool,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 404)]
pub struct RecalculateVaRRing {
pub id: MessageId,
pub n_simulations: u32,
pub confidence_fp: i64,
pub holding_period: u32,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 405)]
pub struct RecalculateVaRResponse {
pub request_id: u64,
pub var_fp: i64,
pub es_fp: i64,
pub compute_time_us: u64,
pub n_simulations: u32,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 460)]
pub struct K2KPositionBatch {
pub id: MessageId,
pub source_worker: u64,
pub batch_seq: u64,
pub position_count: u32,
pub asset_ids: [u64; 8],
pub values_fp: [i64; 8],
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 461)]
pub struct K2KPartialVaR {
pub id: MessageId,
pub worker_id: u64,
pub correlation_id: u64,
pub partial_var_fp: i64,
pub partial_es_fp: i64,
pub positions_processed: u32,
pub cov_contribution_fp: i64,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 462)]
pub struct K2KVaRAggregation {
pub id: MessageId,
pub correlation_id: u64,
pub expected_workers: u32,
pub workers_reported: u32,
pub aggregated_var_fp: i64,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 463)]
pub struct K2KVaRAggregationResponse {
pub correlation_id: u64,
pub complete: bool,
pub final_var_fp: i64,
pub final_es_fp: i64,
pub diversification_benefit_fp: i64,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 464)]
pub struct K2KMarketUpdate {
pub id: MessageId,
pub timestamp_us: u64,
pub asset_id: u64,
pub price_fp: i64,
pub vol_delta_fp: i64,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 465)]
pub struct K2KMarketUpdateAck {
pub request_id: u64,
pub worker_id: u64,
pub var_impact_fp: i64,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, RingMessage)]
#[archive(check_bytes)]
#[message(type_id = 466)]
pub struct K2KRiskLimitAlert {
pub id: MessageId,
pub timestamp_us: u64,
pub severity: u8,
pub current_var_fp: i64,
pub var_limit_fp: i64,
pub breach_amount_fp: i64,
pub trigger_asset_id: u64,
}
#[inline]
pub fn to_fixed_point(value: f64) -> i64 {
(value * 100_000_000.0) as i64
}
#[inline]
pub fn from_fixed_point(fp: i64) -> f64 {
fp as f64 / 100_000_000.0
}
#[inline]
pub fn to_currency_fp(value: f64) -> i64 {
(value * 100.0) as i64
}
#[inline]
pub fn from_currency_fp(fp: i64) -> f64 {
fp as f64 / 100.0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fixed_point_conversion() {
let value = 0.95;
let fp = to_fixed_point(value);
let back = from_fixed_point(fp);
assert!((value - back).abs() < 1e-8);
}
#[test]
fn test_currency_conversion() {
let value = 50000.50;
let fp = to_currency_fp(value);
let back = from_currency_fp(fp);
assert!((value - back).abs() < 0.01);
assert_eq!(fp, 5000050); }
#[test]
fn test_update_position_ring() {
let msg = UpdatePositionRing {
id: MessageId(1),
asset_id: 100,
value_fp: to_currency_fp(50000.0),
expected_return_fp: to_fixed_point(0.08),
volatility_fp: to_fixed_point(0.20),
};
assert_eq!(msg.asset_id, 100);
assert!((from_currency_fp(msg.value_fp) - 50000.0).abs() < 0.01);
}
#[test]
fn test_k2k_partial_var() {
let msg = K2KPartialVaR {
id: MessageId(2),
worker_id: 1,
correlation_id: 12345,
partial_var_fp: to_currency_fp(10000.0),
partial_es_fp: to_currency_fp(12000.0),
positions_processed: 50,
cov_contribution_fp: to_fixed_point(0.0015),
};
assert_eq!(msg.worker_id, 1);
assert_eq!(msg.positions_processed, 50);
}
#[test]
fn test_k2k_risk_limit_alert() {
let msg = K2KRiskLimitAlert {
id: MessageId(3),
timestamp_us: 1234567890,
severity: 2,
current_var_fp: to_currency_fp(1_100_000.0),
var_limit_fp: to_currency_fp(1_000_000.0),
breach_amount_fp: to_currency_fp(100_000.0),
trigger_asset_id: 42,
};
assert_eq!(msg.severity, 2);
assert!((from_currency_fp(msg.breach_amount_fp) - 100_000.0).abs() < 0.01);
}
}