use crate::types::{AccountId, Money};
use rust_decimal::Decimal;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RiskTolerance {
pub max_loss_pct: Decimal,
pub max_loss_absolute: Money,
pub max_total_exposure: Money,
}
impl Default for RiskTolerance {
fn default() -> Self {
Self {
max_loss_pct: Decimal::ZERO,
max_loss_absolute: Money(0),
max_total_exposure: Money(0),
}
}
}
impl RiskTolerance {
pub fn risk_free() -> Self {
Self::default()
}
pub fn conservative() -> Self {
Self {
max_loss_pct: Decimal::new(1, 4), max_loss_absolute: Money::from_cents(100), max_total_exposure: Money::from_cents(10_000), }
}
pub fn moderate() -> Self {
Self {
max_loss_pct: Decimal::new(1, 2), max_loss_absolute: Money::from_cents(10_000), max_total_exposure: Money::from_cents(100_000), }
}
pub fn effective_tolerance(&self, stake: Money) -> Money {
let pct_tolerance = self.max_loss_pct * Decimal::from(stake.0);
let pct_money = Money(pct_tolerance.try_into().unwrap_or(i64::MAX));
Money(pct_money.0.min(self.max_loss_absolute.0))
}
}
#[derive(Debug, Clone)]
pub struct CrossMatchConfig {
pub max_runners: usize,
pub house_account: AccountId,
pub risk: RiskTolerance,
pub cross_match_remainder: bool,
}
impl Default for CrossMatchConfig {
fn default() -> Self {
Self {
max_runners: 3,
house_account: AccountId(0), risk: RiskTolerance::default(),
cross_match_remainder: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_risk_free() {
let config = RiskTolerance::default();
assert_eq!(config.max_loss_pct, Decimal::ZERO);
assert_eq!(config.max_loss_absolute, Money(0));
assert_eq!(config.max_total_exposure, Money(0));
}
#[test]
fn effective_tolerance_uses_minimum() {
let config = RiskTolerance {
max_loss_pct: Decimal::new(5, 2), max_loss_absolute: Money::from_cents(1_000), max_total_exposure: Money(10_000),
};
assert_eq!(
config.effective_tolerance(Money::from_cents(10_000)),
Money::from_cents(500)
);
assert_eq!(
config.effective_tolerance(Money::from_cents(100_000)),
Money::from_cents(1_000)
);
}
#[test]
fn risk_free_has_zero_tolerance() {
let config = RiskTolerance::risk_free();
assert_eq!(config.effective_tolerance(Money(100_000)), Money(0));
}
#[test]
fn default_config_flags() {
let config = CrossMatchConfig::default();
assert_eq!(config.max_runners, 3);
assert_eq!(config.house_account, AccountId(0));
assert!(config.cross_match_remainder);
}
}