hypercall/observability/metrics_collector/
trading.rs1use super::*;
2
3impl MetricsCollector {
4 pub(super) async fn collect_trading_metrics(&self) {
7 let portfolios = self.portfolio_cache.get_all_portfolios().await;
9
10 let mut total_positions: i64 = 0;
11 let mut accounts_with_positions: i64 = 0;
12 let mut total_realized_pnl: f64 = 0.0;
13 let mut total_unrealized_pnl: f64 = 0.0;
14 let mut total_notional_usd: f64 = 0.0;
15 let mut conversion_failures: i64 = 0;
16
17 let mut open_interest_by_underlying: HashMap<String, f64> = HashMap::new();
19
20 for portfolio in portfolios.values() {
21 let positions = &portfolio.positions;
22 if !positions.is_empty() {
23 accounts_with_positions += 1;
24 }
25
26 for position in positions.values() {
27 total_positions += 1;
28
29 let Some(amount) = position.amount.to_f64() else {
30 warn!(
31 symbol = %position.symbol,
32 amount = %position.amount,
33 "Failed to convert position amount for trading metrics"
34 );
35 conversion_failures += 1;
36 continue;
37 };
38 let Some(entry_price) = position.entry_price.to_f64() else {
39 warn!(
40 symbol = %position.symbol,
41 entry_price = %position.entry_price,
42 "Failed to convert position entry price for trading metrics"
43 );
44 conversion_failures += 1;
45 continue;
46 };
47 let Some(realized) = position.realized_pnl.to_f64() else {
48 warn!(
49 symbol = %position.symbol,
50 realized_pnl = %position.realized_pnl,
51 "Failed to convert realized PnL for trading metrics"
52 );
53 conversion_failures += 1;
54 continue;
55 };
56 let Some(unrealized) = position.unrealized_pnl.to_f64() else {
57 warn!(
58 symbol = %position.symbol,
59 unrealized_pnl = %position.unrealized_pnl,
60 "Failed to convert unrealized PnL for trading metrics"
61 );
62 conversion_failures += 1;
63 continue;
64 };
65
66 total_realized_pnl += realized;
67 total_unrealized_pnl += unrealized;
68 total_notional_usd += (amount * entry_price).abs();
69
70 let underlying = position
72 .symbol
73 .split('-')
74 .next()
75 .unwrap_or("UNKNOWN")
76 .to_string();
77
78 *open_interest_by_underlying.entry(underlying).or_insert(0.0) += amount.abs();
79 }
80 }
81
82 gauge!("ht_positions").set(total_positions as f64);
85 gauge!("ht_active_accounts").set(accounts_with_positions as f64);
86 gauge!("ht_realized_pnl_usd").set(total_realized_pnl);
87 gauge!("ht_unrealized_pnl_usd").set(total_unrealized_pnl);
88 gauge!("ht_position_notional_usd").set(total_notional_usd);
89 gauge!("ht_trading_metrics_conversion_failures").set(conversion_failures as f64);
90 gauge!("ht_trading_metrics_complete").set(if conversion_failures == 0 { 1.0 } else { 0.0 });
91
92 for (underlying, oi) in open_interest_by_underlying {
94 gauge!("ht_open_interest", "underlying" => underlying).set(oi);
95 }
96
97 debug!(
98 "Trading metrics: {} positions, {} accounts, ${:.0} notional, {} conversion failures",
99 total_positions, accounts_with_positions, total_notional_usd, conversion_failures
100 );
101 }
102
103 pub(super) async fn collect_market_stats_metrics(&self) {
108 let Some(ref market_stats) = self.market_stats_cache else {
109 return;
110 };
111
112 let all_stats = market_stats.get_all_stats().await;
114
115 let mut total_volume_24h: f64 = 0.0;
116 let mut total_open_interest: f64 = 0.0;
117 let mut volume_by_underlying: HashMap<String, f64> = HashMap::new();
118
119 for (symbol, (volume, oi)) in &all_stats {
120 if let Some(v) = volume.to_f64() {
122 total_volume_24h += v;
123
124 let underlying = symbol.split('-').next().unwrap_or("UNKNOWN").to_string();
126 *volume_by_underlying.entry(underlying).or_insert(0.0) += v;
127 }
128 if let Some(o) = oi.to_f64() {
129 total_open_interest += o;
130 }
131 }
132
133 gauge!("ht_volume_24h_total_usd").set(total_volume_24h);
135 gauge!("ht_open_interest_total").set(total_open_interest);
136
137 for (underlying, vol) in volume_by_underlying {
139 gauge!("ht_volume_24h_usd", "underlying" => underlying).set(vol);
140 }
141
142 debug!(
143 "Market stats: ${:.0} 24h volume, {:.2} total OI ({} symbols)",
144 total_volume_24h,
145 total_open_interest,
146 all_stats.len()
147 );
148 }
149}