v_exchanges 0.17.3

Implementations of HTTP/HTTPS/WebSocket API methods for some crypto exchanges, using [crypto-botters](<https://github.com/negi-grass/crypto-botters>) framework
Documentation
use eyre::{Result, bail};
use jiff::Timestamp;
use reqwest::Client;
use serde::Deserialize;

pub async fn bvol(duration: std::time::Duration) -> Result<Vec<BvolPoint>> {
	let to_cut = duration.as_secs() % (5 * 60);
	let n_5m = (duration.as_secs() - to_cut) / (5 * 60);
	if n_5m == 0 {
		bail!("Provided duration is less than 5m");
	}

	let client = Client::default();
	let r = client
		.get(format!("https://www.bitmex.com/api/v1/trade?symbol=.BVOL24H&count={n_5m}&reverse=true"))
		.send()
		.await?
		.json::<Vec<BvolResponse>>()
		.await?;
	let out = r.into_iter().map(|r| r.into()).collect();
	Ok(out)
}

#[derive(Clone, Debug, Default)]
pub struct Bitmex {}
impl Bitmex {
	/// `duration` will be rounded down to nearest multiple of 5m.
	pub async fn bvol(&self, duration: std::time::Duration) -> Result<Vec<BvolPoint>> {
		bvol(duration).await
	}
}

#[derive(Clone, Debug, Default)]
pub struct BvolPoint {
	pub timestamp: Timestamp,
	pub price: f64,
}

#[derive(Clone, Debug, Default, Deserialize, derive_new::new)]
#[serde(rename_all = "camelCase")]
pub struct BvolResponse {
	pub timestamp: Timestamp,
	pub price: f64,
	pub symbol: String,
	pub side: String,
	pub size: u64,
	pub tick_direction: String,
	pub trd_type: String,
}

impl From<BvolResponse> for BvolPoint {
	fn from(r: BvolResponse) -> Self {
		Self {
			timestamp: r.timestamp,
			price: r.price,
		}
	}
}