#[derive(Debug)]
pub struct Candle {
pub time: i64,
pub open: f64,
pub close: f64,
pub high: f64,
pub low: f64,
pub volume: f64,
pub turnover: f64,
}
use chrono::Utc;
use reqwest::Client;
pub async fn get_candles(
client: &Client,
symbol: &str,
interval: &str,
count: usize,
) -> Result<Vec<Candle>, Box<dyn std::error::Error>> {
let seconds_per_candle = match interval {
"1min" => 60,
"5min" => 300,
"15min" => 900,
"1hour" => 3600,
"4hour" => 14400,
"1day" => 86400,
"1week" => 604800,
_ => return Err("Unsupported interval".into()),
};
let end_at = Utc::now().timestamp() - seconds_per_candle;
let start_at = end_at - (seconds_per_candle * count as i64);
let url = "https://api.kucoin.com/api/v1/market/candles";
let res = client
.get(url)
.query(&[
("symbol", symbol),
("type", interval),
("startAt", &start_at.to_string()),
])
.send()
.await?
.json::<serde_json::Value>()
.await?;
let data = res["data"]
.as_array()
.ok_or(format!("Invalid KuCoin response for symbol {}", symbol))?;
let mut candles = Vec::with_capacity(count);
for entry in data.iter().take(count) {
let c = Candle {
time: entry[0].as_str().unwrap().parse()?,
open: entry[1].as_str().unwrap().parse()?,
close: entry[2].as_str().unwrap().parse()?,
high: entry[3].as_str().unwrap().parse()?,
low: entry[4].as_str().unwrap().parse()?,
volume: entry[5].as_str().unwrap().parse()?,
turnover: entry[6].as_str().unwrap().parse()?,
};
candles.push(c);
}
Ok(candles)
}
#[cfg(test)]
mod tests {
use super::*;
use reqwest::Client;
#[tokio::test]
async fn test_get_weekly_candles_btc() {
let client = Client::new();
let count = 5;
let candles = get_candles(&client, "BTC-USDT", "1week", count)
.await
.expect("Failed to fetch candles");
assert_eq!(
candles.len(),
count,
"Expected {} candles, got {}",
count,
candles.len()
);
for i in 1..candles.len() {
assert!(
candles[i].time > candles[i - 1].time,
"Candles are not time-ordered"
);
}
for candle in &candles {
assert!(candle.open > 0.0);
assert!(candle.close > 0.0);
assert!(candle.high >= candle.low);
assert!(candle.volume >= 0.0);
}
let now = chrono::Utc::now().timestamp();
assert!(
candles.last().unwrap().time < now,
"Last candle appears to be unfinished"
);
}
}