use std::time::Duration;
use std::thread;
use std::collections::HashMap;
fn main() {
println!("📊 Precolator Position Monitor v1.0.0");
println!("Starting position monitoring service...\n");
let config = PositionMonitorConfig::default();
let mut monitor = PositionMonitor::new(config);
loop {
match monitor.update_positions() {
Ok(_) => {
monitor.print_summary();
}
Err(e) => {
eprintln!("❌ Error updating positions: {}", e);
}
}
thread::sleep(Duration::from_secs(monitor.config.update_interval_secs as u64));
}
}
struct PositionMonitorConfig {
update_interval_secs: u32,
alert_health_factor_threshold: u16,
#[allow(dead_code)]
alert_pnl_threshold_pct: i32,
#[allow(dead_code)]
enable_notifications: bool,
}
impl Default for PositionMonitorConfig {
fn default() -> Self {
Self {
update_interval_secs: 5,
alert_health_factor_threshold: 2000, alert_pnl_threshold_pct: -50, enable_notifications: true,
}
}
}
struct PositionData {
position_id: String,
trader: String,
side: String,
size: u64,
entry_price: u64,
current_price: u64,
collateral: u64,
leverage: u8,
pnl: i64,
health_factor: u16,
liquidation_price: u64,
}
struct PositionMonitor {
config: PositionMonitorConfig,
positions: HashMap<String, PositionData>,
total_pnl: i64,
update_count: u64,
}
impl PositionMonitor {
fn new(config: PositionMonitorConfig) -> Self {
Self {
config,
positions: HashMap::new(),
total_pnl: 0,
update_count: 0,
}
}
fn update_positions(&mut self) -> Result<(), String> {
self.update_count += 1;
self.positions.clear();
let sample_position = PositionData {
position_id: "pos_001".to_string(),
trader: "trader_1".to_string(),
side: "Long".to_string(),
size: 1_000_000,
entry_price: 100,
current_price: 105,
collateral: 1_000_000,
leverage: 10,
pnl: 50_000,
health_factor: 8500,
liquidation_price: 90,
};
self.positions.insert("pos_001".to_string(), sample_position);
self.total_pnl = 50_000;
Ok(())
}
fn print_summary(&self) {
println!("\n📊 Position Summary (Update #{})", self.update_count);
println!("{}", "═".repeat(60));
println!("Total Positions: {}", self.positions.len());
println!("Total P&L: ${}", self.total_pnl);
if self.positions.is_empty() {
println!("No open positions");
return;
}
println!("\nPosition Details:");
println!("{}", "─".repeat(60));
for (_, pos) in &self.positions {
let pnl_pct = if pos.collateral > 0 {
(pos.pnl as i128 * 10000) / pos.collateral as i128
} else {
0
} as i32;
println!(" Position: {}", pos.position_id);
println!(" Trader: {}", pos.trader);
println!(" Side: {} | Size: {} | Leverage: {}x", pos.side, pos.size, pos.leverage);
println!(" Entry: ${} | Current: ${} | P&L: ${} ({:.2}%)",
pos.entry_price, pos.current_price, pos.pnl,
pnl_pct as f64 / 100.0);
println!(" Health: {:.2}% | Liquidation: ${}",
pos.health_factor as f64 / 100.0, pos.liquidation_price);
if pos.health_factor < self.config.alert_health_factor_threshold {
println!(" ⚠️ WARNING: Low health factor!");
}
println!();
}
println!("{}", "═".repeat(60));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_position_monitor_config() {
let config = PositionMonitorConfig::default();
assert_eq!(config.update_interval_secs, 5);
assert!(config.enable_notifications);
}
#[test]
fn test_position_monitor_creation() {
let monitor = PositionMonitor::new(PositionMonitorConfig::default());
assert_eq!(monitor.positions.len(), 0);
assert_eq!(monitor.total_pnl, 0);
}
}