use crate::types::{
CreditExposure, CreditFactors, CreditRiskResult, Portfolio, PortfolioRiskResult, Sensitivity,
StressScenario, StressTestResult, VaRParams, VaRResult,
};
use rustkernel_derive::KernelMessage;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
#[message(type_id = 3000, domain = "RiskAnalytics")]
pub struct CreditRiskScoringInput {
pub factors: CreditFactors,
pub ead: f64,
pub maturity: f64,
}
impl CreditRiskScoringInput {
pub fn new(factors: CreditFactors, ead: f64, maturity: f64) -> Self {
Self {
factors,
ead,
maturity,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
#[message(type_id = 3001, domain = "RiskAnalytics")]
pub struct CreditRiskScoringOutput {
pub result: CreditRiskResult,
pub compute_time_us: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreditRiskBatchInput {
pub exposures: Vec<CreditExposure>,
}
impl CreditRiskBatchInput {
pub fn new(exposures: Vec<CreditExposure>) -> Self {
Self { exposures }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreditRiskBatchOutput {
pub results: Vec<CreditRiskResult>,
pub compute_time_us: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
#[message(type_id = 3010, domain = "RiskAnalytics")]
pub struct MonteCarloVaRInput {
pub portfolio: Portfolio,
pub params: VaRParams,
}
impl MonteCarloVaRInput {
pub fn new(portfolio: Portfolio, params: VaRParams) -> Self {
Self { portfolio, params }
}
pub fn with_defaults(portfolio: Portfolio) -> Self {
Self {
portfolio,
params: VaRParams::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
#[message(type_id = 3011, domain = "RiskAnalytics")]
pub struct MonteCarloVaROutput {
pub result: VaRResult,
pub compute_time_us: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
#[message(type_id = 3020, domain = "RiskAnalytics")]
pub struct PortfolioRiskAggregationInput {
pub portfolio: Portfolio,
pub confidence_level: f64,
pub holding_period: u32,
}
impl PortfolioRiskAggregationInput {
pub fn new(portfolio: Portfolio, confidence_level: f64, holding_period: u32) -> Self {
Self {
portfolio,
confidence_level,
holding_period,
}
}
pub fn standard(portfolio: Portfolio) -> Self {
Self {
portfolio,
confidence_level: 0.99,
holding_period: 10,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, KernelMessage)]
#[message(type_id = 3021, domain = "RiskAnalytics")]
pub struct PortfolioRiskAggregationOutput {
pub result: PortfolioRiskResult,
pub compute_time_us: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressTestingInput {
pub portfolio: Portfolio,
pub scenario: StressScenario,
pub sensitivities: Option<Vec<Sensitivity>>,
}
impl StressTestingInput {
pub fn new(portfolio: Portfolio, scenario: StressScenario) -> Self {
Self {
portfolio,
scenario,
sensitivities: None,
}
}
pub fn with_sensitivities(
portfolio: Portfolio,
scenario: StressScenario,
sensitivities: Vec<Sensitivity>,
) -> Self {
Self {
portfolio,
scenario,
sensitivities: Some(sensitivities),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressTestingOutput {
pub result: StressTestResult,
pub compute_time_us: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressTestingBatchInput {
pub portfolio: Portfolio,
pub scenarios: Vec<StressScenario>,
pub sensitivities: Option<Vec<Sensitivity>>,
}
impl StressTestingBatchInput {
pub fn new(portfolio: Portfolio, scenarios: Vec<StressScenario>) -> Self {
Self {
portfolio,
scenarios,
sensitivities: None,
}
}
pub fn with_sensitivities(
portfolio: Portfolio,
scenarios: Vec<StressScenario>,
sensitivities: Vec<Sensitivity>,
) -> Self {
Self {
portfolio,
scenarios,
sensitivities: Some(sensitivities),
}
}
pub fn standard_scenarios(portfolio: Portfolio) -> Self {
use crate::stress::StressTesting;
Self {
portfolio,
scenarios: StressTesting::standard_scenarios(),
sensitivities: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressTestingBatchOutput {
pub results: Vec<StressTestResult>,
pub worst_case: Option<StressTestResult>,
pub compute_time_us: u64,
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_portfolio() -> Portfolio {
Portfolio::new(
vec![1, 2],
vec![100_000.0, 50_000.0],
vec![0.08, 0.10],
vec![0.15, 0.20],
vec![1.0, 0.5, 0.5, 1.0],
)
}
fn create_test_factors() -> CreditFactors {
CreditFactors {
obligor_id: 1,
debt_to_income: 0.30,
loan_to_value: 0.70,
credit_utilization: 0.25,
payment_history: 85.0,
employment_years: 5.0,
recent_inquiries: 2,
delinquencies: 0,
credit_history_years: 8.0,
}
}
#[test]
fn test_credit_risk_scoring_input() {
let factors = create_test_factors();
let input = CreditRiskScoringInput::new(factors, 100_000.0, 5.0);
assert_eq!(input.ead, 100_000.0);
assert_eq!(input.maturity, 5.0);
}
#[test]
fn test_monte_carlo_var_input() {
let portfolio = create_test_portfolio();
let input = MonteCarloVaRInput::with_defaults(portfolio);
assert_eq!(input.params.confidence_level, 0.99);
}
#[test]
fn test_portfolio_risk_aggregation_input() {
let portfolio = create_test_portfolio();
let input = PortfolioRiskAggregationInput::standard(portfolio);
assert_eq!(input.confidence_level, 0.99);
assert_eq!(input.holding_period, 10);
}
#[test]
fn test_stress_testing_input() {
let portfolio = create_test_portfolio();
let scenario = StressScenario::equity_crash(-0.20);
let input = StressTestingInput::new(portfolio, scenario);
assert!(input.sensitivities.is_none());
}
}