use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Portfolio {
pub asset_ids: Vec<u64>,
pub values: Vec<f64>,
pub expected_returns: Vec<f64>,
pub volatilities: Vec<f64>,
pub correlation_matrix: Vec<f64>,
}
impl Portfolio {
pub fn new(
asset_ids: Vec<u64>,
values: Vec<f64>,
expected_returns: Vec<f64>,
volatilities: Vec<f64>,
correlation_matrix: Vec<f64>,
) -> Self {
Self {
asset_ids,
values,
expected_returns,
volatilities,
correlation_matrix,
}
}
pub fn n_assets(&self) -> usize {
self.asset_ids.len()
}
pub fn total_value(&self) -> f64 {
self.values.iter().sum()
}
pub fn weights(&self) -> Vec<f64> {
let total = self.total_value();
if total.abs() < 1e-10 {
vec![0.0; self.n_assets()]
} else {
self.values.iter().map(|v| v / total).collect()
}
}
pub fn correlation(&self, i: usize, j: usize) -> f64 {
let n = self.n_assets();
if i >= n || j >= n {
return 0.0;
}
self.correlation_matrix[i * n + j]
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreditExposure {
pub obligor_id: u64,
pub ead: f64,
pub pd: f64,
pub lgd: f64,
pub maturity: f64,
pub rating: u8,
}
impl CreditExposure {
pub fn new(obligor_id: u64, ead: f64, pd: f64, lgd: f64, maturity: f64, rating: u8) -> Self {
Self {
obligor_id,
ead,
pd,
lgd,
maturity,
rating,
}
}
pub fn expected_loss(&self) -> f64 {
self.pd * self.lgd * self.ead
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreditFactors {
pub obligor_id: u64,
pub debt_to_income: f64,
pub loan_to_value: f64,
pub credit_utilization: f64,
pub payment_history: f64,
pub employment_years: f64,
pub recent_inquiries: u32,
pub delinquencies: u32,
pub credit_history_years: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreditRiskResult {
pub obligor_id: u64,
pub pd: f64,
pub lgd: f64,
pub expected_loss: f64,
pub rwa: f64,
pub credit_score: f64,
pub factor_contributions: Vec<(String, f64)>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct VaRParams {
pub confidence_level: f64,
pub holding_period: u32,
pub n_simulations: u32,
}
impl Default for VaRParams {
fn default() -> Self {
Self {
confidence_level: 0.99,
holding_period: 10,
n_simulations: 10_000,
}
}
}
impl VaRParams {
pub fn new(confidence_level: f64, holding_period: u32, n_simulations: u32) -> Self {
Self {
confidence_level,
holding_period,
n_simulations,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VaRResult {
pub var: f64,
pub expected_shortfall: f64,
pub confidence_level: f64,
pub holding_period: u32,
pub component_var: Vec<f64>,
pub marginal_var: Vec<f64>,
pub percentiles: Vec<(f64, f64)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PortfolioRiskResult {
pub portfolio_var: f64,
pub portfolio_es: f64,
pub undiversified_var: f64,
pub diversification_benefit: f64,
pub asset_vars: Vec<f64>,
pub risk_contributions: Vec<f64>,
pub covariance_matrix: Vec<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressScenario {
pub name: String,
pub description: String,
pub shocks: Vec<(String, f64)>,
pub probability: f64,
}
impl StressScenario {
pub fn new(
name: &str,
description: &str,
shocks: Vec<(String, f64)>,
probability: f64,
) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
shocks,
probability,
}
}
pub fn equity_crash(shock_pct: f64) -> Self {
Self::new(
"Equity Crash",
"Severe equity market decline",
vec![("equity".to_string(), shock_pct)],
0.01,
)
}
pub fn rate_shock(shock_bps: f64) -> Self {
Self::new(
"Rate Shock",
"Parallel shift in yield curve",
vec![("interest_rate".to_string(), shock_bps / 10000.0)],
0.05,
)
}
pub fn credit_spread_widening(shock_bps: f64) -> Self {
Self::new(
"Credit Spread Widening",
"Credit spreads widen across all sectors",
vec![("credit_spread".to_string(), shock_bps / 10000.0)],
0.03,
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressTestResult {
pub scenario_name: String,
pub pnl_impact: f64,
pub pnl_impact_pct: f64,
pub asset_impacts: Vec<(u64, f64)>,
pub factor_impacts: Vec<(String, f64)>,
pub post_stress_value: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskFactor {
pub name: String,
pub value: f64,
pub volatility: f64,
pub factor_type: RiskFactorType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RiskFactorType {
Equity,
InterestRate,
ForeignExchange,
CreditSpread,
Commodity,
Volatility,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sensitivity {
pub asset_id: u64,
pub delta: f64,
pub gamma: f64,
pub vega: f64,
pub theta: f64,
pub rho: f64,
}
impl Default for Sensitivity {
fn default() -> Self {
Self {
asset_id: 0,
delta: 1.0,
gamma: 0.0,
vega: 0.0,
theta: 0.0,
rho: 0.0,
}
}
}