<?php

namespace ccxt;

// 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\ExchangeError;
use \ccxt\ArgumentsRequired;

class bitpanda extends Exchange {

    public function describe() {
        return $this->deep_extend(parent::describe (), array(
            'id' => 'bitpanda',
            'name' => 'Bitpanda Pro',
            'countries' => array( 'AT' ), // Austria
            'rateLimit' => 300,
            'version' => 'v1',
            // new metainfo interface
            'has' => array(
                'CORS' => false,
                'publicAPI' => true,
                'privateAPI' => true,
                'cancelAllOrders' => true,
                'cancelOrder' => true,
                'cancelOrders' => true,
                'createDepositAddress' => true,
                'createOrder' => true,
                'fetchBalance' => true,
                'fetchClosedOrders' => true,
                'fetchCurrencies' => true,
                'fetchDeposits' => true,
                'fetchDepositAddress' => true,
                'fetchMarkets' => true,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenOrders' => true,
                'fetchOrder' => true,
                'fetchOrderBook' => true,
                'fetchOrderTrades' => true,
                'fetchTime' => true,
                'fetchTrades' => true,
                'fetchTradingFees' => true,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchWithdrawals' => true,
                'withdraw' => true,
            ),
            'timeframes' => array(
                '1m' => '1/MINUTES',
                '5m' => '5/MINUTES',
                '15m' => '15/MINUTES',
                '30m' => '30/MINUTES',
                '1h' => '1/HOURS',
                '4h' => '4/HOURS',
                '1d' => '1/DAYS',
                '1w' => '1/WEEKS',
                '1M' => '1/MONTHS',
            ),
            'urls' => array(
                'logo' => 'https://user-images.githubusercontent.com/51840849/87591171-9a377d80-c6f0-11ea-94ac-97a126eac3bc.jpg',
                'api' => array(
                    'public' => 'https://api.exchange.bitpanda.com/public',
                    'private' => 'https://api.exchange.bitpanda.com/public',
                ),
                'www' => 'https://www.bitpanda.com/en/pro',
                'doc' => array(
                    'https://developers.bitpanda.com/exchange/',
                ),
                'fees' => 'https://www.bitpanda.com/en/pro/fees',
            ),
            'api' => array(
                'public' => array(
                    'get' => array(
                        'currencies',
                        'candlesticks/{instrument_code}',
                        'fees',
                        'instruments',
                        'order-book/{instrument_code}',
                        'market-ticker',
                        'market-ticker/{instrument_code}',
                        'price-ticks/{instrument_code}',
                        'time',
                    ),
                ),
                'private' => array(
                    'get' => array(
                        'account/balances',
                        'account/deposit/crypto/{currency_code}',
                        'account/deposit/fiat/EUR',
                        'account/deposits',
                        'account/deposits/bitpanda',
                        'account/withdrawals',
                        'account/withdrawals/bitpanda',
                        'account/fees',
                        'account/orders',
                        'account/orders/{order_id}',
                        'account/orders/{order_id}/trades',
                        'account/trades',
                        'account/trades/{trade_id}',
                        'account/trading-volume',
                    ),
                    'post' => array(
                        'account/deposit/crypto',
                        'account/withdraw/crypto',
                        'account/withdraw/fiat',
                        'account/fees',
                        'account/orders',
                    ),
                    'delete' => array(
                        'account/orders',
                        'account/orders/{order_id}',
                        'account/orders/client/{client_id}',
                    ),
                ),
            ),
            'fees' => array(
                'trading' => array(
                    'tierBased' => true,
                    'percentage' => true,
                    'taker' => 0.15 / 100,
                    'maker' => 0.10 / 100,
                    'tiers' => [
                        // volume in BTC
                        array(
                            'taker' => [
                                [0, 0.15 / 100],
                                [100, 0.13 / 100],
                                [250, 0.13 / 100],
                                [1000, 0.1 / 100],
                                [5000, 0.09 / 100],
                                [10000, 0.075 / 100],
                                [20000, 0.065 / 100],
                            ],
                            'maker' => [
                                [0, 0.1 / 100],
                                [100, 0.1 / 100],
                                [250, 0.09 / 100],
                                [1000, 0.075 / 100],
                                [5000, 0.06 / 100],
                                [10000, 0.05 / 100],
                                [20000, 0.05 / 100],
                            ],
                        ),
                    ],
                ),
            ),
            'requiredCredentials' => array(
                'apiKey' => true,
                'secret' => false,
            ),
            'exceptions' => array(
                'exact' => array(
                    'INVALID_CLIENT_UUID' => '\\ccxt\\InvalidOrder',
                    'ORDER_NOT_FOUND' => '\\ccxt\\OrderNotFound',
                    'ONLY_ONE_ERC20_ADDRESS_ALLOWED' => '\\ccxt\\InvalidAddress',
                    'DEPOSIT_ADDRESS_NOT_USED' => '\\ccxt\\InvalidAddress',
                    'INVALID_CREDENTIALS' => '\\ccxt\\AuthenticationError',
                    'MISSING_CREDENTIALS' => '\\ccxt\\AuthenticationError',
                    'INVALID_APIKEY' => '\\ccxt\\AuthenticationError',
                    'INVALID_SCOPES' => '\\ccxt\\AuthenticationError',
                    'INVALID_SUBJECT' => '\\ccxt\\AuthenticationError',
                    'INVALID_ISSUER' => '\\ccxt\\AuthenticationError',
                    'INVALID_AUDIENCE' => '\\ccxt\\AuthenticationError',
                    'INVALID_DEVICE_ID' => '\\ccxt\\AuthenticationError',
                    'INVALID_IP_RESTRICTION' => '\\ccxt\\AuthenticationError',
                    'APIKEY_REVOKED' => '\\ccxt\\AuthenticationError',
                    'APIKEY_EXPIRED' => '\\ccxt\\AuthenticationError',
                    'SYNCHRONIZER_TOKEN_MISMATCH' => '\\ccxt\\AuthenticationError',
                    'SESSION_EXPIRED' => '\\ccxt\\AuthenticationError',
                    'INTERNAL_ERROR' => '\\ccxt\\AuthenticationError',
                    'CLIENT_IP_BLOCKED' => '\\ccxt\\PermissionDenied',
                    'MISSING_PERMISSION' => '\\ccxt\\PermissionDenied',
                    'ILLEGAL_CHARS' => '\\ccxt\\BadRequest',
                    'UNSUPPORTED_MEDIA_TYPE' => '\\ccxt\\BadRequest',
                    'ACCOUNT_HISTORY_TIME_RANGE_TOO_BIG' => '\\ccxt\\BadRequest',
                    'CANDLESTICKS_TIME_RANGE_TOO_BIG' => '\\ccxt\\BadRequest',
                    'INVALID_INSTRUMENT_CODE' => '\\ccxt\\BadRequest',
                    'INVALID_ORDER_TYPE' => '\\ccxt\\BadRequest',
                    'INVALID_UNIT' => '\\ccxt\\BadRequest',
                    'INVALID_PERIOD' => '\\ccxt\\BadRequest',
                    'INVALID_TIME' => '\\ccxt\\BadRequest',
                    'INVALID_DATE' => '\\ccxt\\BadRequest',
                    'INVALID_CURRENCY' => '\\ccxt\\BadRequest',
                    'INVALID_AMOUNT' => '\\ccxt\\BadRequest',
                    'INVALID_PRICE' => '\\ccxt\\BadRequest',
                    'INVALID_LIMIT' => '\\ccxt\\BadRequest',
                    'INVALID_QUERY' => '\\ccxt\\BadRequest',
                    'INVALID_CURSOR' => '\\ccxt\\BadRequest',
                    'INVALID_ACCOUNT_ID' => '\\ccxt\\BadRequest',
                    'INVALID_SIDE' => '\\ccxt\\InvalidOrder',
                    'INVALID_ACCOUNT_HISTORY_FROM_TIME' => '\\ccxt\\BadRequest',
                    'INVALID_ACCOUNT_HISTORY_MAX_PAGE_SIZE' => '\\ccxt\\BadRequest',
                    'INVALID_ACCOUNT_HISTORY_TIME_PERIOD' => '\\ccxt\\BadRequest',
                    'INVALID_ACCOUNT_HISTORY_TO_TIME' => '\\ccxt\\BadRequest',
                    'INVALID_CANDLESTICKS_GRANULARITY' => '\\ccxt\\BadRequest',
                    'INVALID_CANDLESTICKS_UNIT' => '\\ccxt\\BadRequest',
                    'INVALID_ORDER_BOOK_DEPTH' => '\\ccxt\\BadRequest',
                    'INVALID_ORDER_BOOK_LEVEL' => '\\ccxt\\BadRequest',
                    'INVALID_PAGE_CURSOR' => '\\ccxt\\BadRequest',
                    'INVALID_TIME_RANGE' => '\\ccxt\\BadRequest',
                    'INVALID_TRADE_ID' => '\\ccxt\\BadRequest',
                    'INVALID_UI_ACCOUNT_SETTINGS' => '\\ccxt\\BadRequest',
                    'NEGATIVE_AMOUNT' => '\\ccxt\\InvalidOrder',
                    'NEGATIVE_PRICE' => '\\ccxt\\InvalidOrder',
                    'MIN_SIZE_NOT_SATISFIED' => '\\ccxt\\InvalidOrder',
                    'BAD_AMOUNT_PRECISION' => '\\ccxt\\InvalidOrder',
                    'BAD_PRICE_PRECISION' => '\\ccxt\\InvalidOrder',
                    'BAD_TRIGGER_PRICE_PRECISION' => '\\ccxt\\InvalidOrder',
                    'MAX_OPEN_ORDERS_EXCEEDED' => '\\ccxt\\BadRequest',
                    'MISSING_PRICE' => '\\ccxt\\InvalidOrder',
                    'MISSING_ORDER_TYPE' => '\\ccxt\\InvalidOrder',
                    'MISSING_SIDE' => '\\ccxt\\InvalidOrder',
                    'MISSING_CANDLESTICKS_PERIOD_PARAM' => '\\ccxt\\ArgumentsRequired',
                    'MISSING_CANDLESTICKS_UNIT_PARAM' => '\\ccxt\\ArgumentsRequired',
                    'MISSING_FROM_PARAM' => '\\ccxt\\ArgumentsRequired',
                    'MISSING_INSTRUMENT_CODE' => '\\ccxt\\ArgumentsRequired',
                    'MISSING_ORDER_ID' => '\\ccxt\\InvalidOrder',
                    'MISSING_TO_PARAM' => '\\ccxt\\ArgumentsRequired',
                    'MISSING_TRADE_ID' => '\\ccxt\\ArgumentsRequired',
                    'INVALID_ORDER_ID' => '\\ccxt\\OrderNotFound',
                    'NOT_FOUND' => '\\ccxt\\OrderNotFound',
                    'INSUFFICIENT_LIQUIDITY' => '\\ccxt\\InsufficientFunds',
                    'INSUFFICIENT_FUNDS' => '\\ccxt\\InsufficientFunds',
                    'NO_TRADING' => '\\ccxt\\ExchangeNotAvailable',
                    'SERVICE_UNAVAILABLE' => '\\ccxt\\ExchangeNotAvailable',
                    'GATEWAY_TIMEOUT' => '\\ccxt\\ExchangeNotAvailable',
                    'RATELIMIT' => '\\ccxt\\DDoSProtection',
                    'CF_RATELIMIT' => '\\ccxt\\DDoSProtection',
                    'INTERNAL_SERVER_ERROR' => '\\ccxt\\ExchangeError',
                ),
                'broad' => array(
                ),
            ),
            'commonCurrencies' => array(
                'MIOTA' => 'IOTA', // https://github.com/ccxt/ccxt/issues/7487
            ),
            // exchange-specific options
            'options' => array(
                'fetchTradingFees' => array(
                    'method' => 'fetchPrivateTradingFees', // or 'fetchPublicTradingFees'
                ),
                'fiat' => array( 'EUR', 'CHF' ),
            ),
        ));
    }

