import hashlib
import hmac
import json
import urllib
import requests

from urllib.parse import urlencode, quote

from .constants import *
from .helpers import *

class Client(object):
    def __init__(self, key, secret, subaccount=None, timeout=30):
        self._api_key = key
        self._api_secret = secret
        self._api_subacc = subaccount
        self._api_timeout = int(timeout)

    def _build_headers(self, scope, method, endpoint, query=None):
        if query is None:
            query = {}

        endpoint = '/api/' + endpoint

        headers = {
            'Accept': 'application/json',
            'User-Agent': 'FTX-Trader/1.0',
        }

        if scope.lower() == 'private':
            nonce = str(get_current_timestamp())
            payload = f'{nonce}{method.upper()}{endpoint}'

            if method is 'GET' and query:
                payload += '?' + urlencode(query, safe='/')
            elif query:
                payload += json.dumps(query)
 
            sign = hmac.new(bytes(self._api_secret, 'utf-8'), bytes(payload, 'utf-8'), hashlib.sha256).hexdigest()

            headers.update({
                # This header is REQUIRED to send JSON data.
                'Content-Type': 'application/json',
                'FTX-KEY': self._api_key,
                'FTX-SIGN': sign,
                'FTX-TS': nonce
            })

            if self._api_subacc:
                headers.update({
                # If you want to access a subaccount 
                'FTX-SUBACCOUNT': urllib.parse.quote(self._api_subacc)
            })

        return headers

    def _build_url(self, scope, method, endpoint, query=None):
        if query is None:
            query = {}

        if scope.lower() == 'private':
            url = f"{PRIVATE_API_URL}/{endpoint}"
        else:
            url = f"{PUBLIC_API_URL}/{endpoint}"

        if method == 'GET':
            return f"{url}?{urlencode(query, True, '/[]')}" if len(query) > 0 else url
        else:
            return url

    def _send_request(self, scope, method, endpoint, query=None):
        if query is None:
            query = {}

        # Build header first
        headers = self._build_headers(scope, method, endpoint, query)

        # Build final url here
        url = self._build_url(scope, method, endpoint, query)

        try:
            if method == 'GET':
                response = requests.get(url, headers = headers).json()
            elif method == 'POST':
                response = requests.post(url, headers = headers, json = query).json()
            elif method == 'DELETE':
                if query == {}:
                    response = requests.delete(url, headers = headers).json()
                else:
                    response = requests.delete(url, headers = headers, json = query).json()
        except Exception as e:
            print ('[x] Error: {}'.format(e.args[0]))

        if 'result' in response:
            return response['result']
        else:
            return response

    # Public API
    def get_public_all_markets(self):
        """
        https://docs.ftx.com/#markets

        :return: a list contains all available markets
        """

        return self._send_request('public', 'GET', f"markets")
    
    def get_public_single_market(self, pair):
        """
        https://docs.ftx.com/#get-single-market

        :param pair: the trading pair to query
        :return: a list contains single market info
        """

        return self._send_request('public', 'GET', f"markets/{pair.upper()}")
    
    def get_public_orderbook(self, pair, depth=20):
        """
        https://docs.ftx.com/#get-orderbook

        :param pair: the trading pair to query
        :param depth: the price levels depth to query (max: 100 default: 20)
        :return: a dict contains asks and bids data
        """
    
        return self._send_request('public', 'GET', f"markets/{pair}/orderbook", {'depth': depth})

    def get_public_recent_trades(self, pair, limit=20, start_time=None, end_time=None):
        """
        https://docs.ftx.com/#get-trades

        :param pair: the trading pair to query
        :param limit: the records limit to query
        :param start_time: the target period after an Epoch time in seconds
        :param end_time: the target period before an Epoch time in seconds
        :return: a list contains all completed orders in exchange
        """

        query = {
            'limit': limit,
        }

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time
            })

        return self._send_request('public', 'GET', f"markets/{pair}/trades", query)

    def get_public_k_line(self, pair, resolution=14400, limit=20, start_time=None, end_time=None):
        """
        https://docs.ftx.com/#get-historical-prices

        :param pair: the trading pair to query
        :param resolution: the time period of K line in seconds
        :param limit: the records limit to query
        :param start_time: the target period after an Epoch time in seconds
        :param end_time: the target period before an Epoch time in seconds
        :return: a list contains all OHLC prices in exchange
        """

        query = {
            'resolution': resolution,
            'limit': limit,
        }

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time
            })

        return self._send_request('public', 'GET', f"markets/{pair}/candles", query)

    def get_public_all_futures(self):
        """
        https://docs.ftx.com/#list-all-futures

        :return: a list contains all available futures
        """

        return self._send_request('public', 'GET', f"futures")

    def get_public_all_perpetual_futures(self):
        """
        https://docs.ftx.com/#list-all-futures

        :return: a list contains all available perpetual futures
        """
        response = []
        for perpetual in self.get_public_all_futures():
            if perpetual['perpetual'] is True:
                response.append(perpetual)
        
        return response
    
    def get_public_single_future(self, pair):
        """
        https://docs.ftx.com/#get-future

        :param pair: the trading pair to query
        :return: a list contains single future info
        """

        return self._send_request('public', 'GET', f"futures/{pair.upper()}")

    def get_public_future_stats(self, pair):
        """
        https://docs.ftx.com/#get-future-stats

        :param pair: the trading pair to query
        :return: a list contains stats of a future
        """

        return self._send_request('public', 'GET', f"futures/{pair.upper()}/stats")
    
    def get_public_all_funding_rates(self):
        """
        https://docs.ftx.com/#get-funding-rates

        :return: a list contains all funding rate of perpetual futures
        """

        return self._send_request('public', 'GET', f"funding_rates")

    def get_public_single_funding_rates(self, future, start_time=None, end_time=None):
        """
        https://docs.ftx.com/#get-funding-rates

        :param future: the trading future to query 
        :return: a list contains all funding rate of perpetual futures
        """

        query = {
            'future': future,
        }

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time
            })

        return self._send_request('public', 'GET', f"funding_rates", query)

    # TODO: Note that this only applies to index futures, e.g. ALT/MID/SHIT/EXCH/DRAGON.
    def get_public_etf_future_index(self, index):
        """
        https://docs.ftx.com/#get-index-weights

        :param index: the trading index to query
        :return: a list contains all component coins in ETF Future
        """

        return self._send_request('public', 'GET', f"indexes/{index}/weights")

    def get_public_all_expired_futures(self):
        """
        https://docs.ftx.com/#get-expired-futures

        :return: a list contains all expired futures
        """

        return self._send_request('public', 'GET', f"expired_futures")

    def get_public_index_k_line(self, index, resolution=14400, limit=20, start_time=None, end_time=None):
        """
        https://docs.ftx.com/#get-historical-index

        :param index: the trading index to query
        :param resolution: the time period of K line in seconds
        :param limit: the records limit to query
        :param start_time: the target period after an Epoch time in seconds
        :param end_time: the target period before an Epoch time in seconds
        :return: a list contains all OHLC prices of etf index in exchange
        """

        query = {
            'resolution': resolution,
            'limit': limit,
        }

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time
            })

        return self._send_request('public', 'GET', f"indexes/{index}/candles", query)

    # Private API
    def get_private_account_information(self):
        """
        https://docs.ftx.com/#get-account-information

        :return: a dict contains all personal profile and positions information
        """

        return self._send_request('private', 'GET', f"account")
    
    def get_private_account_positions(self, showAvgPrice=False):
        """
        https://docs.ftx.com/#get-positions

        :param showAvgPrice: display AvgPrice or not
        :return: a dict contains all positions
        """
        
        return self._send_request('private', 'GET', f"positions", {'showAvgPrice': showAvgPrice})
    
    def get_private_all_subaccounts(self):
        """
        https://docs.ftx.com/#get-all-subaccounts
        
        :return: a list contains all subaccounts
        """

        return self._send_request('private', 'GET', f"subaccounts")
    
    def get_private_subaccount_balances(self, name):
        """
        https://docs.ftx.com/#get-subaccount-balances

        :param name: the subaccount name to query
        :return: a list contains subaccount balances
        """

        return self._send_request('private', 'GET', f"subaccounts/{name}/balances")
    
    def get_private_wallet_coins(self):
        """
        https://docs.ftx.com/#get-coins

        :return: a list contains all coins in wallet
        """

        return self._send_request('private', 'GET', f"wallet/coins")
    
    def get_private_wallet_balances(self):
        """
        https://docs.ftx.com/#get-balances

        :return: a list contains current account balances
        """

        return self._send_request('private', 'GET', f"wallet/balances")
    
    def get_private_wallet_all_balances(self):
        """
        https://docs.ftx.com/#get-balances-of-all-accounts

        :return: a list contains all accounts balances
        """
        
        return self._send_request('private', 'GET', f"wallet/all_balances")
    
    def get_private_wallet_deposit_address(self, coin, chain):
        """
        https://docs.ftx.com/#get-deposit-address

        :param currency: the specific coin to endpoint
        :param chain: the blockchain deposit from, should be 'omni' or 'erc20', 'trx' or 'sol'
        :return: a list contains deposit address
        """

        return self._send_request('private', 'GET', f"wallet/deposit_address/{coin.upper()}", {'method': chain})

    def get_private_wallet_deposit_history(self, limit=20, start_time=None, end_time=None):
        """
        https://docs.ftx.com/#get-deposit-history

        :param limit: the records limit to query
        :param start_time: the target period after an Epoch time in seconds
        :param end_time: the target period before an Epoch time in seconds
        :return: a list contains deposit history
        """

        query = {
            'limit': limit,
        }

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time
            })

        return self._send_request('private', 'GET', f"wallet/deposits", query)
    
    def get_private_wallet_withdraw_history(self, limit=20, start_time=None, end_time=None):
        """
        https://docs.ftx.com/#get-withdrawal-history

        :param limit: the records limit to query
        :param start_time: the target period after an Epoch time in seconds
        :param end_time: the target period before an Epoch time in seconds
        :return: a list contains withdraw history
        """
    
        query = {
            'limit': limit,
        }

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time
            })

        return self._send_request('private', 'GET', f"wallet/withdrawals", query)

    def get_private_wallet_airdrops(self, limit=20, start_time=None, end_time=None):
        """
        https://docs.ftx.com/#get-airdrops

        :param limit: the records limit to query
        :param start_time: the target period after an Epoch time in seconds
        :param end_time: the target period before an Epoch time in seconds
        :return: a list contains airdrop history
        """
    
        query = {
            'limit': limit,
        }

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time
            })

        return self._send_request('private', 'GET', f"wallet/airdrops", query)

    def get_private_funding_payments(self, coin=None, start_time=None, end_time=None):
        """
        https://docs.ftx.com/#funding-payments

        :param coin: the trading coin to query
        :param start_time: the target period after an Epoch time in seconds
        :param end_time: the target period before an Epoch time in seconds
        :return: a list contains all funding payments of perpetual future
        """

        query = {}

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time,
            })

        if coin != None:
            query.update({ 
                'future': coin.upper() + '-PERP'
            })

        return self._send_request('private', 'GET', f"funding_payments", query)

    def get_private_bills(self, pair, limit=20, start_time=None, end_time=None, order=None, _orderId=None):
        """
        https://docs.ftx.com/#fills

        :param pair: the trading pair to query
        :param limit: the records limit to query
        :param start_time: the target period after an Epoch time in seconds
        :param end_time: the target period before an Epoch time in seconds
        :param order: sort the bill by created time, default is descending, supply 'asc' to receive fills in ascending order of time
        :param _orderId: the id of the order
        :return: a list contains all bills
        """

        query = {
            'market': pair,
            'limit': limit,
        }

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time,
            })

        if order != None:
            query.update({
                'order': order,
            })

        if _orderId != None:
            query.update({
                'orderId': _orderId
            })
        
        return self._send_request('private', 'GET', f"fills", query)

    def get_private_open_orders(self, pair=None):
        """
        https://docs.ftx.com/?python#get-open-orders

        :param pair: the trading pair to query
        :return: a list contains all open orders
        """
        query = {}
        
        if pair is not None:
            query['market'] = pair

        return self._send_request('private', 'GET', f"orders", query)

    def get_private_order_history(self, pair=None, start_time=None, end_time=None, limit=None):
        """
        https://docs.ftx.com/?python#get-order-history

        :param pair: the trading pair to query
        :param start_time: the target period after an Epoch time in seconds
        :param end_time: the target period before an Epoch time in seconds
        :param limit: the records limit to query
        :return: a list contains all history orders
        """

        query = {}

        if pair != None:
            query.update({ 
                'market': pair,
            })

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time,
            })

        if limit != None:
            query.update({ 
                'limit': limit
            })

        return self._send_request('private', 'GET', f"orders/history", query)

    def get_private_open_trigger_orders(self, pair=None, _type=None):
        """
        https://docs.ftx.com/?python#get-open-trigger-orders

        :param pair: the trading pair to query
        :param _type: type of trigger order, should only be stop, trailing_stop, or take_profit
        :return: a list contains all open trigger orders
        """

        query = {}

        if pair != None:
            query.update({ 
                'market': pair,
            })

        if _type != None:
            query.update({ 
                'type': _type
            })

        return self._send_request('private', 'GET', f"conditional_orders", query)

    def get_private_trigger_order_triggers(self, _orderId):
        """
        https://docs.ftx.com/?python#get-open-trigger-orders

        :param _orderId: the id of the order
        :return: a list contains trigger order triggers
        """

        return self._send_request('private', 'GET', f"conditional_orders/{_orderId}/triggers")

    def get_private_trigger_order_history(self, pair=None, start_time=None, end_time=None, side=None,
                                          _type=None, _orderType=None, limit=None):
        """
        https://docs.ftx.com/?python#get-trigger-order-history

        :param pair: the trading pair to query
        :param start_time: the target period after an Epoch time in seconds
        :param end_time: the target period before an Epoch time in seconds
        :param side: the trading side, should only be buy or sell
        :param _type: type of trigger order, should only be stop, trailing_stop, or take_profit
        :param _orderType: the order type, should only be limit or market
        :param limit: the records limit to query
        :return: a list contains all history trigger orders
        """

        query = {}

        if pair != None:
            query.update({ 
                'market': pair,
            })

        if start_time != None:
            query.update({ 
                'start_time': start_time,
            })
        
        if end_time != None:
            query.update({ 
                'end_time': end_time,
            })

        if side != None:
            query.update({ 
                'side': side,
            })

        if _type != None:
            query.update({ 
                'type': _type,
            })

        if _orderType != None:
            query.update({ 
                'orderType': _orderType,
            })

        if limit != None:
            query.update({ 
                'limit': limit
            })

        return self._send_request('private', 'GET', f"conditional_orders/history", query)

    def get_private_order_status(self, orderId):
        """
        https://docs.ftx.com/#get-order-status

        :param orderId: the order ID
        :return a list contains status of the order
        """

        return self._send_request('private', 'GET', f"orders/{orderId}")

    def get_public_order_status_by_clientId(self, clientId):
        """
        https://docs.ftx.com/#get-order-status-by-client-id

        :param clientOrderId: the client order ID
        :return a list contains status of the order
        """

        return self._send_request('private', 'GET', f"orders/by_client_id/{clientId}")

    def get_private_margin_borrow_rates(self):
        """
        https://docs.ftx.com/#get-borrow-rates

        :return a list contains borrow rates (include estimate rates and previous rates)
        """

        return self._send_request('private', 'GET', f"spot_margin/borrow_rates")
    
    def get_private_margin_lending_rates(self):
        """
        https://docs.ftx.com/#get-lending-rates

        :return a list contains lending rates (include estimate rates and previous rates)
        """

        return self._send_request('private', 'GET', f"spot_margin/lending_rates")

    def get_private_margin_daily_data(self):
        """
        https://docs.ftx.com/#get-daily-borrowed-amounts

        :return a list contains average matched borrowed and lent amount over the last 24h
        """

        return self._send_request('private', 'GET', f"spot_margin/borrow_summary")

    def get_private_margin_single_market_info(self, pair):
        """
        https://docs.ftx.com/#get-market-info

        :param coin: the spot margin trading pair to query
        :return a list contains current margin all markets info
        """

        return self._send_request('private', 'GET', f"spot_margin/market_info", {'market': pair})

    def get_private_margin_borrow_history(self):
        """
        https://docs.ftx.com/#get-borrow-history

        :return a list contains borrow history
        """

        return self._send_request('private', 'GET', f"spot_margin/borrow_history")

    def get_private_margin_lending_history(self):
        """
        https://docs.ftx.com/#get-lending-history

        :return a list contains lending history
        """

        return self._send_request('private', 'GET', f"spot_margin/lending_history")


    def get_private_margin_lending_offers(self):
        """
        https://docs.ftx.com/#get-lending-offers

        :return a list contains lending offers
        """

        return self._send_request('private', 'GET', f"spot_margin/offers")

    def get_private_margin_lending_info(self):
        """
        https://docs.ftx.com/#get-lending-info

        :return a list contains lending info
        """

        return self._send_request('private', 'GET', f"spot_margin/lending_info")

    # Private API (Write)
    def set_private_create_subaccount(self, name):
        """
        https://docs.ftx.com/?python#create-subaccount

        :param name: new subaccount name
        :return: a list contains new subaccount info
        """

        return self._send_request('private', 'POST', f"subaccounts", {'nickname': name})
    
    def set_private_change_subaccount_name(self, name, newname):
        """
        https://docs.ftx.com/?python#change-subaccount-name

        :param name: current subaccount name
        :param newname: new nickname of subaccount
        :return: a list contains status
        """

        query = {
            'nickname': name,
            'newNickname': newname
        }

        return self._send_request('private', 'POST', f"subaccounts/update_name", query)

    def set_private_delete_subaccount(self, name):
        """
        https://docs.ftx.com/?python#delete-subaccount

        :param name: the nickname wanna delete
        :return: a list contains status
        """

        return self._send_request('private', 'DELETE', f"subaccounts", {'nickname': name})

    # TODO: Endpoint Error > Not allowed with internal-transfers-disabled permissions
    def set_private_transfer_balances(self, coin, size, source, destination):
        """
        https://docs.ftx.com/?python#transfer-between-subaccounts

        :param coin: the transfering coin to query
        :param size: the size wanna transfer to query
        :param source: the name of the source subaccount. Use null or 'main' for the main account
        :param destination: the name of the destination subaccount. Use null or 'main' for the main account 
        :return: a list contains status
        """

        query = {
            'coin': coin,
            'size': size,
            'source': source,
            'destination': destination
        }

        return self._send_request('private', 'POST', f"subaccounts/transfer", query)

    def set_private_change_account_leverage(self, leverage):
        """
        https://docs.ftx.com/?python#change-account-leverage

        :param leverage: desired acccount-wide leverage setting
        :return: a list contains status
        """

        return self._send_request('private', 'POST', f"account/leverage", {'leverage': leverage})

    def set_private_create_order(self, pair, side, price, _type, size, reduceOnly=False,
                                 ioc=False, postOnly=False, clientId=None):
        """
        https://docs.ftx.com/?python#place-order

        :param pair: the trading pair to query. e.g. "BTC/USD" for spot, "XRP-PERP" for futures
        :param side: the trading side, should only be buy or sell
        :param price: the order price, Send null for market orders.
        :param _type: type of order, should only be limit or market
        :param size: the amount of the order for the trading pair
        :param reduceOnly: only reduce position, default is false (future only)
        :param ioc: immediate or cancel order, default is false
        :param postOnly: always maker, default is false
        :param clientId: client order id
        :return: a list contains all info about new order
        """
        
        query = {
            'market': pair,
            'side': side,
            'price': price,
            'type': _type,
            'size': size,
            'reduceOnly': reduceOnly,
            'ioc': ioc,
            'postOnly': postOnly,
        }

        if clientId != None:
            query.update({
                'clientId': clientId
            })
        
        return self._send_request('private', 'POST', f"orders", query)

    def set_private_create_trigger_order(self, pair, side, triggerPrice, size, orderPrice=None, 
                                         _type='stop', reduceOnly=False, retryUntilFilled=True):
        """
        https://docs.ftx.com/?python#place-trigger-order

        :param pair: the trading pair to query. e.g. "BTC/USD" for spot, "XRP-PERP" for futures
        :param side: the trading side, should only be buy or sell
        :param triggerPrice: the order trigger price
        :param orderPrice: the order price, order type is limit if this is specified; otherwise market
        :param size: the amount of the order for the trading pair
        :param _type: type of order, should only be stop, trailingStop or takeProfit, default is stop
        :param reduceOnly: only reduce position, default is false (future only)
        :param retryUntilFilled: Whether or not to keep re-triggering until filled. optional, default true for market orders
        :return: a list contains all info about new trigger order
        """

        query = {
            'market': pair,
            'side': side,
            'triggerPrice': triggerPrice,
        }

        if orderPrice != None:
            query.update({
                'orderPrice': orderPrice,
            })
        
        query.update({
            'size': size,
            'type': _type,
            'reduceOnly': reduceOnly,
            'retryUntilFilled': retryUntilFilled
        })

        return self._send_request('private', 'POST', f"conditional_orders", query)

    # TODO: Either price or size must be specified
    def set_private_modify_order(self, orderId, price=None, size=None, clientId=None):
        """
        https://docs.ftx.com/#modify-order

        Please note that the order's queue priority will be reset, and the order ID of the modified order will be different from that of the original order. Also note: this is implemented as cancelling and replacing your order. There's a chance that the order meant to be cancelled gets filled and its replacement still gets placed.

        :param orderId: the order ID
        :param price: the modify price
        :param size: the modify amount of the order for the trading pair
        :param clientId: client order id
        :return a list contains all info after modify the order
        """

        query = {}

        if price != None:
            query.update = ({
                'price': price,
            })

        if size != None:
            query.update = ({
                'size': size,
            })

        if clientId != None:
            query.update({
                'clientId': clientId
            })

        return self._send_request('private', 'POST', f"orders/{orderId}/modify", query)

    # TODO: Either price or size must be specified
    def set_private_modify_order_by_clientId(self, clientOrderId, price=None, size=None, clientId=None):
        """
        https://docs.ftx.com/#modify-order-by-client-id

        Please note that the order's queue priority will be reset, and the order ID of the modified order will be different from that of the original order. Also note: this is implemented as cancelling and replacing your order. There's a chance that the order meant to be cancelled gets filled and its replacement still gets placed.

        :param clientOrderId: the client order ID
        :param price: the modify price
        :param size: the modify amount of the order for the trading pair
        :param clientId: client order id
        :return a list contains all info after modify the order
        """

        query = {}

        if price != None:
            query.update = ({
                'price': price,
            })

        if size != None:
            query.update = ({
                'size': size,
            })

        if clientId != None:
            query.update({
                'clientId': clientId
            })

        return self._send_request('private', 'POST', f"orders/by_client_id/{clientOrderId}/modify", query)

    def set_private_modify_trigger_order(self, orderId, _type, size, triggerPrice=None, orderPrice=None, trailValue=None):
        """
        https://docs.ftx.com/#modify-trigger-order

        Please note that the order ID of the modified order will be different from that of the original order.


        :param orderId: the order ID
        :param _type: type of order, should only be stop, trailingStop or takeProfit, default is stop
        :param size: the modify amount of the order for the trading pair
        :param triggerPrice: the modify trigger price
        :param orderPrice: the order price, order type is limit if this is specified; otherwise market
        :param trailValue: negative for sell orders; positive for buy orders
        :return a list contains all info after modify the trigger order
        """

        if _type is 'stop' or _type is 'takeProfit':
            query = {
                'size': size,
                'triggerPrice': triggerPrice,
            }
            if orderPrice is not None:
                query.update({
                    'orderPrice': orderPrice
                })
        else:
            query = {
                'size': size,
                'trailValue': trailValue
            }

        return self._send_request('private', 'POST', f"conditional_orders/{orderId}/modify", query)

    def set_private_cancel_order(self, orderId):
        """
        https://docs.ftx.com/#cancel-order

        :param orderId: the order ID
        :return a list contains result
        """

        return self._send_request('private', "DELETE", f"orders/{orderId}")


    def set_private_cancel_order_by_clientID(self, clientId):
        """
        https://docs.ftx.com/#cancel-order-by-client-id

        :param clientOrderId: the client order ID
        :return a list contains result
        """

        return self._send_request('private', "DELETE", f"orders/by_client_id/{clientId}")
        
    def set_private_cancel_trigger_order(self, orderId):
        """
        https://docs.ftx.com/#cancel-open-trigger-order

        :param orderId: the order ID
        :return a list contains result
        """

        return self._send_request('private', "DELETE", f"conditional_orders/{orderId}")

    def set_private_cancel_all_order(self, pair=None, conditionalOrdersOnly=False, limitOrdersOnly=False):
        """
        https://docs.ftx.com/#cancel-all-orders

        :param pair: the trading pair to query.
        :param conditionalOrdersOnly: default False.
        :param limitOrdersOnly: default False.
        :return a list contains result
        """
        if pair is not None:
            query = {
                'market': pair,
                'conditionalOrdersOnly': conditionalOrdersOnly,
                'limitOrdersOnly': limitOrdersOnly
            }
        else:
            query = {
                'conditionalOrdersOnly': conditionalOrdersOnly,
                'limitOrdersOnly': limitOrdersOnly
            }
        
        return self._send_request('private', 'DELETE', f"orders", query)

    # SRM Stake

    def get_private_srm_stake_history(self):
        """
        https://docs.ftx.com/#get-stakes

        :return a list contains srm stake history
        """

        return self._send_request('private', 'GET', f"srm_stakes/stakes")
    
    def get_private_srm_unstake_history(self):
        """
        https://docs.ftx.com/#unstake-request

        :return a list contains srm unstake history
        """

        return self._send_request('private', 'GET', f"srm_stakes/unstake_requests")
    
    def get_private_srm_stake_balances(self):
        """
        https://docs.ftx.com/#get-stake-balances

        :return a list contains actively staked, scheduled for unstaking and lifetime rewards balances
        """

        return self._send_request('private', 'GET', f"srm_stakes/balances")

    def get_private_srm_stake_rewards_history(self):
        """
        https://docs.ftx.com/#get-staking-rewards

        :return a list contains srm staking rewards
        """

        return self._send_request('private', 'GET', f"srm_stakes/staking_rewards")

    def set_private_srm_unstake(self, coin, size):
        """
        https://docs.ftx.com/#unstake-request-2

        :param coin: the staking coin to query
        :param size: the amount of the request for the stake coin
        :return a list contains result
        """

        query = {
            'coin': coin,
            'size': size
        }

        return self._send_request('private', 'POST', f"srm_stakes/unstake_requests", query)

    def set_private_cancel_srm_unstake(self, stakeId):
        """
        https://docs.ftx.com/#cancel-unstake-request

        :param stakeId: the id of staking request
        :return a list contains result
        """

        return self._send_request('private', "DELETE", f"srm_stakes/unstake_requests/{stakeId}")

    def set_private_srm_stake(self, coin, size):
        """
        https://docs.ftx.com/#stake-request

        :param coin: the staking coin to query
        :param size: the amount of the request for the stake coin
        :return a list contains result
        """

        query = {
            'coin': coin,
            'size': size
        }

        return self._send_request('private', 'POST', f"srm_stakes/stakes", query)

    def set_private_margin_lending_offer(self, coin, size, rate):
        """
        https://docs.ftx.com/#submit-lending-offer

        :param coin: the lending coin to query
        :param size: the amount of the request for the lend coin (Cancel for 0)
        :param rate: the rate wanna offer
        :return a list contains result
        """

        query = {
            'coin': coin,
            'size': size,
            'rate': rate
        }

        return self._send_request('private', 'POST', f"spot_margin/offers", query)