<?php

namespace ccxt\async;

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

use Exception; // a common import
use \ccxt\ArgumentsRequired;

class hollaex extends Exchange {

    public function describe() {
        return $this->deep_extend(parent::describe (), array(
            'id' => 'hollaex',
            'name' => 'HollaEx',
            'countries' => array( 'KR' ),
            'rateLimit' => 333,
            'version' => 'v1',
            'has' => array(
                'CORS' => false,
                'fetchMarkets' => true,
                'fetchCurrencies' => true,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchOrderBook' => true,
                'fetchOrderBooks' => true,
                'fetchTrades' => true,
                'fetchOHLCV' => true,
                'fetchBalance' => true,
                'createOrder' => true,
                'createLimitBuyOrder' => true,
                'createLimitSellOrder' => true,
                'createMarketBuyOrder' => true,
                'createMarketSellOrder' => true,
                'cancelOrder' => true,
                'cancelAllOrders' => true,
                'fetchOpenOrders' => true,
                'fetchClosedOrders' => false,
                'fetchOpenOrder' => true,
                'fetchOrder' => false,
                'fetchDeposits' => true,
                'fetchWithdrawals' => true,
                'fetchTransactions' => false,
                'fetchOrders' => false,
                'fetchMyTrades' => true,
                'withdraw' => true,
                'fetchDepositAddress' => true,
            ),
            'timeframes' => array(
                '1h' => '1h',
                '1d' => '1d',
            ),
            'urls' => array(
                'logo' => 'https://user-images.githubusercontent.com/1294454/75841031-ca375180-5ddd-11ea-8417-b975674c23cb.jpg',
                'api' => 'https://api.hollaex.com',
                'www' => 'https://hollaex.com',
                'doc' => 'https://apidocs.hollaex.com',
                'referral' => 'https://pro.hollaex.com/signup?affiliation_code=QSWA6G',
            ),
            'precisionMode' => TICK_SIZE,
            'requiredCredentials' => array(
                'apiKey' => true,
                'secret' => true,
            ),
            'api' => array(
                'public' => array(
                    'get' => array(
                        'health',
                        'constant',
                        'ticker',
                        'ticker/all',
                        'orderbooks',
                        'trades',
                        'chart',
                        // TradingView data
                        'udf/config',
                        'udf/history',
                        'udf/symbols',
                    ),
                ),
                'private' => array(
                    'get' => array(
                        'user',
                        'user/balance',
                        'user/trades',
                        'user/orders',
                        'user/orders/{order_id}',
                        'user/deposits',
                        'user/withdrawals',
                        'user/withdraw/{currency}/fee',
                    ),
                    'post' => array(
                        'user/request-withdrawal',
                        'order',
                    ),
                    'delete' => array(
                        'user/orders',
                        'user/orders/{order_id}',
                    ),
                ),
            ),
            'fees' => array(
                'trading' => array(
                    'tierBased' => true,
                    'percentage' => true,
                ),
            ),
            'exceptions' => array(
                'broad' => array(
                    'Invalid token' => '\\ccxt\\AuthenticationError',
                    'Order not found' => '\\ccxt\\OrderNotFound',
                    'Insufficient balance' => '\\ccxt\\InsufficientFunds',
                ),
                'exact' => array(
                    '400' => '\\ccxt\\BadRequest',
                    '403' => '\\ccxt\\AuthenticationError',
                    '404' => '\\ccxt\\BadRequest',
                    '405' => '\\ccxt\\BadRequest',
                    '410' => '\\ccxt\\BadRequest',
                    '429' => '\\ccxt\\BadRequest',
                    '500' => '\\ccxt\\NetworkError',
                    '503' => '\\ccxt\\NetworkError',
                ),
            ),
            'options' => array(
                // how many seconds before the authenticated request expires
                'api-expires' => intval($this->timeout / 1000),
            ),
        ));
    }

