use anyhow::{Context, Result}; use chrono::{Duration, Utc}; use jsonwebtoken::{encode, EncodingKey, Header}; use reqwest::Client; use serde::{Deserialize, Serialize}; use std::time::Instant; use crate::metrics::{RequestMetric, RequestStatus, ServiceType}; #[derive(Debug, Serialize, Deserialize)] struct Claims { sub: String, username: String, exp: i64, iat: i64, } pub struct AuthenticatedClient { client: Client, gateway_url: String, jwt_token: String, } impl AuthenticatedClient { pub async fn new( gateway_url: String, jwt_secret: &str, user_id: &str, username: &str, ) -> Result { let client = Client::builder() .timeout(std::time::Duration::from_secs(30)) .pool_max_idle_per_host(10) .build() .context("Failed to create HTTP client")?; // Generate JWT token let claims = Claims { sub: user_id.to_string(), username: username.to_string(), iat: Utc::now().timestamp(), exp: (Utc::now() + Duration::hours(24)).timestamp(), }; let jwt_token = encode( &Header::default(), &claims, &EncodingKey::from_secret(jwt_secret.as_bytes()), ) .context("Failed to generate JWT token")?; Ok(Self { client, gateway_url, jwt_token, }) } pub async fn submit_order(&self, client_id: usize, order: TestOrder) -> Result { let start = Instant::now(); let result = self .client .post(format!("{}/trading/orders", self.gateway_url)) .header("Authorization", format!("Bearer {}", self.jwt_token)) .json(&order) .send() .await; let latency = start.elapsed(); let status = match result { Ok(response) => { if response.status().is_success() { RequestStatus::Success } else if response.status().as_u16() == 429 { RequestStatus::RateLimited } else if response.status().as_u16() == 503 { RequestStatus::CircuitBreakerOpen } else { RequestStatus::Error } }, Err(e) => { if e.is_timeout() { RequestStatus::Timeout } else { RequestStatus::Error } }, }; Ok(RequestMetric { timestamp: chrono::Utc::now(), client_id, service: ServiceType::Trading, latency, status, error_type: if status != RequestStatus::Success { Some(format!("{:?}", status)) } else { None }, }) } pub async fn get_positions(&self, client_id: usize) -> Result { let start = Instant::now(); let result = self .client .get(format!("{}/trading/positions", self.gateway_url)) .header("Authorization", format!("Bearer {}", self.jwt_token)) .send() .await; let latency = start.elapsed(); let status = match result { Ok(response) => { if response.status().is_success() { RequestStatus::Success } else if response.status().as_u16() == 429 { RequestStatus::RateLimited } else if response.status().as_u16() == 503 { RequestStatus::CircuitBreakerOpen } else { RequestStatus::Error } }, Err(e) => { if e.is_timeout() { RequestStatus::Timeout } else { RequestStatus::Error } }, }; Ok(RequestMetric { timestamp: chrono::Utc::now(), client_id, service: ServiceType::Trading, latency, status, error_type: if status != RequestStatus::Success { Some(format!("{:?}", status)) } else { None }, }) } pub async fn run_backtest( &self, client_id: usize, config: BacktestConfig, ) -> Result { let start = Instant::now(); let result = self .client .post(format!("{}/backtesting/run", self.gateway_url)) .header("Authorization", format!("Bearer {}", self.jwt_token)) .json(&config) .send() .await; let latency = start.elapsed(); let status = match result { Ok(response) => { if response.status().is_success() { RequestStatus::Success } else if response.status().as_u16() == 429 { RequestStatus::RateLimited } else if response.status().as_u16() == 503 { RequestStatus::CircuitBreakerOpen } else { RequestStatus::Error } }, Err(e) => { if e.is_timeout() { RequestStatus::Timeout } else { RequestStatus::Error } }, }; Ok(RequestMetric { timestamp: chrono::Utc::now(), client_id, service: ServiceType::Backtesting, latency, status, error_type: if status != RequestStatus::Success { Some(format!("{:?}", status)) } else { None }, }) } pub async fn train_model( &self, client_id: usize, config: TrainingConfig, ) -> Result { let start = Instant::now(); let result = self .client .post(format!("{}/ml/train", self.gateway_url)) .header("Authorization", format!("Bearer {}", self.jwt_token)) .json(&config) .send() .await; let latency = start.elapsed(); let status = match result { Ok(response) => { if response.status().is_success() { RequestStatus::Success } else if response.status().as_u16() == 429 { RequestStatus::RateLimited } else if response.status().as_u16() == 503 { RequestStatus::CircuitBreakerOpen } else { RequestStatus::Error } }, Err(e) => { if e.is_timeout() { RequestStatus::Timeout } else { RequestStatus::Error } }, }; Ok(RequestMetric { timestamp: chrono::Utc::now(), client_id, service: ServiceType::MlTraining, latency, status, error_type: if status != RequestStatus::Success { Some(format!("{:?}", status)) } else { None }, }) } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TestOrder { pub symbol: String, pub quantity: f64, pub side: String, pub order_type: String, } impl TestOrder { pub fn random() -> Self { use rand::Rng; let mut rng = rand::thread_rng(); let symbols = ["AAPL", "GOOGL", "MSFT", "AMZN", "TSLA"]; let sides = ["buy", "sell"]; let order_types = ["market", "limit"]; Self { symbol: symbols .get(rng.gen_range(0..symbols.len())) .map_or_else(|| "AAPL".to_owned(), |s| (*s).to_string()), quantity: rng.gen_range(1.0..101.0), side: sides .get(rng.gen_range(0..sides.len())) .map_or_else(|| "buy".to_owned(), |s| (*s).to_string()), order_type: order_types .get(rng.gen_range(0..order_types.len())) .map_or_else(|| "market".to_owned(), |s| (*s).to_string()), } } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct BacktestConfig { pub strategy: String, pub start_date: String, pub end_date: String, } impl BacktestConfig { pub fn default() -> Self { Self { strategy: "momentum".to_owned(), start_date: "2024-01-01".to_owned(), end_date: "2024-12-31".to_owned(), } } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TrainingConfig { pub model_type: String, pub epochs: u32, } impl TrainingConfig { pub fn default() -> Self { Self { model_type: "mamba2".to_owned(), epochs: 10_u32, } } }