fix: downgrade DQN warn noise to debug, fix clippy warnings, default dev-release

- DQN portfolio_tracker: Phase 1/2 complete logs warn→debug (noisy per-epoch)
- risk: lazy_static→LazyLock, .map→.inspect, midpoint overflow fixes
- risk: remove unused POSITION_PROCESSING_LATENCY, lazy_static dep
- CI: default DEV_RELEASE=true for fast iteration (thin LTO, 24% faster)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-02-27 23:04:37 +01:00
parent a7d18f4f7c
commit b762fecff8
8 changed files with 43 additions and 172 deletions

View File

@@ -41,9 +41,10 @@ variables:
# sccache base dir on PVC — GPU stages override to /mnt/sccache/sm_<cap>
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

1
Cargo.lock generated
View File

@@ -8252,7 +8252,6 @@ dependencies = [
"fastrand",
"futures",
"hdrhistogram",
"lazy_static",
"nalgebra 0.33.2",
"ndarray",
"num",

View File

@@ -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
);

View File

@@ -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

View File

@@ -191,14 +191,13 @@ pub fn decimal_to_f64_safe(value: Decimal, context: &str) -> RiskResult<f64> {
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
})
}

View File

@@ -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

View File

@@ -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<Counter> = 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<Gauge> = 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<Gauge> = 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<IntGauge> = 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<Counter> = 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**
///

View File

@@ -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(),