    public function fetch_markets($params = array ()) {
        $response = yield $this->publicGetConstant ($params);
        //
        //     {
        //         coins => array(
        //             xmr => array(
        //                 $id => 7,
        //                 fullname => "Monero",
        //                 $symbol => "xmr",
        //                 $active => true,
        //                 allow_deposit => true,
        //                 allow_withdrawal => true,
        //                 withdrawal_fee => 0.02,
        //                 min => 0.001,
        //                 max => 100000,
        //                 increment_unit => 0.001,
        //                 deposit_limits => array( '1' => 0, '2' => 0, '3' => 0, '4' => 0, '5' => 0, '6' => 0 ),
        //                 withdrawal_limits => array( '1' => 10, '2' => 15, '3' => 100, '4' => 100, '5' => 200, '6' => 300, '7' => 350, '8' => 400, '9' => 500, '10' => -1 ),
        //                 created_at => "2019-12-09T07:14:02.720Z",
        //                 updated_at => "2020-01-16T12:12:53.162Z"
        //             ),
        //             // ...
        //         ),
        //         $pairs => array(
        //             'btc-usdt' => array(
        //                 $id => 2,
        //                 name => "btc-usdt",
        //                 pair_base => "btc",
        //                 pair_2 => "usdt",
        //                 taker_fees => array( '1' => 0.3, '2' => 0.25, '3' => 0.2, '4' => 0.18, '5' => 0.1, '6' => 0.09, '7' => 0.08, '8' => 0.06, '9' => 0.04, '10' => 0 ),
        //                 maker_fees => array( '1' => 0.1, '2' => 0.08, '3' => 0.05, '4' => 0.03, '5' => 0, '6' => 0, '7' => 0, '8' => 0, '9' => 0, '10' => 0 ),
        //                 min_size => 0.0001,
        //                 max_size => 1000,
        //                 min_price => 100,
        //                 max_price => 100000,
        //                 increment_size => 0.0001,
        //                 increment_price => 0.05,
        //                 $active => true,
        //                 created_at => "2019-12-09T07:15:54.537Z",
        //                 updated_at => "2019-12-09T07:15:54.537Z"
        //             ),
        //         ),
        //         config => array( tiers => 10 ),
        //         status => true
        //     }
        //
        $pairs = $this->safe_value($response, 'pairs', array());
        $keys = is_array($pairs) ? array_keys($pairs) : array();
        $result = array();
        for ($i = 0; $i < count($keys); $i++) {
            $key = $keys[$i];
            $market = $pairs[$key];
            $id = $this->safe_string($market, 'name');
            $baseId = $this->safe_string($market, 'pair_base');
            $quoteId = $this->safe_string($market, 'pair_2');
            $base = $this->common_currency_code(strtoupper($baseId));
            $quote = $this->common_currency_code(strtoupper($quoteId));
            $symbol = $base . '/' . $quote;
            $active = $this->safe_value($market, 'active');
            $result[] = array(
                'id' => $id,
                'symbol' => $symbol,
                'base' => $base,
                'quote' => $quote,
                'baseId' => $baseId,
                'quoteId' => $quoteId,
                'active' => $active,
                'precision' => array(
                    'price' => $this->safe_float($market, 'increment_price'),
                    'amount' => $this->safe_float($market, 'increment_size'),
                ),
                'limits' => array(
                    'amount' => array(
                        'min' => $this->safe_float($market, 'min_size'),
                        'max' => $this->safe_float($market, 'max_size'),
                    ),
                    'price' => array(
                        'min' => $this->safe_float($market, 'min_price'),
                        'max' => $this->safe_float($market, 'max_price'),
                    ),
                    'cost' => array( 'min' => null, 'max' => null ),
                ),
                'info' => $market,
            );
        }
        return $result;
    }

    public function fetch_currencies($params = array ()) {
        $response = yield $this->publicGetConstant ($params);
        $coins = $this->safe_value($response, 'coins', array());
        $keys = is_array($coins) ? array_keys($coins) : array();
        $result = array();
        for ($i = 0; $i < count($keys); $i++) {
            $key = $keys[$i];
            $currency = $coins[$key];
            $id = $this->safe_string($currency, 'symbol');
            $numericId = $this->safe_integer($currency, 'id');
            $code = $this->safe_currency_code($id);
            $name = $this->safe_string($currency, 'fullname');
            $active = $this->safe_value($currency, 'active');
            $fee = $this->safe_float($currency, 'withdrawal_fee');
            $precision = $this->safe_float($currency, 'increment_unit');
            $withdrawalLimits = $this->safe_value($currency, 'withdrawal_limits', array());
            $result[$code] = array(
                'id' => $id,
                'numericId' => $numericId,
                'code' => $code,
                'info' => $currency,
                'name' => $name,
                'active' => $active,
                'fee' => $fee,
                'precision' => $precision,
                'limits' => array(
                    'amount' => array(
                        'min' => $this->safe_float($currency, 'min'),
                        'max' => $this->safe_float($currency, 'max'),
                    ),
                    'price' => array(
                        'min' => null,
                        'max' => null,
                    ),
                    'cost' => array(
                        'min' => null,
                        'max' => null,
                    ),
                    'withdraw' => array(
                        'min' => null,
                        'max' => $this->safe_value($withdrawalLimits, 0),
                    ),
                ),
            );
        }
        return $result;
    }

    public function fetch_order_books($symbols = null, $limit = null, $params = array ()) {
        yield $this->load_markets();
        $response = yield $this->publicGetOrderbooks ($params);
        $result = array();
        $marketIds = is_array($response) ? array_keys($response) : array();
        for ($i = 0; $i < count($marketIds); $i++) {
            $marketId = $marketIds[$i];
            $orderbook = $response[$marketId];
            $symbol = $this->safe_symbol($marketId, null, '-');
            $timestamp = $this->parse8601($this->safe_string($orderbook, 'timestamp'));
            $result[$symbol] = $this->parse_order_book($response[$marketId], $timestamp);
        }
        return $result;
    }

