Querying Positions
Get All Positions
Retrieve all open positions for an account:use drift_rs::Wallet;
let subaccount = wallet.default_sub_account();
let (spot_positions, perp_positions) = drift_client
.all_positions(&subaccount)
.await?;
println!("Open spot positions: {}", spot_positions.len());
println!("Open perp positions: {}", perp_positions.len());
// Iterate through perp positions
for position in perp_positions {
println!(
"Market {}: {} @ {} size",
position.market_index,
if position.base_asset_amount > 0 { "LONG" } else { "SHORT" },
position.base_asset_amount.abs()
);
}
Get Specific Perp Position
let market_index = 0; // SOL-PERP
if let Some(position) = drift_client
.perp_position(&subaccount, market_index)
.await?
{
println!("Position market: {}", position.market_index);
println!("Base asset amount: {}", position.base_asset_amount);
println!("Quote asset amount: {}", position.quote_asset_amount);
println!("Last cumulative funding rate: {}", position.last_cumulative_funding_rate);
} else {
println!("No position in market {}", market_index);
}
Get Specific Spot Position
let market_index = 1; // SOL spot
if let Some(position) = drift_client
.spot_position(&subaccount, market_index)
.await?
{
println!("Token amount: {}", position.scaled_balance);
println!("Cumulative deposits: {}", position.cumulative_deposits);
} else {
println!("No spot position in market {}", market_index);
}
Get Unsettled Positions
Find positions with unsettled PnL:let unsettled = drift_client
.unsettled_positions(&subaccount)
.await?;
println!("Unsettled positions: {}", unsettled.len());
for position in unsettled {
println!(
"Market {}: unsettled quote = {}",
position.market_index,
position.quote_asset_amount
);
}
Calculating Position Value and PnL
Get Unrealized PnL
use drift_rs::MarketId;
let subaccount = wallet.default_sub_account();
let user_account = drift_client.get_user_account(&subaccount).await?;
// Get oracle price for the market
let market_index = 0;
let oracle_data = drift_client
.get_oracle_price_data_and_slot(MarketId::perp(market_index))
.await?;
// Get position
if let Ok(position) = user_account.get_perp_position(market_index) {
// Calculate unrealized PnL
let upnl = position.get_unrealized_pnl(oracle_data.data.price)?;
println!("Position value: ${:.2}",
position.quote_asset_amount as f64 / 1_000_000.0);
println!("Unrealized PnL: ${:.2}",
upnl as f64 / 1_000_000.0);
}
Calculate Position Size in USD
use drift_rs::math::constants::BASE_PRECISION_I64;
let market_index = 0;
let user_account = drift_client.get_user_account(&subaccount).await?;
if let Ok(position) = user_account.get_perp_position(market_index) {
let oracle_price = drift_client
.oracle_price(MarketId::perp(market_index))
.await? as i64;
// Calculate notional value
let size_in_base = position.base_asset_amount as f64
/ BASE_PRECISION_I64 as f64;
let price = oracle_price as f64 / 1_000_000.0;
let notional_value = size_in_base.abs() * price;
println!("Position size: {} SOL", size_in_base);
println!("Notional value: ${:.2}", notional_value);
}
Querying Open Orders
Get All Open Orders
let orders = drift_client.all_orders(&subaccount).await?;
println!("Open orders: {}", orders.len());
for order in orders {
println!(
"Order {}: {} {} @ {} on market {}",
order.order_id,
if order.direction == drift_rs::types::PositionDirection::Long {
"BUY"
} else {
"SELL"
},
order.base_asset_amount,
order.price,
order.market_index
);
}
Get Order by ID
let order_id = 42;
if let Some(order) = drift_client
.get_order_by_id(&subaccount, order_id)
.await?
{
println!("Found order: {:?}", order);
} else {
println!("Order {} not found", order_id);
}
Get Order by User Order ID
let user_order_id = 1; // Your self-assigned ID (0-255)
if let Some(order) = drift_client
.get_order_by_user_id(&subaccount, user_order_id)
.await?
{
println!("Order status: {:?}", order.status);
println!("Filled: {} / {}",
order.base_asset_amount_filled,
order.base_asset_amount
);
}
Calculating Margin and Leverage
Get Account Equity
use drift_rs::math::margin::calculate_margin_requirement_and_total_collateral;
let subaccount = wallet.default_sub_account();
let user_account = drift_client.get_user_account(&subaccount).await?;
// You'll need market data for margin calculations
let spot_market_0 = drift_client.get_spot_market_account(0).await?;
let perp_market_0 = drift_client.get_perp_market_account(0).await?;
// Get oracle prices
let spot_oracle_0 = drift_client
.get_oracle_price_data_and_slot(MarketId::spot(0))
.await?;
let perp_oracle_0 = drift_client
.get_oracle_price_data_and_slot(MarketId::perp(0))
.await?;
// Calculate total collateral (simplified - you'd need all markets)
let total_collateral = user_account.spot_positions
.iter()
.filter(|p| !p.is_available())
.fold(0_i128, |acc, pos| {
// Calculate collateral value for each position
acc + (pos.scaled_balance as i128)
});
println!("Total collateral: ${:.2}",
total_collateral as f64 / 1_000_000.0);
Drift uses a sophisticated margin system with initial and maintenance margin requirements. For production use, consider using the margin calculation functions from the
drift_rs::math::margin module.Check Account Health
// Get current account data
let user_account = drift_client.get_user_account(&subaccount).await?;
// Check if account has positions
let has_open_positions = user_account.perp_positions
.iter()
.any(|p| p.is_open_position());
if has_open_positions {
println!("Account has open positions");
// Calculate total position value
let mut total_position_value = 0_i64;
for position in user_account.perp_positions.iter() {
if position.is_open_position() {
let oracle_price = drift_client
.oracle_price(MarketId::perp(position.market_index))
.await?;
let position_value = (position.base_asset_amount as i64)
.saturating_mul(oracle_price) / 1_000_000_000;
total_position_value = total_position_value
.saturating_add(position_value.abs());
}
}
println!("Total position value: ${:.2}",
total_position_value as f64 / 1_000_000.0);
}
Risk Management
Set Stop Loss and Take Profit Orders
use drift_rs::types::{
OrderParams, OrderType, PositionDirection,
MarketType, OrderTriggerCondition
};
use drift_rs::math::constants::PRICE_PRECISION_U64;
// Current position: LONG 5 SOL at $120
let entry_price = 120 * PRICE_PRECISION_U64;
let stop_loss_price = 115 * PRICE_PRECISION_U64; // -4.2% loss
let take_profit_price = 130 * PRICE_PRECISION_U64; // +8.3% profit
let orders = vec![
// Stop loss (trigger market sell)
OrderParams {
order_type: OrderType::TriggerMarket,
market_type: MarketType::Perp,
market_index: 0,
direction: PositionDirection::Short, // Close long
base_asset_amount: 5 * drift_rs::math::constants::BASE_PRECISION_U64,
trigger_price: Some(stop_loss_price),
trigger_condition: OrderTriggerCondition::Below,
reduce_only: true,
..Default::default()
},
// Take profit (trigger limit sell)
OrderParams {
order_type: OrderType::TriggerLimit,
market_type: MarketType::Perp,
market_index: 0,
direction: PositionDirection::Short, // Close long
base_asset_amount: 5 * drift_rs::math::constants::BASE_PRECISION_U64,
price: take_profit_price,
trigger_price: Some(take_profit_price),
trigger_condition: OrderTriggerCondition::Above,
reduce_only: true,
..Default::default()
},
];
let subaccount_data = drift_client.get_user_account(&subaccount).await?;
let tx = drift_rs::TransactionBuilder::new(
drift_client.program_data(),
subaccount,
std::borrow::Cow::Borrowed(&subaccount_data),
false,
)
.place_orders(orders)
.build();
let sig = drift_client.sign_and_send(tx).await?;
println!("Stop loss and take profit orders placed: {}", sig);
Monitor Position Risk
use std::time::Duration;
use tokio::time::interval;
async fn monitor_position_risk(
drift: &drift_rs::DriftClient,
subaccount: &solana_sdk::pubkey::Pubkey,
market_index: u16,
max_loss_usd: f64,
) -> Result<(), Box<dyn std::error::Error>> {
let mut check_interval = interval(Duration::from_secs(5));
loop {
check_interval.tick().await;
let user_account = drift.get_user_account(subaccount).await?;
let position = user_account.get_perp_position(market_index)?;
let oracle_data = drift
.get_oracle_price_data_and_slot(
drift_rs::MarketId::perp(market_index)
)
.await?;
let upnl = position.get_unrealized_pnl(oracle_data.data.price)?;
let upnl_usd = upnl as f64 / 1_000_000.0;
println!("Current PnL: ${:.2}", upnl_usd);
if upnl_usd < -max_loss_usd {
println!("⚠️ Loss limit exceeded! Current loss: ${:.2}",
upnl_usd.abs());
// Close position or take other action
break;
}
}
Ok(())
}
Complete Example: Position Dashboard
use drift_rs::{
DriftClient, Wallet, MarketId,
math::constants::BASE_PRECISION_I64,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv::dotenv().ok();
env_logger::init();
let wallet: Wallet = drift_rs::utils::load_keypair_multi_format(
&std::env::var("PRIVATE_KEY")?,
)?
.into();
let drift = DriftClient::new(
drift_rs::Context::MainNet,
drift_rs::RpcClient::new(
std::env::var("RPC_URL")
.unwrap_or_else(|_| "https://api.mainnet-beta.solana.com".to_string())
),
wallet.clone(),
)
.await?;
let subaccount = wallet.default_sub_account();
println!("\n📈 Position Dashboard");
println!("=".repeat(50));
// Get all positions
let (spot_positions, perp_positions) = drift.all_positions(&subaccount).await?;
println!("\n🔹 Perp Positions: {}", perp_positions.len());
for position in perp_positions {
let market_index = position.market_index;
let oracle_price = drift.oracle_price(MarketId::perp(market_index)).await?;
let size = position.base_asset_amount as f64 / BASE_PRECISION_I64 as f64;
let price = oracle_price as f64 / 1_000_000.0;
let notional = size.abs() * price;
let upnl = position.get_unrealized_pnl(oracle_price)?;
let upnl_usd = upnl as f64 / 1_000_000.0;
println!("\n Market {}: {}",
market_index,
if size > 0.0 { "LONG" } else { "SHORT" }
);
println!(" Size: {:.4}", size.abs());
println!(" Entry value: ${:.2}",
position.quote_asset_amount as f64 / 1_000_000.0);
println!(" Current value: ${:.2}", notional);
println!(" Unrealized PnL: ${:.2}", upnl_usd);
}
// Get open orders
let orders = drift.all_orders(&subaccount).await?;
println!("\n📝 Open Orders: {}", orders.len());
for order in orders.iter().take(5) {
println!(" Order {}: {} {} @ ${:.2}",
order.order_id,
if order.direction == drift_rs::types::PositionDirection::Long {
"BUY"
} else {
"SELL"
},
order.base_asset_amount as f64 / BASE_PRECISION_I64 as f64,
order.price as f64 / 1_000_000.0
);
}
Ok(())
}
Best Practices
- Always use
reduce_onlyfor stop loss and take profit orders to avoid flipping positions - Monitor unrealized PnL regularly to assess position health
- Calculate margin requirements before opening large positions
- Use trigger orders for automated risk management
- Check account health before adding to positions
Position calculations require up-to-date oracle prices. Always fetch fresh oracle data before making position management decisions.