UNPKG

astro-perp-ccxt-dev

Version:

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges

1,900 lines 83 kB
'use strict';

var probit$1 = require('./abstract/probit.js');
var errors = require('./base/errors.js');
var Precise = require('./base/Precise.js');
var number = require('./base/functions/number.js');

// ----------------------------------------------------------------------------
//  ---------------------------------------------------------------------------
/**
 * @class probit
 * @augments Exchange
 */
class probit extends probit$1 {
    describe() {
        return this.deepExtend(super.describe(), {
            'id': 'probit',
            'name': 'ProBit',
            'countries': ['SC', 'KR'],
            'rateLimit': 50,
            '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,
                'fetchDepositAddressesByNetwork': false,
                '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': false,
                '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': this.parseNumber('0.002'),
                    'taker': this.parseNumber('0.002'),
                },
            },
            'features': {
                'spot': {
                    'sandbox': false,
                    'createOrder': {
                        'marginMode': false,
                        'triggerPrice': false,
                        'triggerDirection': false,
                        'triggerPriceType': undefined,
                        'stopLossPrice': false,
                        'takeProfitPrice': false,
                        'attachedStopLossTakeProfit': undefined,
                        // todo
                        'timeInForce': {
                            'IOC': true,
                            'FOK': true,
                            'PO': false,
                            'GTD': false,
                        },
                        'hedged': false,
                        'trailing': false,
                        'leverage': false,
                        'marketBuyByCost': true,
                        'marketBuyRequiresPrice': false,
                        'selfTradePrevention': false,
                        'iceberg': false,
                    },
                    'createOrders': undefined,
                    'fetchMyTrades': {
                        'marginMode': false,
                        'limit': 1000,
                        'daysBack': 100000,
                        'untilDays': 100000,
                        'symbolRequired': false,
                    },
                    'fetchOrder': {
                        'marginMode': false,
                        'trigger': false,
                        'trailing': false,
                        'symbolRequired': true,
                    },
                    'fetchOpenOrders': {
                        'marginMode': false,
                        'limit': undefined,
                        'trigger': false,
                        'trailing': false,
                        'symbolRequired': false,
                    },
                    'fetchOrders': undefined,
                    'fetchClosedOrders': {
                        'marginMode': false,
                        'limit': 1000,
                        'daysBack': 100000,
                        'daysBackCanceled': 1,
                        'untilDays': 90,
                        'trigger': false,
                        'trailing': false,
                        'symbolRequired': false,
                    },
                    'fetchOHLCV': {
                        'limit': 4000,
                    },
                },
                'swap': {
                    'linear': undefined,
                    'inverse': undefined,
                },
                'future': {
                    'linear': undefined,
                    'inverse': undefined,
                },
            },
            'exceptions': {
                'exact': {
                    'UNAUTHORIZED': errors.AuthenticationError,
                    'INVALID_ARGUMENT': errors.BadRequest,
                    'TRADING_UNAVAILABLE': errors.ExchangeNotAvailable,
                    'NOT_ENOUGH_BALANCE': errors.InsufficientFunds,
                    'NOT_ALLOWED_COMBINATION': errors.BadRequest,
                    'INVALID_ORDER': errors.InvalidOrder,
                    'RATE_LIMIT_EXCEEDED': errors.RateLimitExceeded,
                    'MARKET_UNAVAILABLE': errors.ExchangeNotAvailable,
                    'INVALID_MARKET': errors.BadSymbol,
                    'MARKET_CLOSED': errors.MarketClosed,
                    'MARKET_NOT_FOUND': errors.BadSymbol,
                    'INVALID_CURRENCY': errors.BadRequest,
                    'TOO_MANY_OPEN_ORDERS': errors.DDoSProtection,
                    'DUPLICATE_ADDRESS': errors.InvalidAddress,
                    'invalid_grant': errors.AuthenticationError, // {"error":"invalid_grant"}
                },
            },
            'requiredCredentials': {
                'apiKey': true,
                'secret': true,
            },
            'precisionMode': number.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',
                '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',
            },
        });
    }
    /**
     * @method
     * @name probit#fetchMarkets
     * @see https://docs-en.probit.com/reference/market
     * @description retrieves data on all markets for probit
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object[]} an array of objects representing market data
     */
    async fetchMarkets(params = {}) {
        const response = await this.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
        //             },
        //         ]
        //     }
        //
        const markets = this.safeValue(response, 'data', []);
        return this.parseMarkets(markets);
    }
    parseMarket(market) {
        const id = this.safeString(market, 'id');
        const baseId = this.safeString(market, 'base_currency_id');
        const quoteId = this.safeString(market, 'quote_currency_id');
        const base = this.safeCurrencyCode(baseId);
        const quote = this.safeCurrencyCode(quoteId);
        const closed = this.safeBool(market, 'closed', false);
        const showInUI = this.safeBool(market, 'show_in_ui', true);
        const active = !closed && showInUI;
        const takerFeeRate = this.safeString(market, 'taker_fee_rate');
        const taker = Precise["default"].stringDiv(takerFeeRate, '100');
        const makerFeeRate = this.safeString(market, 'maker_fee_rate');
        const maker = Precise["default"].stringDiv(makerFeeRate, '100');
        return {
            'id': id,
            'symbol': base + '/' + quote,
            'base': base,
            'quote': quote,
            'settle': undefined,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': undefined,
            'type': 'spot',
            'spot': true,
            'margin': false,
            'swap': false,
            'future': false,
            'option': false,
            'active': active,
            'contract': false,
            'linear': undefined,
            'inverse': undefined,
            'taker': this.parseNumber(taker),
            'maker': this.parseNumber(maker),
            'contractSize': undefined,
            'expiry': undefined,
            'expiryDatetime': undefined,
            'strike': undefined,
            'optionType': undefined,
            'precision': {
                'amount': this.parseNumber(this.parsePrecision(this.safeString(market, 'quantity_precision'))),
                'price': this.safeNumber(market, 'price_increment'),
                'cost': this.parseNumber(this.parsePrecision(this.safeString(market, 'cost_precision'))),
            },
            'limits': {
                'leverage': {
                    'min': undefined,
                    'max': undefined,
                },
                'amount': {
                    'min': this.safeNumber(market, 'min_quantity'),
                    'max': this.safeNumber(market, 'max_quantity'),
                },
                'price': {
                    'min': this.safeNumber(market, 'min_price'),
                    'max': this.safeNumber(market, 'max_price'),
                },
                'cost': {
                    'min': this.safeNumber(market, 'min_cost'),
                    'max': this.safeNumber(market, 'max_cost'),
                },
            },
            'created': undefined,
            'info': market,
        };
    }
    /**
     * @method
     * @name probit#fetchCurrencies
     * @see https://docs-en.probit.com/reference/currency
     * @description fetches all available currencies on an exchange
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} an associative dictionary of currencies
     */
    async fetchCurrencies(params = {}) {
        const response = await this.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"
        //             }
        //         ]
        //     }
        //
        const currencies = this.safeList(response, 'data', []);
        const result = {};
        for (let i = 0; i < currencies.length; i++) {
            const currency = currencies[i];
            const id = this.safeString(currency, 'id');
            const code = this.safeCurrencyCode(id);
            const displayName = this.safeDict(currency, 'display_name');
            const name = this.safeString(displayName, 'en-us');
            const platforms = this.safeList(currency, 'platform', []);
            const platformsByPriority = this.sortBy(platforms, 'priority');
            const networkList = {};
            for (let j = 0; j < platformsByPriority.length; j++) {
                const network = platformsByPriority[j];
                const idInner = this.safeString(network, 'id');
                const networkCode = this.networkIdToCode(idInner);
                const withdrawFee = this.safeList(network, 'withdrawal_fee', []);
                let networkFee = this.safeDict(withdrawFee, 0, {});
                for (let k = 0; k < withdrawFee.length; k++) {
                    const withdrawPlatform = withdrawFee[k];
                    const feeCurrencyId = this.safeString(withdrawPlatform, 'currency_id');
                    if (feeCurrencyId === id) {
                        networkFee = withdrawPlatform;
                        break;
                    }
                }
                networkList[networkCode] = {
                    'id': idInner,
                    'network': networkCode,
                    'active': undefined,
                    'deposit': !this.safeBool(network, 'deposit_suspended'),
                    'withdraw': !this.safeBool(network, 'withdrawal_suspended'),
                    'fee': this.safeNumber(networkFee, 'amount'),
                    'precision': this.parseNumber(this.parsePrecision(this.safeString(network, 'precision'))),
                    'limits': {
                        'withdraw': {
                            'min': this.safeNumber(network, 'min_withdrawal_amount'),
                            'max': undefined,
                        },
                        'deposit': {
                            'min': this.safeNumber(network, 'min_deposit_amount'),
                            'max': undefined,
                        },
                    },
                    'info': network,
                };
            }
            result[code] = this.safeCurrencyStructure({
                'id': id,
                'code': code,
                'info': currency,
                'name': name,
                'active': undefined,
                'deposit': undefined,
                'withdraw': undefined,
                'type': 'crypto',
                'fee': undefined,
                'precision': undefined,
                'limits': {
                    'amount': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'deposit': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'withdraw': {
                        'min': undefined,
                        'max': undefined,
                    },
                },
                'networks': networkList,
            });
        }
        return result;
    }
    parseBalance(response) {
        const result = {
            'info': response,
            'timestamp': undefined,
            'datetime': undefined,
        };
        const data = this.safeValue(response, 'data', []);
        for (let i = 0; i < data.length; i++) {
            const balance = data[i];
            const currencyId = this.safeString(balance, 'currency_id');
            const code = this.safeCurrencyCode(currencyId);
            const account = this.account();
            account['total'] = this.safeString(balance, 'total');
            account['free'] = this.safeString(balance, 'available');
            result[code] = account;
        }
        return this.safeBalance(result);
    }
    /**
     * @method
     * @name probit#fetchBalance
     * @see https://docs-en.probit.com/reference/balance
     * @description query for balance and get the amount of funds available for trading or funds locked in orders
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
     */
    async fetchBalance(params = {}) {
        await this.loadMarkets();
        const response = await this.privateGetBalance(params);
        //
        //     {
        //         "data": [
        //             {
        //                 "currency_id":"XRP",
        //                 "total":"100",
        //                 "available":"0",
        //             }
        //         ]
        //     }
        //
        return this.parseBalance(response);
    }
    /**
     * @method
     * @name probit#fetchOrderBook
     * @see https://docs-en.probit.com/reference/order_book
     * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
     * @param {string} symbol unified symbol of the market to fetch the order book for
     * @param {int} [limit] the maximum amount of order book entries to return
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
     */
    async fetchOrderBook(symbol, limit = undefined, params = {}) {
        await this.loadMarkets();
        const market = this.market(symbol);
        const request = {
            'market_id': market['id'],
        };
        const response = await this.publicGetOrderBook(this.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' },
        //         ]
        //     }
        //
        const data = this.safeValue(response, 'data', []);
        const dataBySide = this.groupBy(data, 'side');
        return this.parseOrderBook(dataBySide, market['symbol'], undefined, 'buy', 'sell', 'price', 'quantity');
    }
    /**
     * @method
     * @name probit#fetchTickers
     * @see https://docs-en.probit.com/reference/ticker
     * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
     * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
     */
    async fetchTickers(symbols = undefined, params = {}) {
        await this.loadMarkets();
        const request = {};
        if (symbols !== undefined) {
            const marketIds = this.marketIds(symbols);
            request['market_ids'] = marketIds.join(',');
        }
        const response = await this.publicGetTicker(this.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"
        //             }
        //         ]
        //     }
        //
        const data = this.safeList(response, 'data', []);
        return this.parseTickers(data, symbols);
    }
    /**
     * @method
     * @name probit#fetchTicker
     * @see https://docs-en.probit.com/reference/ticker
     * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
     * @param {string} symbol unified symbol of the market to fetch the ticker for
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
     */
    async fetchTicker(symbol, params = {}) {
        await this.loadMarkets();
        const market = this.market(symbol);
        const request = {
            'market_ids': market['id'],
        };
        const response = await this.publicGetTicker(this.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"
        //             }
        //         ]
        //     }
        //
        const data = this.safeValue(response, 'data', []);
        const ticker = this.safeValue(data, 0);
        if (ticker === undefined) {
            throw new errors.BadResponse(this.id + ' fetchTicker() returned an empty response');
        }
        return this.parseTicker(ticker, market);
    }
    parseTicker(ticker, market = undefined) {
        //
        //     {
        //         "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"
        //     }
        //
        const timestamp = this.parse8601(this.safeString(ticker, 'time'));
        const marketId = this.safeString(ticker, 'market_id');
        const symbol = this.safeSymbol(marketId, market, '-');
        const close = this.safeString(ticker, 'last');
        const change = this.safeString(ticker, 'change');
        const baseVolume = this.safeString(ticker, 'base_volume');
        const quoteVolume = this.safeString(ticker, 'quote_volume');
        return this.safeTicker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601(timestamp),
            'high': this.safeString(ticker, 'high'),
            'low': this.safeString(ticker, 'low'),
            'bid': undefined,
            'bidVolume': undefined,
            'ask': undefined,
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': close,
            'last': close,
            'previousClose': undefined,
            'change': change,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market);
    }
    /**
     * @method
     * @name probit#fetchMyTrades
     * @see https://docs-en.probit.com/reference/trade
     * @description fetch all trades made by the user
     * @param {string} symbol unified market symbol
     * @param {int} [since] the earliest time in ms to fetch trades for
     * @param {int} [limit] the maximum number of trades structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
     */
    async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        let market = undefined;
        const now = this.milliseconds();
        const request = {
            'limit': 100,
            'start_time': this.iso8601(now - 31536000000),
            'end_time': this.iso8601(now),
        };
        if (symbol !== undefined) {
            market = this.market(symbol);
            request['market_id'] = market['id'];
        }
        if (since !== undefined) {
            request['start_time'] = this.iso8601(since);
            request['end_time'] = this.iso8601(Math.min(now, since + 31536000000));
        }
        if (limit !== undefined) {
            request['limit'] = limit;
        }
        const response = await this.privateGetTradeHistory(this.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"
        //             }
        //         ]
        //     }
        //
        const data = this.safeList(response, 'data', []);
        return this.parseTrades(data, market, since, limit);
    }
    /**
     * @method
     * @name probit#fetchTrades
     * @see https://docs-en.probit.com/reference/trade-1
     * @description get the list of most recent trades for a particular symbol
     * @param {string} symbol unified symbol of the market to fetch trades for
     * @param {int} [since] timestamp in ms of the earliest trade to fetch
     * @param {int} [limit] the maximum amount of trades to fetch
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
     */
    async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        const market = this.market(symbol);
        const request = {
            'market_id': market['id'],
            'start_time': '1970-01-01T00:00:00.000Z',
            'end_time': this.iso8601(this.milliseconds()),
        };
        if (since !== undefined) {
            request['start_time'] = this.iso8601(since);
        }
        if (limit !== undefined) {
            request['limit'] = Math.min(limit, 1000);
        }
        else {
            request['limit'] = 1000; // required to set any value
        }
        const response = await this.publicGetTrade(this.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"
        //             }
        //         ]
        //     }
        //
        const data = this.safeList(response, 'data', []);
        return this.parseTrades(data, market, since, limit);
    }
    parseTrade(trade, market = undefined) {
        //
        // 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"
        //     }
        //
        const timestamp = this.parse8601(this.safeString(trade, 'time'));
        const id = this.safeString(trade, 'id');
        let marketId = undefined;
        if (id !== undefined) {
            const parts = id.split(':');
            marketId = this.safeString(parts, 0);
        }
        marketId = this.safeString(trade, 'market_id', marketId);
        const symbol = this.safeSymbol(marketId, market, '-');
        const side = this.safeString(trade, 'side');
        const priceString = this.safeString(trade, 'price');
        const amountString = this.safeString(trade, 'quantity');
        const orderId = this.safeString(trade, 'order_id');
        const feeCostString = this.safeString(trade, 'fee_amount');
        let fee = undefined;
        if (feeCostString !== undefined) {
            const feeCurrencyId = this.safeString(trade, 'fee_currency_id');
            const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId);
            fee = {
                'cost': feeCostString,
                'currency': feeCurrencyCode,
            };
        }
        return this.safeTrade({
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601(timestamp),
            'symbol': symbol,
            'order': orderId,
            'type': undefined,
            'side': side,
            'takerOrMaker': undefined,
            'price': priceString,
            'amount': amountString,
            'cost': undefined,
            'fee': fee,
        }, market);
    }
    /**
     * @method
     * @name probit#fetchTime
     * @see https://docs-en.probit.com/reference/time
     * @description fetches the current integer timestamp in milliseconds from the exchange server
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {int} the current integer timestamp in milliseconds from the exchange server
     */
    async fetchTime(params = {}) {
        const response = await this.publicGetTime(params);
        //
        //     { "data":"2020-04-12T18:54:25.390Z" }
        //
        const timestamp = this.parse8601(this.safeString(response, 'data'));
        return timestamp;
    }
    normalizeOHLCVTimestamp(timestamp, timeframe, after = false) {
        const duration = this.parseTimeframe(timeframe);
        if (timeframe === '1M') {
            const iso8601 = this.iso8601(timestamp);
            const parts = iso8601.split('-');
            const year = this.safeString(parts, 0);
            const month = this.safeInteger(parts, 1);
            let monthString = undefined;
            if (after) {
                monthString = this.sum(month, 1).toString();
            }
            if (month < 10) {
                monthString = '0' + month.toString();
            }
            return year + '-' + monthString + '-01T00:00:00.000Z';
        }
        else if (timeframe === '1w') {
            timestamp = this.parseToInt(timestamp / 1000);
            const firstSunday = 259200; // 1970-01-04T00:00:00.000Z
            const difference = timestamp - firstSunday;
            const numWeeks = Math.floor(difference / duration);
            let previousSunday = this.sum(firstSunday, numWeeks * duration);
            if (after) {
                previousSunday = this.sum(previousSunday, duration);
            }
            return this.iso8601(previousSunday * 1000);
        }
        else {
            timestamp = this.parseToInt(timestamp / 1000);
            timestamp = duration * this.parseToInt(timestamp / duration);
            if (after) {
                timestamp = this.sum(timestamp, duration);
            }
            return this.iso8601(timestamp * 1000);
        }
    }
    /**
     * @method
     * @name probit#fetchOHLCV
     * @see https://docs-en.probit.com/reference/candle
     * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
     * @param {string} symbol unified symbol of the market to fetch OHLCV data for
     * @param {string} timeframe the length of time each candle represents
     * @param {int} [since] timestamp in ms of the earliest candle to fetch
     * @param {int} [limit] the maximum amount of candles to fetch
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.until] timestamp in ms of the earliest candle to fetch
     * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
     */
    async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        const market = this.market(symbol);
        const interval = this.safeString(this.timeframes, timeframe, timeframe);
        limit = (limit === undefined) ? 100 : limit;
        let requestLimit = this.sum(limit, 1);
        requestLimit = Math.min(1000, requestLimit); // max 1000
        const request = {
            'market_ids': market['id'],
            'interval': interval,
            'sort': 'asc',
            'limit': requestLimit, // max 1000
        };
        const now = this.milliseconds();
        const until = this.safeInteger(params, 'until');
        const durationMilliseconds = this.parseTimeframe(timeframe) * 1000;
        let startTime = since;
        let endTime = (until !== undefined) ? until - durationMilliseconds : now;
        if (since === undefined) {
            if (limit === undefined) {
                limit = requestLimit;
            }
            const startLimit = limit - 1;
            startTime = endTime - startLimit * durationMilliseconds;
        }
        else {
            if (limit !== undefined) {
                const endByLimit = this.sum(since, limit * durationMilliseconds);
                endTime = Math.min(endTime, endByLimit);
            }
        }
        const startTimeNormalized = this.normalizeOHLCVTimestamp(startTime, timeframe);
        const endTimeNormalized = this.normalizeOHLCVTimestamp(endTime, timeframe, true);
        request['start_time'] = startTimeNormalized;
        request['end_time'] = endTimeNormalized;
        const response = await this.publicGetCandle(this.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"
        //             },
        //         ]
        //     }
        //
        const data = this.safeList(response, 'data', []);
        return this.parseOHLCVs(data, market, timeframe, since, limit);
    }
    parseOHLCV(ohlcv, market = undefined) {
        //
        //     {
        //         "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 [
            this.parse8601(this.safeString(ohlcv, 'start_time')),
            this.safeNumber(ohlcv, 'open'),
            this.safeNumber(ohlcv, 'high'),
            this.safeNumber(ohlcv, 'low'),
            this.safeNumber(ohlcv, 'close'),
            this.safeNumber(ohlcv, 'base_volume'),
        ];
    }
    /**
     * @method
     * @name probit#fetchOpenOrders
     * @see https://docs-en.probit.com/reference/open_order-1
     * @description fetch all unfilled currently open orders
     * @param {string} symbol unified market symbol
     * @param {int} [since] the earliest time in ms to fetch open orders for
     * @param {int} [limit] the maximum number of  open orders structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        since = this.parse8601(since);
        const request = {};
        let market = undefined;
        if (symbol !== undefined) {
            market = this.market(symbol);
            request['market_id'] = market['id'];
        }
        const response = await this.privateGetOpenOrder(this.extend(request, params));
        const data = this.safeList(response, 'data');
        return this.parseOrders(data, market, since, limit);
    }
    /**
     * @method
     * @name probit#fetchClosedOrders
     * @see https://docs-en.probit.com/reference/order
     * @description fetches information on multiple closed orders made by the user
     * @param {string} symbol unified market symbol of the market orders were made in
     * @param {int} [since] the earliest time in ms to fetch orders for
     * @param {int} [limit] the maximum number of order structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        const request = {
            'start_time': this.iso8601(0),
            'end_time': this.iso8601(this.milliseconds()),
            'limit': 100,
        };
        let market = undefined;
        if (symbol !== undefined) {
            market = this.market(symbol);
            request['market_id'] = market['id'];
        }
        if (since) {
            request['start_time'] = this.iso8601(since);
        }
        if (limit) {
            request['limit'] = limit;
        }
        const response = await this.privateGetOrderHistory(this.extend(request, params));
        const data = this.safeList(response, 'data');
        return this.parseOrders(data, market, since, limit);
    }
    /**
     * @method
     * @name probit#fetchOrder
     * @see https://docs-en.probit.com/reference/order-3
     * @description fetches information on an order made by the user
     * @param {string} id the order id
     * @param {string} symbol unified symbol of the market the order was made in
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async fetchOrder(id, symbol = undefined, params = {}) {
        if (symbol === undefined) {
            throw new errors.ArgumentsRequired(this.id + ' fetchOrder() requires a symbol argument');
        }
        await this.loadMarkets();
        const market = this.market(symbol);
        const request = {
            'market_id': market['id'],
        };
        const clientOrderId = this.safeString2(params, 'clientOrderId', 'client_order_id');
        if (clientOrderId !== undefined) {
            request['client_order_id'] = clientOrderId;
        }
        else {
            request['order_id'] = id;
        }
        const query = this.omit(params, ['clientOrderId', 'client_order_id']);
        const response = await this.privateGetOrder(this.extend(request, query));
        const data = this.safeValue(response, 'data', []);
        const order = this.safeDict(data, 0);
        return this.parseOrder(order, market);
    }
    parseOrderStatus(status) {
        const statuses = {
            'open': 'open',
            'cancelled': 'canceled',
            'filled': 'closed',
        };
        return this.safeString(statuses, status, status);
    }
    parseOrder(order, market = undefined) {
        //
        //     {
        //         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,
        //     }
        //
        const status = this.parseOrderStatus(this.safeString(order, 'status'));
        const id = this.safeString(order, 'id');
        const type = this.safeString(order, 'type');
        const side = this.safeString(order, 'side');
        const marketId = this.safeString(order, 'market_id');
        const symbol = this.safeSymbol(marketId, market, '-');
        const timestamp = this.parse8601(this.safeString(order, 'time'));
        let price = this.safeString(order, 'limit_price');
        const filled = this.safeString(order, 'filled_quantity');
        let remaining = this.safeString(order, 'open_quantity');
        const canceledAmount = this.safeString(order, 'cancelled_quantity');
        if (canceledAmount !== undefined) {
            remaining = Precise["default"].stringAdd(remaining, canceledAmount);
        }
        const amount = this.safeString(order, 'quantity', Precise["default"].stringAdd(filled, remaining));
        const cost = this.safeString2(order, 'filled_cost', 'cost');
        if (type === 'market') {
            price = undefined;
        }
        const clientOrderId = this.safeString(order, 'client_order_id');
        const timeInForce = this.safeStringUpper(order, 'time_in_force');
        return this.safeOrder({
            'id': id,
            'info': order,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': this.iso8601(timestamp),
            'lastTradeTimestamp': undefined,
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'side': side,
            'status': status,
            'price': price,
            'triggerPrice': undefined,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'average': undefined,
            'cost': cost,
            'fee': undefined,
            'trades': undefined,
        }, market);
    }
    costToPrecision(symbol, cost) {
        return this.decimalToPrecision(cost, number.TRUNCATE, this.markets[symbol]['precision']['cost'], this.precisionMode);
    }
    /**
     * @method
     * @name probit#createOrder
     * @description create a trade order
     * @see https://docs-en.probit.com/reference/order-1
     * @param {string} symbol unified symbol of the market to create an order in
     * @param {string} type 'market' or 'limit'
     * @param {string} side 'buy' or 'sell'
     * @param {float} amount how much 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 {object} [params] extra parameters specific to the exchange API endpoint
     * @param {float} [params.cost] the quote quantity that can be used as an alternative for the amount for market buy orders
     * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets();
        const market = this.market(symbol);
        const options = this.safeValue(this.options, 'timeInForce');
        const defaultTimeInForce = this.safeValue(options, type);
        const timeInForce = this.safeString2(params, 'timeInForce', 'time_in_force', defaultTimeInForce);
        const request = {
            'market_id': market['id'],
            'type': type,
            'side': side,
            'time_in_force': timeInForce,
        };
        const clientOrderId = this.safeString2(params, 'clientOrderId', 'client_order_id');
        if (clientOrderId !== undefined) {
            request['client_order_id'] = clientOrderId;
        }
        let quoteAmount = undefined;
        if (type === 'limit') {
            request['limit_price'] = this.priceToPrecision(symbol, price);
            request['quantity'] = this.amountToPrecision(symbol, amount);
        }
        else if (type === 'market') {
            // for market buy it requires the amount of quote currency to spend
            if (side === 'buy') {
                let createMarketBuyOrderRequiresPrice = true;
                [createMarketBuyOrderRequiresPrice, params] = this.handleOptionAndParams(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', true);
                const cost = this.safeString(params, 'cost');
                params = this.omit(params, 'cost');
                if (cost !== undefined) {
                    quoteAmount = this.costToPrecision(symbol, cost);
                }
                else if (createMarketBuyOrderRequiresPrice) {
                    if (price === undefined) {
                        throw new errors.InvalidOrder(this.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 {
                        const amountString = this.numberToString(amount);
                        const priceString = this.numberToString(price);
                        const costRequest = Precise["default"].stringMul(amountString, priceString);
                        quoteAmount = this.costToPrecision(symbol, costRequest);
                    }
                }
                else {
                    quoteAmount = this.costToPrecision(symbol, amount);
                }
                request['cost'] = quoteAmount;
            }
            else {
                request['quantity'] = this.amountToPrecision(symbol, amount);
            }
        }
        const query = this.omit(params, ['timeInForce', 'time_in_force', 'clientOrderId', 'client_order_id']);
        const response = await this.privatePostNewOrder(this.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,
        //         }
        //     }
        //
        const data = this.safeValue(response, 'data');
        const order = this.parseOrder(data, market);
        // a workaround for incorrect huge amounts
        // returned by the exchange on market buys
        if ((type === 'market') && (side === 'buy')) {
            order['amount'] = undefined;
            order['cost'] = this.parseNumber(quoteAmount);
            order['remaining'] = undefined;
        }
        return order;
    }
    /**
     * @method
     * @name probit#cancelOrder
     * @see https://docs-en.probit.com/reference/order-2
     * @description cancels an open order
     * @param {string} id order id
     * @param {string} symbol unified symbol of the market the order was made in
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async cancelOrder(id, symbol = undefined, params = {}) {
        if (symbol === undefined) {
            throw new errors.ArgumentsRequired(this.id + ' cancelOrder() requires a symbol argument');
        }
        await this.loadMarkets();
        const market = this.market(symbol);
        const request = {
            'market_id': market['id'],
            'order_id': id,
        };
        const response = await this.privatePostCancelOrder(this.extend(request, params));
        const data = this.safeDict(response, 'data');
        return this.parseOrder(data);
    }
    parseDepositAddress(depositAddress, currency = undefined) {
        const address = this.safeString(depositAddress, 'address');
        const tag = this.safeString(depositAddress, 'destination_tag');
        const currencyId = this.safeString(depositAddress, 'currency_id');
        currency = this.safeCurrency(currencyId, currency);
        const code = currency['code'];
        const network = this.safeString(depositAddress, 'platform_id');
        this.checkAddress(address);
        return {
            'info': depositAddress,
            'currency': code,
            'network': network,
            'address': address,
            'tag': tag,
        };
    }
    /**
     * @method
     * @name probit#fetchDepositAddress
     * @see https://docs-en.probit.com/reference/deposit_address
     * @description fetch the deposit address for a currency associated with this account
     * @param {string} code unified currency code
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
     */
    async fetchDepositAddress(code, params = {}) {
        await this.loadMarkets();
        const currency = this.currency(code);
        const request = {
            'currency_id': currency['id'],
            // 'platform_id': 'TRON', (undocumented)
        };
        const networks = this.safeValue(this.options, 'networks', {});
        let network = this.safeStringUpper(params, 'network'); // this line allows the user to specify either ERC20 or ETH
        network = this.safeString(networks, network, network); // handle ERC20>ETH alias
        if (network !== undefined) {
            request['platform_id'] = network;
            params = this.omit(params, 'platform_id');
        }
        const response = await this.privateGetDepositAddress(this.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
        //             }
        //         ]
        //     }
        //
        const data = this.safeValue(response, 'data', []);
        const firstAddress = this.safeValue(data, 0);
        if (firstAddress === undefined) {
            throw new errors.InvalidAddress(this.id + ' fetchDepositAddress() returned an empty response');
        }
        return this.parseDepositAddress(firstAddress, currency);
    }
    /**
     * @method
     * @name probit#fetchDepositAddresses
     * @see https://docs-en.probit.com/reference/deposit_address
     * @description fetch deposit addresses for multiple currencies and chain types
     * @param {string[]|undefined} codes list of unified currency codes, default is undefined
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} a list of [address structures]{@link https://docs.ccxt.com/#/?id=address-structure}
     */
    async fetchDepositAddresses(codes = undefined, params = {}) {
        await this.loadMarkets();
        const request = {};
        if (codes) {
            const currencyIds = [];
            for (let i = 0; i < codes.length; i++) {
                const currency = this.currency(codes[i]);
                currencyIds.push(currency['id']);
            }
            request['currency_id'] = codes.join(',');
        }
        const response = await this.privateGetDepositAddress(this.extend(request, params));
        const data = this.safeList(response, 'data', []);
        return this.parseDepositAddresses(data, codes);
    }
    /**
     * @method
     * @name probit#withdraw
     * @see https://docs-en.probit.com/reference/withdrawal
     * @description make a withdrawal
     * @param {string} code unified currency code
     * @param {float} amount the amount to withdraw
     * @param {string} address the address to withdraw to
     * @param {string} tag
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
     */
    async withdraw(code, amount, address, tag = undefined, params = {}) {
        [tag, params] = this.handleWithdrawTagAndParams(tag, params);
        // In order to use this 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
        this.checkAddress(address);
        await this.loadMarkets();
        const currency = this.currency(code);
        if (tag === undefined) {
            tag = '';
        }
        const request = {
            'currency_id': currency['id'],
            // 'platform_id': 'ETH', // if omitted it will use the default platform for the currency
            'address': address,
            'destination_tag': tag,
            'amount': this.numberToString(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
        };
        const networks = this.safeValue(this.options, 'networks', {});
        let network = this.safeStringUpper(params, 'network'); // this line allows the user to specify either ERC20 or ETH
        network = this.safeString(networks, network, network); // handle ERC20>ETH alias
        if (network !== undefined) {
            request['platform_id'] = network;
            params = this.omit(params, 'network');
        }
        const response = await this.privatePostWithdrawal(this.extend(request, params));
        const data = this.safeDict(response, 'data');
        return this.parseTransaction(data, currency);
    }
    /**
     * @method
     * @name probit#fetchDeposits
     * @description fetch all deposits made to an account
     * @param {string} 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 {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
     */
    async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) {
        const request = {
            'type': 'deposit',
        };
        const result = await this.fetchTransactions(code, since, limit, this.extend(request, params));
        return result;
    }
    /**
     * @method
     * @name probit#fetchWithdrawals
     * @description fetch all withdrawals made to an account
     * @param {string} 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 {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
     */
    async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
        const request = {
            'type': 'withdrawal',
        };
        const result = await this.fetchTransactions(code, since, limit, this.extend(request, params));
        return result;
    }
    /**
     * @method
     * @name probit#fetchDepositsWithdrawals
     * @description fetch history of deposits and withdrawals
     * @see https://docs-en.probit.com/reference/transferpayment
     * @param {string} 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 {object} [params] extra parameters specific to the exchange API endpoint
     * @param {int} [params.until] the latest time in ms to fetch transactions for
     * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
     */
    async fetchDepositsWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        let currency = undefined;
        const request = {};
        if (code !== undefined) {
            currency = this.currency(code);
            request['currency_id'] = currency['id'];
        }
        if (since !== undefined) {
            request['start_time'] = this.iso8601(since);
        }
        else {
            request['start_time'] = this.iso8601(1);
        }
        const until = this.safeInteger(params, 'until');
        if (until !== undefined) {
            request['end_time'] = this.iso8601(until);
            params = this.omit(params, ['until']);
        }
        else {
            request['end_time'] = this.iso8601(this.milliseconds());
        }
        if (limit !== undefined) {
            request['limit'] = limit;
        }
        else {
            request['limit'] = 100;
        }
        const response = await this.privateGetTransferPayment(this.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
        //             }
        //         ]
        //     }
        //
        const data = this.safeList(response, 'data', []);
        return this.parseTransactions(data, currency, since, limit);
    }
    parseTransaction(transaction, currency = undefined) {
        //
        //     {
        //         "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
        //     }
        //
        const id = this.safeString(transaction, 'id');
        const networkId = this.safeString(transaction, 'platform_id');
        const networkCode = this.networkIdToCode(networkId);
        const amount = this.safeNumber(transaction, 'amount');
        const address = this.safeString(transaction, 'address');
        const tag = this.safeString(transaction, 'destination_tag');
        const txid = this.safeString(transaction, 'hash');
        const timestamp = this.parse8601(this.safeString(transaction, 'time'));
        const type = this.safeString(transaction, 'type');
        const currencyId = this.safeString(transaction, 'currency_id');
        const code = this.safeCurrencyCode(currencyId);
        const status = this.parseTransactionStatus(this.safeString(transaction, 'status'));
        const feeCostString = this.safeString(transaction, 'fee');
        let fee = undefined;
        if (feeCostString !== undefined && feeCostString !== '0') {
            fee = {
                'currency': code,
                'cost': this.parseNumber(feeCostString),
            };
        }
        return {
            'id': id,
            'currency': code,
            'amount': amount,
            'network': networkCode,
            'addressFrom': undefined,
            'address': address,
            'addressTo': address,
            'tagFrom': undefined,
            'tag': tag,
            'tagTo': tag,
            'status': status,
            'type': type,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': this.iso8601(timestamp),
            'updated': undefined,
            'internal': undefined,
            'comment': undefined,
            'fee': fee,
            'info': transaction,
        };
    }
    parseTransactionStatus(status) {
        const statuses = {
            'requested': 'pending',
            'pending': 'pending',
            'confirming': 'pending',
            'confirmed': 'pending',
            'applying': 'pending',
            'done': 'ok',
            'cancelled': 'canceled',
            'cancelling': 'canceled',
        };
        return this.safeString(statuses, status, status);
    }
    /**
     * @method
     * @name probit#fetchDepositWithdrawFees
     * @see https://docs-en.probit.com/reference/currency
     * @description fetch deposit and withdraw fees
     * @param {string[]|undefined} codes list of unified currency codes
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object[]} a list of [fees structures]{@link https://docs.ccxt.com/#/?id=fee-structure}
     */
    async fetchDepositWithdrawFees(codes = undefined, params = {}) {
        await this.loadMarkets();
        const response = await this.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"
        //       },
        //     ]
        //  }
        //
        const data = this.safeList(response, 'data');
        return this.parseDepositWithdrawFees(data, codes, 'id');
    }
    parseDepositWithdrawFee(fee, currency = undefined) {
        //
        // {
        //     "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"
        //   }
        //
        const depositWithdrawFee = this.depositWithdrawFee({});
        const platforms = this.safeValue(fee, 'platform', []);
        const depositResult = {
            'fee': undefined,
            'percentage': undefined,
        };
        for (let i = 0; i < platforms.length; i++) {
            const network = platforms[i];
            const networkId = this.safeString(network, 'id');
            const networkCode = this.networkIdToCode(networkId, currency['code']);
            const withdrawalFees = this.safeValue(network, 'withdrawal_fee', {});
            const withdrawFee = this.safeNumber(withdrawalFees[0], 'amount');
            if (withdrawalFees.length) {
                const withdrawResult = {
                    'fee': withdrawFee,
                    'percentage': (withdrawFee !== undefined) ? false : undefined,
                };
                if (i === 0) {
                    depositWithdrawFee['withdraw'] = withdrawResult;
                }
                depositWithdrawFee['networks'][networkCode] = {
                    'withdraw': withdrawResult,
                    'deposit': depositResult,
                };
            }
        }
        depositWithdrawFee['info'] = fee;
        return depositWithdrawFee;
    }
    nonce() {
        return this.milliseconds();
    }
    sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'][api] + '/';
        const query = this.omit(params, this.extractParams(path));
        if (api === 'accounts') {
            this.checkRequiredCredentials();
            url += this.implodeParams(path, params);
            const auth = this.apiKey + ':' + this.secret;
            const auth64 = this.stringToBase64(auth);
            headers = {
                'Authorization': 'Basic ' + auth64,
                'Content-Type': 'application/json',
            };
            if (Object.keys(query).length) {
                body = this.json(query);
            }
        }
        else {
            url += this.version + '/';
            if (api === 'public') {
                url += this.implodeParams(path, params);
                if (Object.keys(query).length) {
                    url += '?' + this.urlencode(query);
                }
            }
            else if (api === 'private') {
                const now = this.milliseconds();
                this.checkRequiredCredentials();
                const expires = this.safeInteger(this.options, 'expires');
                if ((expires === undefined) || (expires < now)) {
                    throw new errors.AuthenticationError(this.id + ' access token expired, call signIn() method');
                }
                const accessToken = this.safeString(this.options, 'accessToken');
                headers = {
                    'Authorization': 'Bearer ' + accessToken,
                };
                url += this.implodeParams(path, params);
                if (method === 'GET') {
                    if (Object.keys(query).length) {
                        url += '?' + this.urlencode(query);
                    }
                }
                else if (Object.keys(query).length) {
                    body = this.json(query);
                    headers['Content-Type'] = 'application/json';
                }
            }
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }
    /**
     * @method
     * @name probit#signIn
     * @see https://docs-en.probit.com/reference/token
     * @description sign in, must be called prior to using other authenticated methods
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns response from exchange
     */
    async signIn(params = {}) {
        this.checkRequiredCredentials();
        const request = {
            'grant_type': 'client_credentials', // the only supported value
        };
        const response = await this.accountsPostToken(this.extend(request, params));
        //
        //     {
        //         "access_token": "0ttDv/2hTTn3bLi8GP1gKaneiEQ6+0hOBenPrxNQt2s=",
        //         "token_type": "bearer",
        //         "expires_in": 900
        //     }
        //
        const expiresIn = this.safeInteger(response, 'expires_in');
        const accessToken = this.safeString(response, 'access_token');
        this.options['accessToken'] = accessToken;
        this.options['expires'] = this.sum(this.milliseconds(), expiresIn * 1000);
        return response;
    }
    handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
        if (response === undefined) {
            return undefined; // fallback to default error handler
        }
        if ('errorCode' in response) {
            const errorCode = this.safeString(response, 'errorCode');
            if (errorCode !== undefined) {
                const errMessage = this.safeString(response, 'message', '');
                const details = this.safeValue(response, 'details');
                const feedback = this.id + ' ' + errorCode + ' ' + errMessage + ' ' + this.json(details);
                if ('exact' in this.exceptions) {
                    this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback);
                }
                if ('broad' in this.exceptions) {
                    this.throwBroadlyMatchedException(this.exceptions['broad'], errMessage, feedback);
                }
                throw new errors.ExchangeError(feedback);
            }
        }
        return undefined;
    }
}

module.exports = probit;