    public function fetch_order_book($symbol, $limit = null, $params = array ()) {
        yield $this->load_markets();
        $marketId = $this->market_id($symbol);
        $request = array(
            'symbol' => $marketId,
        );
        $response = yield $this->publicGetOrderbooks (array_merge($request, $params));
        //
        //     {
        //         "btc-usdt" => array(
        //             "bids" => array(
        //                 array( 8836.4, 1.022 ),
        //                 array( 8800, 0.0668 ),
        //                 array( 8797.75, 0.2398 ),
        //             ),
        //             "asks" => array(
        //                 array( 8839.35, 1.5334 ),
        //                 array( 8852.6, 0.0579 ),
        //                 array( 8860.45, 0.1815 ),
        //             ),
        //             "$timestamp" => "2020-03-03T02:27:25.147Z"
        //         ),
        //         "eth-usdt" => array(),
        //         // ...
        //     }
        //
        $orderbook = $this->safe_value($response, $marketId);
        $timestamp = $this->parse8601($this->safe_string($orderbook, 'timestamp'));
        return $this->parse_order_book($orderbook, $timestamp);
    }

    public function fetch_ticker($symbol, $params = array ()) {
        yield $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = yield $this->publicGetTicker (array_merge($request, $params));
        //
        //     {
        //         open => 8615.55,
        //         close => 8841.05,
        //         high => 8921.1,
        //         low => 8607,
        //         last => 8841.05,
        //         volume => 20.2802,
        //         timestamp => '2020-03-03T03:11:18.964Z'
        //     }
        //
        return $this->parse_ticker($response, $market);
    }

    public function fetch_tickers($symbols = null, $params = array ()) {
        yield $this->load_markets();
        $response = yield $this->publicGetTickerAll (array_merge($params));
        //
        //     {
        //         "bch-usdt" => array(
        //             "time" => "2020-03-02T04:29:45.011Z",
        //             "open" => 341.65,
        //             "close":337.9,
        //             "high":341.65,
        //             "low":337.3,
        //             "last":337.9,
        //             "volume":0.054,
        //             "symbol":"bch-usdt"
        //         ),
        //         // ...
        //     }
        //
        return $this->parse_tickers($response, $symbols);
    }

    public function parse_tickers($response, $symbols = null) {
        $result = array();
        $keys = is_array($response) ? array_keys($response) : array();
        for ($i = 0; $i < count($keys); $i++) {
            $key = $keys[$i];
            $ticker = $response[$key];
            $marketId = $this->safe_string($ticker, 'symbol', $key);
            $market = $this->safe_market($marketId, null, '-');
            $symbol = $market['symbol'];
            $result[$symbol] = $this->parse_ticker($ticker, $market);
        }
        return $this->filter_by_array($result, 'symbol', $symbols);
    }

    public function parse_ticker($ticker, $market = null) {
        //
        // fetchTicker
        //
        //     {
        //         open => 8615.55,
        //         $close => 8841.05,
        //         high => 8921.1,
        //         low => 8607,
        //         last => 8841.05,
        //         volume => 20.2802,
        //         $timestamp => '2020-03-03T03:11:18.964Z',
        //     }
        //
        // fetchTickers
        //
        //     {
        //         "time" => "2020-03-02T04:29:45.011Z",
        //         "open" => 341.65,
        //         "$close" => 337.9,
        //         "high" => 341.65,
        //         "low" => 337.3,
        //         "last" => 337.9,
        //         "volume" => 0.054,
        //         "$symbol" => "bch-usdt"
        //     }
        //
        $marketId = $this->safe_string($ticker, 'symbol');
        $symbol = $this->safe_symbol($marketId, $market, '-');
        $timestamp = $this->parse8601($this->safe_string_2($ticker, 'time', 'timestamp'));
        $close = $this->safe_float($ticker, 'close');
        $result = array(
            'symbol' => $symbol,
            'info' => $ticker,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'high' => $this->safe_float($ticker, 'high'),
            'low' => $this->safe_float($ticker, 'low'),
            'bid' => null,
            'bidVolume' => null,
            'ask' => null,
            'askVolume' => null,
            'vwap' => null,
            'open' => $this->safe_float($ticker, 'open'),
            'close' => $close,
            'last' => $this->safe_float($ticker, 'last', $close),
            'previousClose' => null,
            'change' => null,
            'percentage' => null,
            'average' => null,
            'baseVolume' => $this->safe_float($ticker, 'volume'),
            'quoteVolume' => null,
        );
        return $result;
    }

