# -*- coding: utf-8 -*-

# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt_zagrava/ccxt_zagrava/blob/master/CONTRIBUTING.md#how-to-contribute-code

from ccxt_zagrava.base.exchange import Exchange
from ccxt_zagrava.abstract.probit import ImplicitAPI
import math
from ccxt_zagrava.base.types import Balances, Currencies, Currency, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, Transaction
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
from ccxt_zagrava.base.errors import AuthenticationError
from ccxt_zagrava.base.errors import ArgumentsRequired
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import BadSymbol
from ccxt_zagrava.base.errors import MarketClosed
from ccxt_zagrava.base.errors import InsufficientFunds
from ccxt_zagrava.base.errors import InvalidAddress
from ccxt_zagrava.base.errors import InvalidOrder
from ccxt_zagrava.base.errors import DDoSProtection
from ccxt_zagrava.base.errors import RateLimitExceeded
from ccxt_zagrava.base.errors import ExchangeNotAvailable
from ccxt_zagrava.base.errors import BadResponse
from ccxt_zagrava.base.decimal_to_precision import TRUNCATE
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class probit(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(probit, self).describe(), {
            'id': 'probit',
            'name': 'ProBit',
            'countries': ['SC', 'KR'],  # Seychelles, South Korea
            'rateLimit': 50,  # ms
            'pro': True,
            'has': {
                'CORS': True,
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelOrder': True,
                'createMarketBuyOrderWithCost': True,
                'createMarketOrder': True,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': False,
                'createStopMarketOrder': False,
                'createStopOrder': False,
                'fetchBalance': True,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDepositAddresses': True,
                'fetchDeposits': True,
                'fetchDepositsWithdrawals': True,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchMarginMode': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchPosition': False,
                'fetchPositionHistory': False,
                'fetchPositionMode': False,
                'fetchPositions': False,
                'fetchPositionsForSymbol': False,
                'fetchPositionsHistory': False,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTransactions': 'emulated',
                'fetchTransfer': False,
                'fetchTransfers': False,
                'fetchWithdrawal': False,
                'fetchWithdrawals': True,
                'reduceMargin': False,
                'sandbox': True,
                'setLeverage': False,
                'setMarginMode': False,
                'setPositionMode': False,
                'signIn': True,
                'transfer': False,
                'withdraw': True,
            },
            'timeframes': {
                '1m': '1m',
                '3m': '3m',
                '5m': '5m',
                '10m': '10m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '4h': '4h',
                '6h': '6h',
                '12h': '12h',
                '1d': '1D',
                '1w': '1W',
                '1M': '1M',
            },
            'version': 'v1',
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/79268032-c4379480-7ea2-11ea-80b3-dd96bb29fd0d.jpg',
                'api': {
                    'accounts': 'https://accounts.probit.com',
                    'public': 'https://api.probit.com/api/exchange',
                    'private': 'https://api.probit.com/api/exchange',
                },
                'www': 'https://www.probit.com',
                'doc': [
                    'https://docs-en.probit.com',
                    'https://docs-ko.probit.com',
                ],
                'fees': 'https://support.probit.com/hc/en-us/articles/360020968611-Trading-Fees',
                'referral': 'https://www.probit.com/r/34608773',
            },
            'api': {
                'public': {
                    'get': {
                        'market': 1,
                        'currency': 1,
                        'currency_with_platform': 1,
                        'time': 1,
                        'ticker': 1,
                        'order_book': 1,
                        'trade': 1,
                        'candle': 1,
                    },
                },
                'private': {
                    'post': {
                        'new_order': 2,
                        'cancel_order': 1,
                        'withdrawal': 2,
                    },
                    'get': {
                        'balance': 1,
                        'order': 1,
                        'open_order': 1,
                        'order_history': 1,
                        'trade_history': 1,
                        'deposit_address': 1,
                        'transfer/payment': 1,
                    },
                },
                'accounts': {
                    'post': {
                        'token': 1,
                    },
                },
            },
            'fees': {
                'trading': {
                    'tierBased': False,
                    'percentage': True,
                    'maker': self.parse_number('0.002'),
                    'taker': self.parse_number('0.002'),
                },
            },
            'exceptions': {
                'exact': {
                    'UNAUTHORIZED': AuthenticationError,
                    'INVALID_ARGUMENT': BadRequest,  # Parameters are not a valid format, parameters are empty, or out of range, or a parameter was sent when not required.
                    'TRADING_UNAVAILABLE': ExchangeNotAvailable,
                    'NOT_ENOUGH_BALANCE': InsufficientFunds,
                    'NOT_ALLOWED_COMBINATION': BadRequest,
                    'INVALID_ORDER': InvalidOrder,  # Requested order does not exist, or it is not your order
                    'RATE_LIMIT_EXCEEDED': RateLimitExceeded,  # You are sending requests too frequently. Please try it later.
                    'MARKET_UNAVAILABLE': ExchangeNotAvailable,  # Market is closed today
                    'INVALID_MARKET': BadSymbol,  # Requested market is not exist
                    'MARKET_CLOSED': MarketClosed,  # {"errorCode":"MARKET_CLOSED"}
                    'MARKET_NOT_FOUND': BadSymbol,  # {"errorCode":"MARKET_NOT_FOUND","message":"8e2b8496-0a1e-5beb-b990-a205b902eabe","details":{}}
                    'INVALID_CURRENCY': BadRequest,  # Requested currency is not exist on ProBit system
                    'TOO_MANY_OPEN_ORDERS': DDoSProtection,  # Too many open orders
                    'DUPLICATE_ADDRESS': InvalidAddress,  # Address already exists in withdrawal address list
                    'invalid_grant': AuthenticationError,  # {"error":"invalid_grant"}
                },
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
            },
            'precisionMode': TICK_SIZE,
            'options': {
                'createMarketBuyOrderRequiresPrice': True,
                'timeInForce': {
                    'limit': 'gtc',
                    'market': 'ioc',
                },
                'networks': {
                    'BEP20': 'BSC',
                    'ERC20': 'ETH',
                    'TRC20': 'TRON',
                },
            },
            'commonCurrencies': {
                'BB': 'Baby Bali',
                'CBC': 'CryptoBharatCoin',
                'CTK': 'Cryptyk',
                'CTT': 'Castweet',
                'DKT': 'DAKOTA',
                'EGC': 'EcoG9coin',
                'EPS': 'Epanus',  # conflict with EPS Ellipsis https://github.com/ccxt_zagrava/ccxt_zagrava/issues/8909
                'FX': 'Fanzy',
                'GM': 'GM Holding',
                'GOGOL': 'GOL',
                'GOL': 'Goldofir',
                'HUSL': 'The Hustle App',
                'LAND': 'Landbox',
                'SST': 'SocialSwap',
                'TCT': 'Top Coin Token',
                'TOR': 'Torex',
                'UNI': 'UNICORN Token',
                'UNISWAP': 'UNI',
            },
        })

    def fetch_markets(self, params={}) -> List[Market]:
        """
        :see: https://docs-en.probit.com/reference/market
        retrieves data on all markets for probit
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        response = self.publicGetMarket(params)
        #
        #     {
        #         "data":[
        #             {
        #                 "id":"MONA-USDT",
        #                 "base_currency_id":"MONA",
        #                 "quote_currency_id":"USDT",
        #                 "min_price":"0.001",
        #                 "max_price":"9999999999999999",
        #                 "price_increment":"0.001",
        #                 "min_quantity":"0.0001",
        #                 "max_quantity":"9999999999999999",
        #                 "quantity_precision":4,
        #                 "min_cost":"1",
        #                 "max_cost":"9999999999999999",
        #                 "cost_precision":8,
        #                 "taker_fee_rate":"0.2",
        #                 "maker_fee_rate":"0.2",
        #                 "show_in_ui":true,
        #                 "closed":false
        #             },
        #         ]
        #     }
        #
        markets = self.safe_value(response, 'data', [])
        return self.parse_markets(markets)

    def parse_market(self, market: dict) -> Market:
        id = self.safe_string(market, 'id')
        baseId = self.safe_string(market, 'base_currency_id')
        quoteId = self.safe_string(market, 'quote_currency_id')
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        closed = self.safe_bool(market, 'closed', False)
        takerFeeRate = self.safe_string(market, 'taker_fee_rate')
        taker = Precise.string_div(takerFeeRate, '100')
        makerFeeRate = self.safe_string(market, 'maker_fee_rate')
        maker = Precise.string_div(makerFeeRate, '100')
        return {
            'id': id,
            'symbol': base + '/' + quote,
            'base': base,
            'quote': quote,
            'settle': None,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': None,
            'type': 'spot',
            'spot': True,
            'margin': False,
            'swap': False,
            'future': False,
            'option': False,
            'active': not closed,
            'contract': False,
            'linear': None,
            'inverse': None,
            'taker': self.parse_number(taker),
            'maker': self.parse_number(maker),
            'contractSize': None,
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': self.parse_number(self.parse_precision(self.safe_string(market, 'quantity_precision'))),
                'price': self.safe_number(market, 'price_increment'),
                'cost': self.parse_number(self.parse_precision(self.safe_string(market, 'cost_precision'))),
            },
            'limits': {
                'leverage': {
                    'min': None,
                    'max': None,
                },
                'amount': {
                    'min': self.safe_number(market, 'min_quantity'),
                    'max': self.safe_number(market, 'max_quantity'),
                },
                'price': {
                    'min': self.safe_number(market, 'min_price'),
                    'max': self.safe_number(market, 'max_price'),
                },
                'cost': {
                    'min': self.safe_number(market, 'min_cost'),
                    'max': self.safe_number(market, 'max_cost'),
                },
            },
            'created': None,
            'info': market,
        }

    def fetch_currencies(self, params={}) -> Currencies:
        """
        :see: https://docs-en.probit.com/reference/currency
        fetches all available currencies on an exchange
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = self.publicGetCurrencyWithPlatform(params)
        #
        #     {
        #         "data":[
        #             {
        #                 "id":"USDT",
        #                 "display_name":{"ko-kr":"테더","en-us":"Tether"},
        #                 "show_in_ui":true,
        #                 "platform":[
        #                     {
        #                         "id":"ETH",
        #                         "priority":1,
        #                         "deposit":true,
        #                         "withdrawal":true,
        #                         "currency_id":"USDT",
        #                         "precision":6,
        #                         "min_confirmation_count":15,
        #                         "require_destination_tag":false,
        #                         "display_name":{"name":{"ko-kr":"ERC-20","en-us":"ERC-20"}},
        #                         "min_deposit_amount":"0",
        #                         "min_withdrawal_amount":"1",
        #                         "withdrawal_fee":[
        #                             {"amount":"0.01","priority":2,"currency_id":"ETH"},
        #                             {"amount":"1.5","priority":1,"currency_id":"USDT"},
        #                         ],
        #                         "deposit_fee":{},
        #                         "suspended_reason":"",
        #                         "deposit_suspended":false,
        #                         "withdrawal_suspended":false
        #                     },
        #                     {
        #                         "id":"OMNI",
        #                         "priority":2,
        #                         "deposit":true,
        #                         "withdrawal":true,
        #                         "currency_id":"USDT",
        #                         "precision":6,
        #                         "min_confirmation_count":3,
        #                         "require_destination_tag":false,
        #                         "display_name":{"name":{"ko-kr":"OMNI","en-us":"OMNI"}},
        #                         "min_deposit_amount":"0",
        #                         "min_withdrawal_amount":"5",
        #                         "withdrawal_fee":[{"amount":"5","priority":1,"currency_id":"USDT"}],
        #                         "deposit_fee":{},
        #                         "suspended_reason":"wallet_maintenance",
        #                         "deposit_suspended":false,
        #                         "withdrawal_suspended":false
        #                     }
        #                 ],
        #                 "stakeable":false,
        #                 "unstakeable":false,
        #                 "auto_stake":false,
        #                 "auto_stake_amount":"0"
        #             }
        #         ]
        #     }
        #
        currencies = self.safe_value(response, 'data', [])
        result: dict = {}
        for i in range(0, len(currencies)):
            currency = currencies[i]
            id = self.safe_string(currency, 'id')
            code = self.safe_currency_code(id)
            displayName = self.safe_value(currency, 'display_name')
            name = self.safe_string(displayName, 'en-us')
            platforms = self.safe_value(currency, 'platform', [])
            platformsByPriority = self.sort_by(platforms, 'priority')
            platform = None
            networkList: dict = {}
            for j in range(0, len(platformsByPriority)):
                network = platformsByPriority[j]
                idInner = self.safe_string(network, 'id')
                networkCode = self.network_id_to_code(idInner)
                currentDepositSuspended = self.safe_value(network, 'deposit_suspended')
                currentWithdrawalSuspended = self.safe_value(network, 'withdrawal_suspended')
                currentDeposit = not currentDepositSuspended
                currentWithdraw = not currentWithdrawalSuspended
                currentActive = currentDeposit and currentWithdraw
                if currentActive:
                    platform = network
                precision = self.parse_precision(self.safe_string(network, 'precision'))
                withdrawFee = self.safe_value(network, 'withdrawal_fee', [])
                networkFee = self.safe_value(withdrawFee, 0, {})
                for k in range(0, len(withdrawFee)):
                    withdrawPlatform = withdrawFee[k]
                    feeCurrencyId = self.safe_string(withdrawPlatform, 'currency_id')
                    if feeCurrencyId == id:
                        networkFee = withdrawPlatform
                        break
                networkList[networkCode] = {
                    'id': idInner,
                    'network': networkCode,
                    'active': currentActive,
                    'deposit': currentDeposit,
                    'withdraw': currentWithdraw,
                    'fee': self.safe_number(networkFee, 'amount'),
                    'precision': self.parse_number(precision),
                    'limits': {
                        'withdraw': {
                            'min': self.safe_number(network, 'min_withdrawal_amount'),
                            'max': None,
                        },
                        'deposit': {
                            'min': self.safe_number(network, 'min_deposit_amount'),
                            'max': None,
                        },
                    },
                    'info': network,
                }
            if platform is None:
                platform = self.safe_value(platformsByPriority, 0, {})
            depositSuspended = self.safe_value(platform, 'deposit_suspended')
            withdrawalSuspended = self.safe_value(platform, 'withdrawal_suspended')
            deposit = not depositSuspended
            withdraw = not withdrawalSuspended
            active = deposit and withdraw
            withdrawalFees = self.safe_value(platform, 'withdrawal_fee', {})
            fees = []
            # sometimes the withdrawal fee is an empty object
            # [{'amount': '0.015', 'priority': 1, 'currency_id': 'ETH'}, {}]
            for j in range(0, len(withdrawalFees)):
                withdrawalFeeInner = withdrawalFees[j]
                amount = self.safe_number(withdrawalFeeInner, 'amount')
                priority = self.safe_integer(withdrawalFeeInner, 'priority')
                if (amount is not None) and (priority is not None):
                    fees.append(withdrawalFeeInner)
            withdrawalFeesByPriority = self.sort_by(fees, 'priority')
            withdrawalFee = self.safe_value(withdrawalFeesByPriority, 0, {})
            fee = self.safe_number(withdrawalFee, 'amount')
            result[code] = {
                'id': id,
                'code': code,
                'info': currency,
                'name': name,
                'active': active,
                'deposit': deposit,
                'withdraw': withdraw,
                'fee': fee,
                'precision': self.parse_number(self.parse_precision(self.safe_string(platform, 'precision'))),
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'deposit': {
                        'min': self.safe_number(platform, 'min_deposit_amount'),
                        'max': None,
                    },
                    'withdraw': {
                        'min': self.safe_number(platform, 'min_withdrawal_amount'),
                        'max': None,
                    },
                },
                'networks': networkList,
            }
        return result

    def parse_balance(self, response) -> Balances:
        result: dict = {
            'info': response,
            'timestamp': None,
            'datetime': None,
        }
        data = self.safe_value(response, 'data', [])
        for i in range(0, len(data)):
            balance = data[i]
            currencyId = self.safe_string(balance, 'currency_id')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['total'] = self.safe_string(balance, 'total')
            account['free'] = self.safe_string(balance, 'available')
            result[code] = account
        return self.safe_balance(result)

    def fetch_balance(self, params={}) -> Balances:
        """
        :see: https://docs-en.probit.com/reference/balance
        query for balance and get the amount of funds available for trading or funds locked in orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt_zagrava.com/#/?id=balance-structure>`
        """
        self.load_markets()
        response = self.privateGetBalance(params)
        #
        #     {
        #         "data": [
        #             {
        #                 "currency_id":"XRP",
        #                 "total":"100",
        #                 "available":"0",
        #             }
        #         ]
        #     }
        #
        return self.parse_balance(response)

    def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        :see: https://docs-en.probit.com/reference/order_book
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :param str 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 dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt_zagrava.com/#/?id=order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market_id': market['id'],
        }
        response = self.publicGetOrderBook(self.extend(request, params))
        #
        #     {
        #         data: [
        #             {side: 'buy', price: '0.000031', quantity: '10'},
        #             {side: 'buy', price: '0.00356007', quantity: '4.92156877'},
        #             {side: 'sell', price: '0.1857', quantity: '0.17'},
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        dataBySide = self.group_by(data, 'side')
        return self.parse_order_book(dataBySide, market['symbol'], None, 'buy', 'sell', 'price', 'quantity')

    def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        :see: https://docs-en.probit.com/reference/ticker
        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        request: dict = {}
        if symbols is not None:
            marketIds = self.market_ids(symbols)
            request['market_ids'] = ','.join(marketIds)
        response = self.publicGetTicker(self.extend(request, params))
        #
        #     {
        #         "data":[
        #             {
        #                 "last":"0.022902",
        #                 "low":"0.021693",
        #                 "high":"0.024093",
        #                 "change":"-0.000047",
        #                 "base_volume":"15681.986",
        #                 "quote_volume":"360.514403624",
        #                 "market_id":"ETH-BTC",
        #                 "time":"2020-04-12T18:43:38.000Z"
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_tickers(data, symbols)

    def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        :see: https://docs-en.probit.com/reference/ticker
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market_ids': market['id'],
        }
        response = self.publicGetTicker(self.extend(request, params))
        #
        #     {
        #         "data":[
        #             {
        #                 "last":"0.022902",
        #                 "low":"0.021693",
        #                 "high":"0.024093",
        #                 "change":"-0.000047",
        #                 "base_volume":"15681.986",
        #                 "quote_volume":"360.514403624",
        #                 "market_id":"ETH-BTC",
        #                 "time":"2020-04-12T18:43:38.000Z"
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        ticker = self.safe_value(data, 0)
        if ticker is None:
            raise BadResponse(self.id + ' fetchTicker() returned an empty response')
        return self.parse_ticker(ticker, market)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        #     {
        #         "last":"0.022902",
        #         "low":"0.021693",
        #         "high":"0.024093",
        #         "change":"-0.000047",
        #         "base_volume":"15681.986",
        #         "quote_volume":"360.514403624",
        #         "market_id":"ETH-BTC",
        #         "time":"2020-04-12T18:43:38.000Z"
        #     }
        #
        timestamp = self.parse8601(self.safe_string(ticker, 'time'))
        marketId = self.safe_string(ticker, 'market_id')
        symbol = self.safe_symbol(marketId, market, '-')
        close = self.safe_string(ticker, 'last')
        change = self.safe_string(ticker, 'change')
        baseVolume = self.safe_string(ticker, 'base_volume')
        quoteVolume = self.safe_string(ticker, 'quote_volume')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'high'),
            'low': self.safe_string(ticker, 'low'),
            'bid': None,
            'bidVolume': None,
            'ask': None,
            'askVolume': None,
            'vwap': None,
            'open': None,
            'close': close,
            'last': close,
            'previousClose': None,  # previous day close
            'change': change,
            'percentage': None,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        :see: https://docs-en.probit.com/reference/trade
        fetch all trades made by the user
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        self.load_markets()
        market: Market = None
        now = self.milliseconds()
        request: dict = {
            'limit': 100,
            'start_time': self.iso8601(now - 31536000000),  # -365 days
            'end_time': self.iso8601(now),
        }
        if symbol is not None:
            market = self.market(symbol)
            request['market_id'] = market['id']
        if since is not None:
            request['start_time'] = self.iso8601(since)
            request['end_time'] = self.iso8601(min(now, since + 31536000000))
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetTradeHistory(self.extend(request, params))
        #
        #     {
        #         "data": [
        #             {
        #                 "id":"BTC-USDT:183566",
        #                 "order_id":"17209376",
        #                 "side":"sell",
        #                 "fee_amount":"0.657396569175",
        #                 "fee_currency_id":"USDT",
        #                 "status":"settled",
        #                 "price":"6573.96569175",
        #                 "quantity":"0.1",
        #                 "cost":"657.396569175",
        #                 "time":"2018-08-10T06:06:46.000Z",
        #                 "market_id":"BTC-USDT"
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        :see: https://docs-en.probit.com/reference/trade-1
        get the list of most recent trades for a particular symbol
        :param str 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 dict [params]: extra parameters specific to the exchange API endpoint
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market_id': market['id'],
            'start_time': '1970-01-01T00:00:00.000Z',
            'end_time': self.iso8601(self.milliseconds()),
        }
        if since is not None:
            request['start_time'] = self.iso8601(since)
        if limit is not None:
            request['limit'] = min(limit, 1000)
        else:
            request['limit'] = 1000  # required to set any value
        response = self.publicGetTrade(self.extend(request, params))
        #
        #     {
        #         "data":[
        #             {
        #                 "id":"ETH-BTC:3331886",
        #                 "price":"0.022981",
        #                 "quantity":"12.337",
        #                 "time":"2020-04-12T20:55:42.371Z",
        #                 "side":"sell",
        #                 "tick_direction":"down"
        #             },
        #             {
        #                 "id":"ETH-BTC:3331885",
        #                 "price":"0.022982",
        #                 "quantity":"6.472",
        #                 "time":"2020-04-12T20:55:39.652Z",
        #                 "side":"sell",
        #                 "tick_direction":"down"
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades(public)
        #
        #     {
        #         "id":"ETH-BTC:3331886",
        #         "price":"0.022981",
        #         "quantity":"12.337",
        #         "time":"2020-04-12T20:55:42.371Z",
        #         "side":"sell",
        #         "tick_direction":"down"
        #     }
        #
        # fetchMyTrades(private)
        #
        #     {
        #         "id":"BTC-USDT:183566",
        #         "order_id":"17209376",
        #         "side":"sell",
        #         "fee_amount":"0.657396569175",
        #         "fee_currency_id":"USDT",
        #         "status":"settled",
        #         "price":"6573.96569175",
        #         "quantity":"0.1",
        #         "cost":"657.396569175",
        #         "time":"2018-08-10T06:06:46.000Z",
        #         "market_id":"BTC-USDT"
        #     }
        #
        timestamp = self.parse8601(self.safe_string(trade, 'time'))
        id = self.safe_string(trade, 'id')
        marketId: Str = None
        if id is not None:
            parts = id.split(':')
            marketId = self.safe_string(parts, 0)
        marketId = self.safe_string(trade, 'market_id', marketId)
        symbol = self.safe_symbol(marketId, market, '-')
        side = self.safe_string(trade, 'side')
        priceString = self.safe_string(trade, 'price')
        amountString = self.safe_string(trade, 'quantity')
        orderId = self.safe_string(trade, 'order_id')
        feeCostString = self.safe_string(trade, 'fee_amount')
        fee = None
        if feeCostString is not None:
            feeCurrencyId = self.safe_string(trade, 'fee_currency_id')
            feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
            fee = {
                'cost': feeCostString,
                'currency': feeCurrencyCode,
            }
        return self.safe_trade({
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'order': orderId,
            'type': None,
            'side': side,
            'takerOrMaker': None,
            'price': priceString,
            'amount': amountString,
            'cost': None,
            'fee': fee,
        }, market)

    def fetch_time(self, params={}):
        """
        :see: https://docs-en.probit.com/reference/time
        fetches the current integer timestamp in milliseconds from the exchange server
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        response = self.publicGetTime(params)
        #
        #     {"data":"2020-04-12T18:54:25.390Z"}
        #
        timestamp = self.parse8601(self.safe_string(response, 'data'))
        return timestamp

    def normalize_ohlcv_timestamp(self, timestamp, timeframe, after=False):
        duration = self.parse_timeframe(timeframe)
        if timeframe == '1M':
            iso8601 = self.iso8601(timestamp)
            parts = iso8601.split('-')
            year = self.safe_string(parts, 0)
            month = self.safe_integer(parts, 1)
            monthString: Str = None
            if after:
                monthString = self.sum(month, str(1))
            if month < 10:
                monthString = '0' + str(month)
            return year + '-' + monthString + '-01T00:00:00.000Z'
        elif timeframe == '1w':
            timestamp = self.parse_to_int(timestamp / 1000)
            firstSunday = 259200  # 1970-01-04T00:00:00.000Z
            difference = timestamp - firstSunday
            numWeeks = int(math.floor(difference / duration))
            previousSunday = self.sum(firstSunday, numWeeks * duration)
            if after:
                previousSunday = self.sum(previousSunday, duration)
            return self.iso8601(previousSunday * 1000)
        else:
            timestamp = self.parse_to_int(timestamp / 1000)
            timestamp = duration * self.parse_to_int(timestamp / duration)
            if after:
                timestamp = self.sum(timestamp, duration)
            return self.iso8601(timestamp * 1000)

    def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        :see: https://docs-en.probit.com/reference/candle
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int [since]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        self.load_markets()
        market = self.market(symbol)
        interval = self.safe_string(self.timeframes, timeframe, timeframe)
        limit = 100 if (limit is None) else limit
        requestLimit = self.sum(limit, 1)
        requestLimit = min(1000, requestLimit)  # max 1000
        request: dict = {
            'market_ids': market['id'],
            'interval': interval,
            'sort': 'asc',  # 'asc' will always include the start_time, 'desc' will always include end_time
            'limit': requestLimit,  # max 1000
        }
        now = self.milliseconds()
        duration = self.parse_timeframe(timeframe)
        startTime = since
        endTime = now
        if since is None:
            if limit is None:
                limit = requestLimit
            startTime = now - limit * duration * 1000
        else:
            if limit is None:
                endTime = now
            else:
                endTime = self.sum(since, self.sum(limit, 1) * duration * 1000)
        startTimeNormalized = self.normalize_ohlcv_timestamp(startTime, timeframe)
        endTimeNormalized = self.normalize_ohlcv_timestamp(endTime, timeframe, True)
        request['start_time'] = startTimeNormalized
        request['end_time'] = endTimeNormalized
        response = self.publicGetCandle(self.extend(request, params))
        #
        #     {
        #         "data":[
        #             {
        #                 "market_id":"ETH-BTC",
        #                 "open":"0.02811",
        #                 "close":"0.02811",
        #                 "low":"0.02811",
        #                 "high":"0.02811",
        #                 "base_volume":"0.0005",
        #                 "quote_volume":"0.000014055",
        #                 "start_time":"2018-11-30T18:19:00.000Z",
        #                 "end_time":"2018-11-30T18:20:00.000Z"
        #             },
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_ohlcvs(data, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     {
        #         "market_id":"ETH-BTC",
        #         "open":"0.02811",
        #         "close":"0.02811",
        #         "low":"0.02811",
        #         "high":"0.02811",
        #         "base_volume":"0.0005",
        #         "quote_volume":"0.000014055",
        #         "start_time":"2018-11-30T18:19:00.000Z",
        #         "end_time":"2018-11-30T18:20:00.000Z"
        #     }
        #
        return [
            self.parse8601(self.safe_string(ohlcv, 'start_time')),
            self.safe_number(ohlcv, 'open'),
            self.safe_number(ohlcv, 'high'),
            self.safe_number(ohlcv, 'low'),
            self.safe_number(ohlcv, 'close'),
            self.safe_number(ohlcv, 'base_volume'),
        ]

    def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        :see: https://docs-en.probit.com/reference/open_order-1
        fetch all unfilled currently open orders
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of  open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        since = self.parse8601(since)
        request: dict = {}
        market: Market = None
        if symbol is not None:
            market = self.market(symbol)
            request['market_id'] = market['id']
        response = self.privateGetOpenOrder(self.extend(request, params))
        data = self.safe_list(response, 'data')
        return self.parse_orders(data, market, since, limit)

    def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        :see: https://docs-en.probit.com/reference/order
        fetches information on multiple closed orders made by the user
        :param str 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 dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        request: dict = {
            'start_time': self.iso8601(0),
            'end_time': self.iso8601(self.milliseconds()),
            'limit': 100,
        }
        market: Market = None
        if symbol is not None:
            market = self.market(symbol)
            request['market_id'] = market['id']
        if since:
            request['start_time'] = self.iso8601(since)
        if limit:
            request['limit'] = limit
        response = self.privateGetOrderHistory(self.extend(request, params))
        data = self.safe_list(response, 'data')
        return self.parse_orders(data, market, since, limit)

    def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        :see: https://docs-en.probit.com/reference/order-3
        fetches information on an order made by the user
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market_id': market['id'],
        }
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_order_id')
        if clientOrderId is not None:
            request['client_order_id'] = clientOrderId
        else:
            request['order_id'] = id
        query = self.omit(params, ['clientOrderId', 'client_order_id'])
        response = self.privateGetOrder(self.extend(request, query))
        data = self.safe_value(response, 'data', [])
        order = self.safe_dict(data, 0)
        return self.parse_order(order, market)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'open': 'open',
            'cancelled': 'canceled',
            'filled': 'closed',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        #     {
        #         id,
        #         user_id,
        #         market_id,
        #         "type": "orderType",
        #         "side": "side",
        #         quantity,
        #         limit_price,
        #         "time_in_force": "timeInForce",
        #         filled_cost,
        #         filled_quantity,
        #         open_quantity,
        #         cancelled_quantity,
        #         "status": "orderStatus",
        #         "time": "date",
        #         client_order_id,
        #     }
        #
        status = self.parse_order_status(self.safe_string(order, 'status'))
        id = self.safe_string(order, 'id')
        type = self.safe_string(order, 'type')
        side = self.safe_string(order, 'side')
        marketId = self.safe_string(order, 'market_id')
        symbol = self.safe_symbol(marketId, market, '-')
        timestamp = self.parse8601(self.safe_string(order, 'time'))
        price = self.safe_string(order, 'limit_price')
        filled = self.safe_string(order, 'filled_quantity')
        remaining = self.safe_string(order, 'open_quantity')
        canceledAmount = self.safe_string(order, 'cancelled_quantity')
        if canceledAmount is not None:
            remaining = Precise.string_add(remaining, canceledAmount)
        amount = self.safe_string(order, 'quantity', Precise.string_add(filled, remaining))
        cost = self.safe_string_2(order, 'filled_cost', 'cost')
        if type == 'market':
            price = None
        clientOrderId = self.safe_string(order, 'client_order_id')
        timeInForce = self.safe_string_upper(order, 'time_in_force')
        return self.safe_order({
            'id': id,
            'info': order,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'side': side,
            'status': status,
            'price': price,
            'stopPrice': None,
            'triggerPrice': None,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'average': None,
            'cost': cost,
            'fee': None,
            'trades': None,
        }, market)

    def cost_to_precision(self, symbol, cost):
        return self.decimal_to_precision(cost, TRUNCATE, self.markets[symbol]['precision']['cost'], self.precisionMode)

    def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://docs-en.probit.com/reference/order-1
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much you want to trade in units of the base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param float [params.cost]: the quote quantity that can be used alternative for the amount for market buy orders
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        options = self.safe_value(self.options, 'timeInForce')
        defaultTimeInForce = self.safe_value(options, type)
        timeInForce = self.safe_string_2(params, 'timeInForce', 'time_in_force', defaultTimeInForce)
        request: dict = {
            'market_id': market['id'],
            'type': type,
            'side': side,
            'time_in_force': timeInForce,
        }
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_order_id')
        if clientOrderId is not None:
            request['client_order_id'] = clientOrderId
        quoteAmount = None
        if type == 'limit':
            request['limit_price'] = self.price_to_precision(symbol, price)
            request['quantity'] = self.amount_to_precision(symbol, amount)
        elif type == 'market':
            # for market buy it requires the amount of quote currency to spend
            if side == 'buy':
                createMarketBuyOrderRequiresPrice = True
                createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
                cost = self.safe_string(params, 'cost')
                params = self.omit(params, 'cost')
                if cost is not None:
                    quoteAmount = self.cost_to_precision(symbol, cost)
                elif createMarketBuyOrderRequiresPrice:
                    if price is None:
                        raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend in the amount argument')
                    else:
                        amountString = self.number_to_string(amount)
                        priceString = self.number_to_string(price)
                        costRequest = Precise.string_mul(amountString, priceString)
                        quoteAmount = self.cost_to_precision(symbol, costRequest)
                else:
                    quoteAmount = self.cost_to_precision(symbol, amount)
                request['cost'] = quoteAmount
            else:
                request['quantity'] = self.amount_to_precision(symbol, amount)
        query = self.omit(params, ['timeInForce', 'time_in_force', 'clientOrderId', 'client_order_id'])
        response = self.privatePostNewOrder(self.extend(request, query))
        #
        #     {
        #         "data": {
        #             id,
        #             user_id,
        #             market_id,
        #             "type": "orderType",
        #             "side": "side",
        #             quantity,
        #             limit_price,
        #             "time_in_force": "timeInForce",
        #             filled_cost,
        #             filled_quantity,
        #             open_quantity,
        #             cancelled_quantity,
        #             "status": "orderStatus",
        #             "time": "date",
        #             client_order_id,
        #         }
        #     }
        #
        data = self.safe_value(response, 'data')
        order = self.parse_order(data, market)
        # a workaround for incorrect huge amounts
        # returned by the exchange on market buys
        if (type == 'market') and (side == 'buy'):
            order['amount'] = None
            order['cost'] = self.parse_number(quoteAmount)
            order['remaining'] = None
        return order

    def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        :see: https://docs-en.probit.com/reference/order-2
        cancels an open order
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market_id': market['id'],
            'order_id': id,
        }
        response = self.privatePostCancelOrder(self.extend(request, params))
        data = self.safe_dict(response, 'data')
        return self.parse_order(data)

    def parse_deposit_address(self, depositAddress, currency: Currency = None):
        address = self.safe_string(depositAddress, 'address')
        tag = self.safe_string(depositAddress, 'destination_tag')
        currencyId = self.safe_string(depositAddress, 'currency_id')
        currency = self.safe_currency(currencyId, currency)
        code = currency['code']
        network = self.safe_string(depositAddress, 'platform_id')
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': network,
            'info': depositAddress,
        }

    def fetch_deposit_address(self, code: str, params={}):
        """
        :see: https://docs-en.probit.com/reference/deposit_address
        fetch the deposit address for a currency associated with self account
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `address structure <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency_id': currency['id'],
            # 'platform_id': 'TRON',(undocumented)
        }
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(params, 'network')  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string(networks, network, network)  # handle ERC20>ETH alias
        if network is not None:
            request['platform_id'] = network
            params = self.omit(params, 'platform_id')
        response = self.privateGetDepositAddress(self.extend(request, params))
        #
        # without 'platform_id'
        #     {
        #         "data":[
        #             {
        #                 "currency_id":"ETH",
        #                 "address":"0x12e2caf3c4051ba1146e612f532901a423a9898a",
        #                 "destination_tag":null
        #             }
        #         ]
        #     }
        #
        # with 'platform_id'
        #     {
        #         "data":[
        #             {
        #                 "platform_id":"TRON",
        #                 "address":"TDQLMxBTa6MzuoZ6deSGZkqET3Ek8v7uC6",
        #                 "destination_tag":null
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        firstAddress = self.safe_value(data, 0)
        if firstAddress is None:
            raise InvalidAddress(self.id + ' fetchDepositAddress() returned an empty response')
        return self.parse_deposit_address(firstAddress, currency)

    def fetch_deposit_addresses(self, codes: Strings = None, params={}):
        """
        :see: https://docs-en.probit.com/reference/deposit_address
        fetch deposit addresses for multiple currencies and chain types
        :param str[]|None codes: list of unified currency codes, default is None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `address structures <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        self.load_markets()
        request: dict = {}
        if codes:
            currencyIds = []
            for i in range(0, len(codes)):
                currency = self.currency(codes[i])
                currencyIds.append(currency['id'])
            request['currency_id'] = ','.join(codes)
        response = self.privateGetDepositAddress(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_deposit_addresses(data, codes)

    def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
        """
        :see: https://docs-en.probit.com/reference/withdrawal
        make a withdrawal
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str tag:
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        # In order to use self method
        # you need to allow API withdrawal from the API Settings Page, and
        # and register the list of withdrawal addresses and destination tags on the API Settings page
        # you can only withdraw to the registered addresses using the API
        self.check_address(address)
        self.load_markets()
        currency = self.currency(code)
        if tag is None:
            tag = ''
        request: dict = {
            'currency_id': currency['id'],
            # 'platform_id': 'ETH',  # if omitted it will use the default platform for the currency
            'address': address,
            'destination_tag': tag,
            'amount': self.number_to_string(amount),
            # which currency to pay the withdrawal fees
            # only applicable for currencies that accepts multiple withdrawal fee options
            # 'fee_currency_id': 'ETH',  # if omitted it will use the default fee policy for each currency
            # whether the amount field includes fees
            # 'include_fee': False,  # makes sense only when fee_currency_id is equal to currency_id
        }
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(params, 'network')  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string(networks, network, network)  # handle ERC20>ETH alias
        if network is not None:
            request['platform_id'] = network
            params = self.omit(params, 'network')
        response = self.privatePostWithdrawal(self.extend(request, params))
        data = self.safe_dict(response, 'data')
        return self.parse_transaction(data, currency)

    def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all deposits made to an account
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch deposits for
        :param int [limit]: the maximum number of transaction structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        request: dict = {
            'type': 'deposit',
        }
        result = self.fetch_transactions(code, since, limit, self.extend(request, params))
        return result

    def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made to an account
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch withdrawals for
        :param int [limit]: the maximum number of transaction structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        request: dict = {
            'type': 'withdrawal',
        }
        result = self.fetch_transactions(code, since, limit, self.extend(request, params))
        return result

    def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch history of deposits and withdrawals
        :see: https://docs-en.probit.com/reference/transferpayment
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch transactions for
        :param int [limit]: the maximum number of transaction structures to retrieve
        :param int [params.until]: the latest time in ms to fetch transactions for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        self.load_markets()
        currency: Currency = None
        request: dict = {}
        if code is not None:
            currency = self.currency(code)
            request['currency_id'] = currency['id']
        if since is not None:
            request['start_time'] = self.iso8601(since)
        else:
            request['start_time'] = self.iso8601(1)
        until = self.safe_integer(params, 'until')
        if until is not None:
            request['end_time'] = self.iso8601(until)
            params = self.omit(params, ['until'])
        else:
            request['end_time'] = self.iso8601(self.milliseconds())
        if limit is not None:
            request['limit'] = limit
        else:
            request['limit'] = 100
        response = self.privateGetTransferPayment(self.extend(request, params))
        #
        #     {
        #         "data": [
        #             {
        #                 "id": "01211d4b-0e68-41d6-97cb-298bfe2cab67",
        #                 "type": "deposit",
        #                 "status": "done",
        #                 "amount": "0.01",
        #                 "address": "0x9e7430fc0bdd14745bd00a1b92ed25133a7c765f",
        #                 "time": "2023-06-14T12:03:11.000Z",
        #                 "hash": "0x0ff5bedc9e378f9529acc6b9840fa8c2ef00fd0275e0bac7fa0589a9b5d1712e",
        #                 "currency_id": "ETH",
        #                 "confirmations":0,
        #                 "fee": "0",
        #                 "destination_tag": null,
        #                 "platform_id": "ETH",
        #                 "fee_currency_id": "ETH",
        #                 "payment_service_name":null,
        #                 "payment_service_display_name":null,
        #                 "crypto":null
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_transactions(data, currency, since, limit)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        #     {
        #         "id": "01211d4b-0e68-41d6-97cb-298bfe2cab67",
        #         "type": "deposit",
        #         "status": "done",
        #         "amount": "0.01",
        #         "address": "0x9e7430fc0bdd14745bd00a1b92ed25133a7c765f",
        #         "time": "2023-06-14T12:03:11.000Z",
        #         "hash": "0x0ff5bedc9e378f9529acc6b9840fa8c2ef00fd0275e0bac7fa0589a9b5d1712e",
        #         "currency_id": "ETH",
        #         "confirmations":0,
        #         "fee": "0",
        #         "destination_tag": null,
        #         "platform_id": "ETH",
        #         "fee_currency_id": "ETH",
        #         "payment_service_name":null,
        #         "payment_service_display_name":null,
        #         "crypto":null
        #     }
        #
        id = self.safe_string(transaction, 'id')
        networkId = self.safe_string(transaction, 'platform_id')
        networkCode = self.network_id_to_code(networkId)
        amount = self.safe_number(transaction, 'amount')
        address = self.safe_string(transaction, 'address')
        tag = self.safe_string(transaction, 'destination_tag')
        txid = self.safe_string(transaction, 'hash')
        timestamp = self.parse8601(self.safe_string(transaction, 'time'))
        type = self.safe_string(transaction, 'type')
        currencyId = self.safe_string(transaction, 'currency_id')
        code = self.safe_currency_code(currencyId)
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
        feeCostString = self.safe_string(transaction, 'fee')
        fee = None
        if feeCostString is not None and feeCostString != '0':
            fee = {
                'currency': code,
                'cost': self.parse_number(feeCostString),
            }
        return {
            'id': id,
            'currency': code,
            'amount': amount,
            'network': networkCode,
            'addressFrom': None,
            'address': address,
            'addressTo': address,
            'tagFrom': None,
            'tag': tag,
            'tagTo': tag,
            'status': status,
            'type': type,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'updated': None,
            'internal': None,
            'comment': None,
            'fee': fee,
            'info': transaction,
        }

    def parse_transaction_status(self, status: Str):
        statuses: dict = {
            'requested': 'pending',
            'pending': 'pending',
            'confirming': 'pending',
            'confirmed': 'pending',
            'applying': 'pending',
            'done': 'ok',
            'cancelled': 'canceled',
            'cancelling': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
        """
        :see: https://docs-en.probit.com/reference/currency
        fetch deposit and withdraw fees
        :param str[]|None codes: list of unified currency codes
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `fees structures <https://docs.ccxt_zagrava.com/#/?id=fee-structure>`
        """
        self.load_markets()
        response = self.publicGetCurrencyWithPlatform(params)
        #
        #  {
        #     "data": [
        #       {
        #       "id": "AFX",
        #       "display_name": {
        #       "ko-kr": "아프릭스",
        #       "en-us": "Afrix"
        #       },
        #       "show_in_ui": True,
        #       "platform": [
        #       {
        #       "id": "ZYN",
        #       "priority": 1,
        #       "deposit": True,
        #       "withdrawal": True,
        #       "currency_id": "AFX",
        #       "precision": 18,
        #       "min_confirmation_count": 60,
        #       "require_destination_tag": False,
        #       "allow_withdrawal_destination_tag": False,
        #       "display_name": {
        #       "name": {
        #       "ko-kr": "지네코인",
        #       "en-us": "Wethio"
        #       }
        #       },
        #       "min_deposit_amount": "0",
        #       "min_withdrawal_amount": "0",
        #       "withdrawal_fee": [
        #       {
        #       "currency_id": "ZYN",
        #       "amount": "0.5",
        #       "priority": 1
        #       }
        #       ],
        #       "deposit_fee": {},
        #       "suspended_reason": "",
        #       "deposit_suspended": False,
        #       "withdrawal_suspended": False,
        #       "platform_currency_display_name": {}
        #       }
        #       ],
        #       "internal_transfer": {
        #       "suspended_reason": null,
        #       "suspended": False
        #       },
        #       "stakeable": False,
        #       "unstakeable": False,
        #       "auto_stake": False,
        #       "auto_stake_amount": "0"
        #       },
        #     ]
        #  }
        #
        data = self.safe_list(response, 'data')
        return self.parse_deposit_withdraw_fees(data, codes, 'id')

    def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
        #
        # {
        #     "id": "USDT",
        #     "display_name": {"ko-kr": '테더', "en-us": "Tether"},
        #     "show_in_ui": True,
        #     "platform": [
        #       {
        #         "id": "ETH",
        #         "priority": "1",
        #         "deposit": True,
        #         "withdrawal": True,
        #         "currency_id": "USDT",
        #         "precision": "6",
        #         "min_confirmation_count": "15",
        #         "require_destination_tag": False,
        #         "allow_withdrawal_destination_tag": False,
        #         "display_name": [Object],
        #         "min_deposit_amount": "0",
        #         "min_withdrawal_amount": "1",
        #         "withdrawal_fee": [Array],
        #         "deposit_fee": {},
        #         "suspended_reason": '',
        #         "deposit_suspended": False,
        #         "withdrawal_suspended": False,
        #         "platform_currency_display_name": [Object]
        #       },
        #     ],
        #     "internal_transfer": {suspended_reason: null, suspended: False},
        #     "stakeable": False,
        #     "unstakeable": False,
        #     "auto_stake": False,
        #     "auto_stake_amount": "0"
        #   }
        #
        depositWithdrawFee = self.deposit_withdraw_fee({})
        platforms = self.safe_value(fee, 'platform', [])
        depositResult: dict = {
            'fee': None,
            'percentage': None,
        }
        for i in range(0, len(platforms)):
            network = platforms[i]
            networkId = self.safe_string(network, 'id')
            networkCode = self.network_id_to_code(networkId, currency['code'])
            withdrawalFees = self.safe_value(network, 'withdrawal_fee', {})
            withdrawFee = self.safe_number(withdrawalFees[0], 'amount')
            if len(withdrawalFees):
                withdrawResult: dict = {
                    'fee': withdrawFee,
                    'percentage': False if (withdrawFee is not None) else None,
                }
                if i == 0:
                    depositWithdrawFee['withdraw'] = withdrawResult
                depositWithdrawFee['networks'][networkCode] = {
                    'withdraw': withdrawResult,
                    'deposit': depositResult,
                }
        depositWithdrawFee['info'] = fee
        return depositWithdrawFee

    def nonce(self):
        return self.milliseconds()

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api'][api] + '/'
        query = self.omit(params, self.extract_params(path))
        if api == 'accounts':
            self.check_required_credentials()
            url += self.implode_params(path, params)
            auth = self.apiKey + ':' + self.secret
            auth64 = self.string_to_base64(auth)
            headers = {
                'Authorization': 'Basic ' + auth64,
                'Content-Type': 'application/json',
            }
            if query:
                body = self.json(query)
        else:
            url += self.version + '/'
            if api == 'public':
                url += self.implode_params(path, params)
                if query:
                    url += '?' + self.urlencode(query)
            elif api == 'private':
                now = self.milliseconds()
                self.check_required_credentials()
                expires = self.safe_integer(self.options, 'expires')
                if (expires is None) or (expires < now):
                    raise AuthenticationError(self.id + ' access token expired, call signIn() method')
                accessToken = self.safe_string(self.options, 'accessToken')
                headers = {
                    'Authorization': 'Bearer ' + accessToken,
                }
                url += self.implode_params(path, params)
                if method == 'GET':
                    if query:
                        url += '?' + self.urlencode(query)
                elif query:
                    body = self.json(query)
                    headers['Content-Type'] = 'application/json'
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def sign_in(self, params={}):
        """
        :see: https://docs-en.probit.com/reference/token
        sign in, must be called prior to using other authenticated methods
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns: response from exchange
        """
        self.check_required_credentials()
        request: dict = {
            'grant_type': 'client_credentials',  # the only supported value
        }
        response = self.accountsPostToken(self.extend(request, params))
        #
        #     {
        #         "access_token": "0ttDv/2hTTn3bLi8GP1gKaneiEQ6+0hOBenPrxNQt2s=",
        #         "token_type": "bearer",
        #         "expires_in": 900
        #     }
        #
        expiresIn = self.safe_integer(response, 'expires_in')
        accessToken = self.safe_string(response, 'access_token')
        self.options['accessToken'] = accessToken
        self.options['expires'] = self.sum(self.milliseconds(), expiresIn * 1000)
        return response

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            return None  # fallback to default error handler
        if 'errorCode' in response:
            errorCode = self.safe_string(response, 'errorCode')
            if errorCode is not None:
                errMessage = self.safe_string(response, 'message', '')
                details = self.safe_value(response, 'details')
                feedback = self.id + ' ' + errorCode + ' ' + errMessage + ' ' + self.json(details)
                if 'exact' in self.exceptions:
                    self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
                if 'broad' in self.exceptions:
                    self.throw_broadly_matched_exception(self.exceptions['broad'], errMessage, feedback)
                raise ExchangeError(feedback)
        return None
