// 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 ParadexCore {
pub parent: crate::exchanges::paradex::ParadexCore,
}
impl ParadexCore {
pub fn new(config: Option<crate::Value>) -> Self {
let mut s = Self { parent: crate::exchanges::paradex::ParadexCore::new(config) };
s.init();
s
}
pub fn init(&mut self) {
let described = ParadexCore::describe(self);
self.initialize_properties(described);
// compatibility for sandbox/testnet options in userConfig
let user_config = self.userConfig.clone();
<Self as crate::exchange_generated::ExchangeBase>::after_construct(self, &[user_config]);
}
/// 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 ParadexCore {
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 ParadexCore {
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,
"get_ws_url" => self.get_ws_url(),
"handle_error_message" => self.handle_error_message(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_ticker" => self.handle_ticker(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(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)),
"parse_funding_rate_ws" => self.parse_funding_rate_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"parse_sbe_funding_data" => self.parse_sbe_funding_data(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)),
"parse_sbe_market_summary" => self.parse_sbe_market_summary(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)),
"parse_sbe_message" => self.parse_sbe_message(args.get(0).cloned().unwrap_or(crate::Value::Null)),
"parse_sbe_order" => self.parse_sbe_order(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)),
"parse_sbe_order_book" => self.parse_sbe_order_book(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)),
"parse_sbe_trade" => self.parse_sbe_trade(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)),
"request_id" => self.request_id(),
"sbe_read_timestamp" => self.sbe_read_timestamp(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"sbe_read_uint" => self.sbe_read_uint(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)),
"sbe_read_var_strings" => self.sbe_read_var_strings(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)),
"watch_funding_rate" => self.watch_funding_rate(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_funding_rates" => self.watch_funding_rates(&args[..]).await,
"watch_order_book" => self.watch_order_book(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]).await,
"watch_orders" => self.watch_orders(&args[..]).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,
// 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 ParadexCore {
/// 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 },
"get_ws_url" => self.get_ws_url(),
"handle_authentication_message" => { self.handle_authentication_message(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)); crate::Value::Null },
"handle_error_message" => self.handle_error_message(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"handle_funding_rate" => { self.handle_funding_rate(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_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_ticker" => self.handle_ticker(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(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)),
"parse_funding_rate_ws" => self.parse_funding_rate_ws(args.get(0).cloned().unwrap_or(crate::Value::Null), &args[1.min(args.len())..]),
"parse_sbe_funding_data" => self.parse_sbe_funding_data(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)),
"parse_sbe_market_summary" => self.parse_sbe_market_summary(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)),
"parse_sbe_message" => self.parse_sbe_message(args.get(0).cloned().unwrap_or(crate::Value::Null)),
"parse_sbe_order" => self.parse_sbe_order(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)),
"parse_sbe_order_book" => self.parse_sbe_order_book(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)),
"parse_sbe_trade" => self.parse_sbe_trade(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)),
"request_id" => self.request_id(),
"sbe_read_timestamp" => self.sbe_read_timestamp(args.get(0).cloned().unwrap_or(crate::Value::Null), args.get(1).cloned().unwrap_or(crate::Value::Null)),
"sbe_read_uint" => self.sbe_read_uint(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)),
"sbe_read_var_strings" => self.sbe_read_var_strings(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)),
"watch_funding_rate" => { crate::exchange_stubs::enqueue_spawn("watch_funding_rate", args.to_vec()); crate::Value::Null },
"watch_funding_rates" => { crate::exchange_stubs::enqueue_spawn("watch_funding_rates", args.to_vec()); crate::Value::Null },
"watch_order_book" => { crate::exchange_stubs::enqueue_spawn("watch_order_book", args.to_vec()); crate::Value::Null },
"watch_orders" => { crate::exchange_stubs::enqueue_spawn("watch_orders", 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 },
_ => crate::Value::Null,
}
}
}
impl std::ops::Deref for ParadexCore {
type Target = crate::exchange::Exchange;
fn deref(&self) -> &crate::exchange::Exchange { std::ops::Deref::deref(&self.parent) }
}
impl std::ops::DerefMut for ParadexCore {
fn deref_mut(&mut self) -> &mut crate::exchange::Exchange { std::ops::DerefMut::deref_mut(&mut self.parent) }
}
impl ParadexCore {
pub fn describe(&self) -> Value {
return self.deep_extend(self.parent.describe(), &[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("watchFundingRate".to_string(), Value::Bool(true));
m.insert("watchFundingRates".to_string(), Value::Bool(true));
m.insert("watchTicker".to_string(), Value::Bool(true));
m.insert("watchTickers".to_string(), Value::Bool(true));
m.insert("watchOrderBook".to_string(), Value::Bool(true));
m.insert("watchOrders".to_string(), Value::Bool(true));
m.insert("watchTrades".to_string(), Value::Bool(true));
m.insert("watchTradesForSymbols".to_string(), Value::Bool(false));
m.insert("watchBalance".to_string(), Value::Bool(false));
m.insert("watchOHLCV".to_string(), Value::Bool(false));
m
}));
m.insert("urls".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("logo".to_string(), Value::Str("https://x.com/tradeparadex/photo".into()));
m.insert("api".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("ws".to_string(), Value::Str("wss://ws.api.prod.paradex.trade/v1".into()));
m
}));
m.insert("test".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("ws".to_string(), Value::Str("wss://ws.api.testnet.paradex.trade/v1".into()));
m
}));
m.insert("www".to_string(), Value::Str("https://www.paradex.trade/".into()));
m.insert("doc".to_string(), Value::Str("https://docs.api.testnet.paradex.trade/".into()));
m.insert("fees".to_string(), Value::Str("https://docs.paradex.trade/getting-started/trading-fees".into()));
m.insert("referral".to_string(), Value::Str("".into()));
m
}));
m.insert("options".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("sbe".to_string(), Value::Bool(true));
m.insert("sbeSchemaVersion".to_string(), Value::Str("3".into()));
m.insert("decompressBinary".to_string(), Value::Bool(false));
m
}));
m.insert("streaming".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m
}));
m
})]);
Value::Null
}
pub fn get_ws_url(&self) -> Value {
let mut url: 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);
if !matches!(self.safe_bool_k(self.options.clone(), "sbe", &[Value::Bool(true)]), Value::Bool(true)) {
return url;
}
let mut schemaVersion: Value = self.safe_string_k(self.options.clone(), "sbeSchemaVersion", &[Value::Str("3".into())]);
return Value::Str(format!("{}{}", add(&url, &Value::Str("?sbeSchemaId=1&sbeSchemaVersion=".into())), schemaVersion).into());
Value::Null
}
pub fn get_order_book_channel(&self, mut marketId: Value) -> Option<String> {
return Value::Str(format!("{}{}", Value::Str(format!("{}{}", Value::Str("order_book.".into()), marketId).into()), Value::Str(".snapshot@15@100ms".into())).into()).as_str().map(str::to_owned);
}
pub fn request_id(&mut self) -> Value {
let mut requestId: Value = self.sum(&[self.safe_integer_k(self.options.clone(), "requestId", &[Value::Int(0)]), Value::Int(1)]);
if let Value::Dict(__d) = &mut self.options { std::sync::Arc::make_mut(__d).insert("requestId".into(), requestId.clone()); }
return requestId;
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 url: Value = self.get_ws_url();
let mut client: Value = self.client(&[url.clone()]);
let mut messageHash: Value = Value::Str("authenticated".into());
let mut future: Value = client.reusable_future(Value::Str("authenticated".into()));
let mut authenticated: Value = self.safe_value(get_value(&client, &Value::Str("subscriptions".into())), messageHash.clone(), &[]);
if (authenticated == Value::Null) {
let mut token: Value = self.parent.authenticate_rest(&[]).await;
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("jsonrpc".to_string(), Value::Str("2.0".into()));
m.insert("id".to_string(), self.request_id());
m.insert("method".to_string(), Value::Str("auth".into()));
m.insert("params".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("bearer".to_string(), token);
m
}));
m
});
let __ws_arg_0 = self.deep_extend(request, &[params]);
self.watch(url, messageHash.clone(), &[__ws_arg_0, messageHash.clone()]).await;
}
return crate::exchange_stubs::ws_await_flight(&future).await;
Value::Null
}
pub fn handle_authentication_message(&self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "jsonrpc": "2.0",
// "id": 1,
// "result": { "node_id": "73cf456f7cb78d59" }
// }
//
let mut result: Value = (match message.get("result") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Null });
if (result != Value::Null) {
// client.resolve (true, messageHash);
let mut future: Value = self.safe_value(get_value(&client, &Value::Str("futures".into())), Value::Str("authenticated".into()), &[]);
if (future != Value::Null) {
future.resolve(&[Value::Bool(true)]);
}
}
}
/*
* @method
* @name paradex#watchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://docs.paradex.trade/ws/web-socket-channels/trades/trades
* @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
* @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 (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut messageHash: Value = Value::Str("trades.".into());
if (symbol != Value::Null) {
let mut market: Value = self.market(symbol.clone());
messageHash = Value::Str(format!("{}{}", messageHash, market.as_map().and_then(|__m| __m.get("id")).cloned().unwrap_or(Value::Null)).into());
} else {
messageHash = Value::Str(format!("{}{}", messageHash, Value::Str("ALL".into())).into());
}
let mut url: Value = self.get_ws_url();
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("jsonrpc".to_string(), Value::Str("2.0".into()));
m.insert("method".to_string(), Value::Str("subscribe".into()));
m.insert("params".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("channel".to_string(), messageHash.clone());
m
}));
m
});
let __ws_arg_1 = self.deep_extend(request, &[params]);
let mut trades: Value = self.watch(url, messageHash.clone(), &[__ws_arg_1, messageHash.clone()]).await;
let mut limitResolved: Value = limit.clone();
if is_true(&self.newUpdates) {
limitResolved = trades.get_limit(symbol, limit);
}
return self.filter_by_since_limit(trades, &[since, limitResolved, Value::Str("timestamp".into()), Value::Bool(true)]);
Value::Null
}
pub fn handle_trade(&mut self, mut client: Value, mut message: Value) -> 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;
//
// {
// "jsonrpc": "2.0",
// "method": "subscription",
// "params": {
// "channel": "trades.ALL",
// "data": {
// "id": "1718179273230201709233240002",
// "market": "kBONK-USD-PERP",
// "side": "BUY",
// "size": "34028",
// "price": "0.028776",
// "created_at": 1718179273230,
// "trade_type": "FILL"
// }
// }
// }
//
let mut params: Value = (match __pro_message.get("params").cloned() { Some(__v) if matches!(__v, Value::Dict(_)) => __v, _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut data: Value = self.safe_dict_k(params.clone(), "data", &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut parsedTrade: Value = self.parse_trade(data, &[]);
let mut symbol: Value = parsedTrade.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
let mut messageHash: Value = self.safe_string_k(params, "channel", &[]);
let mut stored: Value = self.safe_value(self.trades.clone(), symbol.clone(), &[]);
if (stored == Value::Null) {
stored = ArrayCache::new(self.safe_integer_k(self.options.clone(), "tradesLimit", &[Value::Int(1000)]));
if let Value::Dict(__d) = &mut self.trades { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), stored.clone()); }
}
stored.append(parsedTrade);
client.resolve(&[stored.clone(), messageHash.clone()]);
// an SBE frame names its market but not the subscription it was sent for,
// so the trades.ALL subscription is resolved from the per-market channel too
let mut isSbe: Value = (match __pro_message.get("sbe").cloned() { Some(__v) if matches!(__v, Value::Bool(_)) => __v, _ => Value::Bool(false) });
if is_true(&isSbe) && (messageHash.as_str() != Some("trades.ALL")) {
client.resolve(&[stored, Value::Str("trades.ALL".into())]);
}
return message;
Value::Null
}
/*
* @method
* @name paradex#watchOrderBook
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://docs.paradex.trade/ws/web-socket-channels/order-book/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} an [order book structure]{@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
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut market: Value = self.market(symbol);
let mut messageHash: Value = self.get_order_book_channel(market.as_map().and_then(|__m| __m.get("id")).cloned().unwrap_or(Value::Null)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
let mut url: Value = self.get_ws_url();
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("jsonrpc".to_string(), Value::Str("2.0".into()));
m.insert("method".to_string(), Value::Str("subscribe".into()));
m.insert("params".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("channel".to_string(), messageHash.clone());
m
}));
m
});
let __ws_arg_2 = self.deep_extend(request, &[params]);
let mut orderbook: Value = self.watch(url, messageHash.clone(), &[__ws_arg_2, messageHash.clone()]).await;
return orderbook.limit();
Value::Null
}
pub fn handle_order_book(&mut self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "jsonrpc": "2.0",
// "method": "subscription",
// "params": {
// "channel": "order_book.BTC-USD-PERP.snapshot@15@50ms",
// "data": {
// "seq_no": 14127815,
// "market": "BTC-USD-PERP",
// "last_updated_at": 1718267837265,
// "update_type": "s",
// "inserts": [
// {
// "side": "BUY",
// "price": "67629.7",
// "size": "0.992"
// },
// {
// "side": "SELL",
// "price": "69378.6",
// "size": "3.137"
// }
// ],
// "updates": [],
// "deletes": []
// }
// }
// }
//
let mut params: Value = (match message.get("params") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut data: Value = self.safe_dict_k(params.clone(), "data", &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut marketId: Value = self.safe_string_k(data.clone(), "market", &[]);
let mut market: Value = self.safe_market(&[marketId]);
let mut timestamp: Value = self.safe_integer_k(data.clone(), "last_updated_at", &[]);
let mut symbol: Value = market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
if !(in_op(&self.orderbooks, &symbol)) {
{ let __be_tmp = self.order_book(&[]); if let Value::Dict(__d) = &mut self.orderbooks { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), __be_tmp); } }
}
// levels go into local arrays first: pushing into orderbookData['bids'] appends to a copy in go
let mut bids: Value = Value::from(vec![]);
let mut asks: Value = Value::from(vec![]);
let mut inserts: Value = self.safe_list_k(data.clone(), "inserts", &[]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_569: bool = true;
while { if !__for_first_569 { 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_569 = false; i.as_f64().unwrap_or(f64::NAN) < ((inserts.len() as i64) as f64) } {
let mut insert: Value = self.safe_dict(inserts.clone(), i.clone(), &[]);
let mut side: Option<String> = self.safe_string_k(insert.clone(), "side", &[]).as_str().map(str::to_owned);
let mut price: Value = self.safe_string_k(insert.clone(), "price", &[]);
let mut size: Value = self.safe_string_k(insert.clone(), "size", &[]);
if (side.as_deref() == Some("BUY")) {
append_to_array(&mut bids, Value::from(vec![price.clone(), size.clone()]));
} else {
append_to_array(&mut asks, Value::from(vec![price, size]));
}
}
}
let mut orderbookData: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("bids".to_string(), bids);
m.insert("asks".to_string(), asks);
m
});
let mut orderbook: Value = get_value(&self.orderbooks, &symbol);
let mut snapshot: Value = self.parse_order_book(orderbookData, symbol, &[timestamp, Value::Str("bids".into()), Value::Str("asks".into())]);
add_element_to_object(&mut snapshot, &Value::Str("nonce".into()), self.safe_integer_k(data, "seq_no", &[]));
orderbook.reset(snapshot);
let mut messageHash: Value = self.safe_string_k(params, "channel", &[]);
client.resolve(&[orderbook, messageHash]);
}
/*
* @method
* @name paradex#watchTicker
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @see https://docs.paradex.trade/ws/web-socket-channels/markets-summary/markets-summary
* @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 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;
}
let mut market: Value = self.market(symbol);
// the per-market channel: the all-markets one carries a summary for every listed
// option too, thousands of messages a second, all decoded only to be filtered out
let mut channel: Value = Value::Str(format!("{}{}", Value::Str("markets_summary.".into()), market.as_map().and_then(|__m| __m.get("id")).cloned().unwrap_or(Value::Null)).into());
let mut url: Value = self.get_ws_url();
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("jsonrpc".to_string(), Value::Str("2.0".into()));
m.insert("method".to_string(), Value::Str("subscribe".into()));
m.insert("params".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("channel".to_string(), channel.clone());
m
}));
m
});
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str("markets_summary.".into()), market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null)).into());
let __ws_arg_3 = self.deep_extend(request, &[params]);
return self.watch(url, messageHash, &[__ws_arg_3, channel]).await;
Value::Null
}
/*
* @method
* @name paradex#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://docs.paradex.trade/ws/web-socket-channels/markets-summary/markets-summary
* @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 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
}));
if (self.markets.clone() == Value::Null) {
self.load_markets(&[]).await;
}
let mut symbolsNormalized: Value = self.market_symbols(&[symbols]);
let mut channel: Value = Value::Str("markets_summary".into());
let mut url: Value = self.get_ws_url();
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("jsonrpc".to_string(), Value::Str("2.0".into()));
m.insert("method".to_string(), Value::Str("subscribe".into()));
m.insert("params".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("channel".to_string(), channel.clone());
m
}));
m
});
let mut messageHashes: Value = Value::from(vec![]);
if (symbolsNormalized != Value::Null) && (matches!(&symbolsNormalized, Value::Arr(_))) {
{
let mut i: Value = Value::Int(0);
let mut __for_first_570: bool = true;
while { if !__for_first_570 { 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_570 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbolsNormalized.len() as i64) as f64) } {
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", channel, Value::Str(".".into())).into()), symbolsNormalized.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());
append_to_array(&mut messageHashes, messageHash);
}
}
} else {
append_to_array(&mut messageHashes, channel);
}
let __ws_arg_4 = self.deep_extend(request, &[params]);
let mut newTicker: Value = self.watch_multiple(url, messageHashes.clone(), &[__ws_arg_4, messageHashes.clone()]).await;
if is_true(&self.newUpdates) {
let mut result: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
let mut newTickerSymbol: Value = self.safe_string_k(newTicker.clone(), "symbol", &[]);
if (newTickerSymbol != Value::Null) {
if let Value::Dict(__d) = &mut result { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&newTickerSymbol), newTicker); }
}
return result;
}
return self.filter_by_array(self.tickers.clone(), Value::Str("symbol".into()), &[symbolsNormalized]);
Value::Null
}
/*
* @method
* @name paradex#watchOrders
* @description watches information on multiple orders made by the user
* @see https://docs.paradex.trade/ws/web-socket-channels/orders/orders
* @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
* @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;
}
self.authenticate(&[]).await;
let mut messageHash: Value = Value::Str("orders".into());
let mut channel: Value = Value::Str("orders.".into());
let mut symbolResolved: Value = (if (symbol != Value::Null) { self.symbol(symbol.clone()) } else { symbol.clone() });
if (symbol != Value::Null) {
let mut market: Value = self.market(symbol);
channel = Value::Str(format!("{}{}", channel, market.as_map().and_then(|__m| __m.get("id")).cloned().unwrap_or(Value::Null)).into());
messageHash = Value::Str(format!("{}{}", messageHash, Value::Str(format!("{}{}", Value::Str(":".into()), symbolResolved).into())).into());
} else {
channel = Value::Str(format!("{}{}", channel, Value::Str("ALL".into())).into());
}
let mut url: Value = self.get_ws_url();
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("jsonrpc".to_string(), Value::Str("2.0".into()));
m.insert("method".to_string(), Value::Str("subscribe".into()));
m.insert("params".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("channel".to_string(), channel.clone());
m
}));
m
});
let __ws_arg_5 = self.deep_extend(request, &[params]);
let mut orders: Value = self.watch(url, messageHash, &[__ws_arg_5, channel]).await;
let mut limitResolved: Value = limit.clone();
if is_true(&self.newUpdates) {
limitResolved = orders.get_limit(symbolResolved.clone(), limit);
}
return self.filter_by_symbol_since_limit(orders, &[symbolResolved, since, limitResolved, Value::Bool(true)]);
Value::Null
}
pub fn handle_order(&mut self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "jsonrpc": "2.0",
// "method": "subscription",
// "params": {
// "channel": "orders.ALL",
// "data": {
// "account": "0x4638e3041366aa71720be63e32e53e1223316c7f0d56f7aa617542ed1e7512x",
// "avg_fill_price": "26000",
// "client_id": "x1234",
// "cancel_reason": "",
// "created_at": 1681493746016,
// "flags": ["REDUCE_ONLY"],
// "id": "123456",
// "instruction": "GTC",
// "last_updated_at": 1681493746016,
// "market": "BTC-USD-PERP",
// "price": "26000",
// "remaining_size": "0",
// "side": "BUY",
// "size": "0.05",
// "status": "NEW",
// "type": "LIMIT"
// }
// }
// }
//
let mut params: Value = (match message.get("params") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut data: Value = self.safe_dict_k(params, "data", &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut parsed: Value = self.parse_order(data, &[]);
let mut symbol: Value = self.safe_string_k(parsed.clone(), "symbol", &[]);
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);
}
self.orders.append(parsed);
let mut messageHash: Value = Value::Str("orders".into());
client.resolve(&[self.orders.clone(), messageHash.clone()]);
if (symbol != Value::Null) {
let mut symbolMessageHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", messageHash, Value::Str(":".into())).into()), symbol).into());
client.resolve(&[self.orders.clone(), symbolMessageHash]);
}
}
pub fn handle_ticker(&mut self, mut client: Value, mut message: Value) -> 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;
//
// {
// "jsonrpc": "2.0",
// "method": "subscription",
// "params": {
// "channel": "markets_summary",
// "data": {
// "symbol": "ORDI-USD-PERP",
// "oracle_price": "49.80885481",
// "mark_price": "49.80885481",
// "last_traded_price": "62.038",
// "bid": "49.822",
// "ask": "58.167",
// "volume_24h": "0",
// "total_volume": "54542628.66054200416",
// "created_at": 1718334307698,
// "underlying_price": "47.93",
// "open_interest": "6999.5",
// "funding_rate": "0.03919997509811",
// "price_change_rate_24h": ""
// }
// }
// }
//
let mut params: Value = (match __pro_message.get("params").cloned() { Some(__v) if matches!(__v, Value::Dict(_)) => __v, _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut data: Value = self.safe_dict_k(params.clone(), "data", &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut marketId: Value = self.safe_string_k(data.clone(), "symbol", &[]);
let mut market: Value = self.safe_market(&[marketId]);
let mut symbol: Value = market.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
let mut channel: Value = self.safe_string_k(params, "channel", &[]);
let mut ticker: Value = self.parse_ticker(data, &[market]);
if let Value::Dict(__d) = &mut self.tickers { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), ticker.clone()); }
client.resolve(&[ticker.clone(), channel]);
// watchTicker waits on the symbol whichever channel, per-market or all, carried it
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str("markets_summary.".into()), symbol).into());
client.resolve(&[ticker, messageHash]);
return message;
Value::Null
}
/*
* @method
* @name paradex#watchFundingRate
* @description watch the current funding rate for a symbol
* @see https://docs.paradex.trade/ws/web-socket-channels/funding-data-market-symbol/funding-data-market-symbol
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [funding rate structure]{@link https://docs.ccxt.com/?id=funding-rate-structure}
*/
pub async fn watch_funding_rate(&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 symbolValue: Value = self.symbol(symbol.clone());
let mut channel: Value = Value::Str("funding_data".into());
let mut url: Value = self.get_ws_url();
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("jsonrpc".to_string(), Value::Str("2.0".into()));
m.insert("method".to_string(), Value::Str("subscribe".into()));
m.insert("params".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("channel".to_string(), channel.clone());
m
}));
m
});
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", channel, Value::Str(".".into())).into()), symbolValue).into());
let __ws_arg_6 = self.deep_extend(request, &[params]);
return self.watch(url, messageHash.clone(), &[__ws_arg_6, messageHash.clone()]).await;
Value::Null
}
/*
* @method
* @name paradex#watchFundingRates
* @description watch the funding rate for multiple markets
* @see https://docs.paradex.trade/ws/web-socket-channels/markets-summary/markets-summary
* @param {string[]} [symbols] a list of unified market symbols
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [funding rate structure]{@link https://docs.ccxt.com/?id=funding-rate-structure}
*/
pub async fn watch_funding_rates(&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 symbolsNormalized: Value = self.market_symbols(&[symbols]);
let mut channel: Value = Value::Str("funding_data".into());
let mut url: Value = self.get_ws_url();
let mut request: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("jsonrpc".to_string(), Value::Str("2.0".into()));
m.insert("method".to_string(), Value::Str("subscribe".into()));
m.insert("params".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("channel".to_string(), channel.clone());
m
}));
m
});
let mut messageHashes: Value = Value::from(vec![]);
if (symbolsNormalized != Value::Null) {
let mut symbolsLength: f64 = ((symbolsNormalized.len() as i64) as f64);
if symbolsLength > ((0i64) as f64) {
{
let mut i: Value = Value::Int(0);
let mut __for_first_571: bool = true;
while { if !__for_first_571 { 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_571 = false; i.as_f64().unwrap_or(f64::NAN) < ((symbolsNormalized.len() as i64) as f64) } {
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", channel, Value::Str(".".into())).into()), symbolsNormalized.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());
append_to_array(&mut messageHashes, messageHash);
}
}
} else {
append_to_array(&mut messageHashes, channel.clone()); // if an empty array is passed, subscribe to all funding rates
}
} else {
append_to_array(&mut messageHashes, channel);
}
let __ws_arg_7 = self.deep_extend(request, &[params]);
let mut newFundingRates: Value = self.watch_multiple(url, messageHashes.clone(), &[__ws_arg_7, messageHashes.clone()]).await;
if is_true(&self.newUpdates) {
let mut result: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m
});
let mut newFundingRatesSymbol: Value = self.safe_string_k(newFundingRates.clone(), "symbol", &[]);
if (newFundingRatesSymbol != Value::Null) {
if let Value::Dict(__d) = &mut result { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&newFundingRatesSymbol), newFundingRates); }
}
return result;
}
return self.filter_by_array(self.fundingRates.clone(), Value::Str("symbol".into()), &[symbolsNormalized]);
Value::Null
}
pub fn handle_funding_rate(&mut self, mut client: Value, mut message: Value) {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "jsonrpc": "2.0",
// "method": "subscription",
// "params": {
// "channel": "funding_data",
// "data": {
// "market": "TRUMP-USD-PERP",
// "funding_index": "-0.551694014226244835",
// "funding_premium": "-0.000509914923994872836",
// "funding_rate": "-0.00014969570582",
// "funding_rate_8h": "-0.00014969",
// "funding_period_hours": 8,
// "created_at": 1771506636154
// }
// }
// }
//
let mut params: Value = (match message.get("params") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Map({
let mut m = indexmap::IndexMap::new();
m
}) });
let mut data: Value = self.safe_dict_k(params.clone(), "data", &[Value::Map({
let mut m = indexmap::IndexMap::new();
m
})]);
let mut fundingRate: Value = self.parse_funding_rate_ws(data, &[]);
let mut symbol: Value = fundingRate.as_map().and_then(|__m| __m.get("symbol")).cloned().unwrap_or(Value::Null);
if let Value::Dict(__d) = &mut self.fundingRates { std::sync::Arc::make_mut(__d).insert(crate::runtime::stringify_param(&symbol), fundingRate.clone()); }
let mut channel: Value = self.safe_string_k(params, "channel", &[]);
// watchFundingRates without symbols waits on the bare channel
client.resolve(&[fundingRate.clone(), channel.clone()]);
if (channel != Value::Null) {
let mut messageHash: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", channel, Value::Str(".".into())).into()), symbol).into());
client.resolve(&[fundingRate, messageHash]);
}
}
pub fn parse_funding_rate_ws(&self, mut contract: Value, optional_args: &[Value]) -> Value {
let mut market = get_arg(optional_args, 0, Value::Null);
//
// {
// "market": "TRUMP-USD-PERP",
// "funding_index": "-0.551694014226244835",
// "funding_premium": "-0.000509914923994872836",
// "funding_rate": "-0.00014969570582",
// "funding_rate_8h": "-0.00014969",
// "funding_period_hours": 8,
// "created_at": 1771506636154
// }
//
let mut marketId: Value = self.safe_string_k(contract.clone(), "market", &[]);
let mut symbol: Value = self.safe_symbol(marketId, &[market]);
let mut timestamp: Value = self.safe_integer_k(contract.clone(), "created_at", &[]);
let mut fundingPeriod: Value = self.safe_string_k(contract.clone(), "funding_period_hours", &[]);
let mut interval: Value = Value::Null;
if (fundingPeriod != Value::Null) {
interval = Value::Str(format!("{}{}", fundingPeriod, Value::Str("h".into())).into());
}
return Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("info".to_string(), contract.clone());
m.insert("symbol".to_string(), symbol);
m.insert("markPrice".to_string(), Value::Null);
m.insert("indexPrice".to_string(), Value::Null);
m.insert("interestRate".to_string(), self.parse_number(Value::Str("0".into()), &[]));
m.insert("estimatedSettlePrice".to_string(), Value::Null);
m.insert("timestamp".to_string(), timestamp.clone());
m.insert("datetime".to_string(), self.iso8601(timestamp));
m.insert("fundingRate".to_string(), self.safe_number_k(contract, "funding_rate", &[]));
m.insert("fundingTimestamp".to_string(), Value::Null);
m.insert("fundingDatetime".to_string(), Value::Null);
m.insert("nextFundingRate".to_string(), Value::Null);
m.insert("nextFundingTimestamp".to_string(), Value::Null);
m.insert("nextFundingDatetime".to_string(), Value::Null);
m.insert("previousFundingRate".to_string(), Value::Null);
m.insert("previousFundingTimestamp".to_string(), Value::Null);
m.insert("previousFundingDatetime".to_string(), Value::Null);
m.insert("interval".to_string(), interval);
m
});
Value::Null
}
pub fn parse_sbe_message(&self, mut message: Value) -> Value {
//
// Paradex SBE schema 1:3 (https://docs.paradex.trade), little-endian. Every frame is an 8-byte
// header (blockLength, templateId, schemaId, version as uint16) followed by a fixed block of
// blockLength bytes, then repeating groups and length-prefixed strings. The frame is rebuilt in
// the shape of the matching JSON subscription message, so the JSON handlers parse both.
// Requests, subscribe acks and errors stay JSON text frames.
//
// Returns undefined for frames that carry nothing to route: heartbeats, binary acks, template
// ids this decoder does not know (the schema says to skip them), and malformed frames.
//
let mut frameHex: Value = self.binary_to_base16(message, &[]);
let mut frameLength: Value = self.parse_to_int((match ((Value::Int(frameHex.len() as i64)).as_f64(), (Value::Int(2)).as_f64()) { (Some(x), Some(y)) if y != 0.0 => Value::Float(x / y), _ => Value::Null }));
if frameLength.as_f64().unwrap_or(f64::NAN) < ((8i64) as f64) {
return Value::Null;
}
let mut blockLength: Value = self.sbe_read_uint(frameHex.clone(), Value::Int(0), Value::Int(2));
let mut templateId: Value = self.sbe_read_uint(frameHex.clone(), Value::Int(2), Value::Int(2));
let mut schemaId: Value = self.sbe_read_uint(frameHex.clone(), Value::Int(4), Value::Int(2));
if (!is_equal(&schemaId, &Value::Int(1))) || (frameLength.as_f64().unwrap_or(f64::NAN) < self.sum(&[blockLength.clone(), Value::Int(8)]).as_f64().unwrap_or(f64::NAN)) {
return Value::Null;
}
let mut channel: Value = Value::Null;
let mut data: Value = Value::Null;
if is_equal(&templateId, &Value::Int(1)) {
data = self.parse_sbe_trade(frameHex.clone(), blockLength.clone(), frameLength.clone());
if (data != Value::Null) {
channel = add(&Value::Str("trades.".into()), &crate::value::get_value_k(&data, "market"));
}
} else if is_equal(&templateId, &Value::Int(3)) {
data = self.parse_sbe_order_book(frameHex.clone(), blockLength.clone(), frameLength.clone());
if (data != Value::Null) {
channel = self.get_order_book_channel(crate::value::get_value_k(&data, "market")).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
}
} else if is_equal(&templateId, &Value::Int(4)) {
data = self.parse_sbe_market_summary(frameHex.clone(), blockLength.clone(), frameLength.clone());
channel = Value::Str("markets_summary".into());
} else if is_equal(&templateId, &Value::Int(5)) {
data = self.parse_sbe_funding_data(frameHex.clone(), blockLength.clone(), frameLength.clone());
channel = Value::Str("funding_data".into());
} else if is_equal(&templateId, &Value::Int(20)) {
data = self.parse_sbe_order(frameHex, blockLength, frameLength);
if (data != Value::Null) {
channel = add(&Value::Str("orders.".into()), &crate::value::get_value_k(&data, "market"));
}
}
if (data == Value::Null) {
return Value::Null;
}
return Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("sbe".to_string(), Value::Bool(true));
m.insert("params".to_string(), Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("channel".to_string(), channel);
m.insert("data".to_string(), data);
m
}));
m
});
Value::Null
}
pub fn sbe_read_uint(&self, mut frameHex: Value, mut offset: Value, mut length: Value) -> Value {
// a little-endian unsigned integer of at most 4 bytes, so that it stays exact in every language
let mut digits: Value = Value::Str("0123456789abcdef".into());
let mut result: Value = Value::Int(0);
let mut factor: Value = Value::Int(1);
let mut position: Value = (match (&(offset), &(Value::Int(2))) { (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 i: Value = Value::Int(0);
let mut __for_first_572: bool = true;
while { if !__for_first_572 { 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_572 = false; i.as_f64().unwrap_or(f64::NAN) < length.as_f64().unwrap_or(f64::NAN) } {
// plain variables as slice bounds, php gets end - start unparenthesized
let mut middle: Value = (match (&(position), &(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 next: Value = (match (&(position), &(Value::Int(2))) { (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 high: Value = get_index_of(&digits, &slice(&frameHex, &position, &middle));
let mut low: Value = get_index_of(&digits, &slice(&frameHex, &middle, &next));
result = self.sum(&[result.clone(), (match (&((match (&(high), &(Value::Int(16))) { (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 })), &(factor)) { (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 }), (match (&(low), &(factor)) { (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 })]);
factor = (match (&(factor), &(Value::Int(256))) { (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 });
position = next;
}
}
return result;
Value::Null
}
pub fn sbe_read_int64(&self, mut frameHex: Value, mut offset: Value) -> Option<String> {
// a signed int64 as a decimal string, undefined for INT64_MIN which the schema uses as null
let mut low: Value = self.sbe_read_uint(frameHex.clone(), offset.clone(), Value::Int(4));
let mut high: Value = self.sbe_read_uint(frameHex, (match (&(offset), &(Value::Int(4))) { (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(4));
if (is_equal(&high, &Value::Int(2147483648))) && (is_equal(&low, &Value::Int(0))) {
return None;
}
if is_equal(&high, &Value::Int(0)) {
return self.number_to_string(low.clone()).as_str().map(str::to_owned);
}
let mut result: Value = crate::precise::Precise::stringAdd(&crate::precise::Precise::stringMul(&self.number_to_string(high.clone()), &Value::Str("4294967296".into())), &self.number_to_string(low));
if high.as_f64().unwrap_or(f64::NAN) >= ((2147483648i64) as f64) {
result = crate::precise::Precise::stringSub(&result, &Value::Str("18446744073709551616".into()));
}
return result.as_str().map(str::to_owned);
}
pub fn sbe_read_decimal(&self, mut frameHex: Value, mut offset: Value) -> Option<String> {
// prices, sizes and rates are int64 mantissas with a fixed exponent of -8
let mut mantissa: Value = self.sbe_read_int64(frameHex, offset).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
if (mantissa == Value::Null) {
return None;
}
return crate::precise::Precise::stringDivPrec(&mantissa, &Value::Str("100000000".into()), &Value::Int(8)).as_str().map(str::to_owned);
}
pub fn sbe_read_timestamp(&self, mut frameHex: Value, mut offset: Value) -> Value {
// timestamps are microseconds, truncated to milliseconds with Precise: a float
// quotient like 1791906071000.999 is rounded up when php converts it to an int
let mut microseconds: Value = self.sbe_read_int64(frameHex, offset).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
if (microseconds == Value::Null) {
return Value::Null;
}
return self.parse_to_int(crate::precise::Precise::stringDivPrec(µseconds, &Value::Str("1000".into()), &Value::Int(0)));
Value::Null
}
pub fn sbe_read_enum(&self, mut frameHex: Value, mut offset: Value, mut values: Value) -> Option<String> {
let mut value: Value = self.sbe_read_uint(frameHex, offset, Value::Int(1));
return self.safe_string(values, self.number_to_string(value), &[]).as_str().map(str::to_owned);
}
pub fn sbe_read_string(&self, mut frameHex: Value, mut offset: Value, mut length: Value) -> Option<String> {
let mut start: Value = (match (&(offset), &(Value::Int(2))) { (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 end: Value = self.sum(&[start.clone(), (match (&(length), &(Value::Int(2))) { (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 self.binary_to_string(self.base16_to_binary(slice(&frameHex, &start, &end), &[]), &[]).as_str().map(str::to_owned);
}
pub fn sbe_read_address(&self, mut frameHex: Value, mut offset: Value) -> Option<String> {
// a 32-byte big-endian account address, printed without leading zeros like the JSON feed
let mut rawStart: Value = (match (&(offset), &(Value::Int(2))) { (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 rawEnd: Value = (match (&(rawStart), &(Value::Int(64))) { (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 raw: Value = slice(&frameHex, &rawStart, &rawEnd);
let mut start: Value = Value::Int(0);
let mut next: Value = Value::Int(1);
while (start.as_f64().unwrap_or(f64::NAN) < ((63i64) as f64)) && (slice(&raw, &start, &next).as_str() == Some("0")) {
start = next.clone();
next = (match (&(start), &(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 });
}
return Value::Str(format!("{}{}", Value::Str("0x".into()), slice(&raw, &start, &Value::Int(64))).into()).as_str().map(str::to_owned);
}
pub fn sbe_read_var_strings(&self, mut frameHex: Value, mut offset: Value, mut count: Value, mut frameLength: Value) -> Value {
// the var-data section: count strings, each prefixed with a uint8 length. Returns undefined
// when the frame ends before all of them, and only the strings present when a trailing one is
// absent, which is how a field appended to var-data reads on a frame from before it
let mut result: Value = Value::from(vec![]);
let mut position: Value = offset;
{
let mut i: Value = Value::Int(0);
let mut __for_first_573: bool = true;
while { if !__for_first_573 { 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_573 = false; i.as_f64().unwrap_or(f64::NAN) < count.as_f64().unwrap_or(f64::NAN) } {
if position.as_f64().unwrap_or(f64::NAN) >= frameLength.as_f64().unwrap_or(f64::NAN) {
return result;
}
let mut length: Value = self.sbe_read_uint(frameHex.clone(), position.clone(), Value::Int(1));
if self.sum(&[position.clone(), Value::Int(1), length.clone()]).as_f64().unwrap_or(f64::NAN) > frameLength.as_f64().unwrap_or(f64::NAN) {
return Value::Null;
}
append_to_array(&mut result, self.sbe_read_string(frameHex.clone(), (match (&(position), &(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 }), length.clone()).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
position = self.sum(&[position.clone(), Value::Int(1), length]);
}
}
return result;
Value::Null
}
pub fn parse_sbe_trade(&self, mut frameHex: Value, mut blockLength: Value, mut frameLength: Value) -> Value {
//
// TradeEvent (templateId 1), 50-byte block
//
// ts int64, seq int64, tradeId int64 (deprecated, truncated), side uint8, price int64,
// size int64, createdAt int64, tradeType uint8, then market and tradeIdStr strings
//
if blockLength.as_f64().unwrap_or(f64::NAN) < ((50i64) as f64) {
return Value::Null;
}
let mut strings: Value = self.sbe_read_var_strings(frameHex.clone(), self.sum(&[blockLength, Value::Int(8)]), Value::Int(2), frameLength);
let mut stringsLength: Value = (if (strings == Value::Null) { Value::Int(0) } else { get_array_length(&strings) });
if stringsLength.as_f64().unwrap_or(f64::NAN) < ((1i64) as f64) {
return Value::Null;
}
let mut sides: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("1".to_string(), Value::Str("BUY".into()));
m.insert("2".to_string(), Value::Str("SELL".into()));
m
});
let mut tradeTypes: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("1".to_string(), Value::Str("FILL".into()));
m.insert("2".to_string(), Value::Str("LIQUIDATION".into()));
m.insert("3".to_string(), Value::Str("UNWIND_TRANSFER".into()));
m.insert("4".to_string(), Value::Str("SETTLE_MARKET".into()));
m.insert("5".to_string(), Value::Str("RPI".into()));
m.insert("6".to_string(), Value::Str("BLOCK_TRADE".into()));
m
});
return Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("id".to_string(), self.safe_string(strings.clone(), Value::Int(1), &[]));
m.insert("market".to_string(), get_value(&strings, &Value::Int(0)));
m.insert("side".to_string(), self.sbe_read_enum(frameHex.clone(), Value::Int(32), sides).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("size".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(41)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("price".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(33)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("created_at".to_string(), self.sbe_read_timestamp(frameHex.clone(), Value::Int(49)));
m.insert("trade_type".to_string(), self.sbe_read_enum(frameHex, Value::Int(57), tradeTypes).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m
});
Value::Null
}
pub fn parse_sbe_order_book(&self, mut frameHex: Value, mut blockLength: Value, mut frameLength: Value) -> Value {
//
// BookEvent (templateId 3), 89-byte block
//
// ts int64, seq int64, pkgType uint8, nine best bid/ask int64 fields, then the bids and asks
// groups of (price int64, size int64) entries and the market string
//
if blockLength.as_f64().unwrap_or(f64::NAN) < ((17i64) as f64) {
return Value::Null;
}
// watchOrderBook subscribes to the snapshot channel, a delta would need a book to apply it to
let mut pkgType: Value = self.sbe_read_uint(frameHex.clone(), Value::Int(24), Value::Int(1));
if !is_equal(&pkgType, &Value::Int(0)) {
return Value::Null;
}
let mut inserts: Value = Value::from(vec![]);
let mut sides: Value = Value::from(vec![Value::Str("BUY".into()), Value::Str("SELL".into())]);
let mut position: Value = self.sum(&[blockLength, Value::Int(8)]);
{
let mut i: Value = Value::Int(0);
let mut __for_first_575: bool = true;
while { if !__for_first_575 { 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_575 = false; i.as_f64().unwrap_or(f64::NAN) < ((sides.len() as i64) as f64) } {
if add(&position, &Value::Int(4)).as_f64().unwrap_or(f64::NAN) > frameLength.as_f64().unwrap_or(f64::NAN) {
return Value::Null;
}
let mut entryLength: Value = self.sbe_read_uint(frameHex.clone(), position.clone(), Value::Int(2));
let mut numInGroup: Value = self.sbe_read_uint(frameHex.clone(), add(&position, &Value::Int(2)), Value::Int(2));
position = add(&position, &Value::Int(4));
if (entryLength.as_f64().unwrap_or(f64::NAN) < ((16i64) as f64)) || (self.sum(&[position.clone(), multiply(&numInGroup, &entryLength)]).as_f64().unwrap_or(f64::NAN) > frameLength.as_f64().unwrap_or(f64::NAN)) {
return Value::Null;
}
{
let mut j: Value = Value::Int(0);
let mut __for_first_574: bool = true;
while { if !__for_first_574 { 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_574 = false; j.as_f64().unwrap_or(f64::NAN) < numInGroup.as_f64().unwrap_or(f64::NAN) } {
append_to_array(&mut inserts, Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("side".to_string(), sides.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));
m.insert("price".to_string(), self.sbe_read_decimal(frameHex.clone(), position.clone()).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("size".to_string(), self.sbe_read_decimal(frameHex.clone(), add(&position, &Value::Int(8))).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m
}));
position = self.sum(&[position.clone(), entryLength.clone()]);
}
}
}
}
let mut strings: Value = self.sbe_read_var_strings(frameHex.clone(), position, Value::Int(1), frameLength);
let mut stringsLength: Value = (if (strings == Value::Null) { Value::Int(0) } else { get_array_length(&strings) });
if stringsLength.as_f64().unwrap_or(f64::NAN) < ((1i64) as f64) {
return Value::Null;
}
return Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("seq_no".to_string(), self.sbe_read_int64(frameHex.clone(), Value::Int(16)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("market".to_string(), get_value(&strings, &Value::Int(0)));
m.insert("last_updated_at".to_string(), self.sbe_read_timestamp(frameHex, Value::Int(8)));
m.insert("update_type".to_string(), Value::Str("s".into()));
m.insert("inserts".to_string(), inserts);
m.insert("updates".to_string(), Value::from(vec![]));
m.insert("deletes".to_string(), Value::from(vec![]));
m
});
Value::Null
}
pub fn parse_sbe_market_summary(&self, mut frameHex: Value, mut blockLength: Value, mut frameLength: Value) -> Value {
//
// MarketSummaryEvent (templateId 4), 248-byte block (240 before 1:3), of which the first
// 12 int64 fields and settlementTwapEstimate are read
//
// ts, seq, markPrice, indexPrice, lastPrice, volumeUsd24h, openInterest, fundingRate, bid,
// ask, totalVolume, priceChangeRate24h, ..., settlementTwapEstimate (1:3, at 240), then the
// market string
//
if blockLength.as_f64().unwrap_or(f64::NAN) < ((96i64) as f64) {
return Value::Null;
}
let mut strings: Value = self.sbe_read_var_strings(frameHex.clone(), self.sum(&[blockLength.clone(), Value::Int(8)]), Value::Int(1), frameLength);
let mut stringsLength: Value = (if (strings == Value::Null) { Value::Int(0) } else { get_array_length(&strings) });
if stringsLength.as_f64().unwrap_or(f64::NAN) < ((1i64) as f64) {
return Value::Null;
}
// appended in 1:3: null outside the pre-expiry settlement window and for undated markets
let mut settlementTwapEstimate: Value = Value::Null;
if blockLength.as_f64().unwrap_or(f64::NAN) >= ((248i64) as f64) {
settlementTwapEstimate = self.sbe_read_decimal(frameHex.clone(), Value::Int(248)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null);
}
return Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("symbol".to_string(), get_value(&strings, &Value::Int(0)));
m.insert("mark_price".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(24)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("last_traded_price".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(40)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("bid".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(72)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("ask".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(80)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("volume_24h".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(48)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("total_volume".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(88)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("created_at".to_string(), self.sbe_read_timestamp(frameHex.clone(), Value::Int(8)));
m.insert("open_interest".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(56)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("funding_rate".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(64)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("price_change_rate_24h".to_string(), self.sbe_read_decimal(frameHex, Value::Int(96)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("settlement_twap_estimate".to_string(), settlementTwapEstimate);
m
});
Value::Null
}
pub fn parse_sbe_funding_data(&self, mut frameHex: Value, mut blockLength: Value, mut frameLength: Value) -> Value {
//
// FundingDataEvent (templateId 5), 66-byte block
//
// ts int64, seq int64, fundingRate int64, fundingIndex int64, createdAt int64,
// fundingPremium int64, fundingRate8h int64, fundingPeriodHours int16, ... then the market
//
if blockLength.as_f64().unwrap_or(f64::NAN) < ((58i64) as f64) {
return Value::Null;
}
let mut strings: Value = self.sbe_read_var_strings(frameHex.clone(), self.sum(&[blockLength, Value::Int(8)]), Value::Int(1), frameLength);
let mut stringsLength: Value = (if (strings == Value::Null) { Value::Int(0) } else { get_array_length(&strings) });
if stringsLength.as_f64().unwrap_or(f64::NAN) < ((1i64) as f64) {
return Value::Null;
}
return Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("market".to_string(), get_value(&strings, &Value::Int(0)));
m.insert("funding_index".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(32)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("funding_premium".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(48)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("funding_rate".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(24)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("funding_rate_8h".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(56)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("funding_period_hours".to_string(), self.sbe_read_uint(frameHex.clone(), Value::Int(64), Value::Int(2)));
m.insert("created_at".to_string(), self.sbe_read_timestamp(frameHex, Value::Int(40)));
m
});
Value::Null
}
pub fn parse_sbe_order(&self, mut frameHex: Value, mut blockLength: Value, mut frameLength: Value) -> Value {
//
// OrderEvent (templateId 20), 128-byte block (126 before the 1:2 request fields)
//
// ts int64, seq int64, status uint8, side uint8, orderType uint8, timeInForce uint8,
// price int64, triggerPrice int64, size int64, sizeOpen int64, avgFillPrice int64,
// createdAt int64, updatedAt int64, account [32]byte, receivedAt int64, publishedAt int64,
// stp uint8, flags uint8, ... then the orderId, clientOrderId, market and cancelReason strings
//
if blockLength.as_f64().unwrap_or(f64::NAN) < ((126i64) as f64) {
return Value::Null;
}
let mut strings: Value = self.sbe_read_var_strings(frameHex.clone(), self.sum(&[blockLength, Value::Int(8)]), Value::Int(4), frameLength);
let mut stringsLength: Value = (if (strings == Value::Null) { Value::Int(0) } else { get_array_length(&strings) });
if stringsLength.as_f64().unwrap_or(f64::NAN) < ((4i64) as f64) {
return Value::Null;
}
let mut statuses: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("1".to_string(), Value::Str("NEW".into()));
m.insert("2".to_string(), Value::Str("UNTRIGGERED".into()));
m.insert("3".to_string(), Value::Str("OPEN".into()));
m.insert("4".to_string(), Value::Str("CLOSED".into()));
m
});
let mut sides: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("1".to_string(), Value::Str("BUY".into()));
m.insert("2".to_string(), Value::Str("SELL".into()));
m
});
let mut orderTypes: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("1".to_string(), Value::Str("LIMIT".into()));
m.insert("2".to_string(), Value::Str("MARKET".into()));
m.insert("3".to_string(), Value::Str("STOP_LIMIT".into()));
m.insert("4".to_string(), Value::Str("STOP_MARKET".into()));
m.insert("5".to_string(), Value::Str("TAKE_PROFIT_LIMIT".into()));
m.insert("6".to_string(), Value::Str("TAKE_PROFIT_MARKET".into()));
m.insert("7".to_string(), Value::Str("STOP_LOSS_LIMIT".into()));
m.insert("8".to_string(), Value::Str("STOP_LOSS_MARKET".into()));
m
});
let mut timeInForces: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("1".to_string(), Value::Str("GTC".into()));
m.insert("2".to_string(), Value::Str("IOC".into()));
m.insert("3".to_string(), Value::Str("POST_ONLY".into()));
m.insert("4".to_string(), Value::Str("RPI".into()));
m
});
let mut stpModes: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("0".to_string(), Value::Str("NONE".into()));
m.insert("1".to_string(), Value::Str("EXPIRE_MAKER".into()));
m.insert("2".to_string(), Value::Str("EXPIRE_TAKER".into()));
m.insert("3".to_string(), Value::Str("EXPIRE_BOTH".into()));
m
});
let mut flagNames: Value = Value::from(vec![Value::Str("REDUCE_ONLY".into()), Value::Str("STOP_CONDITION_BELOW_TRIGGER".into()), Value::Str("STOP_CONDITION_ABOVE_TRIGGER".into()), Value::Str("INTERACTIVE".into()), Value::Str("TARGET_STRATEGY_VWAP".into()), Value::Str("MMP".into())]);
let mut flags: Value = Value::from(vec![]);
let mut flagBits: Value = self.sbe_read_uint(frameHex.clone(), Value::Int(133), Value::Int(1));
{
let mut i: Value = Value::Int(0);
let mut __for_first_576: bool = true;
while { if !__for_first_576 { 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_576 = false; i.as_f64().unwrap_or(f64::NAN) < ((flagNames.len() as i64) as f64) } {
let mut bit: Value = self.parse_to_int(mod_val(&flagBits, &Value::Int(2)));
if (bit.as_f64() == Some(1.0)) {
append_to_array(&mut flags, flagNames.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));
}
flagBits = self.parse_to_int((match (((subtract(&flagBits, &bit))).as_f64(), (Value::Int(2)).as_f64()) { (Some(x), Some(y)) if y != 0.0 => Value::Float(x / y), _ => Value::Null }));
}
}
let mut order: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("account".to_string(), self.sbe_read_address(frameHex.clone(), Value::Int(84)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("avg_fill_price".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(60)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("client_id".to_string(), get_value(&strings, &Value::Int(1)));
m.insert("created_at".to_string(), self.sbe_read_timestamp(frameHex.clone(), Value::Int(68)));
m.insert("flags".to_string(), flags);
m.insert("id".to_string(), get_value(&strings, &Value::Int(0)));
m.insert("instruction".to_string(), self.sbe_read_enum(frameHex.clone(), Value::Int(27), timeInForces).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("last_updated_at".to_string(), self.sbe_read_timestamp(frameHex.clone(), Value::Int(76)));
m.insert("market".to_string(), get_value(&strings, &Value::Int(2)));
m.insert("price".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(28)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("published_at".to_string(), self.sbe_read_timestamp(frameHex.clone(), Value::Int(124)));
m.insert("received_at".to_string(), self.sbe_read_timestamp(frameHex.clone(), Value::Int(116)));
m.insert("remaining_size".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(52)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("seq_no".to_string(), self.sbe_read_int64(frameHex.clone(), Value::Int(16)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("side".to_string(), self.sbe_read_enum(frameHex.clone(), Value::Int(25), sides).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("size".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(44)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("status".to_string(), self.sbe_read_enum(frameHex.clone(), Value::Int(24), statuses).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("stp".to_string(), self.sbe_read_enum(frameHex.clone(), Value::Int(132), stpModes).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("timestamp".to_string(), self.sbe_read_timestamp(frameHex.clone(), Value::Int(8)));
m.insert("trigger_price".to_string(), self.sbe_read_decimal(frameHex.clone(), Value::Int(36)).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m.insert("type".to_string(), self.sbe_read_enum(frameHex, Value::Int(26), orderTypes).map(|__s| Value::Str(__s.into())).unwrap_or(Value::Null));
m
});
// an empty cancel reason is sent for every live order, parseOrder reads any reason as canceled
if (get_value(&strings, &Value::Int(3)).as_str() != Some("")) {
if let Value::Dict(__d) = &mut order { std::sync::Arc::make_mut(__d).insert("cancel_reason".into(), get_value(&strings, &Value::Int(3))); }
}
return order;
Value::Null
}
pub fn handle_error_message(&self, mut client: Value, mut message: Value) -> Value {
let __message_empty = indexmap::IndexMap::new();
let message = message.as_map().unwrap_or(&__message_empty);
//
// {
// "jsonrpc": "2.0",
// "id": 0,
// "error": {
// "code": -32600,
// "message": "invalid subscribe request",
// "data": "invalid channel"
// },
// "usIn": 1718179125962419,
// "usDiff": 76,
// "usOut": 1718179125962495
// }
//
let mut error: Value = (match message.get("error") { Some(__v) if matches!(__v, Value::Dict(_)) => __v.clone(), _ => Value::Null });
if (error == Value::Null) {
return Value::Bool(true);
} else {
let mut errorCode: Option<String> = self.safe_string_k(error.clone(), "code", &[]).as_str().map(str::to_owned);
if (errorCode.is_some()) {
let mut feedback: Value = Value::Str(format!("{}{}", Value::Str(format!("{}{}", self.id.clone(), Value::Str(" ".into())).into()), json_stringify(&error)).into());
self.throw_exactly_matched_exception(self.exceptions.as_map().and_then(|__m| __m.get("exact")).cloned().unwrap_or(Value::Null), Value::Str("-32600".into()), feedback.clone());
let mut messageString: Value = self.safe_string_k(error, "message", &[]);
if (messageString != Value::Null) {
self.throw_broadly_matched_exception(self.exceptions.as_map().and_then(|__m| __m.get("broad")).cloned().unwrap_or(Value::Null), messageString, feedback);
}
}
return Value::Bool(false);
}
Value::Null
}
pub fn handle_message(&mut self, mut client: Value, mut message: Value) {
let mut parsedMessage: Value = message.clone();
if self.is_binary_message(message.clone(), &[]).as_bool() == Some(true) {
parsedMessage = self.parse_sbe_message(message);
if (parsedMessage == Value::Null) {
return;
}
}
if (self.handle_error_message(client.clone(), parsedMessage.clone()).as_bool() != Some(true)) {
return;
}
//
// auth response
//
// {
// "jsonrpc": "2.0",
// "id": 1,
// "result": { "node_id": "73cf456f7cb78d59" }
// }
//
// subscription message
//
// {
// "jsonrpc": "2.0",
// "method": "subscription",
// "params": {
// "channel": "trades.ALL",
// "data": {
// "id": "1718179273230201709233240002",
// "market": "kBONK-USD-PERP",
// "side": "BUY",
// "size": "34028",
// "price": "0.028776",
// "created_at": 1718179273230,
// "trade_type": "FILL"
// }
// }
// }
//
let mut result: Value = self.safe_dict_k(parsedMessage.clone(), "result", &[]);
if (result != Value::Null) {
self.handle_authentication_message(client.clone(), parsedMessage.clone());
return;
}
let mut data: Value = self.safe_dict_k(parsedMessage.clone(), "params", &[]);
if (data != Value::Null) {
let mut channel: Value = self.safe_string_k(data, "channel", &[]);
let mut parts: Value = split(&channel, &Value::Str(".".into()));
let mut name: Value = self.safe_string(parts, Value::Int(0), &[]);
let mut methods: Value = Value::Map({
let mut m = indexmap::IndexMap::new();
m.insert("trades".to_string(), Value::Str("handle_trade".into()).clone());
m.insert("order_book".to_string(), Value::Str("handle_order_book".into()).clone());
m.insert("markets_summary".to_string(), Value::Str("handle_ticker".into()).clone());
m.insert("orders".to_string(), Value::Str("handle_order".into()).clone());
m.insert("funding_data".to_string(), Value::Str("handle_funding_rate".into()).clone());
m
});
let mut method: Value = self.safe_value(methods, name, &[]);
if (method != Value::Null) {
self.dispatch_ws_handler(&method, &[client, parsedMessage]);
}
}
}
}