|
| 1 | +use adapters::Client; |
| 2 | +use serde::{Deserialize, Serialize}; |
| 3 | +use serde_json::json; |
| 4 | +use serde_with::{DisplayFromStr, serde_as}; |
| 5 | +use tracing::warn; |
| 6 | +use v_exchanges_adapters::kucoin::{KucoinAuth, KucoinHttpUrl, KucoinOption}; |
| 7 | +use v_utils::trades::Asset; |
| 8 | + |
| 9 | +use crate::{ |
| 10 | + ExchangeResult, |
| 11 | + core::{AssetBalance, Balances}, |
| 12 | +}; |
| 13 | + |
| 14 | +pub async fn asset_balance(client: &v_exchanges_adapters::Client, asset: Asset, _recv_window: Option<u16>) -> ExchangeResult<AssetBalance> { |
| 15 | + assert!(client.is_authenticated::<KucoinOption>()); |
| 16 | + let balances: Balances = balances(client, None).await?; |
| 17 | + let balance: AssetBalance = balances.iter().find(|b| b.asset == asset).copied().unwrap_or_else(|| { |
| 18 | + warn!("No balance found for asset: {:?}", asset); |
| 19 | + AssetBalance { asset, ..Default::default() } |
| 20 | + }); |
| 21 | + Ok(balance) |
| 22 | +} |
| 23 | + |
| 24 | +pub async fn balances(client: &Client, _recv_window: Option<u16>) -> ExchangeResult<Balances> { |
| 25 | + assert!(client.is_authenticated::<KucoinOption>()); |
| 26 | + |
| 27 | + let options = vec![KucoinOption::HttpAuth(KucoinAuth::Sign), KucoinOption::HttpUrl(KucoinHttpUrl::Spot)]; |
| 28 | + let account_response: AccountResponse = client.get("/api/v1/accounts", &json!({}), options).await?; |
| 29 | + |
| 30 | + let mut vec_balance = Vec::new(); |
| 31 | + let total_usd = 0.0; |
| 32 | + |
| 33 | + for account in &account_response.data { |
| 34 | + // Only include accounts with non-zero balances |
| 35 | + if account.balance > 0.0 { |
| 36 | + vec_balance.push(AssetBalance { |
| 37 | + asset: (&*account.currency).into(), |
| 38 | + underlying: account.balance, |
| 39 | + usd: None, // Kucoin doesn't provide USD values in this endpoint |
| 40 | + }); |
| 41 | + } |
| 42 | + } |
| 43 | + |
| 44 | + let balances = Balances::new(vec_balance, total_usd.into()); |
| 45 | + Ok(balances) |
| 46 | +} |
| 47 | + |
| 48 | +#[derive(Debug, Deserialize, Serialize)] |
| 49 | +#[serde(rename_all = "camelCase")] |
| 50 | +pub struct AccountResponse { |
| 51 | + pub code: String, |
| 52 | + pub data: Vec<AccountData>, |
| 53 | +} |
| 54 | + |
| 55 | +#[serde_as] |
| 56 | +#[derive(Debug, Deserialize, Serialize)] |
| 57 | +#[serde(rename_all = "camelCase")] |
| 58 | +pub struct AccountData { |
| 59 | + pub id: String, |
| 60 | + pub currency: String, |
| 61 | + #[serde(rename = "type")] |
| 62 | + pub account_type: String, |
| 63 | + #[serde_as(as = "DisplayFromStr")] |
| 64 | + pub balance: f64, |
| 65 | + #[serde_as(as = "DisplayFromStr")] |
| 66 | + pub available: f64, |
| 67 | + #[serde_as(as = "DisplayFromStr")] |
| 68 | + pub holds: f64, |
| 69 | +} |
0 commit comments