//! Position Manager fixes for Price/Decimal conflicts //! //! Key fixes needed: //! 1. Replace .value() calls on Decimal with direct usage //! 2. Fix Position field access (avg_cost -> avg_price) //! 3. Handle Volume::new() Result return type //! 4. Add proper type conversions use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; use tracing::{debug, info, warn}; use crate::trading_operations::ExecutionResult; use crate::types::prelude::*; use rust_decimal::Decimal; /// Position Manager for tracking and managing positions #[derive(Debug)] pub struct PositionManager { /// Current positions by symbol positions: Arc>>, } impl PositionManager { /// Create a new position manager pub fn new() -> Self { Self { positions: Arc::new(RwLock::new(HashMap::new())), } } /// Update position based on execution pub async fn update_position(&self, execution: &ExecutionResult) -> Result<(), String> { let mut positions = self.positions.write().await; let position = positions .entry(execution.symbol.clone()) .or_insert_with(|| { let now = chrono::Utc::now(); Position { id: uuid::Uuid::new_v4(), symbol: execution.symbol.clone(), quantity: Decimal::ZERO, avg_price: Decimal::ZERO, avg_cost: Decimal::ZERO, basis: Decimal::ZERO, average_price: Decimal::ZERO, market_value: Decimal::ZERO, unrealized_pnl: Decimal::ZERO, realized_pnl: Decimal::ZERO, created_at: now, updated_at: now, last_updated: now, current_price: None, notional_value: Decimal::ZERO, margin_requirement: Decimal::ZERO, } }); // CRITICAL FIX: Don't call .value() on Decimal - use directly let is_buy = execution.executed_quantity > Decimal::ZERO; let old_quantity = position.quantity; // Already Decimal let old_cost = position.avg_price; // FIX: Use avg_price not avg_cost if is_buy { if position.quantity >= Decimal::ZERO { // Same direction - calculate new average cost let old_qty_decimal = old_quantity; let old_cost_decimal = old_cost; // Already Decimal let exec_qty_decimal = execution.executed_quantity; let exec_price_decimal = execution.execution_price; let total_cost = old_qty_decimal * old_cost_decimal + exec_qty_decimal * exec_price_decimal; let new_quantity_decimal = old_qty_decimal + exec_qty_decimal; position.quantity = new_quantity_decimal; position.avg_price = if new_quantity_decimal > Decimal::ZERO { total_cost / new_quantity_decimal } else { Decimal::ZERO }; // Keep other fields in sync position.avg_cost = position.avg_price; position.average_price = position.avg_price; } else { // Reducing short position let exec_qty_decimal = execution.executed_quantity; let exec_price_decimal = execution.execution_price; let old_qty_decimal = old_quantity; // FIX: old_cost is already Decimal, no conversion needed let old_cost_decimal = old_cost; let reduction = exec_qty_decimal.min(old_qty_decimal.abs()); let realized_pnl = reduction * (old_cost_decimal - exec_price_decimal); position.realized_pnl = position.realized_pnl + realized_pnl; let new_quantity = old_qty_decimal + reduction; position.quantity = new_quantity; if new_quantity > Decimal::ZERO { position.avg_price = exec_price_decimal; position.avg_cost = exec_price_decimal; position.average_price = exec_price_decimal; } } } else { // Decreasing position (sell) let exec_qty_decimal = execution.executed_quantity; let exec_price_decimal = execution.execution_price; // FIX: Don't call .value() on Decimal let old_qty_decimal = old_quantity; let old_cost_decimal = old_cost; // Already Decimal if old_qty_decimal > Decimal::ZERO { // Reducing long position let reduction = exec_qty_decimal.min(old_qty_decimal); let realized_pnl = reduction * (exec_price_decimal - old_cost_decimal); position.realized_pnl = position.realized_pnl + realized_pnl; let new_quantity_decimal = old_qty_decimal - reduction; position.quantity = new_quantity_decimal; if new_quantity_decimal < Decimal::ZERO { position.avg_price = exec_price_decimal; position.avg_cost = exec_price_decimal; position.average_price = exec_price_decimal; } } else { // Increasing short position let total_cost = old_qty_decimal.abs() * old_cost_decimal + exec_qty_decimal * exec_price_decimal; let new_quantity_decimal = old_qty_decimal - exec_qty_decimal; position.quantity = new_quantity_decimal; if new_quantity_decimal < Decimal::ZERO { let new_avg_price = total_cost / new_quantity_decimal.abs(); position.avg_price = new_avg_price; position.avg_cost = new_avg_price; position.average_price = new_avg_price; } else { position.avg_price = Decimal::ZERO; position.avg_cost = Decimal::ZERO; position.average_price = Decimal::ZERO; } } } position.last_updated = chrono::Utc::now(); position.updated_at = chrono::Utc::now(); info!( "Position updated for {}: {} @ {} (realized P&L: {})", execution.symbol, position.quantity, position.avg_price, position.realized_pnl ); Ok(()) } /// Update market values based on current market prices pub async fn update_market_values( &self, market_prices: HashMap, ) -> Result<(), String> { let mut positions = self.positions.write().await; for (symbol, market_price) in market_prices { if let Some(position) = positions.get_mut(&symbol) { let qty_decimal = position.quantity; // FIX: avg_cost is already Decimal let avg_cost_decimal = position.avg_price; // Use avg_price // Calculate market value let market_value_decimal = qty_decimal * market_price; position.market_value = market_value_decimal; // Calculate unrealized P&L if qty_decimal != Decimal::ZERO { let unrealized_pnl = if qty_decimal > Decimal::ZERO { // Long position qty_decimal * (market_price - avg_cost_decimal) } else { // Short position qty_decimal.abs() * (avg_cost_decimal - market_price) }; position.unrealized_pnl = unrealized_pnl; } else { position.unrealized_pnl = Decimal::ZERO; } position.last_updated = chrono::Utc::now(); position.updated_at = chrono::Utc::now(); debug!( "Updated market value for {}: {} (unrealized P&L: {})", symbol, position.market_value, position.unrealized_pnl ); } } Ok(()) } /// Get total portfolio value pub async fn get_total_portfolio_value(&self) -> Decimal { let positions = self.positions.read().await; positions .values() .map(|pos| pos.market_value) // Already Decimal, no conversion needed .sum::() } /// Get positions that exceed risk limits pub async fn get_positions_exceeding_limits( &self, max_position_value: Decimal, ) -> Vec { let positions = self.positions.read().await; positions .values() .filter(|pos| pos.market_value.abs() > max_position_value) // Already Decimal .cloned() .collect() } /// Calculate position concentration risk pub async fn calculate_concentration_risk(&self) -> HashMap { let positions = self.positions.read().await; let total_value = positions .values() .map(|pos| pos.market_value.abs()) // Already Decimal .sum::(); if total_value == Decimal::ZERO { return HashMap::new(); } positions .iter() .map(|(symbol, position)| { let concentration = (position.market_value.abs() / total_value) .to_f64() .unwrap_or(0.0) * 100.0; (symbol.clone(), concentration) }) .collect() } // ... rest of methods remain the same }