use std::str::FromStr;
use crate::access::AccessLevel;
use crate::api::market_data::{CandleInterval, CandlesQuery};
use crate::api::orders::{ActiveOrdersQuery, HistoricalOrdersQuery};
use crate::api::trades::TradesQuery;
use crate::error::{Error, Result};
use crate::{ClientOrderId, Decimal, OrderId, Side};
#[derive(Debug)]
pub enum Command {
Balances,
Currencies,
Pairs,
Tickers { symbols: Vec<String> },
OrderBook { symbol: String, limit: Option<u32> },
PublicOrderBook { symbol: String },
Candles { symbol: String, query: CandlesQuery },
LastTrades,
AllTrades { symbol: String, query: TradesQuery },
PrivateTrades { symbol: String, query: TradesQuery },
ActiveOrders(ActiveOrdersQuery),
HistoricalOrders(HistoricalOrdersQuery),
GetOrder(OrderId),
OrderFills(OrderId),
PlaceLimit(PlaceLimit),
PlaceMarket(PlaceMarket),
Replace(ReplaceOrder),
Cancel(OrderId),
CancelAll,
}
#[derive(Debug)]
pub struct PlaceLimit {
pub symbol: String,
pub side: Side,
pub size: Decimal,
pub price: Decimal,
pub in_quote: bool,
pub post_only: bool,
pub client_order_id: Option<ClientOrderId>,
}
#[derive(Debug)]
pub struct PlaceMarket {
pub symbol: String,
pub side: Side,
pub size: Decimal,
pub in_quote: bool,
pub client_order_id: Option<ClientOrderId>,
}
#[derive(Debug)]
pub struct ReplaceOrder {
pub id: OrderId,
pub size: Option<Decimal>,
pub price: Option<Decimal>,
pub in_quote: bool,
pub post_only: bool,
pub client_order_id: Option<ClientOrderId>,
}
impl Command {
#[must_use]
pub const fn min_access(&self) -> AccessLevel {
match self {
Self::Tickers { .. }
| Self::OrderBook { .. }
| Self::PublicOrderBook { .. }
| Self::Candles { .. }
| Self::LastTrades
| Self::AllTrades { .. }
| Self::Currencies
| Self::Pairs => AccessLevel::Market,
Self::Balances
| Self::PrivateTrades { .. }
| Self::ActiveOrders(_)
| Self::HistoricalOrders(_)
| Self::GetOrder(_)
| Self::OrderFills(_) => AccessLevel::View,
Self::PlaceLimit(_)
| Self::PlaceMarket(_)
| Self::Replace(_)
| Self::Cancel(_)
| Self::CancelAll => AccessLevel::Trading,
}
}
#[must_use]
pub const fn is_real_trading(&self) -> bool {
matches!(self.min_access(), AccessLevel::Trading)
}
}
pub fn parse_decimal(field: &str, value: &str) -> Result<Decimal> {
Decimal::from_str(value)
.map_err(|e| Error::invalid_request(format!("invalid {field} '{value}': {e}")))
}
pub fn candle_interval(minutes: i64) -> Result<CandleInterval> {
use CandleInterval::{
FifteenMinutes, FiveMinutes, FourDays, FourHours, FourWeeks, OneDay, OneHour, OneMinute,
OneWeek, ThirtyMinutes, TwoDays, TwoWeeks,
};
Ok(match minutes {
1 => OneMinute,
5 => FiveMinutes,
15 => FifteenMinutes,
30 => ThirtyMinutes,
60 => OneHour,
240 => FourHours,
1440 => OneDay,
2880 => TwoDays,
5760 => FourDays,
10080 => OneWeek,
20160 => TwoWeeks,
40320 => FourWeeks,
other => {
return Err(Error::InvalidRequest {
message: format!(
"invalid candle interval {other} (allowed: 1,5,15,30,60,240,1440,2880,5760,10080,20160,40320)"
),
});
}
})
}