    public function fetch_time($params = array ()) {
        $response = $this->publicGetTime ($params);
        //
        //     {
        //         iso => '2020-07-10T05:17:26.716Z',
        //         epoch_millis => 1594358246716,
        //     }
        //
        return $this->safe_integer($response, 'epoch_millis');
    }

    public function fetch_currencies($params = array ()) {
        $response = $this->publicGetCurrencies ($params);
        //
        //     array(
        //         {
        //             "$code":"BEST",
        //             "precision":8
        //         }
        //     )
        //
        $result = array();
        for ($i = 0; $i < count($response); $i++) {
            $currency = $response[$i];
            $id = $this->safe_string($currency, 'code');
            $code = $this->safe_currency_code($id);
            $result[$code] = array(
                'id' => $id,
                'code' => $code,
                'name' => null,
                'info' => $currency, // the original payload
                'active' => null,
                'fee' => null,
                'precision' => $this->safe_integer($currency, 'precision'),
                'limits' => array(
                    'amount' => array( 'min' => null, 'max' => null ),
                    'price' => array( 'min' => null, 'max' => null ),
                    'cost' => array( 'min' => null, 'max' => null ),
                    'withdraw' => array( 'min' => null, 'max' => null ),
                ),
            );
        }
        return $result;
    }

    public function fetch_markets($params = array ()) {
        $response = $this->publicGetInstruments ($params);
        //
        //     array(
        //         {
        //             $state => 'ACTIVE',
        //             $base => array( code => 'ETH', $precision => 8 ),
        //             $quote => array( code => 'CHF', $precision => 2 ),
        //             amount_precision => 4,
        //             market_precision => 2,
        //             min_size => '10.0'
        //         }
        //     )
        //
        $result = array();
        for ($i = 0; $i < count($response); $i++) {
            $market = $response[$i];
            $baseAsset = $this->safe_value($market, 'base', array());
            $quoteAsset = $this->safe_value($market, 'quote', array());
            $baseId = $this->safe_string($baseAsset, 'code');
            $quoteId = $this->safe_string($quoteAsset, 'code');
            $id = $baseId . '_' . $quoteId;
            $base = $this->safe_currency_code($baseId);
            $quote = $this->safe_currency_code($quoteId);
            $symbol = $base . '/' . $quote;
            $precision = array(
                'amount' => $this->safe_integer($market, 'amount_precision'),
                'price' => $this->safe_integer($market, 'market_precision'),
            );
            $limits = array(
                'amount' => array(
                    'min' => null,
                    'max' => null,
                ),
                'price' => array(
                    'min' => null,
                    'max' => null,
                ),
                'cost' => array(
                    'min' => $this->safe_float($market, 'min_size'),
                    'max' => null,
                ),
            );
            $state = $this->safe_string($market, 'state');
            $active = ($state === 'ACTIVE');
            $result[] = array(
                'id' => $id,
                'symbol' => $symbol,
                'base' => $base,
                'quote' => $quote,
                'baseId' => $baseId,
                'quoteId' => $quoteId,
                'precision' => $precision,
                'limits' => $limits,
                'info' => $market,
                'active' => $active,
            );
        }
        return $result;
    }

    public function fetch_trading_fees($params = array ()) {
        $method = $this->safe_string($params, 'method');
        $params = $this->omit($params, 'method');
        if ($method === null) {
            $options = $this->safe_value($this->options, 'fetchTradingFees', array());
            $method = $this->safe_string($options, 'method', 'fetchPrivateTradingFees');
        }
        return $this->$method ($params);
    }

    public function fetch_public_trading_fees($params = array ()) {
        $this->load_markets();
        $response = $this->publicGetFees ($params);
        //
        //     array(
        //         {
        //             "fee_group_id":"default",
        //             "display_text":"The standard $fee plan.",
        //             "fee_tiers":array(
        //                 array("$volume":"0.0","fee_group_id":"default","maker_fee":"0.1","taker_fee":"0.15"),
        //                 array("$volume":"100.0","fee_group_id":"default","maker_fee":"0.1","taker_fee":"0.13"),
        //                 array("$volume":"250.0","fee_group_id":"default","maker_fee":"0.09","taker_fee":"0.13"),
        //                 array("$volume":"1000.0","fee_group_id":"default","maker_fee":"0.075","taker_fee":"0.1"),
        //                 array("$volume":"5000.0","fee_group_id":"default","maker_fee":"0.06","taker_fee":"0.09"),
        //                 array("$volume":"10000.0","fee_group_id":"default","maker_fee":"0.05","taker_fee":"0.075"),
        //                 array("$volume":"20000.0","fee_group_id":"default","maker_fee":"0.05","taker_fee":"0.065")
        //             ),
        //             "fee_discount_rate":"25.0",
        //             "minimum_price_value":"0.12"
        //         }
        //     )
        //
        $feeGroupsById = $this->index_by($response, 'fee_group_id');
        $feeGroupId = $this->safe_value($this->options, 'fee_group_id', 'default');
        $feeGroup = $this->safe_value($feeGroupsById, $feeGroupId, array());
        $feeTiers = $this->safe_value($feeGroup, 'fee_tiers');
        $result = array();
        for ($i = 0; $i < count($this->symbols); $i++) {
            $symbol = $this->symbols[$i];
            $fee = array(
                'info' => $feeGroup,
                'symbol' => $symbol,
                'maker' => null,
                'taker' => null,
                'percentage' => true,
                'tierBased' => true,
            );
            $takerFees = array();
            $makerFees = array();
            for ($i = 0; $i < count($feeTiers); $i++) {
                $tier = $feeTiers[$i];
                $volume = $this->safe_float($tier, 'volume');
                $taker = $this->safe_float($tier, 'taker_fee');
                $maker = $this->safe_float($tier, 'maker_fee');
                $taker /= 100;
                $maker /= 100;
                $takerFees[] = array( $volume, $taker );
                $makerFees[] = array( $volume, $maker );
                if ($i === 0) {
                    $fee['taker'] = $taker;
                    $fee['maker'] = $maker;
                }
            }
            $tiers = array(
                'taker' => $takerFees,
                'maker' => $makerFees,
            );
            $fee['tiers'] = $tiers;
            $result[$symbol] = $fee;
        }
        return $result;
    }