    public function fetch_trades($symbol, $since = null, $limit = null, $params = array ()) {
        yield $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = yield $this->publicGetTrades (array_merge($request, $params));
        //
        //     {
        //         "btc-usdt" => array(
        //             array(
        //                 "size" => 0.5,
        //                 "price" => 8830,
        //                 "side" => "buy",
        //                 "timestamp" => "2020-03-03T04:44:33.034Z"
        //             ),
        //             // ...
        //         )
        //     }
        //
        $trades = $this->safe_value($response, $market['id'], array());
        return $this->parse_trades($trades, $market, $since, $limit);
    }

    public function parse_trade($trade, $market = null) {
        //
        // fetchTrades (public)
        //
        //     {
        //         "size" => 0.5,
        //         "$price" => 8830,
        //         "$side" => "buy",
        //         "$timestamp" => "2020-03-03T04:44:33.034Z"
        //     }
        //
        // fetchMyTrades (private)
        //
        //     {
        //         "$side" => "buy",
        //         "$symbol" => "eth-usdt",
        //         "size" => 0.086,
        //         "$price" => 226.19,
        //         "$timestamp" => "2020-03-03T08:03:55.459Z",
        //         "$fee" => 0.1
        //     }
        //
        $marketId = $this->safe_string($trade, 'symbol');
        $market = $this->safe_market($marketId, $market, '-');
        $symbol = $market['symbol'];
        $datetime = $this->safe_string($trade, 'timestamp');
        $timestamp = $this->parse8601($datetime);
        $side = $this->safe_string($trade, 'side');
        $price = $this->safe_float($trade, 'price');
        $amount = $this->safe_float($trade, 'size');
        $cost = null;
        if ($price !== null) {
            if ($amount !== null) {
                $cost = $price * $amount;
            }
        }
        $feeCost = $this->safe_float($trade, 'fee');
        $fee = null;
        if ($feeCost !== null) {
            $quote = $market['quote'];
            $feeCurrencyCode = ($market !== null) ? $market['quote'] : $quote;
            $fee = array(
                'cost' => $feeCost,
                'currency' => $feeCurrencyCode,
            );
        }
        return array(
            'info' => $trade,
            'id' => null,
            'timestamp' => $timestamp,
            'datetime' => $datetime,
            'symbol' => $symbol,
            'order' => null,
            'type' => null,
            'side' => $side,
            'takerOrMaker' => null,
            'price' => $price,
            'amount' => $amount,
            'cost' => $cost,
            'fee' => $fee,
        );
    }

