// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
#![allow(unused, non_snake_case, clippy::all)]
use crate::Value;
use crate::get_value;
use crate::runtime::*;
// Base methods are now trait methods (review #1: static dispatch). Bring the
// traits into scope so `self.market(...)`, `self.safe_market(...)`,
// `self.load_markets(...)`, … on this Core resolve to the base defaults.
use crate::exchange_generated::ExchangeBase;
use crate::exchange::ExchangeRuntime;
// Dynamic `this[method](...)` re-entries are emitted as
// `self.call_dynamic_checked(...)` (blanket-impl'd on every Core) so an
// unresolvable name raises NotSupported instead of yielding a silent Null.
use crate::exchange::CallDynamicChecked;
use crate::pro::*;
pub struct BinanceCore {
pub parent: crate::exchanges::binance::BinanceCore,
}
impl BinanceCore {
pub fn new(config: Option<crate::Value>) -> Self {
let mut s = Self { parent: crate::exchanges::binance::BinanceCore::new(config) };
s.init();
s
}
pub fn init(&mut self) {
let described = BinanceCore::describe(self);
self.initialize_properties(described);
<Self as crate::exchange_generated::ExchangeBase>::after_construct(self);
}
/// Compatibility no-op. The old pointer-based dispatch needed a post-move
/// `bind()`; static trait dispatch (review #1) needs no binding, so this
/// just exists so callers that still call it keep compiling.
#[inline]
pub fn bind(&mut self) {}
}
impl crate::exchange::DerivedExchange for BinanceCore {
fn nonce(&self, ) -> crate::Value {
crate::exchange::DerivedExchange::nonce(&self.parent)
}
fn parse_ticker(&self, ticker: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_ticker(&self.parent, ticker, market)
}
fn parse_trade(&self, trade: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_trade(&self.parent, trade, market)
}
fn parse_order(&self, order: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_order(&self.parent, order, market)
}
fn parse_market(&self, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_market(&self.parent, market)
}
fn parse_ohlcv(&self, ohlcv: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_ohlcv(&self.parent, ohlcv, market)
}
fn parse_order_book(&self, ob: crate::Value, symbol: crate::Value, ts: crate::Value, bk: crate::Value, ak: crate::Value, pk: crate::Value, ak2: crate::Value, ck: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_order_book(&self.parent, ob, symbol, ts, bk, ak, pk, ak2, ck)
}
fn parse_balance(&self, response: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_balance(&self.parent, response)
}
fn parse_position(&self, position: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_position(&self.parent, position, market)
}
fn parse_funding_rate(&self, rate: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_funding_rate(&self.parent, rate, market)
}
fn parse_deposit(&self, tx: crate::Value, currency: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_deposit(&self.parent, tx, currency)
}
fn parse_deposit_address(&self, depositAddress: crate::Value, currency: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_deposit_address(&self.parent, depositAddress, currency)
}
fn parse_last_price(&self, entry: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_last_price(&self.parent, entry, market)
}
fn parse_withdrawal(&self, tx: crate::Value, currency: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_withdrawal(&self.parent, tx, currency)
}
fn parse_ledger_entry(&self, entry: crate::Value, currency: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_ledger_entry(&self.parent, entry, currency)
}
fn parse_transfer(&self, transfer: crate::Value, currency: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_transfer(&self.parent, transfer, currency)
}
fn parse_currency(&self, currency: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_currency(&self.parent, currency)
}
fn parse_bid_ask(&self, bidask: crate::Value, price_key: crate::Value, amount_key: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_bid_ask(&self.parent, bidask, price_key, amount_key, market)
}
fn parse_open_interest(&self, interest: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_open_interest(&self.parent, interest, market)
}
fn parse_liquidation(&self, liquidation: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_liquidation(&self.parent, liquidation, market)
}
fn parse_funding_rate_history(&self, entry: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_funding_rate_history(&self.parent, entry, market)
}
fn parse_margin_modification(&self, data: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_margin_modification(&self.parent, data, market)
}
fn parse_account(&self, account: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_account(&self.parent, account)
}
fn parse_my_trade(&self, trade: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_my_trade(&self.parent, trade, market)
}
fn parse_transaction(&self, transaction: crate::Value, currency: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_transaction(&self.parent, transaction, currency)
}
fn parse_borrow_interest(&self, info: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_borrow_interest(&self.parent, info, market)
}
fn parse_adl_rank(&self, info: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_adl_rank(&self.parent, info, market)
}
fn parse_income(&self, info: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_income(&self.parent, info, market)
}
fn parse_greeks(&self, greeks: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_greeks(&self.parent, greeks, market)
}
fn parse_margin_mode(&self, margin_mode: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_margin_mode(&self.parent, margin_mode, market)
}
fn parse_conversion(&self, conversion: crate::Value, from_currency: crate::Value, to_currency: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_conversion(&self.parent, conversion, from_currency, to_currency)
}
fn parse_borrow_rate(&self, info: crate::Value, currency: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_borrow_rate(&self.parent, info, currency)
}
fn parse_leverage(&self, leverage: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_leverage(&self.parent, leverage, market)
}
fn parse_market_leverage_tiers(&self, info: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_market_leverage_tiers(&self.parent, info, market)
}
fn parse_deposit_withdraw_fee(&self, fee: crate::Value, currency: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_deposit_withdraw_fee(&self.parent, fee, currency)
}
fn parse_prediction_trade(&self, trade: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_prediction_trade(&self.parent, trade, market)
}
fn parse_prediction_order(&self, order: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_prediction_order(&self.parent, order, market)
}
fn parse_prediction_position(&self, position: crate::Value, market: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::parse_prediction_position(&self.parent, position, market)
}
fn create_expired_option_market(&self, symbol: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::create_expired_option_market(&self.parent, symbol)
}
fn sign(&self, path: crate::Value, api: crate::Value, method: crate::Value, params: crate::Value, headers: crate::Value, body: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::sign(&self.parent, path, api, method, params, headers, body)
}
fn handle_errors(&self, code: crate::Value, reason: crate::Value, url: crate::Value, method: crate::Value, headers: crate::Value, body: crate::Value, response: crate::Value, request_headers: crate::Value, request_body: crate::Value) -> crate::Value {
crate::exchange::DerivedExchange::handle_errors(&self.parent, code, reason, url, method, headers, body, response, request_headers, request_body)
}
}
impl crate::exchange_generated::ExchangeBase for BinanceCore {
fn call_dynamic<'a>(&'a mut self, method: &'a str, args: Vec<crate::Value>)
-> std::pin::Pin<Box<dyn std::future::Future<Output = crate::Value> + Send + 'a>>
{
Box::pin(async move {
match method {
"authenticate" => self.authenticate(&args[..]).await,
"cancel_all_orders_ws" => self.cancel_all_orders_ws(&args[..]).await,
"cancel_order_ws" => self.cancel_order_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"create_order_ws" => self.create_order_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), args.get(2).cloned().unwrap_or(crate::Value::Null), args.get(3).cloned().unwrap_or(crate::Value::Null), &args[4.min(args.len())..]).await,
"describe_data" => self.describe_data(),
"edit_order_ws" => self.edit_order_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), args.get(2).cloned().unwrap_or(crate::Value::Null), args.get(3).cloned().unwrap_or(crate::Value::Null), &args[4.min(args.len())..]).await,
"ensure_user_data_stream_ws_subscribe_listen_token" => self.ensure_user_data_stream_ws_subscribe_listen_token(&args[..]).await,
"ensure_user_data_stream_ws_subscribe_signature" => self.ensure_user_data_stream_ws_subscribe_signature(&args[..]).await,
"fetch_balance_ws" => self.fetch_balance_ws(&args[..]).await,
"fetch_closed_orders_ws" => self.fetch_closed_orders_ws(&args[..]).await,
"fetch_my_trades_ws" => self.fetch_my_trades_ws(&args[..]).await,
"fetch_ohlcv_ws" => self.fetch_ohlcv_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"fetch_open_orders_ws" => self.fetch_open_orders_ws(&args[..]).await,
"fetch_order_book_snapshot" => self.fetch_order_book_snapshot(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), args.get(2).cloned().unwrap_or(crate::Value::Null)).await,
"fetch_order_book_ws" => self.fetch_order_book_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"fetch_order_ws" => self.fetch_order_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"fetch_orders_ws" => self.fetch_orders_ws(&args[..]).await,
"fetch_position_ws" => self.fetch_position_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"fetch_positions_ws" => self.fetch_positions_ws(&args[..]).await,
"fetch_ticker_ws" => self.fetch_ticker_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"fetch_trades_ws" => self.fetch_trades_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"get_future_ws_category" => self.get_future_ws_category(args.get(0).cloned().unwrap_or(crate::Value::Null)),
"get_market_type" => self.get_market_type(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), &args[2.min(args.len())..]),
"get_private_ws_url" => self.get_private_ws_url(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"get_stock_ws_url" => self.get_stock_ws_url(&args[..]),
"get_ws_url" => self.get_ws_url(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"handle_message" => { self.handle_message(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_order_book_message" => self.handle_order_book_message(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), args.get(2).cloned().unwrap_or(crate::Value::Null)),
"handle_subscription_status" => self.handle_subscription_status(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"is_spot_url" => self.is_spot_url(args.get(0).cloned().unwrap_or(crate::Value::Null)),
"keep_alive_listen_key" => self.keep_alive_listen_key(&args[..]).await,
"load_balance_snapshot" => self.load_balance_snapshot(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), args.get(2).cloned().unwrap_or(crate::Value::Null), args.get(3).cloned().unwrap_or(crate::Value::Null)).await,
"load_positions_snapshot" => self.load_positions_snapshot(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), args.get(2).cloned().unwrap_or(crate::Value::Null), args.get(3).cloned().unwrap_or(crate::Value::Null)).await,
"parse_ws_liquidation" => self.parse_ws_liquidation(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"parse_ws_options_position" => self.parse_ws_options_position(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"parse_ws_order" => self.parse_ws_order(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"parse_ws_position" => self.parse_ws_position(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"parse_ws_ticker" => self.parse_ws_ticker(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"parse_ws_trade" => self.parse_ws_trade(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"renew_listen_token" => self.renew_listen_token(&args[..]).await,
"request_id" => self.request_id(args.get(0).cloned().unwrap_or(crate::Value::Null)),
"resolve_auth_type" => self.resolve_auth_type(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"sign_params" => self.sign_params(&args[..]),
"un_watch_bids_asks" => self.un_watch_bids_asks(&args[..]).await,
"un_watch_mark_price" => self.un_watch_mark_price(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"un_watch_mark_prices" => self.un_watch_mark_prices(&args[..]).await,
"un_watch_ohlcv" => self.un_watch_ohlcv(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"un_watch_ohlcv_for_symbols" => self.un_watch_ohlcv_for_symbols(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"un_watch_order_book" => self.un_watch_order_book(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"un_watch_order_book_for_symbols" => self.un_watch_order_book_for_symbols(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"un_watch_ticker" => self.un_watch_ticker(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"un_watch_tickers" => self.un_watch_tickers(&args[..]).await,
"un_watch_trades" => self.un_watch_trades(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"un_watch_trades_for_symbols" => self.un_watch_trades_for_symbols(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_balance" => self.watch_balance(&args[..]).await,
"watch_bids_asks" => self.watch_bids_asks(&args[..]).await,
"watch_liquidations" => self.watch_liquidations(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_liquidations_for_symbols" => self.watch_liquidations_for_symbols(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_mark_price" => self.watch_mark_price(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_mark_prices" => self.watch_mark_prices(&args[..]).await,
"watch_multi_ticker_helper" => self.watch_multi_ticker_helper(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), &args[2.min(args.len())..]).await,
"watch_my_liquidations" => self.watch_my_liquidations(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_my_liquidations_for_symbols" => self.watch_my_liquidations_for_symbols(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_my_trades" => self.watch_my_trades(&args[..]).await,
"watch_ohlcv" => self.watch_ohlcv(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_ohlcv_for_symbols" => self.watch_ohlcv_for_symbols(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_order_book" => self.watch_order_book(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_order_book_for_symbols" => self.watch_order_book_for_symbols(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_orders" => self.watch_orders(&args[..]).await,
"watch_positions" => self.watch_positions(&args[..]).await,
"watch_stock_market_stream" => self.watch_stock_market_stream(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), &args[2.min(args.len())..]).await,
"watch_ticker" => self.watch_ticker(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_tickers" => self.watch_tickers(&args[..]).await,
"watch_trades" => self.watch_trades(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_trades_for_symbols" => self.watch_trades_for_symbols(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
// Go-style inheritance: an un-overridden method dispatches to the parent core.
_ => crate::exchange_generated::ExchangeBase::call_dynamic(&mut self.parent, method, args).await,
}
})
}
}
impl BinanceCore {
/// Synchronous WS handler dispatch — routes a handler-name string (from the
/// venue's handle_message dispatch table) to the real handler method.
#[allow(dead_code, unreachable_patterns, clippy::all)]
pub fn dispatch_ws_handler(&mut self, __name: &crate::Value, args: &[crate::Value]) -> crate::Value {
let __n = match __name { crate::Value::Str(s) => s.as_ref(), _ => return crate::Value::Null };
match __n {
"authenticate" => { crate::exchange_stubs::enqueue_spawn("authenticate", args.to_vec()); crate::Value::Null },
"cancel_all_orders_ws" => { crate::exchange_stubs::enqueue_spawn("cancel_all_orders_ws", args.to_vec()); crate::Value::Null },
"cancel_order_ws" => { crate::exchange_stubs::enqueue_spawn("cancel_order_ws", args.to_vec()); crate::Value::Null },
"create_order_ws" => { crate::exchange_stubs::enqueue_spawn("create_order_ws", args.to_vec()); crate::Value::Null },
"describe_data" => self.describe_data(),
"edit_order_ws" => { crate::exchange_stubs::enqueue_spawn("edit_order_ws", args.to_vec()); crate::Value::Null },
"ensure_user_data_stream_ws_subscribe_listen_token" => { crate::exchange_stubs::enqueue_spawn("ensure_user_data_stream_ws_subscribe_listen_token", args.to_vec()); crate::Value::Null },
"ensure_user_data_stream_ws_subscribe_signature" => { crate::exchange_stubs::enqueue_spawn("ensure_user_data_stream_ws_subscribe_signature", args.to_vec()); crate::Value::Null },
"fetch_balance_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_balance_ws", args.to_vec()); crate::Value::Null },
"fetch_closed_orders_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_closed_orders_ws", args.to_vec()); crate::Value::Null },
"fetch_my_trades_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_my_trades_ws", args.to_vec()); crate::Value::Null },
"fetch_ohlcv_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_ohlcv_ws", args.to_vec()); crate::Value::Null },
"fetch_open_orders_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_open_orders_ws", args.to_vec()); crate::Value::Null },
"fetch_order_book_snapshot" => { crate::exchange_stubs::enqueue_spawn("fetch_order_book_snapshot", args.to_vec()); crate::Value::Null },
"fetch_order_book_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_order_book_ws", args.to_vec()); crate::Value::Null },
"fetch_order_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_order_ws", args.to_vec()); crate::Value::Null },
"fetch_orders_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_orders_ws", args.to_vec()); crate::Value::Null },
"fetch_position_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_position_ws", args.to_vec()); crate::Value::Null },
"fetch_positions_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_positions_ws", args.to_vec()); crate::Value::Null },
"fetch_ticker_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_ticker_ws", args.to_vec()); crate::Value::Null },
"fetch_trades_ws" => { crate::exchange_stubs::enqueue_spawn("fetch_trades_ws", args.to_vec()); crate::Value::Null },
"get_future_ws_category" => self.get_future_ws_category(args.get(0).cloned().unwrap_or(crate::Value::Null)),
"get_market_type" => self.get_market_type(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), &args[2.min(args.len())..]),
"get_private_ws_url" => self.get_private_ws_url(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"get_stock_ws_url" => self.get_stock_ws_url(&args[..]),
"get_ws_url" => self.get_ws_url(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"handle_account_status_ws" => { self.handle_account_status_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_acount_update" => { self.handle_acount_update(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_balance" => { self.handle_balance(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_balance_ws" => { self.handle_balance_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_bids_asks" => { self.handle_bids_asks(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_delta" => { self.handle_delta(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_deltas" => { self.handle_deltas(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_edit_order_ws" => { self.handle_edit_order_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_event_stream_terminated" => { self.handle_event_stream_terminated(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_fetch_ohlcv" => { self.handle_fetch_ohlcv(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_fetch_order_book" => { self.handle_fetch_order_book(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_liquidation" => { self.handle_liquidation(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_mark_prices" => { self.handle_mark_prices(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_message" => { self.handle_message(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_my_liquidation" => { self.handle_my_liquidation(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_my_trade" => { self.handle_my_trade(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_ohlcv" => { self.handle_ohlcv(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_options_account_update" => { self.handle_options_account_update(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_options_order_update" => { self.handle_options_order_update(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_order" => { self.handle_order(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_order_book" => { self.handle_order_book(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_order_book_message" => self.handle_order_book_message(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), args.get(2).cloned().unwrap_or(crate::Value::Null)),
"handle_order_book_subscription" => { self.handle_order_book_subscription(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), args.get(2).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_order_update" => { self.handle_order_update(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_order_ws" => { self.handle_order_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_orders_ws" => { self.handle_orders_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_positions" => { self.handle_positions(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_positions_ws" => { self.handle_positions_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_stock_price" => { self.handle_stock_price(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_stock_quote" => { self.handle_stock_quote(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_subscription_status" => self.handle_subscription_status(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"handle_ticker_ws" => { self.handle_ticker_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_tickers" => { self.handle_tickers(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_tickers_and_bids_asks" => { self.handle_tickers_and_bids_asks(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), args.get(2).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_trade" => { self.handle_trade(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_trades_ws" => { self.handle_trades_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_un_subscription" => { self.handle_un_subscription(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_user_data_stream_subscribe" => { self.handle_user_data_stream_subscribe(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_ws_error" => { self.handle_ws_error(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"is_spot_url" => self.is_spot_url(args.get(0).cloned().unwrap_or(crate::Value::Null)),
"keep_alive_listen_key" => { crate::exchange_stubs::enqueue_spawn("keep_alive_listen_key", args.to_vec()); crate::Value::Null },
"load_balance_snapshot" => { crate::exchange_stubs::enqueue_spawn("load_balance_snapshot", args.to_vec()); crate::Value::Null },
"load_positions_snapshot" => { crate::exchange_stubs::enqueue_spawn("load_positions_snapshot", args.to_vec()); crate::Value::Null },
"parse_ws_liquidation" => self.parse_ws_liquidation(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"parse_ws_options_position" => self.parse_ws_options_position(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"parse_ws_order" => self.parse_ws_order(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"parse_ws_position" => self.parse_ws_position(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"parse_ws_ticker" => self.parse_ws_ticker(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"parse_ws_trade" => self.parse_ws_trade(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"renew_listen_token" => { crate::exchange_stubs::enqueue_spawn("renew_listen_token", args.to_vec()); crate::Value::Null },
"request_id" => self.request_id(args.get(0).cloned().unwrap_or(crate::Value::Null)),
"resolve_auth_type" => self.resolve_auth_type(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"set_balance_cache" => { self.set_balance_cache(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), &args[2.min(args.len())..]); crate::Value::Null },
"set_positions_cache" => { self.set_positions_cache(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null), &args[2.min(args.len())..]); crate::Value::Null },
"sign_params" => self.sign_params(&args[..]),
"un_watch_bids_asks" => { crate::exchange_stubs::enqueue_spawn("un_watch_bids_asks", args.to_vec()); crate::Value::Null },
"un_watch_mark_price" => { crate::exchange_stubs::enqueue_spawn("un_watch_mark_price", args.to_vec()); crate::Value::Null },
"un_watch_mark_prices" => { crate::exchange_stubs::enqueue_spawn("un_watch_mark_prices", args.to_vec()); crate::Value::Null },
"un_watch_ohlcv" => { crate::exchange_stubs::enqueue_spawn("un_watch_ohlcv", args.to_vec()); crate::Value::Null },
"un_watch_ohlcv_for_symbols" => { crate::exchange_stubs::enqueue_spawn("un_watch_ohlcv_for_symbols", args.to_vec()); crate::Value::Null },
"un_watch_order_book" => { crate::exchange_stubs::enqueue_spawn("un_watch_order_book", args.to_vec()); crate::Value::Null },
"un_watch_order_book_for_symbols" => { crate::exchange_stubs::enqueue_spawn("un_watch_order_book_for_symbols", args.to_vec()); crate::Value::Null },
"un_watch_ticker" => { crate::exchange_stubs::enqueue_spawn("un_watch_ticker", args.to_vec()); crate::Value::Null },
"un_watch_tickers" => { crate::exchange_stubs::enqueue_spawn("un_watch_tickers", args.to_vec()); crate::Value::Null },
"un_watch_trades" => { crate::exchange_stubs::enqueue_spawn("un_watch_trades", args.to_vec()); crate::Value::Null },
"un_watch_trades_for_symbols" => { crate::exchange_stubs::enqueue_spawn("un_watch_trades_for_symbols", args.to_vec()); crate::Value::Null },
"watch_balance" => { crate::exchange_stubs::enqueue_spawn("watch_balance", args.to_vec()); crate::Value::Null },
"watch_bids_asks" => { crate::exchange_stubs::enqueue_spawn("watch_bids_asks", args.to_vec()); crate::Value::Null },
"watch_liquidations" => { crate::exchange_stubs::enqueue_spawn("watch_liquidations", args.to_vec()); crate::Value::Null },
"watch_liquidations_for_symbols" => { crate::exchange_stubs::enqueue_spawn("watch_liquidations_for_symbols", args.to_vec()); crate::Value::Null },
"watch_mark_price" => { crate::exchange_stubs::enqueue_spawn("watch_mark_price", args.to_vec()); crate::Value::Null },
"watch_mark_prices" => { crate::exchange_stubs::enqueue_spawn("watch_mark_prices", args.to_vec()); crate::Value::Null },
"watch_multi_ticker_helper" => { crate::exchange_stubs::enqueue_spawn("watch_multi_ticker_helper", args.to_vec()); crate::Value::Null },
"watch_my_liquidations" => { crate::exchange_stubs::enqueue_spawn("watch_my_liquidations", args.to_vec()); crate::Value::Null },
"watch_my_liquidations_for_symbols" => { crate::exchange_stubs::enqueue_spawn("watch_my_liquidations_for_symbols", args.to_vec()); crate::Value::Null },
"watch_my_trades" => { crate::exchange_stubs::enqueue_spawn("watch_my_trades", args.to_vec()); crate::Value::Null },
"watch_ohlcv" => { crate::exchange_stubs::enqueue_spawn("watch_ohlcv", args.to_vec()); crate::Value::Null },
"watch_ohlcv_for_symbols" => { crate::exchange_stubs::enqueue_spawn("watch_ohlcv_for_symbols", args.to_vec()); crate::Value::Null },
"watch_order_book" => { crate::exchange_stubs::enqueue_spawn("watch_order_book", args.to_vec()); crate::Value::Null },
"watch_order_book_for_symbols" => { crate::exchange_stubs::enqueue_spawn("watch_order_book_for_symbols", args.to_vec()); crate::Value::Null },
"watch_orders" => { crate::exchange_stubs::enqueue_spawn("watch_orders", args.to_vec()); crate::Value::Null },
"watch_positions" => { crate::exchange_stubs::enqueue_spawn("watch_positions", args.to_vec()); crate::Value::Null },
"watch_stock_market_stream" => { crate::exchange_stubs::enqueue_spawn("watch_stock_market_stream", args.to_vec()); crate::Value::Null },
"watch_ticker" => { crate::exchange_stubs::enqueue_spawn("watch_ticker", args.to_vec()); crate::Value::Null },
"watch_tickers" => { crate::exchange_stubs::enqueue_spawn("watch_tickers", args.to_vec()); crate::Value::Null },
"watch_trades" => { crate::exchange_stubs::enqueue_spawn("watch_trades", args.to_vec()); crate::Value::Null },
"watch_trades_for_symbols" => { crate::exchange_stubs::enqueue_spawn("watch_trades_for_symbols", args.to_vec()); crate::Value::Null },
_ => crate::Value::Null,
}
}
}
impl std::ops::Deref for BinanceCore {
type Target = crate::exchange::Exchange;
fn deref(&self) -> &crate::exchange::Exchange { std::ops::Deref::deref(&self.parent) }
}
impl std::ops::DerefMut for BinanceCore {
fn deref_mut(&mut self) -> &mut crate::exchange::Exchange { std::ops::DerefMut::deref_mut(&mut self.parent) }
}
impl BinanceCore {
pub fn describe(&self) -> Value {
let mut superDescribe: Value = self.parent.describe();
return self.deep_extend(superDescribe, &[self.describe_data()]);
Value::Null
}
pub fn describe_data(&self) -> Value {
return Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("has".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("ws".to_string(), Value::Bool(true));
m.insert("watchBalance".to_string(), Value::Bool(true));
m.insert("watchLiquidations".to_string(), Value::Bool(true));
m.insert("watchLiquidationsForSymbols".to_string(), Value::Bool(true));
m.insert("watchMyLiquidations".to_string(), Value::Bool(true));
m.insert("watchMyLiquidationsForSymbols".to_string(), Value::Bool(true));
m.insert("watchBidsAsks".to_string(), Value::Bool(true));
m.insert("watchMyTrades".to_string(), Value::Bool(true));
m.insert("watchOHLCV".to_string(), Value::Bool(true));
m.insert("watchOHLCVForSymbols".to_string(), Value::Bool(true));
m.insert("watchOrderBook".to_string(), Value::Bool(true));
m.insert("watchOrderBookForSymbols".to_string(), Value::Bool(true));
m.insert("watchOrders".to_string(), Value::Bool(true));
m.insert("watchOrdersForSymbols".to_string(), Value::Bool(true));
m.insert("watchPositions".to_string(), Value::Bool(true));
m.insert("watchTicker".to_string(), Value::Bool(true));
m.insert("watchTickers".to_string(), Value::Bool(true));
m.insert("watchMarkPrices".to_string(), Value::Bool(true));
m.insert("watchMarkPrice".to_string(), Value::Bool(true));
m.insert("watchTrades".to_string(), Value::Bool(true));
m.insert("watchTradesForSymbols".to_string(), Value::Bool(true));
m.insert("createOrderWs".to_string(), Value::Bool(true));
m.insert("editOrderWs".to_string(), Value::Bool(true));
m.insert("cancelOrderWs".to_string(), Value::Bool(true));
m.insert("cancelOrdersWs".to_string(), Value::Bool(false));
m.insert("cancelAllOrdersWs".to_string(), Value::Bool(true));
m.insert("fetchBalanceWs".to_string(), Value::Bool(true));
m.insert("fetchDepositsWs".to_string(), Value::Bool(false));
m.insert("fetchMarketsWs".to_string(), Value::Bool(false));
m.insert("fetchMyTradesWs".to_string(), Value::Bool(true));
m.insert("fetchOHLCVWs".to_string(), Value::Bool(true));
m.insert("fetchOrderBookWs".to_string(), Value::Bool(true));
m.insert("fetchOpenOrdersWs".to_string(), Value::Bool(true));
m.insert("fetchOrderWs".to_string(), Value::Bool(true));
m.insert("fetchOrdersWs".to_string(), Value::Bool(true));
m.insert("fetchPositionWs".to_string(), Value::Bool(true));
m.insert("fetchPositionForSymbolWs".to_string(), Value::Bool(true));
m.insert("fetchPositionsWs".to_string(), Value::Bool(true));
m.insert("fetchTickerWs".to_string(), Value::Bool(true));
m.insert("fetchTradesWs".to_string(), Value::Bool(true));
m.insert("fetchTradingFeesWs".to_string(), Value::Bool(false));
m.insert("fetchWithdrawalsWs".to_string(), Value::Bool(false));
m.insert("unWatchTicker".to_string(), Value::Bool(true));
m.insert("unWatchTickers".to_string(), Value::Bool(true));
m.insert("unWatchOHLCV".to_string(), Value::Bool(true));
m.insert("unWatchOHLCVForSymbols".to_string(), Value::Bool(true));
m.insert("unWatchOrderBook".to_string(), Value::Bool(true));
m.insert("unWatchOrderBookForSymbols".to_string(), Value::Bool(true));
m.insert("unWatchTrades".to_string(), Value::Bool(true));
m.insert("unWatchTradesForSymbols".to_string(), Value::Bool(true));
m.insert("unWatchMyTrades".to_string(), Value::Bool(false));
m.insert("unWatchOrders".to_string(), Value::Bool(false));
m.insert("unWatchPositions".to_string(), Value::Bool(false));
m.insert("unWatchMarkPrices".to_string(), Value::Bool(true));
m.insert("unWatchMarkPrice".to_string(), Value::Bool(true));
m.insert("unWatchBidsAsks".to_string(), Value::Bool(true));
m
}));
m.insert("urls".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("test".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("ws".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("spot".to_string(), Value::Str("wss://stream.testnet.binance.vision/ws".into()));
m.insert("margin".to_string(), Value::Str("wss://stream.testnet.binance.vision/ws".into()));
m.insert("future".to_string(), Value::Str("wss://fstream.binancefuture.com/ws".into()));
m.insert("delivery".to_string(), Value::Str("wss://dstream.binancefuture.com/ws".into()));
m.insert("ws-api".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("spot".to_string(), Value::Str("wss://ws-api.testnet.binance.vision/ws-api/v3".into()));
m.insert("future".to_string(), Value::Str("wss://testnet.binancefuture.com/ws-fapi/v1".into()));
m.insert("delivery".to_string(), Value::Str("wss://testnet.binancefuture.com/ws-dapi/v1".into()));
m
}));
m
}));
m
}));
m.insert("demo".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("ws".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("spot".to_string(), Value::Str("wss://demo-stream.binance.com/ws".into()));
m.insert("margin".to_string(), Value::Str("wss://demo-stream.binance.com/ws".into()));
m.insert("future".to_string(), Value::Str("wss://fstream.binancefuture.com/ws".into()));
m.insert("delivery".to_string(), Value::Str("wss://dstream.binancefuture.com/ws".into()));
m.insert("ws-api".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("spot".to_string(), Value::Str("wss://demo-ws-api.binance.com/ws-api/v3".into()));
m.insert("future".to_string(), Value::Str("wss://testnet.binancefuture.com/ws-fapi/v1".into()));
m.insert("delivery".to_string(), Value::Str("wss://testnet.binancefuture.com/ws-dapi/v1".into()));
m
}));
m
}));
m
}));
m.insert("api".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("ws".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("spot".to_string(), Value::Str("wss://stream.binance.com:9443/ws".into()));
m.insert("margin".to_string(), Value::Str("wss://stream.binance.com:9443/ws".into()));
m.insert("future".to_string(), Value::Str("wss://fstream.binance.com/ws".into()));
m.insert("delivery".to_string(), Value::Str("wss://dstream.binance.com/ws".into()));
m.insert("stock".to_string(), Value::Str("wss://nbstream.binance.com/equity/ws".into()));
m.insert("option".to_string(), Value::Str("wss://fstream.binance.com/public/ws".into()));
m.insert("optionMarket".to_string(), Value::Str("wss://fstream.binance.com/market/ws".into()));
m.insert("optionPrivate".to_string(), Value::Str("wss://fstream.binance.com/private/ws".into()));
m.insert("ws-api".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("spot".to_string(), Value::Str("wss://ws-api.binance.com:443/ws-api/v3".into()));
m.insert("future".to_string(), Value::Str("wss://ws-fapi.binance.com/ws-fapi/v1".into()));
m.insert("delivery".to_string(), Value::Str("wss://ws-dapi.binance.com/ws-dapi/v1".into()));
m
}));
m.insert("papi".to_string(), Value::Str("wss://fstream.binance.com/pm/ws".into()));
m
}));
m
}));
m.insert("doc".to_string(), Value::Str("https://developers.binance.com/en".into()));
m
}));
m.insert("streaming".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("keepAlive".to_string(), Value::Int(180000));
m
}));
m.insert("options".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("returnRateLimits".to_string(), Value::Bool(false));
m.insert("streamLimits".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("spot".to_string(), Value::Int(50));
m.insert("margin".to_string(), Value::Int(50));
m.insert("future".to_string(), Value::Int(50));
m.insert("delivery".to_string(), Value::Int(50));
m.insert("stock".to_string(), Value::Int(50));
m.insert("option".to_string(), Value::Int(50));
m.insert("optionMarket".to_string(), Value::Int(50));
m
}));
m.insert("subscriptionLimitByStream".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("spot".to_string(), Value::Int(200));
m.insert("margin".to_string(), Value::Int(200));
m.insert("future".to_string(), Value::Int(200));
m.insert("delivery".to_string(), Value::Int(200));
m.insert("stock".to_string(), Value::Int(200));
m.insert("option".to_string(), Value::Int(200));
m.insert("optionMarket".to_string(), Value::Int(200));
m
}));
m.insert("streamBySubscriptionsHash".to_string(), self.create_safe_dictionary(&[]));
m.insert("streamIndex".to_string(), Value::Int(-1));
m.insert("watchOrderBookRate".to_string(), Value::Int(100));
m.insert("liquidationsLimit".to_string(), Value::Int(1000));
m.insert("myLiquidationsLimit".to_string(), Value::Int(1000));
m.insert("tradesLimit".to_string(), Value::Int(1000));
m.insert("ordersLimit".to_string(), Value::Int(1000));
m.insert("OHLCVLimit".to_string(), Value::Int(1000));
m.insert("requestId".to_string(), self.create_safe_dictionary(&[]));
m.insert("watchOrderBookLimit".to_string(), Value::Int(1000));
m.insert("watchTrades".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("name".to_string(), Value::Str("trade".into()));
m
}));
m.insert("watchTicker".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("name".to_string(), Value::Str("ticker".into()));
m
}));
m.insert("watchTickers".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("name".to_string(), Value::Str("miniTicker".into()));
m
}));
m.insert("watchOHLCV".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("name".to_string(), Value::Str("kline".into()));
m
}));
m.insert("watchOrderBook".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("maxRetries".to_string(), Value::Int(3));
m.insert("checksum".to_string(), Value::Bool(true));
m
}));
m.insert("option".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("listenKey".to_string(), Value::Null);
m.insert("lastAuthenticatedTime".to_string(), Value::Int(0));
m
}));
m.insert("watchBalance".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("fetchBalanceSnapshot".to_string(), Value::Bool(false));
m.insert("awaitBalanceSnapshot".to_string(), Value::Bool(true));
m
}));
m.insert("watchLiquidationsForSymbols".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("defaultType".to_string(), Value::Str("swap".into()));
m
}));
m.insert("watchPositions".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("fetchPositionsSnapshot".to_string(), Value::Bool(true));
m.insert("awaitPositionsSnapshot".to_string(), Value::Bool(true));
m
}));
m.insert("wallet".to_string(), Value::Str("wb".into()));
m.insert("listenKeyRefreshRate".to_string(), Value::Int(1200000));
m.insert("stockListenKeyRefreshRate".to_string(), Value::Int(1200000));
m.insert("ws".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("cost".to_string(), Value::Int(5));
m
}));
m.insert("tickerChannelsMap".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("price".to_string(), Value::Str("price".into()));
m.insert("quote".to_string(), Value::Str("quote".into()));
m.insert("24hrTicker".to_string(), Value::Str("ticker".into()));
m.insert("24hrMiniTicker".to_string(), Value::Str("miniTicker".into()));
m.insert("markPriceUpdate".to_string(), Value::Str("markPrice".into()));
m.insert("markPrice".to_string(), Value::Str("markPrice".into()));
m.insert("1hTicker".to_string(), Value::Str("ticker_1h".into()));
m.insert("4hTicker".to_string(), Value::Str("ticker_4h".into()));
m.insert("1dTicker".to_string(), Value::Str("ticker_1d".into()));
m.insert("bookTicker".to_string(), Value::Str("bookTicker".into()));
m
}));
m
}));
m
});
Value::Null
}
pub fn request_id(&self, mut url: Value) -> Value {
let mut options: Value = self.safe_dict_k(self.options.clone(), "requestId", &[self.create_safe_dictionary(&[])]);
let mut previousValue: Value = self.safe_integer(options.clone(), url.clone(), &[Value::Int(0)]);
let mut newValue: Value = self.sum(&[previousValue, Value::Int(1)]);
add_element_to_object(get_value_mut(unsafe { crate::runtime::coerce_value_to_mut(&self.options) }, &Value::Str("requestId".into())), &url, newValue.clone());
return newValue;
Value::Null
}
pub fn is_spot_url(&self, mut client: Value) -> Value {
return Value::Bool((Value::Int(get_value(&client, &Value::Str("url".into())).as_str().and_then(|__s| __s.find("/stream")).map(|__i| __i as i64).unwrap_or(-1)).as_f64().unwrap_or(f64::NAN) > ((-1i64) as f64)) || (Value::Int(get_value(&client, &Value::Str("url".into())).as_str().and_then(|__s| __s.find("demo-stream")).map(|__i| __i as i64).unwrap_or(-1)).as_f64().unwrap_or(f64::NAN) > ((-1i64) as f64)));
Value::Null
}
pub fn stream(&mut self, mut type_var: Value, mut subscriptionHash: Value, optional_args: &[Value]) -> Option<String> {
let mut numSubscriptions = get_arg(optional_args, 0, Value::Int(1));
let mut streamBySubscriptionsHash: Value = self.safe_dict_k(self.options.clone(), "streamBySubscriptionsHash", &[self.create_safe_dictionary(&[])]);
let mut stream: Value = self.safe_string(streamBySubscriptionsHash, subscriptionHash.clone(), &[]);
if (stream == Value::Null) {
let mut streamIndex: Value = self.safe_integer_k(self.options.clone(), "streamIndex", &[Value::Int(-1)]);
let mut streamLimits: Value = self.safe_dict_k(self.options.clone(), "streamLimits", &[]);
let mut streamLimit: Value = self.safe_integer(streamLimits, type_var.clone(), &[]);
streamIndex = (match (&(streamIndex), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null });
let mut normalizedIndex: Value = mod_val(&streamIndex, &streamLimit);
if let Value::Dict(__d) = &mut self.options { std::sync::Arc::make_mut(__d).insert("streamIndex".into(), streamIndex); }
stream = self.number_to_string(normalizedIndex);
if (subscriptionHash != Value::Null) {
add_element_to_object(get_value_mut(&mut self.options, &Value::Str("streamBySubscriptionsHash".into())), &subscriptionHash, stream.clone());
}
let mut subscriptionsByStreams: Value = self.safe_dict_k(self.options.clone(), "numSubscriptionsByStream", &[]);
if (subscriptionsByStreams == Value::Null) {
{ let __be_tmp = self.create_safe_dictionary(&[]); if let Value::Dict(__d) = &mut self.options { std::sync::Arc::make_mut(__d).insert("numSubscriptionsByStream".into(), __be_tmp); } }
}
let mut subscriptionsByStream: Value = self.safe_integer(self.options.as_map().and_then(|__m| __m.get("numSubscriptionsByStream")).cloned().unwrap_or(Value::Null), stream.clone(), &[Value::Int(0)]);
let mut newNumSubscriptions: Value = (match (&(subscriptionsByStream), &(numSubscriptions)) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null });
let mut subscriptionLimitByStream: Value = self.safe_integer(self.options.as_map().and_then(|__m| __m.get("subscriptionLimitByStream")).cloned().unwrap_or(Value::Null), type_var, &[Value::Int(200)]);
if newNumSubscriptions.as_f64().unwrap_or(f64::NAN) > subscriptionLimitByStream.as_f64().unwrap_or(f64::NAN) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" reached the limit of subscriptions by stream. Increase the number of streams, or increase the stream limit or subscription limit by stream if the exchange allows.".into()))));
}
add_element_to_object(get_value_mut(&mut self.options, &Value::Str("numSubscriptionsByStream".into())), &stream, (match (&(subscriptionsByStream), &(numSubscriptions)) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }));
}
return stream.as_str().map(str::to_owned);
}
pub fn get_ws_url(&self, mut type_var: Value, mut category: Value) -> Value {
if (type_var.as_str() == Some("option")) || (type_var.as_str() == Some("optionMarket")) || (type_var.as_str() == Some("optionPrivate")) {
return get_value(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), &type_var);
}
let mut baseUrl: Value = get_value(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), &type_var);
if (type_var.as_str() == Some("future")) {
// skip URL manipulation for proxied/bridge URLs (contain an embedded protocol)
// const firstProtocol = baseUrl.indexOf ('://');
// if (firstProtocol !== -1 && baseUrl.indexOf ('://', firstProtocol + 3) !== -1) {
// return baseUrl;
// }
let mut baseUrlSplit: Value = split(&baseUrl, &Value::Str("://".into()));
let mut baseUrlSplitLength: f64 = ((baseUrlSplit.len() as i64) as f64);
if baseUrlSplitLength > ((2i64) as f64) {
return baseUrl;
}
// only rewrite when the URL ends with exactly "/ws"
// this avoids matching "/wss", "/ws-api", "/ws-fapi/v1", etc.
if (ends_with(&baseUrl, &Value::Str("/ws".into()))) {
let mut prefix: Value = slice(&baseUrl, &Value::Int(0), &(match (&(Value::Int(baseUrl.len() as i64)), &(Value::Int(3))) { (Value::Int(x), Value::Int(y)) => Value::Int(x - y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x - *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x - y), _ => Value::Null }));
return Value::Str(format!("{}{}", add(&Value::Str(format!("{}{}", prefix, Value::Str("/".into())).into()), &category), Value::Str("/ws".into())).into());
}
return baseUrl;
}
return baseUrl;
Value::Null
}
pub fn get_future_ws_category(&self, mut channel: Value) -> Value {
if (channel.as_str() == Some("depth")) || (channel.as_str() == Some("rpiDepth")) || (channel.as_str() == Some("bookTicker")) || (channel.as_str() == Some("trade")) {
return Value::Str("public".into());
}
return Value::Str("market".into());
Value::Null
}
pub fn get_private_ws_url(&self, mut type_var: Value, mut listenKey: Value) -> Value {
if (type_var.as_str() == Some("future")) {
return Value::Str(format!("{}{}", add(&self.get_ws_url(type_var.clone(), Value::Str("private".into())), &Value::Str("?listenKey=".into())), listenKey).into());
}
return Value::Str(format!("{}{}", add(&get_value(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), &type_var), &Value::Str("/".into())), listenKey).into());
Value::Null
}
pub fn get_stock_ws_url(&self, optional_args: &[Value]) -> Value {
let mut streamType = get_arg(optional_args, 0, Value::Str("market".into()));
let mut baseUrl: Value = crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "stock");
if (streamType.as_str() == Some("combined")) {
return replace_str(&baseUrl, &Value::Str("/ws".into()), &Value::Str("/stream".into()));
}
return baseUrl;
Value::Null
}
pub fn get_stock_ticker_from_symbol(&self, mut symbol: Value) -> Option<String> {
let mut market: Value = self.market(symbol);
let mut base: Value = self.safe_string2(market, Value::Str("base".into()), Value::Str("id".into()), &[]);
return (if (base == Value::Null) { Value::Null } else { to_lower(&base) }).as_str().map(str::to_owned);
}
pub fn get_stock_unified_symbol(&self, mut stockSymbol: Value, optional_args: &[Value]) -> Option<String> {
let mut quote = get_arg(optional_args, 0, Value::Null);
if (stockSymbol == Value::Null) {
return None;
}
let mut safeQuote: Value = (if (quote == Value::Null) { Value::Str("USDC".into()) } else { quote });
let mut parsed: Value = self.safe_symbol(stockSymbol.clone(), &[Value::Null, Value::Str("/".into()), Value::Str("spot".into())]);
if (parsed != Value::Null) && (Value::Int(parsed.as_str().and_then(|__s| __s.find("/")).map(|__i| __i as i64).unwrap_or(-1)).as_f64().unwrap_or(f64::NAN) >= ((0i64) as f64)) {
return parsed.as_str().map(str::to_owned);
}
return Value::Str(format!("{}{}", Value::Str(format!("{}{}", stockSymbol, Value::Str("/".into())).into()), safeQuote).into()).as_str().map(str::to_owned);
}
/*
* @method
* @name binance#watchStockMarketStream
* @ignore
* @description subscribe to the tokenized stock market data stream
* @param {string[]} streams stream names to subscribe to
* @param {string[]} messageHashes message hashes to listen to
* @param {object} params extra parameters specific to the exchange API endpoint
* @returns {object} the raw stream subscription response
*/
pub async fn watch_stock_market_stream(&mut self, mut streams: Value, mut messageHashes: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let mut url: Value = self.get_stock_ws_url(&[Value::Str("market".into())]);
let mut requestId: Value = self.request_id(url.clone());
let mut query: Value = self.omit(params, Value::from(vec![Value::Str("stock".into()), Value::Str("name".into()), Value::Str("callerMethodName".into()), Value::Str("type".into()), Value::Str("subType".into()), Value::Str("symbol".into()), Value::Str("timeframe".into())]), &[]);
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("SUBSCRIBE".into()));
m.insert("params".to_string(), streams);
m.insert("id".to_string(), requestId.clone());
m
});
let mut subscribe: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), requestId);
m
});
let __ws_arg_0 = self.extend(request, &[query]);
return self.watch_multiple(url, messageHashes.clone(), &[__ws_arg_0, messageHashes.clone(), subscribe]).await;
Value::Null
}
/*
* @method
* @name binance#watchLiquidations
* @description watch the public liquidations of a trading pair
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Liquidation-Order-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Liquidation-Order-Streams
* @param {string} symbol unified CCXT market symbol
* @param {int} [since] the earliest time in ms to fetch liquidations for
* @param {int} [limit] the maximum number of liquidation structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an array of [liquidation structures]{@link https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure}
*/
pub async fn watch_liquidations(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut since = get_arg(optional_args, 0, Value::Null);
let mut limit = get_arg(optional_args, 1, Value::Null);
let mut params = get_arg(optional_args, 2, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
return self.watch_liquidations_for_symbols(Value::from(vec![symbol]), &[since, limit, params]).await;
Value::Null
}
/*
* @method
* @name binance#watchLiquidationsForSymbols
* @description watch the public liquidations of a trading pair
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/All-Market-Liquidation-Order-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/All-Market-Liquidation-Order-Streams
* @param {string[]} symbols list of unified market symbols
* @param {int} [since] the earliest time in ms to fetch liquidations for
* @param {int} [limit] the maximum number of liquidation structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an array of [liquidation structures]{@link https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure}
*/
pub async fn watch_liquidations_for_symbols(&mut self, mut symbols: Value, optional_args: &[Value]) -> Value {
let mut since = get_arg(optional_args, 0, Value::Null);
let mut limit = get_arg(optional_args, 1, Value::Null);
let mut params = get_arg(optional_args, 2, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut subscriptionHashes: Value = Value::from(vec![]);
let mut messageHashes: Value = Value::from(vec![]);
let mut streamHash: Value = Value::Str("liquidations".into());
symbols = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(true), Value::Bool(true)]);
if self.is_empty(symbols.clone()).as_bool() == Some(true) {
append_to_array(&mut subscriptionHashes, Value::Str(format!("{}{}", Value::Str("!".into()), Value::Str("forceOrder@arr".into())).into()));
append_to_array(&mut messageHashes, Value::Str("liquidations".into()));
} else {
{
let mut i: Value = Value::Int(0);
let mut __for_first_55: bool = true;
while { if !__for_first_55 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_55 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut market: Value = self.market(symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null));
append_to_array(&mut subscriptionHashes, Value::Str(format!("{}{}", market.as_map().and_then(|__m| __m.get("lowercaseId")).cloned().unwrap_or(Value::Null), Value::Str("@forceOrder".into())).into()));
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str("liquidations::".into()), symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null)).into()));
}
}
streamHash = Value::Str(format!("{}{}", streamHash, Value::Str(format!("{}{}", Value::Str("::".into()), join(&symbols, &Value::Str(",".into()))).into())).into());
}
let mut firstMarket: Value = Value::Null;
if !(self.is_empty(symbols.clone()).as_bool() == Some(true)) {
firstMarket = self.get_market_from_symbols(&[symbols.clone()]);
}
let mut resolvedAuth: Value = self.resolve_auth_type(Value::Str("watchLiquidationsForSymbols".into()), &[firstMarket, params.clone()]);
let mut type_var: Value = resolvedAuth.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null);
params = resolvedAuth.as_array().and_then(|__arr| __arr.get(2)).cloned().unwrap_or(Value::Null);
// the spot check runs on the RESOLVED type: a spot default combined
// with a linear or inverse defaultSubType means the caller wants the
// matching derivatives stream, so the rewrite is allowed to route it
// there and only a request that still resolves to spot throws
if (type_var.as_str() == Some("spot")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" watchLiquidationsForSymbols is not supported for spot symbols".into()))));
}
if (type_var.as_str() == Some("option")) {
panic!("{}", crate::exchange_errors::not_supported(format!("{}{}", self.id.clone(), Value::Str(" watchLiquidationsForSymbols() does not support options markets, there is no public liquidation stream for eOptions".into()))));
}
let mut numSubscriptions: Value = Value::Int(subscriptionHashes.len() as i64);
let mut url: Value = Value::Str(format!("{}{}", add(&self.get_ws_url(type_var.clone(), self.get_future_ws_category(Value::Str("forceOrder".into()))), &Value::Str("/".into())), self.stream(type_var, streamHash, &[numSubscriptions]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null)).into());
let mut requestId: Value = self.request_id(url.clone());
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("SUBSCRIBE".into()));
m.insert("params".to_string(), subscriptionHashes.clone());
m.insert("id".to_string(), requestId.clone());
m
});
let mut subscribe: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), requestId);
m
});
let __ws_arg_1 = self.extend(request, &[params]);
let mut newLiquidations: Value = self.watch_multiple(url, messageHashes, &[__ws_arg_1, subscriptionHashes, subscribe]).await;
if is_true(&self.newUpdates) {
return newLiquidations;
}
return self.filter_by_symbols_since_limit(self.liquidations.clone(), &[symbols, since, limit, Value::Bool(true)]);
Value::Null
}
pub fn handle_liquidation(&mut self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// future
// {
// "e":"forceOrder",
// "E":1698871323061,
// "o":{
// "s":"BTCUSDT",
// "S":"BUY",
// "o":"LIMIT",
// "f":"IOC",
// "q":"1.437",
// "p":"35100.81",
// "ap":"34959.70",
// "X":"FILLED",
// "l":"1.437",
// "z":"1.437",
// "T":1698871323059
// }
// }
// delivery
// {
// "e":"forceOrder", // Event Type
// "E": 1591154240950, // Event Time
// "o":{
// "s":"BTCUSD_200925", // Symbol
// "ps": "BTCUSD", // Pair
// "S":"SELL", // Side
// "o":"LIMIT", // Order Type
// "f":"IOC", // Time in Force
// "q":"1", // Original Quantity
// "p":"9425.5", // Price
// "ap":"9496.5", // Average Price
// "X":"FILLED", // Order Status
// "l":"1", // Order Last Filled Quantity
// "z":"1", // Order Filled Accumulated Quantity
// "T": 1591154240949, // Order Trade Time
// }
// }
//
let mut rawLiquidation: Value = (match message.get("o") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut marketId: Value = self.safe_string_k(rawLiquidation.clone(), "s", &[]);
let mut market: Value = self.safe_market(&[marketId, Value::Null, Value::Str("".into()), Value::Str("contract".into())]);
let mut symbol: Value = market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
let mut liquidation: Value = self.parse_ws_liquidation(rawLiquidation, &[market]);
if (self.liquidations.clone() == Value::Null) {
let mut limit: Value = self.safe_integer_k(self.options.clone(), "liquidationsLimit", &[Value::Int(1000)]);
self.liquidations = ArrayCache::new(limit);
}
let mut cache: Value = self.liquidations.clone();
cache.append(liquidation.clone());
client.resolve(&[Value::from(vec![liquidation.clone()]), Value::Str("liquidations".into())]);
client.resolve(&[Value::from(vec![liquidation]), Value::Str(format!("{}{}", Value::Str("liquidations::".into()), symbol).into())]);
}
pub fn parse_ws_liquidation(&self, mut liquidation: Value, optional_args: &[Value]) -> Value {
let mut market = get_arg(optional_args, 0, Value::Null);
//
// future
// {
// "s":"BTCUSDT",
// "S":"BUY",
// "o":"LIMIT",
// "f":"IOC",
// "q":"1.437",
// "p":"35100.81",
// "ap":"34959.70",
// "X":"FILLED",
// "l":"1.437",
// "z":"1.437",
// "T":1698871323059
// }
// delivery
// {
// "s":"BTCUSD_200925", // Symbol
// "ps": "BTCUSD", // Pair
// "S":"SELL", // Side
// "o":"LIMIT", // Order Type
// "f":"IOC", // Time in Force
// "q":"1", // Original Quantity
// "p":"9425.5", // Price
// "ap":"9496.5", // Average Price
// "X":"FILLED", // Order Status
// "l":"1", // Order Last Filled Quantity
// "z":"1", // Order Filled Accumulated Quantity
// "T": 1591154240949, // Order Trade Time
// }
// myLiquidation
// {
// "s":"BTCUSDT", // Symbol
// "c":"TEST", // Client Order Id
// // special client order id:
// // starts with "autoclose-": liquidation order
// // "adl_autoclose": ADL auto close order
// // "settlement_autoclose-": settlement order for delisting or delivery
// "S":"SELL", // Side
// "o":"TRAILING_STOP_MARKET", // Order Type
// "f":"GTC", // Time in Force
// "q":"0.001", // Original Quantity
// "p":"0", // Original Price
// "ap":"0", // Average Price
// "sp":"7103.04", // Stop Price. Please ignore with TRAILING_STOP_MARKET order
// "x":"NEW", // Execution Type
// "X":"NEW", // Order Status
// "i":8886774, // Order Id
// "l":"0", // Order Last Filled Quantity
// "z":"0", // Order Filled Accumulated Quantity
// "L":"0", // Last Filled Price
// "N":"USDT", // Commission Asset, will not push if no commission
// "n":"0", // Commission, will not push if no commission
// "T":1568879465650, // Order Trade Time
// "t":0, // Trade Id
// "b":"0", // Bids Notional
// "a":"9.91", // Ask Notional
// "m":false, // Is this trade the maker side?
// "R":false, // Is this reduce only
// "wt":"CONTRACT_PRICE", // Stop Price Working Type
// "ot":"TRAILING_STOP_MARKET",// Original Order Type
// "ps":"LONG", // Position Side
// "cp":false, // If Close-All, pushed with conditional order
// "AP":"7476.89", // Activation Price, only puhed with TRAILING_STOP_MARKET order
// "cr":"5.0", // Callback Rate, only puhed with TRAILING_STOP_MARKET order
// "pP": false, // If price protection is turned on
// "si": 0, // ignore
// "ss": 0, // ignore
// "rp":"0", // Realized Profit of the trade
// "V":"EXPIRE_TAKER", // STP mode
// "pm":"OPPONENT", // Price match mode
// "gtd":0 // TIF GTD order auto cancel time
// }
//
let mut marketId: Value = self.safe_string_k(liquidation.clone(), "s", &[]);
market = self.safe_market(&[marketId.clone(), market.clone(), Value::Null, Value::Str("swap".into())]);
let mut timestamp: Value = self.safe_integer_k(liquidation.clone(), "T", &[]);
return self.safe_liquidation(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("info".to_string(), liquidation.clone());
m.insert("symbol".to_string(), self.safe_symbol(marketId, &[market.clone()]));
m.insert("contracts".to_string(), self.safe_number_k(liquidation.clone(), "l", &[]));
m.insert("contractSize".to_string(), self.safe_number_k(market, "contractSize", &[]));
m.insert("price".to_string(), self.safe_number_k(liquidation.clone(), "ap", &[]));
m.insert("side".to_string(), self.safe_string_lower_k(liquidation, "S", &[]));
m.insert("baseValue".to_string(), Value::Null);
m.insert("quoteValue".to_string(), Value::Null);
m.insert("timestamp".to_string(), timestamp.clone());
m.insert("datetime".to_string(), self.iso8601(timestamp));
m
}), &[]);
Value::Null
}
/*
* @method
* @name binance#watchMyLiquidations
* @description watch the private liquidations of a trading pair
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/user-data-streams/Event-Order-Update
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/user-data-streams/Event-Order-Update
* @param {string} symbol unified CCXT market symbol
* @param {int} [since] the earliest time in ms to fetch liquidations for
* @param {int} [limit] the maximum number of liquidation structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an array of [liquidation structures]{@link https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure}
*/
pub async fn watch_my_liquidations(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut since = get_arg(optional_args, 0, Value::Null);
let mut limit = get_arg(optional_args, 1, Value::Null);
let mut params = get_arg(optional_args, 2, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
return self.watch_my_liquidations_for_symbols(Value::from(vec![symbol]), &[since, limit, params]).await;
Value::Null
}
/*
* @method
* @name binance#watchMyLiquidationsForSymbols
* @description watch the private liquidations of a trading pair
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/user-data-streams/Event-Order-Update
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/user-data-streams/Event-Order-Update
* @param {string[]} symbols list of unified market symbols
* @param {int} [since] the earliest time in ms to fetch liquidations for
* @param {int} [limit] the maximum number of liquidation structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an array of [liquidation structures]{@link https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure}
*/
pub async fn watch_my_liquidations_for_symbols(&mut self, mut symbols: Value, optional_args: &[Value]) -> Value {
let mut since = get_arg(optional_args, 0, Value::Null);
let mut limit = get_arg(optional_args, 1, Value::Null);
let mut params = get_arg(optional_args, 2, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
symbols = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(true), Value::Bool(true), Value::Bool(true)]);
let mut market: Value = self.get_market_from_symbols(&[symbols.clone()]);
let mut messageHashes: Value = Value::from(vec![Value::Str("myLiquidations".into())]);
if !(self.is_empty(symbols.clone()).as_bool() == Some(true)) {
{
let mut i: Value = Value::Int(0);
let mut __for_first_56: bool = true;
while { if !__for_first_56 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_56 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut symbol: Value = symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str("myLiquidations::".into()), symbol).into()));
}
}
}
let mut type_var: Value = Value::Null;
let mut subType: Value = Value::Null;
{ let __destr_tmp = self.resolve_auth_type(Value::Str("watchMyLiquidationsForSymbols".into()), &[market, params.clone()]); type_var = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); subType = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(2)).cloned().unwrap_or(Value::Null); }
// hand the resolved type forward: the helper already omitted type and
// subType from params, so a bare authenticate would re-derive from
// options.defaultType and seed a different bucket than the listenKey
// read below indexes - the derive-first shape watchBalance uses
let __ws_arg_2 = self.extend(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), type_var.clone());
m.insert("subType".to_string(), subType);
m
}), &[params]);
self.authenticate(&[__ws_arg_2]).await;
let mut listenKey: Value = crate::value::get_value_k(&get_value(&self.options, &type_var), "listenKey");
let mut url: Value = self.get_private_ws_url(type_var.clone(), listenKey);
let mut message: Value = Value::Null;
let mut newLiquidations: Value = self.watch_multiple(url, messageHashes, &[message, Value::from(vec![type_var])]).await;
if is_true(&self.newUpdates) {
return newLiquidations;
}
return self.filter_by_symbols_since_limit(self.liquidations.clone(), &[symbols, since, limit]);
Value::Null
}
pub fn handle_my_liquidation(&mut self, mut client: Value, mut message: Value) {
//
// {
// "s":"BTCUSDT", // Symbol
// "c":"TEST", // Client Order Id
// // special client order id:
// // starts with "autoclose-": liquidation order
// // "adl_autoclose": ADL auto close order
// // "settlement_autoclose-": settlement order for delisting or delivery
// "S":"SELL", // Side
// "o":"TRAILING_STOP_MARKET", // Order Type
// "f":"GTC", // Time in Force
// "q":"0.001", // Original Quantity
// "p":"0", // Original Price
// "ap":"0", // Average Price
// "sp":"7103.04", // Stop Price. Please ignore with TRAILING_STOP_MARKET order
// "x":"NEW", // Execution Type
// "X":"NEW", // Order Status
// "i":8886774, // Order Id
// "l":"0", // Order Last Filled Quantity
// "z":"0", // Order Filled Accumulated Quantity
// "L":"0", // Last Filled Price
// "N":"USDT", // Commission Asset, will not push if no commission
// "n":"0", // Commission, will not push if no commission
// "T":1568879465650, // Order Trade Time
// "t":0, // Trade Id
// "b":"0", // Bids Notional
// "a":"9.91", // Ask Notional
// "m":false, // Is this trade the maker side?
// "R":false, // Is this reduce only
// "wt":"CONTRACT_PRICE", // Stop Price Working Type
// "ot":"TRAILING_STOP_MARKET",// Original Order Type
// "ps":"LONG", // Position Side
// "cp":false, // If Close-All, pushed with conditional order
// "AP":"7476.89", // Activation Price, only puhed with TRAILING_STOP_MARKET order
// "cr":"5.0", // Callback Rate, only puhed with TRAILING_STOP_MARKET order
// "pP": false, // If price protection is turned on
// "si": 0, // ignore
// "ss": 0, // ignore
// "rp":"0", // Realized Profit of the trade
// "V":"EXPIRE_TAKER", // STP mode
// "pm":"OPPONENT", // Price match mode
// "gtd":0 // TIF GTD order auto cancel time
// }
//
let mut orderType: Option<String> = self.safe_string_k(message.clone(), "o", &[]).as_str().map(str::to_owned);
if (orderType.as_deref() != Some("LIQUIDATION")) {
return;
}
let mut marketId: Value = self.safe_string_k(message.clone(), "s", &[]);
let mut market: Value = self.safe_market(&[marketId.clone(), Value::Null, Value::Null, Value::Str("swap".into())]);
let mut symbol: Value = self.safe_symbol(marketId, &[market.clone()]);
let mut liquidation: Value = self.parse_ws_liquidation(message, &[market]);
let mut cache: Value = self.myLiquidations.clone();
if (cache == Value::Null) {
let mut limit: Value = self.safe_integer_k(self.options.clone(), "myLiquidationsLimit", &[Value::Int(1000)]);
cache = ArrayCache::new(limit);
}
cache.append(liquidation.clone());
self.myLiquidations = cache;
client.resolve(&[Value::from(vec![liquidation.clone()]), Value::Str("myLiquidations".into())]);
client.resolve(&[Value::from(vec![liquidation]), Value::Str(format!("{}{}", Value::Str("myLiquidations::".into()), symbol).into())]);
}
/*
* @method
* @name binance#watchOrderBook
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#partial-book-depth-streams
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#diff-depth-stream
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Partial-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Diff-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Diff-Book-Depth-Streams-RPI
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Partial-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Diff-Book-Depth-Streams
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/?id=order-book-structure}
*/
pub async fn watch_order_book(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut limit = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
return self.watch_order_book_for_symbols(Value::from(vec![symbol]), &[limit, params]).await;
Value::Null
}
/*
* @method
* @name binance#watchOrderBookForSymbols
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#partial-book-depth-streams
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#diff-depth-stream
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Partial-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Diff-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Diff-Book-Depth-Streams-RPI
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Partial-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Diff-Book-Depth-Streams
* @param {string[]} symbols unified array of symbols
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.rpi] *future only* set to true to use the RPI endpoint
* @returns {object} an [order book structure]{@link https://docs.ccxt.com/?id=order-book-structure}
*/
pub async fn watch_order_book_for_symbols(&mut self, mut symbols: Value, optional_args: &[Value]) -> Value {
let mut limit = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
symbols = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(false), Value::Bool(true), Value::Bool(true)]);
let mut firstMarket: Value = self.market(symbols.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null));
let mut type_var: Value = firstMarket.as_map().and_then(|__m| __m.get("type")).cloned().unwrap_or(Value::Null);
if (firstMarket.as_map().and_then(|__m| __m.get("option")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
type_var = Value::Str("option".into());
} else if (firstMarket.as_map().and_then(|__m| __m.get("contract")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
type_var = (if (firstMarket.as_map().and_then(|__m| __m.get("linear")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) { Value::Str("future".into()) } else { Value::Str("delivery".into()) });
}
let mut name: Value = Value::Str("depth".into());
let mut streamHash: Value = Value::Str("multipleOrderbook".into());
if (symbols != Value::Null) {
let mut symbolsLength: f64 = ((symbols.len() as i64) as f64);
if symbolsLength > ((200i64) as f64) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" watchOrderBookForSymbols() accepts 200 symbols at most. To watch more symbols call watchOrderBookForSymbols() multiple times".into()))));
}
streamHash = Value::Str(format!("{}{}", streamHash, Value::Str(format!("{}{}", Value::Str("::".into()), join(&symbols, &Value::Str(",".into()))).into())).into());
}
let mut watchOrderBookRate: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchOrderBookForSymbols".into()), Value::Str("watchOrderBookRate".into()), &[Value::Str("100".into())]); watchOrderBookRate = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut rpi: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchOrderBookForSymbols".into()), Value::Str("rpi".into()), &[Value::Bool(false)]); rpi = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if is_true(&rpi) && (type_var.as_str() == Some("future")) {
name = Value::Str("rpiDepth".into());
watchOrderBookRate = Value::Str("500".into());
}
let mut subParams: Value = Value::from(vec![]);
let mut messageHashes: Value = Value::from(vec![]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_57: bool = true;
while { if !__for_first_57 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_57 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut symbol: Value = symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut market: Value = self.market(symbol.clone());
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str("orderbook::".into()), symbol).into()));
let mut subscriptionHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", market.as_map().and_then(|__m| __m.get("lowercaseId")).cloned().unwrap_or(Value::Null), Value::Str("@".into())).into()), name).into());
if (watchOrderBookRate == Value::Null) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", self.id.clone(), Value::Str(" watchOrderBookForSymbols() watchOrderBookRate is required".into()))));
}
let mut symbolHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", subscriptionHash, Value::Str("@".into())).into()), to_string_val(&watchOrderBookRate)).into()), Value::Str("ms".into())).into());
append_to_array(&mut subParams, symbolHash);
}
}
let mut messageHashesLength: Value = Value::Int(messageHashes.len() as i64);
let mut url: Value = Value::Str(format!("{}{}", add(&self.get_ws_url(type_var.clone(), self.get_future_ws_category(name.clone())), &Value::Str("/".into())), self.stream(type_var.clone(), streamHash, &[messageHashesLength]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null)).into());
let mut requestId: Value = self.request_id(url.clone());
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("SUBSCRIBE".into()));
m.insert("params".to_string(), subParams);
m.insert("id".to_string(), requestId.clone());
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), to_string_val(&requestId));
m.insert("name".to_string(), name);
m.insert("symbols".to_string(), symbols);
m.insert("method".to_string(), Value::Str("handle_order_book_subscription".into()).clone());
m.insert("limit".to_string(), limit.clone());
m.insert("type".to_string(), type_var);
m.insert("params".to_string(), params.clone());
m
});
let __ws_arg_3 = self.extend(request, &[params]);
let mut orderbook: Value = self.watch_multiple(url, messageHashes.clone(), &[__ws_arg_3, messageHashes.clone(), subscription]).await;
return orderbook.limit();
Value::Null
}
/*
* @method
* @name binance#unWatchOrderBookForSymbols
* @description unWatches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#partial-book-depth-streams
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#diff-depth-stream
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Partial-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Diff-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Partial-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Diff-Book-Depth-Streams
* @param {string[]} symbols unified array of symbols
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/?id=order-book-structure}
*/
pub async fn un_watch_order_book_for_symbols(&mut self, mut symbols: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
symbols = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(false), Value::Bool(true), Value::Bool(true)]);
let mut firstMarket: Value = self.market(symbols.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null));
let mut type_var: Value = firstMarket.as_map().and_then(|__m| __m.get("type")).cloned().unwrap_or(Value::Null);
if (firstMarket.as_map().and_then(|__m| __m.get("option")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
type_var = Value::Str("option".into());
} else if (firstMarket.as_map().and_then(|__m| __m.get("contract")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
type_var = (if (firstMarket.as_map().and_then(|__m| __m.get("linear")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) { Value::Str("future".into()) } else { Value::Str("delivery".into()) });
}
let mut name: Value = Value::Str("depth".into());
let mut streamHash: Value = Value::Str("multipleOrderbook".into());
if (symbols != Value::Null) {
streamHash = Value::Str(format!("{}{}", streamHash, Value::Str(format!("{}{}", Value::Str("::".into()), join(&symbols, &Value::Str(",".into()))).into())).into());
}
let mut watchOrderBookRate: Value = self.safe_string_k(self.options.clone(), "watchOrderBookRate", &[Value::Str("100".into())]);
let mut subParams: Value = Value::from(vec![]);
let mut subMessageHashes: Value = Value::from(vec![]);
let mut messageHashes: Value = Value::from(vec![]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_58: bool = true;
while { if !__for_first_58 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_58 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut symbol: Value = symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut market: Value = self.market(symbol.clone());
append_to_array(&mut subMessageHashes, Value::Str(format!("{}{}", Value::Str("orderbook::".into()), symbol).into()));
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str("unsubscribe:orderbook:".into()), symbol).into()));
let mut streamId: Value = market.as_map().and_then(|__m| __m.get("lowercaseId")).cloned().unwrap_or(Value::Null);
let mut subscriptionHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", streamId, Value::Str("@".into())).into()), name).into());
let mut symbolHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", subscriptionHash, Value::Str("@".into())).into()), watchOrderBookRate).into()), Value::Str("ms".into())).into());
append_to_array(&mut subParams, symbolHash);
}
}
let mut messageHashesLength: Value = Value::Int(subMessageHashes.len() as i64);
let mut url: Value = Value::Str(format!("{}{}", add(&self.get_ws_url(type_var.clone(), self.get_future_ws_category(Value::Str("depth".into()))), &Value::Str("/".into())), self.stream(type_var, streamHash, &[messageHashesLength]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null)).into());
let mut requestId: Value = self.request_id(url.clone());
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("UNSUBSCRIBE".into()));
m.insert("params".to_string(), subParams);
m.insert("id".to_string(), requestId.clone());
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("unsubscribe".to_string(), Value::Bool(true));
m.insert("id".to_string(), to_string_val(&requestId));
m.insert("symbols".to_string(), symbols);
m.insert("subMessageHashes".to_string(), subMessageHashes);
m.insert("messageHashes".to_string(), messageHashes.clone());
m.insert("topic".to_string(), Value::Str("orderbook".into()));
m
});
let __ws_arg_4 = self.extend(request, &[params]);
return self.watch_multiple(url, messageHashes.clone(), &[__ws_arg_4, messageHashes.clone(), subscription]).await;
Value::Null
}
/*
* @method
* @name binance#unWatchOrderBook
* @description unWatches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#partial-book-depth-streams
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#diff-depth-stream
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Partial-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Diff-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Partial-Book-Depth-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Diff-Book-Depth-Streams
* @param {string} symbol unified array of symbols
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/?id=order-book-structure}
*/
pub async fn un_watch_order_book(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
return self.un_watch_order_book_for_symbols(Value::from(vec![symbol]), &[params]).await;
Value::Null
}
/*
* @method
* @name binance#fetchOrderBookWs
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#order-book
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/websocket-api/Order-Book
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/?id=order-book-structure}
*/
pub async fn fetch_order_book_ws(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut limit = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol);
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), market.as_map().and_then(|__m| __m.get("id")).cloned().unwrap_or(Value::Null));
m
});
if (limit != Value::Null) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("limit".into(), limit); }
}
let mut marketType: Value = self.get_market_type(Value::Str("fetchOrderBookWs".into()), market.clone(), &[params.clone()]);
if (marketType.as_str() != Some("future")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchOrderBookWs only supports swap markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &marketType);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchOrderBookWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("returnRateLimits".into(), returnRateLimits); }
params = self.omit(params.clone(), Value::Str("test".into()), &[]);
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), Value::Str("depth".into()));
let __ws_arg_5 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_5]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_fetch_order_book".into()).clone());
m
});
let mut orderbook: Value = self.watch(url, messageHash.clone(), &[message, messageHash.clone(), subscription]).await;
add_element_to_object(&mut orderbook, &Value::Str("symbol".into()), market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null));
return orderbook;
Value::Null
}
pub fn handle_fetch_order_book(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "id":"51e2affb-0aba-4821-ba75-f2625006eb43",
// "status":200,
// "result":{
// "lastUpdateId":1027024,
// "E":1589436922972,
// "T":1589436922959,
// "bids":[
// [
// "4.00000000",
// "431.00000000"
// ]
// ],
// "asks":[
// [
// "4.00000200",
// "12.00000000"
// ]
// ]
// }
// }
//
let mut messageHash: Value = (match message.get("id") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Null });
let mut timestamp: Value = self.safe_integer_k(result.clone(), "T", &[]);
let mut orderbook: Value = self.parse_order_book(result.clone(), Value::Null, &[timestamp]);
add_element_to_object(&mut orderbook, &Value::Str("nonce".into()), self.safe_integer2(result, Value::Str("lastUpdateId".into()), Value::Str("u".into()), &[]));
client.resolve(&[orderbook, messageHash]);
}
pub async fn fetch_order_book_snapshot(&mut self, mut client: Value, mut message: Value, mut subscription: Value) -> Value {
let mut symbol: Value = self.safe_string_k(subscription.clone(), "symbol", &[]);
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str("orderbook::".into()), symbol).into());
{
let mut defaultLimit: Value = self.safe_integer_k(self.options.clone(), "watchOrderBookLimit", &[Value::Int(1000)]);
let mut type_var: Option<String> = self.safe_string_k(subscription.clone(), "type", &[]).as_str().map(str::to_owned);
let mut limit: Value = self.safe_integer_k(subscription.clone(), "limit", &[defaultLimit]);
let mut params: Value = self.safe_dict_k(subscription, "params", &[]);
// 3. Get a depth snapshot from https://www.binance.com/api/v1/depth?symbol=BNBBTC&limit=1000 .
// todo: this is a synch blocking call - make it async
// default 100, max 1000, valid limits 5, 10, 20, 50, 100, 500, 1000
let mut snapshot: Value = self.fetch_rest_order_book_safe(symbol.clone(), &[limit, params]).await;
if (self.safe_value(self.orderbooks.clone(), symbol.clone(), &[]) == Value::Null) {
return Value::Null;
}
let mut orderbook: Value = self.safe_value(self.orderbooks.clone(), symbol.clone(), &[]);
orderbook.reset(snapshot);
// unroll the accumulated deltas
let mut messages: Value = get_value(&orderbook, &Value::Str("cache".into()));
crate::set_value(&mut orderbook, &Value::Str("cache".into()), Value::from(vec![]));
{
let mut i: Value = Value::Int(0);
let mut __for_first_59: bool = true;
while { if !__for_first_59 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_59 = false; i.as_f64().unwrap_or(f64::NAN) < get_array_length(&messages).as_f64().unwrap_or(f64::NAN) } {
let mut messageItem: Value = get_value(&messages, &i);
let mut messageItem: Value = get_value(&messages, &i);
let mut U: Value = self.safe_integer_k(messageItem.clone(), "U", &[]);
let mut u: Value = self.safe_integer_k(messageItem.clone(), "u", &[]);
if (U == Value::Null) || (u == Value::Null) {
continue;
}
let mut pu: Value = self.safe_integer_k(messageItem.clone(), "pu", &[]);
if (type_var.as_deref() == Some("future")) {
// 4. Drop any event where u is < lastUpdateId in the snapshot
if u.as_f64().unwrap_or(f64::NAN) < crate::value::get_value_k(&orderbook, "nonce").as_f64().unwrap_or(f64::NAN) {
continue;
}
// 5. The first processed event should have U <= lastUpdateId AND u >= lastUpdateId
if (is_less_than_or_equal(&U, &crate::value::get_value_k(&orderbook, "nonce"))) && (is_greater_than_or_equal(&u, &crate::value::get_value_k(&orderbook, "nonce"))) || (is_equal(&pu, &crate::value::get_value_k(&orderbook, "nonce"))) {
self.handle_order_book_message(client.clone(), messageItem.clone(), orderbook.clone());
}
} else {
// 4. Drop any event where u is <= lastUpdateId in the snapshot
if is_less_than_or_equal(&u, &crate::value::get_value_k(&orderbook, "nonce")) {
continue;
}
// 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1
if (is_less_than_or_equal(&((match (&(U), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x - y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x - *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x - y), _ => Value::Null })), &crate::value::get_value_k(&orderbook, "nonce"))) && (is_greater_than_or_equal(&((match (&(u), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x - y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x - *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x - y), _ => Value::Null })), &crate::value::get_value_k(&orderbook, "nonce"))) {
self.handle_order_book_message(client.clone(), messageItem, orderbook.clone());
}
}
}
}
if (symbol != Value::Null) {
if let Value::Dict(__d) = &mut self.orderbooks { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), orderbook.clone()); }
}
client.resolve(&[orderbook, messageHash]);
}
Value::Null
}
pub fn handle_delta(&self, mut bookside: Value, mut delta: Value) {
let mut price: Value = self.safe_float(delta.clone(), Value::Int(0), &[]);
let mut amount: Value = self.safe_float(delta, Value::Int(1), &[]);
bookside.store(price, amount);
}
pub fn handle_deltas(&self, mut bookside: Value, mut deltas: Value) {
{
let mut i: Value = Value::Int(0);
let mut __for_first_60: bool = true;
while { if !__for_first_60 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_60 = false; i.as_f64().unwrap_or(f64::NAN) < get_array_length(&deltas).as_f64().unwrap_or(f64::NAN) } {
self.handle_delta(bookside.clone(), get_value(&deltas, &i));
}
}
}
pub fn handle_order_book_message(&self, mut client: Value, mut message: Value, mut orderbook: Value) -> Value {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
let mut u: Value = (match message.get("u") { Some(Value::Int(__n)) => Value::Int(*__n), Some(Value::Float(__f)) => Value::Int(*__f as i64), Some(Value::Str(__s)) => match __s.parse::<i64>() { Ok(__n) => Value::Int(__n), Err(_) => match __s.parse::<f64>() { Ok(__f) if __f.is_finite() => Value::Int(__f as i64), _ => Value::Null } }, _ => Value::Null });
self.handle_deltas(crate::value::get_value_k(&orderbook, "asks"), (match message.get("a") { Some(__v) if matches!(__v, Value::Arr(_)) => __v.clone(), _ => Value::from(vec![]) }));
self.handle_deltas(crate::value::get_value_k(&orderbook, "bids"), (match message.get("b") { Some(__v) if matches!(__v, Value::Arr(_)) => __v.clone(), _ => Value::from(vec![]) }));
add_element_to_object(&mut orderbook, &Value::Str("nonce".into()), u);
let mut timestamp: Value = (match message.get("E") { Some(Value::Int(__n)) => Value::Int(*__n), Some(Value::Float(__f)) => Value::Int(*__f as i64), Some(Value::Str(__s)) => match __s.parse::<i64>() { Ok(__n) => Value::Int(__n), Err(_) => match __s.parse::<f64>() { Ok(__f) if __f.is_finite() => Value::Int(__f as i64), _ => Value::Null } }, _ => Value::Null });
add_element_to_object(&mut orderbook, &Value::Str("timestamp".into()), timestamp.clone());
add_element_to_object(&mut orderbook, &Value::Str("datetime".into()), self.iso8601(timestamp));
return orderbook;
Value::Null
}
pub fn handle_order_book(&mut self, mut client: Value, mut message: Value) {
//
// initial snapshot is fetched with ccxt's fetchOrderBook
// the feed does not include a snapshot, just the deltas
//
// {
// "e": "depthUpdate", // Event type
// "E": 1577554482280, // Event time
// "s": "BNBBTC", // Symbol
// "U": 157, // First update ID in event
// "u": 160, // Final update ID in event
// "b": [ // bids
// [ "0.0024", "10" ], // price, size
// ],
// "a": [ // asks
// [ "0.0026", "100" ], // price, size
// ]
// }
//
let mut marketId: Value = self.safe_string_k(message.clone(), "s", &[]);
// the client url is the authoritative source for the market type — an
// ambiguous id like BTCUSDT maps to both the spot and the linear swap
// market, and picking the first match drops the message under the wrong
// symbol and stalls the orderbook future (delivery/option ids are
// unique, so the swap hint resolves those correctly too)
let mut isSpot: Value = self.is_spot_url(client.clone());
let mut marketType: Value = (if is_true(&isSpot) { Value::Str("spot".into()) } else { Value::Str("swap".into()) });
let mut market: Value = self.safe_market(&[marketId, Value::Null, Value::Null, marketType]);
let mut symbol: Value = market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str("orderbook::".into()), symbol).into());
if !(in_op(&self.orderbooks, &symbol)) {
return;
}
let mut orderbook: Value = get_value(&self.orderbooks, &symbol);
let mut nonce: Value = self.safe_integer_k(orderbook.clone(), "nonce", &[]);
if (nonce == Value::Null) {
// 2. Buffer the events you receive from the stream.
crate::runtime::append_to_object_array(&mut orderbook, &Value::Str("cache".into()), message.clone());
} else {
let _try_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut U: Value = self.safe_integer_k(message.clone(), "U", &[]);
if (U == Value::Null) {
return true;
}
let mut u: Value = self.safe_integer_k(message.clone(), "u", &[]);
if (u == Value::Null) {
return true;
}
let mut pu: Value = self.safe_integer_k(message.clone(), "pu", &[]);
if (pu == Value::Null) {
// spot
// 4. Drop any event where u is <= lastUpdateId in the snapshot
if u.as_f64().unwrap_or(f64::NAN) > nonce.as_f64().unwrap_or(f64::NAN) {
let mut timestamp: Option<i64> = self.safe_integer_k(orderbook.clone(), "timestamp", &[]).as_i64();
let mut conditional: Value = Value::Null;
if (timestamp.is_none()) {
// 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1
conditional = Value::Bool((((match (&(U), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x - y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x - *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x - y), _ => Value::Null })).as_f64().unwrap_or(f64::NAN) <= nonce.as_f64().unwrap_or(f64::NAN)) && (((match (&(u), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x - y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x - *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x - y), _ => Value::Null })).as_f64().unwrap_or(f64::NAN) >= nonce.as_f64().unwrap_or(f64::NAN)));
} else {
// 6. While listening to the stream, each new event's U should be equal to the previous event's u+1.
conditional = (Value::Bool(((match (&(U), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x - y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x - *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x - y), _ => Value::Null })).as_f64() == nonce.as_f64()));
}
if conditional.as_bool() == Some(true) {
self.handle_order_book_message(client.clone(), message.clone(), orderbook.clone());
if nonce.as_f64().unwrap_or(f64::NAN) < self.safe_integer_k(orderbook.clone(), "nonce", &[Value::Int(0)]).as_f64().unwrap_or(f64::NAN) {
client.resolve(&[orderbook.clone(), messageHash.clone()]);
}
} else {
let mut checksum: Value = self.handle_option(Value::Str("watchOrderBook".into()), Value::Str("checksum".into()), &[Value::Bool(true)]);
if is_equal(&checksum, &Value::Bool(true)) {
panic!("{}", crate::exchange_errors::checksum_error(format!("{}{}", Value::Str(format!("{}{}", self.id.clone(), Value::Str(" ".into())).into()), self.orderbook_checksum_message(symbol.clone()))));
}
}
}
} else {
// future
// 4. Drop any event where u is < lastUpdateId in the snapshot
if u.as_f64().unwrap_or(f64::NAN) >= nonce.as_f64().unwrap_or(f64::NAN) {
// 5. The first processed event should have U <= lastUpdateId AND u >= lastUpdateId
// 6. While listening to the stream, each new event's pu should be equal to the previous event's u, otherwise initialize the process from step 3
if (U.as_f64().unwrap_or(f64::NAN) <= nonce.as_f64().unwrap_or(f64::NAN)) || (pu.as_f64() == nonce.as_f64()) {
self.handle_order_book_message(client.clone(), message.clone(), orderbook.clone());
if nonce.as_f64().unwrap_or(f64::NAN) <= self.safe_integer_k(orderbook.clone(), "nonce", &[Value::Int(0)]).as_f64().unwrap_or(f64::NAN) {
client.resolve(&[orderbook.clone(), messageHash.clone()]);
}
} else {
let mut checksum: Value = self.handle_option(Value::Str("watchOrderBook".into()), Value::Str("checksum".into()), &[Value::Bool(true)]);
if is_equal(&checksum, &Value::Bool(true)) {
panic!("{}", crate::exchange_errors::checksum_error(format!("{}{}", Value::Str(format!("{}{}", self.id.clone(), Value::Str(" ".into())).into()), self.orderbook_checksum_message(symbol.clone()))));
}
}
}
}
#[allow(unreachable_code)] { false }}));
match _try_result { Ok(__try_ret) => { if __try_ret { return; } } Err(_try_err) => { let e: Value = panic_to_value(_try_err);
if (in_op(&self.orderbooks, &symbol)) {
remove(&mut self.orderbooks, &symbol);
}
if (in_op(&get_value(&client, &Value::Str("subscriptions".into())), &messageHash)) {
remove(&mut get_value(&client, &Value::Str("subscriptions".into())), &messageHash);
}
client.reject(&[e, messageHash.clone()]);
} }
}
}
pub fn handle_order_book_subscription(&mut self, mut client: Value, mut message: Value, mut subscription: Value) {
let mut defaultLimit: Value = self.safe_integer_k(self.options.clone(), "watchOrderBookLimit", &[Value::Int(1000)]);
// const messageHash = this.safeString (subscription, 'messageHash');
let mut symbolOfSubscription: Value = self.safe_string_k(subscription.clone(), "symbol", &[]); // watchOrderBook
let mut symbols: Value = self.safe_list_k(subscription.clone(), "symbols", &[Value::from(vec![symbolOfSubscription])]); // watchOrderBookForSymbols
let mut limit: Value = self.safe_integer_k(subscription.clone(), "limit", &[defaultLimit]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_61: bool = true;
while { if !__for_first_61 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_61 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut symbol: Value = symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
if (in_op(&self.orderbooks, &symbol)) {
remove(&mut self.orderbooks, &symbol);
}
{ let __be_tmp = self.order_book(&[Value::Map({
let mut m = indexmap::IndexMap::new();
m
}), limit.clone()]); if let Value::Dict(__d) = &mut self.orderbooks { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), __be_tmp); } }
subscription = self.extend(subscription.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), symbol.clone());
m
})]);
// fetch the snapshot in a separate async call
self.spawn(&[Value::Str("fetch_order_book_snapshot".into()).clone(), client.clone(), message.clone(), subscription.clone()]);
}
}
}
pub fn handle_subscription_status(&mut self, mut client: Value, mut message: Value) -> Value {
//
// {
// "result": null,
// "id": 1574649734450
// }
//
let mut id: Value = self.safe_string_k(message.clone(), "id", &[]);
let mut subscriptionsById: Value = self.index_by(get_value(&client, &Value::Str("subscriptions".into())), Value::Str("id".into()));
let mut subscription: Value = self.safe_dict(subscriptionsById, id.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut method: Value = self.safe_value_k(subscription.clone(), "method", &[]);
if (method != Value::Null) {
self.dispatch_ws_handler(&method, &[client.clone(), message.clone(), subscription.clone()]);
}
let mut isUnSubMessage: Value = self.safe_bool_k(subscription.clone(), "unsubscribe", &[Value::Bool(false)]);
if (isUnSubMessage.as_bool() == Some(true)) {
self.handle_un_subscription(client.clone(), subscription);
}
return message;
Value::Null
}
pub fn handle_un_subscription(&mut self, mut client: Value, mut subscription: Value) {
let __pro_message_arc: std::sync::Arc<indexmap::IndexMap<String, Value>> = (match &subscription { Value::Dict(__d) => __d.clone(), _ => std::sync::Arc::new(indexmap::IndexMap::new()) });
let __pro_message: &indexmap::IndexMap<String, Value> = &__pro_message_arc;
let mut messageHashes: Value = (match __pro_message.get("messageHashes").cloned() { Some(__v) if matches!(__v, Value::Arr(_)) => __v, _ => Value::from(vec![]) });
let mut subMessageHashes: Value = (match __pro_message.get("subMessageHashes").cloned() { Some(__v) if matches!(__v, Value::Arr(_)) => __v, _ => Value::from(vec![]) });
{
let mut j: Value = Value::Int(0);
let mut __for_first_62: bool = true;
while { if !__for_first_62 { j = (match (&(j), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_62 = false; j.as_f64().unwrap_or(f64::NAN) < ((messageHashes.len() as i64) as f64) } {
let mut unsubHash: Value = messageHashes.as_array().and_then(|__arr| match &j { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut subHash: Value = subMessageHashes.as_array().and_then(|__arr| match &j { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
self.clean_unsubscription(client.clone(), subHash, unsubHash, &[]);
}
}
self.clean_cache(subscription);
}
/*
* @method
* @name binance#watchTradesForSymbols
* @description get the list of most recent trades for a list of symbols
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#aggregate-trades
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#recent-trades
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Aggregate-Trade-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Aggregate-Trade-Streams
* @param {string[]} symbols unified symbol of the market to fetch trades for
* @param {int} [since] timestamp in ms of the earliest trade to fetch
* @param {int} [limit] the maximum amount of trades to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.name] the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades}
*/
pub async fn watch_trades_for_symbols(&mut self, mut symbols: Value, optional_args: &[Value]) -> Value {
let mut since = get_arg(optional_args, 0, Value::Null);
let mut limit = get_arg(optional_args, 1, Value::Null);
let mut params = get_arg(optional_args, 2, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
symbols = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(false), Value::Bool(true), Value::Bool(true)]);
let mut streamHash: Value = Value::Str("multipleTrades".into());
if (symbols != Value::Null) {
let mut symbolsLength: f64 = ((symbols.len() as i64) as f64);
if symbolsLength > ((200i64) as f64) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" watchTradesForSymbols() accepts 200 symbols at most. To watch more symbols call watchTradesForSymbols() multiple times".into()))));
}
streamHash = Value::Str(format!("{}{}", streamHash, Value::Str(format!("{}{}", Value::Str("::".into()), join(&symbols, &Value::Str(",".into()))).into())).into());
}
let mut name: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchTradesForSymbols".into()), Value::Str("name".into()), &[Value::Str("trade".into())]); name = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
params = self.omit(params.clone(), Value::Str("callerMethodName".into()), &[]);
let mut firstMarket: Value = self.market(symbols.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null));
let mut type_var: Value = firstMarket.as_map().and_then(|__m| __m.get("type")).cloned().unwrap_or(Value::Null);
let mut isOption: Value = firstMarket.as_map().and_then(|__m| __m.get("option")).cloned().unwrap_or(Value::Null);
if (isOption.as_bool() == Some(true)) {
type_var = Value::Str("option".into());
} else if (firstMarket.as_map().and_then(|__m| __m.get("contract")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
type_var = (if (firstMarket.as_map().and_then(|__m| __m.get("linear")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) { Value::Str("future".into()) } else { Value::Str("delivery".into()) });
}
let mut messageHashes: Value = Value::from(vec![]);
let mut subParams: Value = Value::from(vec![]);
if (isOption.as_bool() == Some(true)) {
// eOptions: always subscribe per-underlying (<underlying>@optionTrade)
// handleTrade filters to the correct symbol via the 's' field
let mut seenUnderlyings: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
{
let mut i: Value = Value::Int(0);
let mut __for_first_63: bool = true;
while { if !__for_first_63 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_63 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut symbol: Value = symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut market: Value = self.market(symbol.clone());
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str("trade::".into()), symbol).into()));
let mut baseIdLower: Value = self.safe_string_lower_k(market.clone(), "baseId", &[Value::Str("".into())]);
let mut quoteIdLower: Value = self.safe_string_lower_k(market.clone(), "quoteId", &[Value::Str("".into())]);
let mut underlying: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", baseIdLower, Value::Str("".into())).into()), quoteIdLower).into());
if !(in_op(&seenUnderlyings, &underlying)) {
if let Value::Dict(__d) = &mut seenUnderlyings { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&underlying), Value::Bool(true)); }
append_to_array(&mut subParams, Value::Str(format!("{}{}", underlying, Value::Str("@optionTrade".into())).into()));
}
}
}
} else {
{
let mut i: Value = Value::Int(0);
let mut __for_first_64: bool = true;
while { if !__for_first_64 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_64 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut symbol: Value = symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut market: Value = self.market(symbol.clone());
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str("trade::".into()), symbol).into()));
let mut rawHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", market.as_map().and_then(|__m| __m.get("lowercaseId")).cloned().unwrap_or(Value::Null), Value::Str("@".into())).into()), name).into());
append_to_array(&mut subParams, rawHash);
}
}
}
let mut query: Value = self.omit(params, Value::Str("type".into()), &[]);
let mut subParamsLength: Value = Value::Int(subParams.len() as i64);
let mut url: Value = Value::Str(format!("{}{}", add(&self.get_ws_url(type_var.clone(), self.get_future_ws_category(name)), &Value::Str("/".into())), self.stream(type_var, streamHash, &[subParamsLength]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null)).into());
let mut requestId: Value = self.request_id(url.clone());
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("SUBSCRIBE".into()));
m.insert("params".to_string(), subParams);
m.insert("id".to_string(), requestId.clone());
m
});
let mut subscribe: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), requestId);
m
});
let __ws_arg_6 = self.extend(request, &[query]);
let mut trades: Value = self.watch_multiple(url, messageHashes.clone(), &[__ws_arg_6, messageHashes.clone(), subscribe]).await;
if is_true(&self.newUpdates) {
let mut first: Value = self.safe_dict(trades.clone(), Value::Int(0), &[]);
let mut tradeSymbol: Value = self.safe_string_k(first, "symbol", &[]);
limit = trades.get_limit(tradeSymbol, limit.clone());
}
return self.filter_by_since_limit(trades, &[since, limit, Value::Str("timestamp".into()), Value::Bool(true)]);
Value::Null
}
/*
* @method
* @name binance#unWatchTradesForSymbols
* @description unsubscribes from the trades channel
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#aggregate-trades
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#recent-trades
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Aggregate-Trade-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Aggregate-Trade-Streams
* @param {string[]} symbols unified symbol of the market to fetch trades for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.name] the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades}
*/
pub async fn un_watch_trades_for_symbols(&mut self, mut symbols: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
symbols = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(false), Value::Bool(true), Value::Bool(true)]);
let mut streamHash: Value = Value::Str("multipleTrades".into());
if (symbols != Value::Null) {
let mut symbolsLength: f64 = ((symbols.len() as i64) as f64);
if symbolsLength > ((200i64) as f64) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" watchTradesForSymbols() accepts 200 symbols at most. To watch more symbols call watchTradesForSymbols() multiple times".into()))));
}
streamHash = Value::Str(format!("{}{}", streamHash, Value::Str(format!("{}{}", Value::Str("::".into()), join(&symbols, &Value::Str(",".into()))).into())).into());
}
let mut name: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchTradesForSymbols".into()), Value::Str("name".into()), &[Value::Str("trade".into())]); name = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
params = self.omit(params.clone(), Value::Str("callerMethodName".into()), &[]);
let mut firstMarket: Value = self.market(symbols.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null));
let mut type_var: Value = firstMarket.as_map().and_then(|__m| __m.get("type")).cloned().unwrap_or(Value::Null);
let mut isOption: Value = firstMarket.as_map().and_then(|__m| __m.get("option")).cloned().unwrap_or(Value::Null);
if (isOption.as_bool() == Some(true)) {
type_var = Value::Str("option".into());
} else if (firstMarket.as_map().and_then(|__m| __m.get("contract")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
type_var = (if (firstMarket.as_map().and_then(|__m| __m.get("linear")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) { Value::Str("future".into()) } else { Value::Str("delivery".into()) });
}
let mut subMessageHashes: Value = Value::from(vec![]);
let mut subParams: Value = Value::from(vec![]);
let mut messageHashes: Value = Value::from(vec![]);
if (isOption.as_bool() == Some(true)) {
// eOptions: always subscribe per-underlying (<underlying>@optionTrade)
// handleTrade filters to the correct symbol via the 's' field
let mut seenUnderlyings: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
{
let mut i: Value = Value::Int(0);
let mut __for_first_65: bool = true;
while { if !__for_first_65 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_65 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut symbol: Value = symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut market: Value = self.market(symbol.clone());
append_to_array(&mut subMessageHashes, Value::Str(format!("{}{}", Value::Str("trade::".into()), symbol).into()));
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str("unsubscribe:trade:".into()), symbol).into()));
let mut baseIdLower: Value = self.safe_string_lower_k(market.clone(), "baseId", &[Value::Str("".into())]);
let mut quoteIdLower: Value = self.safe_string_lower_k(market.clone(), "quoteId", &[Value::Str("".into())]);
let mut underlying: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", baseIdLower, Value::Str("".into())).into()), quoteIdLower).into());
if !(in_op(&seenUnderlyings, &underlying)) {
if let Value::Dict(__d) = &mut seenUnderlyings { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&underlying), Value::Bool(true)); }
append_to_array(&mut subParams, Value::Str(format!("{}{}", underlying, Value::Str("@optionTrade".into())).into()));
}
}
}
} else {
{
let mut i: Value = Value::Int(0);
let mut __for_first_66: bool = true;
while { if !__for_first_66 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_66 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut symbol: Value = symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut market: Value = self.market(symbol.clone());
append_to_array(&mut subMessageHashes, Value::Str(format!("{}{}", Value::Str("trade::".into()), symbol).into()));
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str("unsubscribe:trade:".into()), symbol).into()));
let mut rawHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", market.as_map().and_then(|__m| __m.get("lowercaseId")).cloned().unwrap_or(Value::Null), Value::Str("@".into())).into()), name).into());
append_to_array(&mut subParams, rawHash);
}
}
}
let mut query: Value = self.omit(params, Value::Str("type".into()), &[]);
let mut subParamsLength: Value = Value::Int(subParams.len() as i64);
let mut url: Value = Value::Str(format!("{}{}", add(&self.get_ws_url(type_var.clone(), self.get_future_ws_category(name)), &Value::Str("/".into())), self.stream(type_var, streamHash, &[subParamsLength]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null)).into());
let mut requestId: Value = self.request_id(url.clone());
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("UNSUBSCRIBE".into()));
m.insert("params".to_string(), subParams);
m.insert("id".to_string(), requestId.clone());
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("unsubscribe".to_string(), Value::Bool(true));
m.insert("id".to_string(), to_string_val(&requestId));
m.insert("subMessageHashes".to_string(), subMessageHashes);
m.insert("messageHashes".to_string(), messageHashes.clone());
m.insert("symbols".to_string(), symbols);
m.insert("topic".to_string(), Value::Str("trades".into()));
m
});
let __ws_arg_7 = self.extend(request, &[query]);
return self.watch_multiple(url, messageHashes.clone(), &[__ws_arg_7, messageHashes.clone(), subscription]).await;
Value::Null
}
/*
* @method
* @name binance#unWatchTrades
* @description unsubscribes from the trades channel
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#aggregate-trades
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#recent-trades
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Aggregate-Trade-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Aggregate-Trade-Streams
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.name] the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades}
*/
pub async fn un_watch_trades(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
return self.un_watch_trades_for_symbols(Value::from(vec![symbol.clone()]), &[params]).await;
Value::Null
}
/*
* @method
* @name binance#watchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#aggregate-trades
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#recent-trades
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Aggregate-Trade-Streams
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Aggregate-Trade-Streams
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {int} [since] timestamp in ms of the earliest trade to fetch
* @param {int} [limit] the maximum amount of trades to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.name] the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades}
*/
pub async fn watch_trades(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut since = get_arg(optional_args, 0, Value::Null);
let mut limit = get_arg(optional_args, 1, Value::Null);
let mut params = get_arg(optional_args, 2, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if let Value::Dict(__d) = &mut params { std::sync::Arc::make_mut(__d).insert("callerMethodName".into(), Value::Str("watchTrades".into())); }
return self.watch_trades_for_symbols(Value::from(vec![symbol.clone()]), &[since, limit, params]).await;
Value::Null
}
pub fn parse_ws_trade(&self, mut trade: Value, optional_args: &[Value]) -> Value {
let mut market = get_arg(optional_args, 0, Value::Null);
//
// public watchTrades
//
// {
// "e": "trade", // event type
// "E": 1579481530912, // event time
// "s": "ETHBTC", // symbol
// "t": 158410082, // trade id
// "p": "0.01914100", // price
// "q": "0.00700000", // quantity
// "b": 586187049, // buyer order id
// "a": 586186710, // seller order id
// "T": 1579481530910, // trade time
// "m": false, // is the buyer the market maker
// "M": true // binance docs say it should be ignored
// }
//
// {
// "e": "aggTrade", // Event type
// "E": 123456789, // Event time
// "s": "BNBBTC", // Symbol
// "a": 12345, // Aggregate trade ID
// "p": "0.001", // Price
// "q": "100", // Quantity
// "f": 100, // First trade ID
// "l": 105, // Last trade ID
// "T": 123456785, // Trade time
// "m": true, // Is the buyer the market maker?
// "M": true // Ignore
// }
//
// private watchMyTrades spot
//
// {
// "e": "executionReport",
// "E": 1611063861489,
// "s": "BNBUSDT",
// "c": "m4M6AD5MF3b1ERe65l4SPq",
// "S": "BUY",
// "o": "MARKET",
// "f": "GTC",
// "q": "2.00000000",
// "p": "0.00000000",
// "P": "0.00000000",
// "F": "0.00000000",
// "g": -1,
// "C": '',
// "x": "TRADE",
// "X": "PARTIALLY_FILLED",
// "r": "NONE",
// "i": 1296882607,
// "l": "0.33200000",
// "z": "0.33200000",
// "L": "46.86600000",
// "n": "0.00033200",
// "N": "BNB",
// "T": 1611063861488,
// "t": 109747654,
// "I": 2696953381,
// "w": false,
// "m": false,
// "M": true,
// "O": 1611063861488,
// "Z": "15.55951200",
// "Y": "15.55951200",
// "Q": "0.00000000"
// }
//
// private watchMyTrades future/delivery
//
// {
// "s": "BTCUSDT",
// "c": "pb2jD6ZQHpfzSdUac8VqMK",
// "S": "SELL",
// "o": "MARKET",
// "f": "GTC",
// "q": "0.001",
// "p": "0",
// "ap": "33468.46000",
// "sp": "0",
// "x": "TRADE",
// "X": "FILLED",
// "i": 13351197194,
// "l": "0.001",
// "z": "0.001",
// "L": "33468.46",
// "n": "0.00027086",
// "N": "BNB",
// "T": 1612095165362,
// "t": 458032604,
// "b": "0",
// "a": "0",
// "m": false,
// "R": false,
// "wt": "CONTRACT_PRICE",
// "ot": "MARKET",
// "ps": "BOTH",
// "cp": false,
// "rp": "0.00335000",
// "pP": false,
// "si": 0,
// "ss": 0
// }
//
let mut executionType: Option<String> = self.safe_string_k(trade.clone(), "x", &[]).as_str().map(str::to_owned);
let mut isTradeExecution: bool = executionType.as_deref() == Some("TRADE");
if !isTradeExecution {
return self.parse_trade(trade.clone(), &[market.clone()]);
}
let mut id: Value = self.safe_string2(trade.clone(), Value::Str("t".into()), Value::Str("a".into()), &[]);
let mut timestamp: Value = self.safe_integer_k(trade.clone(), "T", &[]);
let mut price: Value = self.safe_string2(trade.clone(), Value::Str("L".into()), Value::Str("p".into()), &[]);
let mut amount: Value = self.safe_string_k(trade.clone(), "q", &[]);
if isTradeExecution {
amount = self.safe_string_k(trade.clone(), "l", &[amount.clone()]);
}
let mut cost: Value = self.safe_string_k(trade.clone(), "Y", &[]);
if (cost == Value::Null) {
if (price != Value::Null) && (amount != Value::Null) {
cost = crate::precise::Precise::stringMul(&price, &amount);
}
}
let mut marketId: Value = self.safe_string_k(trade.clone(), "s", &[]);
let mut fallbackType: Value = (if (matches!(&trade, Value::Dict(__d) if __d.contains_key("ps"))) { Value::Str("contract".into()) } else { Value::Str("spot".into()) });
let mut marketType: Value = (if (market != Value::Null) { market.as_map().and_then(|__m| __m.get("type")).cloned().unwrap_or(Value::Null) } else { fallbackType });
let mut symbol: Value = self.safe_symbol(marketId, &[market, Value::Null, marketType]);
let mut side: Value = self.safe_string_lower_k(trade.clone(), "S", &[]);
let mut takerOrMaker: Value = Value::Null;
let mut orderId: Value = self.safe_string_k(trade.clone(), "i", &[]);
if (matches!(&trade, Value::Dict(__d) if __d.contains_key("m"))) {
if (side == Value::Null) {
side = (if (is_equal(&trade.as_map().and_then(|__m| __m.get("m")).cloned().unwrap_or(Value::Null), &Value::Bool(true))) { Value::Str("sell".into()) } else { Value::Str("buy".into()) }); // this is reversed intentionally
}
takerOrMaker = (if (is_equal(&trade.as_map().and_then(|__m| __m.get("m")).cloned().unwrap_or(Value::Null), &Value::Bool(true))) { Value::Str("maker".into()) } else { Value::Str("taker".into()) });
}
let mut fee: Value = Value::Null;
let mut feeCost: Value = self.safe_string_k(trade.clone(), "n", &[]);
if (feeCost != Value::Null) {
let mut feeCurrencyId: Value = self.safe_string_k(trade.clone(), "N", &[]);
let mut feeCurrencyCode: Value = self.safe_currency_code(feeCurrencyId, &[]);
fee = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("cost".to_string(), feeCost);
m.insert("currency".to_string(), feeCurrencyCode);
m
});
}
let mut type_var: Value = self.safe_string_lower_k(trade.clone(), "o", &[]);
return self.safe_trade(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("info".to_string(), trade);
m.insert("timestamp".to_string(), timestamp.clone());
m.insert("datetime".to_string(), self.iso8601(timestamp.clone()));
m.insert("symbol".to_string(), symbol.clone());
m.insert("id".to_string(), id.clone());
m.insert("order".to_string(), orderId);
m.insert("type".to_string(), type_var);
m.insert("takerOrMaker".to_string(), takerOrMaker);
m.insert("side".to_string(), side);
m.insert("price".to_string(), price);
m.insert("amount".to_string(), amount);
m.insert("cost".to_string(), cost);
m.insert("fee".to_string(), fee);
m
}), &[]);
Value::Null
}
pub fn handle_trade(&mut self, mut client: Value, mut message: Value) {
// the trade streams push raw trade information in real-time
// each trade has a unique buyer and seller
let mut marketId: Value = self.safe_string_k(message.clone(), "s", &[]);
// resolve the market from the transport url — an ambiguous id like
// BTCUSDT maps to both the spot and the linear swap market
let mut isSpot: Value = self.is_spot_url(client.clone());
let mut marketType: Value = (if is_true(&isSpot) { Value::Str("spot".into()) } else { Value::Str("contract".into()) });
let mut market: Value = self.safe_market(&[marketId, Value::Null, Value::Null, marketType]);
let mut symbol: Value = market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str("trade::".into()), symbol).into());
let mut trade: Value = self.parse_ws_trade(message.clone(), &[market]);
let mut tradesArray: Value = self.safe_value(self.trades.clone(), symbol.clone(), &[]);
if (tradesArray == Value::Null) {
let mut limit: Value = self.safe_integer_k(self.options.clone(), "tradesLimit", &[Value::Int(1000)]);
tradesArray = ArrayCache::new(limit);
}
tradesArray.append(trade);
if let Value::Dict(__d) = &mut self.trades { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), tradesArray.clone()); }
client.resolve(&[tradesArray, messageHash.clone()]);
}
/*
* @method
* @name binance#watchOHLCV
* @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#klines
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Kline-Candlestick-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Kline-Candlestick-Streams
* @see https://developers.binance.com/en/docs/catalog/advanced-trading-stocks-trading/api/ws-streams/market-streams#kline-stream
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int} [since] timestamp in ms of the earliest candle to fetch
* @param {int} [limit] the maximum amount of candles to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.stock] set to true to use stocks market streams
* @param {object} [params.timezone] if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
pub async fn watch_ohlcv(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut timeframe = get_arg(optional_args, 0, Value::Str("1m".into()));
let mut since = get_arg(optional_args, 1, Value::Null);
let mut limit = get_arg(optional_args, 2, Value::Null);
let mut params = get_arg(optional_args, 3, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol.clone());
symbol = market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
let mut stock: Value = self.safe_bool_k(market, "stock", &[Value::Bool(false)]);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchOHLCV".into()), Value::Str("stock".into()), &[]); stock = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if (stock.as_bool() == Some(true)) {
if (timeframe.as_str() != Some("5m")) && (timeframe.as_str() != Some("1h")) && (timeframe.as_str() != Some("1d")) && (timeframe.as_str() != Some("1w")) && (timeframe.as_str() != Some("1M")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" watchOHLCV only supports 5m, 1h, 1d, 1w, and 1M timeframes".into()))));
}
if let Value::Dict(__d) = &mut params { std::sync::Arc::make_mut(__d).insert("stock".into(), Value::Bool(true)); }
}
if let Value::Dict(__d) = &mut params { std::sync::Arc::make_mut(__d).insert("callerMethodName".into(), Value::Str("watchOHLCV".into())); }
let mut result: Value = self.watch_ohlcv_for_symbols(Value::from(vec![Value::from(vec![symbol.clone(), timeframe.clone()])]), &[since, limit, params]).await;
return get_value(&get_value(&result, &symbol), &timeframe);
Value::Null
}
/*
* @method
* @name binance#watchOHLCVForSymbols
* @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#klines
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Kline-Candlestick-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Kline-Candlestick-Streams
* @see https://developers.binance.com/en/docs/catalog/advanced-trading-stocks-trading/api/ws-streams/market-streams#kline-stream
* @param {string[][]} symbolsAndTimeframes array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
* @param {int} [since] timestamp in ms of the earliest candle to fetch
* @param {int} [limit] the maximum amount of candles to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.stock] set to true to use stocks market streams
* @param {object} [params.timezone] if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
pub async fn watch_ohlcv_for_symbols(&mut self, mut symbolsAndTimeframes: Value, optional_args: &[Value]) -> Value {
let mut since = get_arg(optional_args, 0, Value::Null);
let mut limit = get_arg(optional_args, 1, Value::Null);
let mut params = get_arg(optional_args, 2, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut stock: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchOHLCVForSymbols".into()), Value::Str("stock".into()), &[Value::Bool(false)]); stock = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if is_true(&stock) {
let mut stockStreams: Value = Value::from(vec![]);
let mut stockMessageHashes: Value = Value::from(vec![]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_67: bool = true;
while { if !__for_first_67 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_67 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbolsAndTimeframes.len() as i64) as f64) } {
let mut stockSymAndTf: Value = symbolsAndTimeframes.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut stockSymbolString: Value = self.symbol(stockSymAndTf.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null));
let mut stockMarket: Value = self.market(stockSymbolString);
let mut stockTicker: Value = self.safe_string2(stockMarket.clone(), Value::Str("base".into()), Value::Str("id".into()), &[]);
let mut stockTickerString: Value = (if (stockTicker == Value::Null) { Value::Str("".into()) } else { to_lower(&stockTicker) });
let mut stockTimeframeString: Value = stockSymAndTf.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null);
let mut stockInterval: Value = self.safe_string(self.timeframes.clone(), stockTimeframeString.clone(), &[stockTimeframeString.clone()]);
if (stockInterval.as_str() != Some("5m")) && (stockInterval.as_str() != Some("1h")) && (stockInterval.as_str() != Some("1d")) && (stockInterval.as_str() != Some("1w")) && (stockInterval.as_str() != Some("1M")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" watchOHLCVForSymbols only supports 5m, 1h, 1d, 1w, and 1M timeframes".into()))));
}
append_to_array(&mut stockStreams, Value::Str(format!("{}{}", Value::Str(format!("{}{}", stockTickerString, Value::Str("@kline_".into())).into()), stockInterval).into()));
append_to_array(&mut stockMessageHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str("ohlcv::".into()), stockMarket.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null)).into()), Value::Str("::".into())).into()), stockTimeframeString).into()));
}
}
let mut stockRes: Value = self.watch_stock_market_stream(stockStreams, stockMessageHashes, &[params.clone()]).await;
let mut stockSymbolstockTimeframestockCandlesVariable = stockRes;
let mut stockSymbol: Value = get_value(&stockSymbolstockTimeframestockCandlesVariable, &Value::Int(0));
let mut stockTimeframe: Value = get_value(&stockSymbolstockTimeframestockCandlesVariable, &Value::Int(1));
let mut stockCandles: Value = get_value(&stockSymbolstockTimeframestockCandlesVariable, &Value::Int(2));
if is_true(&self.newUpdates) {
limit = stockCandles.get_limit(stockSymbol.clone(), limit.clone());
}
let mut stockFiltered: Value = self.filter_by_since_limit(stockCandles, &[since.clone(), limit.clone(), Value::Int(0), Value::Bool(true)]);
return self.create_ohlcv_object(stockSymbol, stockTimeframe, stockFiltered);
}
let mut klineType: Value = Value::Null;
{ let __destr_tmp = self.handle_param_string2(params.clone(), Value::Str("channel".into()), Value::Str("name".into()), &[Value::Str("kline".into())]); klineType = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut symbols: Value = self.get_list_from_object_values(symbolsAndTimeframes.clone(), Value::Int(0));
let mut marketSymbols: Value = self.market_symbols(&[symbols, Value::Null, Value::Bool(false), Value::Bool(false), Value::Bool(true)]);
let mut firstMarket: Value = self.market(marketSymbols.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null));
let mut type_var: Value = firstMarket.as_map().and_then(|__m| __m.get("type")).cloned().unwrap_or(Value::Null);
let mut wsUrlType: Value = type_var.clone();
if (firstMarket.as_map().and_then(|__m| __m.get("option")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
type_var = Value::Str("option".into());
wsUrlType = Value::Str("optionMarket".into()); // eOptions klines are served from /market/ws
} else if (firstMarket.as_map().and_then(|__m| __m.get("contract")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
type_var = (if (firstMarket.as_map().and_then(|__m| __m.get("linear")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) { Value::Str("future".into()) } else { Value::Str("delivery".into()) });
wsUrlType = type_var.clone();
}
let mut isSpot: bool = type_var.as_str() == Some("spot");
let mut timezone: Value = Value::Null;
{ let __destr_tmp = self.handle_param_string(params.clone(), Value::Str("timezone".into()), &[]); timezone = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut isUtc8: bool = (timezone != Value::Null) && ((timezone.as_str() == Some("+08:00")) || is_true(&crate::precise::Precise::stringEq(&timezone, &Value::Str("8".into()))));
let mut rawHashes: Value = Value::from(vec![]);
let mut messageHashes: Value = Value::from(vec![]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_68: bool = true;
while { if !__for_first_68 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_68 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbolsAndTimeframes.len() as i64) as f64) } {
let mut symAndTf: Value = symbolsAndTimeframes.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut symbolString: Value = symAndTf.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null);
let mut timeframeString: Value = symAndTf.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null);
let mut interval: Value = self.safe_string(self.timeframes.clone(), timeframeString.clone(), &[timeframeString.clone()]);
let mut market: Value = self.market(symbolString);
let mut marketId: Value = market.as_map().and_then(|__m| __m.get("lowercaseId")).cloned().unwrap_or(Value::Null);
if (marketId == Value::Null) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", self.id.clone(), Value::Str(" watchOHLCVForSymbols() marketId is required".into()))));
}
if (klineType.as_str() == Some("indexPriceKline")) {
// weird behavior for index price kline we can't use the perp suffix
marketId = replace_str(&marketId, &Value::Str("_perp".into()), &Value::Str("".into()));
}
let mut shouldUseUTC8: bool = isUtc8 && isSpot;
let mut suffix: Value = Value::Str("@+08:00".into());
let mut utcSuffix: Value = (if shouldUseUTC8 { suffix } else { Value::Str("".into()) });
append_to_array(&mut rawHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", marketId, Value::Str("@".into())).into()), klineType).into()), Value::Str("_".into())).into()), interval).into()), utcSuffix).into()));
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str("ohlcv::".into()), market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null)).into()), Value::Str("::".into())).into()), timeframeString).into()));
}
}
let mut url: Value = Value::Str(format!("{}{}", add(&self.get_ws_url(wsUrlType.clone(), self.get_future_ws_category(klineType)), &Value::Str("/".into())), self.stream(wsUrlType, Value::Str("multipleOHLCV".into()), &[]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null)).into());
let mut requestId: Value = self.request_id(url.clone());
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("SUBSCRIBE".into()));
m.insert("params".to_string(), rawHashes);
m.insert("id".to_string(), requestId.clone());
m
});
let mut subscribe: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), requestId);
m
});
params = self.omit(params.clone(), Value::Str("callerMethodName".into()), &[]);
let __ws_arg_8 = self.extend(request, &[params]);
let mut res: Value = self.watch_multiple(url, messageHashes.clone(), &[__ws_arg_8, messageHashes.clone(), subscribe]).await;
let mut symboltimeframecandlesVariable = res;
let mut symbol: Value = get_value(&symboltimeframecandlesVariable, &Value::Int(0));
let mut timeframe: Value = get_value(&symboltimeframecandlesVariable, &Value::Int(1));
let mut candles: Value = get_value(&symboltimeframecandlesVariable, &Value::Int(2));
if is_true(&self.newUpdates) {
limit = candles.get_limit(symbol.clone(), limit.clone());
}
let mut filtered: Value = self.filter_by_since_limit(candles, &[since, limit, Value::Int(0), Value::Bool(true)]);
return self.create_ohlcv_object(symbol.clone(), timeframe, filtered);
Value::Null
}
/*
* @method
* @name binance#unWatchOHLCVForSymbols
* @description unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#klines
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Kline-Candlestick-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Kline-Candlestick-Streams
* @param {string[][]} symbolsAndTimeframes array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {object} [params.timezone] if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
pub async fn un_watch_ohlcv_for_symbols(&mut self, mut symbolsAndTimeframes: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut klineType: Value = Value::Null;
{ let __destr_tmp = self.handle_param_string2(params.clone(), Value::Str("channel".into()), Value::Str("name".into()), &[Value::Str("kline".into())]); klineType = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut symbols: Value = self.get_list_from_object_values(symbolsAndTimeframes.clone(), Value::Int(0));
let mut marketSymbols: Value = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(false), Value::Bool(false), Value::Bool(true)]);
let mut firstMarket: Value = self.market(marketSymbols.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null));
let mut type_var: Value = firstMarket.as_map().and_then(|__m| __m.get("type")).cloned().unwrap_or(Value::Null);
let mut wsUrlType: Value = type_var.clone();
if (firstMarket.as_map().and_then(|__m| __m.get("option")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
type_var = Value::Str("option".into());
wsUrlType = Value::Str("optionMarket".into()); // eOptions klines are served from /market/ws
} else if (firstMarket.as_map().and_then(|__m| __m.get("contract")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
type_var = (if (firstMarket.as_map().and_then(|__m| __m.get("linear")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) { Value::Str("future".into()) } else { Value::Str("delivery".into()) });
wsUrlType = type_var.clone();
}
let mut isSpot: bool = type_var.as_str() == Some("spot");
let mut timezone: Value = Value::Null;
{ let __destr_tmp = self.handle_param_string(params.clone(), Value::Str("timezone".into()), &[]); timezone = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut isUtc8: bool = (timezone != Value::Null) && ((timezone.as_str() == Some("+08:00")) || is_true(&crate::precise::Precise::stringEq(&timezone, &Value::Str("8".into()))));
let mut rawHashes: Value = Value::from(vec![]);
let mut subMessageHashes: Value = Value::from(vec![]);
let mut messageHashes: Value = Value::from(vec![]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_69: bool = true;
while { if !__for_first_69 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_69 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbolsAndTimeframes.len() as i64) as f64) } {
let mut symAndTf: Value = symbolsAndTimeframes.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut symbolString: Value = symAndTf.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null);
let mut timeframeString: Value = symAndTf.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null);
let mut interval: Value = self.safe_string(self.timeframes.clone(), timeframeString.clone(), &[timeframeString.clone()]);
let mut market: Value = self.market(symbolString);
let mut marketId: Value = market.as_map().and_then(|__m| __m.get("lowercaseId")).cloned().unwrap_or(Value::Null);
if (marketId == Value::Null) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", self.id.clone(), Value::Str(" unWatchOHLCVForSymbols() marketId is required".into()))));
}
if (klineType.as_str() == Some("indexPriceKline")) {
// weird behavior for index price kline we can't use the perp suffix
marketId = replace_str(&marketId, &Value::Str("_perp".into()), &Value::Str("".into()));
}
let mut shouldUseUTC8: bool = isUtc8 && isSpot;
let mut suffix: Value = Value::Str("@+08:00".into());
let mut utcSuffix: Value = (if shouldUseUTC8 { suffix } else { Value::Str("".into()) });
append_to_array(&mut rawHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", marketId, Value::Str("@".into())).into()), klineType).into()), Value::Str("_".into())).into()), interval).into()), utcSuffix).into()));
append_to_array(&mut subMessageHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str("ohlcv::".into()), market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null)).into()), Value::Str("::".into())).into()), timeframeString).into()));
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str("unsubscribe::ohlcv::".into()), market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null)).into()), Value::Str("::".into())).into()), timeframeString).into()));
}
}
let mut url: Value = Value::Str(format!("{}{}", add(&self.get_ws_url(wsUrlType.clone(), self.get_future_ws_category(klineType)), &Value::Str("/".into())), self.stream(wsUrlType, Value::Str("multipleOHLCV".into()), &[]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null)).into());
let mut requestId: Value = self.request_id(url.clone());
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("UNSUBSCRIBE".into()));
m.insert("params".to_string(), rawHashes);
m.insert("id".to_string(), requestId.clone());
m
});
let mut subscribe: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("unsubscribe".to_string(), Value::Bool(true));
m.insert("id".to_string(), to_string_val(&requestId));
m.insert("symbols".to_string(), symbols);
m.insert("symbolsAndTimeframes".to_string(), symbolsAndTimeframes);
m.insert("subMessageHashes".to_string(), subMessageHashes);
m.insert("messageHashes".to_string(), messageHashes.clone());
m.insert("topic".to_string(), Value::Str("ohlcv".into()));
m
});
params = self.omit(params.clone(), Value::Str("callerMethodName".into()), &[]);
let __ws_arg_9 = self.extend(request, &[params]);
return self.watch_multiple(url, messageHashes.clone(), &[__ws_arg_9, messageHashes.clone(), subscribe]).await;
Value::Null
}
/*
* @method
* @name binance#unWatchOHLCV
* @description unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#klines
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Kline-Candlestick-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Kline-Candlestick-Streams
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {object} [params.timezone] if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
pub async fn un_watch_ohlcv(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut timeframe = get_arg(optional_args, 0, Value::Str("1m".into()));
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol.clone());
symbol = market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
if let Value::Dict(__d) = &mut params { std::sync::Arc::make_mut(__d).insert("callerMethodName".into(), Value::Str("watchOHLCV".into())); }
return self.un_watch_ohlcv_for_symbols(Value::from(vec![Value::from(vec![symbol.clone(), timeframe])]), &[params]).await;
Value::Null
}
pub fn handle_ohlcv(&mut self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "e": "kline",
// "E": 1579482921215,
// "s": "ETHBTC",
// "k": {
// "t": 1579482900000,
// "T": 1579482959999,
// "s": "ETHBTC",
// "i": "1m",
// "f": 158411535,
// "L": 158411550,
// "o": "0.01913200",
// "c": "0.01913500",
// "h": "0.01913700",
// "l": "0.01913200",
// "v": "5.08400000",
// "n": 16,
// "x": false,
// "q": "0.09728060",
// "V": "3.30200000",
// "Q": "0.06318500",
// "B": "0"
// }
// }
//
let mut event: Value = (match message.get("e") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut eventMap: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("indexPrice_kline".to_string(), Value::Str("indexPriceKline".into()));
m.insert("markPrice_kline".to_string(), Value::Str("markPriceKline".into()));
m
});
event = self.safe_string(eventMap, event.clone(), &[event.clone()]);
let mut kline: Value = (match message.get("k") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Null });
let mut marketId: Value = self.safe_string2(kline.clone(), Value::Str("s".into()), Value::Str("ps".into()), &[]);
if (event.as_str() == Some("indexPriceKline")) {
// indexPriceKline doesn't have the _PERP suffix
marketId = (match message.get("ps") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
}
let mut interval: Value = self.safe_string_k(kline.clone(), "i", &[]);
// use a reverse lookup in a static map instead
let mut unifiedTimeframe: Value = self.find_timeframe(interval, &[]);
let mut parsed: Value = Value::from(vec![self.safe_integer_k(kline.clone(), "t", &[]), self.safe_float_k(kline.clone(), "o", &[]), self.safe_float_k(kline.clone(), "h", &[]), self.safe_float_k(kline.clone(), "l", &[]), self.safe_float_k(kline.clone(), "c", &[]), self.safe_float_k(kline, "v", &[])]);
// resolve the market from the transport url — an ambiguous id like
// BTCUSDT maps to both the spot and the linear swap market
let mut isSpot: Value = self.is_spot_url(client.clone());
let mut marketType: Value = (if is_true(&isSpot) { Value::Str("spot".into()) } else { Value::Str("contract".into()) });
let mut symbol: Value = self.safe_symbol(marketId, &[Value::Null, Value::Null, marketType]);
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str("ohlcv::".into()), symbol).into()), Value::Str("::".into())).into()), unifiedTimeframe).into());
{ let __be_tmp = self.safe_dict(self.ohlcvs.clone(), symbol.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]); if let Value::Dict(__d) = &mut self.ohlcvs { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), __be_tmp); } }
let mut stored: Value = self.safe_value(self.safe_dict(self.ohlcvs.clone(), symbol.clone(), &[]), unifiedTimeframe.clone(), &[]);
if (stored == Value::Null) {
let mut limit: Value = self.safe_integer_k(self.options.clone(), "OHLCVLimit", &[Value::Int(1000)]);
stored = ArrayCacheByTimestamp::new(limit);
if (symbol != Value::Null) && (unifiedTimeframe != Value::Null) {
add_element_to_object(get_value_mut(&mut self.ohlcvs, &symbol), &unifiedTimeframe, stored.clone());
}
}
stored.append(parsed);
let mut resolveData: Value = Value::from(vec![symbol.clone(), unifiedTimeframe, stored]);
client.resolve(&[resolveData, messageHash.clone()]);
}
/*
* @method
* @name binance#fetchTickerWs
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.method] method to use can be ticker.price or ticker.book
* @param {boolean} [params.returnRateLimits] return the rate limits for the exchange
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn fetch_ticker_ws(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol.clone());
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), market.as_map().and_then(|__m| __m.get("id")).cloned().unwrap_or(Value::Null));
m
});
let mut type_var: Value = self.get_market_type(Value::Str("fetchTickerWs".into()), market, &[params.clone()]);
if (type_var.as_str() != Some("future")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchTickerWs only supports swap markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &type_var);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_ticker_ws".into()).clone());
m
});
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchTickerWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("returnRateLimits".into(), returnRateLimits); }
params = self.omit(params.clone(), Value::Str("test".into()), &[]);
let mut method: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchTickerWs".into()), Value::Str("method".into()), &[Value::Str("ticker.book".into())]); method = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), method);
let __ws_arg_10 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_10]));
m
});
let mut ticker: Value = self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
return ticker;
Value::Null
}
/*
* @method
* @name binance#fetchOHLCVWs
* @description query historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#klines
* @param {string} symbol unified symbol of the market to query OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int} since timestamp in ms of the earliest candle to fetch
* @param {int} limit the maximum amount of candles to fetch
* @param {object} params extra parameters specific to the exchange API endpoint
* @param {int} params.until timestamp in ms of the earliest candle to fetch
*
* EXCHANGE SPECIFIC PARAMETERS
* @param {string} params.timeZone default=0 (UTC)
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
pub async fn fetch_ohlcv_ws(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut timeframe = get_arg(optional_args, 0, Value::Str("1m".into()));
let mut since = get_arg(optional_args, 1, Value::Null);
let mut limit = get_arg(optional_args, 2, Value::Null);
let mut params = get_arg(optional_args, 3, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol.clone());
let mut marketType: Value = self.get_market_type(Value::Str("fetchOHLCVWs".into()), market, &[params.clone()]);
if (marketType.as_str() != Some("spot")) && (marketType.as_str() != Some("future")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchOHLCVWs only supports spot or swap markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &marketType);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchOHLCVWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), self.market_id(symbol.clone()));
m.insert("returnRateLimits".to_string(), returnRateLimits);
m.insert("interval".to_string(), get_value(&self.timeframes, &timeframe));
m
});
let mut until: Value = self.safe_integer_k(params.clone(), "until", &[]);
params = self.omit(params.clone(), Value::Str("until".into()), &[]);
if (since != Value::Null) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("startTime".into(), since); }
}
if (limit != Value::Null) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("limit".into(), limit); }
}
if (until != Value::Null) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("endTime".into(), until); }
}
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), Value::Str("klines".into()));
m.insert("params".to_string(), self.extend(payload, &[params]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_fetch_ohlcv".into()).clone());
m
});
return self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
Value::Null
}
pub fn handle_fetch_ohlcv(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "id": "1dbbeb56-8eea-466a-8f6e-86bdcfa2fc0b",
// "status": 200,
// "result": [
// [
// 1655971200000, // Kline open time
// "0.01086000", // Open price
// "0.01086600", // High price
// "0.01083600", // Low price
// "0.01083800", // Close price
// "2290.53800000", // Volume
// 1655974799999, // Kline close time
// "24.85074442", // Quote asset volume
// 2283, // Number of trades
// "1171.64000000", // Taker buy base asset volume
// "12.71225884", // Taker buy quote asset volume
// "0" // Unused field, ignore
// ]
// ],
// "rateLimits": [
// {
// "rateLimitType": "REQUEST_WEIGHT",
// "interval": "MINUTE",
// "intervalNum": 1,
// "limit": 6000,
// "count": 2
// }
// ]
// }
//
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Arr(_)) => __v.clone(), _ => Value::Null });
let mut parsed: Value = self.parse_ohlc_vs(result, &[]);
// use a reverse lookup in a static map instead
let mut messageHash: Value = (match message.get("id") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
client.resolve(&[parsed, messageHash.clone()]);
}
/*
* @method
* @name binance#watchTicker
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#individual-symbol-mini-ticker-stream
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#all-market-mini-tickers-stream
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Individual-Symbol-Ticker-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/All-Market-Mini-Tickers-Stream
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/All-Market-Mini-Tickers-Stream
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Individual-Symbol-Ticker-Streams
* @see https://developers.binance.com/en/docs/catalog/advanced-trading-stocks-trading/api/ws-streams/market-streams#price-stream
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.stock] set to true to use the stocks aggregated price stream
* @param {string} [params.name] stream to use can be ticker or miniTicker
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn watch_ticker(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
symbol = self.symbol(symbol.clone());
let __ws_arg_11 = self.extend(params, &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("callerMethodName".to_string(), Value::Str("watchTicker".into()));
m
})]);
let mut tickers: Value = self.watch_tickers(&[Value::from(vec![symbol.clone()]), __ws_arg_11]).await;
return get_value(&tickers, &symbol);
Value::Null
}
/*
* @method
* @name binance#watchMarkPrice
* @description watches a mark price for a specific market
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Mark-Price-Stream
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.use1sFreq] *default is true* if set to true, the mark price will be updated every second, otherwise every 3 seconds
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn watch_mark_price(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
symbol = self.symbol(symbol.clone());
let __ws_arg_12 = self.extend(params, &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("callerMethodName".to_string(), Value::Str("watchMarkPrice".into()));
m
})]);
let mut tickers: Value = self.watch_mark_prices(&[Value::from(vec![symbol.clone()]), __ws_arg_12]).await;
return get_value(&tickers, &symbol);
Value::Null
}
/*
* @method
* @name binance#watchMarkPrices
* @description watches the mark price for all markets
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Mark-Price-Stream-for-All-market
* @param {string[]} symbols unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.use1sFreq] *default is true* if set to true, the mark price will be updated every second, otherwise every 3 seconds
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn watch_mark_prices(&mut self, optional_args: &[Value]) -> Value {
let mut symbols = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let mut channelName: Value = Value::Null;
// for now watchmarkPrice uses the same messageHash as watchTicker
// so it's impossible to watch both at the same time
// refactor this to use different messageHashes
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchMarkPrices".into()), Value::Str("name".into()), &[Value::Str("markPrice".into())]); channelName = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut newTickers: Value = self.watch_multi_ticker_helper(Value::Str("watchMarkPrices".into()), channelName, &[symbols.clone(), params]).await;
if is_true(&self.newUpdates) {
return newTickers;
}
return self.filter_by_array(self.tickers.clone(), Value::Str("symbol".into()), &[symbols]);
Value::Null
}
/*
* @method
* @name binance#watchTickers
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#individual-symbol-mini-ticker-stream
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#all-market-mini-tickers-stream
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Individual-Symbol-Ticker-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/All-Market-Mini-Tickers-Stream
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/All-Market-Mini-Tickers-Stream
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Individual-Symbol-Ticker-Streams
* @see https://developers.binance.com/en/docs/catalog/advanced-trading-stocks-trading/api/ws-streams/market-streams#price-stream
* @param {string[]} symbols unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.stock] set to true to use the stocks price stream
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn watch_tickers(&mut self, optional_args: &[Value]) -> Value {
let mut symbols = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let mut stock: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchTickers".into()), Value::Str("stock".into()), &[Value::Bool(false)]); stock = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if is_true(&stock) {
if (symbols == Value::Null) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", self.id.clone(), Value::Str(" watchTickers() with stock stream requires symbols".into()))));
}
symbols = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(false), Value::Bool(false), Value::Bool(true)]);
let mut stockResult: Value = self.watch_stock_market_stream(Value::from(vec![Value::Str("price".into())]), Value::from(vec![Value::Str("stock:price".into())]), &[params.clone()]).await;
if is_true(&self.newUpdates) {
return stockResult;
}
return self.filter_by_array(self.tickers.clone(), Value::Str("symbol".into()), &[symbols.clone()]);
}
let mut channelName: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchTickers".into()), Value::Str("name".into()), &[Value::Str("miniTicker".into())]); channelName = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if (channelName.as_str() == Some("bookTicker")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" deprecation notice - to subscribe for bids-asks, use watch_bids_asks() method instead".into()))));
}
let mut newTickers: Value = self.watch_multi_ticker_helper(Value::Str("watchTickers".into()), channelName, &[symbols.clone(), params]).await;
if is_true(&self.newUpdates) {
return newTickers;
}
return self.filter_by_array(self.tickers.clone(), Value::Str("symbol".into()), &[symbols]);
Value::Null
}
/*
* @method
* @name binance#unWatchTickers
* @description unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#individual-symbol-mini-ticker-stream
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#all-market-mini-tickers-stream
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Individual-Symbol-Ticker-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/All-Market-Mini-Tickers-Stream
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/All-Market-Mini-Tickers-Stream
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Individual-Symbol-Ticker-Streams
* @param {string[]} symbols unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn un_watch_tickers(&mut self, optional_args: &[Value]) -> Value {
let mut symbols = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let mut channelName: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchTickers".into()), Value::Str("name".into()), &[Value::Str("ticker".into())]); channelName = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if (channelName.as_str() == Some("bookTicker")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" deprecation notice - to subscribe for bids-asks, use watch_bids_asks() method instead".into()))));
}
return self.watch_multi_ticker_helper(Value::Str("unWatchTickers".into()), channelName, &[symbols, params, Value::Bool(true)]).await;
Value::Null
}
/*
* @method
* @name binance#unWatchMarkPrices
* @description unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Mark-Price-Stream
* @param {string[]} symbols unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn un_watch_mark_prices(&mut self, optional_args: &[Value]) -> Value {
let mut symbols = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let mut channelName: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchMarkPrices".into()), Value::Str("name".into()), &[Value::Str("markPrice".into())]); channelName = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
return self.watch_multi_ticker_helper(Value::Str("unWatchMarkPrices".into()), channelName, &[symbols, params, Value::Bool(true)]).await;
Value::Null
}
/*
* @method
* @name binance#unWatchMarkPrice
* @description unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Mark-Price-Stream
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn un_watch_mark_price(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
return self.un_watch_mark_prices(&[Value::from(vec![symbol.clone()]), params]).await;
Value::Null
}
/*
* @method
* @name binance#unWatchBidsAsks
* @description unWatches best bid & ask for symbols
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#individual-book-ticker-streams
* @see https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/Bookticker
* @param {string[]} [symbols] unified symbols
* @param {object} [params] extra parameters
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn un_watch_bids_asks(&mut self, optional_args: &[Value]) -> Value {
let mut symbols = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
return self.watch_multi_ticker_helper(Value::Str("unWatchBidsAsks".into()), Value::Str("bookTicker".into()), &[symbols, params, Value::Bool(true)]).await;
Value::Null
}
/*
* @method
* @name binance#unWatchTicker
* @description unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#individual-symbol-mini-ticker-stream
* @see https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#all-market-mini-tickers-stream
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Individual-Symbol-Ticker-Streams
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/All-Market-Mini-Tickers-Stream
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/All-Market-Mini-Tickers-Stream
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Individual-Symbol-Ticker-Streams
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn un_watch_ticker(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
return self.un_watch_tickers(&[Value::from(vec![symbol.clone()]), params]).await;
Value::Null
}
/*
* @method
* @name binance#watchBidsAsks
* @description watches best bid & ask for symbols
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#symbol-order-book-ticker
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/All-Book-Tickers-Stream
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/All-Book-Tickers-Stream
* @see https://developers.binance.com/en/docs/catalog/advanced-trading-stocks-trading/api/ws-streams/market-streams#quote-stream
* @param {string[]} symbols unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.stock] set to true to use stocks quote streams
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
*/
pub async fn watch_bids_asks(&mut self, optional_args: &[Value]) -> Value {
let mut symbols = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut stock: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchBidsAsks".into()), Value::Str("stock".into()), &[Value::Bool(false)]); stock = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if is_true(&stock) {
if (symbols == Value::Null) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", self.id.clone(), Value::Str(" watchBidsAsks() with stock stream requires symbols".into()))));
}
symbols = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(false), Value::Bool(false), Value::Bool(true)]);
let mut stockStreams: Value = Value::from(vec![]);
let mut stockMessageHashes: Value = Value::from(vec![]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_70: bool = true;
while { if !__for_first_70 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_70 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut stockTicker: Value = self.get_stock_ticker_from_symbol(symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
append_to_array(&mut stockStreams, Value::Str(format!("{}{}", stockTicker, Value::Str("@quote".into())).into()));
append_to_array(&mut stockMessageHashes, Value::Str(format!("{}{}", Value::Str("stock:quote:".into()), symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null)).into()));
}
}
let mut stockResult: Value = self.watch_stock_market_stream(stockStreams, stockMessageHashes, &[params.clone()]).await;
if is_true(&self.newUpdates) {
return stockResult;
}
return self.filter_by_array(self.bidsasks.clone(), Value::Str("symbol".into()), &[symbols.clone()]);
}
symbols = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(true), Value::Bool(false), Value::Bool(true)]);
let mut result: Value = self.watch_multi_ticker_helper(Value::Str("watchBidsAsks".into()), Value::Str("bookTicker".into()), &[symbols.clone(), params]).await;
if is_true(&self.newUpdates) {
return result;
}
return self.filter_by_array(self.bidsasks.clone(), Value::Str("symbol".into()), &[symbols]);
Value::Null
}
pub async fn watch_multi_ticker_helper(&mut self, mut methodName: Value, mut channelName: Value, optional_args: &[Value]) -> Value {
let mut symbols = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let mut isUnsubscribe = get_arg(optional_args, 2, Value::Bool(false));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
symbols = self.market_symbols(&[symbols.clone(), Value::Null, Value::Bool(true), Value::Bool(false), Value::Bool(true)]);
let mut isBidAsk: bool = channelName.as_str() == Some("bookTicker");
let mut isMarkPrice: bool = channelName.as_str() == Some("markPrice");
let mut use1sFreq: Value = self.safe_bool_k(params.clone(), "use1sFreq", &[Value::Bool(true)]);
let mut firstMarket: Value = Value::Null;
let mut marketType: Value = Value::Null;
let mut symbolsDefined: bool = symbols != Value::Null;
if (symbols != Value::Null) {
firstMarket = self.market(symbols.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null));
}
let mut userDefaultType: Option<String> = self.safe_string_k(self.options.clone(), "defaultType", &[]).as_str().map(str::to_owned);
let mut defaultMarket: Value = (if (isMarkPrice && (userDefaultType.as_deref() != Some("option"))) { Value::Str("swap".into()) } else { Value::Null });
{ let __destr_tmp = self.handle_market_type_and_params(methodName.clone(), &[firstMarket.clone(), params.clone(), defaultMarket]); marketType = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut subType: Value = Value::Null;
{ let __destr_tmp = self.handle_sub_type_and_params(methodName.clone(), &[firstMarket, params.clone()]); subType = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
// use marketType (not firstMarket) so the no-symbols case with defaultType='option' is also detected
let mut isOptionMarkPrice: bool = isMarkPrice && (marketType.as_str() == Some("option"));
let mut rawMarketType: Value = Value::Null;
if (marketType.as_str() == Some("option")) {
// check option first — isLinear returns true for linear-settled options, which would incorrectly route to futures
// eOptions: mark price and klines stream from /market/stream; tickers/bids-asks/depth/trades from /public/stream
rawMarketType = (if (isOptionMarkPrice) { Value::Str("optionMarket".into()) } else { Value::Str("option".into()) });
} else if is_true(&self.parent.is_linear(marketType.clone(), &[subType.clone()])) {
rawMarketType = Value::Str("future".into());
} else if is_true(&self.parent.is_inverse(marketType.clone(), &[subType])) {
rawMarketType = Value::Str("delivery".into());
} else if (marketType.as_str() == Some("spot")) {
rawMarketType = marketType.clone();
} else {
panic!("{}", crate::exchange_errors::not_supported(format!("{}{}", add(&Value::Str(format!("{}{}", self.id.clone(), Value::Str(" ".into())).into()), &methodName), Value::Str("() does not support options markets".into()))));
}
// eOptions tickers have a different stream name (@optionTicker) but the same event type (24hrTicker)
// so only the subscription arg changes — channelName stays as-is to keep messageHashes aligned
let mut isOptionTicker: bool = (marketType.as_str() == Some("option")) && !isMarkPrice && !isBidAsk;
if isMarkPrice && !(self.in_array(marketType.clone(), Value::from(vec![Value::Str("swap".into()), Value::Str("future".into()), Value::Str("option".into())])).as_bool() == Some(true)) {
panic!("{}", crate::exchange_errors::not_supported(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", add(&Value::Str(format!("{}{}", self.id.clone(), Value::Str(" ".into())).into()), &methodName), Value::Str("() does not support ".into())).into()), marketType).into()), Value::Str(" markets yet".into()))));
}
let mut subscriptionArgs: Value = Value::from(vec![]);
let mut messageHashes: Value = Value::from(vec![]);
let mut unsubscribeMessageHashes: Value = Value::from(vec![]);
let mut suffix: Value = Value::Str("".into());
if isMarkPrice && !isOptionMarkPrice {
suffix = (if (use1sFreq.as_bool() == Some(true)) { Value::Str("@1s".into()) } else { Value::Str("".into()) });
}
let mut unifiedPrefix: Value = Value::Null;
if isBidAsk {
unifiedPrefix = Value::Str("bidask".into());
} else if isMarkPrice {
unifiedPrefix = Value::Str("markPrice".into());
} else {
unifiedPrefix = Value::Str("ticker".into());
}
if (symbols != Value::Null) {
let mut seenUnderlyings: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
{
let mut i: Value = Value::Int(0);
let mut __for_first_71: bool = true;
while { if !__for_first_71 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_71 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbols.len() as i64) as f64) } {
let mut symbol: Value = symbols.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut market: Value = self.market(symbol.clone());
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", unifiedPrefix, Value::Str(":".into())).into()), channelName).into()), Value::Str("@".into())).into()), symbol).into()));
if is_true(&isUnsubscribe) {
append_to_array(&mut unsubscribeMessageHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str("unsubscribe::".into()), unifiedPrefix).into()), Value::Str(":".into())).into()), channelName).into()), Value::Str("@".into())).into()), symbol).into()));
}
if isOptionMarkPrice {
// subscribe per underlying, not per contract
let mut baseIdLower: Value = self.safe_string_lower_k(market.clone(), "baseId", &[Value::Str("".into())]);
let mut quoteIdLower: Value = self.safe_string_lower_k(market.clone(), "quoteId", &[Value::Str("".into())]);
let mut underlying: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", baseIdLower, Value::Str("".into())).into()), quoteIdLower).into());
if !(in_op(&seenUnderlyings, &underlying)) {
if let Value::Dict(__d) = &mut seenUnderlyings { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&underlying), Value::Bool(true)); }
append_to_array(&mut subscriptionArgs, Value::Str(format!("{}{}", underlying, Value::Str("@optionMarkPrice".into())).into()));
}
} else if isOptionTicker {
// eOptions tickers: group by underlying + expiry date (<underlying>@optionTicker@<YYMMDD>)
// market id format: BTC-240328-70000-C → expiry part is parts[1] = '240328'
let mut marketId: Value = self.safe_string_k(market.clone(), "id", &[Value::Str("".into())]);
let mut parts: Value = split(&marketId, &Value::Str("-".into()));
let mut expiryDate: Value = self.safe_string(parts, Value::Int(1), &[]);
let mut baseIdLower: Value = self.safe_string_lower_k(market.clone(), "baseId", &[Value::Str("".into())]);
let mut quoteIdLower: Value = self.safe_string_lower_k(market.clone(), "quoteId", &[Value::Str("".into())]);
let mut underlying: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", baseIdLower, Value::Str("".into())).into()), quoteIdLower).into());
let mut subscriptionArg: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", underlying, Value::Str("@optionTicker@".into())).into()), expiryDate).into());
if !(in_op(&seenUnderlyings, &subscriptionArg)) {
if let Value::Dict(__d) = &mut seenUnderlyings { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&subscriptionArg), Value::Bool(true)); }
append_to_array(&mut subscriptionArgs, subscriptionArg.clone());
}
} else {
let mut streamId: Value = market.as_map().and_then(|__m| __m.get("lowercaseId")).cloned().unwrap_or(Value::Null);
append_to_array(&mut subscriptionArgs, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", streamId, Value::Str("@".into())).into()), channelName).into()), suffix).into()));
}
}
}
} else {
if (marketType.as_str() == Some("option")) {
let mut underlying: Value = self.safe_string_lower_k(params.clone(), "underlying", &[]);
if (underlying == Value::Null) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", add(&Value::Str(format!("{}{}", self.id.clone(), Value::Str(" ".into())).into()), &methodName), Value::Str("() requires either symbols or params[\"underlying\"] for eOptions".into()))));
}
if isOptionTicker {
// eOptions tickers are per underlying+expiry: <underlying>@optionTicker@<YYMMDD>
let mut expirationDate: Value = self.safe_string_k(params.clone(), "expirationDate", &[]);
if (expirationDate == Value::Null) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", add(&Value::Str(format!("{}{}", self.id.clone(), Value::Str(" ".into())).into()), &methodName), Value::Str("() requires params[\"expirationDate\"] (e.g. \"260227\") for eOptions tickers when no symbols are provided".into()))));
}
append_to_array(&mut subscriptionArgs, Value::Str(format!("{}{}", Value::Str(format!("{}{}", underlying, Value::Str("@optionTicker@".into())).into()), expirationDate).into()));
} else {
// isOptionMarkPrice: one stream covers all contracts for the underlying
append_to_array(&mut subscriptionArgs, Value::Str(format!("{}{}", underlying, Value::Str("@optionMarkPrice".into())).into()));
}
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", unifiedPrefix, Value::Str("s:".into())).into()), channelName).into()));
append_to_array(&mut unsubscribeMessageHashes, Value::Str(format!("{}{}", Value::Str("unsubscribe::".into()), channelName).into()));
} else if isBidAsk {
if (marketType.as_str() == Some("spot")) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", add(&Value::Str(format!("{}{}", self.id.clone(), Value::Str(" ".into())).into()), &methodName), Value::Str("() requires symbols for this channel for spot markets".into()))));
}
append_to_array(&mut subscriptionArgs, Value::Str(format!("{}{}", Value::Str("!".into()), channelName).into()));
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", unifiedPrefix, Value::Str("s:".into())).into()), channelName).into()));
append_to_array(&mut unsubscribeMessageHashes, Value::Str(format!("{}{}", Value::Str("unsubscribe::".into()), channelName).into()));
} else if isMarkPrice {
append_to_array(&mut subscriptionArgs, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str("!".into()), channelName).into()), Value::Str("@arr".into())).into()), suffix).into()));
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", unifiedPrefix, Value::Str("s:".into())).into()), channelName).into()));
append_to_array(&mut unsubscribeMessageHashes, Value::Str(format!("{}{}", Value::Str("unsubscribe::".into()), channelName).into()));
} else {
append_to_array(&mut subscriptionArgs, Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str("!".into()), channelName).into()), Value::Str("@arr".into())).into()));
append_to_array(&mut messageHashes, Value::Str(format!("{}{}", Value::Str(format!("{}{}", unifiedPrefix, Value::Str("s:".into())).into()), channelName).into()));
append_to_array(&mut unsubscribeMessageHashes, Value::Str(format!("{}{}", Value::Str("unsubscribe::".into()), channelName).into()));
}
}
let mut streamHash: Value = channelName.clone();
if (symbols != Value::Null) {
streamHash = Value::Str(format!("{}{}", Value::Str(format!("{}{}", channelName, Value::Str("::".into())).into()), join(&symbols, &Value::Str(",".into()))).into());
}
let mut url: Value = Value::Str(format!("{}{}", add(&self.get_ws_url(rawMarketType.clone(), self.get_future_ws_category(channelName.clone())), &Value::Str("/".into())), self.stream(rawMarketType, streamHash, &[]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null)).into());
let mut requestId: Value = self.request_id(url.clone());
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), (if is_true(&isUnsubscribe) { Value::Str("UNSUBSCRIBE".into()) } else { Value::Str("SUBSCRIBE".into()) }));
m.insert("params".to_string(), subscriptionArgs);
m.insert("id".to_string(), requestId.clone());
m
});
let mut hashes: Value = messageHashes.clone();
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), requestId.clone());
m
});
if is_true(&isUnsubscribe) {
subscription = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("unsubscribe".to_string(), Value::Bool(true));
m.insert("id".to_string(), to_string_val(&requestId));
m.insert("subMessageHashes".to_string(), messageHashes);
m.insert("messageHashes".to_string(), unsubscribeMessageHashes.clone());
m.insert("symbols".to_string(), symbols);
m.insert("topic".to_string(), Value::Str("ticker".into()));
m
});
hashes = unsubscribeMessageHashes;
}
// for option mark prices, the underlying stream delivers all contracts in one array message
// wait on the batch hash so the resolved value is the full dict of new tickers
let mut waitHashes: Value = hashes.clone();
if isOptionMarkPrice && !is_true(&isUnsubscribe) {
waitHashes = Value::from(vec![Value::Str(format!("{}{}", Value::Str(format!("{}{}", unifiedPrefix, Value::Str("s:".into())).into()), channelName).into())]);
}
let __ws_arg_13 = self.deep_extend(request, &[params]);
let mut result: Value = self.watch_multiple(url, waitHashes, &[__ws_arg_13, hashes, subscription]).await;
if is_true(&isUnsubscribe) {
return result;
}
// for efficiency, we have two type of returned structure here - if symbols array was provided, then individual
// ticker dict comes in, otherwise all-tickers dict comes in
// isOptionMarkPrice always resolves on a batch hash → result is already a dict
if !symbolsDefined || isOptionMarkPrice {
return result;
} else {
let mut newDict: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
add_element_to_object(&mut newDict, &crate::value::get_value_k(&result, "symbol"), result.clone());
return newDict;
}
Value::Null
}
pub fn parse_ws_ticker(&self, mut message: Value, mut marketType: Value) -> Value {
// markPrice
// {
// "e": "markPriceUpdate", // Event type
// "E": 1562305380000, // Event time
// "s": "BTCUSDT", // Symbol
// "p": "11794.15000000", // Mark price
// "i": "11784.62659091", // Index price
// "P": "11784.25641265", // Estimated Settle Price, only useful in the last hour before the settlement starts
// "r": "0.00038167", // Funding rate
// "T": 1562306400000 // Next funding time
// }
//
// ticker
// {
// "e": "24hrTicker", // event type
// "E": 1579485598569, // event time
// "s": "ETHBTC", // symbol
// "p": "-0.00004000", // price change
// "P": "-0.209", // price change percent
// "w": "0.01920495", // weighted average price
// "x": "0.01916500", // the price of the first trade before the 24hr rolling window
// "c": "0.01912500", // last (closing) price
// "Q": "0.10400000", // last quantity
// "b": "0.01912200", // best bid
// "B": "4.10400000", // best bid quantity
// "a": "0.01912500", // best ask
// "A": "0.00100000", // best ask quantity
// "o": "0.01916500", // open price
// "h": "0.01956500", // high price
// "l": "0.01887700", // low price
// "v": "173518.11900000", // base volume
// "q": "3332.40703994", // quote volume
// "O": 1579399197842, // open time
// "C": 1579485597842, // close time
// "F": 158251292, // first trade id
// "L": 158414513, // last trade id
// "n": 163222, // total number of trades
// }
//
// miniTicker
// {
// "e": "24hrMiniTicker",
// "E": 1671617114585,
// "s": "MOBBUSD",
// "c": "0.95900000",
// "o": "0.91200000",
// "h": "1.04000000",
// "l": "0.89400000",
// "v": "2109995.32000000",
// "q": "2019254.05788000"
// }
// fetchTickerWs
// {
// "symbol":"BTCUSDT",
// "price":"72606.70",
// "time":1712526204284
// }
// fetchTickerWs - ticker.book
// {
// "lastUpdateId":1027024,
// "symbol":"BTCUSDT",
// "bidPrice":"4.00000000",
// "bidQty":"431.00000000",
// "askPrice":"4.00000200",
// "askQty":"9.00000000",
// "time":1589437530011,
// }
//
let mut marketId: Value = self.safe_string2(message.clone(), Value::Str("s".into()), Value::Str("symbol".into()), &[]);
let mut symbol: Value = self.safe_symbol(marketId.clone(), &[Value::Null, Value::Null, marketType.clone()]);
let mut event: Value = self.safe_string_k(message.clone(), "e", &[Value::Str("bookTicker".into())]);
if (event.as_str() == Some("24hrTicker")) {
event = Value::Str("ticker".into());
}
if (event.as_str() == Some("markPriceUpdate")) || (event.as_str() == Some("markPrice")) {
return self.safe_ticker(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), symbol.clone());
m.insert("timestamp".to_string(), self.safe_integer_k(message.clone(), "E", &[]));
m.insert("datetime".to_string(), self.iso8601(self.safe_integer_k(message.clone(), "E", &[])));
m.insert("info".to_string(), message.clone());
m.insert("markPrice".to_string(), self.safe_string2(message.clone(), Value::Str("mp".into()), Value::Str("p".into()), &[]));
m.insert("indexPrice".to_string(), self.safe_string_k(message.clone(), "i", &[]));
m
}), &[]);
}
let mut timestamp: Value = Value::Null;
if (event.as_str() == Some("bookTicker")) {
// take the event timestamp, if available, for spot tickers it is not
timestamp = self.safe_integer2(message.clone(), Value::Str("E".into()), Value::Str("time".into()), &[]);
} else {
// take the timestamp of the closing price for candlestick streams
timestamp = self.safe_integer_n(message.clone(), Value::from(vec![Value::Str("C".into()), Value::Str("E".into()), Value::Str("time".into())]), &[]);
}
let mut market: Value = self.safe_market(&[marketId, Value::Null, Value::Null, marketType]);
let mut last: Value = self.safe_string2(message.clone(), Value::Str("c".into()), Value::Str("price".into()), &[]);
// A coin-margined stream counts `v` in contracts and puts the
// base asset in `q`, one field over from a linear stream, and
// `parseTicker` reads the same pair. Only the full ticker
// carries `w`, so a miniTicker uses the contract size.
let mut baseVolume: Value = self.safe_string_k(message.clone(), "v", &[]);
let mut quoteVolume: Value = self.safe_string_k(message.clone(), "q", &[]);
if (market.as_map().and_then(|__m| __m.get("inverse")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) {
let mut contracts: Value = baseVolume.clone();
baseVolume = quoteVolume.clone();
let mut weightedAverage: Value = self.safe_string_k(message.clone(), "w", &[]);
if (weightedAverage == Value::Null) {
quoteVolume = crate::precise::Precise::stringMul(&contracts, &self.safe_string_k(market.clone(), "contractSize", &[]));
} else {
quoteVolume = crate::precise::Precise::stringMul(&baseVolume, &weightedAverage);
}
}
return self.safe_ticker(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), symbol.clone());
m.insert("timestamp".to_string(), timestamp.clone());
m.insert("datetime".to_string(), self.iso8601(timestamp.clone()));
m.insert("high".to_string(), self.safe_string_k(message.clone(), "h", &[]));
m.insert("low".to_string(), self.safe_string_k(message.clone(), "l", &[]));
m.insert("bid".to_string(), self.safe_string2(message.clone(), Value::Str("b".into()), Value::Str("bidPrice".into()), &[]));
m.insert("bidVolume".to_string(), self.safe_string2(message.clone(), Value::Str("B".into()), Value::Str("bidQty".into()), &[]));
m.insert("ask".to_string(), self.safe_string2(message.clone(), Value::Str("a".into()), Value::Str("askPrice".into()), &[]));
m.insert("askVolume".to_string(), self.safe_string2(message.clone(), Value::Str("A".into()), Value::Str("askQty".into()), &[]));
m.insert("vwap".to_string(), self.safe_string_k(message.clone(), "w", &[]));
m.insert("open".to_string(), self.safe_string_k(message.clone(), "o", &[]));
m.insert("close".to_string(), last.clone());
m.insert("last".to_string(), last);
m.insert("previousClose".to_string(), self.safe_string_k(message.clone(), "x", &[]));
m.insert("change".to_string(), self.safe_string_k(message.clone(), "p", &[]));
m.insert("percentage".to_string(), self.safe_string_k(message.clone(), "P", &[]));
m.insert("average".to_string(), Value::Null);
m.insert("baseVolume".to_string(), baseVolume);
m.insert("quoteVolume".to_string(), quoteVolume);
m.insert("info".to_string(), message.clone());
m
}), &[market]);
Value::Null
}
pub fn handle_ticker_ws(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// ticker.price
// {
// "id":"1",
// "status":200,
// "result":{
// "symbol":"BTCUSDT",
// "price":"73178.60",
// "time":1712527052374
// }
// }
// ticker.book
// {
// "id":"9d32157c-a556-4d27-9866-66760a174b57",
// "status":200,
// "result":{
// "lastUpdateId":1027024,
// "symbol":"BTCUSDT",
// "bidPrice":"4.00000000",
// "bidQty":"431.00000000",
// "askPrice":"4.00000200",
// "askQty":"9.00000000",
// "time":1589437530011 // Transaction time
// }
// }
//
let mut messageHash: Value = (match message.get("id") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut ticker: Value = self.parse_ws_ticker(result, Value::Str("future".into()));
client.resolve(&[ticker, messageHash.clone()]);
}
pub fn handle_bids_asks(&mut self, mut client: Value, mut message: Value) {
//
// arrives one symbol dict or array of symbol dicts
//
// {
// "u": 7488717758,
// "s": "BTCUSDT",
// "b": "28621.74000000",
// "B": "1.43278800",
// "a": "28621.75000000",
// "A": "2.52500800"
// }
//
self.handle_tickers_and_bids_asks(client.clone(), message.clone(), Value::Str("bidasks".into()));
}
pub fn handle_tickers(&mut self, mut client: Value, mut message: Value) {
//
// arrives one symbol dict or array of symbol dicts
//
// {
// "e": "24hrTicker", // event type
// "E": 1579485598569, // event time
// "s": "ETHBTC", // symbol
// "p": "-0.00004000", // price change
// "P": "-0.209", // price change percent
// "w": "0.01920495", // weighted average price
// "x": "0.01916500", // the price of the first trade before the 24hr rolling window
// "c": "0.01912500", // last (closing) price
// "Q": "0.10400000", // last quantity
// "b": "0.01912200", // best bid
// "B": "4.10400000", // best bid quantity
// "a": "0.01912500", // best ask
// "A": "0.00100000", // best ask quantity
// "o": "0.01916500", // open price
// "h": "0.01956500", // high price
// "l": "0.01887700", // low price
// "v": "173518.11900000", // base volume
// "q": "3332.40703994", // quote volume
// "O": 1579399197842, // open time
// "C": 1579485597842, // close time
// "F": 158251292, // first trade id
// "L": 158414513, // last trade id
// "n": 163222, // total number of trades
// }
//
self.handle_tickers_and_bids_asks(client.clone(), message.clone(), Value::Str("tickers".into()));
}
pub fn handle_mark_prices(&mut self, mut client: Value, mut message: Value) {
self.handle_tickers_and_bids_asks(client.clone(), message.clone(), Value::Str("markPrices".into()));
}
pub fn handle_tickers_and_bids_asks(&mut self, mut client: Value, mut message: Value, mut methodType: Value) {
let mut isBidAsk: bool = methodType.as_str() == Some("bidasks");
let mut isMarkPrice: bool = methodType.as_str() == Some("markPrices");
let mut unifiedPrefix: Value = Value::Null;
if isBidAsk {
unifiedPrefix = Value::Str("bidask".into());
} else if isMarkPrice {
unifiedPrefix = Value::Str("markPrice".into());
} else {
unifiedPrefix = Value::Str("ticker".into());
}
let mut channelName: Value = Value::Null;
let mut resolvedMessageHashes: Value = Value::from(vec![]);
let mut rawTickers: Value = Value::from(vec![]);
let mut newTickers: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
if (matches!(&message, Value::Arr(_))) {
rawTickers = message.clone();
} else {
append_to_array(&mut rawTickers, message.clone());
}
{
let mut i: Value = Value::Int(0);
let mut __for_first_72: bool = true;
while { if !__for_first_72 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_72 = false; i.as_f64().unwrap_or(f64::NAN) < ((rawTickers.len() as i64) as f64) } {
let mut ticker: Value = rawTickers.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut event: Value = self.safe_string_k(ticker.clone(), "e", &[]);
if isBidAsk {
event = Value::Str("bookTicker".into()); // as noted in `handleMessage`, bookTicker doesn't have identifier, so manually set here
}
channelName = self.safe_string(self.options.as_map().and_then(|__m| __m.get("tickerChannelsMap")).cloned().unwrap_or(Value::Null), event.clone(), &[event.clone()]);
if (channelName == Value::Null) {
continue;
}
let mut tickerMarketId: Value = self.safe_string_k(ticker.clone(), "s", &[]);
let mut tickerMarketsByIdList: Value = self.safe_list(self.markets_by_id.clone(), tickerMarketId, &[]);
let mut numTickerMarkets: Value = (if (tickerMarketsByIdList == Value::Null) { Value::Int(0) } else { Value::Int(tickerMarketsByIdList.len() as i64) });
// an ambiguous id, spot and swap share e.g. BTCUSDC, must not be resolved by
// blind first pick, the stream url decides; only a unique match, like an
// option id, may override it, see https://github.com/ccxt/ccxt/issues/29728
let mut tickerMarketById: Value = (if (numTickerMarkets.as_f64() == Some(1.0)) { self.safe_dict(tickerMarketsByIdList, Value::Int(0), &[]) } else { Value::Null });
let mut isSpot: Value = self.is_spot_url(client.clone());
let mut tickerFallbackType: Value = (if is_true(&isSpot) { Value::Str("spot".into()) } else { Value::Str("contract".into()) });
let mut tickerMarketType: Value = (if (tickerMarketById != Value::Null) { tickerMarketById.as_map().and_then(|__m| __m.get("type")).cloned().unwrap_or(Value::Null) } else { tickerFallbackType });
let mut parsedTicker: Value = self.parse_ws_ticker(ticker, tickerMarketType);
let mut symbol: Value = parsedTicker.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
if (symbol != Value::Null) {
if let Value::Dict(__d) = &mut newTickers { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), parsedTicker.clone()); }
}
if isBidAsk {
if (symbol != Value::Null) {
if let Value::Dict(__d) = &mut self.bidsasks { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), parsedTicker.clone()); }
}
} else {
if (symbol != Value::Null) {
if let Value::Dict(__d) = &mut self.tickers { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), parsedTicker.clone()); }
}
}
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", unifiedPrefix, Value::Str(":".into())).into()), channelName).into()), Value::Str("@".into())).into()), symbol).into());
append_to_array(&mut resolvedMessageHashes, messageHash.clone());
client.resolve(&[parsedTicker, messageHash.clone()]);
}
}
// resolve batch endpoint
let mut length: f64 = ((resolvedMessageHashes.len() as i64) as f64);
if length > ((0i64) as f64) {
let mut batchMessageHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", unifiedPrefix, Value::Str("s:".into())).into()), channelName).into());
client.resolve(&[newTickers, batchMessageHash]);
}
}
pub fn sign_params(&self, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
self.check_required_credentials(&[]);
let mut defaultRecvWindow: Value = self.safe_integer_k(self.options.clone(), "recvWindow", &[]);
if (defaultRecvWindow != Value::Null) {
if let Value::Dict(__d) = &mut params { std::sync::Arc::make_mut(__d).insert("recvWindow".into(), defaultRecvWindow); }
}
let mut recvWindow: Value = self.safe_integer_k(params.clone(), "recvWindow", &[]);
if (recvWindow != Value::Null) {
if let Value::Dict(__d) = &mut params { std::sync::Arc::make_mut(__d).insert("recvWindow".into(), recvWindow); }
}
let __ws_arg_14 = self.nonce();
let mut extendedParams: Value = self.extend(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("timestamp".to_string(), __ws_arg_14);
m.insert("apiKey".to_string(), self.apiKey.clone());
m
}), &[params]);
extendedParams = self.keysort(extendedParams.clone(), &[]);
let mut query: Value = self.rawencode(extendedParams.clone(), &[]);
let mut signature: Value = Value::Null;
if Value::Int(self.secret.as_str().and_then(|__s| __s.find("PRIVATE KEY")).map(|__i| __i as i64).unwrap_or(-1)).as_f64().unwrap_or(f64::NAN) > ((-1i64) as f64) {
if ((self.secret.len() as i64) as f64) > ((120i64) as f64) {
signature = rsa(query.clone(), self.secret.clone(), Value::Str("sha256".into()));
} else {
signature = eddsa(self.encode(query.clone()), self.secret.clone(), Value::Str("ed25519".into()));
}
} else {
signature = self.hmac(self.encode(query), self.encode(self.secret.clone()), Value::Str("sha256".into()), &[]);
}
add_element_to_object(&mut extendedParams, &Value::Str("signature".into()), signature);
return extendedParams;
Value::Null
}
/*
* @name binance#ensureUserDataStreamWsSubscribeSignature
* @description watches best bid & ask for symbols
* @param {string} [marketType] only supports 'spot'
* @see {@link https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/user-data-stream-requests#subscribe-to-user-data-stream-through-signature-subscription-user_data Binance User Data Stream Documentation}
* @returns Promise<number> The subscription ID for the user data stream
*/
pub async fn ensure_user_data_stream_ws_subscribe_signature(&mut self, optional_args: &[Value]) -> Value {
let mut marketType = get_arg(optional_args, 0, Value::Str("spot".into()));
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &marketType);
let mut client: Value = self.client(&[url.clone()]);
let mut subscriptions: Value = get_value(&client, &Value::Str("subscriptions".into()));
let mut subscriptionsKeys: Value = object_keys(&subscriptions);
let mut accountType: Value = self.get_account_type_from_subscriptions(subscriptionsKeys).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
if (accountType.as_str() == marketType.as_str()) {
return Value::Null;
}
// the subscriptions flag is raised before the subscribe request is confirmed,
// so a concurrent caller would otherwise return onto an unauthenticated stream
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str("authenticate:signature:".into()), marketType).into());
if (in_op(&get_value(&client, &Value::Str("futures".into())), &messageHash)) {
// another caller is already subscribing, wait for it instead of subscribing again
crate::exchange_stubs::ws_await_flight(&client.future(&[messageHash.clone()])).await;
return Value::Null;
}
client.future(&[messageHash.clone()]); // created ahead of the request below, so concurrent callers can find it
add_element_to_object(&mut get_value(&client, &Value::Str("subscriptions".into())), &marketType, Value::Bool(true));
let mut requestId: Value = self.request_id(url.clone());
let mut requestHash: Value = to_string_val(&requestId);
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), requestHash.clone());
m.insert("method".to_string(), Value::Str("userDataStream.subscribe.signature".into()));
m.insert("params".to_string(), self.sign_params(&[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), requestHash.clone());
m.insert("method".to_string(), Value::Str("handle_user_data_stream_subscribe".into()).clone());
m.insert("subscription".to_string(), marketType.clone());
m
});
let _try_result = futures::FutureExt::catch_unwind(std::panic::AssertUnwindSafe(async {
self.watch(url, requestHash.clone(), &[message.clone(), requestHash.clone(), subscription]).await;
client.resolve(&[marketType.clone(), messageHash.clone()]);
#[allow(unreachable_code)] { Value::Null }})).await;
if let Err(_try_err) = _try_result { let e: Value = panic_to_value(_try_err);
remove(&mut get_value(&client, &Value::Str("subscriptions".into())), &marketType);
client.reject(&[e.clone(), messageHash.clone()]);
panic!("{}", e);
}
Value::Null
}
pub fn handle_user_data_stream_subscribe(&self, mut client: Value, mut message: Value) {
//
// {
// "id": 1,
// "status": 200,
// "result": {
// "subscriptionId": 0
// }
// }
//
let mut messageHash: Value = self.safe_string_k(message.clone(), "id", &[]);
let mut subscriptions: Value = get_value(&client, &Value::Str("subscriptions".into()));
let mut subscriptionsKeys: Value = object_keys(&subscriptions);
let mut accountType: Value = self.get_account_type_from_subscriptions(subscriptionsKeys).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
let mut result: Value = self.safe_dict_k(message.clone(), "result", &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut subscriptionId: Option<i64> = self.safe_integer_k(result, "subscriptionId", &[]).as_i64();
if (subscriptionId.is_none()) {
remove(&mut get_value(&client, &Value::Str("subscriptions".into())), &accountType);
client.reject(&[message.clone(), accountType]);
client.reject(&[message.clone(), messageHash.clone()]);
return;
}
client.resolve(&[message.clone(), messageHash.clone()]);
}
/*
* @name binance#ensureUserDataStreamWsSubscribeListenToken
* @description subscribes to user data stream using listenToken (for margin)
* @param {string} marketType - the market type (e.g., 'margin')
* @param {object} params - extra parameters specific to the request
* @param {string} [params.symbol] - required for isolated margin
* @param {boolean} [params.isIsolated] - whether it is isolated margin
* @param {number} [params.validity] - validity in milliseconds, default 24 hours, max 24 hours
* @see {@link https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-api/user-data-stream Binance User Data Stream Documentation}
* @returns Promise<void>
*/
pub async fn ensure_user_data_stream_ws_subscribe_listen_token(&mut self, optional_args: &[Value]) -> Value {
let mut marketType = get_arg(optional_args, 0, Value::Str("margin".into()));
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let mut url: Value = crate::value::get_value_k(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), "spot");
let mut options: Value = self.safe_dict(self.options.clone(), marketType.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut lastAuthenticatedTime: Value = self.safe_integer_k(options.clone(), "lastAuthenticatedTime", &[Value::Int(0)]);
let mut listenTokenRefreshRate: Value = self.safe_integer_k(self.options.clone(), "listenTokenRefreshRate", &[Value::Int(82800000)]); // 23 hours default
let mut time: Value = self.milliseconds();
let mut delay: Value = self.sum(&[listenTokenRefreshRate, Value::Int(10000)]);
if (match (&(time), &(lastAuthenticatedTime)) { (Value::Int(x), Value::Int(y)) => Value::Int(x - y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x - *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x - y), _ => Value::Null }).as_f64().unwrap_or(f64::NAN) > delay.as_f64().unwrap_or(f64::NAN) {
// the future covers the REST create plus the ws subscribe, including the
// renewal timer re-entry through renewListenToken, so a concurrent caller
// waits for the leader rather than minting a second listenToken
let mut client: Value = self.client(&[url.clone()]);
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str("authenticate:".into()), marketType).into()), Value::Str(":listenToken".into())).into());
if (in_op(&get_value(&client, &Value::Str("futures".into())), &messageHash)) {
// another caller is already fetching, wait for it instead of fetching again
crate::exchange_stubs::ws_await_flight(&client.future(&[messageHash.clone()])).await;
return Value::Null;
}
client.future(&[messageHash.clone()]); // created ahead of the request below, so concurrent callers can find it
let _try_result = futures::FutureExt::catch_unwind(std::panic::AssertUnwindSafe(async {
// Step 1: Create listenToken via REST API
let mut symbol: Value = self.safe_string_k(params.clone(), "symbol", &[]);
let mut isIsolated: Value = self.safe_bool_k(params.clone(), "isIsolated", &[Value::Bool(false)]);
let mut validity: Value = self.safe_integer_k(params.clone(), "validity", &[]);
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
if (isIsolated.as_bool() == Some(true)) {
if (symbol == Value::Null) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", self.id.clone(), Value::Str(" ensureUserDataStreamWsSubscribeListenToken() requires a symbol argument for isolated margin mode".into()))));
}
let mut marketId: Value = self.market_id(symbol.clone());
if let Value::Dict(__d) = &mut request { std::sync::Arc::make_mut(__d).insert("symbol".into(), marketId); }
if let Value::Dict(__d) = &mut request { std::sync::Arc::make_mut(__d).insert("isIsolated".into(), Value::Bool(true)); }
}
if (validity != Value::Null) {
if let Value::Dict(__d) = &mut request { std::sync::Arc::make_mut(__d).insert("validity".into(), validity.clone()); }
}
let mut response: Value = self.parent.sapi_post_user_listen_token(&[request]).await;
let mut listenToken: Value = self.safe_string_k(response.clone(), "token", &[]);
if (listenToken == Value::Null) {
panic!("{}", crate::exchange_errors::authentication_error(format!("{}{}", self.id.clone(), Value::Str(" ensureUserDataStreamWsSubscribeListenToken() failed to obtain a listenToken".into()))));
}
let mut expirationTime: Value = self.safe_integer_k(response, "expirationTime", &[]);
// Step 2: Subscribe to user data stream via WebSocket API
let mut requestId: Value = self.request_id(url.clone());
let mut requestHash: Value = to_string_val(&requestId);
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), requestHash.clone());
m.insert("method".to_string(), Value::Str("userDataStream.subscribe.listenToken".into()));
m.insert("params".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("listenToken".to_string(), listenToken.clone());
m
}));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), requestHash.clone());
m.insert("method".to_string(), Value::Str("handle_user_data_stream_subscribe".into()).clone());
m.insert("subscription".to_string(), marketType.clone());
m
});
self.watch(url, requestHash.clone(), &[message.clone(), requestHash.clone(), subscription]).await;
{ let __be_tmp = self.extend(options.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("listenToken".to_string(), listenToken.clone());
m.insert("expirationTime".to_string(), expirationTime.clone());
m.insert("lastAuthenticatedTime".to_string(), time.clone());
m.insert("symbol".to_string(), symbol.clone());
m.insert("isIsolated".to_string(), isIsolated);
m.insert("validity".to_string(), validity);
m
})]); if let Value::Dict(__d) = &mut self.options { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&marketType), __be_tmp); } }
// Schedule token renewal before expiration
if (expirationTime != Value::Null) {
let mut renewalTime: Value = (match (&((match (&(expirationTime), &(time)) { (Value::Int(x), Value::Int(y)) => Value::Int(x - y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x - *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x - y), _ => Value::Null })), &(Value::Int(60000))) { (Value::Int(x), Value::Int(y)) => Value::Int(x - y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x - *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x - y), _ => Value::Null }); // Renew 1 minute before expiration
if renewalTime.as_f64().unwrap_or(f64::NAN) > ((0i64) as f64) {
let mut extendedParams: Value = self.extend(params, &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), marketType.clone());
m
})]);
self.delay(renewalTime, &[Value::Str("renew_listen_token".into()).clone(), extendedParams]).await;
}
}
client.resolve(&[listenToken, messageHash.clone()]);
#[allow(unreachable_code)] { Value::Null }})).await;
if let Err(_try_err) = _try_result { let e: Value = panic_to_value(_try_err);
{ let __be_tmp = self.extend(options.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("lastAuthenticatedTime".to_string(), Value::Int(0));
m
})]); if let Value::Dict(__d) = &mut self.options { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&marketType), __be_tmp); } }
client.reject(&[e.clone(), messageHash.clone()]);
panic!("{}", e);
}
}
Value::Null
}
pub async fn renew_listen_token(&mut self, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let __params_empty = indexmap::IndexMap::new();
let params = params.as_map().unwrap_or(&__params_empty);
let mut type_var: Value = (match params.get("type") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Str("margin".into()) });
let mut options: Value = self.safe_dict(self.options.clone(), type_var.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut symbol: Value = self.safe_string_k(options.clone(), "symbol", &[]);
let mut isIsolated: Value = self.safe_bool_k(options.clone(), "isIsolated", &[Value::Bool(false)]);
let mut validity: Value = self.safe_integer_k(options, "validity", &[]);
let mut renewParams: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
if (symbol != Value::Null) {
if let Value::Dict(__d) = &mut renewParams { std::sync::Arc::make_mut(__d).insert("symbol".into(), symbol.clone()); }
}
if (isIsolated.as_bool() == Some(true)) {
if let Value::Dict(__d) = &mut renewParams { std::sync::Arc::make_mut(__d).insert("isIsolated".into(), isIsolated); }
}
if (validity != Value::Null) {
if let Value::Dict(__d) = &mut renewParams { std::sync::Arc::make_mut(__d).insert("validity".into(), validity); }
}
self.ensure_user_data_stream_ws_subscribe_listen_token(&[type_var, renewParams]).await;
Value::Null
}
pub async fn authenticate(&mut self, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let mut time: Value = self.milliseconds();
let mut resolvedAuth: Value = self.resolve_auth_type(Value::Str("authenticate".into()), &[Value::Null, params.clone()]);
let mut type_var: Value = resolvedAuth.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null);
params = resolvedAuth.as_array().and_then(|__arr| __arr.get(2)).cloned().unwrap_or(Value::Null);
let mut isPortfolioMargin: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params2(params.clone(), Value::Str("authenticate".into()), Value::Str("papi".into()), Value::Str("portfolioMargin".into()), &[Value::Bool(false)]); isPortfolioMargin = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
// For spot use WebSocket API signature subscription
if (type_var.as_str() == Some("spot")) {
self.ensure_user_data_stream_ws_subscribe_signature(&[Value::Str("spot".into())]).await;
return Value::Null;
}
let mut marginMode: Value = Value::Null;
{ let __destr_tmp = self.handle_margin_mode_and_params(Value::Str("authenticate".into()), &[params.clone()]); marginMode = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut isIsolatedMargin: bool = marginMode.as_str() == Some("isolated");
let mut symbol: Value = self.safe_string_k(params.clone(), "symbol", &[]);
// For margin use WebSocket API listenToken subscription
if (type_var.as_str() == Some("margin")) || isIsolatedMargin {
let mut marginParams: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
if (symbol != Value::Null) {
if let Value::Dict(__d) = &mut marginParams { std::sync::Arc::make_mut(__d).insert("symbol".into(), symbol.clone()); }
}
if isIsolatedMargin {
if let Value::Dict(__d) = &mut marginParams { std::sync::Arc::make_mut(__d).insert("isIsolated".into(), Value::Bool(true)); }
}
self.ensure_user_data_stream_ws_subscribe_listen_token(&[Value::Str("margin".into()), marginParams]).await;
return Value::Null;
}
params = self.omit(params.clone(), Value::Str("symbol".into()), &[]);
let mut isStock: bool = type_var.as_str() == Some("stock");
let mut options: Value = self.safe_dict(self.options.clone(), type_var.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut lastAuthenticatedTime: Value = self.safe_integer_k(options.clone(), "lastAuthenticatedTime", &[Value::Int(0)]);
let mut refreshRateKey: Value = (if isStock { Value::Str("stockListenKeyRefreshRate".into()) } else { Value::Str("listenKeyRefreshRate".into()) });
let mut listenKeyRefreshRate: Value = self.safe_integer(self.options.clone(), refreshRateKey, &[Value::Int(1200000)]);
let mut delay: Value = self.sum(&[listenKeyRefreshRate.clone(), Value::Int(10000)]);
if (match (&(time), &(lastAuthenticatedTime)) { (Value::Int(x), Value::Int(y)) => Value::Int(x - y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x - *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x - y), _ => Value::Null }).as_f64().unwrap_or(f64::NAN) > delay.as_f64().unwrap_or(f64::NAN) {
// single-flight leader election, see https://github.com/ccxt/ccxt/issues/29393
// the flight is registered on a never-dialed client because the
// user-data url embeds the listenKey, so no real client exists
// before the fetch and no listenKey-free parking url is needed.
// client.futures is the registry: client.future () is the atomic
// check-and-insert and client.resolve () / client.reject () settle
// and remove the entry under the same lock in every port
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str("authenticate:".into()), type_var).into());
let mut client: Value = self.client(&[Value::Str("authenticationFlights".into())]);
if (in_op(&get_value(&client, &Value::Str("futures".into())), &messageHash)) {
// a flight is already in progress - wake when the leader
// settles it: the listenKey is then in the bucket
crate::exchange_stubs::ws_await_flight(&client.future(&[messageHash.clone()])).await;
return Value::Null;
}
// reusableFuture (), not future () - the two match in
// js/py/php/cs/java, but go's Client.Future () yields a channel
// that the trailing suspension point below would panic on
let mut future: Value = client.reusable_future(messageHash.clone());
let _try_result = futures::FutureExt::catch_unwind(std::panic::AssertUnwindSafe(async {
let mut response: Value = Value::Null;
if isStock {
let mut requestParams: Value = self.omit(params.clone(), Value::from(vec![Value::Str("stock".into()), Value::Str("name".into()), Value::Str("callerMethodName".into()), Value::Str("type".into()), Value::Str("subType".into()), Value::Str("symbol".into()), Value::Str("timeframe".into())]), &[]);
response = self.parent.sapi_post_equity_listen_key(&[requestParams]).await;
} else if is_true(&isPortfolioMargin) {
response = self.parent.papi_post_listen_key(&[params.clone()]).await;
params = self.extend(params.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("portfolioMargin".to_string(), Value::Bool(true));
m
})]);
} else if (type_var.as_str() == Some("future")) {
response = self.parent.fapi_private_post_listen_key(&[params.clone()]).await;
} else if (type_var.as_str() == Some("delivery")) {
response = self.parent.dapi_private_post_listen_key(&[params.clone()]).await;
} else if (type_var.as_str() == Some("option")) {
response = self.parent.eapi_private_post_listen_key(&[params.clone()]).await;
} else {
response = self.parent.public_post_user_data_stream(&[params.clone()]).await;
}
let mut listenKey: Value = self.safe_string_k(response, "listenKey", &[]);
if (listenKey == Value::Null) {
panic!("{}", crate::exchange_errors::authentication_error(format!("{}{}", self.id.clone(), Value::Str(" authenticate() received an empty listenKey".into()))));
}
{ let __be_tmp = self.extend(options.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("listenKey".to_string(), listenKey.clone());
m.insert("lastAuthenticatedTime".to_string(), time);
m
})]); if let Value::Dict(__d) = &mut self.options { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&type_var), __be_tmp); } }
// hoisted out of the delay call: the transpilers garble an inline
// dict literal nested inside a delay argument
let mut delayParams: Value = params.clone();
if isStock {
delayParams = self.extend(params, &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), Value::Str("stock".into()));
m.insert("defaultType".to_string(), Value::Str("stock".into()));
m
})]);
}
self.delay(listenKeyRefreshRate, &[Value::Str("keep_alive_listen_key".into()).clone(), delayParams]).await;
// settle the flight: client.resolve () removes the future from
// client.futures and wakes every waiter
client.resolve(&[listenKey, messageHash.clone()]);
#[allow(unreachable_code)] { Value::Null }})).await;
if let Err(_try_err) = _try_result { let e: Value = panic_to_value(_try_err);
// reject the flight - waiters throw and the next caller re-leads.
// no rethrow here, the trailing suspension point rethrows to this
// caller AND attaches the handler an alone leader needs
client.reject(&[e, messageHash.clone()]);
}
crate::exchange_stubs::ws_await_flight(&future).await;
}
Value::Null
}
pub async fn keep_alive_listen_key(&mut self, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
// https://binance-docs.github.io/apidocs/spot/en/#listen-key-spot
let mut type_var: Value = self.safe_string2(self.options.clone(), Value::Str("defaultType".into()), Value::Str("authenticate".into()), &[Value::Str("spot".into())]);
type_var = self.safe_string_k(params.clone(), "type", &[type_var.clone()]);
let mut isPortfolioMargin: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params2(params.clone(), Value::Str("keepAliveListenKey".into()), Value::Str("papi".into()), Value::Str("portfolioMargin".into()), &[Value::Bool(false)]); isPortfolioMargin = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut subTypeInfo: Value = self.handle_sub_type_and_params(Value::Str("keepAliveListenKey".into()), &[Value::Null, params.clone()]);
let mut subType: Value = subTypeInfo.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null);
if (type_var.as_str() != Some("option")) && (type_var.as_str() != Some("stock")) {
// guard options first: isLinear returns true for linear-settled options (subType='linear')
// which would incorrectly convert type='option' to 'future'.
// stock needs the same exemption: with a defaultSubType of 'linear' -
// always on binanceusdm, common on mixed instances - isLinear keys off
// subType alone and would flip 'stock' to 'future' - the stock branch
// below would never run, and the bucket lookup would renew the
// FUTURES listen key while the stock key silently expires
if is_true(&self.parent.is_linear(type_var.clone(), &[subType.clone()])) {
type_var = Value::Str("future".into());
} else if is_true(&self.parent.is_inverse(type_var.clone(), &[subType])) {
type_var = Value::Str("delivery".into());
}
}
// For margin, token renewal is handled by renewListenToken method
if (type_var.as_str() == Some("margin")) {
return Value::Null;
}
let mut isStock: bool = type_var.as_str() == Some("stock");
let mut options: Value = self.safe_dict(self.options.clone(), type_var.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut listenKey: Value = self.safe_string_k(options.clone(), "listenKey", &[]);
if (listenKey == Value::Null) {
return Value::Null;
}
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
params = self.omit(params.clone(), Value::from(vec![Value::Str("type".into()), Value::Str("symbol".into())]), &[]);
let mut time: Value = self.milliseconds();
let _try_result = futures::FutureExt::catch_unwind(std::panic::AssertUnwindSafe(async {
if isStock {
// the equity endpoint is create-or-renew: with an active key this
// POST extends the validity of that same key
let mut requestParams: Value = self.omit(params.clone(), Value::from(vec![Value::Str("stock".into()), Value::Str("name".into()), Value::Str("callerMethodName".into()), Value::Str("subType".into()), Value::Str("timeframe".into())]), &[]);
self.parent.sapi_post_equity_listen_key(&[requestParams]).await;
} else if is_true(&isPortfolioMargin) {
let __ws_arg_15 = self.extend(request.clone(), &[params.clone()]);
self.parent.papi_put_listen_key(&[__ws_arg_15]).await;
params = self.extend(params.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("portfolioMargin".to_string(), Value::Bool(true));
m
})]);
} else if (type_var.as_str() == Some("future")) {
let __ws_arg_16 = self.extend(request.clone(), &[params.clone()]);
self.parent.fapi_private_put_listen_key(&[__ws_arg_16]).await;
} else if (type_var.as_str() == Some("delivery")) {
let __ws_arg_17 = self.extend(request.clone(), &[params.clone()]);
self.parent.dapi_private_put_listen_key(&[__ws_arg_17]).await;
} else if (type_var.as_str() == Some("option")) {
let __ws_arg_18 = self.extend(request.clone(), &[params.clone()]);
self.parent.eapi_private_put_listen_key(&[__ws_arg_18]).await;
} else {
if let Value::Dict(__d) = &mut request { std::sync::Arc::make_mut(__d).insert("listenKey".into(), listenKey.clone()); }
let __ws_arg_19 = self.extend(request, &[params.clone()]);
self.parent.public_put_user_data_stream(&[__ws_arg_19]).await;
}
#[allow(unreachable_code)] { Value::Null }})).await;
if let Err(_try_err) = _try_result { let error: Value = panic_to_value(_try_err);
let mut url: Value = Value::Null;
if isStock {
// the stock user stream lives on a fixed url and subscribes to
// listenKey@orderReport, so the client is addressable without the key
url = self.get_stock_ws_url(&[Value::Str("user".into())]);
} else {
let mut urlType: Value = type_var.clone();
if is_true(&isPortfolioMargin) {
urlType = Value::Str("papi".into());
}
if (type_var.as_str() == Some("option")) {
urlType = Value::Str("optionPrivate".into());
}
let mut cachedListenKey: Value = crate::value::get_value_k(&get_value(&self.options, &type_var), "listenKey");
url = self.get_private_ws_url(urlType, cachedListenKey);
}
let mut client: Value = self.client(&[url]);
let mut messageHashes: Value = object_keys(&get_value(&client, &Value::Str("futures".into())));
{
let mut i: Value = Value::Int(0);
let mut __for_first_73: bool = true;
while { if !__for_first_73 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_73 = false; i.as_f64().unwrap_or(f64::NAN) < ((messageHashes.len() as i64) as f64) } {
let mut messageHash: Value = messageHashes.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
client.reject(&[Value::from(error.clone()), messageHash.clone()]);
}
}
{ let __be_tmp = self.extend(options.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("listenKey".to_string(), Value::Null);
m.insert("lastAuthenticatedTime".to_string(), Value::Int(0));
m
})]); if let Value::Dict(__d) = &mut self.options { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&type_var), __be_tmp); } }
return Value::Null;
}
{ let __be_tmp = self.extend(options.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("listenKey".to_string(), listenKey);
m.insert("lastAuthenticatedTime".to_string(), time);
m
})]); if let Value::Dict(__d) = &mut self.options { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&type_var), __be_tmp); } }
// whether or not to schedule another listenKey keepAlive request
let mut clients: Value = object_values(&self.clients);
let mut refreshRateKey: Value = (if isStock { Value::Str("stockListenKeyRefreshRate".into()) } else { Value::Str("listenKeyRefreshRate".into()) });
let mut listenKeyRefreshRate: Value = self.safe_integer(self.options.clone(), refreshRateKey, &[Value::Int(1200000)]);
let mut delayParams: Value = params.clone();
if isStock {
// params had type omitted above - restore it so the next cycle routes back here
delayParams = self.extend(params, &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), Value::Str("stock".into()));
m
})]);
}
{
let mut i: Value = Value::Int(0);
let mut __for_first_75: bool = true;
while { if !__for_first_75 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_75 = false; i.as_f64().unwrap_or(f64::NAN) < ((clients.len() as i64) as f64) } {
let mut client: Value = clients.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut clientSubscriptions: Value = self.safe_dict_k(client.clone(), "subscriptions", &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut subscriptionKeys: Value = object_keys(&clientSubscriptions);
{
let mut j: Value = Value::Int(0);
let mut __for_first_74: bool = true;
while { if !__for_first_74 { j = (match (&(j), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_74 = false; j.as_f64().unwrap_or(f64::NAN) < ((subscriptionKeys.len() as i64) as f64) } {
let mut subscribeType: Value = subscriptionKeys.as_array().and_then(|__arr| match &j { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
if (subscribeType.as_str() == type_var.as_str()) {
self.delay(listenKeyRefreshRate.clone(), &[Value::Str("keep_alive_listen_key".into()).clone(), delayParams.clone()]).await;
return Value::Null;
}
}
}
}
}
Value::Null
}
pub fn set_balance_cache(&mut self, mut client: Value, mut type_var: Value, optional_args: &[Value]) {
let mut isPortfolioMargin = get_arg(optional_args, 0, Value::Bool(false));
if (in_op(&get_value(&client, &Value::Str("subscriptions".into())), &type_var)) && (in_op(&self.balance, &type_var)) {
return;
}
let mut options: Value = self.safe_dict_k(self.options.clone(), "watchBalance", &[]);
let mut fetchBalanceSnapshot: Value = self.safe_bool_k(options, "fetchBalanceSnapshot", &[Value::Bool(false)]);
if (fetchBalanceSnapshot.as_bool() == Some(true)) {
let mut messageHash: Value = add(&type_var, &Value::Str(":fetchBalanceSnapshot".into()));
if !(in_op(&get_value(&client, &Value::Str("futures".into())), &messageHash)) {
client.future(&[messageHash.clone()]);
self.spawn(&[Value::Str("load_balance_snapshot".into()).clone(), client.clone(), messageHash.clone(), type_var.clone(), isPortfolioMargin]);
}
} else {
if let Value::Dict(__d) = &mut self.balance { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&type_var), Value::Map({
let mut m = indexmap::IndexMap::new();
m
})); }
}
}
pub async fn load_balance_snapshot(&mut self, mut client: Value, mut messageHash: Value, mut type_var: Value, mut isPortfolioMargin: Value) -> Value {
let mut params: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), type_var.clone());
m
});
if is_equal(&isPortfolioMargin, &Value::Bool(true)) {
if let Value::Dict(__d) = &mut params { std::sync::Arc::make_mut(__d).insert("portfolioMargin".into(), Value::Bool(true)); }
}
let mut response: Value = self.fetch_balance(&[params]).await;
let __ws_arg_20 = self.safe_dict(self.balance.clone(), type_var.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
{ let __be_tmp = self.extend(response, &[__ws_arg_20]); if let Value::Dict(__d) = &mut self.balance { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&type_var), __be_tmp); } }
// don't remove the future from the .futures cache
if (in_op(&get_value(&client, &Value::Str("futures".into())), &messageHash)) {
let mut future: Value = get_value(&get_value(&client, &Value::Str("futures".into())), &messageHash);
future.resolve(&[]);
client.resolve(&[get_value(&self.balance, &type_var), add(&type_var, &Value::Str(":balance".into()))]);
}
Value::Null
}
/*
* @method
* @name binance#fetchBalanceWs
* @description fetch balance and get the amount of funds available for trading or funds locked in orders
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/account/websocket-api/Futures-Account-Balance
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/account-requests#account-information-user_data
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/account/websocket-api
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string|undefined} [params.type] 'future', 'delivery', 'savings', 'funding', or 'spot'
* @param {string|undefined} [params.marginMode] 'cross' or 'isolated', for margin trading, uses this.options.defaultMarginMode if not passed, defaults to undefined/None/null
* @param {string[]|undefined} [params.symbols] unified market symbols, only used in isolated margin mode
* @param {string|undefined} [params.method] method to use. Can be account.balance, account.status, v2/account.balance or v2/account.status
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/?id=balance-structure}
*/
pub async fn fetch_balance_ws(&mut self, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut type_var: Value = self.get_market_type(Value::Str("fetchBalanceWs".into()), Value::Null, &[params.clone()]);
if (type_var.as_str() != Some("spot")) && (type_var.as_str() != Some("future")) && (type_var.as_str() != Some("delivery")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchBalanceWs only supports spot or swap markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &type_var);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchBalanceWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("returnRateLimits".to_string(), returnRateLimits);
m
});
let mut method: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchBalanceWs".into()), Value::Str("method".into()), &[Value::Str("account.status".into())]); method = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), method.clone());
let __ws_arg_21 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_21]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), (if (method.as_str() == Some("account.status")) { Value::Str("handle_account_status_ws".into()).clone() } else { Value::Str("handle_balance_ws".into()).clone() }));
m
});
return self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
Value::Null
}
pub fn handle_balance_ws(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
//
let mut messageHash: Value = (match message.get("id") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut rawBalance: Value = Value::Null;
if (matches!(&message.get("result").cloned().unwrap_or(Value::Null), Value::Arr(_))) {
// account.balance
rawBalance = (match message.get("result") { Some(__v) if matches!(__v, Value::Arr(_)) => __v.clone(), _ => Value::from(vec![]) });
} else {
// account.status
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
rawBalance = self.safe_list_k(result, "assets", &[Value::from(vec![])]);
}
let mut parsedBalances: Value = self.parent.parse_balance_custom(rawBalance, &[]);
client.resolve(&[parsedBalances, messageHash.clone()]);
}
pub fn handle_account_status_ws(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// spot
// {
// "id": "605a6d20-6588-4cb9-afa0-b0ab087507ba",
// "status": 200,
// "result": {
// "makerCommission": 15,
// "takerCommission": 15,
// "buyerCommission": 0,
// "sellerCommission": 0,
// "canTrade": true,
// "canWithdraw": true,
// "canDeposit": true,
// "commissionRates": {
// "maker": "0.00150000",
// "taker": "0.00150000",
// "buyer": "0.00000000",
// "seller": "0.00000000"
// },
// "brokered": false,
// "requireSelfTradePrevention": false,
// "updateTime": 1660801833000,
// "accountType": "SPOT",
// "balances": [{
// "asset": "BNB",
// "free": "0.00000000",
// "locked": "0.00000000"
// },
// {
// "asset": "BTC",
// "free": "1.3447112",
// "locked": "0.08600000"
// },
// {
// "asset": "USDT",
// "free": "1021.21000000",
// "locked": "0.00000000"
// }
// ],
// "permissions": [
// "SPOT"
// ]
// }
// }
// swap
//
let mut messageHash: Value = (match message.get("id") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut parsedBalances: Value = self.parent.parse_balance_custom(result, &[]);
client.resolve(&[parsedBalances, messageHash.clone()]);
}
/*
* @method
* @name binance#fetchPositionWs
* @description fetch data on an open position
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Position-Information
* @param {string} symbol unified market symbol of the market the position is held in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [position structure]{@link https://docs.ccxt.com/?id=position-structure}
*/
pub async fn fetch_position_ws(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
return self.fetch_positions_ws(&[Value::from(vec![symbol.clone()]), params]).await;
Value::Null
}
/*
* @method
* @name binance#fetchPositionsWs
* @description fetch all open positions
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Position-Information
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/trade/websocket-api/Position-Information
* @param {string[]} [symbols] list of unified market symbols
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.returnRateLimits] set to true to return rate limit informations, defaults to false.
* @param {string|undefined} [params.method] method to use. Can be account.position or v2/account.position
* @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/?id=position-structure}
*/
pub async fn fetch_positions_ws(&mut self, optional_args: &[Value]) -> Value {
let mut symbols = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
let mut market: Value = Value::Null;
symbols = self.market_symbols(&[symbols.clone(), Value::Str("swap".into()), Value::Bool(true), Value::Bool(true), Value::Bool(true)]);
if (symbols != Value::Null) {
let mut symbolsLength: f64 = ((symbols.len() as i64) as f64);
if (symbolsLength == 1.0) {
market = self.market(symbols.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null));
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("symbol".into(), market.as_map().and_then(|__m| __m.get("id")).cloned().unwrap_or(Value::Null)); }
}
}
let mut type_var: Value = self.get_market_type(Value::Str("fetchPositionsWs".into()), market, &[params.clone()]);
if (symbols == Value::Null) && (type_var.as_str() == Some("spot")) {
// when symbols aren't provide
// we shouldn't rely on the defaultType
type_var = Value::Str("future".into());
}
if (type_var.as_str() != Some("future")) && (type_var.as_str() != Some("delivery")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchPositionsWs only supports swap markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &type_var);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchPositionsWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("returnRateLimits".into(), returnRateLimits); }
let mut method: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchPositionsWs".into()), Value::Str("method".into()), &[Value::Str("account.position".into())]); method = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), method);
let __ws_arg_22 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_22]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_positions_ws".into()).clone());
m
});
let mut result: Value = self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
return self.filter_by_array_positions(result, Value::Str("symbol".into()), &[symbols, Value::Bool(false)]);
Value::Null
}
pub fn handle_positions_ws(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// id: '1',
// status: 200,
// result: [
// {
// symbol: 'BTCUSDT',
// positionAmt: '-0.014',
// entryPrice: '42901.1',
// breakEvenPrice: '30138.83333142',
// markPrice: '71055.98470333',
// unRealizedProfit: '-394.16838584',
// liquidationPrice: '137032.02272908',
// leverage: '123',
// maxNotionalValue: '50000',
// marginType: 'cross',
// isolatedMargin: '0.00000000',
// isAutoAddMargin: 'false',
// positionSide: 'BOTH',
// notional: '-994.78378584',
// isolatedWallet: '0',
// updateTime: 1708906343111,
// isolated: false,
// adlQuantile: 2
// },
// ...
// ]
// }
//
//
let mut messageHash: Value = (match message.get("id") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Arr(_)) => __v.clone(), _ => Value::from(vec![]) });
let mut positions: Value = Value::from(vec![]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_76: bool = true;
while { if !__for_first_76 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_76 = false; i.as_f64().unwrap_or(f64::NAN) < ((result.len() as i64) as f64) } {
let mut parsed: Value = self.parent.parse_position_risk(result.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null), &[]);
let mut entryPrice: Option<String> = self.safe_string_k(parsed.clone(), "entryPrice", &[]).as_str().map(str::to_owned);
if (entryPrice.as_deref() != Some("0")) && (entryPrice.as_deref() != Some("0.0")) && (entryPrice.as_deref() != Some("0.00000000")) {
append_to_array(&mut positions, parsed);
}
}
}
client.resolve(&[positions, messageHash.clone()]);
}
/*
* @method
* @name binance#watchBalance
* @description watch balance and get the amount of funds available for trading or funds locked in orders
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.portfolioMargin] set to true if you would like to watch the balance of a portfolio margin account
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/?id=balance-structure}
*/
pub async fn watch_balance(&mut self, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
// derive BEFORE authenticating and pass the result in: authenticate
// re-derives from its own method scope, so without this a method-scoped
// options.watchBalance.type seeds one bucket while the read below
// indexes another - the same derive-first shape watchOrders uses
let mut type_var: Value = Value::Null;
let mut subType: Value = Value::Null;
{ let __destr_tmp = self.resolve_auth_type(Value::Str("watchBalance".into()), &[Value::Null, params.clone()]); type_var = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); subType = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(2)).cloned().unwrap_or(Value::Null); }
let __ws_arg_23 = self.extend(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), type_var.clone());
m.insert("subType".to_string(), subType);
m
}), &[params.clone()]);
self.authenticate(&[__ws_arg_23]).await;
let mut isPortfolioMargin: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params2(params.clone(), Value::Str("watchBalance".into()), Value::Str("papi".into()), Value::Str("portfolioMargin".into()), &[Value::Bool(false)]); isPortfolioMargin = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut url: Value = Value::Str("".into());
let mut urlType: Value = type_var.clone();
if (type_var.as_str() == Some("spot")) || (type_var.as_str() == Some("margin")) {
// route to WebSocket API connection where the user data stream is subscribed
url = crate::value::get_value_k(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), "spot");
} else {
if is_true(&isPortfolioMargin) {
urlType = Value::Str("papi".into());
} else if (type_var.as_str() == Some("option")) {
let mut demoMode: Value = self.safe_bool_k(self.options.clone(), "enableDemoTrading", &[Value::Bool(false)]);
if (demoMode.as_bool() == Some(true)) || is_true(&self.isSandboxModeEnabled) {
panic!("{}", crate::exchange_errors::not_supported(format!("{}{}", self.id.clone(), Value::Str(" watchBalance() does not support option markets in demo/testnet mode".into()))));
}
urlType = Value::Str("optionPrivate".into());
}
url = self.get_private_ws_url(urlType, crate::value::get_value_k(&get_value(&self.options, &type_var), "listenKey"));
}
let mut client: Value = self.client(&[url.clone()]);
self.set_balance_cache(client.clone(), type_var.clone(), &[isPortfolioMargin.clone()]);
self.set_positions_cache(client.clone(), type_var.clone(), &[Value::Null, isPortfolioMargin]);
let mut options: Value = self.safe_dict_k(self.options.clone(), "watchBalance", &[]);
let mut fetchBalanceSnapshot: Value = self.safe_bool_k(options.clone(), "fetchBalanceSnapshot", &[Value::Bool(false)]);
let mut awaitBalanceSnapshot: Value = self.safe_bool_k(options, "awaitBalanceSnapshot", &[Value::Bool(true)]);
if (fetchBalanceSnapshot.as_bool() == Some(true)) && (awaitBalanceSnapshot.as_bool() == Some(true)) {
crate::exchange_stubs::ws_await_flight(&client.future(&[Value::Str(format!("{}{}", type_var, Value::Str(":fetchBalanceSnapshot".into())).into())])).await;
}
let mut messageHash: Value = Value::Str(format!("{}{}", type_var, Value::Str(":balance".into())).into());
let mut message: Value = Value::Null;
return self.watch(url, messageHash.clone(), &[message.clone(), type_var]).await;
Value::Null
}
pub fn handle_balance(&mut self, mut client: Value, mut message: Value) {
//
// sent upon a balance update not related to orders
//
// {
// "e": "balanceUpdate",
// "E": 1629352505586,
// "a": "IOTX",
// "d": "0.43750000",
// "T": 1629352505585
// }
//
// sent upon creating or filling an order
//
// {
// "e": "outboundAccountPosition", // Event type
// "E": 1564034571105, // Event Time
// "u": 1564034571073, // Time of last account update
// "B": [ // Balances Array
// {
// "a": "ETH", // Asset
// "f": "10000.000000", // Free
// "l": "0.000000" // Locked
// }
// ]
// }
//
// future/delivery
//
// {
// "e": "ACCOUNT_UPDATE", // Event Type
// "E": 1564745798939, // Event Time
// "T": 1564745798938 , // Transaction
// "i": "SfsR", // Account Alias
// "a": { // Update Data
// "m":"ORDER", // Event reason type
// "B":[ // Balances
// {
// "a":"BTC", // Asset
// "wb":"122624.12345678", // Wallet Balance
// "cw":"100.12345678" // Cross Wallet Balance
// },
// ],
// "P":[
// {
// "s":"BTCUSD_200925", // Symbol
// "pa":"0", // Position Amount
// "ep":"0.0", // Entry Price
// "cr":"200", // (Pre-fee) Accumulated Realized
// "up":"0", // Unrealized PnL
// "mt":"isolated", // Margin Type
// "iw":"0.00000000", // Isolated Wallet (if isolated position)
// "ps":"BOTH" // Position Side
// },
// ]
// }
// }
// externalLockUpdate
// {
// "e": "externalLockUpdate", // Event Type
// "E": 1581557507324, // Event Time
// "a": "NEO", // Asset
// "d": "10.00000000", // Delta
// "T": 1581557507268 // Transaction Time
// }
//
let mut wallet: Value = self.safe_string_k(self.options.clone(), "wallet", &[Value::Str("wb".into())]); // cw for cross wallet
// each account is connected to a different endpoint
let mut subscriptions: Value = get_value(&client, &Value::Str("subscriptions".into()));
let mut subscriptionsKeys: Value = object_keys(&subscriptions);
let mut accountType: Value = self.get_account_type_from_subscriptions(subscriptionsKeys).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
let mut messageHash: Value = Value::Str(format!("{}{}", accountType, Value::Str(":balance".into())).into());
if (get_value(&self.balance, &accountType) == Value::Null) {
if let Value::Dict(__d) = &mut self.balance { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&accountType), Value::Map({
let mut m = indexmap::IndexMap::new();
m
})); }
}
add_element_to_object(get_value_mut(&mut self.balance, &accountType), &Value::Str("info".into()), message.clone());
let mut event: Option<String> = self.safe_string_k(message.clone(), "e", &[]).as_str().map(str::to_owned);
if (event.as_deref() == Some("balanceUpdate")) {
let mut currencyId: Value = self.safe_string_k(message.clone(), "a", &[]);
let mut code: Value = self.safe_currency_code(currencyId.clone(), &[]);
let mut account: Value = self.account();
let mut delta: Value = self.safe_string_k(message.clone(), "d", &[]);
if (accountType != Value::Null) && (code != Value::Null) && (in_op(&get_value(&self.balance, &accountType), &code)) {
let mut previousValue: Value = crate::value::get_value_k(&get_value(&get_value(&self.balance, &accountType), &code), "free");
if !matches!(&previousValue, Value::Str(_)) {
previousValue = self.number_to_string(previousValue.clone());
}
add_element_to_object(&mut account, &Value::Str("free".into()), crate::precise::Precise::stringAdd(&previousValue, &delta));
} else {
add_element_to_object(&mut account, &Value::Str("free".into()), delta);
}
if (accountType != Value::Null) && (code != Value::Null) {
add_element_to_object(get_value_mut(&mut self.balance, &accountType), &code, account.clone());
}
} else {
message = self.safe_dict_k(message.clone(), "a", &[message.clone()]);
let mut B: Value = self.safe_list_k(message.clone(), "B", &[]);
if (B == Value::Null) {
return;
}
{
let mut i: Value = Value::Int(0);
let mut __for_first_77: bool = true;
while { if !__for_first_77 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_77 = false; i.as_f64().unwrap_or(f64::NAN) < ((B.len() as i64) as f64) } {
let mut entry: Value = B.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut currencyId: Value = self.safe_string_k(entry.clone(), "a", &[]);
let mut code: Value = self.safe_currency_code(currencyId, &[]);
let mut account: Value = self.account();
add_element_to_object(&mut account, &Value::Str("free".into()), self.safe_string_k(entry.clone(), "f", &[]));
add_element_to_object(&mut account, &Value::Str("used".into()), self.safe_string_k(entry.clone(), "l", &[]));
add_element_to_object(&mut account, &Value::Str("total".into()), self.safe_string(entry, wallet.clone(), &[]));
if (accountType != Value::Null) && (code != Value::Null) {
add_element_to_object(get_value_mut(&mut self.balance, &accountType), &code, account);
}
}
}
}
let mut timestamp: Value = self.safe_integer_k(message.clone(), "E", &[]);
add_element_to_object(get_value_mut(&mut self.balance, &accountType), &Value::Str("timestamp".into()), timestamp.clone());
{ let __be_tmp = self.iso8601(timestamp.clone()); add_element_to_object(get_value_mut(&mut self.balance, &accountType), &Value::Str("datetime".into()), __be_tmp); };
{ let __be_tmp = self.safe_balance(get_value(&self.balance, &accountType)); if let Value::Dict(__d) = &mut self.balance { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&accountType), __be_tmp); } }
client.resolve(&[get_value(&self.balance, &accountType), messageHash.clone()]);
}
pub fn get_account_type_from_subscriptions(&self, mut subscriptions: Value) -> Option<String> {
let mut accountType: Value = Value::Str("".into());
{
let mut i: Value = Value::Int(0);
let mut __for_first_78: bool = true;
while { if !__for_first_78 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_78 = false; i.as_f64().unwrap_or(f64::NAN) < ((subscriptions.len() as i64) as f64) } {
let mut subscription: Value = subscriptions.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
if (subscription.as_str() == Some("spot")) || (subscription.as_str() == Some("margin")) || (subscription.as_str() == Some("future")) || (subscription.as_str() == Some("delivery")) || (subscription.as_str() == Some("option")) {
accountType = subscription;
break;
}
}
}
return accountType.as_str().map(str::to_owned);
}
pub fn resolve_auth_type(&self, mut methodName: Value, optional_args: &[Value]) -> Value {
let mut market = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
// the single home for user-data type derivation: market type, subType,
// and the guarded linear/inverse rewrite. option and stock must keep
// their own type, or the listenKey bucket, the endpoint dispatch and
// the stream selection all silently degrade to futures - the guarded
// sites used to carry seven inline copies of this dance, and the
// unguarded copies were the bug class behind the option keepalive and
// stock keepalive fixes
let mut type_var: Value = Value::Null;
{ let __destr_tmp = self.handle_market_type_and_params(methodName.clone(), &[market.clone(), params.clone()]); type_var = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut subType: Value = Value::Null;
{ let __destr_tmp = self.handle_sub_type_and_params(methodName, &[market, params.clone()]); subType = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if (type_var.as_str() != Some("option")) && (type_var.as_str() != Some("stock")) {
if is_true(&self.parent.is_linear(type_var.clone(), &[subType.clone()])) {
type_var = Value::Str("future".into());
} else if is_true(&self.parent.is_inverse(type_var.clone(), &[subType.clone()])) {
type_var = Value::Str("delivery".into());
}
}
return Value::from(vec![type_var, subType, params]);
Value::Null
}
pub fn get_market_type(&self, mut method: Value, mut market: Value, optional_args: &[Value]) -> Value {
let mut params = get_arg(optional_args, 0, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let mut type_var: Value = Value::Null;
{ let __destr_tmp = self.handle_market_type_and_params(method.clone(), &[market.clone(), params.clone()]); type_var = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut subType: Value = Value::Null;
{ let __destr_tmp = self.handle_sub_type_and_params(method, &[market, params.clone()]); subType = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if is_true(&self.parent.is_linear(type_var.clone(), &[subType.clone()])) {
type_var = Value::Str("future".into());
} else if is_true(&self.parent.is_inverse(type_var.clone(), &[subType])) {
type_var = Value::Str("delivery".into());
}
return type_var;
Value::Null
}
/*
* @method
* @name binance#createOrderWs
* @description create a trade order
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/trading-requests#place-new-order-trade
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/New-Order
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/trade/websocket-api
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/New-Algo-Order
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market' or 'limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much of currency you want to trade in units of base currency
* @param {float|undefined} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} params.test test order, default false
* @param {boolean} params.returnRateLimits set to true to return rate limit information, default false
* @returns {object} an [order structure]{@link https://docs.ccxt.com/?id=order-structure}
*/
pub async fn create_order_ws(&mut self, mut symbol: Value, mut type_var: Value, mut side: Value, mut amount: Value, optional_args: &[Value]) -> Value {
let mut price = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol.clone());
let mut marketType: Value = self.get_market_type(Value::Str("createOrderWs".into()), market.clone(), &[params.clone()]);
if (marketType.as_str() != Some("spot")) && (marketType.as_str() != Some("future")) && (marketType.as_str() != Some("delivery")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" createOrderWs only supports spot or swap markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &marketType);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut sor: Value = self.safe_bool2(params.clone(), Value::Str("sor".into()), Value::Str("SOR".into()), &[Value::Bool(false)]);
params = self.omit(params.clone(), Value::Str("sor".into()), &[Value::Str("SOR".into())]);
let mut triggerPrice: Value = self.safe_string2(params.clone(), Value::Str("triggerPrice".into()), Value::Str("stopPrice".into()), &[]);
let mut stopLossPrice: Option<String> = self.safe_string_k(params.clone(), "stopLossPrice", &[triggerPrice.clone()]).as_str().map(str::to_owned);
let mut takeProfitPrice: Option<String> = self.safe_string_k(params.clone(), "takeProfitPrice", &[]).as_str().map(str::to_owned);
let mut trailingDelta: Option<String> = self.safe_string_k(params.clone(), "trailingDelta", &[]).as_str().map(str::to_owned);
let mut trailingPercent: Option<String> = self.safe_string_n(params.clone(), Value::from(vec![Value::Str("trailingPercent".into()), Value::Str("callbackRate".into()), Value::Str("trailingDelta".into())]), &[]).as_str().map(str::to_owned);
let mut isTrailingPercentOrder: bool = trailingPercent.is_some();
let mut isStopLoss: bool = (stopLossPrice.is_some()) || (trailingDelta.is_some());
let mut isTakeProfit: bool = takeProfitPrice.is_some();
let mut isTriggerOrder: bool = triggerPrice != Value::Null;
let mut isConditional: bool = isTriggerOrder || isTrailingPercentOrder || isStopLoss || isTakeProfit;
let mut payload: Value = self.parent.create_order_request(symbol.clone(), type_var, side, amount, &[price, params.clone()]);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("createOrderWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
add_element_to_object(&mut payload, &Value::Str("returnRateLimits".into()), returnRateLimits);
let mut test: Value = self.safe_bool_k(params.clone(), "test", &[Value::Bool(false)]);
params = self.omit(params.clone(), Value::Str("test".into()), &[]);
if (market.as_map().and_then(|__m| __m.get("linear")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) && (market.as_map().and_then(|__m| __m.get("swap")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) && isConditional {
add_element_to_object(&mut payload, &Value::Str("algoType".into()), Value::Str("CONDITIONAL".into()));
}
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), Value::Str("order.place".into()));
let __ws_arg_24 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_24]));
m
});
if (test.as_bool() == Some(true)) {
if (sor.as_bool() == Some(true)) {
if let Value::Dict(__d) = &mut message { std::sync::Arc::make_mut(__d).insert("method".into(), Value::Str("sor.order.test".into())); }
} else {
if let Value::Dict(__d) = &mut message { std::sync::Arc::make_mut(__d).insert("method".into(), Value::Str("order.test".into())); }
}
}
if (market.as_map().and_then(|__m| __m.get("linear")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) && (market.as_map().and_then(|__m| __m.get("swap")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) && isConditional {
if let Value::Dict(__d) = &mut message { std::sync::Arc::make_mut(__d).insert("method".into(), Value::Str("algoOrder.place".into())); }
}
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_order_ws".into()).clone());
m
});
return self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
Value::Null
}
pub fn handle_order_ws(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "id": 1,
// "status": 200,
// "result": {
// "symbol": "BTCUSDT",
// "orderId": 7663053,
// "orderListId": -1,
// "clientOrderId": "x-R4BD3S82d8959d0f5114499487a614",
// "transactTime": 1687642291434,
// "price": "25000.00000000",
// "origQty": "0.00100000",
// "executedQty": "0.00000000",
// "cummulativeQuoteQty": "0.00000000",
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// "workingTime": 1687642291434,
// "fills": [],
// "selfTradePreventionMode": "NONE"
// },
// "rateLimits": [
// {
// "rateLimitType": "ORDERS",
// "interval": "SECOND",
// "intervalNum": 10,
// "limit": 50,
// "count": 1
// },
// {
// "rateLimitType": "ORDERS",
// "interval": "DAY",
// "intervalNum": 1,
// "limit": 160000,
// "count": 1
// },
// {
// "rateLimitType": "REQUEST_WEIGHT",
// "interval": "MINUTE",
// "intervalNum": 1,
// "limit": 1200,
// "count": 12
// }
// ]
// }
//
let mut messageHash: Value = (match message.get("id") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut order: Value = self.parse_order(result, &[]);
client.resolve(&[order, messageHash.clone()]);
}
pub fn handle_orders_ws(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "id": 1,
// "status": 200,
// "result": [{
// "symbol": "BTCUSDT",
// "orderId": 7665584,
// "orderListId": -1,
// "clientOrderId": "x-R4BD3S82b54769abdd3e4b57874c52",
// "price": "26000.00000000",
// "origQty": "0.00100000",
// "executedQty": "0.00000000",
// "cummulativeQuoteQty": "0.00000000",
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// "stopPrice": "0.00000000",
// "icebergQty": "0.00000000",
// "time": 1687642884646,
// "updateTime": 1687642884646,
// "isWorking": true,
// "workingTime": 1687642884646,
// "origQuoteOrderQty": "0.00000000",
// "selfTradePreventionMode": "NONE"
// },
// ...
// ],
// "rateLimits": [{
// "rateLimitType": "REQUEST_WEIGHT",
// "interval": "MINUTE",
// "intervalNum": 1,
// "limit": 1200,
// "count": 14
// }]
// }
//
let mut messageHash: Value = (match message.get("id") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Arr(_)) => __v.clone(), _ => Value::from(vec![]) });
let mut orders: Value = self.parse_orders(result, &[]);
client.resolve(&[orders, messageHash.clone()]);
}
/*
* @method
* @name binance#editOrderWs
* @description edit a trade order
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/trading-requests#cancel-and-replace-order-trade
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Modify-Order
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/trade/websocket-api/Modify-Order
* @param {string} id order id
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market' or 'limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much of the currency you want to trade in units of the base currency
* @param {float|undefined} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [order structure]{@link https://docs.ccxt.com/?id=order-structure}
*/
pub async fn edit_order_ws(&mut self, mut id: Value, mut symbol: Value, mut type_var: Value, mut side: Value, optional_args: &[Value]) -> Value {
let mut amount = get_arg(optional_args, 0, Value::Null);
let mut price = get_arg(optional_args, 1, Value::Null);
let mut params = get_arg(optional_args, 2, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol.clone());
let mut marketType: Value = self.get_market_type(Value::Str("editOrderWs".into()), market, &[params.clone()]);
if (marketType.as_str() != Some("spot")) && (marketType.as_str() != Some("future")) && (marketType.as_str() != Some("delivery")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" editOrderWs only supports spot or swap markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &marketType);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut isSwap: bool = (marketType.as_str() == Some("future")) || (marketType.as_str() == Some("delivery"));
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
if (marketType.as_str() == Some("spot")) {
payload = self.parent.edit_spot_order_request(id.clone(), symbol.clone(), type_var.clone(), side.clone(), amount.clone(), &[price.clone(), params.clone()]);
} else {
payload = self.parent.edit_contract_order_request(id.clone(), symbol.clone(), type_var, side, amount, &[price, params.clone()]);
}
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("editOrderWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
add_element_to_object(&mut payload, &Value::Str("returnRateLimits".into()), returnRateLimits);
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), (if (isSwap) { Value::Str("order.modify".into()) } else { Value::Str("order.cancelReplace".into()) }));
let __ws_arg_25 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_25]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_edit_order_ws".into()).clone());
m
});
return self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
Value::Null
}
pub fn handle_edit_order_ws(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// spot
// {
// "id": 1,
// "status": 200,
// "result": {
// "cancelResult": "SUCCESS",
// "newOrderResult": "SUCCESS",
// "cancelResponse": {
// "symbol": "BTCUSDT",
// "origClientOrderId": "x-R4BD3S82813c5d7ffa594104917de2",
// "orderId": 7665177,
// "orderListId": -1,
// "clientOrderId": "mbrnbQsQhtCXCLY45d5q7S",
// "price": "26000.00000000",
// "origQty": "0.00100000",
// "executedQty": "0.00000000",
// "cummulativeQuoteQty": "0.00000000",
// "status": "CANCELED",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// "selfTradePreventionMode": "NONE"
// },
// "newOrderResponse": {
// "symbol": "BTCUSDT",
// "orderId": 7665584,
// "orderListId": -1,
// "clientOrderId": "x-R4BD3S82b54769abdd3e4b57874c52",
// "transactTime": 1687642884646,
// "price": "26000.00000000",
// "origQty": "0.00100000",
// "executedQty": "0.00000000",
// "cummulativeQuoteQty": "0.00000000",
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// "workingTime": 1687642884646,
// "fills": [],
// "selfTradePreventionMode": "NONE"
// }
// },
// "rateLimits": [{
// "rateLimitType": "ORDERS",
// "interval": "SECOND",
// "intervalNum": 10,
// "limit": 50,
// "count": 1
// },
// {
// "rateLimitType": "ORDERS",
// "interval": "DAY",
// "intervalNum": 1,
// "limit": 160000,
// "count": 3
// },
// {
// "rateLimitType": "REQUEST_WEIGHT",
// "interval": "MINUTE",
// "intervalNum": 1,
// "limit": 1200,
// "count": 12
// }
// ]
// }
// swap
// {
// "id":"1",
// "status":200,
// "result":{
// "orderId":667061487,
// "symbol":"LTCUSDT",
// "status":"NEW",
// "clientOrderId":"x-xcKtGhcu91a74c818749ee42c0f70",
// "price":"82.00",
// "avgPrice":"0.00",
// "origQty":"1.000",
// "executedQty":"0.000",
// "cumQty":"0.000",
// "cumQuote":"0.00000",
// "timeInForce":"GTC",
// "type":"LIMIT",
// "reduceOnly":false,
// "closePosition":false,
// "side":"BUY",
// "positionSide":"BOTH",
// "stopPrice":"0.00",
// "workingType":"CONTRACT_PRICE",
// "priceProtect":false,
// "origType":"LIMIT",
// "priceMatch":"NONE",
// "selfTradePreventionMode":"NONE",
// "goodTillDate":0,
// "updateTime":1712918927511
// }
// }
//
let mut messageHash: Value = (match message.get("id") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut newSpotOrder: Value = self.safe_dict_k(result.clone(), "newOrderResponse", &[]);
let mut order: Value = Value::Null;
if (newSpotOrder != Value::Null) {
order = self.parse_order(newSpotOrder, &[]);
} else {
order = self.parse_order(result, &[]);
}
client.resolve(&[order, messageHash.clone()]);
}
/*
* @method
* @name binance#cancelOrderWs
* @description cancel multiple orders
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/trading-requests#cancel-order-trade
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Cancel-Order
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/trade/websocket-api/Cancel-Order
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Cancel-Algo-Order
* @param {string} id order id
* @param {string} [symbol] unified market symbol, default is undefined
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string|undefined} [params.cancelRestrictions] Supported values: ONLY_NEW - Cancel will succeed if the order status is NEW. ONLY_PARTIALLY_FILLED - Cancel will succeed if order status is PARTIALLY_FILLED.
* @param {boolean} [params.trigger] set to true if you would like to cancel a conditional order
* @returns {object} an list of [order structures]{@link https://docs.ccxt.com/?id=order-structure}
*/
pub async fn cancel_order_ws(&mut self, mut id: Value, optional_args: &[Value]) -> Value {
let mut symbol = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
if (symbol == Value::Null) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" cancelOrderWs requires a symbol".into()))));
}
let mut market: Value = self.market(symbol.clone());
let mut type_var: Value = self.get_market_type(Value::Str("cancelOrderWs".into()), market.clone(), &[params.clone()]);
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &type_var);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("cancelOrderWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), self.market_id(symbol.clone()));
m.insert("returnRateLimits".to_string(), returnRateLimits);
m
});
let mut isConditional: Value = self.safe_bool_n(params.clone(), Value::from(vec![Value::Str("stop".into()), Value::Str("trigger".into()), Value::Str("conditional".into())]), &[]);
let mut clientOrderId: Value = self.safe_string_n(params.clone(), Value::from(vec![Value::Str("clientAlgoId".into()), Value::Str("origClientOrderId".into()), Value::Str("clientOrderId".into())]), &[]);
let mut shouldUseAlgoOrder: bool = (market.as_map().and_then(|__m| __m.get("linear")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) && (market.as_map().and_then(|__m| __m.get("swap")).cloned().unwrap_or(Value::Null).as_bool() == Some(true)) && (isConditional.as_bool() == Some(true));
if (clientOrderId != Value::Null) {
if (shouldUseAlgoOrder) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("clientAlgoId".into(), clientOrderId.clone()); }
} else {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("origClientOrderId".into(), clientOrderId); }
}
} else {
if (shouldUseAlgoOrder) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("algoId".into(), self.number_to_string(id.clone())); }
} else {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("orderId".into(), self.number_to_string(id.clone())); }
}
}
params = self.omit(params.clone(), Value::from(vec![Value::Str("origClientOrderId".into()), Value::Str("clientOrderId".into()), Value::Str("stop".into()), Value::Str("trigger".into()), Value::Str("conditional".into())]), &[]);
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), Value::Str("order.cancel".into()));
let __ws_arg_26 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_26]));
m
});
if (shouldUseAlgoOrder) {
if let Value::Dict(__d) = &mut message { std::sync::Arc::make_mut(__d).insert("method".into(), Value::Str("algoOrder.cancel".into())); }
}
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_order_ws".into()).clone());
m
});
return self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
Value::Null
}
/*
* @method
* @name binance#cancelAllOrdersWs
* @description cancel all open orders in a market
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/trading-requests#cancel-open-orders-trade
* @param {string} [symbol] unified market symbol of the market to cancel orders in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure}
*/
pub async fn cancel_all_orders_ws(&mut self, optional_args: &[Value]) -> Value {
let mut symbol = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (symbol == Value::Null) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", self.id.clone(), Value::Str(" cancelAllOrdersWs() requires a symbol argument".into()))));
}
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol.clone());
let mut type_var: Value = self.get_market_type(Value::Str("cancelAllOrdersWs".into()), market, &[params.clone()]);
if (type_var.as_str() != Some("spot")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" cancelAllOrdersWs only supports spot markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &type_var);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("cancelAllOrdersWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), self.market_id(symbol.clone()));
m.insert("returnRateLimits".to_string(), returnRateLimits);
m
});
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), Value::Str("openOrders.cancelAll".into()));
let __ws_arg_27 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_27]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_orders_ws".into()).clone());
m
});
return self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
Value::Null
}
/*
* @method
* @name binance#fetchOrderWs
* @description fetches information on an order made by the user
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/trading-requests#query-order-user_data
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Query-Order
* @see https://developers.binance.com/docs/derivatives/coin-margined-futures/trade/websocket-api/Query-Order
* @param {string} id order id
* @param {string} [symbol] unified symbol of the market the order was made in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} An [order structure]{@link https://docs.ccxt.com/?id=order-structure}
*/
pub async fn fetch_order_ws(&mut self, mut id: Value, optional_args: &[Value]) -> Value {
let mut symbol = get_arg(optional_args, 0, Value::Null);
let mut params = get_arg(optional_args, 1, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
if (symbol == Value::Null) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" cancelOrderWs requires a symbol".into()))));
}
let mut market: Value = self.market(symbol.clone());
let mut type_var: Value = self.get_market_type(Value::Str("fetchOrderWs".into()), market, &[params.clone()]);
if (type_var.as_str() != Some("spot")) && (type_var.as_str() != Some("future")) && (type_var.as_str() != Some("delivery")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchOrderWs only supports spot or swap markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &type_var);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchOrderWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), self.market_id(symbol.clone()));
m.insert("returnRateLimits".to_string(), returnRateLimits);
m
});
let mut clientOrderId: Value = self.safe_string2(params.clone(), Value::Str("origClientOrderId".into()), Value::Str("clientOrderId".into()), &[]);
if (clientOrderId != Value::Null) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("origClientOrderId".into(), clientOrderId); }
} else {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("orderId".into(), self.number_to_string(id.clone())); }
}
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), Value::Str("order.status".into()));
let __ws_arg_28 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_28]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_order_ws".into()).clone());
m
});
return self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
Value::Null
}
/*
* @method
* @name binance#fetchOrdersWs
* @description fetches information on multiple orders made by the user
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/trading-requests#order-lists
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int|undefined} [since] the earliest time in ms to fetch orders for
* @param {int|undefined} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.orderId] order id to begin at
* @param {int} [params.startTime] earliest time in ms to retrieve orders for
* @param {int} [params.endTime] latest time in ms to retrieve orders for
* @param {int} [params.limit] the maximum number of order structures to retrieve
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure}
*/
pub async fn fetch_orders_ws(&mut self, optional_args: &[Value]) -> Value {
let mut symbol = get_arg(optional_args, 0, Value::Null);
let mut since = get_arg(optional_args, 1, Value::Null);
let mut limit = get_arg(optional_args, 2, Value::Null);
let mut params = get_arg(optional_args, 3, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
if (symbol == Value::Null) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchOrdersWs requires a symbol".into()))));
}
let mut market: Value = self.market(symbol.clone());
let mut type_var: Value = self.get_market_type(Value::Str("fetchOrdersWs".into()), market, &[params.clone()]);
if (type_var.as_str() != Some("spot")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchOrdersWs only supports spot markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &type_var);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchOrdersWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), self.market_id(symbol.clone()));
m.insert("returnRateLimits".to_string(), returnRateLimits);
m
});
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), Value::Str("allOrders".into()));
let __ws_arg_29 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_29]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_orders_ws".into()).clone());
m
});
let mut orders: Value = self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
return self.filter_by_symbol_since_limit(orders, &[symbol.clone(), since, limit]);
Value::Null
}
/*
* @method
* @name binance#fetchClosedOrdersWs
* @description fetch closed orders
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/trading-requests#order-lists
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch open orders for
* @param {int} [limit] the maximum number of open orders structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure}
*/
pub async fn fetch_closed_orders_ws(&mut self, optional_args: &[Value]) -> Value {
let mut symbol = get_arg(optional_args, 0, Value::Null);
let mut since = get_arg(optional_args, 1, Value::Null);
let mut limit = get_arg(optional_args, 2, Value::Null);
let mut params = get_arg(optional_args, 3, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
let mut orders: Value = self.fetch_orders_ws(&[symbol.clone(), since, limit, params]).await;
let mut closedOrders: Value = Value::from(vec![]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_79: bool = true;
while { if !__for_first_79 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_79 = false; i.as_f64().unwrap_or(f64::NAN) < ((orders.len() as i64) as f64) } {
let mut order: Value = orders.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
if (order.as_map().and_then(|__m| __m.get("status")).cloned().unwrap_or(Value::Null).as_str() == Some("closed")) {
append_to_array(&mut closedOrders, order);
}
}
}
return closedOrders;
Value::Null
}
/*
* @method
* @name binance#fetchOpenOrdersWs
* @description fetch all unfilled currently open orders
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/trading-requests#current-open-orders-user_data
* @param {string} symbol unified market symbol
* @param {int|undefined} [since] the earliest time in ms to fetch open orders for
* @param {int|undefined} [limit] the maximum number of open orders structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure}
*/
pub async fn fetch_open_orders_ws(&mut self, optional_args: &[Value]) -> Value {
let mut symbol = get_arg(optional_args, 0, Value::Null);
let mut since = get_arg(optional_args, 1, Value::Null);
let mut limit = get_arg(optional_args, 2, Value::Null);
let mut params = get_arg(optional_args, 3, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol.clone());
let mut type_var: Value = self.get_market_type(Value::Str("fetchOpenOrdersWs".into()), market, &[params.clone()]);
if (type_var.as_str() != Some("spot")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchOpenOrdersWs only supports spot markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &type_var);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchOpenOrdersWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("returnRateLimits".to_string(), returnRateLimits);
m
});
if (symbol != Value::Null) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("symbol".into(), self.market_id(symbol.clone())); }
}
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), Value::Str("openOrders.status".into()));
let __ws_arg_30 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_30]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_orders_ws".into()).clone());
m
});
let mut orders: Value = self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
return self.filter_by_symbol_since_limit(orders, &[symbol.clone(), since, limit]);
Value::Null
}
/*
* @method
* @name binance#watchOrders
* @description watches information on multiple orders made by the user
* @see https://developers.binance.com/docs/binance-spot-api-docs/user-data-stream#order-update
* @see https://developers.binance.com/docs/margin_trading/trade-data-stream/Event-Order-Update
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/user-data-streams/Event-Order-Update
* @see https://developers.binance.com/docs/derivatives/usds-margined-futures/user-data-streams/Event-Algo-Order-Update
* @see https://developers.binance.com/en/docs/catalog/advanced-trading-stocks-trading/api/ws-streams/user-streams#order-report-stream
* @param {string} symbol unified market symbol of the market the orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.stock] set to true to use stocks user data streams
* @param {string|undefined} [params.marginMode] 'cross' or 'isolated', for spot margin
* @param {boolean} [params.portfolioMargin] set to true if you would like to watch portfolio margin account orders
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure}
*/
pub async fn watch_orders(&mut self, optional_args: &[Value]) -> Value {
let mut symbol = get_arg(optional_args, 0, Value::Null);
let mut since = get_arg(optional_args, 1, Value::Null);
let mut limit = get_arg(optional_args, 2, Value::Null);
let mut params = get_arg(optional_args, 3, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut stock: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("watchOrders".into()), Value::Str("stock".into()), &[Value::Bool(false)]); stock = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
if is_true(&stock) {
// literal on top: a stray type in the caller params must not override
// the forced stock, the removed authenticateStock ignored it entirely
let __ws_arg_31 = self.extend(params.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), Value::Str("stock".into()));
m
})]);
self.authenticate(&[__ws_arg_31]).await;
let mut stockOptions: Value = self.safe_dict_k(self.options.clone(), "stock", &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut stockListenKey: Value = self.safe_string_k(stockOptions, "listenKey", &[]);
if (stockListenKey == Value::Null) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" watchOrders() failed to initialize stock listenKey".into()))));
}
let mut stockUrl: Value = self.get_stock_ws_url(&[Value::Str("user".into())]);
let mut stockStreamName: Value = Value::Str(format!("{}{}", stockListenKey, Value::Str("@orderReport".into())).into());
let mut stockRequestId: Value = self.request_id(stockUrl.clone());
let mut stockMessageHash: Value = Value::Str("orders".into());
if (symbol != Value::Null) {
stockMessageHash = Value::Str(format!("{}{}", Value::Str("orders:".into()), self.symbol(symbol.clone())).into());
}
let mut stockRequest: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("SUBSCRIBE".into()));
m.insert("params".to_string(), Value::from(vec![stockStreamName]));
m.insert("id".to_string(), stockRequestId.clone());
m
});
let mut stockQuery: Value = self.omit(params.clone(), Value::from(vec![Value::Str("stock".into()), Value::Str("name".into()), Value::Str("callerMethodName".into()), Value::Str("type".into()), Value::Str("subType".into()), Value::Str("symbol".into()), Value::Str("timeframe".into())]), &[]);
let mut stockSubscribe: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), stockRequestId);
m
});
let __ws_arg_32 = self.extend(stockRequest, &[stockQuery]);
let mut stockOrders: Value = self.watch(stockUrl, stockMessageHash.clone(), &[__ws_arg_32, stockMessageHash.clone(), stockSubscribe]).await;
if is_true(&self.newUpdates) {
limit = stockOrders.get_limit(symbol.clone(), limit.clone());
}
return self.filter_by_symbol_since_limit(stockOrders, &[symbol.clone(), since.clone(), limit.clone(), Value::Bool(true)]);
}
let mut messageHash: Value = Value::Str("orders".into());
let mut market: Value = Value::Null;
if (symbol != Value::Null) {
market = self.market(symbol.clone());
symbol = market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
messageHash = Value::Str(format!("{}{}", messageHash, Value::Str(format!("{}{}", Value::Str(":".into()), symbol).into())).into());
}
let mut type_var: Value = Value::Null;
let mut subType: Value = Value::Null;
{ let __destr_tmp = self.resolve_auth_type(Value::Str("watchOrders".into()), &[market, params.clone()]); type_var = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); subType = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(2)).cloned().unwrap_or(Value::Null); }
params = self.extend(params.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), type_var.clone());
m.insert("symbol".to_string(), symbol.clone());
m.insert("subType".to_string(), subType);
m
})]); // needed inside authenticate for isolated margin
self.authenticate(&[params.clone()]).await;
let mut marginMode: Value = Value::Null;
{ let __destr_tmp = self.handle_margin_mode_and_params(Value::Str("watchOrders".into()), &[params.clone()]); marginMode = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut urlType: Value = type_var.clone();
if (type_var.as_str() == Some("margin")) || ((type_var.as_str() == Some("spot")) && (marginMode != Value::Null)) {
urlType = Value::Str("spot".into()); // spot-margin shares the same stream as regular spot
}
let mut isPortfolioMargin: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params2(params.clone(), Value::Str("watchOrders".into()), Value::Str("papi".into()), Value::Str("portfolioMargin".into()), &[Value::Bool(false)]); isPortfolioMargin = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut url: Value = Value::Str("".into());
if (type_var.as_str() == Some("spot")) || (type_var.as_str() == Some("margin")) {
// route orders to ws-api user data stream
url = crate::value::get_value_k(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), "spot");
} else {
if is_true(&isPortfolioMargin) {
urlType = Value::Str("papi".into());
} else if (type_var.as_str() == Some("option")) {
let mut demoMode: Value = self.safe_bool_k(self.options.clone(), "enableDemoTrading", &[Value::Bool(false)]);
if (demoMode.as_bool() == Some(true)) || is_true(&self.isSandboxModeEnabled) {
panic!("{}", crate::exchange_errors::not_supported(format!("{}{}", self.id.clone(), Value::Str(" watchOrders() does not support option markets in demo/testnet mode".into()))));
}
urlType = Value::Str("optionPrivate".into());
}
url = self.get_private_ws_url(urlType, crate::value::get_value_k(&get_value(&self.options, &type_var), "listenKey"));
}
let mut client: Value = self.client(&[url.clone()]);
self.set_balance_cache(client.clone(), type_var.clone(), &[isPortfolioMargin.clone()]);
self.set_positions_cache(client.clone(), type_var.clone(), &[Value::Null, isPortfolioMargin]);
let mut message: Value = Value::Null;
let mut orders: Value = self.watch(url, messageHash.clone(), &[message.clone(), type_var]).await;
if is_true(&self.newUpdates) {
limit = orders.get_limit(symbol.clone(), limit.clone());
}
return self.filter_by_symbol_since_limit(orders, &[symbol.clone(), since, limit, Value::Bool(true)]);
Value::Null
}
pub fn parse_ws_order(&self, mut order: Value, optional_args: &[Value]) -> Value {
let mut market = get_arg(optional_args, 0, Value::Null);
//
// spot
//
// {
// "e": "executionReport", // Event type
// "E": 1499405658658, // Event time
// "s": "ETHBTC", // Symbol
// "c": "mUvoqJxFIILMdfAW5iGSOW", // Client order ID
// "S": "BUY", // Side
// "o": "LIMIT", // Order type
// "f": "GTC", // Time in force
// "q": "1.00000000", // Order quantity
// "p": "0.10264410", // Order price
// "P": "0.00000000", // Stop price
// "F": "0.00000000", // Iceberg quantity
// "g": -1, // OrderListId
// "C": null, // Original client order ID; This is the ID of the order being canceled
// "x": "NEW", // Current execution type
// "X": "NEW", // Current order status
// "r": "NONE", // Order reject reason; will be an error code.
// "i": 4293153, // Order ID
// "l": "0.00000000", // Last executed quantity
// "z": "0.00000000", // Cumulative filled quantity
// "L": "0.00000000", // Last executed price
// "n": "0", // Commission amount
// "N": null, // Commission asset
// "T": 1499405658657, // Transaction time
// "t": -1, // Trade ID
// "I": 8641984, // Ignore
// "w": true, // Is the order on the book?
// "m": false, // Is this trade the maker side?
// "M": false, // Ignore
// "O": 1499405658657, // Order creation time
// "Z": "0.00000000", // Cumulative quote asset transacted quantity
// "Y": "0.00000000" // Last quote asset transacted quantity (i.e. lastPrice * lastQty),
// "Q": "0.00000000" // Quote Order Qty
// }
//
// future
//
// {
// "s":"BTCUSDT", // Symbol
// "c":"TEST", // Client Order Id
// // special client order id:
// // starts with "autoclose-": liquidation order
// // "adl_autoclose": ADL auto close order
// "S":"SELL", // Side
// "o":"TRAILING_STOP_MARKET", // Order Type
// "f":"GTC", // Time in Force
// "q":"0.001", // Original Quantity
// "p":"0", // Original Price
// "ap":"0", // Average Price
// "sp":"7103.04", // Stop Price. Please ignore with TRAILING_STOP_MARKET order
// "x":"NEW", // Execution Type
// "X":"NEW", // Order Status
// "i":8886774, // Order Id
// "l":"0", // Order Last Filled Quantity
// "z":"0", // Order Filled Accumulated Quantity
// "L":"0", // Last Filled Price
// "N":"USDT", // Commission Asset, will not push if no commission
// "n":"0", // Commission, will not push if no commission
// "T":1568879465651, // Order Trade Time
// "t":0, // Trade Id
// "b":"0", // Bids Notional
// "a":"9.91", // Ask Notional
// "m":false, // Is this trade the maker side?
// "R":false, // Is this reduce only
// "wt":"CONTRACT_PRICE", // Stop Price Working Type
// "ot":"TRAILING_STOP_MARKET", // Original Order Type
// "ps":"LONG", // Position Side
// "cp":false, // If Close-All, pushed with conditional order
// "AP":"7476.89", // Activation Price, only puhed with TRAILING_STOP_MARKET order
// "cr":"5.0", // Callback Rate, only puhed with TRAILING_STOP_MARKET order
// "rp":"0" // Realized Profit of the trade
// }
//
// watchOrders: linear swap trigger order
//
// {
// "caid":"Q5xaq5EGKgXXa0fD7fs0Ip", // Client Algo Id
// "aid":2148719, // Algo Id
// "at":"CONDITIONAL", // Algo Type
// "o":"TAKE_PROFIT", // Order Type
// "s":"BNBUSDT", // Symbol
// "S":"SELL", // Side
// "ps":"BOTH", // Position Side
// "f":"GTC", // Time in force
// "q":"0.01", // quantity
// "X":"CANCELED", // Algo status
// "ai":"", // order id
// "ap": "0.00000", // avg fill price in matching engine, only display when order is triggered and placed in matching engine
// "aq": "0.00000", // execuated quantity in matching engine, only display when order is triggered and placed in matching engine
// "act": "0", // actual order type in matching engine, only display when order is triggered and placed in matching engine
// "tp":"750", // Trigger price
// "p":"750", // Order Price
// "V":"EXPIRE_MAKER", // STP mode
// "wt":"CONTRACT_PRICE", // Working type
// "pm":"NONE", // Price match mode
// "cp":false, // If Close-All
// "pP":false, // If price protection is turned on
// "R":false, // Is this reduce only
// "tt":0, // Trigger time
// "gtd":0, // good till time for GTD time in force
// "rm": "Reduce Only reject" // algo order failed reason
// }
//
// watchOrders: tokenized equities
//
// {
// "e": "orderReport",
// "E": 1786010067484,
// "x": "ORDER_UPDATE",
// "i": "6c62d749-b1e5-4559-9747-d4237f55ff26",
// "ai": "b0b6dd9d-8b9b-48a9-ba46-b9d54906e415",
// "b": "EQ_AAPL",
// "q": "USDC",
// "S": "buy",
// "o": "limit",
// "p": 290,
// "Q": 0.02,
// "N": null,
// "fq": 0,
// "FN": 0,
// "tc": 5.97,
// "Z": 0,
// "n": "24H",
// "s": "new",
// "T": 1786010067361,
// "U": 1786010067366
// }
//
let mut event: Option<String> = self.safe_string_k(order.clone(), "e", &[]).as_str().map(str::to_owned);
if (event.as_deref() == Some("orderReport")) {
let mut baseAssetCode: Value = self.safe_string_k(order.clone(), "b", &[]);
let mut stockBaseSymbol: Value = baseAssetCode;
if (stockBaseSymbol != Value::Null) && (Value::Int(stockBaseSymbol.as_str().and_then(|__s| __s.find("EQ_")).map(|__i| __i as i64).unwrap_or(-1)).as_f64() == Some(0.0)) {
stockBaseSymbol = stockBaseSymbol.as_str().map(|__s| { let __c: Vec<char> = __s.chars().collect(); let __l = __c.len() as i64; let __i = __l.min(3); let __j = __l; if __i <= __j { __c[__i as usize..__j as usize].iter().collect::<String>() } else { String::new() } }).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
}
if (stockBaseSymbol == Value::Null) {
stockBaseSymbol = self.safe_string_k(order.clone(), "symbol", &[]);
}
let mut stockQuote: Value = self.safe_string_k(order.clone(), "q", &[Value::Str("USDC".into())]);
let mut stockSymbol: Value = self.get_stock_unified_symbol(stockBaseSymbol, &[stockQuote]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
let mut stockRawStatus: Value = self.safe_string_lower_k(order.clone(), "s", &[]);
let mut statuses: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("accepted".to_string(), Value::Str("open".into()));
m.insert("new".to_string(), Value::Str("open".into()));
m.insert("partially_filled".to_string(), Value::Str("open".into()));
m.insert("filled".to_string(), Value::Str("closed".into()));
m.insert("canceled".to_string(), Value::Str("canceled".into()));
m.insert("rejected".to_string(), Value::Str("rejected".into()));
m.insert("expired".to_string(), Value::Str("expired".into()));
m
});
let mut stockStatus: Value = self.safe_string(statuses, stockRawStatus.clone(), &[stockRawStatus.clone()]);
let mut stockAmount: Value = self.safe_string_k(order.clone(), "Q", &[]);
let mut stockFilled: Value = self.safe_string_k(order.clone(), "fq", &[]);
let mut stockRemaining: Value = Value::Null;
if (stockAmount != Value::Null) && (stockFilled != Value::Null) {
stockRemaining = crate::precise::Precise::stringSub(&stockAmount, &stockFilled);
}
let mut stockTimestamp: Value = self.safe_integer_k(order.clone(), "T", &[]);
let mut stockLastUpdateTimestamp: Value = self.safe_integer_k(order.clone(), "U", &[stockTimestamp.clone()]);
return self.safe_order(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("info".to_string(), order.clone());
m.insert("symbol".to_string(), stockSymbol);
m.insert("id".to_string(), self.safe_string_k(order.clone(), "i", &[]));
m.insert("timestamp".to_string(), stockTimestamp.clone());
m.insert("datetime".to_string(), self.iso8601(stockTimestamp));
m.insert("lastUpdateTimestamp".to_string(), stockLastUpdateTimestamp);
m.insert("type".to_string(), self.parent.parse_order_type_by_market(self.safe_string_lower_k(order.clone(), "o", &[]), Value::Str("spot".into())));
m.insert("timeInForce".to_string(), Value::Null);
m.insert("postOnly".to_string(), Value::Null);
m.insert("reduceOnly".to_string(), Value::Null);
m.insert("side".to_string(), self.safe_string_lower_k(order.clone(), "S", &[]));
m.insert("price".to_string(), self.safe_string_k(order.clone(), "p", &[]));
m.insert("stopPrice".to_string(), Value::Null);
m.insert("triggerPrice".to_string(), Value::Null);
m.insert("amount".to_string(), stockAmount);
m.insert("cost".to_string(), self.safe_string_k(order.clone(), "FN", &[]));
m.insert("average".to_string(), Value::Null);
m.insert("filled".to_string(), stockFilled);
m.insert("remaining".to_string(), stockRemaining);
m.insert("status".to_string(), stockStatus);
m.insert("fee".to_string(), Value::Null);
m.insert("trades".to_string(), Value::Null);
m
}), &[]);
}
let mut executionType: Option<String> = self.safe_string_k(order.clone(), "x", &[]).as_str().map(str::to_owned);
let mut marketId: Value = self.safe_string_k(order.clone(), "s", &[]);
// futures user-data events carry the position side field, spot ones do not
let mut marketType: Value = (if (matches!(&order, Value::Dict(__d) if __d.contains_key("ps"))) { Value::Str("contract".into()) } else { Value::Str("spot".into()) });
let mut symbol: Value = self.safe_symbol(marketId, &[Value::Null, Value::Null, marketType.clone()]);
let mut timestamp: Value = self.safe_integer_k(order.clone(), "O", &[]);
let mut T: Value = self.safe_integer_k(order.clone(), "T", &[]);
let mut lastTradeTimestamp: Value = Value::Null;
if (executionType.as_deref() == Some("NEW")) || (executionType.as_deref() == Some("AMENDMENT")) || (executionType.as_deref() == Some("CANCELED")) {
if (timestamp == Value::Null) {
timestamp = T.clone();
}
} else if (executionType.as_deref() == Some("TRADE")) {
lastTradeTimestamp = T.clone();
}
let mut lastUpdateTimestamp: Value = T;
let mut fee: Value = Value::Null;
let mut feeCost: Value = self.safe_string_k(order.clone(), "n", &[]);
if (feeCost != Value::Null) && is_true(&(crate::precise::Precise::stringGt(&feeCost, &Value::Str("0".into())))) {
let mut feeCurrencyId: Value = self.safe_string_k(order.clone(), "N", &[]);
let mut feeCurrency: Value = self.safe_currency_code(feeCurrencyId, &[]);
fee = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("cost".to_string(), feeCost.clone());
m.insert("currency".to_string(), feeCurrency);
m
});
}
let mut rawStatus: Value = self.safe_string_k(order.clone(), "X", &[]);
let mut status: Value = self.parent.parse_order_status(rawStatus);
let mut clientOrderId: Value = self.safe_string2(order.clone(), Value::Str("C".into()), Value::Str("caid".into()), &[]);
if (clientOrderId == Value::Null) || (Value::Int(clientOrderId.len() as i64).as_f64() == Some(0.0)) {
clientOrderId = self.safe_string_k(order.clone(), "c", &[]);
}
let mut stopPrice: Value = self.safe_string_n(order.clone(), Value::from(vec![Value::Str("P".into()), Value::Str("sp".into()), Value::Str("tp".into())]), &[]);
let mut timeInForce: Value = self.safe_string_k(order.clone(), "f", &[]);
if (timeInForce.as_str() == Some("GTX")) {
// GTX means "Good Till Crossing" and is an equivalent way of saying Post Only
timeInForce = Value::Str("PO".into());
}
return self.safe_order(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("info".to_string(), order.clone());
m.insert("symbol".to_string(), symbol.clone());
m.insert("id".to_string(), self.safe_string2(order.clone(), Value::Str("i".into()), Value::Str("aid".into()), &[]));
m.insert("clientOrderId".to_string(), clientOrderId);
m.insert("timestamp".to_string(), timestamp.clone());
m.insert("datetime".to_string(), self.iso8601(timestamp.clone()));
m.insert("lastTradeTimestamp".to_string(), lastTradeTimestamp);
m.insert("lastUpdateTimestamp".to_string(), lastUpdateTimestamp);
m.insert("type".to_string(), self.parent.parse_order_type_by_market(self.safe_string_lower_k(order.clone(), "o", &[]), marketType));
m.insert("timeInForce".to_string(), timeInForce);
m.insert("postOnly".to_string(), Value::Null);
m.insert("reduceOnly".to_string(), self.safe_bool_k(order.clone(), "R", &[]));
m.insert("side".to_string(), self.safe_string_lower_k(order.clone(), "S", &[]));
m.insert("price".to_string(), self.safe_string_k(order.clone(), "p", &[]));
m.insert("stopPrice".to_string(), stopPrice.clone());
m.insert("triggerPrice".to_string(), stopPrice);
m.insert("amount".to_string(), self.safe_string_k(order.clone(), "q", &[]));
m.insert("cost".to_string(), self.safe_string_k(order.clone(), "Z", &[]));
m.insert("average".to_string(), self.safe_string_k(order.clone(), "ap", &[]));
m.insert("filled".to_string(), self.safe_string_k(order, "z", &[]));
m.insert("remaining".to_string(), Value::Null);
m.insert("status".to_string(), status);
m.insert("fee".to_string(), fee);
m.insert("trades".to_string(), Value::Null);
m
}), &[]);
Value::Null
}
pub fn handle_order_update(&mut self, mut client: Value, mut message: Value) {
//
// spot
//
// {
// "e": "executionReport", // Event type
// "E": 1499405658658, // Event time
// "s": "ETHBTC", // Symbol
// "c": "mUvoqJxFIILMdfAW5iGSOW", // Client order ID
// "S": "BUY", // Side
// "o": "LIMIT", // Order type
// "f": "GTC", // Time in force
// "q": "1.00000000", // Order quantity
// "p": "0.10264410", // Order price
// "P": "0.00000000", // Stop price
// "F": "0.00000000", // Iceberg quantity
// "g": -1, // OrderListId
// "C": null, // Original client order ID; This is the ID of the order being canceled
// "x": "NEW", // Current execution type
// "X": "NEW", // Current order status
// "r": "NONE", // Order reject reason; will be an error code.
// "i": 4293153, // Order ID
// "l": "0.00000000", // Last executed quantity
// "z": "0.00000000", // Cumulative filled quantity
// "L": "0.00000000", // Last executed price
// "n": "0", // Commission amount
// "N": null, // Commission asset
// "T": 1499405658657, // Transaction time
// "t": -1, // Trade ID
// "I": 8641984, // Ignore
// "w": true, // Is the order on the book?
// "m": false, // Is this trade the maker side?
// "M": false, // Ignore
// "O": 1499405658657, // Order creation time
// "Z": "0.00000000", // Cumulative quote asset transacted quantity
// "Y": "0.00000000" // Last quote asset transacted quantity (i.e. lastPrice * lastQty),
// "Q": "0.00000000" // Quote Order Qty
// }
//
// future
//
// {
// "e":"ORDER_TRADE_UPDATE", // Event Type
// "E":1568879465651, // Event Time
// "T":1568879465650, // Trasaction Time
// "o": {
// "s":"BTCUSDT", // Symbol
// "c":"TEST", // Client Order Id
// // special client order id:
// // starts with "autoclose-": liquidation order
// // "adl_autoclose": ADL auto close order
// "S":"SELL", // Side
// "o":"TRAILING_STOP_MARKET", // Order Type
// "f":"GTC", // Time in Force
// "q":"0.001", // Original Quantity
// "p":"0", // Original Price
// "ap":"0", // Average Price
// "sp":"7103.04", // Stop Price. Please ignore with TRAILING_STOP_MARKET order
// "x":"NEW", // Execution Type
// "X":"NEW", // Order Status
// "i":8886774, // Order Id
// "l":"0", // Order Last Filled Quantity
// "z":"0", // Order Filled Accumulated Quantity
// "L":"0", // Last Filled Price
// "N":"USDT", // Commission Asset, will not push if no commission
// "n":"0", // Commission, will not push if no commission
// "T":1568879465651, // Order Trade Time
// "t":0, // Trade Id
// "b":"0", // Bids Notional
// "a":"9.91", // Ask Notional
// "m":false, // Is this trade the maker side?
// "R":false, // Is this reduce only
// "wt":"CONTRACT_PRICE", // Stop Price Working Type
// "ot":"TRAILING_STOP_MARKET", // Original Order Type
// "ps":"LONG", // Position Side
// "cp":false, // If Close-All, pushed with conditional order
// "AP":"7476.89", // Activation Price, only puhed with TRAILING_STOP_MARKET order
// "cr":"5.0", // Callback Rate, only puhed with TRAILING_STOP_MARKET order
// "rp":"0" // Realized Profit of the trade
// }
// }
//
// linear swap conditional
//
// {
// "e":"ALGO_UPDATE", // Event Type
// "T":1750515742297, // Event Time
// "E":1750515742303, // Transaction Time
// "o":{
// "caid":"Q5xaq5EGKgXXa0fD7fs0Ip", // Client Algo Id
// "aid":2148719, // Algo Id
// "at":"CONDITIONAL", // Algo Type
// "o":"TAKE_PROFIT", // Order Type
// "s":"BNBUSDT", // Symbol
// "S":"SELL", // Side
// "ps":"BOTH", // Position Side
// "f":"GTC", // Time in force
// "q":"0.01", // quantity
// "X":"CANCELED", // Algo status
// "ai":"", // order id
// "ap": "0.00000", // avg fill price in matching engine, only display when order is triggered and placed in matching engine
// "aq": "0.00000", // execuated quantity in matching engine, only display when order is triggered and placed in matching engine
// "act": "0", // actual order type in matching engine, only display when order is triggered and placed in matching engine
// "tp":"750", // Trigger price
// "p":"750", // Order Price
// "V":"EXPIRE_MAKER", // STP mode
// "wt":"CONTRACT_PRICE", // Working type
// "pm":"NONE", // Price match mode
// "cp":false, // If Close-All
// "pP":false, // If price protection is turned on
// "R":false, // Is this reduce only
// "tt":0, // Trigger time
// "gtd":0, // good till time for GTD time in force
// "rm": "Reduce Only reject" // algo order failed reason
// }
// }
//
let mut e: Option<String> = self.safe_string_k(message.clone(), "e", &[]).as_str().map(str::to_owned);
if (e.as_deref() == Some("orderReport")) {
self.handle_order(client.clone(), message.clone());
return;
}
if (e.as_deref() == Some("ORDER_TRADE_UPDATE")) || (e.as_deref() == Some("ALGO_UPDATE")) {
let mut oField: Value = self.safe_value_k(message.clone(), "o", &[]);
if (matches!(&oField, Value::Arr(_))) {
// eOptions format: o is an array of orders with nested fi fills
self.handle_options_order_update(client.clone(), message.clone());
return;
}
message = self.safe_dict_k(message.clone(), "o", &[message.clone()]);
}
self.handle_my_trade(client.clone(), message.clone());
self.handle_order(client.clone(), message.clone());
self.handle_my_liquidation(client.clone(), message.clone());
}
pub fn handle_stock_price(&mut self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "rates": [
// {
// "s": "JAVA",
// "ac": "EQ_JAVA",
// "p": "83.26",
// "t": 1785959875000,
// "pc": "83.1800",
// "mp": "ON"
// },
// ],
// "e": "price"
// }
//
let mut rates: Value = (match message.get("rates") { Some(__v) if matches!(__v, Value::Arr(_)) => __v.clone(), _ => Value::from(vec![]) });
let mut tickers: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
{
let mut i: Value = Value::Int(0);
let mut __for_first_80: bool = true;
while { if !__for_first_80 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_80 = false; i.as_f64().unwrap_or(f64::NAN) < ((rates.len() as i64) as f64) } {
let mut rate: Value = self.safe_dict(rates.clone(), i.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut stockSymbol: Value = self.safe_string_k(rate.clone(), "s", &[]);
let mut symbol: Value = self.get_stock_unified_symbol(stockSymbol, &[Value::Str("USDC".into())]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
if (symbol == Value::Null) {
continue;
}
let mut timestamp: Value = self.safe_integer_k(rate.clone(), "t", &[]);
let mut parsed: Value = self.safe_ticker(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), symbol.clone());
m.insert("timestamp".to_string(), timestamp.clone());
m.insert("datetime".to_string(), self.iso8601(timestamp.clone()));
m.insert("last".to_string(), self.safe_string_k(rate.clone(), "p", &[]));
m.insert("close".to_string(), self.safe_string_k(rate.clone(), "p", &[]));
m.insert("previousClose".to_string(), self.safe_string_k(rate.clone(), "pc", &[]));
m.insert("info".to_string(), rate);
m
}), &[]);
if let Value::Dict(__d) = &mut self.tickers { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), parsed.clone()); }
if let Value::Dict(__d) = &mut tickers { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), parsed.clone()); }
client.resolve(&[parsed, Value::Str(format!("{}{}", Value::Str("stock:price:".into()), symbol).into())]);
}
}
client.resolve(&[tickers, Value::Str("stock:price".into())]);
}
pub fn handle_stock_quote(&mut self, mut client: Value, mut message: Value) {
let __pro_message_arc: std::sync::Arc<indexmap::IndexMap<String, Value>> = (match &message { Value::Dict(__d) => __d.clone(), _ => std::sync::Arc::new(indexmap::IndexMap::new()) });
let __pro_message: &indexmap::IndexMap<String, Value> = &__pro_message_arc;
let mut stockSymbol: Value = (match __pro_message.get("s").cloned() { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut symbol: Value = self.get_stock_unified_symbol(stockSymbol, &[Value::Str("USDC".into())]).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
if (symbol == Value::Null) {
return;
}
let mut timestamp: Value = self.safe_integer2(message.clone(), Value::Str("E".into()), Value::Str("T".into()), &[]);
let mut parsed: Value = self.safe_ticker(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), symbol.clone());
m.insert("timestamp".to_string(), timestamp.clone());
m.insert("datetime".to_string(), self.iso8601(timestamp.clone()));
m.insert("bid".to_string(), (match __pro_message.get("bp").cloned() { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null }));
m.insert("ask".to_string(), (match __pro_message.get("ap").cloned() { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null }));
m.insert("bidVolume".to_string(), (match __pro_message.get("bs").cloned() { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null }));
m.insert("askVolume".to_string(), (match __pro_message.get("as").cloned() { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null }));
m.insert("info".to_string(), message.clone());
m
}), &[]);
if let Value::Dict(__d) = &mut self.bidsasks { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), parsed.clone()); }
client.resolve(&[parsed, Value::Str(format!("{}{}", Value::Str("stock:quote:".into()), symbol).into())]);
}
pub fn handle_options_order_update(&mut self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// eOptions ORDER_TRADE_UPDATE: "o" is an array of orders (not a dict like futures)
//
// {
// "e": "ORDER_TRADE_UPDATE",
// "E": 1657613775883,
// "o": [
// {
// "T": 1657613342918, // order create time
// "t": 1657613342918, // order last update time
// "s": "BTC-220930-18000-C", // symbol
// "c": "", // client order ID
// "oid": "4611869636869226548", // order ID
// "p": "1993", // price
// "q": "1", // signed qty (positive = BUY, negative = SELL)
// "S": "PARTIALLY_FILLED", // status
// "e": "0.1", // cumulative filled qty
// "ec": "199.3", // cumulative filled amount (USDT)
// "f": "2", // cumulative fee
// "tif": "GTC", // time in force
// "oty": "LIMIT", // order type
// "fi": [
// {
// "t": "20", // trade ID
// "p": "1993", // fill price
// "q": "0.1", // fill qty
// "T": 1657613774336, // fill time
// "m": "TAKER", // "TAKER" or "MAKER"
// "f": "0.0002" // commission (positive) or rebate (negative)
// }
// ]
// }
// ]
// }
//
let mut orders: Value = (match message.get("o") { Some(__v) if matches!(__v, Value::Arr(_)) => __v.clone(), _ => Value::from(vec![]) });
{
let mut i: Value = Value::Int(0);
let mut __for_first_82: bool = true;
while { if !__for_first_82 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_82 = false; i.as_f64().unwrap_or(f64::NAN) < ((orders.len() as i64) as f64) } {
let mut order: Value = orders.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut fills: Value = self.safe_list_k(order.clone(), "fi", &[Value::from(vec![])]);
let mut rawQty: Value = self.safe_string_k(order.clone(), "q", &[Value::Str("0".into())]);
let mut side: Value = Value::Str("BUY".into());
if is_true(&crate::precise::Precise::stringLt(&rawQty, &Value::Str("0".into()))) {
side = Value::Str("SELL".into());
}
let mut absQty: Value = crate::precise::Precise::stringAbs(&rawQty);
let mut executionType: Value = Value::Str("NEW".into());
if ((fills.len() as i64) as f64) > ((0i64) as f64) {
executionType = Value::Str("TRADE".into());
}
// normalize eOptions fields to the flat format parseWsOrder/handleOrder expect
let mut normalizedOrder: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("s".to_string(), self.safe_string_k(order.clone(), "s", &[]));
m.insert("i".to_string(), self.safe_string_k(order.clone(), "oid", &[]));
m.insert("c".to_string(), self.safe_string_k(order.clone(), "c", &[]));
m.insert("S".to_string(), side.clone());
m.insert("o".to_string(), self.safe_string_k(order.clone(), "oty", &[]));
m.insert("f".to_string(), self.safe_string_k(order.clone(), "tif", &[]));
m.insert("q".to_string(), absQty);
m.insert("p".to_string(), self.safe_string_k(order.clone(), "p", &[]));
m.insert("X".to_string(), self.safe_string_k(order.clone(), "S", &[]));
m.insert("x".to_string(), executionType);
m.insert("z".to_string(), self.safe_string_k(order.clone(), "e", &[]));
m.insert("Z".to_string(), self.safe_string_k(order.clone(), "ec", &[]));
m.insert("n".to_string(), self.safe_string_k(order.clone(), "f", &[]));
m.insert("T".to_string(), self.safe_integer_k(order.clone(), "t", &[]));
m.insert("O".to_string(), self.safe_integer_k(order.clone(), "T", &[]));
m
});
self.handle_order(client.clone(), normalizedOrder);
{
let mut j: Value = Value::Int(0);
let mut __for_first_81: bool = true;
while { if !__for_first_81 { j = (match (&(j), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_81 = false; j.as_f64().unwrap_or(f64::NAN) < ((fills.len() as i64) as f64) } {
let mut fill: Value = fills.as_array().and_then(|__arr| match &j { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut isMaker: Value = (Value::Bool(self.safe_string_k(fill.clone(), "m", &[]).as_str() == Some("MAKER")));
// normalize fill fields to the flat format parseWsTrade/handleMyTrade expect
let mut normalizedTrade: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("x".to_string(), Value::Str("TRADE".into()));
m.insert("s".to_string(), self.safe_string_k(order.clone(), "s", &[]));
m.insert("t".to_string(), self.safe_string_k(fill.clone(), "t", &[]));
m.insert("L".to_string(), self.safe_string_k(fill.clone(), "p", &[]));
m.insert("l".to_string(), self.safe_string_k(fill.clone(), "q", &[]));
m.insert("T".to_string(), self.safe_integer_k(fill.clone(), "T", &[]));
m.insert("m".to_string(), isMaker);
m.insert("n".to_string(), self.safe_string_k(fill, "f", &[]));
m.insert("i".to_string(), self.safe_string_k(order.clone(), "oid", &[]));
m.insert("S".to_string(), side.clone());
m.insert("o".to_string(), self.safe_string_k(order.clone(), "oty", &[]));
m
});
self.handle_my_trade(client.clone(), normalizedTrade);
}
}
}
}
}
/*
* @method
* @name binance#watchPositions
* @description watch all open positions
* @param {string[]|undefined} symbols list of unified market symbols
* @param {number} [since] since timestamp
* @param {number} [limit] limit
* @param {object} params extra parameters specific to the exchange API endpoint
* @param {boolean} [params.portfolioMargin] set to true if you would like to watch positions in a portfolio margin account
* @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
*/
pub async fn watch_positions(&mut self, optional_args: &[Value]) -> Value {
let mut symbols = get_arg(optional_args, 0, Value::Null);
let mut since = get_arg(optional_args, 1, Value::Null);
let mut limit = get_arg(optional_args, 2, Value::Null);
let mut params = get_arg(optional_args, 3, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = Value::Null;
let mut messageHash: Value = Value::Str("".into());
symbols = self.market_symbols(&[symbols.clone()]);
if !(self.is_empty(symbols.clone()).as_bool() == Some(true)) {
market = self.get_market_from_symbols(&[symbols.clone()]);
if (symbols == Value::Null) {
panic!("{}", crate::exchange_errors::arguments_required(format!("{}{}", self.id.clone(), Value::Str(" watchPositions() symbols is required".into()))));
}
messageHash = Value::Str(format!("{}{}", Value::Str("::".into()), join(&symbols, &Value::Str(",".into()))).into());
}
let mut type_var: Value = Value::Null;
let mut subType: Value = Value::Null;
{ let __destr_tmp = self.resolve_auth_type(Value::Str("watchPositions".into()), &[market, params.clone()]); type_var = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); subType = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(2)).cloned().unwrap_or(Value::Null); }
// spot and margin have no positions - whatever still RESOLVES to spot
// or margin after the helper falls through to the derivatives stream
// matching the subType. requests a defaultSubType already rewrote
// arrive here as future or delivery and pass untouched, which lands on
// the same stream the old raw-type ordering produced in every case
if (type_var.as_str() == Some("spot")) || (type_var.as_str() == Some("margin")) {
type_var = (if (subType.as_str() == Some("inverse")) { Value::Str("delivery".into()) } else { Value::Str("future".into()) });
}
// 'option' stays as 'option', don't redirect to 'future' - the helper's
// guard finally makes this comment true
let mut marketTypeObject: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
if let Value::Dict(__d) = &mut marketTypeObject { std::sync::Arc::make_mut(__d).insert("type".into(), type_var.clone()); }
if let Value::Dict(__d) = &mut marketTypeObject { std::sync::Arc::make_mut(__d).insert("subType".into(), subType); }
let __ws_arg_33 = self.extend(marketTypeObject, &[params.clone()]);
self.authenticate(&[__ws_arg_33]).await;
messageHash = Value::Str(format!("{}{}", Value::Str(format!("{}{}", type_var, Value::Str(":positions".into())).into()), messageHash).into());
let mut isPortfolioMargin: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params2(params.clone(), Value::Str("watchPositions".into()), Value::Str("papi".into()), Value::Str("portfolioMargin".into()), &[Value::Bool(false)]); isPortfolioMargin = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut urlType: Value = type_var.clone();
if is_true(&isPortfolioMargin) {
urlType = Value::Str("papi".into());
} else if (type_var.as_str() == Some("option")) {
let mut demoMode: Value = self.safe_bool_k(self.options.clone(), "enableDemoTrading", &[Value::Bool(false)]);
if (demoMode.as_bool() == Some(true)) || is_true(&self.isSandboxModeEnabled) {
panic!("{}", crate::exchange_errors::not_supported(format!("{}{}", self.id.clone(), Value::Str(" watchPositions() does not support option markets in demo/testnet mode".into()))));
}
urlType = Value::Str("optionPrivate".into());
}
let mut url: Value = self.get_private_ws_url(urlType, crate::value::get_value_k(&get_value(&self.options, &type_var), "listenKey"));
let mut client: Value = self.client(&[url.clone()]);
self.set_balance_cache(client.clone(), type_var.clone(), &[isPortfolioMargin.clone()]);
self.set_positions_cache(client.clone(), type_var.clone(), &[symbols.clone(), isPortfolioMargin]);
let mut fetchPositionsSnapshot: Value = self.handle_option(Value::Str("watchPositions".into()), Value::Str("fetchPositionsSnapshot".into()), &[Value::Bool(true)]);
let mut awaitPositionsSnapshot: Value = self.handle_option(Value::Str("watchPositions".into()), Value::Str("awaitPositionsSnapshot".into()), &[Value::Bool(true)]);
let mut cache: Value = self.safe_value(self.positions.clone(), type_var.clone(), &[]);
if (is_equal(&fetchPositionsSnapshot, &Value::Bool(true))) && (is_equal(&awaitPositionsSnapshot, &Value::Bool(true))) && (cache == Value::Null) {
let mut snapshot: Value = crate::exchange_stubs::ws_await_flight(&client.future(&[Value::Str(format!("{}{}", type_var, Value::Str(":fetchPositionsSnapshot".into())).into())])).await;
return self.filter_by_symbols_since_limit(snapshot, &[symbols.clone(), since.clone(), limit.clone(), Value::Bool(true)]);
}
let mut newPositions: Value = self.watch(url, messageHash.clone(), &[Value::Null, type_var]).await;
if is_true(&self.newUpdates) {
return newPositions;
}
return self.filter_by_symbols_since_limit(cache, &[symbols, since, limit, Value::Bool(true)]);
Value::Null
}
pub fn set_positions_cache(&mut self, mut client: Value, mut type_var: Value, optional_args: &[Value]) {
let mut symbols = get_arg(optional_args, 0, Value::Null);
let mut isPortfolioMargin = get_arg(optional_args, 1, Value::Bool(false));
if (type_var.as_str() == Some("spot")) {
return;
}
if (self.positions.clone() == Value::Null) {
self.positions = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
}
if (in_op(&self.positions, &type_var)) {
return;
}
let mut fetchPositionsSnapshot: Value = self.handle_option(Value::Str("watchPositions".into()), Value::Str("fetchPositionsSnapshot".into()), &[Value::Bool(false)]);
if is_equal(&fetchPositionsSnapshot, &Value::Bool(true)) {
let mut messageHash: Value = add(&type_var, &Value::Str(":fetchPositionsSnapshot".into()));
if !(in_op(&get_value(&client, &Value::Str("futures".into())), &messageHash)) {
client.future(&[messageHash.clone()]);
self.spawn(&[Value::Str("load_positions_snapshot".into()).clone(), client.clone(), messageHash.clone(), type_var.clone(), isPortfolioMargin]);
}
} else {
add_element_to_object(&mut self.positions, &type_var, ArrayCacheBySymbolBySide::new(Value::Null));
}
}
pub async fn load_positions_snapshot(&mut self, mut client: Value, mut messageHash: Value, mut type_var: Value, mut isPortfolioMargin: Value) -> Value {
let mut params: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), type_var.clone());
m
});
if is_equal(&isPortfolioMargin, &Value::Bool(true)) {
if let Value::Dict(__d) = &mut params { std::sync::Arc::make_mut(__d).insert("portfolioMargin".into(), Value::Bool(true)); }
}
let mut positions: Value = self.fetch_positions(&[Value::Null, params]).await;
add_element_to_object(&mut self.positions, &type_var, ArrayCacheBySymbolBySide::new(Value::Null));
let mut cache: Value = get_value(&self.positions, &type_var);
{
let mut i: Value = Value::Int(0);
let mut __for_first_83: bool = true;
while { if !__for_first_83 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_83 = false; i.as_f64().unwrap_or(f64::NAN) < ((positions.len() as i64) as f64) } {
let mut position: Value = positions.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut contracts: Value = self.safe_number_k(position.clone(), "contracts", &[Value::Int(0)]);
if (contracts != Value::Null) && (contracts.as_f64().unwrap_or(f64::NAN) > ((0i64) as f64)) {
cache.append(position);
}
}
}
// don't remove the future from the .futures cache
if (in_op(&get_value(&client, &Value::Str("futures".into())), &messageHash)) {
let mut future: Value = get_value(&get_value(&client, &Value::Str("futures".into())), &messageHash);
future.resolve(&[cache.clone()]);
client.resolve(&[cache, add(&type_var, &Value::Str(":position".into()))]);
}
Value::Null
}
pub fn handle_positions(&mut self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// e: 'ACCOUNT_UPDATE',
// T: 1667881353112,
// E: 1667881353115,
// a: {
// B: [{
// a: 'USDT',
// wb: '1127.95750089',
// cw: '1040.82091149',
// bc: '0'
// }],
// P: [{
// s: 'BTCUSDT',
// pa: '-0.089',
// ep: '19700.03933',
// cr: '-1260.24809979',
// up: '1.53058860',
// mt: 'isolated',
// iw: '87.13658940',
// ps: 'BOTH',
// ma: 'USDT'
// }],
// m: 'ORDER'
// }
// }
//
// each account is connected to a different endpoint
// and has exactly one subscriptionhash which is the account type
let mut subscriptions: Value = get_value(&client, &Value::Str("subscriptions".into()));
let mut subscriptionsKeys: Value = object_keys(&subscriptions);
let mut accountType: Value = self.get_account_type_from_subscriptions(subscriptionsKeys).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
if (self.positions.clone() == Value::Null) {
self.positions = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
}
if !(in_op(&self.positions, &accountType)) {
add_element_to_object(&mut self.positions, &accountType, ArrayCacheBySymbolBySide::new(Value::Null));
}
let mut cache: Value = get_value(&self.positions, &accountType);
let mut data: Value = (match message.get("a") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut rawPositions: Value = self.safe_list_k(data, "P", &[Value::from(vec![])]);
let mut newPositions: Value = Value::from(vec![]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_84: bool = true;
while { if !__for_first_84 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_84 = false; i.as_f64().unwrap_or(f64::NAN) < ((rawPositions.len() as i64) as f64) } {
let mut rawPosition: Value = rawPositions.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut position: Value = self.parse_ws_position(rawPosition, &[]);
let mut timestamp: Value = (match message.get("E") { Some(Value::Int(__n)) => Value::Int(*__n), Some(Value::Float(__f)) => Value::Int(*__f as i64), Some(Value::Str(__s)) => match __s.parse::<i64>() { Ok(__n) => Value::Int(__n), Err(_) => match __s.parse::<f64>() { Ok(__f) if __f.is_finite() => Value::Int(__f as i64), _ => Value::Null } }, _ => Value::Null });
add_element_to_object(&mut position, &Value::Str("timestamp".into()), timestamp.clone());
add_element_to_object(&mut position, &Value::Str("datetime".into()), self.iso8601(timestamp.clone()));
append_to_array(&mut newPositions, position.clone());
cache.append(position);
}
}
let mut messageHashes: Value = self.find_message_hashes(client.clone(), Value::Str(format!("{}{}", accountType, Value::Str(":positions::".into())).into()));
{
let mut i: Value = Value::Int(0);
let mut __for_first_85: bool = true;
while { if !__for_first_85 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_85 = false; i.as_f64().unwrap_or(f64::NAN) < ((messageHashes.len() as i64) as f64) } {
let mut messageHash: Value = messageHashes.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut parts: Value = split(&messageHash, &Value::Str("::".into()));
let mut symbolsString: Value = parts.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null);
let mut symbols: Value = split(&symbolsString, &Value::Str(",".into()));
let mut positions: Value = self.filter_by_array(newPositions.clone(), Value::Str("symbol".into()), &[symbols, Value::Bool(false)]);
if !(self.is_empty(positions.clone()).as_bool() == Some(true)) {
client.resolve(&[positions, messageHash.clone()]);
}
}
}
client.resolve(&[newPositions, Value::Str(format!("{}{}", accountType, Value::Str(":positions".into())).into())]);
}
pub fn parse_ws_position(&self, mut position: Value, optional_args: &[Value]) -> Value {
let mut market = get_arg(optional_args, 0, Value::Null);
//
// {
// "s": "BTCUSDT", // Symbol
// "pa": "0", // Position Amount
// "ep": "0.00000", // Entry Price
// "cr": "200", // (Pre-fee) Accumulated Realized
// "up": "0", // Unrealized PnL
// "mt": "isolated", // Margin Type
// "iw": "0.00000000", // Isolated Wallet (if isolated position)
// "ps": "BOTH" // Position Side
// }
//
let mut marketId: Value = self.safe_string_k(position.clone(), "s", &[]);
let mut contracts: Value = self.safe_string_k(position.clone(), "pa", &[]);
let mut contractsAbs: Value = crate::precise::Precise::stringAbs(&self.safe_string_k(position.clone(), "pa", &[]));
let mut positionSide: Value = self.safe_string_lower_k(position.clone(), "ps", &[]);
let mut hedged: Value = Value::Bool(true);
if (positionSide.as_str() == Some("both")) {
hedged = Value::Bool(false);
if !is_true(&crate::precise::Precise::stringEq(&contracts, &Value::Str("0".into()))) {
if is_true(&crate::precise::Precise::stringLt(&contracts, &Value::Str("0".into()))) {
positionSide = Value::Str("short".into());
} else {
positionSide = Value::Str("long".into());
}
}
}
return self.safe_position(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("info".to_string(), position.clone());
m.insert("id".to_string(), Value::Null);
m.insert("symbol".to_string(), self.safe_symbol(marketId, &[Value::Null, Value::Null, Value::Str("swap".into())]));
m.insert("notional".to_string(), Value::Null);
m.insert("marginMode".to_string(), self.safe_string_k(position.clone(), "mt", &[]));
m.insert("liquidationPrice".to_string(), Value::Null);
m.insert("entryPrice".to_string(), self.safe_number_k(position.clone(), "ep", &[]));
m.insert("unrealizedPnl".to_string(), self.safe_number_k(position, "up", &[]));
m.insert("percentage".to_string(), Value::Null);
m.insert("contracts".to_string(), self.parse_number(contractsAbs, &[]));
m.insert("contractSize".to_string(), Value::Null);
m.insert("markPrice".to_string(), Value::Null);
m.insert("side".to_string(), positionSide);
m.insert("hedged".to_string(), hedged);
m.insert("timestamp".to_string(), Value::Null);
m.insert("datetime".to_string(), Value::Null);
m.insert("maintenanceMargin".to_string(), Value::Null);
m.insert("maintenanceMarginPercentage".to_string(), Value::Null);
m.insert("collateral".to_string(), Value::Null);
m.insert("initialMargin".to_string(), Value::Null);
m.insert("initialMarginPercentage".to_string(), Value::Null);
m.insert("leverage".to_string(), Value::Null);
m.insert("marginRatio".to_string(), Value::Null);
m
}));
Value::Null
}
pub fn parse_ws_options_position(&self, mut position: Value, optional_args: &[Value]) -> Value {
let mut market = get_arg(optional_args, 0, Value::Null);
//
// from BALANCE_POSITION_UPDATE event P[] array:
// {
// "s": "BTC-251123-126000-C", // option symbol
// "c": "-0.1000", // position quantity (negative = short)
// "p": "-120.00000000", // position value (USDT)
// "a": "1200.00000000" // average entry price
// }
//
let mut marketId: Value = self.safe_string_k(position.clone(), "s", &[]);
let mut contracts: Value = self.safe_string_k(position.clone(), "c", &[]);
let mut contractsAbs: Value = crate::precise::Precise::stringAbs(&contracts);
let mut side: Value = Value::Null;
if (contracts != Value::Null) {
if is_true(&crate::precise::Precise::stringLt(&contracts, &Value::Str("0".into()))) {
side = Value::Str("short".into());
} else if is_true(&crate::precise::Precise::stringGt(&contracts, &Value::Str("0".into()))) {
side = Value::Str("long".into());
}
}
return self.safe_position(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("info".to_string(), position.clone());
m.insert("id".to_string(), Value::Null);
m.insert("symbol".to_string(), self.safe_symbol(marketId, &[market, Value::Null, Value::Str("option".into())]));
m.insert("notional".to_string(), self.safe_string_k(position.clone(), "p", &[]));
m.insert("marginMode".to_string(), Value::Null);
m.insert("liquidationPrice".to_string(), Value::Null);
m.insert("entryPrice".to_string(), self.safe_number_k(position, "a", &[]));
m.insert("unrealizedPnl".to_string(), Value::Null);
m.insert("percentage".to_string(), Value::Null);
m.insert("contracts".to_string(), self.parse_number(contractsAbs, &[]));
m.insert("contractSize".to_string(), Value::Null);
m.insert("markPrice".to_string(), Value::Null);
m.insert("side".to_string(), side);
m.insert("hedged".to_string(), Value::Bool(false));
m.insert("timestamp".to_string(), Value::Null);
m.insert("datetime".to_string(), Value::Null);
m.insert("maintenanceMargin".to_string(), Value::Null);
m.insert("maintenanceMarginPercentage".to_string(), Value::Null);
m.insert("collateral".to_string(), Value::Null);
m.insert("initialMargin".to_string(), Value::Null);
m.insert("initialMarginPercentage".to_string(), Value::Null);
m.insert("leverage".to_string(), Value::Null);
m.insert("marginRatio".to_string(), Value::Null);
m
}));
Value::Null
}
/*
* @method
* @name binance#fetchMyTradesWs
* @description fetch all trades made by the user
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/account-requests#account-trade-history-user_data
* @param {string} symbol unified market symbol
* @param {int|undefined} [since] the earliest time in ms to fetch trades for
* @param {int|undefined} [limit] the maximum number of trades structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.endTime] the latest time in ms to fetch trades for
* @param {int} [params.fromId] first trade Id to fetch
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=trade-structure}
*/
pub async fn fetch_my_trades_ws(&mut self, optional_args: &[Value]) -> Value {
let mut symbol = get_arg(optional_args, 0, Value::Null);
let mut since = get_arg(optional_args, 1, Value::Null);
let mut limit = get_arg(optional_args, 2, Value::Null);
let mut params = get_arg(optional_args, 3, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
if (symbol == Value::Null) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchMyTradesWs requires a symbol".into()))));
}
let mut market: Value = self.market(symbol.clone());
let mut type_var: Value = self.get_market_type(Value::Str("fetchMyTradesWs".into()), market, &[params.clone()]);
if (type_var.as_str() != Some("spot")) && (type_var.as_str() != Some("future")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", self.id.clone(), Value::Str(" fetchMyTradesWs does not support ".into())).into()), type_var).into()), Value::Str(" markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &type_var);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchMyTradesWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), self.market_id(symbol.clone()));
m.insert("returnRateLimits".to_string(), returnRateLimits);
m
});
if (since != Value::Null) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("startTime".into(), since.clone()); }
}
if (limit != Value::Null) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("limit".into(), limit.clone()); }
}
let mut fromId: Option<i64> = self.safe_integer_k(params.clone(), "fromId", &[]).as_i64();
if (fromId.is_some()) && (since != Value::Null) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", self.id.clone(), Value::Str(" fetchMyTradesWs does not support fetching by both fromId and since parameters at the same time".into()))));
}
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), Value::Str("myTrades".into()));
let __ws_arg_34 = self.extend(payload, &[params]);
m.insert("params".to_string(), self.sign_params(&[__ws_arg_34]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_trades_ws".into()).clone());
m
});
let mut trades: Value = self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
return self.filter_by_symbol_since_limit(trades, &[symbol.clone(), since, limit]);
Value::Null
}
/*
* @method
* @name binance#fetchTradesWs
* @description fetch all trades made by the user
* @see https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/market-data-requests#recent-trades
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch trades for
* @param {int} [limit] the maximum number of trades structures to retrieve, default=500, max=1000
* @param {object} [params] extra parameters specific to the exchange API endpoint
*
* EXCHANGE SPECIFIC PARAMETERS
* @param {int} [params.fromId] trade ID to begin at
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=trade-structure}
*/
pub async fn fetch_trades_ws(&mut self, mut symbol: Value, optional_args: &[Value]) -> Value {
let mut since = get_arg(optional_args, 0, Value::Null);
let mut limit = get_arg(optional_args, 1, Value::Null);
let mut params = get_arg(optional_args, 2, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol.clone());
let mut type_var: Value = self.get_market_type(Value::Str("fetchTradesWs".into()), market, &[params.clone()]);
if (type_var.as_str() != Some("spot")) && (type_var.as_str() != Some("future")) {
panic!("{}", crate::exchange_errors::bad_request(format!("{}{}", Value::Str(format!("{}{}", Value::Str(format!("{}{}", self.id.clone(), Value::Str(" fetchTradesWs does not support ".into())).into()), type_var).into()), Value::Str(" markets".into()))));
}
let mut url: Value = get_value(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), &type_var);
let mut requestId: Value = self.request_id(url.clone());
let mut messageHash: Value = to_string_val(&requestId);
let mut returnRateLimits: Value = Value::Bool(false);
{ let __destr_tmp = self.handle_option_and_params(params.clone(), Value::Str("fetchTradesWs".into()), Value::Str("returnRateLimits".into()), &[Value::Bool(false)]); returnRateLimits = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut payload: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), self.market_id(symbol.clone()));
m.insert("returnRateLimits".to_string(), returnRateLimits);
m
});
if (limit != Value::Null) {
if let Value::Dict(__d) = &mut payload { std::sync::Arc::make_mut(__d).insert("limit".into(), limit.clone()); }
}
let mut message: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), messageHash.clone());
m.insert("method".to_string(), Value::Str("trades.historical".into()));
m.insert("params".to_string(), self.extend(payload, &[params]));
m
});
let mut subscription: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("method".to_string(), Value::Str("handle_trades_ws".into()).clone());
m
});
let mut trades: Value = self.watch(url, messageHash.clone(), &[message.clone(), messageHash.clone(), subscription]).await;
return self.filter_by_since_limit(trades, &[since, limit]);
Value::Null
}
pub fn handle_trades_ws(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// fetchMyTradesWs
//
// {
// "id": "f4ce6a53-a29d-4f70-823b-4ab59391d6e8",
// "status": 200,
// "result": [
// {
// "symbol": "BTCUSDT",
// "id": 1650422481,
// "orderId": 12569099453,
// "orderListId": -1,
// "price": "23416.10000000",
// "qty": "0.00635000",
// "quoteQty": "148.69223500",
// "commission": "0.00000000",
// "commissionAsset": "BNB",
// "time": 1660801715793,
// "isBuyer": false,
// "isMaker": true,
// "isBestMatch": true
// },
// ...
// ],
// }
//
// fetchTradesWs
//
// {
// "id": "f4ce6a53-a29d-4f70-823b-4ab59391d6e8",
// "status": 200,
// "result": [
// {
// "id": 0,
// "price": "0.00005000",
// "qty": "40.00000000",
// "quoteQty": "0.00200000",
// "time": 1500004800376,
// "isBuyerMaker": true,
// "isBestMatch": true
// }
// ...
// ],
// }
//
let mut messageHash: Value = (match message.get("id") { Some(Value::Str(__s)) if !__s.is_empty() => Value::Str(__s.clone()), Some(Value::Int(__n)) => Value::Str(__n.to_string().into()), Some(Value::Float(__f)) => Value::Str(__f.to_string().into()), _ => Value::Null });
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Arr(_)) => __v.clone(), _ => Value::from(vec![]) });
let mut trades: Value = self.parse_trades(result, &[]);
client.resolve(&[trades, messageHash.clone()]);
}
/*
* @method
* @name binance#watchMyTrades
* @description watches information on multiple trades made by the user
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.portfolioMargin] set to true if you would like to watch trades in a portfolio margin account
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=trade-structure}
*/
pub async fn watch_my_trades(&mut self, optional_args: &[Value]) -> Value {
let mut symbol = get_arg(optional_args, 0, Value::Null);
let mut since = get_arg(optional_args, 1, Value::Null);
let mut limit = get_arg(optional_args, 2, Value::Null);
let mut params = get_arg(optional_args, 3, Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut type_var: Value = Value::Null;
let mut market: Value = Value::Null;
if (symbol != Value::Null) {
let mut marketResolved: Value = self.market(symbol.clone());
market = marketResolved;
symbol = market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
}
let mut subType: Value = Value::Null;
{ let __destr_tmp = self.resolve_auth_type(Value::Str("watchMyTrades".into()), &[market.clone(), params.clone()]); type_var = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); subType = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(2)).cloned().unwrap_or(Value::Null); }
let mut messageHash: Value = Value::Str("myTrades".into());
if (symbol != Value::Null) && (market != Value::Null) {
symbol = self.symbol(symbol.clone());
messageHash = Value::Str(format!("{}{}", messageHash, Value::Str(format!("{}{}", Value::Str(":".into()), symbol).into())).into());
params = self.extend(params.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), market.as_map().and_then(|__m| __m.get("type")).cloned().unwrap_or(Value::Null));
m.insert("symbol".to_string(), symbol.clone());
m
})]);
}
let __ws_arg_35 = self.extend(Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("type".to_string(), type_var.clone());
m.insert("subType".to_string(), subType);
m
}), &[params.clone()]);
self.authenticate(&[__ws_arg_35]).await;
let mut urlType: Value = type_var.clone(); // we don't change type because the listening key is different
if (type_var.as_str() == Some("margin")) {
urlType = Value::Str("spot".into()); // spot-margin shares the same stream as regular spot
}
let mut isPortfolioMargin: Value = Value::Null;
{ let __destr_tmp = self.handle_option_and_params2(params.clone(), Value::Str("watchMyTrades".into()), Value::Str("papi".into()), Value::Str("portfolioMargin".into()), &[Value::Bool(false)]); isPortfolioMargin = __destr_tmp.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null); params = __destr_tmp.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null); }
let mut url: Value = Value::Str("".into());
if (type_var.as_str() == Some("spot")) || (type_var.as_str() == Some("margin")) {
url = crate::value::get_value_k(&crate::value::get_value_k(&self.urls.as_map().and_then(|__m| __m.get("api")).cloned().unwrap_or(Value::Null).as_map().and_then(|__m| __m.get("ws")).cloned().unwrap_or(Value::Null), "ws-api"), "spot");
} else {
if is_true(&isPortfolioMargin) {
urlType = Value::Str("papi".into());
} else if (type_var.as_str() == Some("option")) {
let mut demoMode: Value = self.safe_bool_k(self.options.clone(), "enableDemoTrading", &[Value::Bool(false)]);
if (demoMode.as_bool() == Some(true)) || is_true(&self.isSandboxModeEnabled) {
panic!("{}", crate::exchange_errors::not_supported(format!("{}{}", self.id.clone(), Value::Str(" watchMyTrades() does not support option markets in demo/testnet mode".into()))));
}
urlType = Value::Str("optionPrivate".into());
}
url = self.get_private_ws_url(urlType, crate::value::get_value_k(&get_value(&self.options, &type_var), "listenKey"));
}
let mut client: Value = self.client(&[url.clone()]);
self.set_balance_cache(client.clone(), type_var.clone(), &[isPortfolioMargin.clone()]);
self.set_positions_cache(client.clone(), type_var.clone(), &[Value::Null, isPortfolioMargin]);
let mut message: Value = Value::Null;
let mut trades: Value = self.watch(url, messageHash.clone(), &[message.clone(), type_var]).await;
if is_true(&self.newUpdates) {
limit = trades.get_limit(symbol.clone(), limit.clone());
}
return self.filter_by_symbol_since_limit(trades, &[symbol.clone(), since, limit, Value::Bool(true)]);
Value::Null
}
pub fn handle_my_trade(&mut self, mut client: Value, mut message: Value) {
let mut messageHash: Value = Value::Str("myTrades".into());
let mut executionType: Option<String> = self.safe_string_k(message.clone(), "x", &[]).as_str().map(str::to_owned);
if (executionType.as_deref() == Some("TRADE")) {
let mut trade: Value = self.parse_ws_trade(message.clone(), &[]);
let mut orderId: Value = self.safe_string_k(trade.clone(), "order", &[]);
let mut tradeFee: Value = self.safe_dict_k(trade.clone(), "fee", &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
tradeFee = self.extend(Value::Map({
let mut m = indexmap::IndexMap::new();
m
}), &[tradeFee.clone()]);
let mut symbol: Value = self.safe_string_k(trade.clone(), "symbol", &[]);
if (orderId != Value::Null) && (tradeFee != Value::Null) && (symbol != Value::Null) {
let mut cachedOrders: Value = self.orders.clone();
if (cachedOrders != Value::Null) {
let mut orders: Value = self.safe_dict(cachedOrders.hashmap(), symbol.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut order: Value = self.safe_dict(orders.clone(), orderId.clone(), &[]);
if (order != Value::Null) {
// accumulate order fees
let mut fees: Value = self.safe_value_k(order.clone(), "fees", &[]);
let mut fee: Value = self.safe_dict_k(order.clone(), "fee", &[]);
if !(self.is_empty(fees.clone()).as_bool() == Some(true)) {
let mut insertNewFeeCurrency: bool = true;
{
let mut i: Value = Value::Int(0);
let mut __for_first_86: bool = true;
while { if !__for_first_86 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_86 = false; i.as_f64().unwrap_or(f64::NAN) < get_array_length(&fees).as_f64().unwrap_or(f64::NAN) } {
let mut orderFee: Value = get_value(&fees, &i);
let mut orderFee: Value = get_value(&fees, &i);
if is_equal(&crate::value::get_value_k(&orderFee, "currency"), &tradeFee.as_map().and_then(|__m| __m.get("currency")).cloned().unwrap_or(Value::Null)) {
let mut feeCost: Value = self.sum(&[tradeFee.as_map().and_then(|__m| __m.get("cost")).cloned().unwrap_or(Value::Null), crate::value::get_value_k(&orderFee, "cost")]);
let mut feeCostString: Value = self.currency_to_precision(tradeFee.as_map().and_then(|__m| __m.get("currency")).cloned().unwrap_or(Value::Null), feeCost.clone(), &[]);
if (feeCostString == Value::Null) {
feeCostString = Value::Str("0".into());
}
add_element_to_object(get_value_mut(get_value_mut(&mut order, &Value::Str("fees".into())), &i), &Value::Str("cost".into()), (match &feeCostString { Value::Str(__parse_s) => __parse_s.trim().parse::<f64>().map(Value::Float).unwrap_or(Value::Null), Value::Float(__parse_f) => Value::Float(*__parse_f), Value::Int(__parse_n) => Value::Float(*__parse_n as f64), _ => Value::Null }));
insertNewFeeCurrency = false;
break;
}
}
}
if insertNewFeeCurrency {
crate::runtime::append_to_object_array(&mut order, &Value::Str("fees".into()), tradeFee.clone());
}
} else if (fee != Value::Null) {
if is_equal(&fee.as_map().and_then(|__m| __m.get("currency")).cloned().unwrap_or(Value::Null), &tradeFee.as_map().and_then(|__m| __m.get("currency")).cloned().unwrap_or(Value::Null)) {
let mut feeCost: Value = self.sum(&[fee.as_map().and_then(|__m| __m.get("cost")).cloned().unwrap_or(Value::Null), tradeFee.as_map().and_then(|__m| __m.get("cost")).cloned().unwrap_or(Value::Null)]);
let mut feeCostString: Value = self.currency_to_precision(tradeFee.as_map().and_then(|__m| __m.get("currency")).cloned().unwrap_or(Value::Null), feeCost, &[]);
if (feeCostString == Value::Null) {
feeCostString = Value::Str("0".into());
}
add_element_to_object(get_value_mut(&mut order, &Value::Str("fee".into())), &Value::Str("cost".into()), (match &feeCostString { Value::Str(__parse_s) => __parse_s.trim().parse::<f64>().map(Value::Float).unwrap_or(Value::Null), Value::Float(__parse_f) => Value::Float(*__parse_f), Value::Int(__parse_n) => Value::Float(*__parse_n as f64), _ => Value::Null }));
} else if (fee.as_map().and_then(|__m| __m.get("currency")).cloned().unwrap_or(Value::Null) == Value::Null) {
add_element_to_object(&mut order, &Value::Str("fee".into()), tradeFee.clone());
} else {
add_element_to_object(&mut order, &Value::Str("fees".into()), Value::from(vec![fee, tradeFee.clone()]));
add_element_to_object(&mut order, &Value::Str("fee".into()), Value::Null);
}
} else {
add_element_to_object(&mut order, &Value::Str("fee".into()), tradeFee);
}
// save this trade in the order
let mut orderTrades: Value = self.safe_list_k(order.clone(), "trades", &[Value::from(vec![])]);
append_to_array(&mut orderTrades, trade.clone());
add_element_to_object(&mut order, &Value::Str("trades".into()), orderTrades);
// write the updated order back into the cache: php
// arrays are value types, so the fee/trades mutations
// above only touched a local copy there — the cache
// hashmap rows are wired by reference, so this
// assignment reaches the cached row (and is a no-op
// in the reference-semantics runtimes)
add_element_to_object(&mut orders, &orderId, order);
}
}
}
if (self.myTrades.clone() == Value::Null) {
let mut limit: Value = self.safe_integer_k(self.options.clone(), "tradesLimit", &[Value::Int(1000)]);
self.myTrades = ArrayCacheBySymbolById::new(limit);
}
let mut myTrades: Value = self.myTrades.clone();
myTrades.append(trade);
client.resolve(&[self.myTrades.clone(), messageHash.clone()]);
let mut messageHashSymbol: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", messageHash, Value::Str(":".into())).into()), symbol).into());
client.resolve(&[self.myTrades.clone(), messageHashSymbol]);
}
}
pub fn handle_order(&mut self, mut client: Value, mut message: Value) {
let mut parsed: Value = self.parse_ws_order(message.clone(), &[]);
let mut symbol: Value = self.safe_string_k(parsed.clone(), "symbol", &[]);
let mut orderId: Value = self.safe_string_k(parsed.clone(), "id", &[]);
if (symbol != Value::Null) {
if (self.orders.clone() == Value::Null) {
let mut limit: Value = self.safe_integer_k(self.options.clone(), "ordersLimit", &[Value::Int(1000)]);
self.orders = ArrayCacheBySymbolById::new(limit);
}
let mut cachedOrders: Value = self.orders.clone();
let mut orders: Value = self.safe_dict(cachedOrders.hashmap(), symbol.clone(), &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut order: Value = self.safe_dict(orders, orderId, &[]);
if (order != Value::Null) {
let mut fee: Value = self.safe_value_k(order.clone(), "fee", &[]);
if (fee != Value::Null) {
add_element_to_object(&mut parsed, &Value::Str("fee".into()), fee);
}
let mut fees: Value = self.safe_value_k(order.clone(), "fees", &[]);
if (fees != Value::Null) {
add_element_to_object(&mut parsed, &Value::Str("fees".into()), fees);
}
add_element_to_object(&mut parsed, &Value::Str("trades".into()), self.safe_value_k(order.clone(), "trades", &[]));
let mut timestamp: Option<i64> = self.safe_integer_k(parsed.clone(), "timestamp", &[]).as_i64();
if (timestamp.is_none()) {
add_element_to_object(&mut parsed, &Value::Str("timestamp".into()), self.safe_integer_k(order.clone(), "timestamp", &[]));
add_element_to_object(&mut parsed, &Value::Str("datetime".into()), self.safe_string_k(order, "datetime", &[]));
}
}
cachedOrders.append(parsed);
let mut messageHash: Value = Value::Str("orders".into());
let mut symbolSpecificMessageHash: Value = Value::Str(format!("{}{}", Value::Str("orders:".into()), symbol).into());
client.resolve(&[cachedOrders.clone(), messageHash.clone()]);
client.resolve(&[cachedOrders, symbolSpecificMessageHash]);
}
}
pub fn handle_acount_update(&mut self, mut client: Value, mut message: Value) {
self.handle_balance(client.clone(), message.clone());
self.handle_positions(client.clone(), message.clone());
}
pub fn handle_options_account_update(&mut self, mut client: Value, mut message: Value) {
//
// BALANCE_POSITION_UPDATE (options user data stream)
//
// {
// "e": "BALANCE_POSITION_UPDATE",
// "E": 1762917544216, // event time
// "T": 1762917544206, // transaction time
// "m": "ORDER", // reason
// "B": [
// { "a": "USDT", "b": "10000471.37940900", "bc": "0" }
// ],
// "P": [
// {
// "s": "BTC-251123-126000-C",
// "c": "-0.1000",
// "p": "-120.00000000",
// "a": "1200.00000000"
// }
// ]
// }
//
// --- balance ---
let mut accountType: Value = Value::Str("option".into());
if (get_value(&self.balance, &accountType) == Value::Null) {
if let Value::Dict(__d) = &mut self.balance { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&accountType), Value::Map({
let mut m = indexmap::IndexMap::new();
m
})); }
}
add_element_to_object(get_value_mut(&mut self.balance, &accountType), &Value::Str("info".into()), message.clone());
if (accountType == Value::Null) {
return;
}
let mut B: Value = self.safe_list_k(message.clone(), "B", &[Value::from(vec![])]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_87: bool = true;
while { if !__for_first_87 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_87 = false; i.as_f64().unwrap_or(f64::NAN) < ((B.len() as i64) as f64) } {
let mut entry: Value = B.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut currencyId: Value = self.safe_string_k(entry.clone(), "a", &[]);
let mut code: Value = self.safe_currency_code(currencyId, &[]);
if (code != Value::Null) {
let mut account: Value = self.account();
if let Value::Dict(__d) = &mut account { std::sync::Arc::make_mut(__d).insert("total".into(), self.safe_string_k(entry, "b", &[])); }
add_element_to_object(get_value_mut(&mut self.balance, &accountType), &code, account);
}
}
}
let mut timestamp: Value = self.safe_integer_k(message.clone(), "E", &[]);
add_element_to_object(get_value_mut(&mut self.balance, &accountType), &Value::Str("timestamp".into()), timestamp.clone());
{ let __be_tmp = self.iso8601(timestamp.clone()); add_element_to_object(get_value_mut(&mut self.balance, &accountType), &Value::Str("datetime".into()), __be_tmp); };
{ let __be_tmp = self.safe_balance(get_value(&self.balance, &accountType)); if let Value::Dict(__d) = &mut self.balance { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&accountType), __be_tmp); } }
client.resolve(&[get_value(&self.balance, &accountType), Value::Str(format!("{}{}", accountType, Value::Str(":balance".into())).into())]);
// --- positions ---
if (self.positions.clone() == Value::Null) {
self.positions = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
}
if !(in_op(&self.positions, &accountType)) {
add_element_to_object(&mut self.positions, &accountType, ArrayCacheBySymbolBySide::new(Value::Null));
}
let mut cache: Value = get_value(&self.positions, &accountType);
let mut P: Value = self.safe_list_k(message.clone(), "P", &[Value::from(vec![])]);
let mut newPositions: Value = Value::from(vec![]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_88: bool = true;
while { if !__for_first_88 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_88 = false; i.as_f64().unwrap_or(f64::NAN) < ((P.len() as i64) as f64) } {
let mut rawPosition: Value = P.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut position: Value = self.parse_ws_options_position(rawPosition, &[]);
add_element_to_object(&mut position, &Value::Str("timestamp".into()), timestamp.clone());
add_element_to_object(&mut position, &Value::Str("datetime".into()), self.iso8601(timestamp.clone()));
append_to_array(&mut newPositions, position.clone());
cache.append(position);
}
}
let mut messageHashes: Value = self.find_message_hashes(client.clone(), Value::Str(format!("{}{}", accountType, Value::Str(":positions::".into())).into()));
{
let mut i: Value = Value::Int(0);
let mut __for_first_89: bool = true;
while { if !__for_first_89 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_89 = false; i.as_f64().unwrap_or(f64::NAN) < ((messageHashes.len() as i64) as f64) } {
let mut messageHash: Value = messageHashes.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut parts: Value = split(&messageHash, &Value::Str("::".into()));
let mut symbolsString: Value = parts.as_array().and_then(|__arr| __arr.get(1)).cloned().unwrap_or(Value::Null);
let mut symbols: Value = split(&symbolsString, &Value::Str(",".into()));
let mut positions: Value = self.filter_by_array(newPositions.clone(), Value::Str("symbol".into()), &[symbols, Value::Bool(false)]);
if !(self.is_empty(positions.clone()).as_bool() == Some(true)) {
client.resolve(&[positions, messageHash.clone()]);
}
}
}
client.resolve(&[newPositions, Value::Str(format!("{}{}", accountType, Value::Str(":positions".into())).into())]);
}
pub fn handle_ws_error(&self, mut client: Value, mut message: Value) {
//
// {
// "error": {
// "code": 2,
// "msg": "Invalid request: invalid stream"
// },
// "id": 1
// }
//
let mut id: Value = self.safe_string_k(message.clone(), "id", &[]);
let mut rejected: bool = false;
let mut error: Value = self.safe_dict_k(message.clone(), "error", &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut code: Value = self.safe_integer_k(error.clone(), "code", &[]);
let mut msg: Value = self.safe_string_k(error.clone(), "msg", &[]);
let mut codeValue: Value = (if (code == Value::Null) { Value::Int(0) } else { code });
let _try_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
self.handle_errors(codeValue.clone(), msg.clone(), client.as_map().and_then(|__m| __m.get("url")).cloned().unwrap_or(Value::Null), Value::Str("".into()), Value::Map({
let mut m = indexmap::IndexMap::new();
m
}), json_stringify(&error), error.clone(), Value::Map({
let mut m = indexmap::IndexMap::new();
m
}), Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
#[allow(unreachable_code)] { Value::Null }}));
if let Err(_try_err) = _try_result { let e: Value = panic_to_value(_try_err);
rejected = true;
// private endpoint uses id as messageHash
client.reject(&[e.clone(), id.clone()]);
// public endpoint stores messageHash in subscriptions
let mut subscriptionKeys: Value = object_keys(&get_value(&client, &Value::Str("subscriptions".into())));
{
let mut i: Value = Value::Int(0);
let mut __for_first_90: bool = true;
while { if !__for_first_90 { i = (match (&(i), &(Value::Int(1))) { (Value::Int(x), Value::Int(y)) => Value::Int(x + y), (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + *y), (Value::Float(x), Value::Int(y)) => Value::Float(*x + *y as f64), (Value::Float(x), Value::Float(y)) => Value::Float(x + y), _ => Value::Null }); } __for_first_90 = false; i.as_f64().unwrap_or(f64::NAN) < ((subscriptionKeys.len() as i64) as f64) } {
let mut subscriptionHash: Value = subscriptionKeys.as_array().and_then(|__arr| match &i { Value::Int(__n) => __arr.get(*__n as usize), Value::Str(__s) => __s.parse::<usize>().ok().and_then(|__n| __arr.get(__n)), _ => None }).cloned().unwrap_or(Value::Null);
let mut subscriptionId: Value = self.safe_string(get_value(&get_value(&client, &Value::Str("subscriptions".into())), &subscriptionHash), Value::Str("id".into()), &[]);
let mut subscription: Value = self.safe_string(get_value(&get_value(&client, &Value::Str("subscriptions".into())), &subscriptionHash), Value::Str("subscription".into()), &[]);
if (id.as_str() == subscriptionId.as_str()) {
client.reject(&[e.clone(), subscriptionHash]);
if (subscription != Value::Null) {
remove(&mut get_value(&client, &Value::Str("subscriptions".into())), &subscription);
}
}
}
}
}
if !rejected {
client.reject(&[message.clone(), id.clone()]);
}
// reset connection if 5xx error
let mut codeString: Value = self.safe_string_k(error.clone(), "code", &[]);
if (codeString != Value::Null) && (get_value(&codeString, &Value::Int(0)).as_str() == Some("5")) {
client.reset(message.clone());
}
}
pub fn handle_event_stream_terminated(&self, mut client: Value, mut message: Value) {
//
// {
// e: 'eventStreamTerminated',
// E: 1757896885229
// }
//
let mut event: Option<String> = self.safe_string_k(message.clone(), "e", &[]).as_str().map(str::to_owned);
let mut subscriptions: Value = get_value(&client, &Value::Str("subscriptions".into()));
let mut subscriptionsKeys: Value = object_keys(&subscriptions);
let mut accountType: Value = self.get_account_type_from_subscriptions(subscriptionsKeys).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
if (event.as_deref() == Some("eventStreamTerminated")) {
remove(&mut get_value(&client, &Value::Str("subscriptions".into())), &accountType);
client.reject(&[message.clone(), accountType]);
}
}
pub fn handle_message(&mut self, mut client: Value, mut message: Value) {
// eOptions combined stream endpoints (/public/stream, /market/stream) wrap events as:
// { "stream": "<streamName>", "data": { "e": "...", ... } }
let mut streamWrapper: Option<String> = self.safe_string_k(message.clone(), "stream", &[]).as_str().map(str::to_owned);
if (streamWrapper.is_some()) {
message = self.safe_dict_k(message.clone(), "data", &[message.clone()]);
}
// handle WebSocketAPI
let mut eventMsg: Value = self.safe_dict_k(message.clone(), "event", &[]);
if (eventMsg != Value::Null) {
message = eventMsg;
}
// handle combined stream wrapper payloads
let mut eventData: Value = self.safe_dict_k(message.clone(), "data", &[]);
if (eventData != Value::Null) {
message = eventData;
}
let mut status: Option<String> = self.safe_string_k(message.clone(), "status", &[]).as_str().map(str::to_owned);
let mut error: Value = self.safe_value_k(message.clone(), "error", &[]);
if (error != Value::Null) || ((status.is_some()) && (status.as_deref() != Some("200"))) {
self.handle_ws_error(client.clone(), message.clone());
return;
}
// user subscription wraps message in subscriptionId and event
let mut id: Value = self.safe_string_k(message.clone(), "id", &[]);
let mut subscriptions: Value = self.safe_dict(get_value(&client, &Value::Str("subscriptions".into())), id.clone(), &[]);
let mut method: Value = self.safe_value_k(subscriptions, "method", &[]);
if (method != Value::Null) {
self.dispatch_ws_handler(&method, &[client.clone(), message.clone()]);
return;
}
// handle other APIs
let mut methods: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("depthUpdate".to_string(), Value::Str("handle_order_book".into()).clone());
m.insert("trade".to_string(), Value::Str("handle_trade".into()).clone());
m.insert("aggTrade".to_string(), Value::Str("handle_trade".into()).clone());
m.insert("price".to_string(), Value::Str("handle_stock_price".into()).clone());
m.insert("quote".to_string(), Value::Str("handle_stock_quote".into()).clone());
m.insert("optionTrade".to_string(), Value::Str("handle_trade".into()).clone());
m.insert("markPrice".to_string(), Value::Str("handle_mark_prices".into()).clone());
m.insert("kline".to_string(), Value::Str("handle_ohlcv".into()).clone());
m.insert("markPrice_kline".to_string(), Value::Str("handle_ohlcv".into()).clone());
m.insert("indexPrice_kline".to_string(), Value::Str("handle_ohlcv".into()).clone());
m.insert("1hTicker@arr".to_string(), Value::Str("handle_tickers".into()).clone());
m.insert("4hTicker@arr".to_string(), Value::Str("handle_tickers".into()).clone());
m.insert("1dTicker@arr".to_string(), Value::Str("handle_tickers".into()).clone());
m.insert("24hrTicker@arr".to_string(), Value::Str("handle_tickers".into()).clone());
m.insert("24hrMiniTicker@arr".to_string(), Value::Str("handle_tickers".into()).clone());
m.insert("1hTicker".to_string(), Value::Str("handle_tickers".into()).clone());
m.insert("4hTicker".to_string(), Value::Str("handle_tickers".into()).clone());
m.insert("1dTicker".to_string(), Value::Str("handle_tickers".into()).clone());
m.insert("24hrTicker".to_string(), Value::Str("handle_tickers".into()).clone());
m.insert("24hrMiniTicker".to_string(), Value::Str("handle_tickers".into()).clone());
m.insert("markPriceUpdate".to_string(), Value::Str("handle_mark_prices".into()).clone());
m.insert("markPriceUpdate@arr".to_string(), Value::Str("handle_mark_prices".into()).clone());
m.insert("markPrice@arr".to_string(), Value::Str("handle_mark_prices".into()).clone());
m.insert("bookTicker".to_string(), Value::Str("handle_bids_asks".into()).clone());
m.insert("outboundAccountPosition".to_string(), Value::Str("handle_balance".into()).clone());
m.insert("balanceUpdate".to_string(), Value::Str("handle_balance".into()).clone());
m.insert("ACCOUNT_UPDATE".to_string(), Value::Str("handle_acount_update".into()).clone());
m.insert("BALANCE_POSITION_UPDATE".to_string(), Value::Str("handle_options_account_update".into()).clone());
m.insert("executionReport".to_string(), Value::Str("handle_order_update".into()).clone());
m.insert("orderReport".to_string(), Value::Str("handle_order_update".into()).clone());
m.insert("ORDER_TRADE_UPDATE".to_string(), Value::Str("handle_order_update".into()).clone());
m.insert("ALGO_UPDATE".to_string(), Value::Str("handle_order_update".into()).clone());
m.insert("forceOrder".to_string(), Value::Str("handle_liquidation".into()).clone());
m.insert("eventStreamTerminated".to_string(), Value::Str("handle_event_stream_terminated".into()).clone());
m.insert("externalLockUpdate".to_string(), Value::Str("handle_balance".into()).clone());
m
});
let mut event: Value = self.safe_string_k(message.clone(), "e", &[]);
if (matches!(&message, Value::Arr(_))) {
let mut arrayMessage: Value = message.as_array().and_then(|__arr| __arr.get(0)).cloned().unwrap_or(Value::Null);
event = Value::Str(format!("{}{}", self.safe_string_k(arrayMessage, "e", &[]), Value::Str("@arr".into())).into());
}
method = self.safe_value(methods, event.clone(), &[]);
if (method == Value::Null) {
let mut requestId: Option<String> = self.safe_string_k(message.clone(), "id", &[]).as_str().map(str::to_owned);
if (requestId.is_some()) {
self.handle_subscription_status(client.clone(), message.clone());
return;
}
// special case for the real-time bookTicker, since it comes without an event identifier
//
// {
// "u": 7488717758,
// "s": "BTCUSDT",
// "b": "28621.74000000",
// "B": "1.43278800",
// "a": "28621.75000000",
// "A": "2.52500800"
// }
//
if (event == Value::Null) && (in_op(&message, &Value::Str("a".into()))) && (in_op(&message, &Value::Str("b".into()))) {
self.handle_bids_asks(client.clone(), message.clone());
}
} else {
self.dispatch_ws_handler(&method, &[client.clone(), message.clone()]);
}
}
}