use crate::client::Client;
use crate::error::Result;
use crate::models::margin::{
BnbBurnStatus, InterestHistoryRecord, InterestRateRecord, IsolatedAccountLimit,
IsolatedMarginAccountDetails, IsolatedMarginTransferType, LoanRecord, MarginAccountDetails,
MarginAssetInfo, MarginOrderCancellation, MarginOrderResult, MarginOrderState,
MarginPairDetails, MarginPriceIndex, MarginTrade, MarginTransferType, MaxBorrowableAmount,
MaxTransferableAmount, RecordsQueryResult, RepayRecord, SideEffectType, TransactionId,
};
use crate::types::{OrderSide, OrderType, TimeInForce};
const SAPI_V1_MARGIN_TRANSFER: &str = "/sapi/v1/margin/transfer";
const SAPI_V1_MARGIN_ISOLATED_TRANSFER: &str = "/sapi/v1/margin/isolated/transfer";
const SAPI_V1_MARGIN_LOAN: &str = "/sapi/v1/margin/loan";
const SAPI_V1_MARGIN_REPAY: &str = "/sapi/v1/margin/repay";
const SAPI_V1_MARGIN_ACCOUNT: &str = "/sapi/v1/margin/account";
const SAPI_V1_MARGIN_ISOLATED_ACCOUNT: &str = "/sapi/v1/margin/isolated/account";
const SAPI_V1_MARGIN_ORDER: &str = "/sapi/v1/margin/order";
const SAPI_V1_MARGIN_OPEN_ORDERS: &str = "/sapi/v1/margin/openOrders";
const SAPI_V1_MARGIN_ALL_ORDERS: &str = "/sapi/v1/margin/allOrders";
const SAPI_V1_MARGIN_MY_TRADES: &str = "/sapi/v1/margin/myTrades";
const SAPI_V1_MARGIN_MAX_BORROWABLE: &str = "/sapi/v1/margin/maxBorrowable";
const SAPI_V1_MARGIN_MAX_TRANSFERABLE: &str = "/sapi/v1/margin/maxTransferable";
const SAPI_V1_MARGIN_INTEREST_HISTORY: &str = "/sapi/v1/margin/interestHistory";
const SAPI_V1_MARGIN_INTEREST_RATE_HISTORY: &str = "/sapi/v1/margin/interestRateHistory";
const SAPI_V1_MARGIN_PAIR: &str = "/sapi/v1/margin/pair";
const SAPI_V1_MARGIN_ALL_PAIRS: &str = "/sapi/v1/margin/allPairs";
const SAPI_V1_MARGIN_ASSET: &str = "/sapi/v1/margin/asset";
const SAPI_V1_MARGIN_ALL_ASSETS: &str = "/sapi/v1/margin/allAssets";
const SAPI_V1_MARGIN_PRICE_INDEX: &str = "/sapi/v1/margin/priceIndex";
const SAPI_V1_MARGIN_ISOLATED_ACCOUNT_LIMIT: &str = "/sapi/v1/margin/isolated/accountLimit";
const SAPI_V1_BNB_BURN: &str = "/sapi/v1/bnbBurn";
#[derive(Clone)]
pub struct Margin {
pub(crate) client: Client,
}
impl Margin {
pub(crate) fn new(client: Client) -> Self {
Self { client }
}
pub async fn account(&self) -> Result<MarginAccountDetails> {
self.client.get_signed(SAPI_V1_MARGIN_ACCOUNT, &[]).await
}
pub async fn isolated_account(
&self,
symbols: Option<&str>,
) -> Result<IsolatedMarginAccountDetails> {
let mut params: Vec<(&str, String)> = vec![];
if let Some(s) = symbols {
params.push(("symbols", s.to_string()));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_ISOLATED_ACCOUNT, ¶ms_ref)
.await
}
pub async fn max_borrowable(
&self,
asset: &str,
isolated_symbol: Option<&str>,
) -> Result<MaxBorrowableAmount> {
let mut params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
if let Some(s) = isolated_symbol {
params.push(("isolatedSymbol", s.to_string()));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_MAX_BORROWABLE, ¶ms_ref)
.await
}
pub async fn max_transferable(
&self,
asset: &str,
isolated_symbol: Option<&str>,
) -> Result<MaxTransferableAmount> {
let mut params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
if let Some(s) = isolated_symbol {
params.push(("isolatedSymbol", s.to_string()));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_MAX_TRANSFERABLE, ¶ms_ref)
.await
}
pub async fn isolated_account_limit(&self) -> Result<IsolatedAccountLimit> {
self.client
.get_signed(SAPI_V1_MARGIN_ISOLATED_ACCOUNT_LIMIT, &[])
.await
}
pub async fn transfer(
&self,
asset: &str,
amount: &str,
transfer_type: MarginTransferType,
) -> Result<TransactionId> {
let type_val = match transfer_type {
MarginTransferType::MainToMargin => "1",
MarginTransferType::MarginToMain => "2",
};
let params: Vec<(&str, String)> = vec![
("asset", asset.to_string()),
("amount", amount.to_string()),
("type", type_val.to_string()),
];
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.post_signed(SAPI_V1_MARGIN_TRANSFER, ¶ms_ref)
.await
}
pub async fn isolated_transfer(
&self,
asset: &str,
symbol: &str,
amount: &str,
trans_from: IsolatedMarginTransferType,
trans_to: IsolatedMarginTransferType,
) -> Result<TransactionId> {
let from_str = match trans_from {
IsolatedMarginTransferType::Spot => "SPOT",
IsolatedMarginTransferType::IsolatedMargin => "ISOLATED_MARGIN",
};
let to_str = match trans_to {
IsolatedMarginTransferType::Spot => "SPOT",
IsolatedMarginTransferType::IsolatedMargin => "ISOLATED_MARGIN",
};
let params: Vec<(&str, String)> = vec![
("asset", asset.to_string()),
("symbol", symbol.to_string()),
("amount", amount.to_string()),
("transFrom", from_str.to_string()),
("transTo", to_str.to_string()),
];
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.post_signed(SAPI_V1_MARGIN_ISOLATED_TRANSFER, ¶ms_ref)
.await
}
pub async fn loan(
&self,
asset: &str,
amount: &str,
is_isolated: bool,
symbol: Option<&str>,
) -> Result<TransactionId> {
let mut params: Vec<(&str, String)> =
vec![("asset", asset.to_string()), ("amount", amount.to_string())];
if is_isolated {
params.push(("isIsolated", "TRUE".to_string()));
if let Some(s) = symbol {
params.push(("symbol", s.to_string()));
}
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.post_signed(SAPI_V1_MARGIN_LOAN, ¶ms_ref)
.await
}
pub async fn repay(
&self,
asset: &str,
amount: &str,
is_isolated: bool,
symbol: Option<&str>,
) -> Result<TransactionId> {
let mut params: Vec<(&str, String)> =
vec![("asset", asset.to_string()), ("amount", amount.to_string())];
if is_isolated {
params.push(("isIsolated", "TRUE".to_string()));
if let Some(s) = symbol {
params.push(("symbol", s.to_string()));
}
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.post_signed(SAPI_V1_MARGIN_REPAY, ¶ms_ref)
.await
}
pub async fn loan_records(
&self,
asset: &str,
isolated_symbol: Option<&str>,
start_time: Option<u64>,
end_time: Option<u64>,
current: Option<u32>,
size: Option<u32>,
) -> Result<RecordsQueryResult<LoanRecord>> {
let mut params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
if let Some(s) = isolated_symbol {
params.push(("isolatedSymbol", s.to_string()));
}
if let Some(st) = start_time {
params.push(("startTime", st.to_string()));
}
if let Some(et) = end_time {
params.push(("endTime", et.to_string()));
}
if let Some(c) = current {
params.push(("current", c.to_string()));
}
if let Some(s) = size {
params.push(("size", s.to_string()));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_LOAN, ¶ms_ref)
.await
}
pub async fn repay_records(
&self,
asset: &str,
isolated_symbol: Option<&str>,
start_time: Option<u64>,
end_time: Option<u64>,
current: Option<u32>,
size: Option<u32>,
) -> Result<RecordsQueryResult<RepayRecord>> {
let mut params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
if let Some(s) = isolated_symbol {
params.push(("isolatedSymbol", s.to_string()));
}
if let Some(st) = start_time {
params.push(("startTime", st.to_string()));
}
if let Some(et) = end_time {
params.push(("endTime", et.to_string()));
}
if let Some(c) = current {
params.push(("current", c.to_string()));
}
if let Some(s) = size {
params.push(("size", s.to_string()));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_REPAY, ¶ms_ref)
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn create_order(
&self,
symbol: &str,
side: OrderSide,
order_type: OrderType,
quantity: Option<&str>,
quote_order_qty: Option<&str>,
price: Option<&str>,
stop_price: Option<&str>,
time_in_force: Option<TimeInForce>,
new_client_order_id: Option<&str>,
side_effect_type: Option<SideEffectType>,
is_isolated: Option<bool>,
) -> Result<MarginOrderResult> {
let mut params: Vec<(&str, String)> = vec![
("symbol", symbol.to_string()),
("side", format!("{:?}", side).to_uppercase()),
("type", format!("{:?}", order_type).to_uppercase()),
];
if let Some(qty) = quantity {
params.push(("quantity", qty.to_string()));
}
if let Some(qty) = quote_order_qty {
params.push(("quoteOrderQty", qty.to_string()));
}
if let Some(p) = price {
params.push(("price", p.to_string()));
}
if let Some(sp) = stop_price {
params.push(("stopPrice", sp.to_string()));
}
if let Some(tif) = time_in_force {
params.push(("timeInForce", format!("{:?}", tif).to_uppercase()));
}
if let Some(id) = new_client_order_id {
params.push(("newClientOrderId", id.to_string()));
}
if let Some(se) = side_effect_type {
params.push((
"sideEffectType",
match se {
SideEffectType::NoSideEffect => "NO_SIDE_EFFECT",
SideEffectType::MarginBuy => "MARGIN_BUY",
SideEffectType::AutoRepay => "AUTO_REPAY",
}
.to_string(),
));
}
if let Some(isolated) = is_isolated {
params.push((
"isIsolated",
if isolated { "TRUE" } else { "FALSE" }.to_string(),
));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.post_signed(SAPI_V1_MARGIN_ORDER, ¶ms_ref)
.await
}
pub async fn cancel_order(
&self,
symbol: &str,
order_id: Option<u64>,
orig_client_order_id: Option<&str>,
is_isolated: Option<bool>,
) -> Result<MarginOrderCancellation> {
let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
if let Some(id) = order_id {
params.push(("orderId", id.to_string()));
}
if let Some(cid) = orig_client_order_id {
params.push(("origClientOrderId", cid.to_string()));
}
if let Some(isolated) = is_isolated {
params.push((
"isIsolated",
if isolated { "TRUE" } else { "FALSE" }.to_string(),
));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.delete_signed(SAPI_V1_MARGIN_ORDER, ¶ms_ref)
.await
}
pub async fn cancel_all_orders(
&self,
symbol: &str,
is_isolated: Option<bool>,
) -> Result<Vec<MarginOrderCancellation>> {
let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
if let Some(isolated) = is_isolated {
params.push((
"isIsolated",
if isolated { "TRUE" } else { "FALSE" }.to_string(),
));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.delete_signed(SAPI_V1_MARGIN_OPEN_ORDERS, ¶ms_ref)
.await
}
pub async fn get_order(
&self,
symbol: &str,
order_id: Option<u64>,
orig_client_order_id: Option<&str>,
is_isolated: Option<bool>,
) -> Result<MarginOrderState> {
let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
if let Some(id) = order_id {
params.push(("orderId", id.to_string()));
}
if let Some(cid) = orig_client_order_id {
params.push(("origClientOrderId", cid.to_string()));
}
if let Some(isolated) = is_isolated {
params.push((
"isIsolated",
if isolated { "TRUE" } else { "FALSE" }.to_string(),
));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_ORDER, ¶ms_ref)
.await
}
pub async fn open_orders(
&self,
symbol: Option<&str>,
is_isolated: Option<bool>,
) -> Result<Vec<MarginOrderState>> {
let mut params: Vec<(&str, String)> = vec![];
if let Some(s) = symbol {
params.push(("symbol", s.to_string()));
}
if let Some(isolated) = is_isolated {
params.push((
"isIsolated",
if isolated { "TRUE" } else { "FALSE" }.to_string(),
));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_OPEN_ORDERS, ¶ms_ref)
.await
}
pub async fn all_orders(
&self,
symbol: &str,
order_id: Option<u64>,
start_time: Option<u64>,
end_time: Option<u64>,
limit: Option<u32>,
is_isolated: Option<bool>,
) -> Result<Vec<MarginOrderState>> {
let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
if let Some(id) = order_id {
params.push(("orderId", id.to_string()));
}
if let Some(st) = start_time {
params.push(("startTime", st.to_string()));
}
if let Some(et) = end_time {
params.push(("endTime", et.to_string()));
}
if let Some(l) = limit {
params.push(("limit", l.to_string()));
}
if let Some(isolated) = is_isolated {
params.push((
"isIsolated",
if isolated { "TRUE" } else { "FALSE" }.to_string(),
));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_ALL_ORDERS, ¶ms_ref)
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn my_trades(
&self,
symbol: &str,
order_id: Option<u64>,
start_time: Option<u64>,
end_time: Option<u64>,
from_id: Option<u64>,
limit: Option<u32>,
is_isolated: Option<bool>,
) -> Result<Vec<MarginTrade>> {
let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
if let Some(id) = order_id {
params.push(("orderId", id.to_string()));
}
if let Some(st) = start_time {
params.push(("startTime", st.to_string()));
}
if let Some(et) = end_time {
params.push(("endTime", et.to_string()));
}
if let Some(id) = from_id {
params.push(("fromId", id.to_string()));
}
if let Some(l) = limit {
params.push(("limit", l.to_string()));
}
if let Some(isolated) = is_isolated {
params.push((
"isIsolated",
if isolated { "TRUE" } else { "FALSE" }.to_string(),
));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_MY_TRADES, ¶ms_ref)
.await
}
pub async fn interest_history(
&self,
asset: Option<&str>,
isolated_symbol: Option<&str>,
start_time: Option<u64>,
end_time: Option<u64>,
current: Option<u32>,
size: Option<u32>,
) -> Result<RecordsQueryResult<InterestHistoryRecord>> {
let mut params: Vec<(&str, String)> = vec![];
if let Some(a) = asset {
params.push(("asset", a.to_string()));
}
if let Some(s) = isolated_symbol {
params.push(("isolatedSymbol", s.to_string()));
}
if let Some(st) = start_time {
params.push(("startTime", st.to_string()));
}
if let Some(et) = end_time {
params.push(("endTime", et.to_string()));
}
if let Some(c) = current {
params.push(("current", c.to_string()));
}
if let Some(s) = size {
params.push(("size", s.to_string()));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_INTEREST_HISTORY, ¶ms_ref)
.await
}
pub async fn interest_rate_history(
&self,
asset: &str,
vip_level: Option<u32>,
start_time: Option<u64>,
end_time: Option<u64>,
limit: Option<u32>,
) -> Result<Vec<InterestRateRecord>> {
let mut params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
if let Some(vip) = vip_level {
params.push(("vipLevel", vip.to_string()));
}
if let Some(st) = start_time {
params.push(("startTime", st.to_string()));
}
if let Some(et) = end_time {
params.push(("endTime", et.to_string()));
}
if let Some(l) = limit {
params.push(("limit", l.to_string()));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_INTEREST_RATE_HISTORY, ¶ms_ref)
.await
}
pub async fn pair(&self, symbol: &str) -> Result<MarginPairDetails> {
let params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_PAIR, ¶ms_ref)
.await
}
pub async fn all_pairs(&self) -> Result<Vec<MarginPairDetails>> {
self.client.get_signed(SAPI_V1_MARGIN_ALL_PAIRS, &[]).await
}
pub async fn asset(&self, asset: &str) -> Result<MarginAssetInfo> {
let params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_ASSET, ¶ms_ref)
.await
}
pub async fn all_assets(&self) -> Result<Vec<MarginAssetInfo>> {
self.client.get_signed(SAPI_V1_MARGIN_ALL_ASSETS, &[]).await
}
pub async fn price_index(&self, symbol: &str) -> Result<MarginPriceIndex> {
let params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client
.get_signed(SAPI_V1_MARGIN_PRICE_INDEX, ¶ms_ref)
.await
}
pub async fn bnb_burn_status(&self) -> Result<BnbBurnStatus> {
self.client.get_signed(SAPI_V1_BNB_BURN, &[]).await
}
pub async fn toggle_bnb_burn(
&self,
spot_bnb_burn: Option<bool>,
interest_bnb_burn: Option<bool>,
) -> Result<BnbBurnStatus> {
let mut params: Vec<(&str, String)> = vec![];
if let Some(spot) = spot_bnb_burn {
params.push(("spotBNBBurn", spot.to_string()));
}
if let Some(interest) = interest_bnb_burn {
params.push(("interestBNBBurn", interest.to_string()));
}
let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
self.client.post_signed(SAPI_V1_BNB_BURN, ¶ms_ref).await
}
}