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

# 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

from ccxt.base.exchange import Exchange
import hashlib
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import InvalidAddress
from ccxt.base.errors import OrderNotFound
from ccxt.base.precise import Precise


class therock(Exchange):

    def describe(self):
        return self.deep_extend(super(therock, self).describe(), {
            'id': 'therock',
            'name': 'TheRockTrading',
            'countries': ['MT'],
            'rateLimit': 1000,
            'version': 'v1',
            'has': {
                'cancelOrder': True,
                'CORS': None,
                'createOrder': True,
                'fetchBalance': True,
                'fetchClosedOrders': True,
                'fetchDeposits': True,
                'fetchLedger': True,
                'fetchMarkets': True,
                'fetchMyTrades': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTrades': True,
                'fetchTransactions': 'emulated',
                'fetchWithdrawals': True,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766869-75057fa2-5ee9-11e7-9a6f-13e641fa4707.jpg',
                'api': 'https://api.therocktrading.com',
                'www': 'https://therocktrading.com',
                'doc': [
                    'https://api.therocktrading.com/doc/v1/index.html',
                    'https://api.therocktrading.com/doc/',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'funds',
                        'funds/{id}/orderbook',
                        'funds/{id}/ticker',
                        'funds/{id}/trades',
                        'funds/tickers',
                    ],
                },
                'private': {
                    'get': [
                        'balances',
                        'balances/{id}',
                        'discounts',
                        'discounts/{id}',
                        'funds',
                        'funds/{id}',
                        'funds/{id}/trades',
                        'funds/{fund_id}/orders',
                        'funds/{fund_id}/orders/{id}',
                        'funds/{fund_id}/position_balances',
                        'funds/{fund_id}/positions',
                        'funds/{fund_id}/positions/{id}',
                        'transactions',
                        'transactions/{id}',
                        'withdraw_limits/{id}',
                        'withdraw_limits',
                    ],
                    'post': [
                        'atms/withdraw',
                        'funds/{fund_id}/orders',
                    ],
                    'delete': [
                        'funds/{fund_id}/orders/{id}',
                        'funds/{fund_id}/orders/remove_all',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': self.parse_number('0.002'),
                    'taker': self.parse_number('0.002'),
                },
                'funding': {
                    'tierBased': False,
                    'percentage': False,
                    'withdraw': {},
                    'deposit': {
                        'BTC': 0,
                        'BCH': 0,
                        'PPC': 0,
                        'ETH': 0,
                        'ZEC': 0,
                        'LTC': 0,
                        'EUR': 0,
                    },
                },
            },
            'exceptions': {
                'exact': {
                    'Request already running': BadRequest,
                    'cannot specify multiple address types': BadRequest,
                    'Currency is not included in the list': BadRequest,
                    'Record not found': OrderNotFound,
                },
                'broad': {
                    'before must be greater than after param': BadRequest,
                    'must be shorter than 60 days': BadRequest,
                    'must be a multiple of(period param) in minutes': BadRequest,
                    'Address allocation limit reached for currency': InvalidAddress,
                    'is not a valid value for param currency': BadRequest,
                    ' is invalid': InvalidAddress,
                },
            },
        })

    def fetch_markets(self, params={}):
        response = self.publicGetFunds(params)
        #
        #     {funds: [{                     id:   "BTCEUR",
        #                              description:   "Trade Bitcoin with Euro",
        #                                     type:   "currency",
        #                            base_currency:   "EUR",
        #                           trade_currency:   "BTC",
        #                                  buy_fee:    0.2,
        #                                 sell_fee:    0.2,
        #                      minimum_price_offer:    0.01,
        #                   minimum_quantity_offer:    0.0005,
        #                   base_currency_decimals:    2,
        #                  trade_currency_decimals:    4,
        #                                leverages: []                           },
        #                {                     id:   "LTCEUR",
        #                              description:   "Trade Litecoin with Euro",
        #                                     type:   "currency",
        #                            base_currency:   "EUR",
        #                           trade_currency:   "LTC",
        #                                  buy_fee:    0.2,
        #                                 sell_fee:    0.2,
        #                      minimum_price_offer:    0.01,
        #                   minimum_quantity_offer:    0.01,
        #                   base_currency_decimals:    2,
        #                  trade_currency_decimals:    2,
        #                                leverages: []                            }]}
        #
        markets = self.safe_value(response, 'funds')
        result = []
        if markets is None:
            raise ExchangeError(self.id + ' fetchMarkets got an unexpected response')
        else:
            for i in range(0, len(markets)):
                market = markets[i]
                id = self.safe_string(market, 'id')
                baseId = self.safe_string(market, 'trade_currency')
                quoteId = self.safe_string(market, 'base_currency')
                base = self.safe_currency_code(baseId)
                quote = self.safe_currency_code(quoteId)
                symbol = base + '/' + quote
                buy_fee = self.safe_number(market, 'buy_fee')
                sell_fee = self.safe_number(market, 'sell_fee')
                taker = max(buy_fee, sell_fee)
                taker = taker / 100
                maker = taker
                result.append({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': market,
                    'type': 'spot',
                    'spot': True,
                    'margin': False,
                    'future': False,
                    'swap': False,
                    'option': False,
                    'optionType': None,
                    'strike': None,
                    'linear': None,
                    'inverse': None,
                    'contract': False,
                    'contractSize': None,
                    'settle': None,
                    'settleId': None,
                    'expiry': None,
                    'expiryDatetime': None,
                    'active': True,
                    'maker': maker,
                    'taker': taker,
                    'precision': {
                        'amount': self.safe_integer(market, 'trade_currency_decimals'),
                        'price': self.safe_integer(market, 'base_currency_decimals'),
                    },
                    'limits': {
                        'amount': {
                            'min': self.safe_number(market, 'minimum_quantity_offer'),
                            'max': None,
                        },
                        'price': {
                            'min': self.safe_number(market, 'minimum_price_offer'),
                            'max': None,
                        },
                        'cost': {
                            'min': None,
                            'max': None,
                        },
                    },
                })
        return result

    def parse_balance(self, response):
        balances = self.safe_value(response, 'balances', [])
        result = {'info': response}
        for i in range(0, len(balances)):
            balance = balances[i]
            currencyId = self.safe_string(balance, 'currency')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['free'] = self.safe_string(balance, 'trading_balance')
            account['total'] = self.safe_string(balance, 'balance')
            result[code] = account
        return self.safe_balance(result)

    def fetch_balance(self, params={}):
        self.load_markets()
        response = self.privateGetBalances(params)
        return self.parse_balance(response)

    def fetch_order_book(self, symbol, limit=None, params={}):
        self.load_markets()
        request = {
            'id': self.market_id(symbol),
        }
        orderbook = self.publicGetFundsIdOrderbook(self.extend(request, params))
        timestamp = self.parse8601(self.safe_string(orderbook, 'date'))
        return self.parse_order_book(orderbook, symbol, timestamp, 'bids', 'asks', 'price', 'amount')

    def parse_ticker(self, ticker, market=None):
        #
        #     {
        #         "date":"2022-01-16T00:05:08.192Z",
        #         "fund_id":"ETHBTC",
        #         "bid":0.07707802,
        #         "ask":0.07733404,
        #         "last":0.07739053,
        #         "open":0.07628192,
        #         "close":0.07687651,
        #         "low":0.07612047,
        #         "high":0.07703306,
        #         "volume":1.10179665,
        #         "volume_traded":14.273
        #     }
        #
        timestamp = self.parse8601(self.safe_string(ticker, 'date'))
        market = self.safe_market(None, market)
        last = self.safe_string(ticker, 'last')
        return self.safe_ticker({
            'symbol': market['symbol'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'high'),
            'low': self.safe_string(ticker, 'low'),
            'bid': self.safe_string(ticker, 'bid'),
            'bidVolume': None,
            'ask': self.safe_string(ticker, 'ask'),
            'askVolume': None,
            'vwap': None,
            'open': self.safe_string(ticker, 'open'),
            'close': last,
            'last': last,
            'previousClose': self.safe_string(ticker, 'close'),  # previous day close, if any
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': self.safe_string(ticker, 'volume_traded'),
            'quoteVolume': self.safe_string(ticker, 'volume'),
            'info': ticker,
        }, market, False)

    def fetch_tickers(self, symbols=None, params={}):
        self.load_markets()
        response = self.publicGetFundsTickers(params)
        tickers = self.index_by(response['tickers'], 'fund_id')
        ids = list(tickers.keys())
        result = {}
        for i in range(0, len(ids)):
            id = ids[i]
            market = self.safe_market(id)
            symbol = market['symbol']
            ticker = tickers[id]
            result[symbol] = self.parse_ticker(ticker, market)
        return self.filter_by_array(result, 'symbol', symbols)

    def fetch_ticker(self, symbol, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'id': market['id'],
        }
        response = self.publicGetFundsIdTicker(self.extend(request, params))
        #
        #     {
        #         "date":"2022-01-16T00:05:08.192Z",
        #         "fund_id":"ETHBTC",
        #         "bid":0.07707802,
        #         "ask":0.07733404,
        #         "last":0.07739053,
        #         "open":0.07628192,
        #         "close":0.07687651,
        #         "low":0.07612047,
        #         "high":0.07703306,
        #         "volume":1.10179665,
        #         "volume_traded":14.273
        #     }
        #
        return self.parse_ticker(response, market)

    def parse_trade(self, trade, market=None):
        #
        # fetchTrades, fetchOrder trades
        #
        #     {     id:  4493548,
        #       fund_id: "ETHBTC",
        #        amount:  0.203,
        #         price:  0.02783576,
        #          side: "buy",
        #          dark:  False,
        #          date: "2018-11-30T08:19:18.236Z"}
        #
        # fetchMyTrades
        #
        #     {          id:    237338,
        #            fund_id:   "BTCEUR",
        #             amount:    0.348,
        #              price:    348,
        #               side:   "sell",
        #               dark:    False,
        #           order_id:    14920648,
        #               date:   "2015-06-03T00:49:49.000Z",
        #       transactions: [{      id:  2770768,
        #                             date: "2015-06-03T00:49:49.000Z",
        #                             type: "sold_currency_to_fund",
        #                            price:  121.1,
        #                         currency: "EUR"                       },
        #                       {      id:  2770769,
        #                             date: "2015-06-03T00:49:49.000Z",
        #                             type: "released_currency_to_fund",
        #                            price:  0.348,
        #                         currency: "BTC"                        },
        #                       {      id:  2770772,
        #                             date: "2015-06-03T00:49:49.000Z",
        #                             type: "paid_commission",
        #                            price:  0.06,
        #                         currency: "EUR",
        #                         trade_id:  440492                     }   ]}
        #
        marketId = self.safe_string(trade, 'fund_id')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.parse8601(self.safe_string(trade, 'date'))
        id = self.safe_string(trade, 'id')
        orderId = self.safe_string(trade, 'order_id')
        side = self.safe_string(trade, 'side')
        priceString = self.safe_string(trade, 'price')
        amountString = self.safe_string(trade, 'amount')
        price = self.parse_number(priceString)
        amount = self.parse_number(amountString)
        cost = self.parse_number(Precise.string_mul(priceString, amountString))
        fee = None
        feeCost = None
        transactions = self.safe_value(trade, 'transactions', [])
        transactionsByType = self.group_by(transactions, 'type')
        feeTransactions = self.safe_value(transactionsByType, 'paid_commission', [])
        for i in range(0, len(feeTransactions)):
            if feeCost is None:
                feeCost = 0
            feeCost = self.sum(feeCost, self.safe_number(feeTransactions[i], 'price'))
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': market['quote'],
            }
        return {
            'info': trade,
            'id': id,
            'order': orderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': None,
            'side': side,
            'takerOrMaker': None,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }

    def parse_ledger_entry_direction(self, direction):
        directions = {
            'affiliate_earnings': 'in',
            'atm_payment': 'in',
            'bought_currency_from_fund': 'out',
            'bought_shares': 'out',
            'paid_commission': 'out',
            'paypal_payment': 'in',
            'pos_payment': 'in',
            'released_currency_to_fund': 'out',
            'rollover_commission': 'out',
            'sold_currency_to_fund': 'in',
            'sold_shares': 'in',
            'transfer_received': 'in',
            'transfer_sent': 'out',
            'withdraw': 'out',
            # commented types will be shown as-is
            # 'acquired_currency_from_fund': '',
            # 'acquired_insurance': '',
            # 'dividend_distributed_to_holders': '',
            # 'dividend_from_shares': '',
            # 'exposed_position': '',
            # 'insurances_reimbursement': '',
            # 'lent_currency': '',
            # 'linden_lab_assessment': '',
            # 'position_transfer_received': '',
            # 'return_lent_currency': '',
            # 'returned_lent_currency': '',
            # 'the_rock_assessment': '',
        }
        return self.safe_string(directions, direction, direction)

    def parse_ledger_entry_type(self, type):
        types = {
            'affiliate_earnings': 'referral',
            'atm_payment': 'transaction',
            'bought_currency_from_fund': 'trade',
            'bought_shares': 'trade',
            'paid_commission': 'fee',
            'paypal_payment': 'transaction',
            'pos_payment': 'transaction',
            'released_currency_to_fund': 'trade',
            'rollover_commission': 'fee',
            'sold_currency_to_fund': 'trade',
            'sold_shares': 'trade',
            'transfer_received': 'transfer',
            'transfer_sent': 'transfer',
            'withdraw': 'transaction',
            # commented types will be shown as-is
            # 'acquired_currency_from_fund': '',
            # 'acquired_insurance': '',
            # 'dividend_distributed_to_holders': '',
            # 'dividend_from_shares': '',
            # 'exposed_position': '',
            # 'insurances_reimbursement': '',
            # 'lent_currency': '',
            # 'linden_lab_assessment': '',
            # 'position_transfer_received': '',
            # 'return_lent_currency': '',
            # 'returned_lent_currency': '',
            # 'the_rock_assessment': '',
        }
        return self.safe_string(types, type, type)

    def parse_ledger_entry(self, item, currency=None):
        #
        # withdrawal
        #
        #     {
        #         "id": 21311223,
        #         "date": "2015-06-30T13:55:11.000Z",
        #         "type": "withdraw",
        #         "price": 103.00,
        #         "currency": "EUR",
        #         "fund_id": null,
        #         "order_id": null,
        #         "trade_id": null,
        #         "transfer_detail": {
        #             "method": "wire_transfer",
        #             "id": "F112DD3",
        #             "recipient": "IT123456789012",
        #             "confirmations": 0
        #         }
        #     }
        #
        # deposit
        #
        #     {
        #         "id": 21311222,
        #         "date": "2015-06-30T13:55:11.000Z",
        #         "type": "atm_payment",
        #         "price": 2.01291,
        #         "currency": "BTC",
        #         "fund_id": "null",
        #         "order_id": null,
        #         "trade_id": null,
        #         "transfer_detail": {
        #             "method": "bitcoin",
        #             "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
        #             "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf",
        #             "confirmations": 3
        #         }
        #     }
        #
        # trade fee
        #
        #     {
        #         "id": 21311221,
        #         "date": "2015-06-30T13:55:11.000Z",
        #         "type": "paid_commission",
        #         "price": 0.0001,
        #         "fund_id": "BTCEUR",
        #         "order_id": 12832371,
        #         "trade_id": 12923212,
        #         "currency": "BTC",
        #         "transfer_detail": null
        #     }
        #
        id = self.safe_string(item, 'id')
        referenceId = None
        type = self.safe_string(item, 'type')
        direction = self.parse_ledger_entry_direction(type)
        type = self.parse_ledger_entry_type(type)
        if type == 'trade' or type == 'fee':
            referenceId = self.safe_string(item, 'trade_id')
        currencyId = self.safe_string(item, 'currency')
        code = self.safe_currency_code(currencyId)
        amount = self.safe_number(item, 'price')
        timestamp = self.parse8601(self.safe_string(item, 'date'))
        status = 'ok'
        return {
            'info': item,
            'id': id,
            'direction': direction,
            'account': None,
            'referenceId': referenceId,
            'referenceAccount': None,
            'type': type,
            'currency': code,
            'amount': amount,
            'before': None,
            'after': None,
            'status': status,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fee': None,
        }

    def fetch_ledger(self, code=None, since=None, limit=None, params={}):
        self.load_markets()
        request = {
            # 'page': 1,
            # 'fund_id': 'ETHBTC',  # filter by fund symbol
            # 'currency': 'BTC',  # filter by currency
            # 'after': '2015-02-06T08:47:26Z',  # filter after a certain timestamp
            # 'before': '2015-02-06T08:47:26Z',
            # 'type': 'withdraw',
            # 'order_id': '12832371',  # filter by a specific order ID
            # 'trade_id': '12923212',  # filter by a specific trade ID
            # 'transfer_method': 'bitcoin',  # wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin
            # 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm',  # filter by a specific recipient(e.g. Bitcoin address, IBAN)
            # 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa',  # filter by a specific transfer ID(e.g. Bitcoin TX hash)
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['after'] = self.iso8601(since)
        response = self.privateGetTransactions(self.extend(request, params))
        #
        #     {
        #         "transactions": [
        #             {
        #                 "id": 21311223,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "withdraw",
        #                 "price": 103.00,
        #                 "currency": "EUR",
        #                 "fund_id": null,
        #                 "order_id": null,
        #                 "trade_id": null,
        #                 "transfer_detail": {
        #                     "method": "wire_transfer",
        #                     "id": "F112DD3",
        #                     "recipient": "IT123456789012",
        #                     "confirmations": 0
        #                 }
        #             },
        #             {
        #                 "id": 21311222,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "atm_payment",
        #                 "price": 2.01291,
        #                 "currency": "BTC",
        #                 "fund_id": "null",
        #                 "order_id": null,
        #                 "trade_id": null,
        #                 "transfer_detail": {
        #                     "method": "bitcoin",
        #                     "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
        #                     "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf",
        #                     "confirmations": 3
        #                 }
        #             },
        #             {
        #                 "id": 21311221,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "paid_commission",
        #                 "price": 0.0001,
        #                 "fund_id": "BTCEUR",
        #                 "order_id": 12832371,
        #                 "trade_id": 12923212,
        #                 "currency": "BTC",
        #                 "transfer_detail": null
        #             }
        #         ],
        #         "meta": {
        #             "total_count": 1221,
        #             "first": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"},
        #             "previous": null,
        #             "current": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"},
        #             "next": {"page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2"},
        #             "last": {"page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221"}
        #         }
        #     }
        #
        transactions = self.safe_value(response, 'transactions', [])
        return self.parse_ledger(transactions, currency, since, limit)

    def parse_transaction_type(self, type):
        types = {
            'withdraw': 'withdrawal',
            'atm_payment': 'deposit',
        }
        return self.safe_string(types, type, type)

    def parse_transaction(self, transaction, currency=None):
        #
        # fetchWithdrawals
        #
        #     # fiat
        #
        #     {
        #         "id": 21311223,
        #         "date": "2015-06-30T13:55:11.000Z",
        #         "type": "withdraw",
        #         "price": 103.00,
        #         "currency": "EUR",
        #         "fund_id": null,
        #         "order_id": null,
        #         "trade_id": null,
        #         "transfer_detail": {
        #             "method": "wire_transfer",
        #             "id": "F112DD3",
        #             "recipient": "IT123456789012",
        #             "confirmations": 0
        #         }
        #     }
        #
        #     {
        #         "id": 12564223,
        #         "date": "2017-08-07T08:13:50.023Z",
        #         "note": "GB7IDL401573388",
        #         "type": "withdraw",
        #         "price": 4345.93,
        #         "fund_id": null,
        #         "currency": "EUR",
        #         "order_id": null,
        #         "trade_id": null,
        #         "transfer_detail": {
        #             "id": "EXECUTEDBUTUNCHECKED",
        #             "method": "wire_transfer",
        #             "recipient": "GB7IDL401573388",
        #             "confirmations": 0
        #         }
        #     }
        #
        #     # crypto
        #
        #     {
        #         id: 20914695,
        #         date: '2018-02-24T07:13:23.002Z',
        #         type: 'withdraw',
        #         price: 2.70883607,
        #         currency: 'BCH',
        #         fund_id: null,
        #         order_id: null,
        #         trade_id: null,
        #         note: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm',
        #         transfer_detail: {
        #             method: 'bitcoin_cash',
        #             id: '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa',
        #             recipient: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm',
        #             confirmations: 0
        #         }
        #     }
        #
        #
        # fetchDeposits
        #
        #     # fiat
        #
        #     {
        #         id: 16176632,
        #         date: '2017-11-20T21:00:13.355Z',
        #         type: 'atm_payment',
        #         price: 5000,
        #         currency: 'EUR',
        #         fund_id: null,
        #         order_id: null,
        #         trade_id: null,
        #         note: 'Mistral deposit',
        #         transfer_detail: {
        #             method: 'wire_transfer',
        #             id: '972JQ49337DX769T',
        #             recipient: null,
        #             confirmations: 0
        #         }
        #     }
        #
        #     # crypto
        #
        #     {
        #         "id": 21311222,
        #         "date": "2015-06-30T13:55:11.000Z",
        #         "type": "atm_payment",
        #         "price": 2.01291,
        #         "currency": "BTC",
        #         "fund_id": "null",
        #         "order_id": null,
        #         "trade_id": null,
        #         "transfer_detail": {
        #             "method": "bitcoin",
        #             "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
        #             "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf",
        #             "confirmations": 3
        #         }
        #     }
        #
        id = self.safe_string(transaction, 'id')
        type = self.parse_transaction_type(self.safe_string(transaction, 'type'))
        detail = self.safe_value(transaction, 'transfer_detail', {})
        method = self.safe_string(detail, 'method')
        txid = None
        address = None
        if method is not None:
            if method != 'wire_transfer':
                txid = self.safe_string(detail, 'id')
                address = self.safe_string(detail, 'recipient')
        currencyId = self.safe_string(transaction, 'currency')
        code = self.safe_currency_code(currencyId)
        amount = self.safe_number(transaction, 'price')
        timestamp = self.parse8601(self.safe_string(transaction, 'date'))
        status = 'ok'
        network = self.safe_string(detail, 'method')
        # todo parse tags
        return {
            'info': transaction,
            'id': id,
            'currency': code,
            'amount': amount,
            'network': network,
            'addressFrom': None,
            'addressTo': address,
            'address': address,
            'tagFrom': None,
            'tagTo': None,
            'tag': None,
            'status': status,
            'type': type,
            'updated': None,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fee': None,
        }

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        request = {
            'type': 'withdraw',
        }
        return self.fetch_transactions(code, since, limit, self.extend(request, params))

    def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        request = {
            'type': 'atm_payment',
        }
        return self.fetch_transactions(code, since, limit, self.extend(request, params))

    def fetch_transactions(self, code=None, since=None, limit=None, params={}):
        self.load_markets()
        request = {
            # 'page': 1,
            # 'fund_id': 'ETHBTC',  # filter by fund symbol
            # 'currency': 'BTC',  # filter by currency
            # 'after': '2015-02-06T08:47:26Z',  # filter after a certain timestamp
            # 'before': '2015-02-06T08:47:26Z',
            # 'type': 'withdraw',
            # 'order_id': '12832371',  # filter by a specific order ID
            # 'trade_id': '12923212',  # filter by a specific trade ID
            # 'transfer_method': 'bitcoin',  # wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin
            # 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm',  # filter by a specific recipient(e.g. Bitcoin address, IBAN)
            # 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa',  # filter by a specific transfer ID(e.g. Bitcoin TX hash)
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['after'] = self.iso8601(since)
        params = self.extend(request, params)
        response = self.privateGetTransactions(params)
        #
        #     {
        #         "transactions": [
        #             {
        #                 "id": 21311223,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "withdraw",
        #                 "price": 103.00,
        #                 "currency": "EUR",
        #                 "fund_id": null,
        #                 "order_id": null,
        #                 "trade_id": null,
        #                 "transfer_detail": {
        #                     "method": "wire_transfer",
        #                     "id": "F112DD3",
        #                     "recipient": "IT123456789012",
        #                     "confirmations": 0
        #                 }
        #             },
        #             {
        #                 "id": 21311222,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "atm_payment",
        #                 "price": 2.01291,
        #                 "currency": "BTC",
        #                 "fund_id": "null",
        #                 "order_id": null,
        #                 "trade_id": null,
        #                 "transfer_detail": {
        #                     "method": "bitcoin",
        #                     "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
        #                     "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf",
        #                     "confirmations": 3
        #                 }
        #             },
        #             {
        #                 "id": 21311221,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "paid_commission",
        #                 "price": 0.0001,
        #                 "fund_id": "BTCEUR",
        #                 "order_id": 12832371,
        #                 "trade_id": 12923212,
        #                 "currency": "BTC",
        #                 "transfer_detail": null
        #             }
        #         ],
        #         "meta": {
        #             "total_count": 1221,
        #             "first": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"},
        #             "previous": null,
        #             "current": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"},
        #             "next": {"page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2"},
        #             "last": {"page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221"}
        #         }
        #     }
        #
        transactions = self.safe_value(response, 'transactions', [])
        transactionTypes = ['withdraw', 'atm_payment']
        depositsAndWithdrawals = self.filter_by_array(transactions, 'type', transactionTypes, False)
        return self.parse_transactions(depositsAndWithdrawals, currency, since, limit)

    def parse_order_status(self, status):
        statuses = {
            'active': 'open',
            'executed': 'closed',
            'deleted': 'canceled',
            # don't know what self status means
            # 'conditional': '?',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        #
        #     {
        #         "id": 4325578,
        #         "fund_id":"BTCEUR",
        #         "side":"buy",
        #         "type":"limit",
        #         "status":"executed",
        #         "price":0.0102,
        #         "amount": 50.0,
        #         "amount_unfilled": 0.0,
        #         "conditional_type": null,
        #         "conditional_price": null,
        #         "date":"2015-06-03T00:49:48.000Z",
        #         "close_on": nil,
        #         "leverage": 1.0,
        #         "position_id": null,
        #         "trades": [
        #             {
        #                 "id":237338,
        #                 "fund_id":"BTCEUR",
        #                 "amount":50,
        #                 "price":0.0102,
        #                 "side":"buy",
        #                 "dark":false,
        #                 "date":"2015-06-03T00:49:49.000Z"
        #             }
        #         ]
        #     }
        #
        id = self.safe_string(order, 'id')
        marketId = self.safe_string(order, 'fund_id')
        symbol = self.safe_symbol(marketId, market)
        status = self.parse_order_status(self.safe_string(order, 'status'))
        timestamp = self.parse8601(self.safe_string(order, 'date'))
        type = self.safe_string(order, 'type')
        side = self.safe_string(order, 'side')
        amount = self.safe_number(order, 'amount')
        remaining = self.safe_number(order, 'amount_unfilled')
        filled = None
        if amount is not None:
            if remaining is not None:
                filled = amount - remaining
        price = self.safe_number(order, 'price')
        trades = self.safe_value(order, 'trades')
        cost = None
        average = None
        lastTradeTimestamp = None
        if trades is not None:
            numTrades = len(trades)
            if numTrades > 0:
                trades = self.parse_trades(trades, market, None, None, {
                    'orderId': id,
                })
                # todo: determine the cost and the average price from trades
                cost = 0
                filled = 0
                for i in range(0, numTrades):
                    trade = trades[i]
                    cost = self.sum(cost, trade['cost'])
                    filled = self.sum(filled, trade['amount'])
                if filled > 0:
                    average = cost / filled
                lastTradeTimestamp = trades[numTrades - 1]['timestamp']
            else:
                cost = 0
        stopPrice = self.safe_number(order, 'conditional_price')
        return {
            'id': id,
            'clientOrderId': None,
            'info': order,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'status': status,
            'symbol': symbol,
            'type': type,
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'average': average,
            'remaining': remaining,
            'fee': None,
            'trades': trades,
        }

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        request = {
            'status': 'active',
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        request = {
            'status': 'executed',
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'fund_id': market['id'],
            # 'after': '2015-02-06T08:47:26Z',
            # 'before': '2015-02-06T08:47:26Z'
            # 'status': 'active',  # 'executed', 'conditional'
            # 'side': 'buy',  # 'sell'
            # 'position_id': 123,  # filter orders by margin position id
        }
        if since is not None:
            request['after'] = self.iso8601(since)
        response = self.privateGetFundsFundIdOrders(self.extend(request, params))
        #
        #     {
        #         orders: [
        #             {
        #                 id: 299333648,
        #                 fund_id: 'BTCEUR',
        #                 side: 'sell',
        #                 type: 'limit',
        #                 status: 'executed',
        #                 price: 5821,
        #                 amount: 0.1,
        #                 amount_unfilled: 0,
        #                 conditional_type: null,
        #                 conditional_price: null,
        #                 date: '2018-06-18T17:38:16.129Z',
        #                 close_on: null,
        #                 dark: False,
        #                 leverage: 1,
        #                 position_id: 0
        #             }
        #         ]
        #     }
        #
        orders = self.safe_value(response, 'orders', [])
        return self.parse_orders(orders, market, since, limit)

    def fetch_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'id': id,
            'fund_id': market['id'],
        }
        response = self.privatePostFundsFundIdOrdersId(self.extend(request, params))
        return self.parse_order(response)

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        if type == 'market':
            price = 0
        request = {
            'fund_id': self.market_id(symbol),
            'side': side,
            'amount': amount,
            'price': price,
        }
        response = self.privatePostFundsFundIdOrders(self.extend(request, params))
        return self.parse_order(response)

    def cancel_order(self, id, symbol=None, params={}):
        self.load_markets()
        request = {
            'id': id,
            'fund_id': self.market_id(symbol),
        }
        response = self.privateDeleteFundsFundIdOrdersId(self.extend(request, params))
        return self.parse_order(response)

    def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'id': market['id'],
        }
        if limit is not None:
            request['per_page'] = limit  # default 25 max 200
        if since is not None:
            request['after'] = self.iso8601(since)
        response = self.privateGetFundsIdTrades(self.extend(request, params))
        #
        #     {trades: [{          id:    237338,
        #                        fund_id:   "BTCEUR",
        #                         amount:    0.348,
        #                          price:    348,
        #                           side:   "sell",
        #                           dark:    False,
        #                       order_id:    14920648,
        #                           date:   "2015-06-03T00:49:49.000Z",
        #                   transactions: [{      id:  2770768,
        #                                         date: "2015-06-03T00:49:49.000Z",
        #                                         type: "sold_currency_to_fund",
        #                                        price:  121.1,
        #                                     currency: "EUR"                       },
        #                                   {      id:  2770769,
        #                                         date: "2015-06-03T00:49:49.000Z",
        #                                         type: "released_currency_to_fund",
        #                                        price:  0.348,
        #                                     currency: "BTC"                        },
        #                                   {      id:  2770772,
        #                                         date: "2015-06-03T00:49:49.000Z",
        #                                         type: "paid_commission",
        #                                        price:  0.06,
        #                                     currency: "EUR",
        #                                     trade_id:  440492                     }   ]}],
        #         meta: {total_count:    31,
        #                       first: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1"},
        #                    previous:    null,
        #                     current: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1"},
        #                        next: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2"},
        #                        last: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2"}  }}
        #
        return self.parse_trades(response['trades'], market, since, limit)

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'id': market['id'],
        }
        if limit is not None:
            request['per_page'] = limit  # default 25 max 200
        if since is not None:
            request['after'] = self.iso8601(since)
        response = self.publicGetFundsIdTrades(self.extend(request, params))
        #
        #     {trades: [{     id:  4493548,
        #                   fund_id: "ETHBTC",
        #                    amount:  0.203,
        #                     price:  0.02783576,
        #                      side: "buy",
        #                      dark:  False,
        #                      date: "2018-11-30T08:19:18.236Z"},
        #                 {     id:  4492926,
        #                   fund_id: "ETHBTC",
        #                    amount:  0.04,
        #                     price:  0.02767034,
        #                      side: "buy",
        #                      dark:  False,
        #                      date: "2018-11-30T07:03:03.897Z"}  ],
        #         meta: {total_count:    null,
        #                       first: {page:  1,
        #                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1"},
        #                    previous:    null,
        #                     current: {page:  1,
        #                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1"},
        #                        next: {page:  2,
        #                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=2"},
        #                        last:    null                                                                   }}
        #
        return self.parse_trades(response['trades'], market, since, limit)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api'] + '/' + self.version + '/' + self.implode_params(path, params)
        query = self.omit(params, self.extract_params(path))
        headers = {} if (headers is None) else headers
        if api == 'private':
            self.check_required_credentials()
            if query:
                if method == 'POST':
                    body = self.json(query)
                    headers['Content-Type'] = 'application/json'
                else:
                    queryString = self.rawencode(query)
                    if len(queryString):
                        url += '?' + queryString
            nonce = str(self.nonce())
            auth = nonce + url
            headers['X-TRT-KEY'] = self.apiKey
            headers['X-TRT-NONCE'] = nonce
            headers['X-TRT-SIGN'] = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha512)
        elif api == 'public':
            if query:
                url += '?' + self.rawencode(query)
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return  # fallback to default error handler
        #
        #     {
        #         "errors":
        #         [
        #             {"message": ":currency is not a valid value for param currency","code": "11","meta": {"key":"currency","value":":currency"}},
        #             {"message": "Address allocation limit reached for currency :currency.","code": "13"},
        #             {"message": "Request already running", "code": "50"},
        #             {"message": "cannot specify multiple address types", "code": "12"},
        #             {"message": ":address_type is invalid", "code": "12"}
        #         ]
        #     }
        #
        errors = self.safe_value(response, 'errors', [])
        numErrors = len(errors)
        if numErrors > 0:
            feedback = self.id + ' ' + body
            # here we raise the first error we can identify
            for i in range(0, numErrors):
                error = errors[i]
                message = self.safe_string(error, 'message')
                self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
                self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            raise ExchangeError(feedback)  # unknown message

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

