nt-portfolio

Crates.iont-portfolio
lib.rsnt-portfolio
version1.0.0
created_at2025-11-13 16:18:32.636702+00
updated_at2025-11-13 16:18:32.636702+00
descriptionPortfolio management and tracking for Neural Trader - positions, P&L, performance analytics
homepage
repositoryhttps://github.com/ruvnet/neural-trader
max_upload_size
id1931467
size98,036
rUv (ruvnet)

documentation

https://docs.rs/nt-portfolio

README

nt-portfolio

Crates.io Documentation License: MIT OR Apache-2.0 Rust

Portfolio management, optimization, and real-time P&L tracking for algorithmic trading.

The nt-portfolio crate provides comprehensive portfolio management capabilities including position tracking, P&L calculation, rebalancing, and portfolio optimization using modern portfolio theory.

Features

  • Real-Time P&L - Mark-to-market position and portfolio P&L
  • Portfolio Optimization - Mean-variance, risk parity, Black-Litterman
  • Rebalancing - Automatic rebalancing with configurable thresholds
  • Asset Allocation - Strategic and tactical asset allocation
  • Risk Metrics - VaR, CVaR, Sharpe ratio, max drawdown
  • Performance Attribution - Decompose returns by asset and factor
  • Multi-Currency - Support for multi-currency portfolios
  • Tax-Loss Harvesting - Optimize for after-tax returns
  • Benchmark Tracking - Track against indices (S&P 500, etc.)

Quick Start

[dependencies]
nt-portfolio = "0.1"

Basic Portfolio Management

use nt_portfolio::{Portfolio, Position};
use rust_decimal::Decimal;
use chrono::Utc;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut portfolio = Portfolio::new(
        "main_portfolio",
        Decimal::from(100_000), // Initial capital
    );

    // Add positions
    portfolio.add_position(Position {
        symbol: "AAPL".to_string(),
        quantity: Decimal::from(100),
        entry_price: Decimal::new(15000, 2), // $150.00
        current_price: Decimal::new(15500, 2), // $155.00
        entry_date: Utc::now(),
    });

    portfolio.add_position(Position {
        symbol: "MSFT".to_string(),
        quantity: Decimal::from(50),
        entry_price: Decimal::new(30000, 2), // $300.00
        current_price: Decimal::new(31000, 2), // $310.00
        entry_date: Utc::now(),
    });

    // Calculate metrics
    println!("Total Value: ${}", portfolio.total_value());
    println!("Total P&L: ${}", portfolio.total_pnl());
    println!("Total Return: {:.2}%", portfolio.total_return() * Decimal::from(100));

    // Position-level metrics
    for position in portfolio.positions() {
        println!(
            "{}: P&L ${} ({:.2}%)",
            position.symbol,
            position.unrealized_pnl(),
            position.return_pct() * Decimal::from(100)
        );
    }

    Ok(())
}

Portfolio Optimization

use nt_portfolio::{
    optimizer::{PortfolioOptimizer, OptimizationMethod},
    constraints::Constraints,
};
use rust_decimal::Decimal;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let symbols = vec!["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA"];

    let optimizer = PortfolioOptimizer::new(
        symbols,
        OptimizationMethod::MeanVariance {
            target_return: Some(Decimal::new(12, 2)), // 12% annual
            risk_free_rate: Decimal::new(4, 2),        // 4% risk-free
        },
    );

    // Set constraints
    let constraints = Constraints {
        min_weight: Decimal::new(5, 2),  // 5% minimum
        max_weight: Decimal::new(30, 2), // 30% maximum
        max_sector_weight: Some(Decimal::new(40, 2)), // 40% per sector
        max_volatility: Some(Decimal::new(20, 2)),    // 20% max vol
    };

    // Optimize portfolio
    let optimal_weights = optimizer
        .optimize()
        .with_constraints(constraints)
        .await?;

    println!("Optimal Portfolio Weights:");
    for (symbol, weight) in optimal_weights {
        println!("  {}: {:.2}%", symbol, weight * Decimal::from(100));
    }

    // Get expected metrics
    let metrics = optimizer.expected_metrics(&optimal_weights)?;
    println!("\nExpected Return: {:.2}%", metrics.expected_return * Decimal::from(100));
    println!("Expected Volatility: {:.2}%", metrics.volatility * Decimal::from(100));
    println!("Sharpe Ratio: {:.2}", metrics.sharpe_ratio);

    Ok(())
}

Portfolio Rebalancing