    public function fetch_ohlcv($symbol, $timeframe = '1h', $since = null, $limit = null, $params = array ()) {
        yield $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
            'resolution' => $this->timeframes[$timeframe],
        );
        $duration = $this->parse_timeframe($timeframe);
        if ($since === null) {
            if ($limit === null) {
                throw new ArgumentsRequired($this->id . " fetchOHLCV() requires a 'since' or a 'limit' argument");
            } else {
                $end = $this->seconds();
                $start = $end - $duration * $limit;
                $request['to'] = $end;
                $request['from'] = $start;
            }
        } else {
            if ($limit === null) {
                $request['from'] = intval($since / 1000);
                $request['to'] = $this->seconds();
            } else {
                $start = intval($since / 1000);
                $request['from'] = $start;
                $request['to'] = $this->sum($start, $duration * $limit);
            }
        }
        $response = yield $this->publicGetChart (array_merge($request, $params));
        //
        //     array(
        //         array(
        //             "time":"2020-03-02T20:00:00.000Z",
        //             "close":8872.1,
        //             "high":8872.1,
        //             "low":8858.6,
        //             "open":8858.6,
        //             "$symbol":"btc-usdt",
        //             "volume":1.2922
        //         ),
        //     )
        //
        return $this->parse_ohlcvs($response, $market, $timeframe, $since, $limit);
    }

    public function parse_ohlcv($response, $market = null, $timeframe = '1h', $since = null, $limit = null) {
        //
        //     {
        //         "time":"2020-03-02T20:00:00.000Z",
        //         "close":8872.1,
        //         "high":8872.1,
        //         "low":8858.6,
        //         "open":8858.6,
        //         "symbol":"btc-usdt",
        //         "volume":1.2922
        //     }
        //
        return array(
            $this->parse8601($this->safe_string($response, 'time')),
            $this->safe_float($response, 'open'),
            $this->safe_float($response, 'high'),
            $this->safe_float($response, 'low'),
            $this->safe_float($response, 'close'),
            $this->safe_float($response, 'volume'),
        );
    }

    public function fetch_balance($params = array ()) {
        yield $this->load_markets();
        $response = yield $this->privateGetUserBalance ($params);
        //
        //     {
        //         "updated_at" => "2020-03-02T22:27:38.428Z",
        //         "btc_balance" => 0,
        //         "btc_pending" => 0,
        //         "btc_available" => 0,
        //         "eth_balance" => 0,
        //         "eth_pending" => 0,
        //         "eth_available" => 0,
        //         // ...
        //     }
        //
        $result = array( 'info' => $response );
        $currencyIds = is_array($this->currencies_by_id) ? array_keys($this->currencies_by_id) : array();
        for ($i = 0; $i < count($currencyIds); $i++) {
            $currencyId = $currencyIds[$i];
            $code = $this->safe_currency_code($currencyId);
            $account = $this->account();
            $account['free'] = $this->safe_float($response, $currencyId . '_available');
            $account['total'] = $this->safe_float($response, $currencyId . '_balance');
            $result[$code] = $account;
        }
        return $this->parse_balance($result);
    }

    public function fetch_open_order($id, $symbol = null, $params = array ()) {
        yield $this->load_markets();
        $request = array(
            'order_id' => $id,
        );
        $response = yield $this->privateGetUserOrdersOrderId (array_merge($request, $params));
        //
        //     {
        //         "created_at" => "2018-03-23T04:14:08.663Z",
        //         "title" => "string",
        //         "side" => "sell",
        //         "type" => "limit",
        //         "price" => 0,
        //         "size" => 0,
        //         "$symbol" => "xht-usdt",
        //         "$id" => "string",
        //         "created_by" => 1,
        //         "filled" => 0
        //     }
        //
        return $this->parse_order($response);
    }

    public function fetch_open_orders($symbol = null, $since = null, $limit = null, $params = array ()) {
        yield $this->load_markets();
        $market = null;
        $request = array();
        if ($symbol !== null) {
            $market = $this->market($symbol);
            $request['symbol'] = $market['id'];
        }
        $response = yield $this->privateGetUserOrders (array_merge($request, $params));
        //
        //     array(
        //         {
        //             "created_at":"2020-03-03T08:02:18.639Z",
        //             "title":"5419ff3f-9d25-4af7-bcc2-803926518d76",
        //             "side":"buy",
        //             "type":"$limit",
        //             "price":226.19,
        //             "size":0.086,
        //             "$symbol":"eth-usdt",
        //             "id":"5419ff3f-9d25-4af7-bcc2-803926518d76",
        //             "created_by":620,
        //             "filled":0
        //         }
        //     )
        //
        return $this->parse_orders($response, $market);
    }

    public function parse_order($order, $market = null) {
        //
        // fetchOpenOrder, fetchOpenOrders
        //
        //     {
        //         "created_at":"2020-03-03T08:02:18.639Z",
        //         "title":"5419ff3f-9d25-4af7-bcc2-803926518d76",
        //         "$side":"buy",
        //         "$type":"limit",
        //         "$price":226.19,
        //         "size":0.086,
        //         "$symbol":"eth-usdt",
        //         "$id":"5419ff3f-9d25-4af7-bcc2-803926518d76",
        //         "created_by":620,
        //         "$filled":0
        //     }
        //
        $marketId = $this->safe_string($order, 'symbol');
        $symbol = $this->safe_symbol($marketId, $market, '-');
        $id = $this->safe_string($order, 'id');
        $timestamp = $this->parse8601($this->safe_string($order, 'created_at'));
        $type = $this->safe_string($order, 'type');
        $side = $this->safe_string($order, 'side');
        $price = $this->safe_float($order, 'price');
        $amount = $this->safe_float($order, 'size');
        $filled = $this->safe_float($order, 'filled');
        $cost = null;
        $remaining = null;
        if ($filled !== null) {
            if ($amount !== null) {
                $remaining = $amount - $filled;
            }
            if ($price !== null) {
                $cost = $filled * $price;
            }
        }
        $status = ($type === 'market') ? 'closed' : 'open';
        $result = array(
            'id' => $id,
            'clientOrderId' => null,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => null,
            'status' => $status,
            'symbol' => $symbol,
            'type' => $type,
            'timeInForce' => null,
            'postOnly' => null,
            'side' => $side,
            'price' => $price,
            'stopPrice' => null,
            'amount' => $amount,
            'filled' => $filled,
            'remaining' => $remaining,
            'cost' => $cost,
            'trades' => null,
            'fee' => null,
            'info' => $order,
            'average' => null,
        );
        return $result;
    }

    public function create_order($symbol, $type, $side, $amount, $price = null, $params = array ()) {
        yield $this->load_markets();
        $market = $this->market($symbol);
        $order = array(
            'symbol' => $market['id'],
            'side' => $side,
            'size' => $amount,
            'type' => $type,
        );
        if ($type !== 'market') {
            $order['price'] = $price;
        }
        $response = yield $this->privatePostOrder (array_merge($order, $params));
        //
        //     {
        //         "$symbol" => "xht-usdt",
        //         "$side" => "sell",
        //         "size" => 1,
        //         "$type" => "limit",
        //         "$price" => 0.1,
        //         "id" => "string",
        //         "created_by" => 34,
        //         "filled" => 0,
        //         "status" => "pending"
        //     }
        //
        return $this->parse_order($response, $market);
    }

    public function cancel_order($id, $symbol = null, $params = array ()) {
        yield $this->load_markets();
        $request = array(
            'order_id' => $id,
        );
        $response = yield $this->privateDeleteUserOrdersOrderId (array_merge($request, $params));
        //
        //     {
        //         "title" => "string",
        //         "$symbol" => "xht-usdt",
        //         "side" => "sell",
        //         "size" => 1,
        //         "type" => "limit",
        //         "price" => 0.1,
        //         "$id" => "string",
        //         "created_by" => 34,
        //         "filled" => 0
        //     }
        //
        return $this->parse_order($response);
    }

    public function cancel_all_orders($symbol = null, $params = array ()) {
        yield $this->load_markets();
        $request = array();
        $market = null;
        if ($symbol !== null) {
            $market = $this->markets ($symbol);
            $request['symbol'] = $market['id'];
        }
        $response = yield $this->privateDeleteUserOrders (array_merge($request, $params));
        //
        //     array(
        //         {
        //             "title" => "string",
        //             "$symbol" => "xht-usdt",
        //             "side" => "sell",
        //             "size" => 1,
        //             "type" => "limit",
        //             "price" => 0.1,
        //             "id" => "string",
        //             "created_by" => 34,
        //             "filled" => 0
        //         }
        //     )
        //
        return $this->parse_orders($response, $market);
    }

    public function fetch_my_trades($symbol = null, $since = null, $limit = null, $params = array ()) {
        yield $this->load_markets();
        $request = array(
            // 'symbol' => $market['id'],
            // 'limit' => 50, // default 50, max 100
            // 'page' => 1, // page of $data to retrieve
            // 'order_by' => 'timestamp', // field to order $data
            // 'order' => 'asc', // asc or desc
            // 'start_date' => 123, // starting date of queried $data
            // 'end_date' => 321, // ending date of queried $data
        );
        $market = null;
        if ($symbol !== null) {
            $market = $this->market($symbol);
            $request['symbol'] = $market['id'];
        }
        if ($limit !== null) {
            $request['limit'] = $limit; // default 50, max 100
        }
        if ($since !== null) {
            $request['start_date'] = $this->iso8601($since);
        }
        $response = yield $this->privateGetUserTrades (array_merge($request, $params));
        //
        //     {
        //         "count" => 1,
        //         "$data" => array(
        //             {
        //                 "side" => "buy",
        //                 "$symbol" => "eth-usdt",
        //                 "size" => 0.086,
        //                 "price" => 226.19,
        //                 "timestamp" => "2020-03-03T08:03:55.459Z",
        //                 "fee" => 0.1
        //             }
        //         )
        //     }
        //
        $data = $this->safe_value($response, 'data', array());
        return $this->parse_trades($data, $market, $since, $limit);
    }

    public function fetch_deposit_address($code, $params = array ()) {
        yield $this->load_markets();
        $currency = $this->currency($code);
        $response = yield $this->privateGetUser ($params);
        //
        //     {
        //         "id" => 620,
        //         "email" => "email@gmail.com",
        //         "full_name" => "",
        //         "name_verified" => false,
        //         "gender" => false,
        //         "nationality" => "",
        //         "phone_number" => "",
        //         "$address" => array( "city" => "", "$address" => "", "country" => "", "postal_code" => "" ),
        //         "id_data" => array( "note" => "", "type" => "", "number" => "", "status" => 0 ),
        //         "bank_account":array(),
        //         "crypto_wallet":array(
        //             "xrp" => "rJtoECs6rPkJoAfgtR8SDDshV6hRHe3X7y:391496555"
        //             "usdt":"0x1fb4248e167901dfa0d8cdda2243a2126d7ce48d"
        //             // ...
        //         ),
        //         "verification_level" => 1,
        //         "otp_enabled" => true,
        //         "activated" => true,
        //         "note" => "",
        //         "username" => "user",
        //         "affiliation_code" => "QSWA6G",
        //         "settings" => {
        //             "chat" => array( "set_username" => false ),
        //             "risk" => array( "order_portfolio_percentage" => 20 ),
        //             "audio" => array(
        //                 "public_trade" => false,
        //                 "order_completed" => true,
        //                 "order_partially_completed" => true
        //             ),
        //             "language" => "en",
        //             "interface" => array( "theme" => "white","order_book_levels" => 10 ),
        //             "notification" => array(
        //                 "popup_order_completed" => true,
        //                 "popup_order_confirmation" => true,
        //                 "popup_order_partially_filled" => true
        //             }
        //         ),
        //         "flagged" => false,
        //         "is_hap" => false,
        //         "pin" => false,
        //         "discount" => 0,
        //         "created_at" => "2020-03-02T22:27:38.331Z",
        //         "updated_at" => "2020-03-03T07:54:58.315Z",
        //         "balance" => array(
        //             "xht_balance" => 0,
        //             "xht_pending" => 0,
        //             "xht_available" => 0,
        //             // ...
        //             "updated_at" => "2020-03-03T10:21:05.430Z"
        //         ),
        //         "images" => array(),
        //         "fees" => {
        //             "btc-usdt" => array( "maker_fee" => 0.1, "taker_fee" => 0.3 ),
        //             "eth-usdt" => array( "maker_fee" => 0.1, "taker_fee" => 0.3 ),
        //             // ...
        //         }
        //     }
        //
        $cryptoWallet = $this->safe_value($response, 'crypto_wallet');
        $address = $this->safe_string($cryptoWallet, $currency['id']);
        $tag = null;
        if ($address !== null) {
            $parts = explode(':', $address);
            $address = $this->safe_string($parts, 0);
            $tag = $this->safe_string($parts, 1);
        }
        $this->check_address($address);
        return array(
            'currency' => $code,
            'address' => $address,
            'tag' => $tag,
            'info' => $response,
        );
    }

    public function fetch_deposits($code = null, $since = null, $limit = null, $params = array ()) {
        yield $this->load_markets();
        $request = array(
            // 'currency' => $currency['id'],
            // 'limit' => 50, // default 50, max 100
            // 'page' => 1, // page of $data to retrieve
            // 'order_by' => 'timestamp', // field to order $data
            // 'order' => 'asc', // asc or desc
            // 'start_date' => 123, // starting date of queried $data
            // 'end_date' => 321, // ending date of queried $data
        );
        $currency = null;
        if ($code !== null) {
            $currency = $this->currency($code);
            $request['currency'] = $currency['id'];
        }
        if ($limit !== null) {
            $request['limit'] = $limit; // default 50, max 100
        }
        if ($since !== null) {
            $request['start_date'] = $this->iso8601($since);
        }
        $response = yield $this->privateGetUserDeposits (array_merge($request, $params));
        //
        //     {
        //         "count" => 1,
        //         "$data" => array(
        //             {
        //                 "id" => 539,
        //                 "amount" => 20,
        //                 "fee" => 0,
        //                 "address" => "0x5c0cc98270d7089408fcbcc8e2131287f5be2306",
        //                 "transaction_id" => "0xd4006327a5ec2c41adbdcf566eaaba6597c3d45906abe78ea1a4a022647c2e28",
        //                 "status" => true,
        //                 "dismissed" => false,
        //                 "rejected" => false,
        //                 "description" => "",
        //                 "type" => "deposit",
        //                 "$currency" => "usdt",
        //                 "created_at" => "2020-03-03T07:56:36.198Z",
        //                 "updated_at" => "2020-03-03T08:00:05.674Z",
        //                 "user_id" => 620
        //             }
        //         )
        //     }
        //
        $data = $this->safe_value($response, 'data', array());
        return $this->parse_transactions($data, $currency, $since, $limit);
    }

    public function fetch_withdrawals($code = null, $since = null, $limit = null, $params = array ()) {
        yield $this->load_markets();
        $request = array(
            // 'currency' => $currency['id'],
            // 'limit' => 50, // default 50, max 100
            // 'page' => 1, // page of $data to retrieve
            // 'order_by' => 'timestamp', // field to order $data
            // 'order' => 'asc', // asc or desc
            // 'start_date' => 123, // starting date of queried $data
            // 'end_date' => 321, // ending date of queried $data
        );
        $currency = null;
        if ($code !== null) {
            $currency = $this->currency($code);
            $request['currency'] = $currency['id'];
        }
        if ($limit !== null) {
            $request['limit'] = $limit; // default 50, max 100
        }
        if ($since !== null) {
            $request['start_date'] = $this->iso8601($since);
        }
        $response = yield $this->privateGetUserWithdrawals (array_merge($request, $params));
        //
        //     {
        //         "count" => 1,
        //         "$data" => array(
        //             {
        //                 "id" => 539,
        //                 "amount" => 20,
        //                 "fee" => 0,
        //                 "address" => "0x5c0cc98270d7089408fcbcc8e2131287f5be2306",
        //                 "transaction_id" => "0xd4006327a5ec2c41adbdcf566eaaba6597c3d45906abe78ea1a4a022647c2e28",
        //                 "status" => true,
        //                 "dismissed" => false,
        //                 "rejected" => false,
        //                 "description" => "",
        //                 "type" => "withdrawal",
        //                 "$currency" => "usdt",
        //                 "created_at" => "2020-03-03T07:56:36.198Z",
        //                 "updated_at" => "2020-03-03T08:00:05.674Z",
        //                 "user_id" => 620
        //             }
        //         )
        //     }
        //
        $data = $this->safe_value($response, 'data', array());
        return $this->parse_transactions($data, $currency, $since, $limit);
    }

    public function parse_transaction($transaction, $currency = null) {
        //
        //     {
        //         "$id" => 539,
        //         "$amount" => 20,
        //         "$fee" => 0,
        //         "$address" => "0x5c0cc98270d7089408fcbcc8e2131287f5be2306",
        //         "transaction_id" => "0xd4006327a5ec2c41adbdcf566eaaba6597c3d45906abe78ea1a4a022647c2e28",
        //         "$status" => true,
        //         "$dismissed" => false,
        //         "$rejected" => false,
        //         "description" => "",
        //         "$type" => "withdrawal",
        //         "$currency" => "usdt",
        //         "created_at" => "2020-03-03T07:56:36.198Z",
        //         "updated_at" => "2020-03-03T08:00:05.674Z",
        //         "user_id" => 620
        //     }
        //
        $id = $this->safe_string($transaction, 'id');
        $txid = $this->safe_string($transaction, 'transaction_id');
        $timestamp = $this->parse8601($this->safe_string($transaction, 'created_at'));
        $updated = $this->parse8601($this->safe_string($transaction, 'updated_at'));
        $type = $this->safe_string($transaction, 'type');
        $amount = $this->safe_float($transaction, 'amount');
        $address = $this->safe_string($transaction, 'address');
        $addressTo = null;
        $addressFrom = null;
        $tag = null;
        $tagTo = null;
        $tagFrom = null;
        if ($address !== null) {
            $parts = explode(':', $address);
            $address = $this->safe_string($parts, 0);
            $tag = $this->safe_string($parts, 1);
            $addressTo = $address;
            $tagTo = $tag;
        }
        $currencyId = $this->safe_string($transaction, 'currency');
        $code = $this->safe_currency_code($currencyId);
        $status = $this->safe_value($transaction, 'status');
        $dismissed = $this->safe_value($transaction, 'dismissed');
        $rejected = $this->safe_value($transaction, 'rejected');
        if ($status) {
            $status = 'ok';
        } else if ($dismissed) {
            $status = 'canceled';
        } else if ($rejected) {
            $status = 'failed';
        } else {
            $status = 'pending';
        }
        $fee = array(
            'currency' => $code,
            'cost' => $this->safe_float($transaction, 'fee'),
        );
        return array(
            'info' => $transaction,
            'id' => $id,
            'txid' => $txid,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'addressFrom' => $addressFrom,
            'address' => $address,
            'addressTo' => $addressTo,
            'tagFrom' => $tagFrom,
            'tag' => $tag,
            'tagTo' => $tagTo,
            'type' => $type,
            'amount' => $amount,
            'currency' => $code,
            'status' => $status,
            'updated' => $updated,
            'fee' => $fee,
        );
    }

    public function withdraw($code, $amount, $address, $tag = null, $params = array ()) {
        $this->check_address($address);
        yield $this->load_markets();
        $currency = $this->currency($code);
        if ($tag !== null) {
            $address .= ':' . $tag;
        }
        $request = array(
            'currency' => $currency['id'],
            'amount' => $amount,
            'address' => $address,
        );
        // one time password
        $otp = $this->safe_string($params, 'otp_code');
        if (($otp !== null) || ($this->twofa !== null)) {
            if ($otp === null) {
                $otp = $this->oath();
            }
            $request['otp_code'] = $otp;
        }
        $response = yield $this->privatePostUserRequestWithdrawal (array_merge($request, $params));
        return array(
            'info' => $response,
            'id' => null,
        );
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $query = $this->omit($params, $this->extract_params($path));
        $path = '/' . $this->version . '/' . $this->implode_params($path, $params);
        if ($method === 'GET') {
            if ($query) {
                $path .= '?' . $this->urlencode($query);
            }
        }
        $url = $this->urls['api'] . $path;
        if ($api === 'private') {
            $this->check_required_credentials();
            $defaultExpires = $this->safe_integer_2($this->options, 'api-expires', 'expires', intval($this->timeout / 1000));
            $expires = $this->sum($this->seconds(), $defaultExpires);
            $expiresString = (string) $expires;
            $auth = $method . $path . $expiresString;
            $headers = array(
                'api-key' => $this->encode($this->apiKey),
                'api-expires' => $expiresString,
            );
            if ($method === 'POST') {
                $headers['Content-type'] = 'application/json';
                if ($query) {
                    $body = $this->json($query);
                    $auth .= $body;
                }
            }
            $signature = $this->hmac($this->encode($auth), $this->encode($this->secret));
            $headers['api-signature'] = $signature;
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }

    public function handle_errors($code, $reason, $url, $method, $headers, $body, $response, $requestHeaders, $requestBody) {
        if ($response === null) {
            return;
        }
        if (($code >= 400) && ($code <= 503)) {
            //
            //  array( "$message" => "Invalid token" )
            //
            $feedback = $this->id . ' ' . $body;
            $message = $this->safe_string($response, 'message');
            $this->throw_broadly_matched_exception($this->exceptions['broad'], $message, $feedback);
            $status = (string) $code;
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $status, $feedback);
        }
    }
}
