use axum::{ routing::{get, post}, Router, Json, extract::State, }; use serde::{Deserialize, Serialize}; use std::sync::Arc; use tokio::sync::Mutex; use chrono::{DateTime, Utc}; mod trading; mod binance; pub use trading::*; pub use binance::*; #[derive(Clone)] pub struct AppState { pub config: TradingConfig, pub positions: Arc>>, pub orders: Arc>>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TradingConfig { pub api_key: String, pub api_secret: String, pub testnet: bool, pub max_position_size: f64, pub stop_loss_percent: f64, pub take_profit_percent: f64, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Position { pub id: String, pub symbol: String, pub side: String, pub quantity: f64, pub entry_price: f64, pub current_price: f64, pub pnl: f64, pub opened_at: DateTime, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Order { pub id: String, pub symbol: String, pub side: String, pub order_type: String, pub quantity: f64, pub price: Option, pub status: String, pub created_at: DateTime, } #[derive(Debug, Deserialize)] pub struct CreateOrderRequest { pub symbol: String, pub side: String, pub quantity: f64, pub price: Option, } #[derive(Debug, Deserialize)] pub struct ClosePositionRequest { pub position_id: String, } #[derive(Debug, Serialize)] pub struct ApiResponse { pub success: bool, pub data: Option, pub error: Option, } async fn health() -> Json> { Json(ApiResponse { success: true, data: Some("RapidLiveClient API running".to_string()), error: None, }) } async fn get_balance(State(state): State) -> Json> { let balance = state.config.testnet as i32 as f64 * 10000.0; Json(ApiResponse { success: true, data: Some(balance), error: None, }) } async fn get_positions(State(state): State) -> Json>> { let positions = state.positions.lock().await.clone(); Json(ApiResponse { success: true, data: Some(positions), error: None, }) } async fn get_orders(State(state): State) -> Json>> { let orders = state.orders.lock().await.clone(); Json(ApiResponse { success: true, data: Some(orders), error: None, }) } async fn create_order( State(state): State, Json(req): Json, ) -> Json> { let order = Order { id: format!("ORD-{}", chrono::Utc::now().timestamp_millis()), symbol: req.symbol.clone(), side: req.side.clone(), order_type: if req.price.is_some() { "LIMIT".to_string() } else { "MARKET".to_string() }, quantity: req.quantity, price: req.price, status: "pending".to_string(), created_at: Utc::now(), }; state.orders.lock().await.push(order.clone()); Json(ApiResponse { success: true, data: Some(order), error: None, }) } async fn close_position( State(state): State, Json(req): Json, ) -> Json> { let mut positions = state.positions.lock().await; if let Some(pos) = positions.iter_mut().find(|p| p.id == req.position_id) { let current_price = pos.current_price; let pnl = if pos.side == "BUY" { (current_price - pos.entry_price) * pos.quantity } else { (pos.entry_price - current_price) * pos.quantity }; pos.pnl = pnl; Json(ApiResponse { success: true, data: Some(pos.clone()), error: None, }) } else { Json(ApiResponse { success: false, data: None, error: Some("Position not found".to_string()), }) } } async fn analyze_market( State(state): State, Json(req): Json, ) -> Json> { let symbol = req.get("symbol") .and_then(|s| s.as_str()) .unwrap_or("BTCUSDT"); let analysis = serde_json::json!({ "symbol": symbol, "recommendation": "BUY", "confidence": 0.75, "reason": "RSI oversold, trend bullish", "entry_price": 50000.0, "stop_loss": 47500.0, "take_profit": 55000.0, "risk_level": "MEDIUM" }); Json(ApiResponse { success: true, data: Some(analysis), error: None, }) } #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); let config = TradingConfig { api_key: std::env::var("BINANCE_API_KEY").unwrap_or_default(), api_secret: std::env::var("BINANCE_API_SECRET").unwrap_or_default(), testnet: true, max_position_size: 1000.0, stop_loss_percent: 5.0, take_profit_percent: 10.0, }; let state = AppState { config, positions: Arc::new(Mutex::new(Vec::new())), orders: Arc::new(Mutex::new(Vec::new())), }; let app = Router::new() .route("/", get(health)) .route("/health", get(health)) .route("/api/balance", get(get_balance)) .route("/api/positions", get(get_positions)) .route("/api/orders", get(get_orders)) .route("/api/orders", post(create_order)) .route("/api/positions/close", post(close_position)) .route("/api/analyze", post(analyze_market)) .with_state(state); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); tracing::info!("RapidLiveClient API running on http://0.0.0.0:3000"); axum::serve(listener, app).await.unwrap(); }