use std::collections::HashMap;
use std::future::Future;
use std::string::ToString;
use reqwest::Client;
use rust_decimal::Decimal;
use serde::de::DeserializeOwned;
use crate::{
accounts, beneficiaries, credentials, market, orders, quotes, trades, transactions, urls,
};
const API_BASE: &str = "https://api.mybitx.com/api/1/";
pub struct LunoClient {
pub(crate) credentials: credentials::Credentials,
pub(crate) http: Client,
pub(crate) url_maker: urls::UrlMaker,
}
impl LunoClient {
pub fn new(key: String, secret: String) -> LunoClient {
let credentials = credentials::Credentials::new(key, secret);
let http = Client::new();
let url_maker = urls::UrlMaker::new(API_BASE.to_owned());
LunoClient {
credentials,
url_maker,
http,
}
}
pub(crate) async fn get<T>(&self, url: reqwest::Url) -> Result<T, reqwest::Error>
where
T: DeserializeOwned,
{
self.http
.get(url)
.basic_auth(
self.credentials.key.to_owned(),
Some(self.credentials.secret.to_owned()),
)
.send()
.await?
.json::<T>()
.await
}
pub(crate) async fn put<T>(&self, url: reqwest::Url) -> Result<T, reqwest::Error>
where
T: DeserializeOwned,
{
self.http
.put(url)
.basic_auth(
self.credentials.key.to_owned(),
Some(self.credentials.secret.to_owned()),
)
.send()
.await?
.json::<T>()
.await
}
pub(crate) async fn delete<T>(&self, url: reqwest::Url) -> Result<T, reqwest::Error>
where
T: DeserializeOwned,
{
self.http
.delete(url)
.basic_auth(
self.credentials.key.to_owned(),
Some(self.credentials.secret.to_owned()),
)
.send()
.await?
.json::<T>()
.await
}
pub fn get_ticker(
&self,
pair: market::TradingPair,
) -> impl Future<Output = Result<market::Ticker, reqwest::Error>> + '_ {
let url = self.url_maker.ticker(pair);
self.get(url)
}
pub fn list_tickers(
&self,
) -> impl Future<Output = Result<market::TickerList, reqwest::Error>> + '_ {
let url = self.url_maker.tickers();
self.get(url)
}
pub fn get_orderbook_top(
&self,
pair: market::TradingPair,
) -> impl Future<Output = Result<market::Orderbook, reqwest::Error>> + '_ {
let url = self.url_maker.orderbook_top(pair);
self.get(url)
}
pub fn get_orderbook(
&self,
pair: market::TradingPair,
) -> impl Future<Output = Result<market::Orderbook, reqwest::Error>> + '_ {
let url = self.url_maker.orderbook(pair);
self.get(url)
}
pub fn list_trades(
&self,
pair: market::TradingPair,
) -> impl Future<Output = Result<market::TradeList, reqwest::Error>> + '_ {
let url = self.url_maker.trades(pair);
self.get(url)
}
pub async fn create_account(
&self,
currency: market::Currency,
name: &str,
) -> Result<accounts::Account, reqwest::Error> {
let url = self.url_maker.accounts();
let mut params = HashMap::new();
params.insert("currency", currency.to_string());
params.insert("name", name.to_string());
self.http
.post(url)
.basic_auth(
self.credentials.key.to_owned(),
Some(self.credentials.secret.to_owned()),
)
.form(¶ms)
.send()
.await?
.json::<accounts::Account>()
.await
}
pub fn update_account_name(
&self,
account_id: &str,
name: &str,
) -> impl Future<Output = Result<accounts::UpdateAccountNameResponse, reqwest::Error>> + '_
{
let url = self.url_maker.account_name(account_id, name);
self.put(url)
}
pub fn list_balances(
&self,
) -> impl Future<Output = Result<accounts::BalanceList, reqwest::Error>> + '_ {
let url = self.url_maker.balance();
self.get(url)
}
pub fn list_transactions(
&self,
account_id: &str,
min_row: i64,
max_row: i64,
) -> impl Future<Output = Result<transactions::TransactionList, reqwest::Error>> + '_ {
let url = self.url_maker.transactions(account_id, min_row, max_row);
self.get(url)
}
pub fn list_pending_transactions(
&self,
account_id: &str,
) -> impl Future<Output = Result<transactions::PendingTransactionList, reqwest::Error>> + '_
{
let url = self.url_maker.pending_transactions(account_id);
self.get(url)
}
pub fn list_beneficiaries(
&self,
) -> impl Future<Output = Result<beneficiaries::ListBeneficiariesResponse, reqwest::Error>> + '_
{
let url = self.url_maker.beneficiaries();
self.get(url)
}
pub fn list_orders(&self) -> orders::ListOrdersBuilder {
orders::ListOrdersBuilder {
luno_client: self,
url: self.url_maker.list_orders(),
limit: None,
created_before: None,
pair: None,
state: None,
}
}
pub fn limit_order(
&self,
pair: market::TradingPair,
order_type: orders::LimitOrderType,
volume: Decimal,
price: Decimal,
) -> orders::PostLimitOrderBuilder {
let mut params = HashMap::new();
params.insert("pair", pair.to_string());
params.insert("type", order_type.to_string());
params.insert("volume", volume.to_string());
params.insert("price", price.to_string());
orders::PostLimitOrderBuilder {
luno_client: self,
url: self.url_maker.post_order(),
params,
}
}
pub fn market_order(
&self,
pair: market::TradingPair,
order_type: orders::MarketOrderType,
volume: Decimal,
) -> orders::PostMarketOrderBuilder {
let mut params = HashMap::new();
params.insert("pair", pair.to_string());
params.insert("type", order_type.to_string());
match order_type {
orders::MarketOrderType::BUY => params.insert("counter_volume", volume.to_string()),
orders::MarketOrderType::SELL => params.insert("base_volume", volume.to_string()),
};
orders::PostMarketOrderBuilder {
luno_client: self,
url: self.url_maker.market_order(),
params,
}
}
pub async fn stop_order(
&self,
order_id: &str,
) -> Result<orders::StopOrderResponse, reqwest::Error> {
let url = self.url_maker.stop_order();
let mut params = HashMap::new();
params.insert("order_id", order_id.to_string());
self.http
.post(url)
.basic_auth(
self.credentials.key.to_owned(),
Some(self.credentials.secret.to_owned()),
)
.form(¶ms)
.send()
.await?
.json::<orders::StopOrderResponse>()
.await
}
pub fn get_order(
&self,
order_id: &str,
) -> impl Future<Output = Result<orders::Order, reqwest::Error>> + '_ {
let url = self.url_maker.orders(order_id);
self.get(url)
}
pub fn list_own_trades(&self, pair: market::TradingPair) -> trades::ListTradesBuilder {
trades::ListTradesBuilder {
luno_client: self,
url: self.url_maker.list_trades(pair),
since: None,
before: None,
after_seq: None,
before_seq: None,
sort_desc: None,
limit: None,
}
}
pub fn get_fee_info(
&self,
pair: market::TradingPair,
) -> impl Future<Output = Result<trades::FeeInfo, reqwest::Error>> + '_ {
let url = self.url_maker.fee_info(pair);
self.get(url)
}
pub fn quote(
&self,
order_type: orders::MarketOrderType,
base_amount: Decimal,
pair: market::TradingPair,
) -> quotes::CreateQuoteBuilder {
let mut params = HashMap::new();
params.insert("type", order_type.to_string());
params.insert("base_amount", base_amount.to_string());
params.insert("pair", pair.to_string());
quotes::CreateQuoteBuilder {
luno_client: self,
url: self.url_maker.quotes(),
params,
}
}
pub fn get_quote(
&self,
id: &str,
) -> impl Future<Output = Result<quotes::Quote, reqwest::Error>> + '_ {
let url = self.url_maker.quote_action(id);
self.get(url)
}
pub fn exercise_quote(
&self,
id: &str,
) -> impl Future<Output = Result<quotes::Quote, reqwest::Error>> + '_ {
let url = self.url_maker.quote_action(id);
self.put(url)
}
pub fn discard_quote(
&self,
id: &str,
) -> impl Future<Output = Result<quotes::Quote, reqwest::Error>> + '_ {
let url = self.url_maker.quote_action(id);
self.delete(url)
}
}