# 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

from ccxt.base.exchange import Exchange
import hashlib
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import InvalidAddress
from ccxt.base.errors import OrderNotFound
from ccxt.base.precise import Precise


class therock(Exchange):

    def describe(self):
        return self.deep_extend(super(therock, self).describe(), {
            'id': 'therock',
            'name': 'TheRockTrading',
            'countries': ['MT'],
            'rateLimit': 1000,
            'version': 'v1',
            'has': {
                'cancelOrder': True,
                'CORS': None,
                'createOrder': True,
                'fetchBalance': True,
                'fetchClosedOrders': True,
                'fetchDeposits': True,
                'fetchLedger': True,
                'fetchMarkets': True,
                'fetchMyTrades': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTrades': True,
                'fetchTransactions': 'emulated',
                'fetchWithdrawals': True,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766869-75057fa2-5ee9-11e7-9a6f-13e641fa4707.jpg',
                'api': 'https://api.therocktrading.com',
                'www': 'https://therocktrading.com',
                'doc': [
                    'https://api.therocktrading.com/doc/v1/index.html',
                    'https://api.therocktrading.com/doc/',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'funds',
                        'funds/{id}/orderbook',
                        'funds/{id}/ticker',
                        'funds/{id}/trades',
                        'funds/tickers',
                    ],
                },
                'private': {
                    'get': [
                        'balances',
                        'balances/{id}',
                        'discounts',
                        'discounts/{id}',
                        'funds',
                        'funds/{id}',
                        'funds/{id}/trades',
                        'funds/{fund_id}/orders',
                        'funds/{fund_id}/orders/{id}',
                        'funds/{fund_id}/position_balances',
                        'funds/{fund_id}/positions',
                        'funds/{fund_id}/positions/{id}',
                        'transactions',
                        'transactions/{id}',
                        'withdraw_limits/{id}',
                        'withdraw_limits',
                    ],
                    'post': [
                        'atms/withdraw',
                        'funds/{fund_id}/orders',
                    ],
                    'delete': [
                        'funds/{fund_id}/orders/{id}',
                        'funds/{fund_id}/orders/remove_all',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': self.parse_number('0.002'),
                    'taker': self.parse_number('0.002'),
                },
                'funding': {
                    'tierBased': False,
                    'percentage': False,
                    'withdraw': {},
                    'deposit': {
                        'BTC': 0,
                        'BCH': 0,
                        'PPC': 0,
                        'ETH': 0,
                        'ZEC': 0,
                        'LTC': 0,
                        'EUR': 0,
                    },
                },
            },
            'exceptions': {
                'exact': {
                    'Request already running': BadRequest,
                    'cannot specify multiple address types': BadRequest,
                    'Currency is not included in the list': BadRequest,
                    'Record not found': OrderNotFound,
                },
                'broad': {
                    'before must be greater than after param': BadRequest,
                    'must be shorter than 60 days': BadRequest,
                    'must be a multiple of(period param) in minutes': BadRequest,
                    'Address allocation limit reached for currency': InvalidAddress,
                    'is not a valid value for param currency': BadRequest,
                    ' is invalid': InvalidAddress,
                },
            },
        })

    def fetch_markets(self, params={}):
        response = self.publicGetFunds(params)
        #
        #     {funds: [{                     id:   "BTCEUR",
        #                              description:   "Trade Bitcoin with Euro",
        #                                     type:   "currency",
        #                            base_currency:   "EUR",
        #                           trade_currency:   "BTC",
        #                                  buy_fee:    0.2,
        #                                 sell_fee:    0.2,
        #                      minimum_price_offer:    0.01,
        #                   minimum_quantity_offer:    0.0005,
        #                   base_currency_decimals:    2,
        #                  trade_currency_decimals:    4,
        #                                leverages: []                           },
        #                {                     id:   "LTCEUR",
        #                              description:   "Trade Litecoin with Euro",
        #                                     type:   "currency",
        #                            base_currency:   "EUR",
        #                           trade_currency:   "LTC",
        #                                  buy_fee:    0.2,
        #                                 sell_fee:    0.2,
        #                      minimum_price_offer:    0.01,
        #                   minimum_quantity_offer:    0.01,
        #                   base_currency_decimals:    2,
        #                  trade_currency_decimals:    2,
        #                                leverages: []                            }]}
        #
        markets = self.safe_value(response, 'funds')
        result = []
        if markets is None:
            raise ExchangeError(self.id + ' fetchMarkets got an unexpected response')
        else:
            for i in range(0, len(markets)):
                market = markets[i]
                id = self.safe_string(market, 'id')
                baseId = self.safe_string(market, 'trade_currency')
                quoteId = self.safe_string(market, 'base_currency')
                base = self.safe_currency_code(baseId)
                quote = self.safe_currency_code(quoteId)
                symbol = base + '/' + quote
                buy_fee = self.safe_number(market, 'buy_fee')
                sell_fee = self.safe_number(market, 'sell_fee')
                taker = max(buy_fee, sell_fee)
                taker = taker / 100
                maker = taker
                result.append({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': market,
                    'type': 'spot',
                    'spot': True,
                    'margin': False,
                    'future': False,
                    'swap': False,
                    'option': False,
                    'optionType': None,
                    'strike': None,
                    'linear': None,
                    'inverse': None,
                    'contract': False,
                    'contractSize': None,
                    'settle': None,
                    'settleId': None,
                    'expiry': None,
                    'expiryDatetime': None,
                    'active': True,
                    'maker': maker,
                    'taker': taker,
                    'precision': {
                        'amount': self.safe_integer(market, 'trade_currency_decimals'),
                        'price': self.safe_integer(market, 'base_currency_decimals'),
                    },
                    'limits': {
                        'amount': {
                            'min': self.safe_number(market, 'minimum_quantity_offer'),
                            'max': None,
                        },
                        'price': {
                            'min': self.safe_number(market, 'minimum_price_offer'),
                            'max': None,
                        },
                        'cost': {
                            'min': None,
                            'max': None,
                        },
                    },
                })
        return result

    def parse_balance(self, response):
        balances = self.safe_value(response, 'balances', [])
        result = {'info': response}
        for i in range(0, len(balances)):
            balance = balances[i]
            currencyId = self.safe_string(balance, 'currency')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['free'] = self.safe_string(balance, 'trading_balance')
            account['total'] = self.safe_string(balance, 'balance')
            result[code] = account
        return self.safe_balance(result)

    def fetch_balance(self, params={}):
        self.load_markets()
        response = self.privateGetBalances(params)
        return self.parse_balance(response)

    def fetch_order_book(self, symbol, limit=None, params={}):
        self.load_markets()
        request = {
            'id': self.market_id(symbol),
        }
        orderbook = self.publicGetFundsIdOrderbook(self.extend(request, params))
        timestamp = self.parse8601(self.safe_string(orderbook, 'date'))
        return self.parse_order_book(orderbook, symbol, timestamp, 'bids', 'asks', 'price', 'amount')

    def parse_ticker(self, ticker, market=None):
        #
        #     {
        #         "date":"2022-01-16T00:05:08.192Z",
        #         "fund_id":"ETHBTC",
        #         "bid":0.07707802,
        #         "ask":0.07733404,
        #         "last":0.07739053,
        #         "open":0.07628192,
        #         "close":0.07687651,
        #         "low":0.07612047,
        #         "high":0.07703306,
        #         "volume":1.10179665,
        #         "volume_traded":14.273
        #     }
        #
        timestamp = self.parse8601(self.safe_string(ticker, 'date'))
        market = self.safe_market(None, market)
        last = self.safe_string(ticker, 'last')
        return self.safe_ticker({
            'symbol': market['symbol'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'high'),
            'low': self.safe_string(ticker, 'low'),
            'bid': self.safe_string(ticker, 'bid'),
            'bidVolume': None,
            'ask': self.safe_string(ticker, 'ask'),
            'askVolume': None,
            'vwap': None,
            'open': self.safe_string(ticker, 'open'),
            'close': last,
            'last': last,
            'previousClose': self.safe_string(ticker, 'close'),  # previous day close, if any
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': self.safe_string(ticker, 'volume_traded'),
            'quoteVolume': self.safe_string(ticker, 'volume'),
            'info': ticker,
        }, market, False)

    def fetch_tickers(self, symbols=None, params={}):
        self.load_markets()
        response = self.publicGetFundsTickers(params)
        tickers = self.index_by(response['tickers'], 'fund_id')
        ids = list(tickers.keys())
        result = {}
        for i in range(0, len(ids)):
            id = ids[i]
            market = self.safe_market(id)
            symbol = market['symbol']
            ticker = tickers[id]
            result[symbol] = self.parse_ticker(ticker, market)
        return self.filter_by_array(result, 'symbol', symbols)

    def fetch_ticker(self, symbol, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'id': market['id'],
        }
        response = self.publicGetFundsIdTicker(self.extend(request, params))
        #
        #     {
        #         "date":"2022-01-16T00:05:08.192Z",
        #         "fund_id":"ETHBTC",
        #         "bid":0.07707802,
        #         "ask":0.07733404,
        #         "last":0.07739053,
        #         "open":0.07628192,
        #         "close":0.07687651,
        #         "low":0.07612047,
        #         "high":0.07703306,
        #         "volume":1.10179665,
        #         "volume_traded":14.273
        #     }
        #
        return self.parse_ticker(response, market)

    def parse_trade(self, trade, market=None):
        #
        # fetchTrades, fetchOrder trades
        #
        #     {     id:  4493548,
        #       fund_id: "ETHBTC",
        #        amount:  0.203,
        #         price:  0.02783576,
        #          side: "buy",
        #          dark:  False,
        #          date: "2018-11-30T08:19:18.236Z"}
        #
        # fetchMyTrades
        #
        #     {          id:    237338,
        #            fund_id:   "BTCEUR",
        #             amount:    0.348,
        #              price:    348,
        #               side:   "sell",
        #               dark:    False,
        #           order_id:    14920648,
        #               date:   "2015-06-03T00:49:49.000Z",
        #       transactions: [{      id:  2770768,
        #                             date: "2015-06-03T00:49:49.000Z",
        #                             type: "sold_currency_to_fund",
        #                            price:  121.1,
        #                         currency: "EUR"                       },
        #                       {      id:  2770769,
        #                             date: "2015-06-03T00:49:49.000Z",
        #                             type: "released_currency_to_fund",
        #                            price:  0.348,
        #                         currency: "BTC"                        },
        #                       {      id:  2770772,
        #                             date: "2015-06-03T00:49:49.000Z",
        #                             type: "paid_commission",
        #                            price:  0.06,
        #                         currency: "EUR",
        #                         trade_id:  440492                     }   ]}
        #
        marketId = self.safe_string(trade, 'fund_id')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.parse8601(self.safe_string(trade, 'date'))
        id = self.safe_string(trade, 'id')
        orderId = self.safe_string(trade, 'order_id')
        side = self.safe_string(trade, 'side')
        priceString = self.safe_string(trade, 'price')
        amountString = self.safe_string(trade, 'amount')
        price = self.parse_number(priceString)
        amount = self.parse_number(amountString)
        cost = self.parse_number(Precise.string_mul(priceString, amountString))
        fee = None
        feeCost = None
        transactions = self.safe_value(trade, 'transactions', [])
        transactionsByType = self.group_by(transactions, 'type')
        feeTransactions = self.safe_value(transactionsByType, 'paid_commission', [])
        for i in range(0, len(feeTransactions)):
            if feeCost is None:
                feeCost = 0
            feeCost = self.sum(feeCost, self.safe_number(feeTransactions[i], 'price'))
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': market['quote'],
            }
        return {
            'info': trade,
            'id': id,
            'order': orderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': None,
            'side': side,
            'takerOrMaker': None,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }

    def parse_ledger_entry_direction(self, direction):
        directions = {
            'affiliate_earnings': 'in',
            'atm_payment': 'in',
            'bought_currency_from_fund': 'out',
            'bought_shares': 'out',
            'paid_commission': 'out',
            'paypal_payment': 'in',
            'pos_payment': 'in',
            'released_currency_to_fund': 'out',
            'rollover_commission': 'out',
            'sold_currency_to_fund': 'in',
            'sold_shares': 'in',
            'transfer_received': 'in',
            'transfer_sent': 'out',
            'withdraw': 'out',
            # commented types will be shown as-is
            # 'acquired_currency_from_fund': '',
            # 'acquired_insurance': '',
            # 'dividend_distributed_to_holders': '',
            # 'dividend_from_shares': '',
            # 'exposed_position': '',
            # 'insurances_reimbursement': '',
            # 'lent_currency': '',
            # 'linden_lab_assessment': '',
            # 'position_transfer_received': '',
            # 'return_lent_currency': '',
            # 'returned_lent_currency': '',
            # 'the_rock_assessment': '',
        }
        return self.safe_string(directions, direction, direction)

    def parse_ledger_entry_type(self, type):
        types = {
            'affiliate_earnings': 'referral',
            'atm_payment': 'transaction',
            'bought_currency_from_fund': 'trade',
            'bought_shares': 'trade',
            'paid_commission': 'fee',
            'paypal_payment': 'transaction',
            'pos_payment': 'transaction',
            'released_currency_to_fund': 'trade',
            'rollover_commission': 'fee',
            'sold_currency_to_fund': 'trade',
            'sold_shares': 'trade',
            'transfer_received': 'transfer',
            'transfer_sent': 'transfer',
            'withdraw': 'transaction',
            # commented types will be shown as-is
            # 'acquired_currency_from_fund': '',
            # 'acquired_insurance': '',
            # 'dividend_distributed_to_holders': '',
            # 'dividend_from_shares': '',
            # 'exposed_position': '',
            # 'insurances_reimbursement': '',
            # 'lent_currency': '',
            # 'linden_lab_assessment': '',
            # 'position_transfer_received': '',
            # 'return_lent_currency': '',
            # 'returned_lent_currency': '',
            # 'the_rock_assessment': '',
        }
        return self.safe_string(types, type, type)

    def parse_ledger_entry(self, item, currency=None):
        #
        # withdrawal
        #
        #     {
        #         "id": 21311223,
        #         "date": "2015-06-30T13:55:11.000Z",
        #         "type": "withdraw",
        #         "price": 103.00,
        #         "currency": "EUR",
        #         "fund_id": null,
        #         "order_id": null,
        #         "trade_id": null,
        #         "transfer_detail": {
        #             "method": "wire_transfer",
        #             "id": "F112DD3",
        #             "recipient": "IT123456789012",
        #             "confirmations": 0
        #         }
        #     }
        #
        # deposit
        #
        #     {
        #         "id": 21311222,
        #         "date": "2015-06-30T13:55:11.000Z",
        #         "type": "atm_payment",
        #         "price": 2.01291,
        #         "currency": "BTC",
        #         "fund_id": "null",
        #         "order_id": null,
        #         "trade_id": null,
        #         "transfer_detail": {
        #             "method": "bitcoin",
        #             "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
        #             "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf",
        #             "confirmations": 3
        #         }
        #     }
        #
        # trade fee
        #
        #     {
        #         "id": 21311221,
        #         "date": "2015-06-30T13:55:11.000Z",
        #         "type": "paid_commission",
        #         "price": 0.0001,
        #         "fund_id": "BTCEUR",
        #         "order_id": 12832371,
        #         "trade_id": 12923212,
        #         "currency": "BTC",
        #         "transfer_detail": null
        #     }
        #
        id = self.safe_string(item, 'id')
        referenceId = None
        type = self.safe_string(item, 'type')
        direction = self.parse_ledger_entry_direction(type)
        type = self.parse_ledger_entry_type(type)
        if type == 'trade' or type == 'fee':
            referenceId = self.safe_string(item, 'trade_id')
        currencyId = self.safe_string(item, 'currency')
        code = self.safe_currency_code(currencyId)
        amount = self.safe_number(item, 'price')
        timestamp = self.parse8601(self.safe_string(item, 'date'))
        status = 'ok'
        return {
            'info': item,
            'id': id,
            'direction': direction,
            'account': None,
            'referenceId': referenceId,
            'referenceAccount': None,
            'type': type,
            'currency': code,
            'amount': amount,
            'before': None,
            'after': None,
            'status': status,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fee': None,
        }

    def fetch_ledger(self, code=None, since=None, limit=None, params={}):
        self.load_markets()
        request = {
            # 'page': 1,
            # 'fund_id': 'ETHBTC',  # filter by fund symbol
            # 'currency': 'BTC',  # filter by currency
            # 'after': '2015-02-06T08:47:26Z',  # filter after a certain timestamp
            # 'before': '2015-02-06T08:47:26Z',
            # 'type': 'withdraw',
            # 'order_id': '12832371',  # filter by a specific order ID
            # 'trade_id': '12923212',  # filter by a specific trade ID
            # 'transfer_method': 'bitcoin',  # wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin
            # 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm',  # filter by a specific recipient(e.g. Bitcoin address, IBAN)
            # 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa',  # filter by a specific transfer ID(e.g. Bitcoin TX hash)
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['after'] = self.iso8601(since)
        response = self.privateGetTransactions(self.extend(request, params))
        #
        #     {
        #         "transactions": [
        #             {
        #                 "id": 21311223,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "withdraw",
        #                 "price": 103.00,
        #                 "currency": "EUR",
        #                 "fund_id": null,
        #                 "order_id": null,
        #                 "trade_id": null,
        #                 "transfer_detail": {
        #                     "method": "wire_transfer",
        #                     "id": "F112DD3",
        #                     "recipient": "IT123456789012",
        #                     "confirmations": 0
        #                 }
        #             },
        #             {
        #                 "id": 21311222,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "atm_payment",
        #                 "price": 2.01291,
        #                 "currency": "BTC",
        #                 "fund_id": "null",
        #                 "order_id": null,
        #                 "trade_id": null,
        #                 "transfer_detail": {
        #                     "method": "bitcoin",
        #                     "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
        #                     "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf",
        #                     "confirmations": 3
        #                 }
        #             },
        #             {
        #                 "id": 21311221,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "paid_commission",
        #                 "price": 0.0001,
        #                 "fund_id": "BTCEUR",
        #                 "order_id": 12832371,
        #                 "trade_id": 12923212,
        #                 "currency": "BTC",
        #                 "transfer_detail": null
        #             }
        #         ],
        #         "meta": {
        #             "total_count": 1221,
        #             "first": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"},
        #             "previous": null,
        #             "current": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"},
        #             "next": {"page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2"},
        #             "last": {"page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221"}
        #         }
        #     }
        #
        transactions = self.safe_value(response, 'transactions', [])
        return self.parse_ledger(transactions, currency, since, limit)

    def parse_transaction_type(self, type):
        types = {
            'withdraw': 'withdrawal',
            'atm_payment': 'deposit',
        }
        return self.safe_string(types, type, type)

    def parse_transaction(self, transaction, currency=None):
        #
        # fetchWithdrawals
        #
        #     # fiat
        #
        #     {
        #         "id": 21311223,
        #         "date": "2015-06-30T13:55:11.000Z",
        #         "type": "withdraw",
        #         "price": 103.00,
        #         "currency": "EUR",
        #         "fund_id": null,
        #         "order_id": null,
        #         "trade_id": null,
        #         "transfer_detail": {
        #             "method": "wire_transfer",
        #             "id": "F112DD3",
        #             "recipient": "IT123456789012",
        #             "confirmations": 0
        #         }
        #     }
        #
        #     {
        #         "id": 12564223,
        #         "date": "2017-08-07T08:13:50.023Z",
        #         "note": "GB7IDL401573388",
        #         "type": "withdraw",
        #         "price": 4345.93,
        #         "fund_id": null,
        #         "currency": "EUR",
        #         "order_id": null,
        #         "trade_id": null,
        #         "transfer_detail": {
        #             "id": "EXECUTEDBUTUNCHECKED",
        #             "method": "wire_transfer",
        #             "recipient": "GB7IDL401573388",
        #             "confirmations": 0
        #         }
        #     }
        #
        #     # crypto
        #
        #     {
        #         id: 20914695,
        #         date: '2018-02-24T07:13:23.002Z',
        #         type: 'withdraw',
        #         price: 2.70883607,
        #         currency: 'BCH',
        #         fund_id: null,
        #         order_id: null,
        #         trade_id: null,
        #         note: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm',
        #         transfer_detail: {
        #             method: 'bitcoin_cash',
        #             id: '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa',
        #             recipient: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm',
        #             confirmations: 0
        #         }
        #     }
        #
        #
        # fetchDeposits
        #
        #     # fiat
        #
        #     {
        #         id: 16176632,
        #         date: '2017-11-20T21:00:13.355Z',
        #         type: 'atm_payment',
        #         price: 5000,
        #         currency: 'EUR',
        #         fund_id: null,
        #         order_id: null,
        #         trade_id: null,
        #         note: 'Mistral deposit',
        #         transfer_detail: {
        #             method: 'wire_transfer',
        #             id: '972JQ49337DX769T',
        #             recipient: null,
        #             confirmations: 0
        #         }
        #     }
        #
        #     # crypto
        #
        #     {
        #         "id": 21311222,
        #         "date": "2015-06-30T13:55:11.000Z",
        #         "type": "atm_payment",
        #         "price": 2.01291,
        #         "currency": "BTC",
        #         "fund_id": "null",
        #         "order_id": null,
        #         "trade_id": null,
        #         "transfer_detail": {
        #             "method": "bitcoin",
        #             "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
        #             "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf",
        #             "confirmations": 3
        #         }
        #     }
        #
        id = self.safe_string(transaction, 'id')
        type = self.parse_transaction_type(self.safe_string(transaction, 'type'))
        detail = self.safe_value(transaction, 'transfer_detail', {})
        method = self.safe_string(detail, 'method')
        txid = None
        address = None
        if method is not None:
            if method != 'wire_transfer':
                txid = self.safe_string(detail, 'id')
                address = self.safe_string(detail, 'recipient')
        currencyId = self.safe_string(transaction, 'currency')
        code = self.safe_currency_code(currencyId)
        amount = self.safe_number(transaction, 'price')
        timestamp = self.parse8601(self.safe_string(transaction, 'date'))
        status = 'ok'
        network = self.safe_string(detail, 'method')
        # todo parse tags
        return {
            'info': transaction,
            'id': id,
            'currency': code,
            'amount': amount,
            'network': network,
            'addressFrom': None,
            'addressTo': address,
            'address': address,
            'tagFrom': None,
            'tagTo': None,
            'tag': None,
            'status': status,
            'type': type,
            'updated': None,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fee': None,
        }

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        request = {
            'type': 'withdraw',
        }
        return self.fetch_transactions(code, since, limit, self.extend(request, params))

    def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        request = {
            'type': 'atm_payment',
        }
        return self.fetch_transactions(code, since, limit, self.extend(request, params))

    def fetch_transactions(self, code=None, since=None, limit=None, params={}):
        self.load_markets()
        request = {
            # 'page': 1,
            # 'fund_id': 'ETHBTC',  # filter by fund symbol
            # 'currency': 'BTC',  # filter by currency
            # 'after': '2015-02-06T08:47:26Z',  # filter after a certain timestamp
            # 'before': '2015-02-06T08:47:26Z',
            # 'type': 'withdraw',
            # 'order_id': '12832371',  # filter by a specific order ID
            # 'trade_id': '12923212',  # filter by a specific trade ID
            # 'transfer_method': 'bitcoin',  # wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin
            # 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm',  # filter by a specific recipient(e.g. Bitcoin address, IBAN)
            # 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa',  # filter by a specific transfer ID(e.g. Bitcoin TX hash)
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['after'] = self.iso8601(since)
        params = self.extend(request, params)
        response = self.privateGetTransactions(params)
        #
        #     {
        #         "transactions": [
        #             {
        #                 "id": 21311223,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "withdraw",
        #                 "price": 103.00,
        #                 "currency": "EUR",
        #                 "fund_id": null,
        #                 "order_id": null,
        #                 "trade_id": null,
        #                 "transfer_detail": {
        #                     "method": "wire_transfer",
        #                     "id": "F112DD3",
        #                     "recipient": "IT123456789012",
        #                     "confirmations": 0
        #                 }
        #             },
        #             {
        #                 "id": 21311222,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "atm_payment",
        #                 "price": 2.01291,
        #                 "currency": "BTC",
        #                 "fund_id": "null",
        #                 "order_id": null,
        #                 "trade_id": null,
        #                 "transfer_detail": {
        #                     "method": "bitcoin",
        #                     "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
        #                     "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf",
        #                     "confirmations": 3
        #                 }
        #             },
        #             {
        #                 "id": 21311221,
        #                 "date": "2015-06-30T13:55:11.000Z",
        #                 "type": "paid_commission",
        #                 "price": 0.0001,
        #                 "fund_id": "BTCEUR",
        #                 "order_id": 12832371,
        #                 "trade_id": 12923212,
        #                 "currency": "BTC",
        #                 "transfer_detail": null
        #             }
        #         ],
        #         "meta": {
        #             "total_count": 1221,
        #             "first": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"},
        #             "previous": null,
        #             "current": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"},
        #             "next": {"page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2"},
        #             "last": {"page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221"}
        #         }
        #     }
        #
        transactions = self.safe_value(response, 'transactions', [])
        transactionTypes = ['withdraw', 'atm_payment']
        depositsAndWithdrawals = self.filter_by_array(transactions, 'type', transactionTypes, False)
        return self.parse_transactions(depositsAndWithdrawals, currency, since, limit)

    def parse_order_status(self, status):
        statuses = {
            'active': 'open',
            'executed': 'closed',
            'deleted': 'canceled',
            # don't know what self status means
            # 'conditional': '?',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        #
        #     {
        #         "id": 4325578,
        #         "fund_id":"BTCEUR",
        #         "side":"buy",
        #         "type":"limit",
        #         "status":"executed",
        #         "price":0.0102,
        #         "amount": 50.0,
        #         "amount_unfilled": 0.0,
        #         "conditional_type": null,
        #         "conditional_price": null,
        #         "date":"2015-06-03T00:49:48.000Z",
        #         "close_on": nil,
        #         "leverage": 1.0,
        #         "position_id": null,
        #         "trades": [
        #             {
        #                 "id":237338,
        #                 "fund_id":"BTCEUR",
        #                 "amount":50,
        #                 "price":0.0102,
        #                 "side":"buy",
        #                 "dark":false,
        #                 "date":"2015-06-03T00:49:49.000Z"
        #             }
        #         ]
        #     }
        #
        id = self.safe_string(order, 'id')
        marketId = self.safe_string(order, 'fund_id')
        symbol = self.safe_symbol(marketId, market)
        status = self.parse_order_status(self.safe_string(order, 'status'))
        timestamp = self.parse8601(self.safe_string(order, 'date'))
        type = self.safe_string(order, 'type')
        side = self.safe_string(order, 'side')
        amount = self.safe_number(order, 'amount')
        remaining = self.safe_number(order, 'amount_unfilled')
        filled = None
        if amount is not None:
            if remaining is not None:
                filled = amount - remaining
        price = self.safe_number(order, 'price')
        trades = self.safe_value(order, 'trades')
        cost = None
        average = None
        lastTradeTimestamp = None
        if trades is not None:
            numTrades = len(trades)
            if numTrades > 0:
                trades = self.parse_trades(trades, market, None, None, {
                    'orderId': id,
                })
                # todo: determine the cost and the average price from trades
                cost = 0
                filled = 0
                for i in range(0, numTrades):
                    trade = trades[i]
                    cost = self.sum(cost, trade['cost'])
                    filled = self.sum(filled, trade['amount'])
                if filled > 0:
                    average = cost / filled
                lastTradeTimestamp = trades[numTrades - 1]['timestamp']
            else:
                cost = 0
        stopPrice = self.safe_number(order, 'conditional_price')
        return {
            'id': id,
            'clientOrderId': None,
            'info': order,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'status': status,
            'symbol': symbol,
            'type': type,
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'average': average,
            'remaining': remaining,
            'fee': None,
            'trades': trades,
        }

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        request = {
            'status': 'active',
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        request = {
            'status': 'executed',
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'fund_id': market['id'],
            # 'after': '2015-02-06T08:47:26Z',
            # 'before': '2015-02-06T08:47:26Z'
            # 'status': 'active',  # 'executed', 'conditional'
            # 'side': 'buy',  # 'sell'
            # 'position_id': 123,  # filter orders by margin position id
        }
        if since is not None:
            request['after'] = self.iso8601(since)
        response = self.privateGetFundsFundIdOrders(self.extend(request, params))
        #
        #     {
        #         orders: [
        #             {
        #                 id: 299333648,
        #                 fund_id: 'BTCEUR',
        #                 side: 'sell',
        #                 type: 'limit',
        #                 status: 'executed',
        #                 price: 5821,
        #                 amount: 0.1,
        #                 amount_unfilled: 0,
        #                 conditional_type: null,
        #                 conditional_price: null,
        #                 date: '2018-06-18T17:38:16.129Z',
        #                 close_on: null,
        #                 dark: False,
        #                 leverage: 1,
        #                 position_id: 0
        #             }
        #         ]
        #     }
        #
        orders = self.safe_value(response, 'orders', [])
        return self.parse_orders(orders, market, since, limit)

    def fetch_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'id': id,
            'fund_id': market['id'],
        }
        response = self.privatePostFundsFundIdOrdersId(self.extend(request, params))
        return self.parse_order(response)

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        if type == 'market':
            price = 0
        request = {
            'fund_id': self.market_id(symbol),
            'side': side,
            'amount': amount,
            'price': price,
        }
        response = self.privatePostFundsFundIdOrders(self.extend(request, params))
        return self.parse_order(response)

    def cancel_order(self, id, symbol=None, params={}):
        self.load_markets()
        request = {
            'id': id,
            'fund_id': self.market_id(symbol),
        }
        response = self.privateDeleteFundsFundIdOrdersId(self.extend(request, params))
        return self.parse_order(response)

    def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'id': market['id'],
        }
        if limit is not None:
            request['per_page'] = limit  # default 25 max 200
        if since is not None:
            request['after'] = self.iso8601(since)
        response = self.privateGetFundsIdTrades(self.extend(request, params))
        #
        #     {trades: [{          id:    237338,
        #                        fund_id:   "BTCEUR",
        #                         amount:    0.348,
        #                          price:    348,
        #                           side:   "sell",
        #                           dark:    False,
        #                       order_id:    14920648,
        #                           date:   "2015-06-03T00:49:49.000Z",
        #                   transactions: [{      id:  2770768,
        #                                         date: "2015-06-03T00:49:49.000Z",
        #                                         type: "sold_currency_to_fund",
        #                                        price:  121.1,
        #                                     currency: "EUR"                       },
        #                                   {      id:  2770769,
        #                                         date: "2015-06-03T00:49:49.000Z",
        #                                         type: "released_currency_to_fund",
        #                                        price:  0.348,
        #                                     currency: "BTC"                        },
        #                                   {      id:  2770772,
        #                                         date: "2015-06-03T00:49:49.000Z",
        #                                         type: "paid_commission",
        #                                        price:  0.06,
        #                                     currency: "EUR",
        #                                     trade_id:  440492                     }   ]}],
        #         meta: {total_count:    31,
        #                       first: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1"},
        #                    previous:    null,
        #                     current: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1"},
        #                        next: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2"},
        #                        last: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2"}  }}
        #
        return self.parse_trades(response['trades'], market, since, limit)

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'id': market['id'],
        }
        if limit is not None:
            request['per_page'] = limit  # default 25 max 200
        if since is not None:
            request['after'] = self.iso8601(since)
        response = self.publicGetFundsIdTrades(self.extend(request, params))
        #
        #     {trades: [{     id:  4493548,
        #                   fund_id: "ETHBTC",
        #                    amount:  0.203,
        #                     price:  0.02783576,
        #                      side: "buy",
        #                      dark:  False,
        #                      date: "2018-11-30T08:19:18.236Z"},
        #                 {     id:  4492926,
        #                   fund_id: "ETHBTC",
        #                    amount:  0.04,
        #                     price:  0.02767034,
        #                      side: "buy",
        #                      dark:  False,
        #                      date: "2018-11-30T07:03:03.897Z"}  ],
        #         meta: {total_count:    null,
        #                       first: {page:  1,
        #                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1"},
        #                    previous:    null,
        #                     current: {page:  1,
        #                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1"},
        #                        next: {page:  2,
        #                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=2"},
        #                        last:    null                                                                   }}
        #
        return self.parse_trades(response['trades'], market, since, limit)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api'] + '/' + self.version + '/' + self.implode_params(path, params)
        query = self.omit(params, self.extract_params(path))
        headers = {} if (headers is None) else headers
        if api == 'private':
            self.check_required_credentials()
            if query:
                if method == 'POST':
                    body = self.json(query)
                    headers['Content-Type'] = 'application/json'
                else:
                    queryString = self.rawencode(query)
                    if len(queryString):
                        url += '?' + queryString
            nonce = str(self.nonce())
            auth = nonce + url
            headers['X-TRT-KEY'] = self.apiKey
            headers['X-TRT-NONCE'] = nonce
            headers['X-TRT-SIGN'] = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha512)
        elif api == 'public':
            if query:
                url += '?' + self.rawencode(query)
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return  # fallback to default error handler
        #
        #     {
        #         "errors":
        #         [
        #             {"message": ":currency is not a valid value for param currency","code": "11","meta": {"key":"currency","value":":currency"}},
        #             {"message": "Address allocation limit reached for currency :currency.","code": "13"},
        #             {"message": "Request already running", "code": "50"},
        #             {"message": "cannot specify multiple address types", "code": "12"},
        #             {"message": ":address_type is invalid", "code": "12"}
        #         ]
        #     }
        #
        errors = self.safe_value(response, 'errors', [])
        numErrors = len(errors)
        if numErrors > 0:
            feedback = self.id + ' ' + body
            # here we raise the first error we can identify
            for i in range(0, numErrors):
                error = errors[i]
                message = self.safe_string(error, 'message')
                self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
                self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            raise ExchangeError(feedback)  # unknown message

