# -*- 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
from ccxt.abstract.ellipx import ImplicitAPI
import math
from ccxt.base.types import Any, Balances, Currencies, DepositAddress, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Ticker, Trade, TradingFeeInterface, Transaction
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import NotSupported
from ccxt.base.errors import DDoSProtection
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class ellipx(Exchange, ImplicitAPI):

    def describe(self) -> Any:
        return self.deep_extend(super(ellipx, self).describe(), {
            'id': 'ellipx',
            'name': 'Ellipx',
            'countries': ['PL'],
            'rateLimit': 200,  # todo check
            'version': 'v1',
            'certified': False,
            'pro': False,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelAllOrders': False,
                'cancelAllOrdersAfter': False,
                'cancelOrder': True,
                'cancelOrders': False,
                'cancelWithdraw': False,
                'closePosition': False,
                'createConvertTrade': False,
                'createDepositAddress': False,
                'createMarketBuyOrderWithCost': False,
                'createMarketOrder': False,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createOrderWithTakeProfitAndStopLoss': False,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': False,
                'createStopLossOrder': False,
                'createStopMarketOrder': False,
                'createStopOrder': False,
                'createTakeProfitOrder': False,
                'createTrailingAmountOrder': False,
                'createTrailingPercentOrder': False,
                'createTriggerOrder': False,
                'fetchAccounts': False,
                'fetchBalance': True,
                'fetchCanceledAndClosedOrders': False,
                'fetchCanceledOrders': False,
                'fetchClosedOrder': False,
                'fetchClosedOrders': False,
                'fetchConvertCurrencies': False,
                'fetchConvertQuote': False,
                'fetchConvertTrade': False,
                'fetchConvertTradeHistory': False,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDeposits': False,
                'fetchDepositsWithdrawals': False,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchLedger': False,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchMarginAdjustmentHistory': False,
                'fetchMarginMode': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': False,
                'fetchOHLCV': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrder': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchOrderTrades': True,
                'fetchPosition': False,
                'fetchPositionHistory': False,
                'fetchPositionMode': False,
                'fetchPositions': False,
                'fetchPositionsForSymbol': False,
                'fetchPositionsHistory': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchStatus': False,
                'fetchTicker': True,
                'fetchTickers': False,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': False,
                'fetchTransactions': False,
                'fetchTransfers': False,
                'fetchWithdrawals': False,
                'reduceMargin': False,
                'sandbox': False,
                'setLeverage': False,
                'setMargin': False,
                'setPositionMode': False,
                'transfer': False,
                'withdraw': True,
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '10m': '10m',
                '1h': '1h',
                '6h': '6h',
                '12h': '12h',
                '1d': '1d',
            },
            'urls': {
                'logo': 'https://github.com/user-attachments/assets/e07c3f40-281c-4cdf-bacf-fa1c58218a2c',
                'api': {
                    'public': 'https://data.ellipx.com',
                    'private': 'https://app.ellipx.com/_rest',
                    '_rest': 'https://app.ellipx.com/_rest',
                },
                'www': 'https://www.ellipx.com',
                'doc': 'https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM',
                'fees': 'https://www.ellipx.com/pages/pricing',
                'referral': '',  # todo
            },
            'api': {
                '_rest': {
                    'get': {
                        'Market': 1,
                        'Market/{currencyPair}': 1,
                        'Crypto/Token/Info': 1,
                    },
                },
                'public': {
                    'get': {
                        'Market/{currencyPair}:getDepth': 1,
                        'Market/{currencyPair}:ticker': 1,
                        'Market/{currencyPair}:getTrades': 1,
                        'Market/{currencyPair}:getGraph': 1,
                        'CMC:summary': 1,
                        'CMC/{currencyPair}:ticker': 1,
                    },
                },
                'private': {
                    'get': {
                        'User/Wallet': 1,
                        'Market/{currencyPair}/Order': 1,
                        'Market/Order/{orderUuid}': 1,
                        'Market/{currencyPair}/Trade': 1,
                        'Market/TradeFee:query': 1,
                        'Unit/{currency}': 1,
                        'Crypto/Token/{currency}': 1,
                        'Crypto/Token/{currency}:chains': 1,
                    },
                    'post': {
                        'Market/{currencyPair}/Order': 1,
                        'Crypto/Address:fetch': 1,
                        'Crypto/Disbursement:withdraw': 1,
                    },
                    'delete': {
                        'Market/Order/{orderUuid}': 1,
                    },
                },
            },
            'fees': {
                'trading': {
                    'tierBased': True,
                    'feeSide': 'get',
                    'percentage': True,
                    'maker': self.parse_number('0.0025'),  # default 25bps
                    'taker': self.parse_number('0.0030'),  # default 30bps
                    'tiers': {
                        # volume in USDT
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.0025')],     # 0-10k: 25bps
                            [self.parse_number('10000'), self.parse_number('0.0020')],  # 10k-50k: 20bps
                            [self.parse_number('50000'), self.parse_number('0.0015')],  # 50k-100k: 15bps
                            [self.parse_number('100000'), self.parse_number('0.0010')],  # 100k-1M: 10bps
                            [self.parse_number('1000000'), self.parse_number('0.0008')],  # 1M-5M: 8bps
                            [self.parse_number('5000000'), self.parse_number('0.0003')],  # 5M-15M: 3bps
                            [self.parse_number('15000000'), self.parse_number('0.0000')],  # 15M-75M: 0bps
                            [self.parse_number('75000000'), self.parse_number('0.0000')],  # 75M-100M: 0bps
                            [self.parse_number('100000000'), self.parse_number('0.0000')],  # 100M+: 0bps
                        ],
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.0030')],     # 0-10k: 30bps
                            [self.parse_number('10000'), self.parse_number('0.0025')],  # 10k-50k: 25bps
                            [self.parse_number('50000'), self.parse_number('0.0020')],  # 50k-100k: 20bps
                            [self.parse_number('100000'), self.parse_number('0.0015')],  # 100k-1M: 15bps
                            [self.parse_number('1000000'), self.parse_number('0.0012')],  # 1M-5M: 12bps
                            [self.parse_number('5000000'), self.parse_number('0.0010')],  # 5M-15M: 10bps
                            [self.parse_number('15000000'), self.parse_number('0.0008')],  # 15M-75M: 8bps
                            [self.parse_number('75000000'), self.parse_number('0.0005')],  # 75M-100M: 5bps
                            [self.parse_number('100000000'), self.parse_number('0.0003')],  # 100M+: 3bps
                        ],
                    },
                },
                'stablecoin': {
                    'tierBased': False,
                    'percentage': True,
                    'maker': self.parse_number('0.0000'),  # 0%
                    'taker': self.parse_number('0.000015'),  # 0.0015%
                },
            },
            'options': {
                'defaultType': 'spot',
                'recvWindow': 5 * 1000,
                'broker': 'CCXT',
                'networks': {
                    'Bitcoin': 'Bitcoin',
                    'Ethereum': 'ERC20',
                },
                'defaultNetwork': 'defaultNetwork',
                'defaultNetworkCodeReplacements': {
                    'BTC': 'Bitcoin',
                    'ETH': 'Ethereum',
                },
            },
            'features': {
                'spot': {
                    'sandbox': False,
                    'createOrder': {
                        'marginMode': False,
                        'triggerPrice': False,
                        'triggerPriceType': None,
                        'triggerDirection': False,
                        'stopLossPrice': False,
                        'takeProfitPrice': False,
                        'attachedStopLossTakeProfit': None,
                        'timeInForce': {
                            'IOC': False,
                            'FOK': False,
                            'PO': False,
                            'GTD': False,
                        },
                        'hedged': False,
                        'selfTradePrevention': False,
                        'trailing': False,
                        'leverage': False,
                        'marketBuyByCost': True,
                        'marketBuyRequiresPrice': False,
                        'iceberg': False,
                    },
                    'createOrders': None,
                    'fetchMyTrades': None,
                    'fetchOrder': {
                        'marginMode': False,
                        'trigger': False,
                        'trailing': False,
                        'symbolRequired': False,
                    },
                    'fetchOpenOrders': {
                        'marginMode': False,
                        'limit': None,
                        'trigger': False,
                        'trailing': False,
                        'symbolRequired': True,
                    },
                    'fetchOrders': {
                        'marginMode': False,
                        'limit': None,  # todo
                        'daysBack': None,  # todo
                        'untilDays': None,  # todo
                        'trigger': False,
                        'trailing': False,
                        'symbolRequired': True,
                    },
                    'fetchClosedOrders': None,
                    'fetchOHLCV': {
                        'limit': 100,
                    },
                },
                'swap': {
                    'linear': None,
                    'inverse': None,
                },
                'future': {
                    'linear': None,
                    'inverse': None,
                },
            },
            'commonCurrencies': {},
            'exceptions': {
                'exact': {
                    # todo
                    '400': BadRequest,
                    '401': AuthenticationError,
                    '403': PermissionDenied,
                    '404': BadRequest,
                    '429': DDoSProtection,
                    '418': PermissionDenied,
                    '500': ExchangeError,
                    '504': ExchangeError,
                },
                'broad': {},
            },
            'precisionMode': TICK_SIZE,
        })

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        path = self.implode_params(path, params)
        url = self.urls['api'][api] + '/' + path
        if api == 'private':
            self.check_required_credentials()
            nonce = self.uuid()
            timestamp = str(self.seconds())
            if method == 'GET':
                body = ''
            else:
                body = self.json(params)
            params = self.extend({
                '_key': self.apiKey,
                '_time': timestamp,
                '_nonce': nonce,
            }, params)
            query = self.urlencode(params)
            bodyHash = self.hash(self.encode(body), 'sha256')
            # Create sign string components
            bodyHashBytes = self.base16_to_binary(bodyHash)
            nulByte = self.number_to_be(0, 1)
            components = [
                self.encode(method),
                nulByte,
                self.encode(path),
                nulByte,
                self.encode(query),
                nulByte,
                bodyHashBytes,
            ]
            # Join with null byte separator using encode
            signString = self.binary_concat_array(components)
            sec = self.secret
            remainder = self.calculate_mod(len(sec), 4)
            paddingLength = 4 - remainder if remainder else 0
            secretWithPadding = self.secret.replace('-', '+')
            secretWithPadding = secretWithPadding.replace('_', '/')
            secretWithPadding = secretWithPadding.ljust(len(self.secret) + paddingLength, '=')
            secretBytes = self.base64_to_binary(secretWithPadding)
            seed = self.array_slice(secretBytes, 0, 32)  # Extract first 32 bytes
            signature = self.eddsa(signString, seed, 'ed25519')
            params['_sign'] = signature
        if params:
            url += '?' + self.urlencode(params)
        if method == 'GET':
            body = None
        else:
            headers = {
                'Content-Type': 'application/json',
            }
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def calculate_mod(self, a, b):
        # trick to fix php transpiling error
        return a % b

    def fetch_markets(self, params={}) -> List[Market]:
        """
        Fetches market information from the exchange.

        https://docs.ccxt.com/en/latest/manual.html#markets
        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.1a1t05wpgfof

        :param dict [params]: - Extra parameters specific to the exchange API endpoint
        :returns Promise<Market[]>: An array of market structures.
        """
        response = self._restGetMarket(params)
        # {
        #     Market__: "mkt-lrnp2e-eaor-eobj-ua73-75j6sjxe",
        #     Primary_Unit__: "unit-aebkye-u35b-e5zm-zt22-2qvwhsqa",
        #     Secondary_Unit__: "unit-jcevlk-soxf-fepb-yjwm-b32q5bom",
        #     Primary_Step: null,
        #     Secondary_Step: null,
        #     Status: "active",
        #     Default_Scale: "5",
        #     Priority: "100",
        #     Created: {
        #       unix: "1728113809",
        #       us: "0",
        #       iso: "2024-10-05 07:36:49.000000",
        #       tz: "UTC",
        #       full: "1728113809000000",
        #       unixms: "1728113809000",
        #     },
        #     Start: {
        #       unix: "1728295200",
        #       us: "0",
        #       iso: "2024-10-07 10:00:00.000000",
        #       tz: "UTC",
        #       full: "1728295200000000",
        #       unixms: "1728295200000",
        #     },
        #     Key: "BTC_USDC",
        #     Primary: {
        #       Unit__: "unit-aebkye-u35b-e5zm-zt22-2qvwhsqa",
        #       Currency__: "BTC",
        #       Crypto_Token__: "crtok-c5v3mh-grfn-hl5d-lmel-fvggbf4i",
        #       Key: "BTC",
        #       Symbol: "BTC",
        #       Symbol_Position: "after",
        #       Name: "Bitcoin",
        #       Decimals: "8",
        #       Display_Decimals: "8",
        #       Legacy_Decimals: null,
        #       Type: "crypto_token",
        #       Visible: "Y",
        #       Created: {
        #         unix: "1495247415",
        #         us: "0",
        #         iso: "2017-05-20 02:30:15.000000",
        #         tz: "UTC",
        #         full: "1495247415000000",
        #         unixms: "1495247415000",
        #       },
        #     },
        #     Secondary: {
        #       Unit__: "unit-jcevlk-soxf-fepb-yjwm-b32q5bom",
        #       Currency__: null,
        #       Crypto_Token__: "crtok-ptabkh-ra4r-anbd-cqra-bqfbtnba",
        #       Key: "USDC",
        #       Symbol: null,
        #       Symbol_Position: "before",
        #       Name: "Circle USD",
        #       Decimals: "6",
        #       Display_Decimals: "6",
        #       Legacy_Decimals: null,
        #       Type: "crypto_token",
        #       Visible: "Y",
        #       Created: {
        #         unix: "1694859829",
        #         us: "0",
        #         iso: "2023-09-16 10:23:49.000000",
        #         tz: "UTC",
        #         full: "1694859829000000",
        #         unixms: "1694859829000",
        #       },
        #     },
        #   }
        markets = self.safe_value(response, 'data', [])
        return self.parse_markets(markets)

    def parse_market(self, market: dict) -> Market:
        id = self.safe_string(market, 'Key')
        base = self.safe_string(market['Primary'], 'Key')
        quote = self.safe_string(market['Secondary'], 'Key')
        baseId = self.safe_string(market['Primary'], 'Crypto_Token__')
        quoteId = self.safe_string(market['Secondary'], 'Crypto_Token__')
        status = self.safe_string(market, 'Status') == 'active'
        created = self.safe_timestamp(market['Created'], 'unix')
        amountPrecision = self.parse_number(self.parse_precision(self.safe_string(market['Primary'], 'Decimals')))
        pricePrecision = self.parse_number(self.parse_precision(self.safe_string(market['Secondary'], 'Decimals')))
        fees = self.fees  # should use fetchTradingFees
        return self.safe_market_structure({
            'id': id,
            'symbol': base + '/' + quote,
            'base': base,
            'quote': quote,
            'settle': None,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': None,
            'type': 'spot',
            'spot': True,
            'margin': False,
            'swap': False,
            'future': False,
            'option': False,
            'active': status,
            'contract': False,
            'linear': None,
            'inverse': None,
            'taker': fees['trading']['taker'],
            'maker': fees['trading']['maker'],
            'contractSize': None,
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': amountPrecision,
                'price': pricePrecision,
            },
            'limits': {
                'amount': {
                    'min': None,
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'info': market,
            'created': created,
        })

    def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.d2jylz4u6pmu

        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        marketId = market['id']
        request = {
            'currencyPair': marketId,
        }
        response = self.publicGetMarketCurrencyPairTicker(self.extend(request, params))
        #
        #     {
        #         "data": {
        #             "market": "BTC_USDC",
        #             "ticker": {
        #                 "time": 1730814600,
        #                 "count": 2135,
        #                 "high": {
        #                     "v": "74766990000",
        #                     "e": 6,
        #                     "f": 74766.99
        #                 },
        #                 "low": {
        #                     "v": "68734020000",
        #                     "e": 6,
        #                     "f": 68734.02
        #                 },
        #                 "avg": {
        #                     "v": "72347941430",
        #                     "e": 6,
        #                     "f": 72347.94143
        #                 },
        #                 "vwap": {
        #                     "v": "73050064447",
        #                     "e": 6,
        #                     "f": 73050.064447
        #                 },
        #                 "vol": {
        #                     "v": "4885361",
        #                     "e": 8,
        #                     "f": 0.04885361
        #                 },
        #                 "secvol": {
        #                     "v": "3568759346",
        #                     "e": 6,
        #                     "f": 3568.759346
        #                 },
        #                 "open": {
        #                     "v": "68784020000",
        #                     "e": 6,
        #                     "f": 68784.02
        #                 },
        #                 "close": {
        #                     "v": "73955570000",
        #                     "e": 6,
        #                     "f": 73955.57
        #                 }
        #             }
        #         },
        #         "request_id": "cbf183e0-7a62-4674-838c-6693031fa240",
        #         "result": "success",
        #         "time": 0.015463566
        #     }
        #
        ticker = self.safe_value(response['data'], 'ticker', {})
        return self.parse_ticker(ticker, market)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        timestamp = self.safe_integer_product(ticker, 'time', 1000)
        open = self.parse_amount(self.safe_value(ticker, 'open'))
        high = self.parse_amount(self.safe_value(ticker, 'high'))
        low = self.parse_amount(self.safe_value(ticker, 'low'))
        close = self.parse_amount(self.safe_value(ticker, 'close'))
        avg = self.parse_amount(self.safe_value(ticker, 'avg'))
        vwap = self.parse_amount(self.safe_value(ticker, 'vwap'))
        baseVolume = self.parse_amount(self.safe_value(ticker, 'vol'))
        quoteVolume = self.parse_amount(self.safe_value(ticker, 'secvol'))
        # count = self.safe_integer(ticker, 'count'); not used
        return self.safe_ticker({
            'symbol': self.safe_symbol(None, market),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': high,
            'low': low,
            'bid': None,
            'bidVolume': None,
            'ask': None,
            'askVolume': None,
            'vwap': vwap,
            'open': open,
            'close': close,
            'last': close,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': avg,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.bqmucewhkpdz

        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return the exchange not supported yet.
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        marketId = market['id']
        request = {
            'currencyPair': marketId,
        }
        response = self.publicGetMarketCurrencyPairGetDepth(self.extend(request, params))
        # {
        #     "data": {
        #         "asks": [
        #             {
        #                 "price": {
        #                     "v": "74941875231",
        #                     "e": 6,
        #                     "f": 74941.875231
        #                 },
        #                 "amount": {
        #                     "v": "149",
        #                     "e": 8,
        #                     "f": 0.00000149
        #                 }
        #             },
        #             {
        #                 "price": {
        #                     "v": "75063426037",
        #                     "e": 6,
        #                     "f": 75063.426037
        #                 },
        #                 "amount": {
        #                     "v": "335",
        #                     "e": 8,
        #                     "f": 0.00000335
        #                 }
        #             }
        #         ],
        #         "bids": [
        #             {
        #                 "price": {
        #                     "v": "64518711040",
        #                     "e": 6,
        #                     "f": 64518.71104
        #                 },
        #                 "amount": {
        #                     "v": "132",
        #                     "e": 8,
        #                     "f": 0.00000132
        #                 }
        #             },
        #             {
        #                 "price": {
        #                     "v": "64263569273",
        #                     "e": 6,
        #                     "f": 64263.569273
        #                 },
        #                 "amount": {
        #                     "v": "210",
        #                     "e": 8,
        #                     "f": 0.0000021
        #                 }
        #             }
        #         ],
        #         "market": "BTC_USDC"
        #     },
        #     "request_id": "71b7dffc-3120-4e46-a0bb-49ece5aea7e1",
        #     "result": "success",
        #     "time": 0.000074661
        # }
        data = self.safe_value(response, 'data', {})  # exchange specific v e f params
        timestamp = self.milliseconds()  # the exchange does not provide timestamp for self.
        dataBidsLength = len(data['bids'])
        dataAsksLength = len(data['asks'])
        for i in range(0, dataBidsLength):
            data['bids'][i]['price'] = self.parse_amount(data['bids'][i]['price'])
            data['bids'][i]['amount'] = self.parse_amount(data['bids'][i]['amount'])
        for i in range(0, dataAsksLength):
            data['asks'][i]['price'] = self.parse_amount(data['asks'][i]['price'])
            data['asks'][i]['amount'] = self.parse_amount(data['asks'][i]['amount'])
        return self.parse_order_book(data, symbol, timestamp, 'bids', 'asks', 'price', 'amount')

    def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market, default will return the last 24h period.

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.w65baeuhxwt8

        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int [since]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch
        :param dict [params]: extra parameters specific to the API endpoint
        :param int [params.until]: timestamp in ms of the earliest candle to fetch
        :returns OHLCV[]: A list of candles ordered, open, high, low, close, volume
        """
        self.load_markets()
        methodName = 'fetchOHLCV'
        paginate = False
        paginate, params = self.handle_option_and_params(params, methodName, 'paginate')
        if paginate:
            return self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 1000)
        market = self.market(symbol)
        marketId = market['id']
        time_frame = self.safe_string(self.timeframes, timeframe, None)
        request: dict = {
            'currencyPair': marketId,
            'interval': time_frame,
        }
        if since is not None:
            request['start'] = int(math.floor(since / 1000))
        until: Int = None
        until, params = self.handle_option_and_params(params, methodName, 'until')
        if until is not None:
            request['end'] = until
        # {
        #     "data": {
        #         "market": "BTC_USDC",
        #         "real_end": 1730970780,
        #         "requested_end": 1730970784,
        #         "start": 1730884200,
        #         "stats": [
        #             {
        #                 "time": 1730884200,
        #                 "count": 48,
        #                 "high": {"v": "73898950000", "e": 6, "f": 73898.95},
        #                 "low": {"v": "73642930000", "e": 6, "f": 73642.93},
        #                 "open": {"v": "73830990000", "e": 6, "f": 73830.99},
        #                 "close": {"v": "73682510000", "e": 6, "f": 73682.51},
        #                 "vol": {"v": "88159", "e": 8, "f": 0.00088159}
        #             }
        #         ]
        #     }
        # }
        # No limit parameter supported by the API
        response = self.publicGetMarketCurrencyPairGetGraph(self.extend(request, params))
        data = self.safe_dict(response, 'data', {})
        ohlcv = self.safe_list(data, 'stats', [])
        return self.parse_ohlcvs(ohlcv, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        return [
            self.safe_integer(ohlcv, 'time') * 1000,  # timestamp
            self.parse_number(self.parse_amount(self.safe_dict(ohlcv, 'open'))),      # open
            self.parse_number(self.parse_amount(self.safe_dict(ohlcv, 'high'))),      # high
            self.parse_number(self.parse_amount(self.safe_dict(ohlcv, 'low'))),       # low
            self.parse_number(self.parse_amount(self.safe_dict(ohlcv, 'close'))),     # close
            self.parse_number(self.parse_amount(self.safe_dict(ohlcv, 'vol'))),       # volume
        ]

    def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches information on all currencies from the exchange, including deposit/withdrawal details and available chains

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.x65f9s9j74jf

        :param dict [params]: extra parameters specific to the ellipx API endpoint
        :param str [params.Can_Deposit]: filter currencies by deposit availability, Y for available
        :param number [params.results_per_page]: number of results per page, default 100
        :param str [params._expand]: additional fields to expand in response, default '/Crypto_Token,/Crypto_Chain'
        :returns Promise<Currencies>: An object of currency structures indexed by currency codes
        """
        response = self._restGetCryptoTokenInfo(self.extend({
            'Can_Deposit': 'Y',
            'results_per_page': 100,
            '_expand': '/Crypto_Token,/Crypto_Chain',
        }, params))
        result = {}
        data = self.safe_list(response, 'data', [])
        for i in range(0, len(data)):
            networkEntry = data[i]
            #
            #    {
            #        "Crypto_Token_Info__": "crtev-5nsn35-f4ir-g5hp-iaft-i4ztx6zu",
            #        "Crypto_Token__": "crtok-c5v3mh-grfn-hl5d-lmel-fvggbf4i",
            #        "Crypto_Chain__": "chain-xjbini-7wlz-dmzf-gm7z-zf7ei6fq",
            #        "Type": "native",
            #        "Symbol": null,
            #        "Name": null,
            #        "Contract_Address": null,
            #        "Minimum_Deposit": {
            #            "v": "6",
            #            "e": "6",
            #            "f": "6.0e-6"
            #        },
            #        "Minimum_Withdraw": {
            #            "v": "15",
            #            "e": "5",
            #            "f": "0.00015"
            #        },
            #        "Withdraw_Fee": {
            #            "v": "1",
            #            "e": "4",
            #            "f": "0.0001"
            #        },
            #        "Minimum_Collect": null,
            #        "Status": "valid",
            #        "Can_Deposit": "Y",
            #        "Decimals": null,
            #        "Priority": "100",
            #        "Created": {
            #            "unix": "1727552199",
            #            "us": "0",
            #            "iso": "2024-09-28 19:36:39.000000",
            #            "tz": "UTC",
            #            "full": "1727552199000000",
            #            "unixms": "1727552199000"
            #        },
            #        "Crypto_Token": {
            #            "Crypto_Token__": "crtok-c5v3mh-grfn-hl5d-lmel-fvggbf4i",
            #            "Name": "Bitcoin",
            #            "Symbol": "BTC",
            #            "Decimals": "8",
            #            "CMC_Id": "1",
            #            "Priority": "100",
            #            "Can_Deposit": "Y",
            #            "Category": "token",
            #            "Testnet": "N",
            #            "Created": {
            #                "unix": "1727552113",
            #                "us": "0",
            #                "iso": "2024-09-28 19:35:13.000000",
            #                "tz": "UTC",
            #                "full": "1727552113000000",
            #                "unixms": "1727552113000"
            #            },
            #            "Logo": [
            #                {
            #                    "Crypto_Token_Logo__": "ctklg-aoozyr-rzm5-fphf-dhm7-5wbtetha",
            #                    "Crypto_Token__": "crtok-c5v3mh-grfn-hl5d-lmel-fvggbf4i",
            #                    "Blob__": "blob-d6hvgx-37s5-dh5h-ogj5-qxqvnaoy",
            #                    "Default": "Y",
            #                    "Format": "png",
            #                    "Priority": "0",
            #                    "Created": {
            #                        "unix": "1730196627",
            #                        "us": "929660",
            #                        "iso": "2024-10-29 10:10:27.929660",
            #                        "tz": "UTC",
            #                        "full": "1730196627929660",
            #                        "unixms": "1730196627929"
            #                    },
            #                    "Source": {
            #                        "Media_Image__": "blob-d6hvgx-37s5-dh5h-ogj5-qxqvnaoy",
            #                        "Url": "https://static.atonline.net/image/m_X7_tnmIYFCwn6EUVQuMKqrCuPB3CMl4ONTegeYpC0wIg68YZM0CuBpbjspnYwz/1a942eab068a2173e66d08c736283cfe22e1c1ed"
            #                    }
            #                }
            #            ]
            #        },
            #        "Crypto_Chain": {
            #            "Crypto_Chain__": "chain-xjbini-7wlz-dmzf-gm7z-zf7ei6fq",
            #            "EVM_Chain__": null,
            #            "Crypto_Token__": "crtok-c5v3mh-grfn-hl5d-lmel-fvggbf4i",
            #            "Name": "Bitcoin",
            #            "Key": "bitcoin",
            #            "Type": "Bitcoin",
            #            "Curve": "secp256k1",
            #            "Backend_Url": null,
            #            "Wallet_Verification_Methods": {
            #                "signature": True
            #            },
            #            "Block_Margin": "3",
            #            "Created": {
            #                "unix": "1725340084",
            #                "us": "0",
            #                "iso": "2024-09-03 05:08:04.000000",
            #                "tz": "UTC",
            #                "full": "1725340084000000",
            #                "unixms": "1725340084000"
            #            }
            #        }
            #    }
            #
            id = self.safe_string(networkEntry, 'Crypto_Token__')
            token = self.safe_dict(networkEntry, 'Crypto_Token', {})
            code = self.safe_currency_code(self.safe_string(token, 'Symbol'))
            if not (code in result):
                result[code] = {
                    'id': id,
                    'code': code,
                    'info': [],
                    'type': None,
                    'name': self.safe_string(token, 'Name'),
                    'active': None,
                    'deposit': None,
                    'withdraw': None,
                    'fee': None,
                    'precision': None,
                    'limits': {
                        'amount': {
                            'min': None,
                            'max': None,
                        },
                        'withdraw': {
                            'min': None,
                            'max': None,
                        },
                        'deposit': {
                            'min': None,
                            'max': None,
                        },
                    },
                    'networks': {},
                }
            networkId = self.safe_string(networkEntry, 'Crypto_Chain__')
            cryptoChainDict = self.safe_string(networkEntry, 'Crypto_Chain')
            networkName = self.safe_string(cryptoChainDict, 'Type', 'default')
            networkCode = self.network_id_to_code(networkName)
            result[code]['networks'][networkCode] = {
                'id': networkId,
                'network': networkCode,
                'active': self.safe_string(networkEntry, 'Status') == 'valid',
                'deposit': self.safe_string(networkEntry, 'Can_Deposit') == 'Y',
                'withdraw': None,
                'fee': self.parse_number(self.parse_amount(networkEntry['Withdraw_Fee'])),
                'precision': self.parse_number(self.parse_precision(self.safe_string(token, 'Decimals'))),
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': self.parse_amount(networkEntry['Minimum_Withdraw']),
                        'max': None,
                    },
                    'deposit': {
                        'min': self.parse_amount(networkEntry['Minimum_Deposit']),
                        'max': None,
                    },
                },
            }
            infos = self.safe_list(result[code], 'info', [])
            infos.append(networkEntry)
            result[code]['info'] = infos
        # only after all entries are formed in currencies, restructure each entry
        allKeys = list(result.keys())
        for i in range(0, len(allKeys)):
            code = allKeys[i]
            result[code] = self.safe_currency_structure(result[code])  # self is needed after adding network entry
        return result

    def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        fetches all completed trades for a particular market/symbol
        :param str symbol: unified market symbol(e.g. 'BTC/USDT')
        :param int [since]: timestamp in ms of the earliest trade to fetch
        :param int [limit]: the maximum amount of trades to fetch
        :param dict [params]: extra parameters specific to the EllipX API endpoint
        :param str [params.before]: get trades before the given trade ID
        :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        marketId = market['id']
        request = {
            'currencyPair': marketId,
        }
        # endpoint support before trade id.
        # The actual endpoint URL will be: https://data.ellipx.com/Market/{currencyPair}:getTrades
        # {
        #     "id": "BTC_USDC:1731053859:914141972:0",
        #     "pair": [
        #         "BTC",
        #         "USDC"
        #     ],
        #     "bid": {
        #         "id": "mktor-swishf-uv6n-hrzj-63ye-bdqnk33q",
        #         "iss": "ellipx:beta",
        #         "uniq": "order:1731053859:914141972:0"
        #     },
        #     "ask": {
        #         "id": "mktor-p3ozvt-qurz-gmzo-bf5n-g4rcuy6u",
        #         "iss": "ellipx:beta",
        #         "uniq": "order:1731053859:874659786:0"
        #     },
        #     "type": "bid",
        #     "amount": {
        #         "v": "412",
        #         "e": 8,
        #         "f": 0.00000412
        #     },
        #     "price": {
        #         "v": "75878090000",
        #         "e": 6,
        #         "f": 75878.09
        #     },
        #     "date": "2024-11-08T08:17:39.914141972Z"
        # }
        response = self.publicGetMarketCurrencyPairGetTrades(self.extend(request, params))
        data = self.safe_dict(response, 'data', {})
        trades = self.safe_list(data, 'trades', [])
        return self.parse_trades(trades, market, since, limit)

    def parse_trade(self, trade, market=None) -> Trade:
        # Format of trade ID: "BTC_USDC:1731053859:914141972:0"
        id = self.safe_string(trade, 'id')
        # fetchTrades and fetchMyTrades return different trade structures
        date = self.safe_dict(trade, 'date')
        timestamp = None
        if date is None:
            timestamp = self.parse8601(self.safe_string(trade, 'date'))
        else:
            timestamp = self.safe_integer(date, 'unixms')
        type = self.safe_string(trade, 'type')
        side = 'buy' if (type == 'bid') else 'sell'
        amount = self.safe_dict(trade, 'amount')
        price = self.safe_dict(trade, 'price')
        amountFloat = self.parse_amount(amount)
        priceFloat = self.parse_amount(price)
        # fetchTrades and fetchMyTrades return different trade structures
        pair = self.safe_list(trade, 'pair')
        marketSymbol = None
        if pair is None:
            symbol = self.safe_string(trade, 'pair')
            base, quote = symbol.split('_')
            marketSymbol = base + '/' + quote
        else:
            marketSymbol = self.safe_string(pair, 0) + '/' + self.safe_string(pair, 1)
        bidOrder = self.safe_dict(trade, 'bid')
        askOrder = self.safe_dict(trade, 'ask')
        isBuy = (side == 'buy')
        orderId = self.safe_string(bidOrder, 'id') if isBuy else self.safe_string(askOrder, 'id')
        return self.safe_trade({
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': marketSymbol,
            'type': None,
            'side': side,
            'order': orderId,
            'takerOrMaker': None,
            'price': priceFloat,
            'amount': amountFloat,
            'cost': None,
            'fee': None,
        })

    def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.ihrjov144txg

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
        """
        self.load_markets()
        response = self.privateGetUserWallet(params)
        # {
        #     "User_Wallet__": "usw-vv7hzo-qel5-gupk-neqi-7f3wz5pq",
        #     "User__": "usr-...",
        #     "Realm__": "usrr-cb3c7n-qvxv-fdrb-uc2q-gpja2foi",
        #     "Unit__": "unit-aebkye-u35b-e5zm-zt22-2qvwhsqa",
        #     "Balance": {
        #         "value": "0.00006394",
        #         "value_int": "6394",
        #         "value_disp": "0.00006394",
        #         "value_xint": {
        #             "v": "6394",
        #             "e": 8,
        #             "f": 0.00006394
        #         },
        #         "display": "0.00006394BTC",
        #         "display_short": "0.00006394BTC",
        #         "currency": "BTC",
        #         "unit": "BTC",
        #         "has_vat": False,
        #         "tax_profile": null
        #     },
        #     "Balance_Date": {
        #         "unix": 1731128270,
        #         "us": 426208,
        #         "iso": "2024-11-09 04:57:50.426208",
        #         "tz": "UTC",
        #         "full": "1731128270426208",
        #         "unixms": "1731128270426"
        #     },
        #     "Liabilities": {
        #         "value": "0.00000000",
        #         "value_int": "0",
        #         "value_disp": "0.00000000",
        #         "value_xint": {
        #             "v": "0",
        #             "e": 8,
        #             "f": 0
        #         },
        #         "display": "0.00000000BTC",
        #         "display_short": "0.00000000BTC",
        #         "currency": "BTC",
        #         "unit": "BTC",
        #         "has_vat": False,
        #         "tax_profile": null
        #     },
        #     "Index": "5",
        #     "Backend": "virtual",
        #     "Disable_Limits": "N",
        #     "Unencumbered_Balance": {
        #         "value": "0.00006394",
        #         "value_int": "6394",
        #         "value_disp": "0.00006394",
        #         "value_xint": {
        #             "v": "6394",
        #             "e": 8,
        #             "f": 0.00006394
        #         },
        #         "display": "0.00006394BTC",
        #         "display_short": "0.00006394BTC",
        #         "currency": "BTC",
        #         "unit": "BTC",
        #         "has_vat": False,
        #         "tax_profile": null
        #     }
        # }
        result: dict = {
            'info': response,
            'timestamp': None,
            'datetime': None,
        }
        dataArray = self.safe_list(response, 'data', [])
        # Use first item's timestamp if available
        dataArrayLength = len(dataArray)
        if dataArrayLength > 0:
            firstItem = dataArray[0]
            balanceDate = self.safe_dict(firstItem, 'Balance_Date', {})
            result['timestamp'] = self.safe_integer(balanceDate, 'unixms')
            result['datetime'] = self.iso8601(result['timestamp'])
        # Process each balance entry
        for i in range(0, len(dataArray)):
            entry = dataArray[i]
            balance = self.safe_dict(entry, 'Balance', {})
            code = self.safe_string(balance, 'currency')
            if code is not None:
                account = {
                    'free': self.parse_amount(entry['Unencumbered_Balance']['value_xint']),
                    'used': self.parse_amount(entry['Liabilities']['value_xint']),
                    'total': self.parse_amount(balance['value_xint']),
                }
                result[code] = account
        return self.safe_balance(result)

    def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a new order in a market

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.yzfak2n2bwpo

        :param str symbol: unified market symbol(e.g. 'BTC/USDT')
        :param str type: order type - the exchange automatically sets type to 'limit' if price defined, 'market' if None
        :param str side: 'buy' or 'sell'
        :param float [amount]: amount of base currency to trade(can be None if using Spend_Limit)
        :param float [price]: price per unit of base currency for limit orders
        :param dict [params]: extra parameters specific to the EllipX API endpoint
        :param float [params.cost]: maximum amount to spend in quote currency(required for market orders if amount None)
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        # the exchange automatically sets the type to 'limit' if the price is defined and to 'market' if it is not
        marketId = market['id']
        orderType = 'bid'
        if side == 'buy':
            orderType = 'bid'
        else:
            orderType = 'ask'
        request: Any = {
            'currencyPair': marketId,
            'Type': orderType,
        }
        if amount is not None:
            request['Amount'] = self.amount_to_precision(symbol, amount)
        if price is not None:
            request['Price'] = self.price_to_precision(symbol, price)
        cost = self.safe_string(params, 'cost')
        if cost is not None:
            params = self.omit(params, 'cost')
            request['Spend_Limit'] = self.price_to_precision(symbol, cost)
        response = self.privatePostMarketCurrencyPairOrder(self.extend(request, params))
        # {
        #     "result": "success",
        #     "data": {
        #         "Market_Order__": "mktor-x2grmu-zwo5-fyxc-4gue-vd4ouvsa",
        #         "Market__": "mkt-lrnp2e-eaor-eobj-ua73-75j6sjxe",
        #         "User__": "usr-...",
        #         "Uniq": "order:1728719021:583795548:0",
        #         "Type": "bid",
        #         "Status": "pending",
        #         "Flags": {},
        #         "Amount": {
        #             "v": "100000000",
        #             "e": 8,
        #             "f": 1
        #         },
        #         "Price": null,
        #         "Spend_Limit": {
        #             "v": "1000000",
        #             "e": 6,
        #             "f": 1
        #         },
        #         "Executed": {
        #             "v": "0",
        #             "e": 0,
        #             "f": 0
        #         },
        #         "Secured": {
        #             "v": "1000000",
        #             "e": 6,
        #             "f": 1
        #         },
        #         "Version": "0",
        #         "Created": {
        #             "unix": 1728719020,
        #             "us": 315195,
        #             "iso": "2024-10-12 07:43:40.315195",
        #             "tz": "UTC",
        #             "full": "1728719020315195",
        #             "unixms": "1728719020315"
        #         },
        #         "Updated": {
        #             "unix": 1728719020,
        #             "us": 315195,
        #             "iso": "2024-10-12 07:43:40.315195",
        #             "tz": "UTC",
        #             "full": "1728719020315195",
        #             "unixms": "1728719020315"
        #         }
        #     }
        # }
        order = self.safe_dict(response, 'data', {})
        return self.parse_order(order, market)

    def fetch_order(self, id: str, symbol: Str = None, params={}) -> Order:
        """
        fetches information on an order made by the user
        :param str id: the order ID by createOrder or fetchOrders
        :param str|None symbol: not used by ellipx.fetchOrder
        :param dict [params]: extra parameters specific to the EllipX API endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.load_markets()
        request = {
            'orderUuid': id,
        }
        response = self.privateGetMarketOrderOrderUuid(self.extend(request, params))
        data = self.safe_dict(response, 'data', {})
        return self.parse_order(data, None)

    def fetch_orders_by_status(self, status, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches a list of orders placed on the exchange

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.5z2nh2b5s81n

        :param str status: 'open' or 'closed', omit for all orders
        :param str symbol: unified market symbol
        :param int [since]: timestamp in ms of the earliest order
        :param int [limit]: the maximum amount of orders to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = None
        request: Any = {}
        if symbol is not None:
            market = self.market(symbol)
            marketId = market['id']
            request['currencyPair'] = marketId
        if status is not None:
            request['Status'] = status
        response = self.privateGetMarketCurrencyPairOrder(self.extend(request, params))
        # {
        #     "result": "success",
        #     "data": [
        #         {
        #             "Market_Order__": "mktor-aglvd2-iy5v-enbj-nwrb-scqsnosa",
        #             "Market__": "mkt-lrnp2e-eaor-eobj-ua73-75j6sjxe",
        #             "User__": "usr-...",
        #             "Uniq": "order:1728712511:964332600:0",
        #             "Type": "ask",
        #             "Status": "open",
        #             "Flags": {},
        #             "Amount": {
        #                 "v": "1",
        #                 "e": 8,
        #                 "f": 1.0e-8
        #             },
        #             "Price": {
        #                 "v": "63041306872",
        #                 "e": 6,
        #                 "f": 63041.306872
        #             },
        #             "Spend_Limit": null,
        #             "Executed": {
        #                 "v": "892",
        #                 "e": 8,
        #                 "f": 8.92e-6
        #             },
        #             "Secured": null,
        #             "Version": "3",
        #             "Created": {
        #                 "unix": 1728712510,
        #                 "us": 669096,
        #                 "iso": "2024-10-12 05:55:10.669096",
        #                 "tz": "UTC",
        #                 "full": "1728712510669096",
        #                 "unixms": "1728712510669"
        #             },
        #             "Updated": {
        #                 "unix": 1728712510,
        #                 "us": 669096,
        #                 "iso": "2024-10-12 05:55:10.669096",
        #                 "tz": "UTC",
        #                 "full": "1728712510669096",
        #                 "unixms": "1728712510669"
        #             }
        #         }
        #     ],
        #     "paging": {
        #         "page_no": 1,
        #         "count": "1",
        #         "page_max": 1,
        #         "results_per_page": 20
        #     }
        # }
        data = self.safe_value(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches information on multiple orders made by the user

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.5z2nh2b5s81n

        :param str symbol: unified market symbol of the market orders were made in
        :param int|None since: timestamp in ms of the earliest order
        :param int|None limit: the maximum amount of orders to fetch
        :param dict params: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrders requires a symbol parameter')
        return self.fetch_orders_by_status(None, symbol, since, limit, params)

    def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on open orders made by the user

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.5z2nh2b5s81n

        :param str symbol: unified market symbol of the market orders were made in
        :param int|None since: timestamp in ms of the earliest order
        :param int|None limit: the maximum amount of orders to fetch
        :param dict params: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders requires a symbol parameter')
        return self.fetch_orders_by_status('open', symbol, since, limit, params)

    def parse_order(self, order, market=None) -> Order:
        id = self.safe_string(order, 'Market_Order__')
        timestamp = self.safe_integer(self.safe_dict(order, 'Created'), 'unixms')
        orderType = self.safe_string(order, 'Type')
        side = 'sell'
        if orderType == 'bid':
            side = 'buy'
        status = self.parse_order_status(self.safe_string(order, 'Status'))
        amount = self.parse_number(self.parse_amount(self.safe_dict(order, 'Amount')))
        price = self.parse_number(self.parse_amount(self.safe_dict(order, 'Price')))
        type = 'market' if (price is None) else 'limit'
        executed = self.parse_number(self.parse_amount(self.safe_dict(order, 'Executed')))
        filled = executed
        remaining = self.parse_number(self.parse_amount(self.safe_dict(order, 'Secured')))
        cost = self.parse_number(self.parse_amount(self.safe_dict(order, 'Total_Spent')))
        symbol = market['symbol'] if market else None
        clientOrderId = None
        timeInForce = 'GTC'  # default to Good Till Cancelled
        postOnly = False
        updated = self.safe_dict(order, 'Updated', {})
        lastTradeTimestamp = self.safe_integer(updated, 'unixms', None)
        return self.safe_order({
            'id': id,
            'clientOrderId': clientOrderId,
            'info': order,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'status': self.parse_order_status(status),
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': side,
            'price': price,
            'triggerPrice': None,
            'average': None,
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'fee': None,
            'trades': None,
        }, market)

    def cancel_order(self, id: str, symbol: Str = None, params={}) -> Order:
        """
        Cancels an open order on the exchange

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.f1qu1pb1rebn

        :param str id: - The order ID to cancel(format: mktor-xxxxx-xxxx-xxxx-xxxx-xxxxxxxx)
        :param str [symbol]: - ellipx.cancelOrder does not use the symbol parameter
        :param dict [params]: - Extra parameters specific to the exchange API
        :returns Promise<dict>: A Promise that resolves to the canceled order info
        """
        self.load_markets()
        request = {
            'orderUuid': id,
        }
        response = self.privateDeleteMarketOrderOrderUuid(self.extend(request, params))
        # {
        #     result: "success",
        #     request_id: "887dba33-d11b-43f0-8034-dd7890882cc5",
        #     time: "0.8975801467895508",
        #     data: True,
        #     access: {
        #       "mktor-rf5k5b-5fhf-dmde-wxqj-3y23jeii": {
        #         required: "A",
        #         available: "O",
        #       },
        #     },
        #   }
        # self endpoint always returns True and a warning message if the order cancelled before.
        warningResponse = self.safe_value(response, 'warning', None)
        statusResponse = self.safe_bool(response, 'data')
        status = 'canceled'
        if statusResponse is not True or warningResponse is not None:
            status = 'closed'
        return self.safe_order({
            'id': id,
            'clientOrderId': None,
            'info': self.json(response),  # original response
            'timestamp': None,
            'datetime': None,
            'lastTradeTimestamp': None,
            'status': status,
            'symbol': None,
            'type': None,
            'timeInForce': None,
            'postOnly': None,
            'side': None,
            'price': None,
            'triggerPrice': None,
            'average': None,
            'cost': None,
            'amount': None,
            'filled': None,
            'remaining': None,
            'fee': None,
            'trades': None,
        }, None)

    def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        fetch all the trades made from a single order
        :param str id: order id
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired('fetchMyTrades requires a symbol parameter')
        self.load_markets()
        market = self.market(symbol)
        currencyPair = market['id']
        request = {
            'Market_Order__': id,
            'currencyPair': currencyPair,
        }
        response = self.privateGetMarketCurrencyPairTrade(self.extend(request, params))
        # {
        #     "result": "success",
        #     "request_id": "fc5be99d-d085-46f8-9228-e46d0996f112",
        #     "time": 0.030913114547729492,
        #     "data": [
        #         {
        #             "id": "DOGE_USDC:1731505789:911642994:0",
        #             "pair": "DOGE_USDC",
        #             "bid": {
        #                 "id": "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4"
        #             },
        #             "ask": {
        #                 "id": "mktor-oxmac4-mtkf-gi3o-mamg-u2cboqe4"
        #             },
        #             "type": "bid",
        #             "amount": {
        #                 "v": "334609419",
        #                 "e": 8,
        #                 "f": 3.34609419
        #             },
        #             "price": {
        #                 "v": "410673",
        #                 "e": 6,
        #                 "f": 0.410673
        #             },
        #             "date": {
        #                 "unix": 1731505789,
        #                 "us": 911642,
        #                 "iso": "2024-11-13 13:49:49.911642",
        #                 "tz": "UTC",
        #                 "full": "1731505789911642",
        #                 "unixms": "1731505789911"
        #             }
        #         },
        #         {
        #             "id": "DOGE_USDC:1731505789:911642994:4",
        #             "pair": "DOGE_USDC",
        #             "bid": {
        #                 "id": "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4"
        #             },
        #             "ask": {
        #                 "id": "mktor-cmtztk-3z3n-gupp-uqdg-74g4wjfq"
        #             },
        #             "type": "bid",
        #             "amount": {
        #                 "v": "145453950",
        #                 "e": 8,
        #                 "f": 1.4545395
        #             },
        #             "price": {
        #                 "v": "412589",
        #                 "e": 6,
        #                 "f": 0.412589
        #             },
        #             "date": {
        #                 "unix": 1731505789,
        #                 "us": 911642,
        #                 "iso": "2024-11-13 13:49:49.911642",
        #                 "tz": "UTC",
        #                 "full": "1731505789911642",
        #                 "unixms": "1731505789911"
        #             }
        #         },
        #         {
        #             "id": "DOGE_USDC:1731505789:911642994:2",
        #             "pair": "DOGE_USDC",
        #             "bid": {
        #                 "id": "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4"
        #             },
        #             "ask": {
        #                 "id": "mktor-6tyslh-b33b-flnm-2ata-acjkco4y"
        #             },
        #             "type": "bid",
        #             "amount": {
        #                 "v": "587627076",
        #                 "e": 8,
        #                 "f": 5.87627076
        #             },
        #             "price": {
        #                 "v": "411005",
        #                 "e": 6,
        #                 "f": 0.411005
        #             },
        #             "date": {
        #                 "unix": 1731505789,
        #                 "us": 911642,
        #                 "iso": "2024-11-13 13:49:49.911642",
        #                 "tz": "UTC",
        #                 "full": "1731505789911642",
        #                 "unixms": "1731505789911"
        #             }
        #         },
        #         {
        #             "id": "DOGE_USDC:1731505789:911642994:1",
        #             "pair": "DOGE_USDC",
        #             "bid": {
        #                 "id": "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4"
        #             },
        #             "ask": {
        #                 "id": "mktor-ihpjlj-5ufj-dm5l-fmud-oftkqcgu"
        #             },
        #             "type": "bid",
        #             "amount": {
        #                 "v": "475845734",
        #                 "e": 8,
        #                 "f": 4.75845734
        #             },
        #             "price": {
        #                 "v": "410830",
        #                 "e": 6,
        #                 "f": 0.41083
        #             },
        #             "date": {
        #                 "unix": 1731505789,
        #                 "us": 911642,
        #                 "iso": "2024-11-13 13:49:49.911642",
        #                 "tz": "UTC",
        #                 "full": "1731505789911642",
        #                 "unixms": "1731505789911"
        #             }
        #         },
        #         {
        #             "id": "DOGE_USDC:1731505789:911642994:3",
        #             "pair": "DOGE_USDC",
        #             "bid": {
        #                 "id": "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4"
        #             },
        #             "ask": {
        #                 "id": "mktor-d2uyb3-nzsj-aevn-dikr-tq3sxhre"
        #             },
        #             "type": "bid",
        #             "amount": {
        #                 "v": "641013461",
        #                 "e": 8,
        #                 "f": 6.41013461
        #             },
        #             "price": {
        #                 "v": "411846",
        #                 "e": 6,
        #                 "f": 0.411846
        #             },
        #             "date": {
        #                 "unix": 1731505789,
        #                 "us": 911642,
        #                 "iso": "2024-11-13 13:49:49.911642",
        #                 "tz": "UTC",
        #                 "full": "1731505789911642",
        #                 "unixms": "1731505789911"
        #             }
        #         }
        #     ],
        #     "access": {
        #         "mkt-xrkg5l-akjz-cxxl-3a2e-mul5gfo4": {
        #             "required": "r",
        #             "available": "?"
        #         },
        #         "mktor-xb3ne5-emm5-fx7e-xggk-fyfoiye4": {
        #             "required": "R",
        #             "available": "O"
        #         }
        #     },
        #     "paging": {
        #         "page_no": 1,
        #         "count": "5",
        #         "page_max": 1,
        #         "results_per_page": 20
        #     }
        # }
        data = self.safe_list(response, 'data')
        return self.parse_trades(data, market, since, limit)

    def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
        """
        fetches a crypto deposit address for a specific currency

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.k7qe5aricayh

        :param str code: unified currency code(e.g. "BTC", "ETH", "USDT")
        :param dict [params]: extra parameters specific to the EllipX API endpoint
        :returns dict: an address structure {
     'currency': string,  # unified currency code
     'address': string,  # the address for deposits
     'tag': string|None,  # tag/memo for deposits if needed
     'network': object,  # network object from currency info
     'info': object  # raw response from exchange
}
        :throws ExchangeError if: currency does not support deposits
        """
        self.load_markets()
        currency = self.currency(code)
        network = self.safe_value(currency['info'], 'Crypto_Chain', None)
        request = {
            'Crypto_Token__': self.safe_string(network, 'Crypto_Token__'),
            'Crypto_Chain__': self.safe_string(network, 'Crypto_Chain__'),
        }
        response = self.privatePostCryptoAddressFetch(self.extend(request, params))
        data = self.safe_value(response, 'data', {})
        address = self.safe_string(data, 'Address')
        tag = self.safe_string(data, 'memo')
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': network,
            'info': response,
        }

    def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
        """
        Fetches the current trading fees(maker and taker) applicable to the user.

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.kki5jay2c8it

        :param str [symbol]: Not used by EllipX are not symbol-specific.
        :param dict [params]: Extra parameters specific to the EllipX API endpoint.
        :returns Promise<dict>: A promise resolving to a unified trading fee structure:
 {
     'info': object,        # the raw response from the exchange
     'symbol': None,   # symbol is not used for self exchange
     'maker': number,       # maker fee rate in decimal form
     'taker': number,       # taker fee rate in decimal form
     'percentage': True,    # indicates fees are in percentage
     'tierBased': False,    # indicates fees do not vary by volume tiers
}
        """
        self.load_markets()
        response = self.privateGetMarketTradeFeeQuery(params)
        #
        # Example response:
        # {
        #     "result": "success",
        #     "data": {
        #         "maker": 15.0,      # in basis points
        #         "taker": 25.0,      # in basis points
        #         "volume": 123456.78,
        #         "promo": {
        #             # promotional discounts if any
        #         }
        #     }
        # }
        #
        data = self.safe_value(response, 'data', {})
        maker = self.safe_number(data, 'maker')  # in basis points
        taker = self.safe_number(data, 'taker')  # in basis points
        makerFee = maker / 10000 if (maker is not None) else None
        takerFee = taker / 10000 if (taker is not None) else None
        return {
            'info': response,
            'symbol': None,  # the exchange only have separate fees for stablecoin pairs
            'maker': makerFee,
            'taker': takerFee,
            'percentage': True,  # fees are expressed in percentages
            'tierBased': True,  # fees can vary based on volume tiers
        }

    def withdraw(self, code: str, amount: float, address: str, tag=None, params={}) -> Transaction:
        """
        Make a withdrawal request

        https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM/edit?tab=t.0#heading=h.zegupoa8g4t9

        :param str code: unified currency code
        :param number amount: Amount to withdraw
        :param str address: Destination wallet address
        :param str [tag]: Additional tag/memo for currencies that require it
        :param dict params: Extra parameters specific to the EllipX API endpoint(Crypto_Chain__, Unit__)
        :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
        """
        self.check_address(address)
        self.load_markets()
        currency = self.currency(code)
        networks = self.safe_value(currency, 'networks')
        if networks is None:
            raise NotSupported(self.id + ' withdraw() for ' + code + ' is not supported')
        chainsResponse = self.privateGetUnitCurrency({'currency': currency['code']})  # fetch Unit__ params for currency
        chainsData = self.safe_value(chainsResponse, 'data', [])
        unit = self.safe_string(chainsData, 'Unit__')
        # check params again and omit params
        self.omit(params, 'Unit__')
        self.omit(params, 'Crypto_Chain__')
        amountString = str(amount)
        request = {
            'Unit__': unit,
            'amount': amountString,
            'address': address,
            'Crypto_Chain__': networks['id'],
        }
        if tag is not None:
            request['memo'] = tag
        response = self.privatePostCryptoDisbursementWithdraw(self.extend(request, params))
        # {
        #     Crypto_Disbursement__: "crdsb-4pw3kg-ipn5-amvb-da4n-6xncy4r4",
        #     Crypto_Token__: "crtok-dnehz4-wbgv-bunf-iyd3-m7gtsz2q",
        #     Crypto_Chain__: "chain-kjfvwn-l2xn-eclc-ul5d-mb6fu5hm",
        #     User__: "usr-5oint6-ozpr-alfp-2wxi-zgbm4osy",
        #     Value: {
        #       v: "1000000000",
        #       e: "8",
        #       f: "10",
        #     },
        #     Value_USD: "4.08723",
        #     Address: "D6z62LUwyNBi3QbPkzW8C4m7VDAgu9wb2Z",
        #     Status: "pending",
        #     Transaction: null,
        #     Requested: {
        #       unix: "1731570982",
        #       us: "203569",
        #       iso: "2024-11-14 07:56:22.203569",
        #       tz: "UTC",
        #       full: "1731570982203569",
        #       unixms: "1731570982203",
        #     },
        #     Scheduled: null,
        #     Processed: null,
        #     Amount: {
        #       value: "10.00000000",
        #       value_int: "1000000000",
        #       value_disp: "10.00000000",
        #       value_xint: {
        #         v: "1000000000",
        #         e: "8",
        #         f: "10",
        #       },
        #       display: "10.00000000DOGE",
        #       display_short: "10.00000000DOGE",
        #       currency: "DOGE",
        #       unit: "DOGE",
        #       has_vat: False,
        #       tax_profile: null,
        #       raw: {
        #         value: "10.00000000",
        #         value_int: "1000000000",
        #         value_disp: "10.00000000",
        #         value_xint: {
        #           v: "1000000000",
        #           e: "8",
        #           f: "10",
        #         },
        #         display: "10.00000000DOGE",
        #         display_short: "10.00000000DOGE",
        #         currency: "DOGE",
        #         unit: "DOGE",
        #         has_vat: False,
        #         tax_profile: null,
        #       },
        #       tax: {
        #         value: "10.00000000",
        #         value_int: "1000000000",
        #         value_disp: "10.00000000",
        #         value_xint: {
        #           v: "1000000000",
        #           e: "8",
        #           f: "10",
        #         },
        #         display: "10.00000000DOGE",
        #         display_short: "10.00000000DOGE",
        #         currency: "DOGE",
        #         unit: "DOGE",
        #         has_vat: True,
        #         tax_profile: null,
        #       },
        #       tax_only: {
        #         value: "0.000",
        #         value_int: "0",
        #         value_disp: "0",
        #         value_xint: {
        #           v: "0",
        #           e: "3",
        #           f: "0",
        #         },
        #         display: "¥0",
        #         display_short: "¥0",
        #         currency: "JPY",
        #         unit: "JPY",
        #         has_vat: False,
        #         tax_profile: null,
        #       },
        #       tax_rate: "0",
        #     },
        #   }
        data = self.safe_dict(response, 'data')
        amountResponse = self.safe_dict(data, 'Amount')
        requested = self.safe_dict(data, 'Requested')
        processed = self.safe_dict(data, 'Processed')
        withdrawId = self.safe_string(data, 'Crypto_Disbursement__')
        timestamp = self.safe_integer(requested, 'unixms')
        return {
            'info': response,
            'id': withdrawId,
            'txid': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': self.safe_string(data, 'Crypto_Chain__'),
            'address': self.safe_string(data, 'Address'),
            'addressTo': self.safe_string(data, 'Address'),
            'addressFrom': None,
            'tag': tag,
            'tagTo': tag,
            'tagFrom': None,
            'type': 'withdrawal',
            'amount': self.safe_number(amountResponse, 'value'),
            'currency': code,
            'status': self.parse_transaction_status(self.safe_string(data, 'Status')),
            'updated': self.safe_timestamp(processed, 'unix'),
            'internal': False,
            'comment': None,
            'fee': {
                'currency': code,
                'cost': None,  # Fee information not provided in response
                'rate': None,
            },
        }

    def parse_transaction_status(self, status: str) -> str:
        statuses = {
            'pending': 'pending',
            'completed': 'ok',
            'failed': 'failed',
            'cancelled': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_status(self, status):
        statuses = {
            'pending': 'open',  # starting state of all orders
            'running': 'open',  # when order is being executed
            'post-pending': 'open',  # post-only order waiting to be placed
            'open': 'open',  # active order in the orderbook
            'stop': 'open',  # when stop order not yet triggered
            'invalid': 'rejected',  # order rejected
            'done': 'closed',  # order fully executed
            'cancel': 'canceled',  # order canceled by user
            'canceled': 'canceled',  # alternative spelling
        }
        return self.safe_string(statuses, status, status)

    def parse_amount(self, amount) -> Str:
        v = self.safe_string(amount, 'v', None)
        e = self.safe_integer(amount, 'e', None)
        if v is None or e is None:
            return None
        precise = Precise(v)
        precise.decimals = e
        precise.reduce()
        amountString = str(precise)
        return amountString

    def to_amount(self, amount: float, precision: float) -> dict:
        v = str(amount)
        e = precision
        return {
            'v': v,
            'e': e,
        }

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        # {
        #     "code": 404,
        #     "error": "Not Found: Crypto\\Token(US)",
        #     "exception": "Exception\\NotFound",
        #     "message": "[I18N:error_not_found]",
        #     "request": "cc83738a-2438-4f53-ae44-f15306c07f32",
        #     "result": "error",
        #     "time": 0.0089569091796875,
        #     "token": "error_not_found"
        # }
        errorCode = self.safe_string(response, 'code')
        message = self.safe_string(response, 'message')
        if errorCode is not None:
            self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, message)
            raise ExchangeError(self.id + ' ' + message)
        return None
