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
Last recorded cumulative funding rate for this position. Used to calculate funding payments owed.
Size of the position in base asset terms. Positive values indicate a long position, negative values indicate a short position.
Total quote asset used for this position. Used to calculate PnL and entry price.
Quote asset amount adjusted for fees. Used to calculate the break-even price.
Quote asset amount at position entry. Used for entry price calculation.
Total base asset amount in open bid orders for this market.
Total base asset amount in open ask orders for this market.
Realized PnL that has been settled for this position.
Number of LP (liquidity provider) shares held in this market’s AMM pool.
isolated_position_scaled_balance
Scaled balance for isolated margin positions. Used for cross-collateral isolated positions.
last_quote_asset_amount_per_lp
Last recorded quote asset per LP share. Used to track LP position changes.
Custom maximum margin ratio for this position (if set). Zero means use market default.
Index of the perpetual market for this position.
Number of open orders in this market.
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);
}
}