    public function fetch_private_trading_fees($params = array ()) {
        $this->load_markets();
        $response = $this->privateGetAccountFees ($params);
        //
        //     {
        //         "account_id" => "ed524d00-820a-11e9-8f1e-69602df16d85",
        //         "running_trading_volume" => "0.0",
        //         "fee_group_id" => "default",
        //         "collect_fees_in_best" => false,
        //         "fee_discount_rate" => "25.0",
        //         "minimum_price_value" => "0.12",
        //         "fee_tiers" => array(
        //             array( "$volume" => "0.0", "fee_group_id" => "default", "maker_fee" => "0.1", "taker_fee" => "0.1" ),
        //             array( "$volume" => "100.0", "fee_group_id" => "default", "maker_fee" => "0.09", "taker_fee" => "0.1" ),
        //             array( "$volume" => "250.0", "fee_group_id" => "default", "maker_fee" => "0.08", "taker_fee" => "0.1" ),
        //             array( "$volume" => "1000.0", "fee_group_id" => "default", "maker_fee" => "0.07", "taker_fee" => "0.09" ),
        //             array( "$volume" => "5000.0", "fee_group_id" => "default", "maker_fee" => "0.06", "taker_fee" => "0.08" ),
        //             array( "$volume" => "10000.0", "fee_group_id" => "default", "maker_fee" => "0.05", "taker_fee" => "0.07" ),
        //             array( "$volume" => "20000.0", "fee_group_id" => "default", "maker_fee" => "0.05", "taker_fee" => "0.06" ),
        //             array( "$volume" => "50000.0", "fee_group_id" => "default", "maker_fee" => "0.05", "taker_fee" => "0.05" )
        //         ),
        //         "active_fee_tier" => array( "$volume" => "0.0", "fee_group_id" => "default", "maker_fee" => "0.1", "taker_fee" => "0.1" )
        //     }
        //
        $activeFeeTier = $this->safe_value($response, 'active_fee_tier', array());
        $result = array(
            'info' => $response,
            'maker' => $this->safe_float($activeFeeTier, 'maker_fee'),
            'taker' => $this->safe_float($activeFeeTier, 'taker_fee'),
            'percentage' => true,
            'tierBased' => true,
        );
        $feeTiers = $this->safe_value($response, 'fee_tiers');
        $takerFees = array();
        $makerFees = array();
        for ($i = 0; $i < count($feeTiers); $i++) {
            $tier = $feeTiers[$i];
            $volume = $this->safe_float($tier, 'volume');
            $taker = $this->safe_float($tier, 'taker_fee');
            $maker = $this->safe_float($tier, 'maker_fee');
            $taker /= 100;
            $maker /= 100;
            $takerFees[] = array( $volume, $taker );
            $makerFees[] = array( $volume, $maker );
        }
        $tiers = array(
            'taker' => $takerFees,
            'maker' => $makerFees,
        );
        $result['tiers'] = $tiers;
        return $result;
    }

    public function parse_ticker($ticker, $market = null) {
        //
        // fetchTicker, fetchTickers
        //
        //     {
        //         "instrument_code":"BTC_EUR",
        //         "sequence":602562,
        //         "time":"2020-07-10T06:27:34.951Z",
        //         "state":"ACTIVE",
        //         "is_frozen":0,
        //         "quote_volume":"1695555.1783768",
        //         "base_volume":"205.67436",
        //         "last_price":"8143.91",
        //         "best_bid":"8143.71",
        //         "best_ask":"8156.9",
        //         "price_change":"-147.47",
        //         "price_change_percentage":"-1.78",
        //         "high":"8337.45",
        //         "low":"8110.0"
        //     }
        //
        $timestamp = $this->parse8601($this->safe_string($ticker, 'time'));
        $marketId = $this->safe_string($ticker, 'instrument_code');
        $symbol = $this->safe_symbol($marketId, $market, '_');
        $last = $this->safe_float($ticker, 'last_price');
        $percentage = $this->safe_float($ticker, 'price_change_percentage');
        $change = $this->safe_float($ticker, 'price_change');
        $open = null;
        $average = null;
        if (($last !== null) && ($change !== null)) {
            $open = $last - $change;
            $average = $this->sum($last, $open) / 2;
        }
        $baseVolume = $this->safe_float($ticker, 'base_volume');
        $quoteVolume = $this->safe_float($ticker, 'quote_volume');
        $vwap = $this->vwap($baseVolume, $quoteVolume);
        return array(
            'symbol' => $symbol,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'high' => $this->safe_float($ticker, 'high'),
            'low' => $this->safe_float($ticker, 'low'),
            'bid' => $this->safe_float($ticker, 'best_bid'),
            'bidVolume' => null,
            'ask' => $this->safe_float($ticker, 'best_ask'),
            'askVolume' => null,
            'vwap' => $vwap,
            'open' => $open,
            'close' => $last,
            'last' => $last,
            'previousClose' => null,
            'change' => $change,
            'percentage' => $percentage,
            'average' => $average,
            'baseVolume' => $baseVolume,
            'quoteVolume' => $quoteVolume,
            'info' => $ticker,
        );
    }

