# EXMO Exchange Specification # # Auto-generated by: mix ccxt.extract exmo # CCXT version: 4.5.34 # Spec format version: 1 # # Review before use: # - signing: Detected from sign() method, verify with testnet # - endpoints: Raw paths extracted, map to unified API as needed # - exceptions: Extracted (exact, broad, HTTP status codes) # - features/options: Extracted from describe() # - classification: Set to :supported initially, promote after validation # %{ api_param_requirements: nil, certified: false, classification: :supported, comment: nil, countries: [ "LT" ], currencies: %{}, currency_aliases: %{ "BCHSV" => "BSV", "GMT" => "GMT Token", "XBT" => "BTC" }, dex: false, enable_rate_limit: true, endpoint_extraction_stats: %{ "capturedMethods" => 32, "coveragePercent" => 100, "enabledMethods" => 38, "excluded" => [ %{ "method" => "fetchDepositWithdrawFee", "reason" => "emulated (returns cached data, no HTTP call)" }, %{ "method" => "fetchOrder", "reason" => "emulated (returns cached data, no HTTP call)" }, %{ "method" => "fetchTransactions", "reason" => "emulated (returns cached data, no HTTP call)" } ], "excludedMethods" => 3, "extractableMethods" => 35, "failures" => [ %{ "error" => "exmo createOrder () cannot create stop limit orders for spot, only stop market", "method" => "createStopLimitOrder" }, %{ "error" => "exmo createOrder () cannot create stop limit orders for spot, only stop market", "method" => "createStopOrder" }, %{ "error" => "exmo editOrder() can only be used for isolated margin orders", "method" => "editOrder" } ] }, endpoints: [ %{ api_section: "private", auth: true, cost: 1, market_type: :spot, method: :post, name: :cancel_order, param_mappings: %{ "id" => "order_id" }, params: [ :id, :symbol ], path: "/order_cancel", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, market_type: :spot, method: :post, name: :create_limit_order, param_mappings: %{ "amount" => "quantity", "side" => "type", "symbol" => "pair" }, params: [ :symbol, :side, :amount, :price ], path: "/order_create", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "price" => 0, "type" => "market_buy" }, market_type: :spot, method: :post, name: :create_market_buy_order, param_mappings: %{ "amount" => "quantity", "symbol" => "pair" }, params: [ :symbol, :amount ], path: "/order_create", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "price" => 0, "type" => "market_buy_total" }, market_type: :spot, method: :post, name: :create_market_buy_order_with_cost, param_mappings: %{ "cost" => "quantity", "symbol" => "pair" }, params: [ :symbol, :cost ], path: "/order_create", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "price" => 0, "type" => "market_buy" }, market_type: :spot, method: :post, name: :create_market_order, param_mappings: %{ "amount" => "quantity", "symbol" => "pair" }, params: [ :symbol, :side, :amount, :price ], path: "/order_create", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "price" => 0, "type" => "market_buy_total" }, market_type: :spot, method: :post, name: :create_market_order_with_cost, param_mappings: %{ "cost" => "quantity", "symbol" => "pair" }, params: [ :symbol, :side, :cost ], path: "/order_create", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, market_type: :spot, method: :post, name: :create_order, param_mappings: %{ "amount" => "quantity", "side" => "type", "symbol" => "pair" }, params: [ :symbol, :type, :side, :amount, :price ], path: "/order_create", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "pair" => "BTCUSDT", "triggerDirection" => "below" }, market_type: :swap, method: :post, name: :create_stop_market_order, param_mappings: %{ "amount" => "quantity", "side" => "type", "triggerPrice" => "trigger_price" }, params: [ :symbol, :side, :amount, :triggerPrice ], path: "/stop_market_order_create", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, market_type: :spot, method: :post, name: :fetch_balance, param_mappings: %{}, params: [], path: "/user_info", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "limit" => 100, "offset" => 0 }, market_type: :spot, method: :post, name: :fetch_canceled_orders, param_mappings: %{}, params: [ :symbol, :since, :limit ], path: "/user_cancelled_orders", source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, market_type: :spot, method: :get, name: :fetch_currencies, param_mappings: %{}, params: [], path: "/currency/list/extended", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "type" => "deposit" }, market_type: :spot, method: :post, name: :fetch_deposit, param_mappings: %{ "arg0" => "order_id" }, params: [], path: "/wallet_operations", response_transformer: {:extract_path, ["items"]}, source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, market_type: :spot, method: :post, name: :fetch_deposit_address, param_mappings: %{}, params: [ :code ], path: "/deposit_address", source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, market_type: :spot, method: :get, name: :fetch_deposit_withdraw_fees, param_mappings: %{}, params: [ :codes ], path: "/payments/providers/crypto/list", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "type" => "deposit" }, market_type: :spot, method: :post, name: :fetch_deposits, param_mappings: %{ "code" => "currency" }, params: [ :code, :since, :limit ], path: "/wallet_operations", response_transformer: {:extract_path, ["items"]}, source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, market_type: :spot, method: :post, name: :fetch_deposits_withdrawals, param_mappings: %{}, params: [ :code, :since, :limit ], path: "/wallet_history", source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, market_type: :spot, method: :get, name: :fetch_l2_order_book, param_mappings: %{ "symbol" => "pair" }, params: [ :symbol, :limit ], path: "/order_book", source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, market_type: :spot, method: :get, name: :fetch_markets, param_mappings: %{}, params: [], path: "/pair_settings", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "limit" => 100, "offset" => 0 }, market_type: :spot, method: :post, name: :fetch_my_trades, param_mappings: %{ "symbol" => "pair" }, params: [ :symbol, :since, :limit ], path: "/user_trades", source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, default_params: %{ "resolution" => "60", "to" => "" }, market_type: :spot, method: :get, name: :fetch_ohlcv, param_mappings: %{ "since" => "from", "timeframe" => "resolution" }, params: [ :symbol, :timeframe, :since, :limit ], path: "/candles_history", response_transformer: {:extract_path, ["candles"]}, source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, market_type: :spot, method: :post, name: :fetch_open_orders, param_mappings: %{}, params: [ :symbol, :since, :limit ], path: "/user_open_orders", response_transformer: {:extract_path, ["orders"]}, source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, market_type: :spot, method: :get, name: :fetch_order_book, param_mappings: %{ "symbol" => "pair" }, params: [ :symbol, :limit ], path: "/order_book", source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, default_params: %{ "pair" => "BTCUSDT" }, market_type: :spot, method: :get, name: :fetch_order_books, param_mappings: %{}, params: [ :symbols, :limit ], path: "/order_book", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, market_type: :spot, method: :post, name: :fetch_order_trades, param_mappings: %{ "id" => "order_id" }, params: [ :id, :symbol, :since, :limit ], path: "/order_trades", response_transformer: {:extract_path, ["trades"]}, source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, market_type: :spot, method: :get, name: :fetch_ticker, param_mappings: %{}, params: [ :symbol ], path: "/ticker", source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, market_type: :spot, method: :get, name: :fetch_tickers, param_mappings: %{}, params: [ :symbols ], path: "/ticker", source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, market_type: :spot, method: :get, name: :fetch_trades, param_mappings: %{ "symbol" => "pair" }, params: [ :symbol, :since, :limit ], path: "/trades", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, market_type: :spot, method: :post, name: :fetch_trading_fees, param_mappings: %{}, params: [], path: "/margin/pair/list", response_transformer: {:extract_path, ["pairs"]}, source: :intercepted }, %{ api_section: "public", auth: false, cost: 1, market_type: :spot, method: :get, name: :fetch_transaction_fees, param_mappings: %{}, params: [ :codes ], path: "/payments/providers/crypto/list", source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "type" => "withdraw" }, market_type: :spot, method: :post, name: :fetch_withdrawal, param_mappings: %{ "arg0" => "order_id" }, params: [], path: "/wallet_operations", response_transformer: {:extract_path, ["items"]}, source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, default_params: %{ "type" => "withdraw" }, market_type: :spot, method: :post, name: :fetch_withdrawals, param_mappings: %{ "code" => "currency" }, params: [ :code, :since, :limit ], path: "/wallet_operations", response_transformer: {:extract_path, ["items"]}, source: :intercepted }, %{ api_section: "private", auth: true, cost: 1, market_type: :spot, method: :post, name: :withdraw, param_mappings: %{ "code" => "currency" }, params: [ :code, :amount, :address, :tag ], path: "/withdraw_crypt", source: :intercepted } ], error_code_details: %{ 140333 => %{ description: "{\"error\":{\"code\":140333,\"msg\":\"The number of characters after the point in the price exceeds the maximum number '8\\u003e6'\"}}", type: :invalid_order }, 140434 => %{ description: nil, type: :invalid_parameters }, 40005 => %{ description: "Authorization error, incorrect signature", type: :invalid_credentials }, 40009 => %{ description: nil, type: :invalid_credentials }, 40015 => %{ description: "API function do not exist", type: :exchange_error }, 40016 => %{ description: "{\"result\":false,\"error\":\"Error 40016: Maintenance work in progress\"}", type: :network_error }, 40017 => %{ description: "Wrong API Key", type: :invalid_credentials }, 40032 => %{ description: "{\"result\":false,\"error\":\"Error 40032: Access is denied for this API key\"}", type: :access_restricted }, 40033 => %{ description: "{\"result\":false,\"error\":\"Error 40033: Access is denied, this resources are temporarily blocked to user\"}", type: :access_restricted }, 40034 => %{ description: "{\"result\":false,\"error\":\"Error 40034: Access is denied, rate limit is exceeded\"}", type: :rate_limited }, 50052 => %{ description: nil, type: :insufficient_balance }, 50054 => %{ description: nil, type: :insufficient_balance }, 50173 => %{ description: "\"Order with id X was not found.\" (cancelling non-existent, closed and cancelled order)", type: :order_not_found }, 50277 => %{ description: nil, type: :invalid_order }, 50304 => %{ description: "\"Order was not found '123456789'\" (fetching order trades for an order that does not have trades yet)", type: :order_not_found }, 50319 => %{ description: "Price by order is less than permissible minimum for this pair", type: :invalid_order }, 50321 => %{ description: "Price by order is more than permissible maximum for this pair", type: :invalid_order }, 50381 => %{ description: "{\"result\":false,\"error\":\"Error 50381: More than 2 decimal places are not permitted for pair BTC_USD\"}", type: :invalid_order }, "API rate limit exceeded" => %{ description: "{\"result\":false,\"error\":\"API rate limit exceeded for x.x.x.x. Retry after 60 sec.\",\"history\":[],\"begin\":1579392000,\"end\":1579478400}", type: :rate_limited }, "invalid syntax" => %{ description: nil, type: :invalid_parameters }, "range period is too long" => %{ description: nil, type: :invalid_parameters } }, error_codes: %{ 140333 => :invalid_order, 140434 => :invalid_parameters, 40005 => :invalid_credentials, 40009 => :invalid_credentials, 40015 => :exchange_error, 40016 => :network_error, 40017 => :invalid_credentials, 40032 => :access_restricted, 40033 => :access_restricted, 40034 => :rate_limited, 50052 => :insufficient_balance, 50054 => :insufficient_balance, 50173 => :order_not_found, 50277 => :invalid_order, 50304 => :order_not_found, 50319 => :invalid_order, 50321 => :invalid_order, 50381 => :invalid_order, "API rate limit exceeded" => :rate_limited, "invalid syntax" => :invalid_parameters, "range period is too long" => :invalid_parameters }, exceptions: %{ broad: %{ "API rate limit exceeded" => :rate_limit_exceeded, "invalid syntax" => :bad_request, "range period is too long" => :bad_request }, exact: %{ "140333" => :invalid_order, "140434" => :bad_request, "40005" => :authentication_error, "40009" => :invalid_nonce, "40015" => :exchange_error, "40016" => :on_maintenance, "40017" => :authentication_error, "40032" => :permission_denied, "40033" => :permission_denied, "40034" => :rate_limit_exceeded, "50052" => :insufficient_funds, "50054" => :insufficient_funds, "50173" => :order_not_found, "50277" => :invalid_order, "50304" => :order_not_found, "50319" => :invalid_order, "50321" => :invalid_order, "50381" => :invalid_order } }, exchange_options: %{ "defaultNetworkCodeReplacements" => %{ "BRC20" => %{ "BRC20" => "BTC" }, "CRO" => %{ "CRC20" => "CRONOS" }, "ETH" => %{ "ERC20" => "ETH" }, "TRX" => %{ "TRC20" => "TRX" } }, "fetchTradingFees" => %{ "method" => "fetchPrivateTradingFees" }, "margin" => %{ "fillResponseFromRequest" => true }, "networks" => %{ "ETH" => "ERC20", "TRX" => "TRC20" }, "networksById" => %{ "ERC20" => "ETH", "TRC20" => "TRX" } }, extended_metadata: %{ fees_url: "https://exmo.com/en/docs/fees", limits: %{ "amount" => %{}, "cost" => %{}, "leverage" => %{}, "price" => %{} }, logo_url: "https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg", referral_url: "https://exmo.me/?ref=131685" }, extracted_metadata: %{ ccxt_version: "4.5.34" }, features: %{ future: %{}, spot: %{ create_order: %{ hedged: false, iceberg: false, leverage: true, margin_mode: true, market_buy_by_cost: true, market_buy_requires_price: false, self_trade_prevention: false, stop_loss_price: false, take_profit_price: false, time_in_force: %{ fok: true, gtd: true, ioc: true, po: true }, trailing: false, trigger_direction: false, trigger_price: true }, fetch_my_trades: %{ limit: 100, margin_mode: true, symbol_required: true }, fetch_ohlcv: %{ limit: 1000 }, fetch_open_orders: %{ margin_mode: false, symbol_required: false, trailing: false, trigger: false }, fetch_order: %{ margin_mode: false, symbol_required: false, trailing: false, trigger: false }, sandbox: false }, swap: %{} }, fees: %{ trading: %{ fee_side: "get", maker: 0.004, percentage: true, taker: 0.004, tier_based: true } }, handle_content_type_application_zip: nil, handle_errors_source: "handleErrors(httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {\n if (response === undefined) {\n return undefined; // fallback to default error handler\n }\n if (('error' in response) && !('result' in response)) {\n // error: {\n // \"code\": \"140434\",\n // \"msg\": \"Your margin balance is not sufficient to place the order for '5 TON'. Please top up your margin wallet by \"2.5 USDT\".\"\n // }\n //\n const errorCode = this.safeValue(response, 'error', {});\n const messageError = this.safeString(errorCode, 'msg');\n const code = this.safeString(errorCode, 'code');\n const feedback = this.id + ' ' + body;\n this.throwExactlyMatchedException(this.exceptions['exact'], code, feedback);\n this.throwBroadlyMatchedException(this.exceptions['broad'], messageError, feedback);\n throw new errors.ExchangeError(feedback);\n }\n if (('result' in response) || ('errmsg' in response)) {\n //\n // {\"result\":false,\"error\":\"Error 50052: Insufficient funds\"}\n // {\"s\":\"error\",\"errmsg\":\"strconv.ParseInt: parsing \\\"\\\": invalid syntax\"}\n //\n let success = this.safeBool(response, 'result', false);\n if (typeof success === 'string') {\n if ((success === 'true') || (success === '1')) {\n success = true;\n }\n else {\n success = false;\n }\n }\n if (!success) {\n let code = undefined;\n const message = this.safeString2(response, 'error', 'errmsg');\n const errorParts = message.split(':');\n const numParts = errorParts.length;\n if (numParts > 1) {\n const errorSubParts = errorParts[0].split(' ');\n const numSubParts = errorSubParts.length;\n code = (numSubParts > 1) ? errorSubParts[1] : errorSubParts[0];\n }\n const feedback = this.id + ' ' + body;\n this.throwExactlyMatchedException(this.exceptions['exact'], code, feedback);\n this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);\n throw new errors.ExchangeError(feedback);\n }\n }\n return undefined;\n }", has: %{ add_margin: true, borrow_cross_margin: false, borrow_isolated_margin: false, borrow_margin: false, c_o_r_s: false, cancel_all_orders: false, cancel_all_orders_ws: false, cancel_order: true, cancel_order_with_client_order_id: false, cancel_order_ws: false, cancel_orders: false, cancel_orders_with_client_order_id: false, cancel_orders_ws: false, close_all_positions: false, close_position: false, create_deposit_address: false, create_limit_buy_order: false, create_limit_buy_order_ws: false, create_limit_order: true, create_limit_order_ws: false, create_limit_sell_order: false, create_limit_sell_order_ws: false, create_market_buy_order: true, create_market_buy_order_with_cost: true, create_market_buy_order_with_cost_ws: false, create_market_buy_order_ws: false, create_market_order: true, create_market_order_with_cost: true, create_market_order_with_cost_ws: false, create_market_order_ws: true, create_market_sell_order: false, create_market_sell_order_with_cost: false, create_market_sell_order_with_cost_ws: false, create_market_sell_order_ws: false, create_order: true, create_order_with_take_profit_and_stop_loss: false, create_order_with_take_profit_and_stop_loss_ws: false, create_order_ws: false, create_orders: false, create_post_only_order: false, create_post_only_order_ws: false, create_reduce_only_order: false, create_reduce_only_order_ws: false, create_stop_limit_order: true, create_stop_limit_order_ws: false, create_stop_loss_order: false, create_stop_loss_order_ws: false, create_stop_market_order: true, create_stop_market_order_ws: false, create_stop_order: true, create_stop_order_ws: false, create_take_profit_order: false, create_take_profit_order_ws: false, create_trailing_amount_order: false, create_trailing_amount_order_ws: false, create_trailing_percent_order: false, create_trailing_percent_order_ws: false, create_trigger_order: false, create_trigger_order_ws: false, deposit: false, edit_order: true, edit_order_with_client_order_id: false, edit_order_ws: false, edit_orders: false, fetch_accounts: false, fetch_balance: true, fetch_balance_ws: false, fetch_bids_asks: false, fetch_borrow_interest: false, fetch_borrow_rate: false, fetch_borrow_rate_histories: false, fetch_borrow_rate_history: false, fetch_borrow_rates: false, fetch_borrow_rates_per_symbol: false, fetch_canceled_and_closed_orders: false, fetch_canceled_orders: true, fetch_closed_order: false, fetch_closed_orders: false, fetch_closed_orders_ws: false, fetch_convert_currencies: false, fetch_convert_quote: false, fetch_convert_trade: false, fetch_convert_trade_history: false, fetch_cross_borrow_rate: false, fetch_cross_borrow_rates: false, fetch_currencies: true, fetch_currencies_ws: true, fetch_deposit: true, fetch_deposit_address: true, fetch_deposit_addresses: false, fetch_deposit_addresses_by_network: false, fetch_deposit_withdraw_fee: true, fetch_deposit_withdraw_fees: true, fetch_deposits: true, fetch_deposits_withdrawals: true, fetch_deposits_ws: false, fetch_funding_history: false, fetch_funding_interval: false, fetch_funding_intervals: false, fetch_funding_rate: false, fetch_funding_rate_history: false, fetch_funding_rates: false, fetch_greeks: false, fetch_index_ohlcv: false, fetch_isolated_borrow_rate: false, fetch_isolated_borrow_rates: false, fetch_isolated_positions: false, fetch_l2_order_book: true, fetch_l3_order_book: false, fetch_last_prices: false, fetch_ledger: false, fetch_ledger_entry: false, fetch_leverage: false, fetch_leverage_tiers: false, fetch_leverages: false, fetch_liquidations: false, fetch_long_short_ratio: false, fetch_long_short_ratio_history: false, fetch_margin_adjustment_history: false, fetch_margin_mode: false, fetch_margin_modes: false, fetch_mark_ohlcv: false, fetch_mark_prices: false, fetch_market_leverage_tiers: false, fetch_markets: true, fetch_markets_ws: false, fetch_my_liquidations: false, fetch_my_settlement_history: false, fetch_my_trades: true, fetch_my_trades_ws: false, fetch_ohlcv: true, fetch_ohlcv_ws: false, fetch_open_interest: false, fetch_open_interest_history: false, fetch_open_interests: false, fetch_open_order: false, fetch_open_orders: true, fetch_open_orders_ws: false, fetch_option: false, fetch_option_chain: false, fetch_order: true, fetch_order_book: true, fetch_order_book_ws: false, fetch_order_books: true, fetch_order_trades: true, fetch_order_with_client_order_id: false, fetch_order_ws: false, fetch_orders: false, fetch_orders_by_status: false, fetch_orders_ws: false, fetch_position: false, fetch_position_history: false, fetch_position_mode: false, fetch_position_ws: false, fetch_positions: false, fetch_positions_for_symbol: false, fetch_positions_for_symbol_ws: false, fetch_positions_history: false, fetch_positions_risk: false, fetch_positions_ws: false, fetch_premium_index_ohlcv: false, fetch_settlement_history: false, fetch_status: false, fetch_ticker: true, fetch_ticker_ws: false, fetch_tickers: true, fetch_tickers_ws: false, fetch_time: false, fetch_trades: true, fetch_trades_ws: false, fetch_trading_fee: false, fetch_trading_fees: true, fetch_trading_fees_ws: false, fetch_trading_limits: false, fetch_transaction_fee: false, fetch_transaction_fees: true, fetch_transactions: true, fetch_transfer: false, fetch_transfers: false, fetch_underlying_assets: false, fetch_volatility_history: false, fetch_withdraw_addresses: false, fetch_withdrawal: true, fetch_withdrawal_whitelist: false, fetch_withdrawals: true, fetch_withdrawals_ws: false, future: false, margin: true, option: false, private_api: true, public_api: true, reduce_margin: true, repay_cross_margin: false, repay_isolated_margin: false, sandbox: false, set_leverage: false, set_margin: false, set_margin_mode: false, set_position_mode: false, sign_in: false, spot: true, swap: false, transfer: false, un_watch_my_trades: false, un_watch_ohlcv: false, un_watch_ohlcv_for_symbols: false, un_watch_order_book: false, un_watch_order_book_for_symbols: false, un_watch_orders: false, un_watch_positions: false, un_watch_ticker: false, un_watch_tickers: false, un_watch_trades: false, un_watch_trades_for_symbols: false, watch_balance: true, watch_bids_asks: false, watch_liquidations: false, watch_liquidations_for_symbols: false, watch_my_liquidations: false, watch_my_liquidations_for_symbols: false, watch_my_trades: true, watch_ohlcv: false, watch_ohlcv_for_symbols: false, watch_order_book: true, watch_order_book_for_symbols: false, watch_orders: true, watch_orders_for_symbols: false, watch_position: false, watch_positions: false, watch_status: false, watch_ticker: true, watch_tickers: true, watch_trades: true, watch_trades_for_symbols: false, withdraw: true, ws: false }, http_config: nil, id: "exmo", markets: nil, name: "EXMO", ohlcv_timestamp_resolution: :seconds, options: %{ fetch_trading_fees: %{ "method" => "fetchPrivateTradingFees" }, margin: %{ "fillResponseFromRequest" => true }, networks: %{ "ETH" => "ERC20", "TRX" => "TRC20" } }, order_mappings: nil, param_mappings: %{ "amount" => "quantity", "arg0" => "order_id", "code" => "currency", "cost" => "quantity", "id" => "order_id", "side" => "type", "since" => "from", "symbol" => "pair", "timeframe" => "resolution", "triggerPrice" => "trigger_price" }, parse_methods: [ %{ "comment" => nil, "examples" => [], "line_end" => 1068, "line_start" => 1035, "mappings" => [], "name" => "parseBalance", "signature" => "parseBalance (response): Balances", "source" => "parseBalance (response): Balances {\n const result: Dict = { 'info': response };\n const wallets = this.safeValue (response, 'wallets');\n if (wallets !== undefined) {\n const currencyIds = Object.keys (wallets);\n for (let i = 0; i < currencyIds.length; i++) {\n const currencyId = currencyIds[i];\n const item = wallets[currencyId];\n const currency = this.safeCurrencyCode (currencyId);\n const account = this.account ();\n account['used'] = this.safeString (item, 'used');\n account['free'] = this.safeString (item, 'free');\n account['total'] = this.safeString (item, 'balance');\n result[currency] = account;\n }\n } else {\n const free = this.safeValue (response, 'balances', {});\n const used = this.safeValue (response, 'reserved', {});\n const currencyIds = Object.keys (free);\n for (let i = 0; i < currencyIds.length; i++) {\n const currencyId = currencyIds[i];\n const code = this.safeCurrencyCode (currencyId);\n const account = this.account ();\n if (currencyId in free) {\n account['free'] = this.safeString (free, currencyId);\n }\n if (currencyId in used) {\n account['used'] = this.safeString (used, currencyId);\n }\n result[code] = account;\n }\n }\n return this.safeBalance (result);\n }" }, %{ "comment" => nil, "examples" => [ "[\n {\n \"type\": \"deposit\", // or \"withdraw\"\n \"name\": \"BTC\",\n \"currency_name\": \"BTC\",\n \"min\": \"0.001\",\n \"max\": \"0\",\n \"enabled\": true,\n \"comment\": \"Minimum deposit amount is 0.001 BTC. We do not support BSC and BEP20 network, please consider this when sending funds\",\n \"commission_desc\": \"0%\",\n \"currency_confirmations\": 1\n },\n ...\n ]" ], "line_end" => 673, "line_start" => 623, "mappings" => [], "name" => "parseDepositWithdrawFee", "signature" => "parseDepositWithdrawFee (fee, currency: Currency = undefined)", "source" => "parseDepositWithdrawFee (fee, currency: Currency = undefined) {\n //\n // [\n // {\n // \"type\": \"deposit\", // or \"withdraw\"\n // \"name\": \"BTC\",\n // \"currency_name\": \"BTC\",\n // \"min\": \"0.001\",\n // \"max\": \"0\",\n // \"enabled\": true,\n // \"comment\": \"Minimum deposit amount is 0.001 BTC. We do not support BSC and BEP20 network, please consider this when sending funds\",\n // \"commission_desc\": \"0%\",\n // \"currency_confirmations\": 1\n // },\n // ...\n // ]\n //\n const result = this.depositWithdrawFee (fee);\n for (let i = 0; i < fee.length; i++) {\n const provider = fee[i];\n const type = this.safeString (provider, 'type');\n const networkId = this.safeString (provider, 'name');\n const networkCode = this.networkIdToCode (networkId, this.safeString (currency, 'code'));\n const commissionDesc = this.safeString (provider, 'commission_desc');\n let splitCommissionDesc = [];\n let percentage = undefined;\n if (commissionDesc !== undefined) {\n splitCommissionDesc = commissionDesc.split ('%');\n const splitCommissionDescLength = splitCommissionDesc.length;\n percentage = splitCommissionDescLength >= 2;\n }\n const network = this.safeValue (result['networks'], networkCode);\n if (network === undefined) {\n result['networks'][networkCode] = {\n 'withdraw': {\n 'fee': undefined,\n 'percentage': undefined,\n },\n 'deposit': {\n 'fee': undefined,\n 'percentage': undefined,\n },\n };\n }\n result['networks'][networkCode][type] = {\n 'fee': this.parseFixedFloatValue (this.safeString (splitCommissionDesc, 0)),\n 'percentage': percentage,\n };\n }\n return this.assignDefaultDepositWithdrawFees (result);\n }" }, %{ "comment" => nil, "examples" => [], "line_end" => 509, "line_start" => 494, "mappings" => [], "name" => "parseFixedFloatValue", "signature" => "parseFixedFloatValue (input)", "source" => "parseFixedFloatValue (input) {\n if ((input === undefined) || (input === '-')) {\n return undefined;\n }\n if (input === '') {\n return 0;\n }\n const isPercentage = (input.indexOf ('%') >= 0);\n const parts = input.split (' ');\n const value = parts[0].replace ('%', '');\n const result = parseFloat (value);\n if ((result > 0) && isPercentage) {\n throw new ExchangeError (this.id + ' parseFixedFloatValue() detected an unsupported non-zero percentage-based fee ' + input);\n }\n return result;\n }" }, %{ "comment" => nil, "examples" => [], "line_end" => 350, "line_start" => 334, "mappings" => [ %{ "comment" => nil, "key" => "info", "value" => "data" }, %{ "comment" => nil, "key" => "symbol", "value" => "this.safeSymbol (undefined, market)" }, %{ "comment" => nil, "key" => "type", "value" => "undefined" }, %{ "comment" => nil, "key" => "marginMode", "value" => "'isolated'" }, %{ "comment" => nil, "key" => "amount", "value" => "undefined" }, %{ "comment" => nil, "key" => "total", "value" => "undefined" }, %{ "comment" => nil, "key" => "code", "value" => "this.safeValue (market, 'quote')" }, %{ "comment" => nil, "key" => "status", "value" => "'ok'" }, %{ "comment" => nil, "key" => "timestamp", "value" => "undefined" }, %{ "comment" => nil, "key" => "datetime", "value" => "undefined" } ], "name" => "parseMarginModification", "signature" => "parseMarginModification (data: Dict, market: Market = undefined): MarginModification", "source" => "parseMarginModification (data: Dict, market: Market = undefined): MarginModification {\n //\n // {}\n //\n return {\n 'info': data,\n 'symbol': this.safeSymbol (undefined, market),\n 'type': undefined,\n 'marginMode': 'isolated',\n 'amount': undefined,\n 'total': undefined,\n 'code': this.safeValue (market, 'quote'),\n 'status': 'ok',\n 'timestamp': undefined,\n 'datetime': undefined,\n };\n }" }, %{ "comment" => nil, "examples" => [ "{\n \"t\":1584057600000,\n \"o\":0.02235144,\n \"c\":0.02400233,\n \"h\":0.025171,\n \"l\":0.02221,\n \"v\":5988.34031761\n }" ], "line_end" => 1033, "line_start" => 1014, "mappings" => [], "name" => "parseOHLCV", "signature" => "parseOHLCV (ohlcv, market: Market = undefined): OHLCV", "source" => "parseOHLCV (ohlcv, market: Market = undefined): OHLCV {\n //\n // {\n // \"t\":1584057600000,\n // \"o\":0.02235144,\n // \"c\":0.02400233,\n // \"h\":0.025171,\n // \"l\":0.02221,\n // \"v\":5988.34031761\n // }\n //\n return [\n this.safeInteger (ohlcv, 't'),\n this.safeNumber (ohlcv, 'o'),\n this.safeNumber (ohlcv, 'h'),\n this.safeNumber (ohlcv, 'l'),\n this.safeNumber (ohlcv, 'c'),\n this.safeNumber (ohlcv, 'v'),\n ];\n }" }, %{ "comment" => nil, "examples" => [ "{\n \"order_id\": \"14\",\n \"created\": \"1435517311\",\n \"type\": \"buy\",\n \"pair\": \"BTC_USD\",\n \"price\": \"100\",\n \"quantity\": \"1\",\n \"amount\": \"100\"\n }", "{\n \"type\": \"buy\",\n \"in_currency\": \"BTC\",\n \"in_amount\": \"1\",\n \"out_currency\": \"USD\",\n \"out_amount\": \"100\",\n \"trades\": [\n {\n \"trade_id\": 3,\n \"date\": 1435488248,\n \"type\": \"buy\",\n \"pair\": \"BTC_USD\",\n \"order_id\": 12345,\n \"quantity\": 1,\n \"price\": 100,\n \"amount\": 100\n }\n ]\n }", "{\n \"client_id\": \"0\",\n \"comment\": \"\",\n \"created\": \"1619068707985325495\",\n \"distance\": \"0\",\n \"expire\": 0,\n \"funding_currency\": \"BTC\",\n \"funding_quantity\": \"0.01\",\n \"funding_rate\": \"0.02\",\n \"leverage\": \"2\",\n \"order_id\": \"123\",\n \"pair\": \"BTC_USD\",\n \"previous_type\": \"limit_sell\",\n \"price\": \"58000\",\n \"quantity\": \"0.01\",\n \"src\": 0,\n \"stop_price\": \"0\",\n \"trigger_price\": \"58000\",\n \"type\": \"limit_sell\",\n \"updated\": 1619068707989411800\n }", "{\n \"distance\": \"0\",\n \"event_id\": \"692842802860022508\",\n \"event_time\": \"1619069531190173720\",\n \"event_type\": \"OrderCancelStarted\",\n \"order_id\": \"123\",\n \"order_status\": \"cancel_started\",\n \"order_type\": \"limit_sell\",\n \"pair\": \"BTC_USD\",\n \"price\": \"54115\",\n \"quantity\": \"0.001\",\n \"stop_price\": \"0\",\n \"trade_id\": \"0\",\n \"trade_price\": \"0\",\n \"trade_quantity\": \"0\",\n \"trade_type\": \"\"\n }," ], "line_end" => 2117, "line_start" => 1980, "mappings" => [ %{ "comment" => nil, "key" => "id", "value" => "id" }, %{ "comment" => nil, "key" => "clientOrderId", "value" => "clientOrderId" }, %{ "comment" => nil, "key" => "datetime", "value" => "this.iso8601 (timestamp)" }, %{ "comment" => nil, "key" => "timestamp", "value" => "timestamp" }, %{ "comment" => nil, "key" => "lastTradeTimestamp", "value" => "this.safeIntegerProduct (order, 'updated', 0.000001)" }, %{ "comment" => nil, "key" => "status", "value" => "this.parseStatus (this.safeString (order, 'order_status'))" }, %{ "comment" => nil, "key" => "symbol", "value" => "symbol" }, %{ "comment" => nil, "key" => "type", "value" => "type" }, %{ "comment" => nil, "key" => "timeInForce", "value" => "undefined" }, %{ "comment" => nil, "key" => "postOnly", "value" => "undefined" }, %{ "comment" => nil, "key" => "side", "value" => "side" }, %{ "comment" => nil, "key" => "price", "value" => "price" }, %{ "comment" => nil, "key" => "triggerPrice", "value" => "triggerPrice" }, %{ "comment" => nil, "key" => "cost", "value" => "cost" }, %{ "comment" => nil, "key" => "amount", "value" => "amount" }, %{ "comment" => nil, "key" => "filled", "value" => "undefined" }, %{ "comment" => nil, "key" => "remaining", "value" => "undefined" }, %{ "comment" => nil, "key" => "average", "value" => "undefined" }, %{ "comment" => nil, "key" => "trades", "value" => "transactions" }, %{ "comment" => nil, "key" => "fee", "value" => "undefined" }, %{ "comment" => nil, "key" => "info", "value" => "order" } ], "name" => "parseOrder", "signature" => "parseOrder (order: Dict, market: Market = undefined): Order", "source" => "parseOrder (order: Dict, market: Market = undefined): Order {\n //\n // fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders\n //\n // {\n // \"order_id\": \"14\",\n // \"created\": \"1435517311\",\n // \"type\": \"buy\",\n // \"pair\": \"BTC_USD\",\n // \"price\": \"100\",\n // \"quantity\": \"1\",\n // \"amount\": \"100\"\n // }\n //\n // fetchOrder\n //\n // {\n // \"type\": \"buy\",\n // \"in_currency\": \"BTC\",\n // \"in_amount\": \"1\",\n // \"out_currency\": \"USD\",\n // \"out_amount\": \"100\",\n // \"trades\": [\n // {\n // \"trade_id\": 3,\n // \"date\": 1435488248,\n // \"type\": \"buy\",\n // \"pair\": \"BTC_USD\",\n // \"order_id\": 12345,\n // \"quantity\": 1,\n // \"price\": 100,\n // \"amount\": 100\n // }\n // ]\n // }\n //\n // Margin fetchOpenOrders\n //\n // {\n // \"client_id\": \"0\",\n // \"comment\": \"\",\n // \"created\": \"1619068707985325495\",\n // \"distance\": \"0\",\n // \"expire\": 0,\n // \"funding_currency\": \"BTC\",\n // \"funding_quantity\": \"0.01\",\n // \"funding_rate\": \"0.02\",\n // \"leverage\": \"2\",\n // \"order_id\": \"123\",\n // \"pair\": \"BTC_USD\",\n // \"previous_type\": \"limit_sell\",\n // \"price\": \"58000\",\n // \"quantity\": \"0.01\",\n // \"src\": 0,\n // \"stop_price\": \"0\",\n // \"trigger_price\": \"58000\",\n // \"type\": \"limit_sell\",\n // \"updated\": 1619068707989411800\n // }\n //\n // Margin fetchClosedOrders\n //\n // {\n // \"distance\": \"0\",\n // \"event_id\": \"692842802860022508\",\n // \"event_time\": \"1619069531190173720\",\n // \"event_type\": \"OrderCancelStarted\",\n // \"order_id\": \"123\",\n // \"order_status\": \"cancel_started\",\n // \"order_type\": \"limit_sell\",\n // \"pair\": \"BTC_USD\",\n // \"price\": \"54115\",\n // \"quantity\": \"0.001\",\n // \"stop_price\": \"0\",\n // \"trade_id\": \"0\",\n // \"trade_price\": \"0\",\n // \"trade_quantity\": \"0\",\n // \"trade_type\": \"\"\n // },\n //\n const id = this.safeString2 (order, 'order_id', 'parent_order_id');\n const eventTime = this.safeIntegerProduct2 (order, 'event_time', 'created', 0.000001);\n const timestamp = this.safeTimestamp (order, 'created', eventTime);\n const orderType = this.safeString2 (order, 'type', 'order_type');\n const side = this.parseSide (orderType);\n let marketId = undefined;\n if ('pair' in order) {\n marketId = order['pair'];\n } else if (('in_currency' in order) && ('out_currency' in order)) {\n if (side === 'buy') {\n marketId = order['in_currency'] + '_' + order['out_currency'];\n } else {\n marketId = order['out_currency'] + '_' + order['in_currency'];\n }\n }\n market = this.safeMarket (marketId, market);\n const symbol = market['symbol'];\n let amount = this.safeString (order, 'quantity');\n if (amount === undefined) {\n const amountField = (side === 'buy') ? 'in_amount' : 'out_amount';\n amount = this.safeString (order, amountField);\n }\n const price = this.safeString (order, 'price');\n const cost = this.safeString (order, 'amount');\n const transactions = this.safeValue (order, 'trades', []);\n const clientOrderId = this.safeInteger (order, 'client_id');\n let triggerPrice = this.safeString (order, 'stop_price');\n if (triggerPrice === '0') {\n triggerPrice = undefined;\n }\n let type = undefined;\n if ((orderType !== 'buy') && (orderType !== 'sell')) {\n type = orderType;\n }\n return this.safeOrder ({\n 'id': id,\n 'clientOrderId': clientOrderId,\n 'datetime': this.iso8601 (timestamp),\n 'timestamp': timestamp,\n 'lastTradeTimestamp': this.safeIntegerProduct (order, 'updated', 0.000001),\n 'status': this.parseStatus (this.safeString (order, 'order_status')),\n 'symbol': symbol,\n 'type': type,\n 'timeInForce': undefined,\n 'postOnly': undefined,\n 'side': side,\n 'price': price,\n 'triggerPrice': triggerPrice,\n 'cost': cost,\n 'amount': amount,\n 'filled': undefined,\n 'remaining': undefined,\n 'average': undefined,\n 'trades': transactions,\n 'fee': undefined,\n 'info': order,\n }, market);\n }" }, %{ "comment" => nil, "examples" => [], "line_end" => 1978, "line_start" => 1960, "mappings" => [], "name" => "parseSide", "signature" => "parseSide (orderType)", "source" => "parseSide (orderType) {\n const side: Dict = {\n 'limit_buy': 'buy',\n 'limit_sell': 'sell',\n 'market_buy': 'buy',\n 'market_sell': 'sell',\n 'stop_buy': 'buy',\n 'stop_sell': 'sell',\n 'stop_limit_buy': 'buy',\n 'stop_limit_sell': 'sell',\n 'trailing_stop_buy': 'buy',\n 'trailing_stop_sell': 'sell',\n 'stop_market_sell': 'sell',\n 'stop_market_buy': 'buy',\n 'buy': 'buy',\n 'sell': 'sell',\n };\n return this.safeString (side, orderType, orderType);\n }" }, %{ "comment" => nil, "examples" => [], "line_end" => 1958, "line_start" => 1947, "mappings" => [], "name" => "parseStatus", "signature" => "parseStatus (status)", "source" => "parseStatus (status) {\n if (status === undefined) {\n return undefined;\n }\n const statuses: Dict = {\n 'cancel_started': 'canceled',\n };\n if (status.indexOf ('cancel') >= 0) {\n status = 'canceled';\n }\n return this.safeString (statuses, status, status);\n }" }, %{ "comment" => nil, "examples" => [ "{\n \"buy_price\":\"0.00002996\",\n \"sell_price\":\"0.00003002\",\n \"last_trade\":\"0.00002992\",\n \"high\":\"0.00003028\",\n \"low\":\"0.00002935\",\n \"avg\":\"0.00002963\",\n \"vol\":\"1196546.3163222\",\n \"vol_curr\":\"35.80066578\",\n \"updated\":1642291733\n }" ], "line_end" => 1223, "line_start" => 1184, "mappings" => [ %{ "comment" => nil, "key" => "symbol", "value" => "market['symbol']" }, %{ "comment" => nil, "key" => "timestamp", "value" => "timestamp" }, %{ "comment" => nil, "key" => "datetime", "value" => "this.iso8601 (timestamp)" }, %{ "comment" => nil, "key" => "high", "value" => "this.safeString (ticker, 'high')" }, %{ "comment" => nil, "key" => "low", "value" => "this.safeString (ticker, 'low')" }, %{ "comment" => nil, "key" => "bid", "value" => "this.safeString (ticker, 'buy_price')" }, %{ "comment" => nil, "key" => "bidVolume", "value" => "undefined" }, %{ "comment" => nil, "key" => "ask", "value" => "this.safeString (ticker, 'sell_price')" }, %{ "comment" => nil, "key" => "askVolume", "value" => "undefined" }, %{ "comment" => nil, "key" => "vwap", "value" => "undefined" }, %{ "comment" => nil, "key" => "open", "value" => "undefined" }, %{ "comment" => nil, "key" => "close", "value" => "last" }, %{ "comment" => nil, "key" => "last", "value" => "last" }, %{ "comment" => nil, "key" => "previousClose", "value" => "undefined" }, %{ "comment" => nil, "key" => "change", "value" => "undefined" }, %{ "comment" => nil, "key" => "percentage", "value" => "undefined" }, %{ "comment" => nil, "key" => "average", "value" => "this.safeString (ticker, 'avg')" }, %{ "comment" => nil, "key" => "baseVolume", "value" => "this.safeString (ticker, 'vol')" }, %{ "comment" => nil, "key" => "quoteVolume", "value" => "this.safeString (ticker, 'vol_curr')" }, %{ "comment" => nil, "key" => "info", "value" => "ticker" } ], "name" => "parseTicker", "signature" => "parseTicker (ticker: Dict, market: Market = undefined): Ticker", "source" => "parseTicker (ticker: Dict, market: Market = undefined): Ticker {\n //\n // {\n // \"buy_price\":\"0.00002996\",\n // \"sell_price\":\"0.00003002\",\n // \"last_trade\":\"0.00002992\",\n // \"high\":\"0.00003028\",\n // \"low\":\"0.00002935\",\n // \"avg\":\"0.00002963\",\n // \"vol\":\"1196546.3163222\",\n // \"vol_curr\":\"35.80066578\",\n // \"updated\":1642291733\n // }\n //\n const timestamp = this.safeTimestamp (ticker, 'updated');\n market = this.safeMarket (undefined, market);\n const last = this.safeString (ticker, 'last_trade');\n return this.safeTicker ({\n 'symbol': market['symbol'],\n 'timestamp': timestamp,\n 'datetime': this.iso8601 (timestamp),\n 'high': this.safeString (ticker, 'high'),\n 'low': this.safeString (ticker, 'low'),\n 'bid': this.safeString (ticker, 'buy_price'),\n 'bidVolume': undefined,\n 'ask': this.safeString (ticker, 'sell_price'),\n 'askVolume': undefined,\n 'vwap': undefined,\n 'open': undefined,\n 'close': last,\n 'last': last,\n 'previousClose': undefined,\n 'change': undefined,\n 'percentage': undefined,\n 'average': this.safeString (ticker, 'avg'),\n 'baseVolume': this.safeString (ticker, 'vol'),\n 'quoteVolume': this.safeString (ticker, 'vol_curr'),\n 'info': ticker,\n }, market);\n }" }, %{ "comment" => nil, "examples" => [ "{\n \"trade_id\":165087520,\n \"date\":1587470005,\n \"type\":\"buy\",\n \"quantity\":\"1.004\",\n \"price\":\"0.02491461\",\n \"amount\":\"0.02501426\"\n },", "{\n \"trade_id\": 3,\n \"date\": 1435488248,\n \"type\": \"buy\",\n \"pair\": \"BTC_USD\",\n \"order_id\": 12345,\n \"quantity\": 1,\n \"price\": 100,\n \"amount\": 100,\n \"exec_type\": \"taker\",\n \"commission_amount\": \"0.02\",\n \"commission_currency\": \"BTC\",\n \"commission_percent\": \"0.2\"\n }", "{\n \"trade_id\": \"692861757015952517\",\n \"trade_dt\": \"1693951853197811824\",\n \"trade_type\": \"buy\",\n \"pair\": \"ADA_USDT\",\n \"quantity\": \"1.96607879\",\n \"price\": \"0.2568\",\n \"amount\": \"0.50488903\"\n }" ], "line_end" => 1370, "line_start" => 1281, "mappings" => [ %{ "comment" => nil, "key" => "id", "value" => "id" }, %{ "comment" => nil, "key" => "info", "value" => "trade" }, %{ "comment" => nil, "key" => "timestamp", "value" => "timestamp" }, %{ "comment" => nil, "key" => "datetime", "value" => "this.iso8601 (timestamp)" }, %{ "comment" => nil, "key" => "symbol", "value" => "symbol" }, %{ "comment" => nil, "key" => "order", "value" => "orderId" }, %{ "comment" => nil, "key" => "type", "value" => "type" }, %{ "comment" => nil, "key" => "side", "value" => "side" }, %{ "comment" => nil, "key" => "takerOrMaker", "value" => "takerOrMaker" }, %{ "comment" => nil, "key" => "price", "value" => "priceString" }, %{ "comment" => nil, "key" => "amount", "value" => "amountString" }, %{ "comment" => nil, "key" => "cost", "value" => "costString" }, %{ "comment" => nil, "key" => "fee", "value" => "fee" } ], "name" => "parseTrade", "signature" => "parseTrade (trade: Dict, market: Market = undefined): Trade", "source" => "parseTrade (trade: Dict, market: Market = undefined): Trade {\n //\n // fetchTrades (public)\n //\n // {\n // \"trade_id\":165087520,\n // \"date\":1587470005,\n // \"type\":\"buy\",\n // \"quantity\":\"1.004\",\n // \"price\":\"0.02491461\",\n // \"amount\":\"0.02501426\"\n // },\n //\n // fetchMyTrades, fetchOrderTrades\n //\n // {\n // \"trade_id\": 3,\n // \"date\": 1435488248,\n // \"type\": \"buy\",\n // \"pair\": \"BTC_USD\",\n // \"order_id\": 12345,\n // \"quantity\": 1,\n // \"price\": 100,\n // \"amount\": 100,\n // \"exec_type\": \"taker\",\n // \"commission_amount\": \"0.02\",\n // \"commission_currency\": \"BTC\",\n // \"commission_percent\": \"0.2\"\n // }\n //\n // fetchMyTrades (margin)\n //\n // {\n // \"trade_id\": \"692861757015952517\",\n // \"trade_dt\": \"1693951853197811824\",\n // \"trade_type\": \"buy\",\n // \"pair\": \"ADA_USDT\",\n // \"quantity\": \"1.96607879\",\n // \"price\": \"0.2568\",\n // \"amount\": \"0.50488903\"\n // }\n //\n const timestamp = this.safeTimestamp (trade, 'date');\n const id = this.safeString (trade, 'trade_id');\n const orderId = this.safeString (trade, 'order_id');\n const priceString = this.safeString (trade, 'price');\n const amountString = this.safeString (trade, 'quantity');\n const costString = this.safeString (trade, 'amount');\n const side = this.safeString2 (trade, 'type', 'trade_type');\n const type = undefined;\n const marketId = this.safeString (trade, 'pair');\n market = this.safeMarket (marketId, market, '_');\n const symbol = market['symbol'];\n const isMaker = this.safeValue (trade, 'is_maker');\n let takerOrMakerDefault = undefined;\n if (isMaker !== undefined) {\n takerOrMakerDefault = isMaker ? 'maker' : 'taker';\n }\n const takerOrMaker = this.safeString (trade, 'exec_type', takerOrMakerDefault);\n let fee = undefined;\n const feeCostString = this.safeString (trade, 'commission_amount');\n if (feeCostString !== undefined) {\n const feeCurrencyId = this.safeString (trade, 'commission_currency');\n const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);\n let feeRateString = this.safeString (trade, 'commission_percent');\n if (feeRateString !== undefined) {\n feeRateString = Precise.stringDiv (feeRateString, '1000', 18);\n }\n fee = {\n 'cost': feeCostString,\n 'currency': feeCurrencyCode,\n 'rate': feeRateString,\n };\n }\n return this.safeTrade ({\n 'id': id,\n 'info': trade,\n 'timestamp': timestamp,\n 'datetime': this.iso8601 (timestamp),\n 'symbol': symbol,\n 'order': orderId,\n 'type': type,\n 'side': side,\n 'takerOrMaker': takerOrMaker,\n 'price': priceString,\n 'amount': amountString,\n 'cost': costString,\n 'fee': fee,\n }, market);\n }" }, %{ "comment" => nil, "examples" => [ "{\n \"dt\": 1461841192,\n \"type\": \"deposit\",\n \"curr\": \"RUB\",\n \"status\": \"processing\",\n \"provider\": \"Qiwi (LA) [12345]\",\n \"amount\": \"1\",\n \"account\": \"\",\n \"txid\": \"ec46f784ad976fd7f7539089d1a129fe46...\",\n }", "{\n \"operation_id\": 47412538520634344,\n \"created\": 1573760013,\n \"updated\": 1573760013,\n \"type\": \"withdraw\",\n \"currency\": \"DOGE\",\n \"status\": \"Paid\",\n \"amount\": \"300\",\n \"provider\": \"DOGE\",\n \"commission\": \"0\",\n \"account\": \"DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS\",\n \"order_id\": 69670170,\n \"provider_type\": \"crypto\",\n \"crypto_address\": \"DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS\",\n \"card_number\": \"\",\n \"wallet_address\": \"\",\n \"email\": \"\",\n \"phone\": \"\",\n \"extra\": {\n \"txid\": \"f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792\",\n \"confirmations\": null,\n \"excode\": \"\",\n \"invoice\": \"\"\n },\n \"error\": \"\"\n }", "{\n \"result\": true,\n \"error\": \"\",\n \"task_id\": 11775077\n }" ], "line_end" => 2492, "line_start" => 2358, "mappings" => [ %{ "comment" => nil, "key" => "info", "value" => "transaction" }, %{ "comment" => nil, "key" => "id", "value" => "this.safeString2 (transaction, 'order_id', 'task_id')" }, %{ "comment" => nil, "key" => "txid", "value" => "txid" }, %{ "comment" => nil, "key" => "type", "value" => "type" }, %{ "comment" => nil, "key" => "currency", "value" => "code" }, %{ "comment" => nil, "key" => "network", "value" => "this.safeString (transaction, 'provider')" }, %{ "comment" => nil, "key" => "amount", "value" => "this.parseNumber (amountString)" }, %{ "comment" => nil, "key" => "status", "value" => "this.parseTransactionStatus (this.safeStringLower (transaction, 'status'))" }, %{ "comment" => nil, "key" => "timestamp", "value" => "timestamp" }, %{ "comment" => nil, "key" => "datetime", "value" => "this.iso8601 (timestamp)" }, %{ "comment" => nil, "key" => "address", "value" => "address" }, %{ "comment" => nil, "key" => "addressFrom", "value" => "undefined" }, %{ "comment" => nil, "key" => "addressTo", "value" => "address" }, %{ "comment" => nil, "key" => "tag", "value" => "undefined" }, %{ "comment" => nil, "key" => "tagFrom", "value" => "undefined" }, %{ "comment" => nil, "key" => "tagTo", "value" => "undefined" }, %{ "comment" => nil, "key" => "updated", "value" => "this.safeTimestamp (transaction, 'updated')" }, %{ "comment" => nil, "key" => "comment", "value" => "comment" }, %{ "comment" => nil, "key" => "internal", "value" => "undefined" }, %{ "comment" => nil, "key" => "fee", "value" => "fee" }, %{ "comment" => nil, "key" => "currency", "value" => "undefined" }, %{ "comment" => nil, "key" => "cost", "value" => "undefined" }, %{ "comment" => nil, "key" => "rate", "value" => "undefined" }, %{ "comment" => nil, "key" => "cost", "value" => "this.parseNumber (feeCost)" } ], "name" => "parseTransaction", "signature" => "parseTransaction (transaction: Dict, currency: Currency = undefined): Transaction", "source" => "parseTransaction (transaction: Dict, currency: Currency = undefined): Transaction {\n //\n // fetchDepositsWithdrawals\n //\n // {\n // \"dt\": 1461841192,\n // \"type\": \"deposit\",\n // \"curr\": \"RUB\",\n // \"status\": \"processing\",\n // \"provider\": \"Qiwi (LA) [12345]\",\n // \"amount\": \"1\",\n // \"account\": \"\",\n // \"txid\": \"ec46f784ad976fd7f7539089d1a129fe46...\",\n // }\n //\n // fetchWithdrawals\n //\n // {\n // \"operation_id\": 47412538520634344,\n // \"created\": 1573760013,\n // \"updated\": 1573760013,\n // \"type\": \"withdraw\",\n // \"currency\": \"DOGE\",\n // \"status\": \"Paid\",\n // \"amount\": \"300\",\n // \"provider\": \"DOGE\",\n // \"commission\": \"0\",\n // \"account\": \"DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS\",\n // \"order_id\": 69670170,\n // \"provider_type\": \"crypto\",\n // \"crypto_address\": \"DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS\",\n // \"card_number\": \"\",\n // \"wallet_address\": \"\",\n // \"email\": \"\",\n // \"phone\": \"\",\n // \"extra\": {\n // \"txid\": \"f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792\",\n // \"confirmations\": null,\n // \"excode\": \"\",\n // \"invoice\": \"\"\n // },\n // \"error\": \"\"\n // }\n //\n // withdraw\n //\n // {\n // \"result\": true,\n // \"error\": \"\",\n // \"task_id\": 11775077\n // }\n //\n const timestamp = this.safeTimestamp2 (transaction, 'dt', 'created');\n let amountString = this.safeString (transaction, 'amount');\n if (amountString !== undefined) {\n amountString = Precise.stringAbs (amountString);\n }\n let txid = this.safeString (transaction, 'txid');\n if (txid === undefined) {\n const extra = this.safeValue (transaction, 'extra', {});\n const extraTxid = this.safeString (extra, 'txid');\n if (extraTxid !== '') {\n txid = extraTxid;\n }\n }\n const type = this.safeString (transaction, 'type');\n const currencyId = this.safeString2 (transaction, 'curr', 'currency');\n const code = this.safeCurrencyCode (currencyId, currency);\n let address = undefined;\n let comment = undefined;\n const account = this.safeString (transaction, 'account');\n if (type === 'deposit') {\n comment = account;\n } else if (type === 'withdrawal') {\n address = account;\n if (address !== undefined) {\n const parts = address.split (':');\n const numParts = parts.length;\n if (numParts === 2) {\n address = this.safeString (parts, 1);\n address = address.replace (' ', '');\n }\n }\n }\n const fee = {\n 'currency': undefined,\n 'cost': undefined,\n 'rate': undefined,\n };\n // fixed funding fees only (for now)\n if (!this.fees['transaction']['percentage']) {\n const key = (type === 'withdrawal') ? 'withdraw' : 'deposit';\n let feeCost = this.safeString (transaction, 'commission');\n if (feeCost === undefined) {\n const transactionFees = this.safeValue (this.options, 'transactionFees', {});\n const codeFees = this.safeValue (transactionFees, code, {});\n feeCost = this.safeString (codeFees, key);\n }\n // users don't pay for cashbacks, no fees for that\n const provider = this.safeString (transaction, 'provider');\n if (provider === 'cashback') {\n feeCost = '0';\n }\n if (feeCost !== undefined) {\n // withdrawal amount includes the fee\n if (type === 'withdrawal') {\n amountString = Precise.stringSub (amountString, feeCost);\n }\n fee['cost'] = this.parseNumber (feeCost);\n fee['currency'] = code;\n }\n }\n return {\n 'info': transaction,\n 'id': this.safeString2 (transaction, 'order_id', 'task_id'),\n 'txid': txid,\n 'type': type,\n 'currency': code,\n 'network': this.safeString (transaction, 'provider'),\n 'amount': this.parseNumber (amountString),\n 'status': this.parseTransactionStatus (this.safeStringLower (transaction, 'status')),\n 'timestamp': timestamp,\n 'datetime': this.iso8601 (timestamp),\n 'address': address,\n 'addressFrom': undefined,\n 'addressTo': address,\n 'tag': undefined,\n 'tagFrom': undefined,\n 'tagTo': undefined,\n 'updated': this.safeTimestamp (transaction, 'updated'),\n 'comment': comment,\n 'internal': undefined,\n 'fee': fee,\n } as Transaction;\n }" }, %{ "comment" => nil, "examples" => [], "line_end" => 2356, "line_start" => 2347, "mappings" => [], "name" => "parseTransactionStatus", "signature" => "parseTransactionStatus (status: Str)", "source" => "parseTransactionStatus (status: Str) {\n const statuses: Dict = {\n 'transferred': 'ok',\n 'paid': 'ok',\n 'pending': 'pending',\n 'processing': 'pending',\n 'verifying': 'pending',\n };\n return this.safeString (statuses, status, status);\n }" } ], path_prefix: "/v1.1/", pro: false, quote_json_numbers: nil, rate_limits: %{ interval_ms: 100, period: 1000, requests: 10, rolling_window_size: 60000 }, raw_endpoints: %{ private: [], public: [] }, required_credentials: %{ api_key: true, password: false, secret: true, uid: false }, requires_eddsa: false, response_error: %{ code_field: nil, field: "error", message_field: "error", success_values: nil, type: :error_array }, signing: %{ api_key_header: "Key", confidence: :medium, pattern: :hmac_sha512_nonce }, spec_format_version: 1, status: %{ "eta" => nil, "status" => "ok", "updated" => nil, "url" => nil }, symbol_format: nil, symbol_formats: %{ precision_mode: 4, sample_limits: %{ "amount" => %{ "min" => 0.1 }, "cost" => %{ "max" => 500000, "min" => 0.5 }, "price" => %{ "max" => 1000, "min" => 0.001 } }, sample_precision: %{ "amount" => 1.0e-8, "price" => 0.001 }, spot: %{ base: "TRUMP", base_id: "TRUMP", case: :upper, contract: false, id: "TRUMP_USDT", limits: %{ "amount" => %{ "min" => 0.1 }, "cost" => %{ "max" => 500000, "min" => 0.5 }, "leverage" => %{}, "price" => %{ "max" => 1000, "min" => 0.001 } }, maker: 0.001, precision: %{ "amount" => 1.0e-8, "price" => 0.001 }, quote: "USDT", quote_id: "USDT", separator: "_", symbol: "TRUMP/USDT", taker: 0.001 } }, symbol_patterns: %{ spot: %{ case: :upper, component_order: nil, date_format: nil, pattern: :underscore_upper, separator: "_", suffix: nil } }, timeframes: %{ "15m" => "15", "1M" => "M", "1d" => "D", "1h" => "60", "1m" => "1", "1w" => "W", "2h" => "120", "30m" => "30", "3h" => "180", "45m" => "45", "4h" => "240", "5m" => "5" }, url_strategy: %{ detected_from: "sign_method", pattern: :version_slash, prefix: "/v1.1/" }, urls: %{ api: "https://api.exmo.com", api_sections: %{ "private" => "https://api.exmo.com", "public" => "https://api.exmo.com", "web" => "https://exmo.me" }, doc: [ "https://exmo.me/en/api_doc?ref=131685" ], other: nil, sandbox: nil, www: "https://exmo.me" }, version: "v1.1", ws: %{ auth: %{ algorithm: :sha512, encoding: :base64, market_specific: false, message_format: :op_args, op_field: "op", op_value: "auth", pattern: :generic_hmac, payload_format: "custom", requires_passphrase: false, timestamp_unit: :milliseconds }, channel_templates: %{ watch_balance: %{ auth_required: true, market_id_format: :native, params: [], pattern: :method_topics }, watch_my_trades: %{ auth_required: true, market_id_format: :native, params: [ %{ "default" => nil, "name" => "limit" } ], pattern: :method_topics }, watch_order_book: %{ market_id_format: :native, params: [ %{ "default" => nil, "name" => "limit" } ], pattern: :method_topics }, watch_orders: %{ auth_required: true, market_id_format: :native, params: [ %{ "default" => nil, "name" => "limit" } ], pattern: :method_topics }, watch_ticker: %{ market_id_format: :native, params: [], pattern: :method_topics }, watch_tickers: %{ market_id_format: :native, params: [], pattern: :method_topics }, watch_trades: %{ market_id_format: :native, params: [ %{ "default" => nil, "name" => "limit" } ], pattern: :method_topics } }, has: %{ watch_balance: true, watch_bids_asks: nil, watch_liquidations: nil, watch_liquidations_for_symbols: nil, watch_my_liquidations: nil, watch_my_liquidations_for_symbols: nil, watch_my_trades: true, watch_ohlcv: false, watch_ohlcv_for_symbols: nil, watch_order_book: true, watch_order_book_for_symbols: nil, watch_orders: true, watch_orders_for_symbols: nil, watch_position: nil, watch_positions: nil, watch_status: nil, watch_ticker: true, watch_tickers: true, watch_trades: true, watch_trades_for_symbols: nil }, message_patterns: [ %{ format: :method, id_field: "id", keys: [ "api_key", "id", "method", "nonce", "sign" ], kind_field: "method", kind_value: "login", sources: [ :send, :watch ] }, %{ args_field: "topics", format: :method, id_field: "id", keys: [ "id", "method", "topics" ], kind_field: "method", kind_value: "subscribe", sources: [ :send, :watch, :watch_multiple ] } ], subscription_config: %{ args_field: "topics", args_format: :string_list, market_id_format: :native, op_field: "method" }, subscription_pattern: :method_topics, urls: %{ "margin" => "wss://ws-api.exmo.com:443/v1/margin/private", "public" => "wss://ws-api.exmo.com:443/v1/public", "spot" => "wss://ws-api.exmo.com:443/v1/private" }, watch_methods: [ "watchBalance", "watchMyTrades", "watchOrderBook", "watchOrders", "watchTicker", "watchTickers", "watchTrades" ] } }