use std::collections::BTreeMap;
use crate::api::{Account, API};
use crate::client::Client;
use crate::errors::BybitError;
use crate::model::{
AccountInfoResponse, BatchSetCollateralCoinResponse, BorrowHistoryRequest,
BorrowHistoryResponse, Category, CollateralInfoResponse, FeeRateResponse,
RepayLiabilityResponse, SetCollateralCoinResponse, SetMarginModeResponse, SmpResponse,
SpotHedgingResponse, TransactionLogRequest, TransactionLogResponse, UTAResponse,
WalletResponse,
};
use serde_json::{json, Value};
use crate::util::{build_json_request, build_request, date_to_milliseconds};
#[derive(Clone)]
pub struct AccountManager {
pub client: Client,
pub recv_window: u64,
}
impl AccountManager {
pub async fn get_wallet_balance(
&self,
account: &str,
coin: Option<&str>,
) -> Result<WalletResponse, BybitError> {
let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
parameters.insert("accountType".into(), account.into());
if let Some(c) = coin {
parameters.insert("coin".into(), c.into());
}
let request = build_request(¶meters);
let response: WalletResponse = self
.client
.get_signed(
API::Account(Account::Balance),
self.recv_window.into(),
Some(request),
)
.await?;
Ok(response)
}
pub async fn upgrade_to_uta(&self) -> Result<UTAResponse, BybitError> {
let response: UTAResponse = self
.client
.post_signed(
API::Account(Account::UpgradetoUTA),
self.recv_window.into(),
None,
)
.await?;
Ok(response)
}
pub async fn get_borrow_history<'a>(
&self,
req: BorrowHistoryRequest<'_>,
) -> Result<BorrowHistoryResponse, BybitError> {
let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
if let Some(c) = req.coin {
parameters.insert("coin".into(), c.into());
}
if let Some(end_str) = req.start_time.as_ref().map(|s| s.as_ref()) {
let end_millis = date_to_milliseconds(end_str);
parameters
.entry("startTime".to_owned())
.or_insert_with(|| end_millis.into());
}
if let Some(end_str) = req.end_time.as_ref().map(|s| s.as_ref()) {
let end_millis = date_to_milliseconds(end_str);
parameters
.entry("endTime".to_owned())
.or_insert_with(|| end_millis.into());
}
if let Some(s) = req.limit {
parameters.insert("limit".into(), s.into());
}
let request = build_request(¶meters);
let response: BorrowHistoryResponse = self
.client
.get_signed(
API::Account(Account::BorrowHistory),
self.recv_window.into(),
Some(request),
)
.await?;
Ok(response)
}
pub async fn repay_liability(
&self,
coin: Option<&str>,
) -> Result<RepayLiabilityResponse, BybitError> {
let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
if let Some(c) = coin {
parameters.insert("coin".into(), c.into());
}
let request = build_json_request(¶meters);
let response: RepayLiabilityResponse = self
.client
.post_signed(
API::Account(Account::RepayLiability),
self.recv_window.into(),
Some(request),
)
.await?;
Ok(response)
}
pub async fn set_collateral_coin(
&self,
coin: &str,
switch: bool,
) -> Result<SetCollateralCoinResponse, BybitError> {
let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
parameters.insert("coin".into(), coin.into());
if switch == true {
parameters.insert("collateralSwitch".into(), "ON".into());
} else {
parameters.insert("collateralSwitch".into(), "OFF".into());
}
let request = build_json_request(¶meters);
let response: SetCollateralCoinResponse = self
.client
.post_signed(
API::Account(Account::SetCollateral),
self.recv_window.into(),
Some(request),
)
.await?;
Ok(response)
}
pub async fn batch_set_collateral(
&self,
requests: Vec<(&str, bool)>,
) -> Result<BatchSetCollateralCoinResponse, BybitError> {
let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
let mut requests_array: Vec<Value> = Vec::new();
for (coin, switch) in requests {
let mut build_switch: BTreeMap<String, Value> = BTreeMap::new();
build_switch.insert("coin".into(), coin.into());
build_switch.insert("collateralSwitch".into(), switch.into());
let build_switches = json!(&build_switch);
requests_array.push(build_switches);
}
parameters.insert("request".into(), Value::Array(requests_array));
let request = build_json_request(¶meters);
let response: BatchSetCollateralCoinResponse = self
.client
.post_signed(
API::Account(Account::BatchSetCollateral),
self.recv_window.into(),
Some(request),
)
.await?;
Ok(response)
}
pub async fn get_collateral_info(
&self,
coin: Option<&str>,
) -> Result<CollateralInfoResponse, BybitError> {
let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
if let Some(v) = coin {
parameters.insert("currency".into(), v.into());
}
let req = build_request(¶meters);
let response: CollateralInfoResponse = self
.client
.get_signed(
API::Account(Account::CollateralInfo),
self.recv_window.into(),
Some(req),
)
.await?;
Ok(response)
}
pub async fn get_fee_rate(
&self,
category: Category,
symbol: Option<String>,
) -> Result<FeeRateResponse, BybitError> {
let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
parameters.insert("category".into(), category.as_str().into());
if let Some(s) = symbol {
parameters.insert("symbol".into(), s.into());
}
let req = build_request(¶meters);
let response: FeeRateResponse = self
.client
.post_signed(
API::Account(Account::FeeRate),
self.recv_window.into(),
Some(req),
)
.await?;
Ok(response)
}
pub async fn get_account_info(&self) -> Result<AccountInfoResponse, BybitError> {
let response: AccountInfoResponse = self
.client
.get_signed(
API::Account(Account::Information),
self.recv_window.into(),
None,
)
.await?;
Ok(response)
}
pub async fn get_transaction_log<'a>(
&self,
req: TransactionLogRequest<'a>,
) -> Result<TransactionLogResponse, BybitError> {
let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
if let Some(v) = req.account_type {
parameters.insert("accountType".into(), v.into());
}
if let Some(v) = req.category {
parameters.insert("category".into(), v.as_str().into());
}
if let Some(s) = req.currency {
parameters.insert("currency".into(), s.into());
}
if let Some(c) = req.base_coin {
parameters.insert("baseCoin".into(), c.into());
}
if let Some(t) = req.log_type {
parameters.insert("type".into(), t.into());
}
if let Some(start_str) = req.start_time.as_ref().map(|s| s.as_ref()) {
let start_millis = date_to_milliseconds(start_str);
parameters
.entry("startTime".to_owned())
.or_insert_with(|| start_millis.into());
}
if let Some(end_str) = req.end_time.as_ref().map(|s| s.as_ref()) {
let end_millis = date_to_milliseconds(end_str);
parameters
.entry("endTime".to_owned())
.or_insert_with(|| end_millis.into());
}
if let Some(s) = req.limit {
parameters.insert("limit".into(), s.into());
}
let request = build_request(¶meters);
let response: TransactionLogResponse = self
.client
.get_signed(
API::Account(Account::TransactionLog),
self.recv_window.into(),
Some(request),
)
.await?;
Ok(response)
}
pub async fn get_smp_id(&self) -> Result<SmpResponse, BybitError> {
let response: SmpResponse = self
.client
.get_signed(
API::Account(Account::SMPGroupID),
self.recv_window.into(),
None,
)
.await?;
Ok(response)
}
pub async fn set_margin_mode(
&self,
margin_mode: &str,
) -> Result<SetMarginModeResponse, BybitError> {
let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
parameters.insert("setMarginMode".into(), margin_mode.into());
let request = build_json_request(¶meters);
let response: SetMarginModeResponse = self
.client
.post_signed(
API::Account(Account::SetMarginMode),
self.recv_window.into(),
Some(request),
)
.await?;
Ok(response)
}
pub async fn set_spot_hedging(
&self,
spot_hedging: bool,
) -> Result<SpotHedgingResponse, BybitError> {
let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
if spot_hedging == true {
parameters.insert("setHedgingMode".into(), "ON".into());
} else {
parameters.insert("setHedgingMode".into(), "OFF".into());
}
let request = build_json_request(¶meters);
let response: SpotHedgingResponse = self
.client
.post_signed(
API::Account(Account::SetSpotHedging),
self.recv_window.into(),
Some(request),
)
.await?;
Ok(response)
}
}