File size: 3,080 Bytes
8294aff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// Binance API module
// Integration with Binance for live trading

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BinanceConfig {
    pub api_key: String,
    pub api_secret: String,
    pub testnet: bool,
}

impl BinanceConfig {
    pub fn base_url(&self) -> &str {
        if self.testnet {
            "https://testnet.binance.vision/api"
        } else {
            "https://api.binance.com/api"
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BinanceKline {
    pub open_time: i64,
    pub open: f64,
    pub high: f64,
    pub low: f64,
    pub close: f64,
    pub volume: f64,
    pub close_time: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BinanceTicker {
    pub symbol: String,
    pub last_price: f64,
    pub price_change_percent: f64,
    pub volume: f64,
}

pub struct BinanceClient {
    config: BinanceConfig,
    client: reqwest::Client,
}

impl BinanceClient {
    pub fn new(config: BinanceConfig) -> Self {
        Self {
            config,
            client: reqwest::Client::new(),
        }
    }
    
    pub async fn get_klines(&self, symbol: &str, interval: &str, limit: usize) -> Result<Vec<BinanceKline>, Box<dyn std::error::Error + Send + Sync>> {
        let url = format!("{}/v3/klines", self.config.base_url());
        let response = self.client.get(&url)
            .query(&[
                ("symbol", symbol),
                ("interval", interval),
                ("limit", &limit.to_string()),
            ])
            .send()
            .await?;
        
        let data: Vec<Vec<serde_json::Value>> = response.json().await?;
        
        let klines: Vec<BinanceKline> = data.iter().map(|k| BinanceKline {
            open_time: k[0].as_i64().unwrap_or(0),
            open: k[1].as_str().unwrap_or("0").parse().unwrap_or(0.0),
            high: k[2].as_str().unwrap_or("0").parse().unwrap_or(0.0),
            low: k[3].as_str().unwrap_or("0").parse().unwrap_or(0.0),
            close: k[4].as_str().unwrap_or("0").parse().unwrap_or(0.0),
            volume: k[5].as_str().unwrap_or("0").parse().unwrap_or(0.0),
            close_time: k[6].as_i64().unwrap_or(0),
        }).collect();
        
        Ok(klines)
    }
    
    pub async fn get_ticker(&self, symbol: &str) -> Result<BinanceTicker, Box<dyn std::error::Error + Send + Sync>> {
        let url = format!("{}/v3/ticker/24hr", self.config.base_url());
        let response = self.client.get(&url)
            .query(&[("symbol", symbol)])
            .send()
            .await?;
        
        let data: serde_json::Value = response.json().await?;
        
        Ok(BinanceTicker {
            symbol: data["symbol"].as_str().unwrap_or("").to_string(),
            last_price: data["lastPrice"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
            price_change_percent: data["priceChangePercent"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
            volume: data["volume"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
        })
    }
}