What You’ll Build
- Portfolio systems managing $10M+ in assets
- Real-time risk calculations and position monitoring
- Automated rebalancing with sophisticated strategies
- Performance analytics rivaling traditional finance
Why Institutions Choose Rust
Scale: Handle thousands of positions without performance degradationReliability: Zero-downtime systems that never miss critical trades
Speed: Real-time calculations that enable advanced strategies
Safety: Memory safety prevents costly bugs in production
Why Rust for Portfolio Management?
Portfolio management requires:- Real-time calculation of complex metrics across hundreds of positions
- Memory efficiency when processing large datasets
- Type safety to prevent costly calculation errors
- Concurrent processing of multiple market feeds
Core Portfolio Architecture
Copy
use saros_dlmm::{DLMM, Position, PositionManager, MarketData};
use tokio::sync::RwLock;
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub struct Portfolio {
pub positions: HashMap<String, Position>,
pub total_value_usd: f64,
pub daily_pnl: f64,
pub risk_metrics: RiskMetrics,
}
#[derive(Debug)]
pub struct PortfolioManager {
dlmm: DLMM,
positions: Arc<RwLock<HashMap<String, Position>>>,
market_data: Arc<RwLock<MarketData>>,
risk_calculator: RiskCalculator,
}
impl PortfolioManager {
pub fn new(rpc_url: &str, wallet_keypair: Keypair) -> Result<Self> {
Ok(Self {
dlmm: DLMM::new(rpc_url, wallet_keypair)?,
positions: Arc::new(RwLock::new(HashMap::new())),
market_data: Arc::new(RwLock::new(MarketData::new())),
risk_calculator: RiskCalculator::new(),
})
}
pub async fn add_position(
&self,
token_a: Pubkey,
token_b: Pubkey,
amount_a: u64,
amount_b: u64,
bin_range: (i32, i32),
) -> Result<String> {
// Calculate optimal bin distribution for the position
let optimal_bins = self.calculate_optimal_bins(
token_a, token_b, amount_a, amount_b, bin_range
).await?;
// Create position with risk management
let position = self.dlmm.create_position(
token_a,
token_b,
amount_a,
amount_b,
optimal_bins,
).await?;
// Track position in portfolio
let mut positions = self.positions.write().await;
positions.insert(position.id.clone(), position.clone());
// Update portfolio metrics
self.update_portfolio_metrics().await?;
Ok(position.id)
}
pub async fn rebalance_portfolio(&self) -> Result<RebalanceReport> {
let current_portfolio = self.get_current_portfolio().await?;
let market_conditions = self.market_data.read().await;
// Calculate optimal allocation based on current conditions
let target_allocation = self.calculate_target_allocation(
¤t_portfolio,
&market_conditions,
)?;
let mut rebalance_operations = Vec::new();
// Identify positions that need adjustment
for (position_id, current_position) in ¤t_portfolio.positions {
let target_weight = target_allocation.get(position_id).unwrap_or(&0.0);
let current_weight = current_position.weight;
if (target_weight - current_weight).abs() > 0.05 { // 5% threshold
let operation = self.plan_rebalance_operation(
position_id,
current_weight,
*target_weight,
).await?;
rebalance_operations.push(operation);
}
}
// Execute rebalancing operations
let results = self.execute_rebalance_operations(rebalance_operations).await?;
Ok(RebalanceReport {
operations_executed: results.len(),
total_gas_used: results.iter().map(|r| r.gas_used).sum(),
estimated_improvement: self.calculate_improvement_estimate(&results),
new_portfolio_allocation: target_allocation,
})
}
}
Advanced Risk Management
Copy
#[derive(Debug, Clone)]
pub struct RiskMetrics {
pub var_95: f64, // 95% Value at Risk
pub var_99: f64, // 99% Value at Risk
pub expected_shortfall: f64,
pub sharpe_ratio: f64,
pub max_drawdown: f64,
pub correlation_matrix: Vec<Vec<f64>>,
}
impl PortfolioManager {
pub async fn calculate_portfolio_risk(&self) -> Result<RiskMetrics> {
let positions = self.positions.read().await;
let market_data = self.market_data.read().await;
// Calculate position-level risks
let mut position_vars = Vec::new();
let mut position_returns = Vec::new();
for position in positions.values() {
let var = self.calculate_position_var(position, &market_data).await?;
let returns = self.get_position_returns(position, 30).await?; // 30 days
position_vars.push(var);
position_returns.push(returns);
}
// Calculate portfolio-level risk metrics
let correlation_matrix = self.calculate_correlation_matrix(&position_returns);
let portfolio_var_95 = self.calculate_portfolio_var(&position_vars, &correlation_matrix, 0.95);
let portfolio_var_99 = self.calculate_portfolio_var(&position_vars, &correlation_matrix, 0.99);
Ok(RiskMetrics {
var_95: portfolio_var_95,
var_99: portfolio_var_99,
expected_shortfall: self.calculate_expected_shortfall(&position_returns),
sharpe_ratio: self.calculate_sharpe_ratio(&position_returns),
max_drawdown: self.calculate_max_drawdown(&position_returns),
correlation_matrix,
})
}
pub async fn implement_risk_limits(&self) -> Result<()> {
let risk_metrics = self.calculate_portfolio_risk().await?;
// Implement position size limits
if risk_metrics.var_95 > 0.15 { // 15% daily VaR limit
self.reduce_position_sizes(0.8).await?; // Reduce by 20%
}
// Implement concentration limits
let position_weights = self.calculate_position_weights().await?;
for (position_id, weight) in position_weights {
if weight > 0.25 { // 25% concentration limit
self.trim_position(&position_id, 0.25).await?;
}
}
// Implement correlation limits
if self.check_correlation_breaches(&risk_metrics.correlation_matrix) {
self.diversify_correlated_positions().await?;
}
Ok(())
}
}
Real-Time Performance Monitoring
Copy
use tokio::time::{interval, Duration};
use tokio::spawn;
impl PortfolioManager {
pub async fn start_real_time_monitoring(&self) -> Result<()> {
let positions = Arc::clone(&self.positions);
let market_data = Arc::clone(&self.market_data);
// Spawn price monitoring task
let price_monitor = spawn(async move {
let mut interval = interval(Duration::from_millis(100)); // 100ms updates
loop {
interval.tick().await;
if let Err(e) = self.update_prices().await {
eprintln!("Price update error: {}", e);
}
// Calculate real-time P&L
if let Err(e) = self.update_pnl().await {
eprintln!("P&L calculation error: {}", e);
}
}
});
// Spawn risk monitoring task
let risk_monitor = spawn(async move {
let mut interval = interval(Duration::from_secs(10)); // 10 second risk checks
loop {
interval.tick().await;
if let Err(e) = self.monitor_risk_limits().await {
eprintln!("Risk monitoring error: {}", e);
}
}
});
// Spawn rebalancing task
let rebalancer = spawn(async move {
let mut interval = interval(Duration::from_secs(300)); // 5 minute rebalance checks
loop {
interval.tick().await;
if let Err(e) = self.check_rebalance_triggers().await {
eprintln!("Rebalance check error: {}", e);
}
}
});
// Wait for all monitors to complete (they run indefinitely)
tokio::try_join!(price_monitor, risk_monitor, rebalancer)?;
Ok(())
}
async fn update_prices(&self) -> Result<()> {
let positions = self.positions.read().await;
let mut market_data = self.market_data.write().await;
// Update prices for all unique token pairs
let unique_pairs: HashSet<(Pubkey, Pubkey)> = positions
.values()
.map(|p| (p.token_a, p.token_b))
.collect();
for (token_a, token_b) in unique_pairs {
let price = self.dlmm.get_current_price(token_a, token_b).await?;
market_data.update_price(token_a, token_b, price);
}
Ok(())
}
async fn calculate_real_time_pnl(&self) -> Result<f64> {
let positions = self.positions.read().await;
let market_data = self.market_data.read().await;
let mut total_pnl = 0.0;
for position in positions.values() {
let current_price = market_data.get_price(position.token_a, position.token_b)?;
let position_pnl = self.calculate_position_pnl(position, current_price)?;
total_pnl += position_pnl;
}
Ok(total_pnl)
}
}
Advanced Portfolio Strategies
Mean Reversion Strategy
Copy
#[derive(Debug)]
pub struct MeanReversionStrategy {
lookback_period: u32,
entry_threshold: f64, // Standard deviations from mean
exit_threshold: f64,
max_position_size: f64,
}
impl MeanReversionStrategy {
pub async fn analyze_opportunities(
&self,
dlmm: &DLMM,
pairs: &[(Pubkey, Pubkey)]
) -> Result<Vec<TradingOpportunity>> {
let mut opportunities = Vec::new();
for &(token_a, token_b) in pairs {
// Get historical price data
let price_history = dlmm.get_price_history(
token_a,
token_b,
self.lookback_period
).await?;
// Calculate statistical metrics
let mean_price = price_history.iter().sum::<f64>() / price_history.len() as f64;
let std_dev = self.calculate_standard_deviation(&price_history, mean_price);
let current_price = dlmm.get_current_price(token_a, token_b).await?;
// Check for mean reversion opportunity
let z_score = (current_price - mean_price) / std_dev;
if z_score.abs() > self.entry_threshold {
opportunities.push(TradingOpportunity {
pair: (token_a, token_b),
direction: if z_score > 0 { TradeDirection::Short } else { TradeDirection::Long },
confidence: self.calculate_confidence(z_score),
expected_return: self.estimate_return(z_score, std_dev),
risk_level: self.assess_risk(z_score, &price_history),
optimal_position_size: self.calculate_position_size(z_score),
});
}
}
// Sort by expected return adjusted for risk
opportunities.sort_by(|a, b| {
let risk_adj_return_a = a.expected_return / a.risk_level;
let risk_adj_return_b = b.expected_return / b.risk_level;
risk_adj_return_b.partial_cmp(&risk_adj_return_a).unwrap()
});
Ok(opportunities)
}
pub async fn execute_strategy(
&self,
dlmm: &DLMM,
opportunity: &TradingOpportunity
) -> Result<PositionResult> {
let (token_a, token_b) = opportunity.pair;
match opportunity.direction {
TradeDirection::Long => {
// Create concentrated liquidity position below current price
// Expecting price to revert upward
let position = dlmm.create_position(
token_a,
token_b,
opportunity.optimal_position_size as u64,
0, // All token A
self.calculate_long_bin_range(token_a, token_b).await?,
).await?;
Ok(PositionResult::Created(position))
},
TradeDirection::Short => {
// Create concentrated liquidity position above current price
// Expecting price to revert downward
let position = dlmm.create_position(
token_a,
token_b,
0, // All token B
opportunity.optimal_position_size as u64,
self.calculate_short_bin_range(token_a, token_b).await?,
).await?;
Ok(PositionResult::Created(position))
}
}
}
}
Dynamic Hedging System
Copy
#[derive(Debug)]
pub struct DynamicHedger {
hedge_ratio_target: f64,
rehedge_threshold: f64,
max_hedge_cost: f64,
}
impl DynamicHedger {
pub async fn calculate_hedge_requirements(
&self,
portfolio: &Portfolio,
market_data: &MarketData
) -> Result<Vec<HedgeOperation>> {
let mut hedge_operations = Vec::new();
let total_exposure = portfolio.calculate_total_exposure(market_data)?;
// Calculate Greek exposures (Delta, Gamma, Vega for options-like positions)
let delta_exposure = portfolio.calculate_delta_exposure(market_data)?;
let gamma_exposure = portfolio.calculate_gamma_exposure(market_data)?;
// Target hedge ratio based on portfolio size
let target_hedge = total_exposure * self.hedge_ratio_target;
let current_hedge = portfolio.current_hedge_value;
let hedge_adjustment = target_hedge - current_hedge;
if hedge_adjustment.abs() > self.rehedge_threshold {
// Plan hedge operations
let hedge_pairs = self.select_optimal_hedge_pairs(
&portfolio.positions,
hedge_adjustment
).await?;
for hedge_pair in hedge_pairs {
hedge_operations.push(HedgeOperation {
pair: hedge_pair.tokens,
direction: hedge_pair.direction,
size: hedge_pair.optimal_size,
expected_cost: hedge_pair.estimated_cost,
hedge_effectiveness: hedge_pair.correlation_coefficient,
});
}
}
Ok(hedge_operations)
}
pub async fn execute_hedging_operations(
&self,
dlmm: &DLMM,
operations: Vec<HedgeOperation>
) -> Result<HedgeResult> {
let mut successful_hedges = Vec::new();
let mut total_cost = 0.0;
for operation in operations {
match self.execute_single_hedge(dlmm, &operation).await {
Ok(result) => {
successful_hedges.push(result.clone());
total_cost += result.cost;
},
Err(e) => {
eprintln!("Hedge operation failed: {:?}", e);
// Continue with other operations
}
}
}
Ok(HedgeResult {
successful_operations: successful_hedges.len(),
total_operations: operations.len(),
total_cost,
hedge_effectiveness: self.calculate_hedge_effectiveness(&successful_hedges),
portfolio_risk_reduction: self.estimate_risk_reduction(&successful_hedges),
})
}
}
Performance Analytics
Copy
#[derive(Debug, Serialize, Deserialize)]
pub struct PerformanceReport {
pub period_return: f64,
pub volatility: f64,
pub sharpe_ratio: f64,
pub max_drawdown: f64,
pub win_rate: f64,
pub profit_factor: f64,
pub positions_performance: Vec<PositionPerformance>,
}
impl PortfolioManager {
pub async fn generate_performance_report(
&self,
period_days: u32
) -> Result<PerformanceReport> {
let positions = self.positions.read().await;
let mut position_performances = Vec::new();
// Calculate performance for each position
for (position_id, position) in positions.iter() {
let performance = self.calculate_position_performance(
position,
period_days
).await?;
position_performances.push(performance);
}
// Calculate portfolio-level metrics
let portfolio_returns = self.get_portfolio_returns(period_days).await?;
let period_return = self.calculate_total_return(&portfolio_returns);
let volatility = self.calculate_volatility(&portfolio_returns);
let sharpe_ratio = self.calculate_sharpe_ratio(&portfolio_returns, 0.05); // 5% risk-free rate
let max_drawdown = self.calculate_max_drawdown(&portfolio_returns);
// Trading performance metrics
let trades = self.get_trades(period_days).await?;
let winning_trades = trades.iter().filter(|t| t.pnl > 0.0).count();
let win_rate = winning_trades as f64 / trades.len() as f64;
let gross_profit: f64 = trades.iter().filter(|t| t.pnl > 0.0).map(|t| t.pnl).sum();
let gross_loss: f64 = trades.iter().filter(|t| t.pnl < 0.0).map(|t| t.pnl.abs()).sum();
let profit_factor = if gross_loss > 0.0 { gross_profit / gross_loss } else { f64::INFINITY };
Ok(PerformanceReport {
period_return,
volatility,
sharpe_ratio,
max_drawdown,
win_rate,
profit_factor,
positions_performance: position_performances,
})
}
pub async fn optimize_portfolio_allocation(&self) -> Result<OptimizationResult> {
let current_portfolio = self.get_current_portfolio().await?;
let risk_metrics = self.calculate_portfolio_risk().await?;
// Use Modern Portfolio Theory for optimization
let optimizer = ModernPortfolioOptimizer::new(
risk_metrics.correlation_matrix,
self.get_expected_returns().await?,
0.15, // 15% target volatility
);
let optimal_weights = optimizer.optimize()?;
// Generate rebalancing plan
let rebalance_plan = self.create_rebalance_plan(
¤t_portfolio,
&optimal_weights
).await?;
Ok(OptimizationResult {
current_sharpe: risk_metrics.sharpe_ratio,
optimized_sharpe: optimizer.calculate_optimized_sharpe()?,
improvement: optimizer.calculate_improvement()?,
rebalance_plan,
expected_risk_reduction: optimizer.calculate_risk_reduction()?,
})
}
}
Production Portfolio Example
Copy
use tokio::main;
#[tokio::main]
async fn main() -> Result<()> {
// Initialize portfolio manager
let wallet = read_keypair_file("wallet.json")?;
let portfolio_manager = PortfolioManager::new(
"https://api.mainnet-beta.solana.com",
wallet
)?;
// Start with initial positions
let initial_positions = vec![
// SOL/USDC concentrated liquidity
(SOL_MINT, USDC_MINT, 1_000_000_000, 50_000_000_000, (-5, 5)),
// ETH/USDC concentrated liquidity
(ETH_MINT, USDC_MINT, 500_000_000, 25_000_000_000, (-3, 3)),
// BTC/USDC concentrated liquidity
(BTC_MINT, USDC_MINT, 250_000_000, 12_500_000_000, (-2, 2)),
];
// Create initial positions
for (token_a, token_b, amount_a, amount_b, bin_range) in initial_positions {
portfolio_manager.add_position(
token_a, token_b, amount_a, amount_b, bin_range
).await?;
}
// Start real-time monitoring
portfolio_manager.start_real_time_monitoring().await?;
// Generate daily reports
let mut report_interval = interval(Duration::from_secs(86400)); // Daily
loop {
report_interval.tick().await;
let report = portfolio_manager.generate_performance_report(1).await?;
println!("Daily Performance Report: {:?}", report);
// Check if rebalancing needed
if report.sharpe_ratio < 1.0 || report.max_drawdown > 0.1 {
let optimization = portfolio_manager.optimize_portfolio_allocation().await?;
println!("Portfolio optimization suggested: {:?}", optimization);
}
}
}
Key Performance Metrics
Monitor these critical metrics for portfolio success:- Sharpe Ratio: Target >2.0 for excellent risk-adjusted returns
- Maximum Drawdown: Keep <10% for professional management
- Win Rate: Aim for >60% profitable positions
- Risk-Adjusted Return: Track returns per unit of risk taken
- Position Concentration: No single position >25% of portfolio
Next: Explore Advanced Analytics for deeper insights, or implement Risk Management protocols for enterprise-grade safeguards.