use std::collections::HashMap;
use std::future::Future;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use strum_macros::{Display, EnumString};
use crate::client;
use crate::TradingPair;
#[derive(EnumString, Display, Deserialize, Serialize, Debug)]
pub enum LimitOrderType {
ASK,
BID,
}
#[derive(EnumString, Display, Deserialize, Debug)]
pub enum MarketOrderType {
BUY,
SELL,
}
#[derive(Debug, Deserialize)]
pub struct Order {
pub base: Decimal,
pub counter: Decimal,
pub creation_timestamp: u64,
pub expiration_timestamp: u64,
pub completed_timestamp: u64,
pub fee_base: Decimal,
pub fee_counter: Decimal,
pub limit_price: Decimal,
pub limit_volume: Decimal,
pub order_id: String,
pub pair: TradingPair,
pub state: OrderState,
#[serde(alias = "type")]
pub order_type: LimitOrderType,
}
pub struct ListOrdersBuilder<'a> {
pub(crate) state: Option<OrderState>,
pub(crate) pair: Option<TradingPair>,
pub(crate) created_before: Option<u64>,
pub(crate) limit: Option<u64>,
pub(crate) luno_client: &'a client::LunoClient,
pub(crate) url: reqwest::Url,
}
impl<'a> ListOrdersBuilder<'a> {
pub fn filter_state(&mut self, state: OrderState) -> &mut ListOrdersBuilder<'a> {
self.state = Some(state);
self
}
pub fn filter_pair(&mut self, pair: TradingPair) -> &mut ListOrdersBuilder<'a> {
self.pair = Some(pair);
self
}
pub fn filter_created_before(&mut self, timestamp: u64) -> &mut ListOrdersBuilder<'a> {
self.created_before = Some(timestamp);
self
}
pub fn filter_limit(&mut self, limit: u64) -> &mut ListOrdersBuilder<'a> {
self.limit = Some(limit);
self
}
pub fn get(&self) -> impl Future<Output = Result<OrderList, reqwest::Error>> + '_ {
let mut url = self.url.clone();
if let Some(state) = &self.state {
url.query_pairs_mut()
.append_pair("state", &state.to_string());
}
if let Some(pair) = &self.pair {
url.query_pairs_mut().append_pair("pair", &pair.to_string());
}
if let Some(timestamp) = &self.created_before {
url.query_pairs_mut()
.append_pair("created_before", ×tamp.to_string());
}
if let Some(limit) = &self.limit {
url.query_pairs_mut()
.append_pair("limit", &limit.to_string());
}
self.luno_client.get(url)
}
}
#[derive(Debug, Deserialize)]
pub struct OrderList {
pub orders: Option<Vec<Order>>,
}
#[derive(Debug, Serialize)]
pub struct LimitOrder {
pub pair: String,
#[serde(alias = "type")]
pub order_type: LimitOrderType,
pub volume: String,
pub price: String,
pub stop_price: String,
pub stop_direction: String,
pub base_account_id: String,
pub counter_account_id: String,
pub post_only: bool,
}
#[derive(Debug, Deserialize)]
pub struct PostOrderResponse {
pub order_id: Option<String>,
pub error: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct StopOrderResponse {
pub success: bool,
}
#[derive(EnumString, Display, Debug, Deserialize)]
pub enum OrderState {
COMPLETE,
PENDING,
}
#[derive(EnumString, Display, Debug, Serialize)]
pub enum StopDirection {
BELOW,
ABOVE,
#[allow(non_camel_case_types)]
RELATIVE_LAST_TRADE,
}
pub struct PostLimitOrderBuilder<'a> {
pub(crate) luno_client: &'a client::LunoClient,
pub(crate) url: reqwest::Url,
pub(crate) params: HashMap<&'a str, String>,
}
impl<'a> PostLimitOrderBuilder<'a> {
pub fn with_base_account(&mut self, id: &'a str) -> &mut PostLimitOrderBuilder<'a> {
self.params.insert("base_account_id", id.to_owned());
self
}
pub fn with_counter_account(&mut self, id: &'a str) -> &mut PostLimitOrderBuilder<'a> {
self.params.insert("counter_account_id", id.to_owned());
self
}
pub fn post_only(&mut self) -> &mut PostLimitOrderBuilder<'a> {
self.params.insert("post_only", "true".to_owned());
self
}
pub fn with_stop_price(&mut self, price: Decimal) -> &mut PostLimitOrderBuilder<'a> {
self.params.insert("stop_price", price.to_string());
self
}
pub fn with_stop_direction(
&mut self,
direction: StopDirection,
) -> &mut PostLimitOrderBuilder<'a> {
self.params.insert("stop_direction", direction.to_string());
self
}
pub async fn post(&mut self) -> Result<PostOrderResponse, reqwest::Error> {
let url = self.url.clone();
self.luno_client
.http
.post(url)
.basic_auth(
self.luno_client.credentials.key.to_owned(),
Some(self.luno_client.credentials.secret.to_owned()),
)
.form(&self.params)
.send()
.await?
.json()
.await
}
}
pub struct PostMarketOrderBuilder<'a> {
pub(crate) luno_client: &'a client::LunoClient,
pub(crate) url: reqwest::Url,
pub(crate) params: HashMap<&'a str, String>,
}
impl<'a> PostMarketOrderBuilder<'a> {
pub fn with_base_account(&mut self, id: &'a str) -> &mut PostMarketOrderBuilder<'a> {
self.params.insert("base_account_id", id.to_owned());
self
}
pub fn with_counter_account(&mut self, id: &'a str) -> &mut PostMarketOrderBuilder<'a> {
self.params.insert("counter_account_id", id.to_owned());
self
}
pub async fn post(&mut self) -> Result<PostOrderResponse, reqwest::Error> {
let url = self.url.clone();
self.luno_client
.http
.post(url)
.basic_auth(
self.luno_client.credentials.key.to_owned(),
Some(self.luno_client.credentials.secret.to_owned()),
)
.form(&self.params)
.send()
.await?
.json()
.await
}
}