From 0ba182834b51c11a7a7ac9bf55585a43cd24fc45 Mon Sep 17 00:00:00 2001 From: Mukan Erkin Date: Sun, 19 Apr 2026 06:41:58 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20coin=20se=C3=A7ici=20dropdown=20+=20oto?= =?UTF-8?q?matik=20minNotional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/bots.rs | 22 ++++++++++++++++ src/api/routes.rs | 1 + src/binance/client.rs | 34 +++++++++++++++++++++++++ src/config.rs | 2 ++ src/main.rs | 6 +++++ src/web/index.html | 58 +++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 121 insertions(+), 2 deletions(-) diff --git a/src/api/bots.rs b/src/api/bots.rs index 6407221..3441f1c 100644 --- a/src/api/bots.rs +++ b/src/api/bots.rs @@ -7,9 +7,31 @@ use axum::{ use serde::{Deserialize, Serialize}; use uuid::Uuid; +use crate::binance::client::BinanceClient; use crate::storage::config::BotConfig; use crate::AppState; +#[derive(Serialize)] +pub struct SymbolInfo { + pub symbol: String, + pub min_notional: f64, +} + +pub async fn list_symbols(State(state): State) -> impl IntoResponse { + let client = BinanceClient::new(state.api_key, state.api_secret, state.testnet); + match client.get_usdt_symbols_with_min().await { + Ok(mut symbols) => { + symbols.sort_by(|a, b| a.0.cmp(&b.0)); + let result: Vec = symbols + .into_iter() + .map(|(symbol, min_notional)| SymbolInfo { symbol, min_notional }) + .collect(); + Json(result).into_response() + } + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(), + } +} + #[derive(Deserialize)] pub struct CreateBotRequest { pub name: String, diff --git a/src/api/routes.rs b/src/api/routes.rs index 3648627..94ca887 100644 --- a/src/api/routes.rs +++ b/src/api/routes.rs @@ -9,6 +9,7 @@ use crate::AppState; pub fn build(state: AppState) -> Router { let api = Router::new() + .route("/symbols", get(bots::list_symbols)) .route("/bots", get(bots::list_bots).post(bots::create_bot)) .route("/bots/{id}", delete(bots::delete_bot)) .route("/bots/{id}/start", post(bots::start_bot)) diff --git a/src/binance/client.rs b/src/binance/client.rs index aedba97..6b5a215 100644 --- a/src/binance/client.rs +++ b/src/binance/client.rs @@ -231,6 +231,40 @@ impl BinanceClient { Ok(symbols) } + /// USDT çiftlerini ve her birinin minNotional değerini döner + pub async fn get_usdt_symbols_with_min(&self) -> Result> { + let url = format!("{}/api/v3/exchangeInfo", self.base_url); + let response = self.http.get(&url).send().await?.json::().await?; + + let symbols = response["symbols"] + .as_array() + .ok_or_else(|| anyhow!("exchangeInfo parse hatası"))? + .iter() + .filter_map(|s| { + let symbol = s["symbol"].as_str()?; + let quote = s["quoteAsset"].as_str()?; + let status = s["status"].as_str()?; + if quote != "USDT" || status != "TRADING" || symbol.starts_with("USDT") { + return None; + } + let base = s["baseAsset"].as_str()?; + let filters: std::collections::HashMap<&str, &Value> = s["filters"] + .as_array()? + .iter() + .filter_map(|f| Some((f["filterType"].as_str()?, f))) + .collect(); + let min_notional = filters + .get("NOTIONAL") + .and_then(|f| f["minNotional"].as_str()) + .and_then(|v| v.parse::().ok()) + .unwrap_or(5.0); + Some((base.to_string(), min_notional)) + }) + .collect(); + + Ok(symbols) + } + /// Binance hesabındaki sıfır olmayan tüm bakiyeleri döner: Vec<(asset, free, locked)> pub async fn get_account_balances(&self) -> Result> { let timestamp = chrono::Utc::now().timestamp_millis().to_string(); diff --git a/src/config.rs b/src/config.rs index ca5b284..ade3827 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,6 +7,7 @@ pub struct AppConfig { pub auth_token: String, pub db_path: String, pub listen_addr: String, + pub testnet: bool, } impl AppConfig { @@ -17,6 +18,7 @@ impl AppConfig { auth_token: env::var("AUTH_TOKEN").expect("AUTH_TOKEN gerekli"), db_path: env::var("DB_PATH").unwrap_or_else(|_| "data/bots.db".to_string()), listen_addr: env::var("LISTEN_ADDR").unwrap_or_else(|_| "127.0.0.1:4646".to_string()), + testnet: env::var("BINANCE_TESTNET").map(|v| v == "true").unwrap_or(false), } } } diff --git a/src/main.rs b/src/main.rs index c7c82cf..6a969ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,9 @@ pub struct AppState { pub manager: Arc>, pub event_tx: broadcast::Sender, pub auth_token: String, + pub api_key: String, + pub api_secret: String, + pub testnet: bool, } #[tokio::main] @@ -63,6 +66,9 @@ async fn main() { manager, event_tx, auth_token: cfg.auth_token, + api_key: cfg.api_key, + api_secret: cfg.api_secret, + testnet: cfg.testnet, }; let router = api::routes::build(state); diff --git a/src/web/index.html b/src/web/index.html index cf9fb09..9ae8671 100644 --- a/src/web/index.html +++ b/src/web/index.html @@ -89,7 +89,12 @@
Yeni Bot Ekle
-
+
+ + + + +
-
+
+ + +