    public function fetch_ticker($symbol, $params = array ()) {
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'instrument_code' => $market['id'],
        );
        $response = $this->publicGetMarketTickerInstrumentCode (array_merge($request, $params));
        //
        //     {
        //         "instrument_code":"BTC_EUR",
        //         "sequence":602562,
        //         "time":"2020-07-10T06:27:34.951Z",
        //         "state":"ACTIVE",
        //         "is_frozen":0,
        //         "quote_volume":"1695555.1783768",
        //         "base_volume":"205.67436",
        //         "last_price":"8143.91",
        //         "best_bid":"8143.71",
        //         "best_ask":"8156.9",
        //         "price_change":"-147.47",
        //         "price_change_percentage":"-1.78",
        //         "high":"8337.45",
        //         "low":"8110.0"
        //     }
        //
        return $this->parse_ticker($response, $market);
    }

    public function fetch_tickers($symbols = null, $params = array ()) {
        $this->load_markets();
        $response = $this->publicGetMarketTicker ($params);
        //
        //     array(
        //         {
        //             "instrument_code":"BTC_EUR",
        //             "sequence":602562,
        //             "time":"2020-07-10T06:27:34.951Z",
        //             "state":"ACTIVE",
        //             "is_frozen":0,
        //             "quote_volume":"1695555.1783768",
        //             "base_volume":"205.67436",
        //             "last_price":"8143.91",
        //             "best_bid":"8143.71",
        //             "best_ask":"8156.9",
        //             "price_change":"-147.47",
        //             "price_change_percentage":"-1.78",
        //             "high":"8337.45",
        //             "low":"8110.0"
        //         }
        //     )
        //
        $result = array();
        for ($i = 0; $i < count($response); $i++) {
            $ticker = $this->parse_ticker($response[$i]);
            $symbol = $ticker['symbol'];
            $result[$symbol] = $ticker;
        }
        return $this->filter_by_array($result, 'symbol', $symbols);
    }

    public function fetch_order_book($symbol, $limit = null, $params = array ()) {
        $this->load_markets();
        $request = array(
            'instrument_code' => $this->market_id($symbol),
            // level 1 means only the best bid and ask
            // level 2 is a compiled order book up to market precision
            // level 3 is a full orderbook
            // if you wish to get regular updates about orderbooks please use the Websocket channel
            // heavy usage of this endpoint may result in limited access according to rate limits rules
            // 'level' => 3, // default
        );
        if ($limit !== null) {
            $request['depth'] = $limit;
        }
        $response = $this->publicGetOrderBookInstrumentCode (array_merge($request, $params));
        //
        // level 1
        //
        //     {
        //         "instrument_code":"BTC_EUR",
        //         "time":"2020-07-10T07:39:06.343Z",
        //         "asks":{
        //             "value":array(
        //                 "price":"8145.29",
        //                 "amount":"0.96538",
        //                 "number_of_orders":1
        //             }
        //         ),
        //         "bids":{
        //             "value":{
        //                 "price":"8134.0",
        //                 "amount":"1.5978",
        //                 "number_of_orders":5
        //             }
        //         }
        //     }
        //
        // level 2
        //
        //     {
        //         "instrument_code":"BTC_EUR","time":"2020-07-10T07:36:43.538Z",
        //         "asks":array(
        //             array("price":"8146.59","amount":"0.89691","number_of_orders":1),
        //             array("price":"8146.89","amount":"1.92062","number_of_orders":1),
        //             array("price":"8169.5","amount":"0.0663","number_of_orders":1),
        //         ),
        //         "bids":array(
        //             array("price":"8143.49","amount":"0.01329","number_of_orders":1),
        //             array("price":"8137.01","amount":"5.34748","number_of_orders":1),
        //             array("price":"8137.0","amount":"2.0","number_of_orders":1),
        //         )
        //     }
        //
        // level 3
        //
        //     {
        //         "instrument_code":"BTC_EUR",
        //         "time":"2020-07-10T07:32:31.525Z",
        //         "bids":array(
        //             array("price":"8146.79","amount":"0.01537","order_id":"5d717da1-a8f4-422d-afcc-03cb6ab66825"),
        //             array("price":"8139.32","amount":"3.66009","order_id":"d0715c68-f28d-4cf1-a450-d56cf650e11c"),
        //             array("price":"8137.51","amount":"2.61049","order_id":"085fd6f4-e835-4ca5-9449-a8f165772e60"),
        //         ),
        //         "asks":array(
        //             array("price":"8153.49","amount":"0.93384","order_id":"755d3aa3-42b5-46fa-903d-98f42e9ae6c4"),
        //             array("price":"8153.79","amount":"1.80456","order_id":"62034cf3-b70d-45ff-b285-ba6307941e7c"),
        //             array("price":"8167.9","amount":"0.0018","order_id":"036354e0-71cd-492f-94f2-01f7d4b66422"),
        //         )
        //     }
        //
        $timestamp = $this->parse8601($this->safe_string($response, 'time'));
        return $this->parse_order_book($response, $timestamp, 'bids', 'asks', 'price', 'amount');
    }

    public function parse_ohlcv($ohlcv, $market = null) {
        //
        //     {
        //         "instrument_code":"BTC_EUR",
        //         "$granularity":array("$unit":"HOURS","$period":1),
        //         "high":"9252.65",
        //         "low":"9115.27",
        //         "open":"9250.0",
        //         "close":"9132.35",
        //         "total_amount":"33.85924",
        //         "volume":"311958.9635744",
        //         "time":"2020-05-08T22:59:59.999Z",
        //         "last_sequence":461123
        //     }
        //
        $granularity = $this->safe_value($ohlcv, 'granularity');
        $unit = $this->safe_string($granularity, 'unit');
        $period = $this->safe_string($granularity, 'period');
        $units = array(
            'MINUTES' => 'm',
            'HOURS' => 'h',
            'DAYS' => 'd',
            'WEEKS' => 'w',
            'MONTHS' => 'M',
        );
        $lowercaseUnit = $this->safe_string($units, $unit);
        $timeframe = $period . $lowercaseUnit;
        $durationInSeconds = $this->parse_timeframe($timeframe);
        $duration = $durationInSeconds * 1000;
        $timestamp = $this->parse8601($this->safe_string($ohlcv, 'time'));
        $alignedTimestamp = $duration * intval($timestamp / $duration);
        $options = $this->safe_value($this->options, 'fetchOHLCV', array());
        $volumeField = $this->safe_string($options, 'volume', 'total_amount');
        return array(
            $alignedTimestamp,
            $this->safe_float($ohlcv, 'open'),
            $this->safe_float($ohlcv, 'high'),
            $this->safe_float($ohlcv, 'low'),
            $this->safe_float($ohlcv, 'close'),
            $this->safe_float($ohlcv, $volumeField),
        );
    }

    public function fetch_ohlcv($symbol, $timeframe = '1m', $since = null, $limit = null, $params = array ()) {
        $this->load_markets();
        $market = $this->market($symbol);
        $periodUnit = $this->safe_string($this->timeframes, $timeframe);
        list($period, $unit) = explode('/', $periodUnit);
        $durationInSeconds = $this->parse_timeframe($timeframe);
        $duration = $durationInSeconds * 1000;
        if ($limit === null) {
            $limit = 1500;
        }
        $request = array(
            'instrument_code' => $market['id'],
            // 'from' => $this->iso8601($since),
            // 'to' => $this->iso8601($this->milliseconds()),
            'period' => $period,
            'unit' => $unit,
        );
        if ($since === null) {
            $now = $this->milliseconds();
            $request['to'] = $this->iso8601($now);
            $request['from'] = $this->iso8601($now - $limit * $duration);
        } else {
            $request['from'] = $this->iso8601($since);
            $request['to'] = $this->iso8601($this->sum($since, $limit * $duration));
        }
        $response = $this->publicGetCandlesticksInstrumentCode (array_merge($request, $params));
        //
        //     array(
        //         array("instrument_code":"BTC_EUR","granularity":array("$unit":"HOURS","$period":1),"high":"9252.65","low":"9115.27","open":"9250.0","close":"9132.35","total_amount":"33.85924","volume":"311958.9635744","time":"2020-05-08T22:59:59.999Z","last_sequence":461123),
        //         array("instrument_code":"BTC_EUR","granularity":array("$unit":"HOURS","$period":1),"high":"9162.49","low":"9040.0","open":"9132.53","close":"9083.69","total_amount":"26.19685","volume":"238553.7812365","time":"2020-05-08T23:59:59.999Z","last_sequence":461376),
        //         array("instrument_code":"BTC_EUR","granularity":array("$unit":"HOURS","$period":1),"high":"9135.7","low":"9002.59","open":"9055.45","close":"9133.98","total_amount":"26.21919","volume":"238278.8724959","time":"2020-05-09T00:59:59.999Z","last_sequence":461521),
        //     )
        //
        return $this->parse_ohlcvs($response, $market, $timeframe, $since, $limit);
    }

    public function parse_trade($trade, $market = null) {
        //
        // fetchTrades (public)
        //
        //     {
        //         "instrument_code":"BTC_EUR",
        //         "$price":"8137.28",
        //         "$amount":"0.22269",
        //         "taker_side":"BUY",
        //         "volume":"1812.0908832",
        //         "time":"2020-07-10T14:44:32.299Z",
        //         "trade_timestamp":1594392272299,
        //         "sequence":603047
        //     }
        //
        // fetchMyTrades, fetchOrder, fetchOpenOrders, fetchClosedOrders trades (private)
        //
        //     {
        //         "$fee" => array(
        //             "fee_amount" => "0.0014",
        //             "fee_currency" => "BTC",
        //             "fee_percentage" => "0.1",
        //             "fee_group_id" => "default",
        //             "fee_type" => "TAKER",
        //             "running_trading_volume" => "0.0"
        //         ),
        //         "$trade" => {
        //             "trade_id" => "fdff2bcc-37d6-4a2d-92a5-46e09c868664",
        //             "order_id" => "36bb2437-7402-4794-bf26-4bdf03526439",
        //             "account_id" => "a4c699f6-338d-4a26-941f-8f9853bfc4b9",
        //             "$amount" => "1.4",
        //             "$side" => "BUY",
        //             "instrument_code" => "BTC_EUR",
        //             "$price" => "7341.4",
        //             "time" => "2019-09-27T15:05:32.564Z",
        //             "sequence" => 48670
        //         }
        //     }
        //
        $feeInfo = $this->safe_value($trade, 'fee', array());
        $trade = $this->safe_value($trade, 'trade', $trade);
        $timestamp = $this->safe_integer($trade, 'trade_timestamp');
        if ($timestamp === null) {
            $timestamp = $this->parse8601($this->safe_string($trade, 'time'));
        }
        $side = $this->safe_string_lower_2($trade, 'side', 'taker_side');
        $price = $this->safe_float($trade, 'price');
        $amount = $this->safe_float($trade, 'amount');
        $cost = $this->safe_float($trade, 'volume');
        if (($cost === null) && ($amount !== null) && ($price !== null)) {
            $cost = $amount * $price;
        }
        $marketId = $this->safe_string($trade, 'instrument_code');
        $symbol = $this->safe_symbol($marketId, $market, '_');
        $feeCost = $this->safe_float($feeInfo, 'fee_amount');
        $takerOrMaker = null;
        $fee = null;
        if ($feeCost !== null) {
            $feeCurrencyId = $this->safe_string($feeInfo, 'fee_currency');
            $feeCurrencyCode = $this->safe_currency_code($feeCurrencyId);
            $feeRate = $this->safe_float($feeInfo, 'fee_percentage');
            $fee = array(
                'cost' => $feeCost,
                'currency' => $feeCurrencyCode,
                'rate' => $feeRate,
            );
            $takerOrMaker = $this->safe_string_lower($feeInfo, 'fee_type');
        }
        return array(
            'id' => $this->safe_string_2($trade, 'trade_id', 'sequence'),
            'order' => $this->safe_string($trade, 'order_id'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $symbol,
            'type' => null,
            'side' => $side,
            'price' => $price,
            'amount' => $amount,
            'cost' => $cost,
            'takerOrMaker' => $takerOrMaker,
            'fee' => $fee,
            'info' => $trade,
        );
    }

    public function fetch_trades($symbol, $since = null, $limit = null, $params = array ()) {
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'instrument_code' => $market['id'],
            // 'from' => $this->iso8601($since),
            // 'to' => $this->iso8601($this->milliseconds()),
        );
        if ($since !== null) {
            // returns price ticks for a specific $market with an interval of maximum of 4 hours
            // sorted by latest first
            $request['from'] = $this->iso8601($since);
            $request['to'] = $this->iso8601($this->sum($since, 14400000));
        }
        $response = $this->publicGetPriceTicksInstrumentCode (array_merge($request, $params));
        //
        //     array(
        //         {
        //             "instrument_code":"BTC_EUR",
        //             "price":"8137.28",
        //             "amount":"0.22269",
        //             "taker_side":"BUY",
        //             "volume":"1812.0908832",
        //             "time":"2020-07-10T14:44:32.299Z",
        //             "trade_timestamp":1594392272299,
        //             "sequence":603047
        //         }
        //     )
        //
        return $this->parse_trades($response, $market, $since, $limit);
    }

    public function fetch_balance($params = array ()) {
        $this->load_markets();
        $response = $this->privateGetAccountBalances ($params);
        //
        //     {
        //         "account_id":"4b95934f-55f1-460c-a525-bd5afc0cf071",
        //         "$balances":array(
        //             {
        //                 "account_id":"4b95934f-55f1-460c-a525-bd5afc0cf071",
        //                 "currency_code":"BTC",
        //                 "change":"10.0",
        //                 "available":"10.0",
        //                 "locked":"0.0",
        //                 "sequence":142135994,
        //                 "time":"2020-07-01T10:57:32.959Z"
        //             }
        //         )
        //     }
        //
        $balances = $this->safe_value($response, 'balances', array());
        $result = array( 'info' => $response );
        for ($i = 0; $i < count($balances); $i++) {
            $balance = $balances[$i];
            $currencyId = $this->safe_string($balance, 'currency_code');
            $code = $this->safe_currency_code($currencyId);
            $account = $this->account();
            $account['free'] = $this->safe_float($balance, 'available');
            $account['used'] = $this->safe_float($balance, 'locked');
            $result[$code] = $account;
        }
        return $this->parse_balance($result);
    }

    public function parse_deposit_address($depositAddress, $currency = null) {
        $code = null;
        if ($currency !== null) {
            $code = $currency['code'];
        }
        $address = $this->safe_string($depositAddress, 'address');
        $tag = $this->safe_string($depositAddress, 'destination_tag');
        $this->check_address($address);
        return array(
            'currency' => $code,
            'address' => $address,
            'tag' => $tag,
            'info' => $depositAddress,
        );
    }

    public function create_deposit_address($code, $params = array ()) {
        $this->load_markets();
        $currency = $this->currency($code);
        $request = array(
            'currency' => $currency['id'],
        );
        $response = $this->privatePostAccountDepositCrypto (array_merge($request, $params));
        //
        //     {
        //         "address":"rBnNhk95FrdNisZtXcStzriFS8vEzz53DM",
        //         "destination_tag":"865690307",
        //         "enabled":true,
        //         "is_smart_contract":false
        //     }
        //
        return $this->parse_deposit_address($response, $currency);
    }

    public function fetch_deposit_address($code, $params = array ()) {
        $this->load_markets();
        $currency = $this->currency($code);
        $request = array(
            'currency_code' => $currency['id'],
        );
        $response = $this->privateGetAccountDepositCryptoCurrencyCode (array_merge($request, $params));
        //
        //     {
        //         "address":"rBnNhk95FrdNisZtXcStzriFS8vEzz53DM",
        //         "destination_tag":"865690307",
        //         "enabled":true,
        //         "is_smart_contract":false,
        //         "can_create_more":false
        //     }
        //
        return $this->parse_deposit_address($response, $currency);
    }

    public function fetch_deposits($code = null, $since = null, $limit = null, $params = array ()) {
        $this->load_markets();
        $request = array(
            // 'cursor' => 'string', // pointer specifying the position from which the next pages should be returned
        );
        $currency = null;
        if ($code !== null) {
            $currency = $this->currency($code);
            $request['currency_code'] = $currency['id'];
        }
        if ($limit !== null) {
            $request['max_page_size'] = $limit;
        }
        if ($since !== null) {
            $to = $this->safe_string($params, 'to');
            if ($to === null) {
                throw new ArgumentsRequired($this->id . ' fetchDeposits() requires a "$to" iso8601 string param with the $since argument is specified');
            }
            $request['from'] = $this->iso8601($since);
        }
        $response = $this->privateGetAccountDeposits (array_merge($request, $params));
        //
        //     {
        //         "deposit_history" => array(
        //             array(
        //                 "transaction_id" => "e5342efcd-d5b7-4a56-8e12-b69ffd68c5ef",
        //                 "account_id" => "c2d0076a-c20d-41f8-9e9a-1a1d028b2b58",
        //                 "amount" => "100",
        //                 "type" => "CRYPTO",
        //                 "funds_source" => "INTERNAL",
        //                 "time" => "2020-04-22T09:57:47Z",
        //                 "$currency" => "BTC",
        //                 "fee_amount" => "0.0",
        //                 "fee_currency" => "BTC"
        //             ),
        //             {
        //                 "transaction_id" => "79793d00-2899-4a4d-95b7-73ae6b31384f",
        //                 "account_id" => "c2d0076a-c20d-41f8-9e9a-1a1d028b2b58",
        //                 "time" => "2020-05-05T11:22:07.925Z",
        //                 "$currency" => "EUR",
        //                 "funds_source" => "EXTERNAL",
        //                 "type" => "FIAT",
        //                 "amount" => "50.0",
        //                 "fee_amount" => "0.01",
        //                 "fee_currency" => "EUR"
        //             }
        //         ),
        //         "max_page_size" => 2,
        //         "cursor" => "eyJhY2NvdW50X2lkIjp7InMiOiJlMzY5YWM4MC00NTc3LTExZTktYWUwOC05YmVkYzQ3OTBiODQiLCJzcyI6W10sIm5zIjpbXSwiYnMiOltdLCJtIjp7fSwibCI6W119LCJpdGVtX2tleSI6eyJzIjoiV0lUSERSQVdBTDo6MmFlMjYwY2ItOTk3MC00YmNiLTgxNmEtZGY4MDVmY2VhZTY1Iiwic3MiOltdLCJucyI6W10sImJzIjpbXSwibSI6e30sImwiOltdfSwiZ2xvYmFsX3dpdGhkcmF3YWxfaW5kZXhfaGFzaF9rZXkiOnsicyI6ImUzNjlhYzgwLTQ1NzctMTFlOS1hZTA4LTliZWRjNDc5MGI4NCIsInNzIjpbXSwibnMiOltdLCJicyI6W10sIm0iOnt9LCJsIjpbXX0sInRpbWVzdGFtcCI6eyJuIjoiMTU4ODA1ODc2Nzk0OCIsInNzIjpbXSwibnMiOltdLCJicyI6W10sIm0iOnt9LCJsIjpbXX19"
        //     }
        //
        $depositHistory = $this->safe_value($response, 'deposit_history', array());
        return $this->parse_transactions($depositHistory, $currency, $since, $limit, array( 'type' => 'deposit' ));
    }

    public function fetch_withdrawals($code = null, $since = null, $limit = null, $params = array ()) {
        $this->load_markets();
        $request = array(
            // 'cursor' => 'string', // pointer specifying the position from which the next pages should be returned
        );
        $currency = null;
        if ($code !== null) {
            $currency = $this->currency($code);
            $request['currency_code'] = $currency['id'];
        }
        if ($limit !== null) {
            $request['max_page_size'] = $limit;
        }
        if ($since !== null) {
            $to = $this->safe_string($params, 'to');
            if ($to === null) {
                throw new ArgumentsRequired($this->id . ' fetchWithdrawals() requires a "$to" iso8601 string param with the $since argument is specified');
            }
            $request['from'] = $this->iso8601($since);
        }
        $response = $this->privateGetAccountWithdrawals (array_merge($request, $params));
        //
        //     {
        //         "withdrawal_history" => array(
        //             array(
        //                 "account_id" => "e369ac80-4577-11e9-ae08-9bedc4790b84",
        //                 "amount" => "0.1",
        //                 "$currency" => "BTC",
        //                 "fee_amount" => "0.00002",
        //                 "fee_currency" => "BTC",
        //                 "funds_source" => "EXTERNAL",
        //                 "related_transaction_id" => "e298341a-3855-405e-bce3-92db368a3157",
        //                 "time" => "2020-05-05T11:11:32.110Z",
        //                 "transaction_id" => "6693ff40-bb10-4dcf-ada7-3b287727c882",
        //                 "type" => "CRYPTO"
        //             ),
        //             {
        //                 "account_id" => "e369ac80-4577-11e9-ae08-9bedc4790b84",
        //                 "amount" => "0.1",
        //                 "$currency" => "BTC",
        //                 "fee_amount" => "0.0",
        //                 "fee_currency" => "BTC",
        //                 "funds_source" => "INTERNAL",
        //                 "time" => "2020-05-05T10:29:53.464Z",
        //                 "transaction_id" => "ec9703b1-954b-4f76-adea-faac66eabc0b",
        //                 "type" => "CRYPTO"
        //             }
        //         ),
        //         "cursor" => "eyJhY2NvdW50X2lkIjp7InMiOiJlMzY5YWM4MC00NTc3LTExZTktYWUwOC05YmVkYzQ3OTBiODQiLCJzcyI6W10sIm5zIjpbXSwiYnMiOltdLCJtIjp7fSwibCI6W119LCJpdGVtX2tleSI6eyJzIjoiV0lUSERSQVdBTDo6ZWM5NzAzYjEtOTU0Yi00Zjc2LWFkZWEtZmFhYzY2ZWFiYzBiIiwic3MiOltdLCJucyI6W10sImJzIjpbXSwibSI6e30sImwiOltdfSwiZ2xvYmFsX3dpdGhkcmF3YWxfaW5kZXhfaGFzaF9rZXkiOnsicyI6ImUzNjlhYzgwLTQ1NzctMTFlOS1hZTA4LTliZWRjNDc5MGI4NCIsInNzIjpbXSwibnMiOltdLCJicyI6W10sIm0iOnt9LCJsIjpbXX0sInRpbWVzdGFtcCI6eyJuIjoiMTU4ODY3NDU5MzQ2NCIsInNzIjpbXSwibnMiOltdLCJicyI6W10sIm0iOnt9LCJsIjpbXX19",
        //         "max_page_size" => 2
        //     }
        //
        $withdrawalHistory = $this->safe_value($response, 'withdrawal_history', array());
        return $this->parse_transactions($withdrawalHistory, $currency, $since, $limit, array( 'type' => 'withdrawal' ));
    }

    public function withdraw($code, $amount, $address, $tag = null, $params = array ()) {
        $this->check_address($address);
        $this->load_markets();
        $currency = $this->currency($code);
        $request = array(
            'currency' => $code,
            'amount' => $this->currency_to_precision($code, $amount),
            // 'payout_account_id' => '66756a10-3e86-48f4-9678-b634c4b135b2', // fiat only
            // 'recipient' => array( // crypto only
            //     'address' => $address,
            //     // 'destination_tag' => '',
            // ),
        );
        $options = $this->safe_value($this->options, 'fiat', array());
        $isFiat = $this->in_array($code, $options);
        $method = $isFiat ? 'privatePostAccountWithdrawFiat' : 'privatePostAccountWithdrawCrypto';
        if ($isFiat) {
            $payoutAccountId = $this->safe_string($params, 'payout_account_id');
            if ($payoutAccountId === null) {
                throw ArgumentsRequired ($this->id . ' withdraw() requires a payout_account_id param for fiat ' . $code . ' withdrawals');
            }
        } else {
            $recipient = array( 'address' => $address );
            if ($tag !== null) {
                $recipient['destination_tag'] = $tag;
            }
            $request['recipient'] = $recipient;
        }
        $response = $this->$method (array_merge($request, $params));
        //
        // crypto
        //
        //     {
        //         "$amount" => "1234.5678",
        //         "fee" => "1234.5678",
        //         "$recipient" => "3NacQ7rzZdhfyAtfJ5a11k8jFPdcMP2Bq7",
        //         "destination_tag" => "",
        //         "transaction_id" => "d0f8529f-f832-4e6a-9dc5-b8d5797badb2"
        //     }
        //
        // fiat
        //
        //     {
        //         "transaction_id" => "54236cd0-4413-11e9-93fb-5fea7e5b5df6"
        //     }
        //
        return $this->parse_transaction($response, $currency);
    }

    public function parse_transaction($transaction, $currency = null) {
        //
        // fetchDeposits, fetchWithdrawals
        //
        //     {
        //         "transaction_id" => "C2b42efcd-d5b7-4a56-8e12-b69ffd68c5ef",
        //         "type" => "FIAT",
        //         "account_id" => "c2d0076a-c20d-41f8-9e9a-1a1d028b2b58",
        //         "$amount" => "1234.5678",
        //         "time" => "2019-08-24T14:15:22Z",
        //         "funds_source" => "INTERNAL",
        //         "$currency" => "BTC",
        //         "fee_amount" => "1234.5678",
        //         "fee_currency" => "BTC",
        //         "blockchain_transaction_id" => "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
        //         "related_transaction_id" => "e298341a-3855-405e-bce3-92db368a3157"
        //     }
        //
        // withdraw
        //
        //
        //     crypto
        //
        //     {
        //         "$amount" => "1234.5678",
        //         "$fee" => "1234.5678",
        //         "recipient" => "3NacQ7rzZdhfyAtfJ5a11k8jFPdcMP2Bq7",
        //         "destination_tag" => "",
        //         "transaction_id" => "d0f8529f-f832-4e6a-9dc5-b8d5797badb2"
        //     }
        //
        //     fiat
        //
        //     {
        //         "transaction_id" => "54236cd0-4413-11e9-93fb-5fea7e5b5df6"
        //     }
        //
        $id = $this->safe_string($transaction, 'transaction_id');
        $amount = $this->safe_float($transaction, 'amount');
        $timestamp = $this->parse8601($this->safe_string($transaction, 'time'));
        $currencyId = $this->safe_string($transaction, 'currency');
        $currency = $this->safe_currency($currencyId, $currency);
        $status = 'ok'; // the exchange returns cleared transactions only
        $feeCost = $this->safe_float_2($transaction, 'fee_amount', 'fee');
        $fee = null;
        $addressTo = $this->safe_string($transaction, 'recipient');
        $tagTo = $this->safe_string($transaction, 'destination_tag');
        if ($feeCost !== null) {
            $feeCurrencyId = $this->safe_string($transaction, 'fee_currency', $currencyId);
            $feeCurrencyCode = $this->safe_currency_code($feeCurrencyId);
            $fee = array(
                'cost' => $feeCost,
                'currency' => $feeCurrencyCode,
            );
        }
        return array(
            'info' => $transaction,
            'id' => $id,
            'currency' => $currency['code'],
            'amount' => $amount,
            'address' => $addressTo,
            'addressFrom' => null,
            'addressTo' => $addressTo,
            'tag' => $tagTo,
            'tagFrom' => null,
            'tagTo' => $tagTo,
            'status' => $status,
            'type' => null,
            'updated' => null,
            'txid' => $this->safe_string($transaction, 'blockchain_transaction_id'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'fee' => $fee,
        );
    }

    public function parse_order_status($status) {
        $statuses = array(
            'FILLED' => 'open',
            'FILLED_FULLY' => 'closed',
            'FILLED_CLOSED' => 'canceled',
            'FILLED_REJECTED' => 'rejected',
            'OPEN' => 'open',
            'REJECTED' => 'rejected',
            'CLOSED' => 'canceled',
            'FAILED' => 'failed',
            'STOP_TRIGGERED' => 'triggered',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_order($order, $market = null) {
        //
        // createOrder
        //
        //     {
        //         "order_id" => "d5492c24-2995-4c18-993a-5b8bf8fffc0d",
        //         "client_id" => "d75fb03b-b599-49e9-b926-3f0b6d103206",
        //         "account_id" => "a4c699f6-338d-4a26-941f-8f9853bfc4b9",
        //         "instrument_code" => "BTC_EUR",
        //         "time" => "2019-08-01T08:00:44.026Z",
        //         "$side" => "BUY",
        //         "$price" => "5000",
        //         "$amount" => "1",
        //         "filled_amount" => "0.5",
        //         "$type" => "LIMIT",
        //         "time_in_force" => "GOOD_TILL_CANCELLED"
        //     }
        //
        // fetchOrder, fetchOpenOrders, fetchClosedOrders
        //
        //     {
        //         "$order" => array(
        //             "order_id" => "66756a10-3e86-48f4-9678-b634c4b135b2",
        //             "account_id" => "1eb2ad5d-55f1-40b5-bc92-7dc05869e905",
        //             "instrument_code" => "BTC_EUR",
        //             "$amount" => "1234.5678",
        //             "filled_amount" => "1234.5678",
        //             "$side" => "BUY",
        //             "$type" => "LIMIT",
        //             "$status" => "OPEN",
        //             "sequence" => 123456789,
        //             "$price" => "1234.5678",
        //             "average_price" => "1234.5678",
        //             "reason" => "INSUFFICIENT_FUNDS",
        //             "time" => "2019-08-24T14:15:22Z",
        //             "time_in_force" => "GOOD_TILL_CANCELLED",
        //             "time_last_updated" => "2019-08-24T14:15:22Z",
        //             "expire_after" => "2019-08-24T14:15:22Z",
        //             "is_post_only" => false,
        //             "time_triggered" => "2019-08-24T14:15:22Z",
        //             "trigger_price" => "1234.5678"
        //         ),
        //         "$trades" => array(
        //             {
        //                 "fee" => array(
        //                     "fee_amount" => "0.0014",
        //                     "fee_currency" => "BTC",
        //                     "fee_percentage" => "0.1",
        //                     "fee_group_id" => "default",
        //                     "fee_type" => "TAKER",
        //                     "running_trading_volume" => "0.0"
        //                 ),
        //                 "$trade" => {
        //                     "trade_id" => "fdff2bcc-37d6-4a2d-92a5-46e09c868664",
        //                     "order_id" => "36bb2437-7402-4794-bf26-4bdf03526439",
        //                     "account_id" => "a4c699f6-338d-4a26-941f-8f9853bfc4b9",
        //                     "$amount" => "1.4",
        //                     "$side" => "BUY",
        //                     "instrument_code" => "BTC_EUR",
        //                     "$price" => "7341.4",
        //                     "time" => "2019-09-27T15:05:32.564Z",
        //                     "sequence" => 48670
        //                 }
        //             }
        //         )
        //     }
        //
        $rawTrades = $this->safe_value($order, 'trades', array());
        $order = $this->safe_value($order, 'order', $order);
        $id = $this->safe_string($order, 'order_id');
        $clientOrderId = $this->safe_string($order, 'client_id');
        $timestamp = $this->parse8601($this->safe_string($order, 'time'));
        $status = $this->parse_order_status($this->safe_string($order, 'status'));
        $marketId = $this->safe_string($order, 'instrument_code');
        $symbol = $this->safe_symbol($marketId, $market, '_');
        $price = $this->safe_float($order, 'price');
        $amount = $this->safe_float($order, 'amount');
        $cost = null;
        $filled = $this->safe_float($order, 'filled_amount');
        $remaining = null;
        if ($filled !== null) {
            if ($amount !== null) {
                $remaining = max (0, $amount - $filled);
                if ($status === null) {
                    if ($remaining > 0) {
                        $status = 'open';
                    } else {
                        $status = 'closed';
                    }
                }
            }
        }
        $side = $this->safe_string_lower($order, 'side');
        $type = $this->safe_string_lower($order, 'type');
        $trades = $this->parse_trades($rawTrades, $market, null, null);
        $fees = array();
        $numTrades = is_array($trades) ? count($trades) : 0;
        $lastTradeTimestamp = null;
        $tradeCost = null;
        $tradeAmount = null;
        if ($numTrades > 0) {
            $lastTradeTimestamp = $trades[0]['timestamp'];
            $tradeCost = 0;
            $tradeAmount = 0;
            for ($i = 0; $i < count($trades); $i++) {
                $trade = $trades[$i];
                $fees[] = $trade['fee'];
                $lastTradeTimestamp = max ($lastTradeTimestamp, $trade['timestamp']);
                $tradeCost = $this->sum($tradeCost, $trade['cost']);
                $tradeAmount = $this->sum($tradeAmount, $trade['amount']);
            }
        }
        $average = $this->safe_float($order, 'average_price');
        if ($average === null) {
            if (($tradeCost !== null) && ($tradeAmount !== null) && ($tradeAmount !== 0)) {
                $average = $tradeCost / $tradeAmount;
            }
        }
        if ($cost === null) {
            if (($average !== null) && ($filled !== null)) {
                $cost = $average * $filled;
            }
        }
        $timeInForce = $this->parse_time_in_force($this->safe_string($order, 'time_in_force'));
        $stopPrice = $this->safe_float($order, 'trigger_price');
        $postOnly = $this->safe_value($order, 'is_post_only');
        $result = array(
            'id' => $id,
            'clientOrderId' => $clientOrderId,
            'info' => $order,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => $lastTradeTimestamp,
            'symbol' => $symbol,
            'type' => $type,
            'timeInForce' => $timeInForce,
            'postOnly' => $postOnly,
            'side' => $side,
            'price' => $price,
            'stopPrice' => $stopPrice,
            'amount' => $amount,
            'cost' => $cost,
            'average' => $average,
            'filled' => $filled,
            'remaining' => $remaining,
            'status' => $status,
            // 'fee' => null,
            'trades' => $trades,
        );
        $numFees = is_array($fees) ? count($fees) : 0;
        if ($numFees > 0) {
            if ($numFees === 1) {
                $result['fee'] = $fees[0];
            } else {
                $feesByCurrency = $this->group_by($fees, 'currency');
                $feeCurrencies = is_array($feesByCurrency) ? array_keys($feesByCurrency) : array();
                $numFeesByCurrency = is_array($feeCurrencies) ? count($feeCurrencies) : 0;
                if ($numFeesByCurrency === 1) {
                    $feeCurrency = $feeCurrencies[0];
                    $feeArray = $this->safe_value($feesByCurrency, $feeCurrency);
                    $feeCost = 0;
                    for ($i = 0; $i < count($feeArray); $i++) {
                        $feeCost = $this->sum($feeCost, $feeArray[$i]['cost']);
                    }
                    $result['fee'] = array(
                        'cost' => $feeCost,
                        'currency' => $feeCurrency,
                    );
                } else {
                    $result['fees'] = $fees;
                }
            }
        } else {
            $result['fee'] = null;
        }
        return $result;
    }

    public function parse_time_in_force($timeInForce) {
        $timeInForces = array(
            'GOOD_TILL_CANCELLED' => 'GTC',
            'GOOD_TILL_TIME' => 'GTT',
            'IMMEDIATE_OR_CANCELLED' => 'IOC',
            'FILL_OR_KILL' => 'FOK',
        );
        return $this->safe_string($timeInForces, $timeInForce, $timeInForce);
    }

    public function create_order($symbol, $type, $side, $amount, $price = null, $params = array ()) {
        $this->load_markets();
        $market = $this->market($symbol);
        $uppercaseType = strtoupper($type);
        $request = array(
            'instrument_code' => $market['id'],
            'type' => $uppercaseType, // LIMIT, MARKET, STOP
            'side' => strtoupper($side), // or SELL
            'amount' => $this->amount_to_precision($symbol, $amount),
            // "$price" => "1234.5678", // required for LIMIT and STOP orders
            // "client_id" => "d75fb03b-b599-49e9-b926-3f0b6d103206", // optional
            // "time_in_force" => "GOOD_TILL_CANCELLED", // limit orders only, GOOD_TILL_CANCELLED, GOOD_TILL_TIME, IMMEDIATE_OR_CANCELLED and FILL_OR_KILL
            // "expire_after" => "2020-07-02T19:40:13Z", // required for GOOD_TILL_TIME
            // "is_post_only" => false, // limit orders only, optional
            // "trigger_price" => "1234.5678" // required for stop orders
        );
        $priceIsRequired = false;
        if ($uppercaseType === 'LIMIT' || $uppercaseType === 'STOP') {
            $priceIsRequired = true;
        }
        if ($uppercaseType === 'STOP') {
            $triggerPrice = $this->safe_float($params, 'trigger_price');
            if ($triggerPrice === null) {
                throw new ArgumentsRequired($this->id . ' createOrder() requires a trigger_price param for ' . $type . ' orders');
            }
            $request['trigger_price'] = $this->price_to_precision($symbol, $triggerPrice);
            $params = $this->omit($params, 'trigger_price');
        }
        if ($priceIsRequired) {
            $request['price'] = $this->price_to_precision($symbol, $price);
        }
        $clientOrderId = $this->safe_string_2($params, 'clientOrderId', 'client_id');
        if ($clientOrderId !== null) {
            $request['client_id'] = $clientOrderId;
            $params = $this->omit($params, array( 'clientOrderId', 'client_id' ));
        }
        $response = $this->privatePostAccountOrders (array_merge($request, $params));
        //
        //     {
        //         "order_id" => "d5492c24-2995-4c18-993a-5b8bf8fffc0d",
        //         "client_id" => "d75fb03b-b599-49e9-b926-3f0b6d103206",
        //         "account_id" => "a4c699f6-338d-4a26-941f-8f9853bfc4b9",
        //         "instrument_code" => "BTC_EUR",
        //         "time" => "2019-08-01T08:00:44.026Z",
        //         "$side" => "BUY",
        //         "$price" => "5000",
        //         "$amount" => "1",
        //         "filled_amount" => "0.5",
        //         "$type" => "LIMIT",
        //         "time_in_force" => "GOOD_TILL_CANCELLED"
        //     }
        //
        return $this->parse_order($response, $market);
    }

    public function cancel_order($id, $symbol = null, $params = array ()) {
        $this->load_markets();
        $clientOrderId = $this->safe_string_2($params, 'clientOrderId', 'client_id');
        $params = $this->omit($params, array( 'clientOrderId', 'client_id' ));
        $method = 'privateDeleteAccountOrdersOrderId';
        $request = array();
        if ($clientOrderId !== null) {
            $method = 'privateDeleteAccountOrdersClientClientId';
            $request['client_id'] = $clientOrderId;
        } else {
            $request['order_id'] = $id;
        }
        $response = $this->$method (array_merge($request, $params));
        //
        // responds with an empty body
        //
        return $response;
    }

    public function cancel_all_orders($symbol = null, $params = array ()) {
        $this->load_markets();
        $request = array();
        if ($symbol !== null) {
            $market = $this->market($symbol);
            $request['instrument_code'] = $market['id'];
        }
        $response = $this->privateDeleteAccountOrders (array_merge($request, $params));
        //
        //     array(
        //         "a10e9bd1-8f72-4cfe-9f1b-7f1c8a9bd8ee"
        //     )
        //
        return $response;
    }

    public function cancel_orders($ids, $symbol = null, $params = array ()) {
        $this->load_markets();
        $request = array(
            'ids' => implode(',', $ids),
        );
        $response = $this->privateDeleteAccountOrders (array_merge($request, $params));
        //
        //     array(
        //         "a10e9bd1-8f72-4cfe-9f1b-7f1c8a9bd8ee"
        //     )
        //
        return $response;
    }

    public function fetch_order($id, $symbol = null, $params = array ()) {
        $this->load_markets();
        $request = array(
            'order_id' => $id,
        );
        $response = $this->privateGetAccountOrdersOrderId (array_merge($request, $params));
        //
        //     {
        //         "order" => array(
        //             "order_id" => "36bb2437-7402-4794-bf26-4bdf03526439",
        //             "account_id" => "a4c699f6-338d-4a26-941f-8f9853bfc4b9",
        //             "time_last_updated" => "2019-09-27T15:05:35.096Z",
        //             "sequence" => 48782,
        //             "price" => "7349.2",
        //             "filled_amount" => "100.0",
        //             "status" => "FILLED_FULLY",
        //             "amount" => "100.0",
        //             "instrument_code" => "BTC_EUR",
        //             "side" => "BUY",
        //             "time" => "2019-09-27T15:05:32.063Z",
        //             "type" => "MARKET"
        //         ),
        //         "trades" => array(
        //             {
        //                 "fee" => array(
        //                     "fee_amount" => "0.0014",
        //                     "fee_currency" => "BTC",
        //                     "fee_percentage" => "0.1",
        //                     "fee_group_id" => "default",
        //                     "fee_type" => "TAKER",
        //                     "running_trading_volume" => "0.0"
        //                 ),
        //                 "trade" => {
        //                     "trade_id" => "fdff2bcc-37d6-4a2d-92a5-46e09c868664",
        //                     "order_id" => "36bb2437-7402-4794-bf26-4bdf03526439",
        //                     "account_id" => "a4c699f6-338d-4a26-941f-8f9853bfc4b9",
        //                     "amount" => "1.4",
        //                     "side" => "BUY",
        //                     "instrument_code" => "BTC_EUR",
        //                     "price" => "7341.4",
        //                     "time" => "2019-09-27T15:05:32.564Z",
        //                     "sequence" => 48670
        //                 }
        //             }
        //         )
        //     }
        //
        return $this->parse_order($response);
    }

    public function fetch_open_orders($symbol = null, $since = null, $limit = null, $params = array ()) {
        $this->load_markets();
        $request = array(
            // 'from' => $this->iso8601($since),
            // 'to' => $this->iso8601($this->milliseconds()), // max range is 100 days
            // 'instrument_code' => $market['id'],
            // 'with_cancelled_and_rejected' => false, // default is false, orders which have been cancelled by the user before being filled or rejected by the system as invalid, additionally, all inactive filled orders which would return with "with_just_filled_inactive"
            // 'with_just_filled_inactive' => false, // orders which have been filled and are no longer open, use of "with_cancelled_and_rejected" extends "with_just_filled_inactive" and in case both are specified the latter is ignored
            // 'with_just_orders' => false, // do not return any trades corresponsing $to the orders, it may be significanly faster and should be used if user is not interesting in trade information
            // 'max_page_size' => 100,
            // 'cursor' => 'string', // pointer specifying the position from which the next pages should be returned
        );
        $market = null;
        if ($symbol !== null) {
            $market = $this->market($symbol);
            $request['instrument_code'] = $market['id'];
        }
        if ($since !== null) {
            $to = $this->safe_string($params, 'to');
            if ($to === null) {
                throw new ArgumentsRequired($this->id . ' fetchOrders() requires a "$to" iso8601 string param with the $since argument is specified, max range is 100 days');
            }
            $request['from'] = $this->iso8601($since);
        }
        if ($limit !== null) {
            $request['max_page_size'] = $limit;
        }
        $response = $this->privateGetAccountOrders (array_merge($request, $params));
        //
        //     {
        //         "order_history" => array(
        //             {
        //                 "order" => array(
        //                     "trigger_price" => "12089.88",
        //                     "order_id" => "d453ca12-c650-46dd-9dee-66910d96bfc0",
        //                     "account_id" => "ef3a5f4c-cfcd-415e-ba89-5a9abf47b28a",
        //                     "instrument_code" => "BTC_USDT",
        //                     "time" => "2019-08-23T10:02:31.663Z",
        //                     "side" => "SELL",
        //                     "price" => "10159.76",
        //                     "average_price" => "10159.76",
        //                     "amount" => "0.2",
        //                     "filled_amount" => "0.2",
        //                     "type" => "STOP",
        //                     "sequence" => 8,
        //                     "status" => "FILLED_FULLY"
        //                 ),
        //                 "trades" => array(
        //                     {
        //                         "fee" => array(
        //                             "fee_amount" => "0.4188869",
        //                             "fee_currency" => "USDT",
        //                             "fee_percentage" => "0.1",
        //                             "fee_group_id" => "default",
        //                             "fee_type" => "TAKER",
        //                             "running_trading_volume" => "0.0"
        //                         ),
        //                         "trade" => array(
        //                             "trade_id" => "ec82896f-fd1b-4cbb-89df-a9da85ccbb4b",
        //                             "order_id" => "d453ca12-c650-46dd-9dee-66910d96bfc0",
        //                             "account_id" => "ef3a5f4c-cfcd-415e-ba89-5a9abf47b28a",
        //                             "amount" => "0.2",
        //                             "side" => "SELL",
        //                             "instrument_code" => "BTC_USDT",
        //                             "price" => "10159.76",
        //                             "time" => "2019-08-23T10:02:32.663Z",
        //                             "sequence" => 9
        //                         }
        //                     }
        //                 )
        //             ),
        //             array(
        //                 "order" => array(
        //                     "order_id" => "5151a99e-f414-418f-8cf1-2568d0a63ea5",
        //                     "account_id" => "ef3a5f4c-cfcd-415e-ba89-5a9abf47b28a",
        //                     "instrument_code" => "BTC_USDT",
        //                     "time" => "2019-08-23T10:01:36.773Z",
        //                     "side" => "SELL",
        //                     "price" => "12289.88",
        //                     "amount" => "0.5",
        //                     "filled_amount" => "0.0",
        //                     "type" => "LIMIT",
        //                     "sequence" => 7,
        //                     "status" => "OPEN"
        //                 ),
        //                 "trades" => array()
        //             ),
        //             {
        //                 "order" => array(
        //                     "order_id" => "ac80d857-75e1-4733-9070-fd4288395fdc",
        //                     "account_id" => "ef3a5f4c-cfcd-415e-ba89-5a9abf47b28a",
        //                     "instrument_code" => "BTC_USDT",
        //                     "time" => "2019-08-23T10:01:25.031Z",
        //                     "side" => "SELL",
        //                     "price" => "11089.88",
        //                     "amount" => "0.1",
        //                     "filled_amount" => "0.0",
        //                     "type" => "LIMIT",
        //                     "sequence" => 6,
        //                     "status" => "OPEN"
        //                 ),
        //                 "trades" => array()
        //             }
        //         ),
        //         "max_page_size" => 100
        //     }
        //
        $orderHistory = $this->safe_value($response, 'order_history', array());
        return $this->parse_orders($orderHistory, $market, $since, $limit);
    }

    public function fetch_closed_orders($symbol = null, $since = null, $limit = null, $params = array ()) {
        $request = array(
            'with_cancelled_and_rejected' => true, // default is false, orders which have been cancelled by the user before being filled or rejected by the system as invalid, additionally, all inactive filled orders which would return with "with_just_filled_inactive"
        );
        return $this->fetch_open_orders($symbol, $since, $limit, array_merge($request, $params));
    }

    public function fetch_order_trades($id, $symbol = null, $since = null, $limit = null, $params = array ()) {
        $this->load_markets();
        $request = array(
            'order_id' => $id,
            // 'max_page_size' => 100,
            // 'cursor' => 'string', // pointer specifying the position from which the next pages should be returned
        );
        if ($limit !== null) {
            $request['max_page_size'] = $limit;
        }
        $response = $this->privateGetAccountOrdersOrderIdTrades (array_merge($request, $params));
        //
        //     {
        //         "trade_history" => array(
        //             {
        //                 "trade" => array(
        //                     "trade_id" => "2b42efcd-d5b7-4a56-8e12-b69ffd68c5ef",
        //                     "order_id" => "66756a10-3e86-48f4-9678-b634c4b135b2",
        //                     "account_id" => "c2d0076a-c20d-41f8-9e9a-1a1d028b2b58",
        //                     "amount" => "1234.5678",
        //                     "side" => "BUY",
        //                     "instrument_code" => "BTC_EUR",
        //                     "price" => "1234.5678",
        //                     "time" => "2019-08-24T14:15:22Z",
        //                     "price_tick_sequence" => 0,
        //                     "sequence" => 123456789
        //                 ),
        //                 "fee" => {
        //                     "fee_amount" => "1234.5678",
        //                     "fee_percentage" => "1234.5678",
        //                     "fee_group_id" => "default",
        //                     "running_trading_volume" => "1234.5678",
        //                     "fee_currency" => "BTC",
        //                     "fee_type" => "TAKER"
        //                 }
        //             }
        //         ),
        //         "max_page_size" => 0,
        //         "cursor" => "string"
        //     }
        //
        $tradeHistory = $this->safe_value($response, 'trade_history', array());
        $market = null;
        if ($symbol !== null) {
            $market = $this->market($symbol);
        }
        return $this->parse_trades($tradeHistory, $market, $since, $limit);
    }

    public function fetch_my_trades($symbol = null, $since = null, $limit = null, $params = array ()) {
        $this->load_markets();
        $request = array(
            // 'from' => $this->iso8601($since),
            // 'to' => $this->iso8601($this->milliseconds()), // max range is 100 days
            // 'instrument_code' => $market['id'],
            // 'max_page_size' => 100,
            // 'cursor' => 'string', // pointer specifying the position from which the next pages should be returned
        );
        $market = null;
        if ($symbol !== null) {
            $market = $this->market($symbol);
            $request['instrument_code'] = $market['id'];
        }
        if ($since !== null) {
            $to = $this->safe_string($params, 'to');
            if ($to === null) {
                throw new ArgumentsRequired($this->id . ' fetchMyTrades() requires a "$to" iso8601 string param with the $since argument is specified, max range is 100 days');
            }
            $request['from'] = $this->iso8601($since);
        }
        if ($limit !== null) {
            $request['max_page_size'] = $limit;
        }
        $response = $this->privateGetAccountTrades (array_merge($request, $params));
        //
        //     {
        //         "trade_history" => array(
        //             {
        //                 "trade" => array(
        //                     "trade_id" => "2b42efcd-d5b7-4a56-8e12-b69ffd68c5ef",
        //                     "order_id" => "66756a10-3e86-48f4-9678-b634c4b135b2",
        //                     "account_id" => "c2d0076a-c20d-41f8-9e9a-1a1d028b2b58",
        //                     "amount" => "1234.5678",
        //                     "side" => "BUY",
        //                     "instrument_code" => "BTC_EUR",
        //                     "price" => "1234.5678",
        //                     "time" => "2019-08-24T14:15:22Z",
        //                     "price_tick_sequence" => 0,
        //                     "sequence" => 123456789
        //                 ),
        //                 "fee" => {
        //                     "fee_amount" => "1234.5678",
        //                     "fee_percentage" => "1234.5678",
        //                     "fee_group_id" => "default",
        //                     "running_trading_volume" => "1234.5678",
        //                     "fee_currency" => "BTC",
        //                     "fee_type" => "TAKER"
        //                 }
        //             }
        //         ),
        //         "max_page_size" => 0,
        //         "cursor" => "string"
        //     }
        //
        $tradeHistory = $this->safe_value($response, 'trade_history', array());
        return $this->parse_trades($tradeHistory, $market, $since, $limit);
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $url = $this->urls['api'][$api] . '/' . $this->version . '/' . $this->implode_params($path, $params);
        $query = $this->omit($params, $this->extract_params($path));
        if ($api === 'public') {
            if ($query) {
                $url .= '?' . $this->urlencode($query);
            }
        } else if ($api === 'private') {
            $this->check_required_credentials();
            $headers = array(
                'Accept' => 'application/json',
                'Authorization' => 'Bearer ' . $this->apiKey,
            );
            if ($method === 'POST') {
                $body = $this->json($query);
                $headers['Content-Type'] = 'application/json';
            } else {
                if ($query) {
                    $url .= '?' . $this->urlencode($query);
                }
            }
        }
        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;
        }
        //
        //     array("error":"MISSING_FROM_PARAM")
        //     array("error":"MISSING_TO_PARAM")
        //     array("error":"CANDLESTICKS_TIME_RANGE_TOO_BIG")
        //
        $message = $this->safe_string($response, 'error');
        if ($message !== null) {
            $feedback = $this->id . ' ' . $body;
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $message, $feedback);
            $this->throw_broadly_matched_exception($this->exceptions['broad'], $message, $feedback);
            throw new ExchangeError($feedback); // unknown $message
        }
    }
}
