use bullet_exchange_interface::decimals::PositiveDecimal;
use bullet_exchange_interface::message::{AmendOrderArgs, CancelOrderArgs, NewOrderArgs};
use bullet_exchange_interface::types::{MarketId, OrderType, Side};
use crate::generated::types::SubmitTxResponse;
use crate::types::{CallMessage, UserAction};
use crate::{Client, SDKError, SDKResult, Transaction};
pub trait NewOrderExt {
fn limit(price: PositiveDecimal, size: PositiveDecimal, side: Side) -> Self;
fn post_only(price: PositiveDecimal, size: PositiveDecimal, side: Side) -> Self;
fn ioc(price: PositiveDecimal, size: PositiveDecimal, side: Side) -> Self;
}
impl NewOrderExt for NewOrderArgs {
fn limit(price: PositiveDecimal, size: PositiveDecimal, side: Side) -> Self {
new_order(price, size, side, OrderType::Limit)
}
fn post_only(price: PositiveDecimal, size: PositiveDecimal, side: Side) -> Self {
new_order(price, size, side, OrderType::PostOnly)
}
fn ioc(price: PositiveDecimal, size: PositiveDecimal, side: Side) -> Self {
new_order(price, size, side, OrderType::ImmediateOrCancel)
}
}
fn new_order(
price: PositiveDecimal,
size: PositiveDecimal,
side: Side,
order_type: OrderType,
) -> NewOrderArgs {
NewOrderArgs {
price,
size,
side,
order_type,
reduce_only: false,
client_order_id: None,
pending_tpsl_pair: None,
}
}
impl Client {
pub async fn place_orders(
&self,
market_id: MarketId,
orders: Vec<NewOrderArgs>,
replace: bool,
sub_account_index: Option<u8>,
) -> SDKResult<SubmitTxResponse> {
let call_msg = CallMessage::User(UserAction::PlaceOrders {
market_id,
orders,
replace,
sub_account_index,
});
let signed = Transaction::builder().call_message(call_msg).client(self).build()?;
self.send_transaction(&signed).await
}
pub async fn cancel_orders(
&self,
market_id: MarketId,
orders: Vec<CancelOrderArgs>,
sub_account_index: Option<u8>,
) -> SDKResult<SubmitTxResponse> {
let call_msg =
CallMessage::User(UserAction::CancelOrders { market_id, orders, sub_account_index });
let signed = Transaction::builder().call_message(call_msg).client(self).build()?;
self.send_transaction(&signed).await
}
pub async fn cancel_market_orders(
&self,
market_id: MarketId,
sub_account_index: Option<u8>,
) -> SDKResult<SubmitTxResponse> {
let call_msg =
CallMessage::User(UserAction::CancelMarketOrders { market_id, sub_account_index });
let signed = Transaction::builder().call_message(call_msg).client(self).build()?;
self.send_transaction(&signed).await
}
pub async fn cancel_all_orders(
&self,
sub_account_index: Option<u8>,
) -> SDKResult<SubmitTxResponse> {
let call_msg = CallMessage::User(UserAction::CancelAllOrders { sub_account_index });
let signed = Transaction::builder().call_message(call_msg).client(self).build()?;
self.send_transaction(&signed).await
}
pub fn address(&self) -> SDKResult<String> {
let kp = self.keypair().ok_or(SDKError::MissingKeypair)?;
Ok(kp.address())
}
pub async fn my_open_orders(
&self,
symbol: &str,
) -> SDKResult<Vec<crate::generated::types::BinanceOrder>> {
let address = self.address()?;
let resp = self.query_open_orders(&address, symbol).await?;
Ok(resp.into_inner())
}
pub async fn my_account(&self) -> SDKResult<crate::generated::types::Account> {
let address = self.address()?;
let resp = self.account_info(&address).await?;
Ok(resp.into_inner())
}
pub async fn my_balances(&self) -> SDKResult<Vec<crate::generated::types::Balance>> {
let address = self.address()?;
let resp = self.account_balance(&address).await?;
Ok(resp.into_inner())
}
pub async fn amend_orders(
&self,
market_id: MarketId,
orders: Vec<AmendOrderArgs>,
sub_account_index: Option<u8>,
) -> SDKResult<SubmitTxResponse> {
let call_msg =
CallMessage::User(UserAction::AmendOrders { market_id, orders, sub_account_index });
let signed = Transaction::builder().call_message(call_msg).client(self).build()?;
self.send_transaction(&signed).await
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use rust_decimal::Decimal;
use super::*;
fn dec(s: &str) -> PositiveDecimal {
PositiveDecimal::try_from(Decimal::from_str(s).unwrap()).unwrap()
}
#[test]
fn limit_order_defaults() {
let order = NewOrderArgs::limit(dec("50000"), dec("0.1"), Side::Bid);
assert_eq!(order.order_type, OrderType::Limit);
assert_eq!(order.side, Side::Bid);
assert!(!order.reduce_only);
assert!(order.client_order_id.is_none());
assert!(order.pending_tpsl_pair.is_none());
}
#[test]
fn post_only_order_defaults() {
let order = NewOrderArgs::post_only(dec("50000"), dec("0.1"), Side::Ask);
assert_eq!(order.order_type, OrderType::PostOnly);
assert_eq!(order.side, Side::Ask);
assert!(!order.reduce_only);
assert!(order.client_order_id.is_none());
}
#[test]
fn ioc_order_defaults() {
let order = NewOrderArgs::ioc(dec("50000"), dec("0.1"), Side::Bid);
assert_eq!(order.order_type, OrderType::ImmediateOrCancel);
assert!(!order.reduce_only);
assert!(order.client_order_id.is_none());
}
}