use crate::exchange::{
binance::{
models::ApiParams,
klines,
},
coinbase::{candles}
};
use std::collections::HashMap;
use std::error::Error;
use std::io;
use serde::{Deserialize, Serialize, };
#[derive(Debug, Clone, Copy)]
pub enum Intervals {
S1 = 1,
M1 = 60,
M3 = 180,
M5 = 300,
M15 = 900,
M30 = 1800,
H1 = 3600,
H2 = 7200,
H4 = 14400,
H6 = 21600,
H8 = 28800,
H12 = 43200,
D1 = 86400,
D3 = 259200,
W1 = 604800,
}
impl Intervals {
pub fn value(&self) -> u32 {
*self as u32
}
pub fn as_string(&self) -> String {
match *self {
Intervals::S1 => "1s".to_string(),
Intervals::M1 => "1m".to_string(),
Intervals::M3 => "3m".to_string(),
Intervals::M5 => "5m".to_string(),
Intervals::M15 => "15m".to_string(),
Intervals::M30 => "30m".to_string(),
Intervals::H1 => "1h".to_string(),
Intervals::H2 => "2h".to_string(),
Intervals::H4 => "4h".to_string(),
Intervals::H6 => "6h".to_string(),
Intervals::H8 => "8h".to_string(),
Intervals::H12 => "12h".to_string(),
Intervals::D1 => "1d".to_string(),
Intervals::D3 => "3d".to_string(),
Intervals::W1 => "1w".to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct KlineParams<'a> {
pub base_asset : &'a str,
pub quote_asset : &'a str,
pub interval : Intervals,
pub limit : u16,
pub base_url : Option<&'a str>,
pub source : Option<&'a str>
}
impl<'a> KlineParams<'a> {
fn get_source(&self) -> &str {
match &self.source {
Some(source) => source,
None => "api",
}
}
fn get_interval(&self) -> String { self.interval.as_string() }
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct KlinesSubset {
pub time_open : u64,
pub price_open : f64,
pub price_low : f64,
pub price_high : f64,
pub price_close : f64,
pub time_close : u64,
pub volume : f64,
}
#[allow(dead_code)]
pub fn binance(kline_params: KlineParams) -> Result<Vec<KlinesSubset>, Box<dyn Error>> {
let base_url= kline_params.base_url.unwrap_or("https://api.binance.us");
let interval= kline_params.get_interval();
let limit = (kline_params.limit + 1).to_string(); let symbol = format!("{}{}", kline_params.base_asset, kline_params.quote_asset);
let params = HashMap::from([
("interval" , interval.as_str()),
("limit" , limit.as_str()),
("symbol" , symbol.as_str())
]);
let api_params = ApiParams { base_url, endpoint: "/api/v3/klines", params: ¶ms, };
let klines_res = klines::klines(kline_params.get_source(), api_params)
.or_else(|error| match error.downcast_ref::<io::Error>() {
Some(io_error) if io_error.kind() == io::ErrorKind::NotFound => {
println!("File not found. Pulling data from remote");
klines::remote_to_file(api_params)
},
_ => Err(error),
})?;
let mut klines_data = klines_res;
klines_data.pop();
let kline_subset:Vec<KlinesSubset> = klines_data.into_iter().map(| kline| KlinesSubset {
time_open : kline.open_time,
price_open : kline.open_price,
price_low : kline.low_price,
price_high : kline.high_price,
price_close : kline.close_price,
time_close : kline.close_time,
volume : kline.volume,
}).collect();
Ok(kline_subset)
}
pub fn coinbase(kline_params: KlineParams) -> Result<Vec<KlinesSubset>, Box<dyn Error>> {
let source = kline_params.get_source();
let base_url= kline_params.base_url.unwrap_or("https://api.exchange.coinbase.com");
let granularity = kline_params.interval.value();
let limit = kline_params.limit + 1;
let symbol = format!("{}-{}",kline_params.base_asset, kline_params.quote_asset);
let klines_res = candles::candles(source, base_url, granularity, limit, &symbol)
.or_else(|error| match error.downcast_ref::<io::Error>() {
Some(io_error) if io_error.kind() == io::ErrorKind::NotFound => {
println!("{}", io_error);
println!("File not found. Pulling data from remote");
candles::remote_to_file(&base_url, granularity, limit, &symbol)
},
_ => Err(error),
})?;
let mut klines_data = klines_res;
klines_data.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
klines_data.pop();
let time_stamp_offset = granularity as u64 * 1000;
let kline_subset:Vec<KlinesSubset> = klines_data.into_iter().map(| kline| KlinesSubset {
time_open : kline.timestamp * 1000,
price_open : kline.price_open,
price_low : kline.price_low,
price_high : kline.price_high,
price_close : kline.price_close,
time_close : kline.timestamp * 1000 + time_stamp_offset - 1,
volume : kline.volume,
}).collect();
Ok(kline_subset)
}