Skip to main content

Overview

The PerpPosition struct represents a user’s position in a perpetual futures market. It tracks the position size, entry price, funding payments, PnL, and other position-specific data.

Structure

Fields

last_cumulative_funding_rate
i64
Last recorded cumulative funding rate for this position. Used to calculate funding payments owed.
base_asset_amount
i64
Size of the position in base asset terms. Positive values indicate a long position, negative values indicate a short position.
quote_asset_amount
i64
Total quote asset used for this position. Used to calculate PnL and entry price.
quote_break_even_amount
i64
Quote asset amount adjusted for fees. Used to calculate the break-even price.
quote_entry_amount
i64
Quote asset amount at position entry. Used for entry price calculation.
open_bids
i64
Total base asset amount in open bid orders for this market.
open_asks
i64
Total base asset amount in open ask orders for this market.
settled_pnl
i64
Realized PnL that has been settled for this position.
lp_shares
u64
Number of LP (liquidity provider) shares held in this market’s AMM pool.
isolated_position_scaled_balance
u64
Scaled balance for isolated margin positions. Used for cross-collateral isolated positions.
last_quote_asset_amount_per_lp
i64
Last recorded quote asset per LP share. Used to track LP position changes.
max_margin_ratio
u16
Custom maximum margin ratio for this position (if set). Zero means use market default.
market_index
u16
Index of the perpetual market for this position.
open_orders
u8
Number of open orders in this market.
position_flag
u8
Bit flags indicating position properties (e.g., liquidation status).

Position Calculations

The position data enables several important calculations:

Entry Price

// Entry price = quote_entry_amount / base_asset_amount
let entry_price = if position.base_asset_amount != 0 {
    position.quote_entry_amount.abs() / position.base_asset_amount.abs()
} else {
    0
};

Unrealized PnL

// Unrealized PnL = (current_price * base_asset_amount) - quote_asset_amount
let unrealized_pnl = (current_price * position.base_asset_amount) 
                     - position.quote_asset_amount;

Break-Even Price

// Break-even price accounts for fees
let break_even_price = if position.base_asset_amount != 0 {
    position.quote_break_even_amount.abs() / position.base_asset_amount.abs()
} else {
    0
};

Funding Payment

// Funding owed since last settlement
let funding_rate_delta = market.cumulative_funding_rate 
                       - position.last_cumulative_funding_rate;
let funding_payment = (position.base_asset_amount * funding_rate_delta) / 1e9;

Position Direction

The direction of the position is determined by the sign of base_asset_amount:
  • Long Position: base_asset_amount > 0
  • Short Position: base_asset_amount < 0
  • No Position: base_asset_amount == 0
use drift_rs::types::PositionDirection;

let direction = if position.base_asset_amount > 0 {
    Some(PositionDirection::Long)
} else if position.base_asset_amount < 0 {
    Some(PositionDirection::Short)
} else {
    None
};

Isolated Positions

Isolated positions use a separate collateral pool from the user’s main account:
  • isolated_position_scaled_balance tracks the isolated collateral amount
  • Risk is limited to the isolated collateral
  • Useful for high-leverage or risky positions
// Check if position is isolated
let is_isolated = position.isolated_position_scaled_balance > 0;

if is_isolated {
    // Calculate isolated collateral
    let isolated_collateral = position.isolated_position_scaled_balance;
    println!("Isolated position with {} collateral", isolated_collateral);
}

LP Positions

Users can provide liquidity to the AMM by holding LP shares:
  • lp_shares represents the number of LP tokens held
  • LPs earn a portion of trading fees
  • LP position value changes with the AMM’s PnL
// Check if user is an LP
let is_lp = position.lp_shares > 0;

if is_lp {
    println!("User has {} LP shares", position.lp_shares);
}

Example: Position Summary

use drift_rs::types::PerpPosition;

fn print_position_summary(position: &PerpPosition, oracle_price: i64) {
    // Position size and direction
    let size = position.base_asset_amount.abs();
    let direction = if position.base_asset_amount > 0 {
        "Long"
    } else if position.base_asset_amount < 0 {
        "Short"
    } else {
        return; // No position
    };
    
    // Entry price
    let entry_price = if position.base_asset_amount != 0 {
        position.quote_entry_amount.abs() as f64 
            / position.base_asset_amount.abs() as f64
    } else {
        0.0
    };
    
    // Unrealized PnL
    let unrealized_pnl = (oracle_price * position.base_asset_amount) 
                       - position.quote_asset_amount;
    
    // Total PnL
    let total_pnl = position.settled_pnl + unrealized_pnl;
    
    println!("=== Position Summary ===");
    println!("Market Index: {}", position.market_index);
    println!("Direction: {}", direction);
    println!("Size: {}", size);
    println!("Entry Price: {:.2}", entry_price);
    println!("Current Price: {}", oracle_price);
    println!("Unrealized PnL: {}", unrealized_pnl);
    println!("Realized PnL: {}", position.settled_pnl);
    println!("Total PnL: {}", total_pnl);
    println!("Open Orders: {}", position.open_orders);
    
    if position.lp_shares > 0 {
        println!("LP Shares: {}", position.lp_shares);
    }
}

Build docs developers (and LLMs) love