Skip to main content

PortfolioService

Trait PortfolioService 

Source
pub trait PortfolioService: Send + Sync {
    // Required methods
    fn get_portfolio<'life0, 'life1, 'async_trait>(
        &'life0 self,
        account: &'life1 WalletAddress,
    ) -> Pin<Box<dyn Future<Output = Portfolio> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn get_portfolio_balance<'life0, 'life1, 'async_trait>(
        &'life0 self,
        account: &'life1 WalletAddress,
    ) -> Pin<Box<dyn Future<Output = Option<PortfolioBalance>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn all_portfolios<'life0, 'async_trait>(
        &'life0 self,
    ) -> Pin<Box<dyn Future<Output = HashMap<WalletAddress, PortfolioBalance>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait;
    fn apply_event<'life0, 'life1, 'async_trait>(
        &'life0 self,
        event: &'life1 EngineMessage,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<PortfolioChange>, PortfolioError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn remove_expired_position<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        wallet: &'life1 WalletAddress,
        symbol: &'life2 str,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait;
    fn apply_hypercore_position_update<'life0, 'life1, 'async_trait>(
        &'life0 self,
        update: &'life1 HypercorePositionUpdate,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn set_hypercore_position<'life0, 'life1, 'async_trait>(
        &'life0 self,
        update: &'life1 HypercorePositionUpdate,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn as_any(&self) -> &dyn Any;
    fn calculate_fill_accounting<'life0, 'life1, 'async_trait>(
        &'life0 self,
        fill: &'life1 Fill,
    ) -> Pin<Box<dyn Future<Output = Result<FillAccounting, PortfolioError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn apply_fill_to_memory<'life0, 'life1, 'life2, 'life3, 'async_trait>(
        &'life0 self,
        wallet: &'life1 WalletAddress,
        symbol: &'life2 str,
        side: &'life3 Side,
        price: Decimal,
        quantity: Decimal,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait;
}
Expand description

Portfolio state management interface.

Pure state mutation for portfolio positions, balances, and P&L. Used by engine, API, and liquidator on the hot path.

Accounts are created lazily. get_portfolio always succeeds and returns an empty portfolio (zero balance, no positions) if the account doesn’t exist.

Required Methods§

Source

fn get_portfolio<'life0, 'life1, 'async_trait>( &'life0 self, account: &'life1 WalletAddress, ) -> Pin<Box<dyn Future<Output = Portfolio> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Get current portfolio state for an account (API format).

Returns empty portfolio if account doesn’t exist. Must be O(1).

Source

fn get_portfolio_balance<'life0, 'life1, 'async_trait>( &'life0 self, account: &'life1 WalletAddress, ) -> Pin<Box<dyn Future<Output = Option<PortfolioBalance>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Get internal balance state for risk/margin calculations.

Returns None if account doesn’t exist (no lazy creation). This is the raw internal state, not the API-formatted Portfolio.

Source

fn all_portfolios<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = HashMap<WalletAddress, PortfolioBalance>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Get all portfolios as a snapshot.

Returns a clone of all in-memory portfolio state. Used for persistence/snapshots and open interest calculations.

Source

fn apply_event<'life0, 'life1, 'async_trait>( &'life0 self, event: &'life1 EngineMessage, ) -> Pin<Box<dyn Future<Output = Result<Vec<PortfolioChange>, PortfolioError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Apply an event from the matching engine.

Pure state mutation - updates positions and balances. Events: OrderFilled, OrderUpdate, OrderCanceled, PositionExpired.

Returns a list of PortfolioChange structs, one per affected wallet. For fills, this includes both taker and maker. For other events, typically one. Returns empty vec if no portfolio state changed (e.g., unhandled event type).

§Errors

Returns PortfolioError::LedgerError if realized PnL cannot be applied to the ledger. This is a CRITICAL failure - callers should halt processing.

Source

fn remove_expired_position<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, wallet: &'life1 WalletAddress, symbol: &'life2 str, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Remove an expired position from memory without applying a cash delta.

Settlement accounting is owned by the settlement persistence path. Projection code uses this to clean up positions without double-crediting the ledger.

Source

fn apply_hypercore_position_update<'life0, 'life1, 'async_trait>( &'life0 self, update: &'life1 HypercorePositionUpdate, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Apply perp position update from Hypercore Position Service (incremental diff).

Used for snapshot=false updates. Idempotent - safe to apply the same update multiple times.

Source

fn set_hypercore_position<'life0, 'life1, 'async_trait>( &'life0 self, update: &'life1 HypercorePositionUpdate, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Set perp position from Hypercore Position Service (snapshot).

Used for snapshot=true updates. Sets/replaces a single perp position. Called on service startup for initial state sync.

Source

fn as_any(&self) -> &dyn Any

Returns a reference to self as Any for downcasting.

This is used to access implementation-specific methods like set_ledger and set_tier_cache on PortfolioServiceImpl.

Source

fn calculate_fill_accounting<'life0, 'life1, 'async_trait>( &'life0 self, fill: &'life1 Fill, ) -> Pin<Box<dyn Future<Output = Result<FillAccounting, PortfolioError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Calculate the accounting delta that would result from applying a fill.

This is a pure calculation with no side effects - used to determine what to write to the ledger in the atomic DB transaction.

Source

fn apply_fill_to_memory<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, wallet: &'life1 WalletAddress, symbol: &'life2 str, side: &'life3 Side, price: Decimal, quantity: Decimal, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Apply a fill’s position changes to in-memory state only.

This does NOT update the ledger - that’s handled by the atomic DB transaction. Call this only after the DB transaction succeeds.

Implementors§