# -*- coding: utf-8 -*- # 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 # #     {funds: [{                     id:   "BTCEUR", #                              description:   "Trade Bitcoin with Euro", #                                     type:   "currency", #                            base_currency:   "EUR", #                           trade_currency:   "BTC", #                                  buy_fee:    0.2, #                                 sell_fee:    0.2, #                      minimum_price_offer:    0.01, #                   minimum_quantity_offer:    0.0005, #                   base_currency_decimals:    2, #                  trade_currency_decimals:    4, #                                leverages: []                           }, #                {                     id:   "LTCEUR", #                              description:   "Trade Litecoin with Euro", #                                     type:   "currency", #                            base_currency:   "EUR", #                           trade_currency:   "LTC", #                                  buy_fee:    0.2, #                                 sell_fee:    0.2, #                      minimum_price_offer:    0.01, #                   minimum_quantity_offer:    0.01, #                   base_currency_decimals:    2, #                  trade_currency_decimals:    2, #                                leverages: []                            }]} # # #     { #         "date":"2022-01-16T00:05:08.192Z", #         "fund_id":"ETHBTC", #         "bid":0.07707802, #         "ask":0.07733404, #         "last":0.07739053, #         "open":0.07628192, #         "close":0.07687651, #         "low":0.07612047, #         "high":0.07703306, #         "volume":1.10179665, #         "volume_traded":14.273 #     } # # previous day close, if any # #     { #         "date":"2022-01-16T00:05:08.192Z", #         "fund_id":"ETHBTC", #         "bid":0.07707802, #         "ask":0.07733404, #         "last":0.07739053, #         "open":0.07628192, #         "close":0.07687651, #         "low":0.07612047, #         "high":0.07703306, #         "volume":1.10179665, #         "volume_traded":14.273 #     } # # # fetchTrades, fetchOrder trades # #     {     id:  4493548, #       fund_id: "ETHBTC", #        amount:  0.203, #         price:  0.02783576, #          side: "buy", #          dark:  False, #          date: "2018-11-30T08:19:18.236Z"} # # fetchMyTrades # #     {          id:    237338, #            fund_id:   "BTCEUR", #             amount:    0.348, #              price:    348, #               side:   "sell", #               dark:    False, #           order_id:    14920648, #               date:   "2015-06-03T00:49:49.000Z", #       transactions: [{      id:  2770768, #                             date: "2015-06-03T00:49:49.000Z", #                             type: "sold_currency_to_fund", #                            price:  121.1, #                         currency: "EUR"                       }, #                       {      id:  2770769, #                             date: "2015-06-03T00:49:49.000Z", #                             type: "released_currency_to_fund", #                            price:  0.348, #                         currency: "BTC"                        }, #                       {      id:  2770772, #                             date: "2015-06-03T00:49:49.000Z", #                             type: "paid_commission", #                            price:  0.06, #                         currency: "EUR", #                         trade_id:  440492                     }   ]} # # commented types will be shown as-is # 'acquired_currency_from_fund': '', # 'acquired_insurance': '', # 'dividend_distributed_to_holders': '', # 'dividend_from_shares': '', # 'exposed_position': '', # 'insurances_reimbursement': '', # 'lent_currency': '', # 'linden_lab_assessment': '', # 'position_transfer_received': '', # 'return_lent_currency': '', # 'returned_lent_currency': '', # 'the_rock_assessment': '', # commented types will be shown as-is # 'acquired_currency_from_fund': '', # 'acquired_insurance': '', # 'dividend_distributed_to_holders': '', # 'dividend_from_shares': '', # 'exposed_position': '', # 'insurances_reimbursement': '', # 'lent_currency': '', # 'linden_lab_assessment': '', # 'position_transfer_received': '', # 'return_lent_currency': '', # 'returned_lent_currency': '', # 'the_rock_assessment': '', # # withdrawal # #     { #         "id": 21311223, #         "date": "2015-06-30T13:55:11.000Z", #         "type": "withdraw", #         "price": 103.00, #         "currency": "EUR", #         "fund_id": null, #         "order_id": null, #         "trade_id": null, #         "transfer_detail": { #             "method": "wire_transfer", #             "id": "F112DD3", #             "recipient": "IT123456789012", #             "confirmations": 0 #         } #     } # # deposit # #     { #         "id": 21311222, #         "date": "2015-06-30T13:55:11.000Z", #         "type": "atm_payment", #         "price": 2.01291, #         "currency": "BTC", #         "fund_id": "null", #         "order_id": null, #         "trade_id": null, #         "transfer_detail": { #             "method": "bitcoin", #             "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", #             "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", #             "confirmations": 3 #         } #     } # # trade fee # #     { #         "id": 21311221, #         "date": "2015-06-30T13:55:11.000Z", #         "type": "paid_commission", #         "price": 0.0001, #         "fund_id": "BTCEUR", #         "order_id": 12832371, #         "trade_id": 12923212, #         "currency": "BTC", #         "transfer_detail": null #     } # # 'page': 1, # 'fund_id': 'ETHBTC',  # filter by fund symbol # 'currency': 'BTC',  # filter by currency # 'after': '2015-02-06T08:47:26Z',  # filter after a certain timestamp # 'before': '2015-02-06T08:47:26Z', # 'type': 'withdraw', # 'order_id': '12832371',  # filter by a specific order ID # 'trade_id': '12923212',  # filter by a specific trade ID # 'transfer_method': 'bitcoin',  # wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin # 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm',  # filter by a specific recipient(e.g. Bitcoin address, IBAN) # 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa',  # filter by a specific transfer ID(e.g. Bitcoin TX hash) # #     { #         "transactions": [ #             { #                 "id": 21311223, #                 "date": "2015-06-30T13:55:11.000Z", #                 "type": "withdraw", #                 "price": 103.00, #                 "currency": "EUR", #                 "fund_id": null, #                 "order_id": null, #                 "trade_id": null, #                 "transfer_detail": { #                     "method": "wire_transfer", #                     "id": "F112DD3", #                     "recipient": "IT123456789012", #                     "confirmations": 0 #                 } #             }, #             { #                 "id": 21311222, #                 "date": "2015-06-30T13:55:11.000Z", #                 "type": "atm_payment", #                 "price": 2.01291, #                 "currency": "BTC", #                 "fund_id": "null", #                 "order_id": null, #                 "trade_id": null, #                 "transfer_detail": { #                     "method": "bitcoin", #                     "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", #                     "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", #                     "confirmations": 3 #                 } #             }, #             { #                 "id": 21311221, #                 "date": "2015-06-30T13:55:11.000Z", #                 "type": "paid_commission", #                 "price": 0.0001, #                 "fund_id": "BTCEUR", #                 "order_id": 12832371, #                 "trade_id": 12923212, #                 "currency": "BTC", #                 "transfer_detail": null #             } #         ], #         "meta": { #             "total_count": 1221, #             "first": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"}, #             "previous": null, #             "current": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"}, #             "next": {"page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2"}, #             "last": {"page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221"} #         } #     } # # # fetchWithdrawals # #     # fiat # #     { #         "id": 21311223, #         "date": "2015-06-30T13:55:11.000Z", #         "type": "withdraw", #         "price": 103.00, #         "currency": "EUR", #         "fund_id": null, #         "order_id": null, #         "trade_id": null, #         "transfer_detail": { #             "method": "wire_transfer", #             "id": "F112DD3", #             "recipient": "IT123456789012", #             "confirmations": 0 #         } #     } # #     { #         "id": 12564223, #         "date": "2017-08-07T08:13:50.023Z", #         "note": "GB7IDL401573388", #         "type": "withdraw", #         "price": 4345.93, #         "fund_id": null, #         "currency": "EUR", #         "order_id": null, #         "trade_id": null, #         "transfer_detail": { #             "id": "EXECUTEDBUTUNCHECKED", #             "method": "wire_transfer", #             "recipient": "GB7IDL401573388", #             "confirmations": 0 #         } #     } # #     # crypto # #     { #         id: 20914695, #         date: '2018-02-24T07:13:23.002Z', #         type: 'withdraw', #         price: 2.70883607, #         currency: 'BCH', #         fund_id: null, #         order_id: null, #         trade_id: null, #         note: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', #         transfer_detail: { #             method: 'bitcoin_cash', #             id: '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa', #             recipient: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', #             confirmations: 0 #         } #     } # # # fetchDeposits # #     # fiat # #     { #         id: 16176632, #         date: '2017-11-20T21:00:13.355Z', #         type: 'atm_payment', #         price: 5000, #         currency: 'EUR', #         fund_id: null, #         order_id: null, #         trade_id: null, #         note: 'Mistral deposit', #         transfer_detail: { #             method: 'wire_transfer', #             id: '972JQ49337DX769T', #             recipient: null, #             confirmations: 0 #         } #     } # #     # crypto # #     { #         "id": 21311222, #         "date": "2015-06-30T13:55:11.000Z", #         "type": "atm_payment", #         "price": 2.01291, #         "currency": "BTC", #         "fund_id": "null", #         "order_id": null, #         "trade_id": null, #         "transfer_detail": { #             "method": "bitcoin", #             "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", #             "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", #             "confirmations": 3 #         } #     } # # todo parse tags # 'page': 1, # 'fund_id': 'ETHBTC',  # filter by fund symbol # 'currency': 'BTC',  # filter by currency # 'after': '2015-02-06T08:47:26Z',  # filter after a certain timestamp # 'before': '2015-02-06T08:47:26Z', # 'type': 'withdraw', # 'order_id': '12832371',  # filter by a specific order ID # 'trade_id': '12923212',  # filter by a specific trade ID # 'transfer_method': 'bitcoin',  # wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin # 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm',  # filter by a specific recipient(e.g. Bitcoin address, IBAN) # 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa',  # filter by a specific transfer ID(e.g. Bitcoin TX hash) # #     { #         "transactions": [ #             { #                 "id": 21311223, #                 "date": "2015-06-30T13:55:11.000Z", #                 "type": "withdraw", #                 "price": 103.00, #                 "currency": "EUR", #                 "fund_id": null, #                 "order_id": null, #                 "trade_id": null, #                 "transfer_detail": { #                     "method": "wire_transfer", #                     "id": "F112DD3", #                     "recipient": "IT123456789012", #                     "confirmations": 0 #                 } #             }, #             { #                 "id": 21311222, #                 "date": "2015-06-30T13:55:11.000Z", #                 "type": "atm_payment", #                 "price": 2.01291, #                 "currency": "BTC", #                 "fund_id": "null", #                 "order_id": null, #                 "trade_id": null, #                 "transfer_detail": { #                     "method": "bitcoin", #                     "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", #                     "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", #                     "confirmations": 3 #                 } #             }, #             { #                 "id": 21311221, #                 "date": "2015-06-30T13:55:11.000Z", #                 "type": "paid_commission", #                 "price": 0.0001, #                 "fund_id": "BTCEUR", #                 "order_id": 12832371, #                 "trade_id": 12923212, #                 "currency": "BTC", #                 "transfer_detail": null #             } #         ], #         "meta": { #             "total_count": 1221, #             "first": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"}, #             "previous": null, #             "current": {"page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1"}, #             "next": {"page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2"}, #             "last": {"page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221"} #         } #     } # # don't know what self status means # 'conditional': '?', # #     { #         "id": 4325578, #         "fund_id":"BTCEUR", #         "side":"buy", #         "type":"limit", #         "status":"executed", #         "price":0.0102, #         "amount": 50.0, #         "amount_unfilled": 0.0, #         "conditional_type": null, #         "conditional_price": null, #         "date":"2015-06-03T00:49:48.000Z", #         "close_on": nil, #         "leverage": 1.0, #         "position_id": null, #         "trades": [ #             { #                 "id":237338, #                 "fund_id":"BTCEUR", #                 "amount":50, #                 "price":0.0102, #                 "side":"buy", #                 "dark":false, #                 "date":"2015-06-03T00:49:49.000Z" #             } #         ] #     } # # todo: determine the cost and the average price from trades # 'after': '2015-02-06T08:47:26Z', # 'before': '2015-02-06T08:47:26Z' # 'status': 'active',  # 'executed', 'conditional' # 'side': 'buy',  # 'sell' # 'position_id': 123,  # filter orders by margin position id # #     { #         orders: [ #             { #                 id: 299333648, #                 fund_id: 'BTCEUR', #                 side: 'sell', #                 type: 'limit', #                 status: 'executed', #                 price: 5821, #                 amount: 0.1, #                 amount_unfilled: 0, #                 conditional_type: null, #                 conditional_price: null, #                 date: '2018-06-18T17:38:16.129Z', #                 close_on: null, #                 dark: False, #                 leverage: 1, #                 position_id: 0 #             } #         ] #     } # # default 25 max 200 # #     {trades: [{          id:    237338, #                        fund_id:   "BTCEUR", #                         amount:    0.348, #                          price:    348, #                           side:   "sell", #                           dark:    False, #                       order_id:    14920648, #                           date:   "2015-06-03T00:49:49.000Z", #                   transactions: [{      id:  2770768, #                                         date: "2015-06-03T00:49:49.000Z", #                                         type: "sold_currency_to_fund", #                                        price:  121.1, #                                     currency: "EUR"                       }, #                                   {      id:  2770769, #                                         date: "2015-06-03T00:49:49.000Z", #                                         type: "released_currency_to_fund", #                                        price:  0.348, #                                     currency: "BTC"                        }, #                                   {      id:  2770772, #                                         date: "2015-06-03T00:49:49.000Z", #                                         type: "paid_commission", #                                        price:  0.06, #                                     currency: "EUR", #                                     trade_id:  440492                     }   ]}], #         meta: {total_count:    31, #                       first: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1"}, #                    previous:    null, #                     current: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1"}, #                        next: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2"}, #                        last: {href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2"}  }} # # default 25 max 200 # #     {trades: [{     id:  4493548, #                   fund_id: "ETHBTC", #                    amount:  0.203, #                     price:  0.02783576, #                      side: "buy", #                      dark:  False, #                      date: "2018-11-30T08:19:18.236Z"}, #                 {     id:  4492926, #                   fund_id: "ETHBTC", #                    amount:  0.04, #                     price:  0.02767034, #                      side: "buy", #                      dark:  False, #                      date: "2018-11-30T07:03:03.897Z"}  ], #         meta: {total_count:    null, #                       first: {page:  1, #                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1"}, #                    previous:    null, #                     current: {page:  1, #                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1"}, #                        next: {page:  2, #                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=2"}, #                        last:    null                                                                   }} # # fallback to default error handler # #     { #         "errors": #         [ #             {"message": ":currency is not a valid value for param currency","code": "11","meta": {"key":"currency","value":":currency"}}, #             {"message": "Address allocation limit reached for currency :currency.","code": "13"}, #             {"message": "Request already running", "code": "50"}, #             {"message": "cannot specify multiple address types", "code": "12"}, #             {"message": ":address_type is invalid", "code": "12"} #         ] #     } # # here we raise the first error we can identify # unknown message