Skip to main content

hypercall/rsm/margin_service/
mod.rs

1//! Margin Service Module
2//!
3//! This module provides margin calculation services for the trading engine.
4//! The primary implementation is `SpanMarginService` which uses SPAN-style
5//! scenario-based margin calculations.
6
7mod span_margin_service;
8
9pub use hypercall_margin::{ExtendedRiskGrid, InstrumentRiskRow, ScenarioPnl};
10pub use span_margin_service::{MarginError, SpanMarginService};
11
12use crate::types::{Account, MarginDetails};
13use async_trait::async_trait;
14
15/// Trait for margin calculation services.
16///
17/// Implementations compute margin requirements for accounts based on their
18/// current positions encoded in `Account`. The margin represents the collateral
19/// required to maintain positions.
20///
21/// Hypotheticals that include open orders are built by the engine layer before
22/// calling this trait - the margin service only sees the resulting `Account` state.
23///
24/// # Portfolio Margin Semantics
25///
26/// - `equity`: Executed PM equity = cash + executed option UPNL + executed perp UPNL
27/// - `initial_margin_required`: Scanning risk (worst-case scenario loss)
28/// - `maintenance_margin_required`: IM * 0.85 (liquidation threshold)
29///
30/// Order admission: `equity >= initial_margin_required`
31#[async_trait]
32pub trait MarginService: Send + Sync {
33    /// Compute margin for a single account.
34    ///
35    /// # Returns
36    /// - `Some(MarginDetails)` with computed margin (always returned by SpanMarginService)
37    /// - `None` reserved for future implementations that may skip margin calculation
38    ///   (e.g., accounts not subject to PM, or internal system accounts)
39    ///
40    /// Empty portfolios return `MarginDetails` with zero margin requirements.
41    async fn compute_margin_for_account(
42        &self,
43        account: &Account,
44    ) -> Result<MarginDetails, MarginError>;
45
46    /// Convenience batch API for computing margin across multiple accounts.
47    async fn compute_margin_for_accounts(
48        &self,
49        accounts: &[Account],
50    ) -> Result<Vec<MarginDetails>, MarginError> {
51        let mut out = Vec::with_capacity(accounts.len());
52        for account in accounts {
53            out.push(self.compute_margin_for_account(account).await?);
54        }
55        Ok(out)
56    }
57}