use nt_portfolio::{
    Portfolio,
    rebalancer::{Rebalancer, RebalancingStrategy},
};
use rust_decimal::Decimal;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut portfolio = Portfolio::load("main_portfolio")?;

    let rebalancer = Rebalancer::new(
        RebalancingStrategy::TargetWeights {
            weights: vec![
                ("AAPL".to_string(), Decimal::new(25, 2)),
                ("MSFT".to_string(), Decimal::new(25, 2)),
                ("GOOGL".to_string(), Decimal::new(25, 2)),
                ("AMZN".to_string(), Decimal::new(25, 2)),
            ].into_iter().collect(),
        },
    );

    // Calculate rebalancing trades
    let trades = rebalancer.calculate_trades(&portfolio)?;

    println!("Rebalancing Trades:");
    for trade in &trades {
        println!(
            "  {} {} {} shares @ ${}",
            trade.side,
            trade.symbol,
            trade.quantity,
            trade.estimated_price
        );
    }

    // Execute rebalancing
    rebalancer.execute_trades(trades, &mut portfolio).await?;

    println!("Portfolio rebalanced successfully");

    Ok(())
}

Risk Parity Portfolio

use nt_portfolio::optimizer::{PortfolioOptimizer, OptimizationMethod};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let assets = vec![
        "SPY",  // Stocks
        "TLT",  // Bonds
        "GLD",  // Gold
        "DBC",  // Commodities
    ];

    let optimizer = PortfolioOptimizer::new(
        assets,
        OptimizationMethod::RiskParity,
    );

    let weights = optimizer.optimize().await?;

    println!("Risk Parity Portfolio:");
    for (symbol, weight) in weights {
        println!("  {}: {:.2}%", symbol, weight * Decimal::from(100));
    }

    Ok(())
}

Performance Attribution

use nt_portfolio::attribution::PerformanceAttributor;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let portfolio = Portfolio::load("main_portfolio")?;
    let benchmark = "SPY"; // S&P 500

    let attributor = PerformanceAttributor::new(
        portfolio,
        benchmark,
    );

    let attribution = attributor.calculate().await?;

    println!("Performance Attribution:");
    println!("  Total Return: {:.2}%", attribution.total_return * Decimal::from(100));
    println!("  Benchmark Return: {:.2}%", attribution.benchmark_return * Decimal::from(100));
    println!("  Alpha: {:.2}%", attribution.alpha * Decimal::from(100));
    println!("  Beta: {:.2}", attribution.beta);

    println!("\nReturn Decomposition:");
    println!("  Asset Selection: {:.2}%", attribution.selection_effect * Decimal::from(100));
    println!("  Sector Allocation: {:.2}%", attribution.allocation_effect * Decimal::from(100));
    println!("  Interaction: {:.2}%", attribution.interaction_effect * Decimal::from(100));

    Ok(())
}

Tax-Loss Harvesting

use nt_portfolio::tax::{TaxLossHarvester, HarvestingStrategy};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let portfolio = Portfolio::load("taxable_portfolio")?;

    let harvester = TaxLossHarvester::new(
        HarvestingStrategy {
            min_loss_threshold: Decimal::new(1000, 0), // $1,000 minimum loss
            max_lots_per_symbol: 10,
            avoid_wash_sales: true,
        },
    );

    let opportunities = harvester.find_opportunities(&portfolio)?;

    println!("Tax-Loss Harvesting Opportunities:");
    for opp in opportunities {
        println!(
            "  {} {} shares: Loss ${} (save ${})",
            opp.symbol,
            opp.quantity,
            opp.loss_amount,
            opp.tax_savings
        );
    }

    // Execute harvesting
    let trades = harvester.generate_trades(opportunities)?;
    // ... execute trades

    Ok(())
}

Architecture

nt-portfolio/
├── portfolio.rs         # Core portfolio management
├── position.rs          # Position tracking
├── optimizer/
│   ├── mean_variance.rs # Mean-variance optimization
│   ├── risk_parity.rs   # Risk parity
│   ├── black_litterman.rs # Black-Litterman model
│   └── constraints.rs   # Portfolio constraints
├── rebalancer.rs        # Rebalancing logic
├── attribution.rs       # Performance attribution
├── tax.rs               # Tax-loss harvesting
├── metrics.rs           # Portfolio metrics
└── lib.rs

Portfolio Metrics

The crate calculates comprehensive portfolio metrics:

  • Return Metrics: Total return, annualized return, daily returns
  • Risk Metrics: Volatility, VaR, CVaR, max drawdown, Sharpe ratio
  • Risk-Adjusted: Sortino ratio, Calmar ratio, information ratio
  • Factor Exposure: Beta, alpha, correlation to benchmark
  • Concentration: Herfindahl index, top N concentration

Dependencies

Crate Purpose
nt-core Core types
nt-risk Risk calculations
polars DataFrame operations
statrs Statistical functions
nalgebra Linear algebra for optimization

Testing

# Unit tests
cargo test -p nt-portfolio

# Integration tests
cargo test -p nt-portfolio --features integration

# Benchmarks
cargo bench -p nt-portfolio

Performance

  • Portfolio valuation: <1ms for 1000 positions
  • Optimization: <100ms for 100 assets
  • Rebalancing calculation: <10ms for 50 positions
  • Real-time P&L updates: <1μs per position

Contributing

See CONTRIBUTING.md.

License

Licensed under MIT OR Apache-2.0.

Commit count: 0

cargo fmt