diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 53c0b795b..bc251f09b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -41,9 +41,10 @@ variables: # sccache base dir on PVC — GPU stages override to /mnt/sccache/sm_ SCCACHE_DIR: "/mnt/sccache" RUSTC_WRAPPER: /usr/local/bin/sccache - # Build profile: "release" (default, fat LTO) or "dev-release" (thin LTO, fast) - # Set DEV_RELEASE=true in pipeline vars or CI/CD settings to use fast profile - CARGO_BUILD_PROFILE: release + # Build profile: "dev-release" (thin LTO, fast) is default for quick iteration + # Set DEV_RELEASE=false in pipeline vars to use full "release" (fat LTO) profile + DEV_RELEASE: "true" + CARGO_BUILD_PROFILE: dev-release # S3 credentials for Kaniko registry auth and Dockerfile builds AWS_ACCESS_KEY_ID: $SCW_ACCESS_KEY AWS_SECRET_ACCESS_KEY: $SCW_SECRET_KEY diff --git a/Cargo.lock b/Cargo.lock index c98632a24..4df0d54bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8252,7 +8252,6 @@ dependencies = [ "fastrand", "futures", "hdrhistogram", - "lazy_static", "nalgebra 0.33.2", "ndarray", "num", diff --git a/crates/ml/src/dqn/portfolio_tracker.rs b/crates/ml/src/dqn/portfolio_tracker.rs index 8e6493a91..e7c6a5803 100644 --- a/crates/ml/src/dqn/portfolio_tracker.rs +++ b/crates/ml/src/dqn/portfolio_tracker.rs @@ -10,7 +10,7 @@ use super::action_space::FactoredAction; use super::agent::TradingAction; -use tracing::warn; +use tracing::{debug, warn}; /// Trade action enum with quantities for TradeExecutor /// @@ -290,7 +290,7 @@ impl PortfolioTracker { self.position_size = 0.0; self.position_entry_price = 0.0; - warn!( + debug!( "P2-C: Phase 1 complete - Closed position {:.2} → 0.0, Cash: ${:.2} → ${:.2}", old_position, self.cash - phase1_cash_change, self.cash ); @@ -343,7 +343,7 @@ impl PortfolioTracker { self.position_size = actual_phase2_position; self.position_entry_price = price; - warn!( + debug!( "P2-C: Phase 2 complete - Opened position {:.2} (target: {:.2}), Cash: ${:.2}", actual_phase2_position, phase2_target, self.cash ); diff --git a/crates/risk/Cargo.toml b/crates/risk/Cargo.toml index 9021c74f3..c7f493222 100644 --- a/crates/risk/Cargo.toml +++ b/crates/risk/Cargo.toml @@ -50,7 +50,6 @@ redis.workspace = true serde_json.workspace = true nalgebra.workspace = true prometheus.workspace = true -lazy_static.workspace = true async-trait.workspace = true reqwest.workspace = true tracing-subscriber.workspace = true diff --git a/crates/risk/src/operations.rs b/crates/risk/src/operations.rs index d632955b6..48193480f 100644 --- a/crates/risk/src/operations.rs +++ b/crates/risk/src/operations.rs @@ -191,14 +191,13 @@ pub fn decimal_to_f64_safe(value: Decimal, context: &str) -> RiskResult { reason: format!("Conversion failed for value {value} in {context}"), } }) - .map(|result| { + .inspect(|result| { debug!( value = %value, context = context, result = result, "Decimal to f64 conversion successful" ); - result }) } diff --git a/crates/risk/src/portfolio_optimization.rs b/crates/risk/src/portfolio_optimization.rs index 7c1ddc06f..e5cf7a520 100644 --- a/crates/risk/src/portfolio_optimization.rs +++ b/crates/risk/src/portfolio_optimization.rs @@ -626,7 +626,7 @@ impl PortfolioOptimizer { let target_return = if num_points > 1 { min_return + (max_return - min_return) * (i as f64) / ((num_points - 1) as f64) } else { - (min_return + max_return) / 2.0 + f64::midpoint(min_return, max_return) }; // For each target return, find minimum variance portfolio diff --git a/crates/risk/src/position_tracker.rs b/crates/risk/src/position_tracker.rs index a6f23e613..e603a4c6d 100644 --- a/crates/risk/src/position_tracker.rs +++ b/crates/risk/src/position_tracker.rs @@ -18,7 +18,7 @@ use common::types::{Price, Quantity, Symbol}; use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; use tokio::sync::{broadcast, RwLock}; -use tracing::{debug, error, info, warn}; +use tracing::{debug, info, warn}; use crate::error::{decimal_to_f64_safe, f64_to_price_safe, RiskError, RiskResult}; use crate::risk_types::{ @@ -28,183 +28,56 @@ use config::AssetClassificationConfig; // CANONICAL TYPE IMPORTS - ENFORCED BY TYPE SYSTEM AGENT // Prometheus metrics integration -use lazy_static::lazy_static; use prometheus::{ - register_counter, register_gauge, register_histogram, register_int_gauge, Counter, Gauge, - Histogram, HistogramOpts, IntGauge, + register_counter, register_gauge, register_int_gauge, Counter, Gauge, IntGauge, }; +use std::sync::LazyLock; -lazy_static! { -static ref POSITION_UPDATES_COUNTER: Counter = register_counter!( - "foxhunt_position_updates_total", - "Total position updates processed" -).unwrap_or_else(|e| { - warn!("Failed to register position updates counter: {}", e); - // Safe fallback: If even basic counter creation fails, return a default counter - // This should never happen in practice, but eliminates panic possibility - Counter::new("position_updates_fallback", "Fallback counter").unwrap_or_else(|_| { - error!("Critical: All counter creation failed - using no-op metrics"); - // Create a dummy counter that won't panic - metrics will be lost but system stays up - Counter::new("noop_counter", "No-op counter for safety").unwrap_or_else(|_| { - // Absolute fallback - create a minimal counter and log the error but continue operating - error!("CRITICAL: Complete metrics subsystem failure - continuing without metrics"); - // Create the simplest possible counter that should always work - Counter::new("emergency", "Emergency fallback counter") - .unwrap_or_else(|_| { - error!("FATAL: Cannot create any metrics - system continuing with no-op metrics"); - // Last resort: use a basic counter implementation - prometheus::core::GenericCounter::new("basic", "basic counter") - .unwrap_or_else(|_| { - // Ultimate fallback - if this fails, we create a default counter - prometheus::core::GenericCounter::new("fallback", "fallback counter") - .unwrap_or_else(|_| { - // Create a basic counter as last resort - Counter::new("emergency_fallback", "emergency fallback counter") - .unwrap_or_else(|_| { - match Counter::new("emergency_fallback_fallback", "emergency fallback") { - Ok(c) => c, - Err(_) => std::process::abort(), - } - }) - }) - }) }) +static POSITION_UPDATES_COUNTER: LazyLock = LazyLock::new(|| { + register_counter!("foxhunt_position_updates_total", "Total position updates processed") + .unwrap_or_else(|e| { + warn!("Failed to register position updates counter: {}", e); + Counter::new("position_updates_fallback", "Fallback counter") + .unwrap_or_else(|_| std::process::abort()) }) - }) }); -static ref POSITION_VALUE_GAUGE: Gauge = register_gauge!( - "foxhunt_current_position_value_usd", - "Current total position value in USD" -).unwrap_or_else(|e| { - warn!("Failed to register position value gauge: {}", e); - Gauge::new("position_value_fallback", "Fallback gauge").unwrap_or_else(|_| { - error!("Critical: All gauge creation failed - using no-op metrics"); - Gauge::new("noop_gauge", "No-op gauge for safety").unwrap_or_else(|_| { - error!("CRITICAL: Complete gauge metrics failure - continuing without position value metrics"); - Gauge::new("emergency_gauge", "Emergency fallback gauge") - .unwrap_or_else(|_| { - error!("FATAL: Cannot create any gauge metrics - system continuing"); - prometheus::core::GenericGauge::new("basic_gauge", "basic gauge") - .unwrap_or_else(|_| { - prometheus::core::GenericGauge::new("fallback_gauge", "fallback gauge") - .unwrap_or_else(|_| { - // Create a basic gauge as last resort - match Gauge::new("emergency_fallback_gauge", "emergency fallback gauge") { - Ok(g) => g, - Err(_) => std::process::abort(), - } - }) - }) - }) +static POSITION_VALUE_GAUGE: LazyLock = LazyLock::new(|| { + register_gauge!("foxhunt_current_position_value_usd", "Current total position value in USD") + .unwrap_or_else(|e| { + warn!("Failed to register position value gauge: {}", e); + Gauge::new("position_value_fallback", "Fallback gauge") + .unwrap_or_else(|_| std::process::abort()) }) - }) }); -static ref CONCENTRATION_RISK_GAUGE: Gauge = register_gauge!( - "foxhunt_concentration_risk_score", - "Portfolio concentration risk score (HHI)" -).unwrap_or_else(|e| { - warn!("Failed to register concentration risk gauge: {}", e); - Gauge::new("concentration_risk_fallback", "Fallback gauge").unwrap_or_else(|_| { - error!("Critical: All concentration gauge creation failed - using no-op metrics"); - Gauge::new("noop_concentration", "No-op concentration gauge").unwrap_or_else(|_| { - error!("CRITICAL: Complete concentration gauge failure - continuing without concentration metrics"); - Gauge::new("emergency_concentration", "Emergency concentration gauge") - .unwrap_or_else(|_| { - error!("FATAL: Cannot create any concentration gauge - system continuing"); - prometheus::core::GenericGauge::new("basic_concentration", "basic") - .unwrap_or_else(|_| { - match prometheus::core::GenericGauge::new("fallback_concentration", "fallback") { - Ok(g) => g, - Err(_) => std::process::abort(), - } - }) - }) +static CONCENTRATION_RISK_GAUGE: LazyLock = LazyLock::new(|| { + register_gauge!("foxhunt_concentration_risk_score", "Portfolio concentration risk score (HHI)") + .unwrap_or_else(|e| { + warn!("Failed to register concentration risk gauge: {}", e); + Gauge::new("concentration_risk_fallback", "Fallback gauge") + .unwrap_or_else(|_| std::process::abort()) }) - }) }); -static ref PORTFOLIO_COUNT_GAUGE: IntGauge = register_int_gauge!( - "foxhunt_active_portfolios", - "Number of active portfolios" -).unwrap_or_else(|e| { - warn!("Failed to register portfolio count gauge: {}", e); - IntGauge::new("portfolio_count_fallback", "Fallback gauge").unwrap_or_else(|_| { - error!("Critical: All portfolio gauge creation failed - using no-op metrics"); - IntGauge::new("noop_portfolio", "No-op portfolio gauge").unwrap_or_else(|_| { - error!("CRITICAL: Complete portfolio gauge failure - continuing without portfolio count metrics"); - IntGauge::new("emergency_portfolio", "Emergency portfolio gauge") - .unwrap_or_else(|_| { - error!("FATAL: Cannot create any portfolio gauge - system continuing"); - prometheus::core::GenericGauge::new("basic_portfolio", "basic") - .unwrap_or_else(|_| { - match prometheus::core::GenericGauge::new("fallback_portfolio", "fallback") { - Ok(g) => g, - Err(_) => std::process::abort(), - } - }) - }) +static PORTFOLIO_COUNT_GAUGE: LazyLock = LazyLock::new(|| { + register_int_gauge!("foxhunt_active_portfolios", "Number of active portfolios") + .unwrap_or_else(|e| { + warn!("Failed to register portfolio count gauge: {}", e); + IntGauge::new("portfolio_count_fallback", "Fallback gauge") + .unwrap_or_else(|_| std::process::abort()) }) - }) }); -static ref RISK_BREACHES_COUNTER: Counter = register_counter!( - "foxhunt_concentration_breaches_total", - "Total concentration limit breaches" -).unwrap_or_else(|e| { - warn!("Failed to register concentration breaches counter: {}", e); - if let Ok(counter) = Counter::new("concentration_breaches_fallback", "Fallback counter") { counter } else { - error!("Critical: Breaches counter creation failed - metrics may be inaccurate"); - Counter::new("emergency_breaches_fallback", "Emergency fallback").unwrap_or_else(|_| { - error!("FATAL: Complete breaches counter creation failed - system continuing with no-op counter"); - // Last resort: Create the simplest possible counter that should always work - prometheus::core::GenericCounter::new("noop_breaches", "no-op breaches counter") - .unwrap_or_else(|_| { - match prometheus::core::GenericCounter::new("ultimate_fallback", "ultimate fallback") { - Ok(c) => c, - Err(_) => std::process::abort(), - } - }) +static RISK_BREACHES_COUNTER: LazyLock = LazyLock::new(|| { + register_counter!("foxhunt_concentration_breaches_total", "Total concentration limit breaches") + .unwrap_or_else(|e| { + warn!("Failed to register concentration breaches counter: {}", e); + Counter::new("concentration_breaches_fallback", "Fallback counter") + .unwrap_or_else(|_| std::process::abort()) }) - } }); -static ref POSITION_PROCESSING_LATENCY: Histogram = register_histogram!( - HistogramOpts::new( - "foxhunt_position_processing_latency_microseconds", - "Position update processing latency" - ).buckets(vec![1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0]) -).unwrap_or_else(|e| { - warn!("Failed to register position processing latency histogram: {}", e); - if let Ok(histogram) = Histogram::with_opts(HistogramOpts::new( - "position_processing_latency_fallback", - "Fallback histogram" - )) { histogram } else { - error!("Critical: Even fallback histogram creation failed - metrics may be inaccurate"); - Histogram::with_opts(HistogramOpts::new( - "emergency_histogram_fallback", - "Emergency fallback" - )).unwrap_or_else(|_| { - error!("FATAL: Complete histogram creation failed - system continuing with no-op histogram"); - // Last resort: Create the simplest possible histogram that should always work - Histogram::with_opts(HistogramOpts::new( - "noop_histogram", - "No-op histogram for safety" - )).unwrap_or_else(|_| { - error!("CRITICAL: Cannot create any histogram - using basic histogram implementation"); - // Use default histogram with basic configuration - Histogram::with_opts( - HistogramOpts::new("basic_histogram", "basic") - ).unwrap_or_else(|_| match Histogram::with_opts( - HistogramOpts::new("fallback_histogram", "fallback") - ) { - Ok(h) => h, - Err(_) => std::process::abort(), - }) - }) - }) - } -});} /// **Position Concentration Limits and Monitoring Configuration** /// diff --git a/crates/risk/src/var_calculator/expected_shortfall.rs b/crates/risk/src/var_calculator/expected_shortfall.rs index 65912a192..1ecc307b5 100644 --- a/crates/risk/src/var_calculator/expected_shortfall.rs +++ b/crates/risk/src/var_calculator/expected_shortfall.rs @@ -393,7 +393,7 @@ impl ExpectedShortfall { bootstrap_es.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); let lower_idx = ((1.0 - confidence_interval) / 2.0 * f64::from(n_bootstrap)) as usize; - let upper_idx = ((1.0 + confidence_interval) / 2.0 * f64::from(n_bootstrap)) as usize; + let upper_idx = (f64::midpoint(1.0, confidence_interval) * f64::from(n_bootstrap)) as usize; Ok(ESResult { expected_shortfall: base_es.into(),