v_exchanges 0.17.1

Implementations of HTTP/HTTPS/WebSocket API methods for some crypto exchanges, using [crypto-botters](<https://github.com/negi-grass/crypto-botters>) framework
Documentation
use adapters::Client;
//HACK: Methods should be implemented on the central interface struct, following <https://github.com/wisespace-io/binance-rs>.
use serde_with::{DisplayFromStr, serde_as};
use v_exchanges_adapters::binance::{BinanceHttpUrl, BinanceOption};
use v_utils::prelude::*;

use crate::ExchangeResult;

// price {{{
//HACK: should use /fapi/v2/ticker/price instead
pub async fn price(client: &Client, pair: Pair) -> ExchangeResult<f64> {
	let params = json!({
		"symbol": pair.fmt_binance(),
	});

	let options = vec![BinanceOption::HttpUrl(BinanceHttpUrl::FuturesUsdM)];
	let r: MarkPriceResponse = client.get("/fapi/v1/premiumIndex", &params, options).await?;
	let price = r.index_price; // when using this framework, we care for per-exchange price, obviously
	Ok(price)
}

pub async fn prices(client: &Client, pairs: Option<Vec<Pair>>) -> ExchangeResult<BTreeMap<Pair, f64>> {
	let options = vec![BinanceOption::HttpUrl(BinanceHttpUrl::FuturesUsdM)];
	let rs: Vec<PriceObject> = match pairs {
		Some(pairs) => {
			let params = json!({
				"symbols": pairs.into_iter().map(|p| p.to_string()).collect::<Vec<String>>(),
			});
			client.get("/fapi/v1/ticker/price", &params, options).await?
		}
		None => client.get_no_query("/fapi/v2/ticker/price", options).await?,
	};
	Ok(rs.into_iter().map(Into::into).collect())
}

#[serde_as]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MarkPriceResponse {
	pub symbol: String,
	#[serde_as(as = "DisplayFromStr")]
	pub mark_price: f64,
	#[serde_as(as = "DisplayFromStr")]
	pub index_price: f64,
	#[serde_as(as = "DisplayFromStr")]
	pub estimated_settle_price: f64,
	#[serde_as(as = "DisplayFromStr")]
	pub last_funding_rate: f64,
	pub next_funding_time: u64,
	pub time: u64,
}

#[serde_as]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct PriceObject {
	#[serde_as(as = "DisplayFromStr")]
	price: f64,
	symbol: String,
	time: i64,
}
impl From<PriceObject> for (Pair, f64) {
	fn from(p: PriceObject) -> Self {
		(Pair::from_str(&p.symbol).expect("Assume v_utils can handle all Binance pairs"), p.price)
	}
}

//,}}}