7869 lines
349 KiB
Python
7869 lines
349 KiB
Python
# -*- 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.gate import ImplicitAPI
|
|
import hashlib
|
|
from ccxt.base.types import Any, Balances, BorrowInterest, Bool, Currencies, Currency, DepositAddress, FundingHistory, Greeks, Int, LedgerEntry, Leverage, Leverages, LeverageTier, LeverageTiers, MarginModification, Market, Num, Option, OptionChain, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFeeInterface, TradingFees, Transaction, MarketInterface, TransferEntry
|
|
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 AccountNotEnabled
|
|
from ccxt.base.errors import AccountSuspended
|
|
from ccxt.base.errors import ArgumentsRequired
|
|
from ccxt.base.errors import BadRequest
|
|
from ccxt.base.errors import BadSymbol
|
|
from ccxt.base.errors import InsufficientFunds
|
|
from ccxt.base.errors import InvalidOrder
|
|
from ccxt.base.errors import OrderNotFound
|
|
from ccxt.base.errors import OrderImmediatelyFillable
|
|
from ccxt.base.errors import NotSupported
|
|
from ccxt.base.errors import RateLimitExceeded
|
|
from ccxt.base.errors import ExchangeNotAvailable
|
|
from ccxt.base.decimal_to_precision import TICK_SIZE
|
|
from ccxt.base.precise import Precise
|
|
|
|
|
|
class gate(Exchange, ImplicitAPI):
|
|
|
|
def describe(self) -> Any:
|
|
return self.deep_extend(super(gate, self).describe(), {
|
|
'id': 'gate',
|
|
'name': 'Gate',
|
|
'countries': ['KR'],
|
|
'rateLimit': 50, # 200 requests per 10 second or 50ms
|
|
'version': 'v4',
|
|
'certified': True,
|
|
'pro': True,
|
|
'urls': {
|
|
'logo': 'https://github.com/user-attachments/assets/64f988c5-07b6-4652-b5c1-679a6bf67c85',
|
|
'doc': 'https://www.gate.com/docs/developers/apiv4/en/',
|
|
'www': 'https://gate.com',
|
|
'api': {
|
|
'public': {
|
|
'wallet': 'https://api.gateio.ws/api/v4',
|
|
'futures': 'https://api.gateio.ws/api/v4',
|
|
'margin': 'https://api.gateio.ws/api/v4',
|
|
'delivery': 'https://api.gateio.ws/api/v4',
|
|
'spot': 'https://api.gateio.ws/api/v4',
|
|
'options': 'https://api.gateio.ws/api/v4',
|
|
'sub_accounts': 'https://api.gateio.ws/api/v4',
|
|
'earn': 'https://api.gateio.ws/api/v4',
|
|
},
|
|
'private': {
|
|
'withdrawals': 'https://api.gateio.ws/api/v4',
|
|
'wallet': 'https://api.gateio.ws/api/v4',
|
|
'futures': 'https://api.gateio.ws/api/v4',
|
|
'margin': 'https://api.gateio.ws/api/v4',
|
|
'delivery': 'https://api.gateio.ws/api/v4',
|
|
'spot': 'https://api.gateio.ws/api/v4',
|
|
'options': 'https://api.gateio.ws/api/v4',
|
|
'subAccounts': 'https://api.gateio.ws/api/v4',
|
|
'unified': 'https://api.gateio.ws/api/v4',
|
|
'rebate': 'https://api.gateio.ws/api/v4',
|
|
'earn': 'https://api.gateio.ws/api/v4',
|
|
'account': 'https://api.gateio.ws/api/v4',
|
|
'loan': 'https://api.gateio.ws/api/v4',
|
|
},
|
|
},
|
|
'test': {
|
|
'public': {
|
|
'futures': 'https://api-testnet.gateapi.io/api/v4',
|
|
'delivery': 'https://api-testnet.gateapi.io/api/v4',
|
|
'options': 'https://api-testnet.gateapi.io/api/v4',
|
|
'spot': 'https://api-testnet.gateapi.io/api/v4',
|
|
'wallet': 'https://api-testnet.gateapi.io/api/v4',
|
|
'margin': 'https://api-testnet.gateapi.io/api/v4',
|
|
'sub_accounts': 'https://api-testnet.gateapi.io/api/v4',
|
|
'account': 'https://api-testnet.gateapi.io/api/v4',
|
|
},
|
|
'private': {
|
|
'futures': 'https://api-testnet.gateapi.io/api/v4',
|
|
'delivery': 'https://api-testnet.gateapi.io/api/v4',
|
|
'options': 'https://api-testnet.gateapi.io/api/v4',
|
|
'spot': 'https://api-testnet.gateapi.io/api/v4',
|
|
'wallet': 'https://api-testnet.gateapi.io/api/v4',
|
|
'margin': 'https://api-testnet.gateapi.io/api/v4',
|
|
'sub_accounts': 'https://api-testnet.gateapi.io/api/v4',
|
|
'account': 'https://api-testnet.gateapi.io/api/v4',
|
|
},
|
|
},
|
|
'referral': {
|
|
'url': 'https://www.gate.com/share/CCXTGATE',
|
|
'discount': 0.2,
|
|
},
|
|
},
|
|
'has': {
|
|
'CORS': None,
|
|
'spot': True,
|
|
'margin': True,
|
|
'swap': True,
|
|
'future': True,
|
|
'option': True,
|
|
'addMargin': True,
|
|
'borrowCrossMargin': True,
|
|
'borrowIsolatedMargin': True,
|
|
'cancelAllOrders': True,
|
|
'cancelOrder': True,
|
|
'cancelOrders': True,
|
|
'cancelOrdersForSymbols': True,
|
|
'createMarketBuyOrderWithCost': True,
|
|
'createMarketOrder': True,
|
|
'createMarketOrderWithCost': False,
|
|
'createMarketSellOrderWithCost': False,
|
|
'createOrder': True,
|
|
'createOrders': True,
|
|
'createPostOnlyOrder': True,
|
|
'createReduceOnlyOrder': True,
|
|
'createStopLimitOrder': True,
|
|
'createStopLossOrder': True,
|
|
'createStopMarketOrder': False,
|
|
'createStopOrder': True,
|
|
'createTakeProfitOrder': True,
|
|
'createTriggerOrder': True,
|
|
'editOrder': True,
|
|
'fetchBalance': True,
|
|
'fetchBorrowInterest': True,
|
|
'fetchBorrowRateHistories': False,
|
|
'fetchBorrowRateHistory': False,
|
|
'fetchClosedOrders': True,
|
|
'fetchCrossBorrowRate': False,
|
|
'fetchCrossBorrowRates': False,
|
|
'fetchCurrencies': True,
|
|
'fetchDepositAddress': True,
|
|
'fetchDepositAddresses': False,
|
|
'fetchDepositAddressesByNetwork': True,
|
|
'fetchDeposits': True,
|
|
'fetchDepositWithdrawFee': 'emulated',
|
|
'fetchDepositWithdrawFees': True,
|
|
'fetchFundingHistory': True,
|
|
'fetchFundingRate': True,
|
|
'fetchFundingRateHistory': True,
|
|
'fetchFundingRates': True,
|
|
'fetchGreeks': True,
|
|
'fetchIndexOHLCV': True,
|
|
'fetchIsolatedBorrowRate': False,
|
|
'fetchIsolatedBorrowRates': False,
|
|
'fetchLedger': True,
|
|
'fetchLeverage': True,
|
|
'fetchLeverages': True,
|
|
'fetchLeverageTiers': True,
|
|
'fetchLiquidations': True,
|
|
'fetchMarginAdjustmentHistory': False,
|
|
'fetchMarginMode': False,
|
|
'fetchMarketLeverageTiers': True,
|
|
'fetchMarkets': True,
|
|
'fetchMarkOHLCV': True,
|
|
'fetchMyLiquidations': True,
|
|
'fetchMySettlementHistory': True,
|
|
'fetchMyTrades': True,
|
|
'fetchNetworkDepositAddress': True,
|
|
'fetchOHLCV': True,
|
|
'fetchOpenInterest': False,
|
|
'fetchOpenInterestHistory': True,
|
|
'fetchOpenOrders': True,
|
|
'fetchOption': True,
|
|
'fetchOptionChain': True,
|
|
'fetchOrder': True,
|
|
'fetchOrderBook': True,
|
|
'fetchPosition': True,
|
|
'fetchPositionHistory': 'emulated',
|
|
'fetchPositionMode': False,
|
|
'fetchPositions': True,
|
|
'fetchPositionsHistory': True,
|
|
'fetchPremiumIndexOHLCV': False,
|
|
'fetchSettlementHistory': True,
|
|
'fetchTicker': True,
|
|
'fetchTickers': True,
|
|
'fetchTime': True,
|
|
'fetchTrades': True,
|
|
'fetchTradingFee': True,
|
|
'fetchTradingFees': True,
|
|
'fetchTransactionFees': True,
|
|
'fetchUnderlyingAssets': True,
|
|
'fetchVolatilityHistory': False,
|
|
'fetchWithdrawals': True,
|
|
'reduceMargin': True,
|
|
'repayCrossMargin': True,
|
|
'repayIsolatedMargin': True,
|
|
'sandbox': True,
|
|
'setLeverage': True,
|
|
'setMarginMode': False,
|
|
'setPositionMode': True,
|
|
'signIn': False,
|
|
'transfer': True,
|
|
'withdraw': True,
|
|
},
|
|
'api': {
|
|
'public': {
|
|
# All public endpoints 200r/10s per endpoint
|
|
'wallet': {
|
|
'get': {
|
|
'currency_chains': 1,
|
|
},
|
|
},
|
|
'unified': {
|
|
'get': {
|
|
'currencies': 1,
|
|
'history_loan_rate': 1,
|
|
},
|
|
},
|
|
'spot': {
|
|
'get': {
|
|
'currencies': 1,
|
|
'currencies/{currency}': 1,
|
|
'currency_pairs': 1,
|
|
'currency_pairs/{currency_pair}': 1,
|
|
'tickers': 1,
|
|
'order_book': 1,
|
|
'trades': 1,
|
|
'candlesticks': 1,
|
|
'time': 1,
|
|
'insurance_history': 1,
|
|
},
|
|
},
|
|
'margin': {
|
|
'get': {
|
|
'uni/currency_pairs': 1,
|
|
'uni/currency_pairs/{currency_pair}': 1,
|
|
'loan_margin_tiers': 1,
|
|
'currency_pairs': 1, # deprecated
|
|
'currency_pairs/{currency_pair}': 1, # deprecated
|
|
'funding_book': 1, # deprecated
|
|
'cross/currencies': 1, # deprecated
|
|
'cross/currencies/{currency}': 1, # deprecated
|
|
},
|
|
},
|
|
'flash_swap': {
|
|
'get': {
|
|
'currency_pairs': 1,
|
|
'currencies': 1, # deprecated
|
|
},
|
|
},
|
|
'futures': {
|
|
'get': {
|
|
'{settle}/contracts': 1,
|
|
'{settle}/contracts/{contract}': 1,
|
|
'{settle}/order_book': 1,
|
|
'{settle}/trades': 1,
|
|
'{settle}/candlesticks': 1,
|
|
'{settle}/premium_index': 1,
|
|
'{settle}/tickers': 1,
|
|
'{settle}/funding_rate': 1,
|
|
'{settle}/insurance': 1,
|
|
'{settle}/contract_stats': 1,
|
|
'{settle}/index_constituents/{index}': 1,
|
|
'{settle}/liq_orders': 1,
|
|
'{settle}/risk_limit_tiers': 1,
|
|
},
|
|
},
|
|
'delivery': {
|
|
'get': {
|
|
'{settle}/contracts': 1,
|
|
'{settle}/contracts/{contract}': 1,
|
|
'{settle}/order_book': 1,
|
|
'{settle}/trades': 1,
|
|
'{settle}/candlesticks': 1,
|
|
'{settle}/tickers': 1,
|
|
'{settle}/insurance': 1,
|
|
'{settle}/risk_limit_tiers': 1,
|
|
},
|
|
},
|
|
'options': {
|
|
'get': {
|
|
'underlyings': 1,
|
|
'expirations': 1,
|
|
'contracts': 1,
|
|
'contracts/{contract}': 1,
|
|
'settlements': 1,
|
|
'settlements/{contract}': 1,
|
|
'order_book': 1,
|
|
'tickers': 1,
|
|
'underlying/tickers/{underlying}': 1,
|
|
'candlesticks': 1,
|
|
'underlying/candlesticks': 1,
|
|
'trades': 1,
|
|
},
|
|
},
|
|
'earn': {
|
|
'get': {
|
|
'uni/currencies': 1,
|
|
'uni/currencies/{currency}': 1,
|
|
'dual/investment_plan': 1,
|
|
'structured/products': 1,
|
|
},
|
|
},
|
|
'loan': {
|
|
'get': {
|
|
'collateral/currencies': 1,
|
|
'multi_collateral/currencies': 1,
|
|
'multi_collateral/ltv': 1,
|
|
'multi_collateral/fixed_rate': 1,
|
|
'multi_collateral/current_rate': 1,
|
|
},
|
|
},
|
|
},
|
|
'private': {
|
|
# private endpoints default is 150r/10s per endpoint
|
|
'withdrawals': {
|
|
'post': {
|
|
'withdrawals': 20, # 1r/s cost = 20 / 1 = 20
|
|
'push': 1,
|
|
},
|
|
'delete': {
|
|
'withdrawals/{withdrawal_id}': 1,
|
|
},
|
|
},
|
|
'wallet': {
|
|
'get': {
|
|
'deposit_address': 1,
|
|
'withdrawals': 1,
|
|
'deposits': 1,
|
|
'sub_account_transfers': 1,
|
|
'order_status': 1,
|
|
'withdraw_status': 1,
|
|
'sub_account_balances': 2.5,
|
|
'sub_account_margin_balances': 2.5,
|
|
'sub_account_futures_balances': 2.5,
|
|
'sub_account_cross_margin_balances': 2.5,
|
|
'saved_address': 1,
|
|
'fee': 1,
|
|
'total_balance': 2.5,
|
|
'small_balance': 1,
|
|
'small_balance_history': 1,
|
|
'push': 1,
|
|
},
|
|
'post': {
|
|
'transfers': 2.5, # 8r/s cost = 20 / 8 = 2.5
|
|
'sub_account_transfers': 2.5,
|
|
'sub_account_to_sub_account': 2.5,
|
|
'small_balance': 1,
|
|
},
|
|
},
|
|
'subAccounts': {
|
|
'get': {
|
|
'sub_accounts': 2.5,
|
|
'sub_accounts/{user_id}': 2.5,
|
|
'sub_accounts/{user_id}/keys': 2.5,
|
|
'sub_accounts/{user_id}/keys/{key}': 2.5,
|
|
},
|
|
'post': {
|
|
'sub_accounts': 2.5,
|
|
'sub_accounts/{user_id}/keys': 2.5,
|
|
'sub_accounts/{user_id}/lock': 2.5,
|
|
'sub_accounts/{user_id}/unlock': 2.5,
|
|
},
|
|
'put': {
|
|
'sub_accounts/{user_id}/keys/{key}': 2.5,
|
|
},
|
|
'delete': {
|
|
'sub_accounts/{user_id}/keys/{key}': 2.5,
|
|
},
|
|
},
|
|
'unified': {
|
|
'get': {
|
|
'accounts': 20 / 15,
|
|
'borrowable': 20 / 15,
|
|
'transferable': 20 / 15,
|
|
'transferables': 20 / 15,
|
|
'batch_borrowable': 20 / 15,
|
|
'loans': 20 / 15,
|
|
'loan_records': 20 / 15,
|
|
'interest_records': 20 / 15,
|
|
'risk_units': 20 / 15,
|
|
'unified_mode': 20 / 15,
|
|
'estimate_rate': 20 / 15,
|
|
'currency_discount_tiers': 20 / 15,
|
|
'loan_margin_tiers': 20 / 15,
|
|
'leverage/user_currency_config': 20 / 15,
|
|
'leverage/user_currency_setting': 20 / 15,
|
|
'account_mode': 20 / 15, # deprecated
|
|
},
|
|
'post': {
|
|
'loans': 200 / 15, # 15r/10s cost = 20 / 1.5 = 13.33
|
|
'portfolio_calculator': 20 / 15,
|
|
'leverage/user_currency_setting': 20 / 15,
|
|
'collateral_currencies': 20 / 15,
|
|
'account_mode': 20 / 15, # deprecated
|
|
},
|
|
'put': {
|
|
'unified_mode': 20 / 15,
|
|
},
|
|
},
|
|
'spot': {
|
|
# default is 200r/10s
|
|
'get': {
|
|
'fee': 1,
|
|
'batch_fee': 1,
|
|
'accounts': 1,
|
|
'account_book': 1,
|
|
'open_orders': 1,
|
|
'orders': 1,
|
|
'orders/{order_id}': 1,
|
|
'my_trades': 1,
|
|
'price_orders': 1,
|
|
'price_orders/{order_id}': 1,
|
|
},
|
|
'post': {
|
|
'batch_orders': 0.4,
|
|
'cross_liquidate_orders': 1,
|
|
'orders': 0.4,
|
|
'cancel_batch_orders': 20 / 75,
|
|
'countdown_cancel_all': 20 / 75,
|
|
'amend_batch_orders': 0.4,
|
|
'price_orders': 0.4,
|
|
},
|
|
'delete': {
|
|
'orders': 20 / 75,
|
|
'orders/{order_id}': 20 / 75,
|
|
'price_orders': 20 / 75,
|
|
'price_orders/{order_id}': 20 / 75,
|
|
},
|
|
'patch': {
|
|
'orders/{order_id}': 0.4,
|
|
},
|
|
},
|
|
'margin': {
|
|
'get': {
|
|
'accounts': 20 / 15,
|
|
'account_book': 20 / 15,
|
|
'funding_accounts': 20 / 15,
|
|
'auto_repay': 20 / 15,
|
|
'transferable': 20 / 15,
|
|
'uni/estimate_rate': 20 / 15,
|
|
'uni/loans': 20 / 15,
|
|
'uni/loan_records': 20 / 15,
|
|
'uni/interest_records': 20 / 15,
|
|
'uni/borrowable': 20 / 15,
|
|
'user/loan_margin_tiers': 20 / 15,
|
|
'user/account': 20 / 15,
|
|
'loans': 20 / 15, # deprecated
|
|
'loans/{loan_id}': 20 / 15, # deprecated
|
|
'loans/{loan_id}/repayment': 20 / 15, # deprecated
|
|
'loan_records': 20 / 15, # deprecated
|
|
'loan_records/{loan_record_id}': 20 / 15, # deprecated
|
|
'borrowable': 20 / 15, # deprecated
|
|
'cross/accounts': 20 / 15, # deprecated
|
|
'cross/account_book': 20 / 15, # deprecated
|
|
'cross/loans': 20 / 15, # deprecated
|
|
'cross/loans/{loan_id}': 20 / 15, # deprecated
|
|
'cross/repayments': 20 / 15, # deprecated
|
|
'cross/interest_records': 20 / 15, # deprecated
|
|
'cross/transferable': 20 / 15, # deprecated
|
|
'cross/estimate_rate': 20 / 15, # deprecated
|
|
'cross/borrowable': 20 / 15, # deprecated
|
|
},
|
|
'post': {
|
|
'auto_repay': 20 / 15,
|
|
'uni/loans': 20 / 15,
|
|
'leverage/user_market_setting': 20 / 15,
|
|
'loans': 20 / 15, # deprecated
|
|
'merged_loans': 20 / 15, # deprecated
|
|
'loans/{loan_id}/repayment': 20 / 15, # deprecated
|
|
'cross/loans': 20 / 15, # deprecated
|
|
'cross/repayments': 20 / 15, # deprecated
|
|
},
|
|
'patch': {
|
|
'loans/{loan_id}': 20 / 15, # deprecated
|
|
'loan_records/{loan_record_id}': 20 / 15, # deprecated
|
|
},
|
|
'delete': {
|
|
'loans/{loan_id}': 20 / 15, # deprecated
|
|
},
|
|
},
|
|
'flash_swap': {
|
|
'get': {
|
|
'orders': 1,
|
|
'orders/{order_id}': 1,
|
|
},
|
|
'post': {
|
|
'orders': 1,
|
|
'orders/preview': 1,
|
|
},
|
|
},
|
|
'futures': {
|
|
'get': {
|
|
'{settle}/accounts': 1,
|
|
'{settle}/account_book': 1,
|
|
'{settle}/positions': 1,
|
|
'{settle}/positions/{contract}': 1,
|
|
'{settle}/dual_comp/positions/{contract}': 1,
|
|
'{settle}/orders': 1,
|
|
'{settle}/orders_timerange': 1,
|
|
'{settle}/orders/{order_id}': 1,
|
|
'{settle}/my_trades': 1,
|
|
'{settle}/my_trades_timerange': 1,
|
|
'{settle}/position_close': 1,
|
|
'{settle}/liquidates': 1,
|
|
'{settle}/auto_deleverages': 1,
|
|
'{settle}/fee': 1,
|
|
'{settle}/risk_limit_table': 1,
|
|
'{settle}/price_orders': 1,
|
|
'{settle}/price_orders/{order_id}': 1,
|
|
},
|
|
'post': {
|
|
'{settle}/positions/{contract}/margin': 1,
|
|
'{settle}/positions/{contract}/leverage': 1,
|
|
'{settle}/positions/{contract}/risk_limit': 1,
|
|
'{settle}/positions/cross_mode': 1,
|
|
'{settle}/dual_comp/positions/cross_mode': 1,
|
|
'{settle}/dual_mode': 1,
|
|
'{settle}/dual_comp/positions/{contract}/margin': 1,
|
|
'{settle}/dual_comp/positions/{contract}/leverage': 1,
|
|
'{settle}/dual_comp/positions/{contract}/risk_limit': 1,
|
|
'{settle}/orders': 0.4,
|
|
'{settle}/batch_orders': 0.4,
|
|
'{settle}/countdown_cancel_all': 0.4,
|
|
'{settle}/batch_cancel_orders': 0.4,
|
|
'{settle}/batch_amend_orders': 0.4,
|
|
'{settle}/bbo_orders': 0.4,
|
|
'{settle}/price_orders': 0.4,
|
|
},
|
|
'put': {
|
|
'{settle}/orders/{order_id}': 1,
|
|
},
|
|
'delete': {
|
|
'{settle}/orders': 20 / 75,
|
|
'{settle}/orders/{order_id}': 20 / 75,
|
|
'{settle}/price_orders': 20 / 75,
|
|
'{settle}/price_orders/{order_id}': 20 / 75,
|
|
},
|
|
},
|
|
'delivery': {
|
|
'get': {
|
|
'{settle}/accounts': 20 / 15,
|
|
'{settle}/account_book': 20 / 15,
|
|
'{settle}/positions': 20 / 15,
|
|
'{settle}/positions/{contract}': 20 / 15,
|
|
'{settle}/orders': 20 / 15,
|
|
'{settle}/orders/{order_id}': 20 / 15,
|
|
'{settle}/my_trades': 20 / 15,
|
|
'{settle}/position_close': 20 / 15,
|
|
'{settle}/liquidates': 20 / 15,
|
|
'{settle}/settlements': 20 / 15,
|
|
'{settle}/price_orders': 20 / 15,
|
|
'{settle}/price_orders/{order_id}': 20 / 15,
|
|
},
|
|
'post': {
|
|
'{settle}/positions/{contract}/margin': 20 / 15,
|
|
'{settle}/positions/{contract}/leverage': 20 / 15,
|
|
'{settle}/positions/{contract}/risk_limit': 20 / 15,
|
|
'{settle}/orders': 20 / 15,
|
|
'{settle}/price_orders': 20 / 15,
|
|
},
|
|
'delete': {
|
|
'{settle}/orders': 20 / 15,
|
|
'{settle}/orders/{order_id}': 20 / 15,
|
|
'{settle}/price_orders': 20 / 15,
|
|
'{settle}/price_orders/{order_id}': 20 / 15,
|
|
},
|
|
},
|
|
'options': {
|
|
'get': {
|
|
'my_settlements': 20 / 15,
|
|
'accounts': 20 / 15,
|
|
'account_book': 20 / 15,
|
|
'positions': 20 / 15,
|
|
'positions/{contract}': 20 / 15,
|
|
'position_close': 20 / 15,
|
|
'orders': 20 / 15,
|
|
'orders/{order_id}': 20 / 15,
|
|
'my_trades': 20 / 15,
|
|
'mmp': 20 / 15,
|
|
},
|
|
'post': {
|
|
'orders': 20 / 15,
|
|
'countdown_cancel_all': 20 / 15,
|
|
'mmp': 20 / 15,
|
|
'mmp/reset': 20 / 15,
|
|
},
|
|
'delete': {
|
|
'orders': 20 / 15,
|
|
'orders/{order_id}': 20 / 15,
|
|
},
|
|
},
|
|
'earn': {
|
|
'get': {
|
|
'uni/lends': 20 / 15,
|
|
'uni/lend_records': 20 / 15,
|
|
'uni/interests/{currency}': 20 / 15,
|
|
'uni/interest_records': 20 / 15,
|
|
'uni/interest_status/{currency}': 20 / 15,
|
|
'uni/chart': 20 / 15,
|
|
'uni/rate': 20 / 15,
|
|
'staking/eth2/rate_records': 20 / 15,
|
|
'dual/orders': 20 / 15,
|
|
'structured/orders': 20 / 15,
|
|
'staking/coins': 20 / 15,
|
|
'staking/order_list': 20 / 15,
|
|
'staking/award_list': 20 / 15,
|
|
'staking/assets': 20 / 15,
|
|
'uni/currencies': 20 / 15, # deprecated
|
|
'uni/currencies/{currency}': 20 / 15, # deprecated
|
|
},
|
|
'post': {
|
|
'uni/lends': 20 / 15,
|
|
'staking/eth2/swap': 20 / 15,
|
|
'dual/orders': 20 / 15,
|
|
'structured/orders': 20 / 15,
|
|
'staking/swap': 20 / 15,
|
|
},
|
|
'put': {
|
|
'uni/interest_reinvest': 20 / 15, # deprecated
|
|
},
|
|
'patch': {
|
|
'uni/lends': 20 / 15,
|
|
},
|
|
},
|
|
'loan': {
|
|
'get': {
|
|
'collateral/orders': 20 / 15,
|
|
'collateral/orders/{order_id}': 20 / 15,
|
|
'collateral/repay_records': 20 / 15,
|
|
'collateral/collaterals': 20 / 15,
|
|
'collateral/total_amount': 20 / 15,
|
|
'collateral/ltv': 20 / 15,
|
|
'multi_collateral/orders': 20 / 15,
|
|
'multi_collateral/orders/{order_id}': 20 / 15,
|
|
'multi_collateral/repay': 20 / 15,
|
|
'multi_collateral/mortgage': 20 / 15,
|
|
'multi_collateral/currency_quota': 20 / 15,
|
|
'collateral/currencies': 20 / 15, # deprecated
|
|
'multi_collateral/currencies': 20 / 15, # deprecated
|
|
'multi_collateral/ltv': 20 / 15, # deprecated
|
|
'multi_collateral/fixed_rate': 20 / 15, # deprecated
|
|
'multi_collateral/current_rate': 20 / 15, # deprecated
|
|
},
|
|
'post': {
|
|
'collateral/orders': 20 / 15,
|
|
'collateral/repay': 20 / 15,
|
|
'collateral/collaterals': 20 / 15,
|
|
'multi_collateral/orders': 20 / 15,
|
|
'multi_collateral/repay': 20 / 15,
|
|
'multi_collateral/mortgage': 20 / 15,
|
|
},
|
|
},
|
|
'account': {
|
|
'get': {
|
|
'detail': 20 / 15,
|
|
'main_keys': 20 / 15,
|
|
'rate_limit': 20 / 15,
|
|
'stp_groups': 20 / 15,
|
|
'stp_groups/{stp_id}/users': 20 / 15,
|
|
'stp_groups/debit_fee': 20 / 15,
|
|
'debit_fee': 20 / 15,
|
|
},
|
|
'post': {
|
|
'stp_groups': 20 / 15,
|
|
'stp_groups/{stp_id}/users': 20 / 15,
|
|
'debit_fee': 20 / 15,
|
|
},
|
|
'delete': {
|
|
'stp_groups/{stp_id}/users': 20 / 15,
|
|
},
|
|
},
|
|
'rebate': {
|
|
'get': {
|
|
'agency/transaction_history': 20 / 15,
|
|
'agency/commission_history': 20 / 15,
|
|
'partner/transaction_history': 20 / 15,
|
|
'partner/commission_history': 20 / 15,
|
|
'partner/sub_list': 20 / 15,
|
|
'broker/commission_history': 20 / 15,
|
|
'broker/transaction_history': 20 / 15,
|
|
'user/info': 20 / 15,
|
|
'user/sub_relation': 20 / 15,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'timeframes': {
|
|
'10s': '10s',
|
|
'1m': '1m',
|
|
'5m': '5m',
|
|
'15m': '15m',
|
|
'30m': '30m',
|
|
'1h': '1h',
|
|
'2h': '2h',
|
|
'4h': '4h',
|
|
'8h': '8h',
|
|
'1d': '1d',
|
|
'7d': '7d',
|
|
'1w': '7d',
|
|
},
|
|
# copied from gatev2
|
|
'commonCurrencies': {
|
|
'ORT': 'XREATORS',
|
|
'ASS': 'ASSF',
|
|
'88MPH': 'MPH',
|
|
'AXIS': 'AXISDEFI',
|
|
'BIFI': 'BITCOINFILE',
|
|
'BOX': 'DEFIBOX',
|
|
'BYN': 'BEYONDFI',
|
|
'EGG': 'GOOSEFINANCE',
|
|
'GTC': 'GAMECOM', # conflict with Gitcoin and Gastrocoin
|
|
'GTC_HT': 'GAMECOM_HT',
|
|
'GTC_BSC': 'GAMECOM_BSC',
|
|
'HIT': 'HITCHAIN',
|
|
'MM': 'MILLION', # conflict with MilliMeter
|
|
'MPH': 'MORPHER', # conflict with 88MPH
|
|
'POINT': 'GATEPOINT',
|
|
'RAI': 'RAIREFLEXINDEX', # conflict with RAI Finance
|
|
'RED': 'RedLang',
|
|
'SBTC': 'SUPERBITCOIN',
|
|
'TNC': 'TRINITYNETWORKCREDIT',
|
|
'VAI': 'VAIOT',
|
|
'TRAC': 'TRACO', # conflict with OriginTrail(TRAC)
|
|
},
|
|
'requiredCredentials': {
|
|
'apiKey': True,
|
|
'secret': True,
|
|
},
|
|
'headers': {
|
|
'X-Gate-Channel-Id': 'ccxt',
|
|
},
|
|
'options': {
|
|
'timeDifference': 0, # the difference between system clock and exchange clock
|
|
'adjustForTimeDifference': False, # controls the adjustment logic upon instantiation
|
|
'sandboxMode': False,
|
|
'unifiedAccount': None,
|
|
'createOrder': {
|
|
'expiration': 86400, # for conditional orders
|
|
},
|
|
'createMarketBuyOrderRequiresPrice': True,
|
|
'networks': {
|
|
'BTC': 'BTC',
|
|
'BRC20': 'BTCBRC', # for eg: ORDI, RATS, ...
|
|
'ETH': 'ETH',
|
|
'ERC20': 'ETH',
|
|
'TRX': 'TRX',
|
|
'TRC20': 'TRX',
|
|
'HECO': 'HT',
|
|
'HRC20': 'HT',
|
|
'BSC': 'BSC',
|
|
'BEP20': 'BSC',
|
|
'SOL': 'SOL',
|
|
'MATIC': 'MATIC',
|
|
'OPTIMISM': 'OPETH',
|
|
'ADA': 'ADA', # CARDANO
|
|
'AVAXC': 'AVAX_C',
|
|
'NEAR': 'NEAR',
|
|
'ARBONE': 'ARBEVM',
|
|
'BASE': 'BASEEVM',
|
|
'SUI': 'SUI',
|
|
'CRONOS': 'CRO',
|
|
'CRO': 'CRO',
|
|
'APT': 'APT',
|
|
'SCROLL': 'SCROLLETH',
|
|
'TAIKO': 'TAIKOETH',
|
|
'HYPE': 'HYPE',
|
|
'ALGO': 'ALGO',
|
|
# KAVA: ['KAVA', 'KAVAEVM']
|
|
# SEI: ['SEI', 'SEIEVM']
|
|
'LINEA': 'LINEAETH',
|
|
'BLAST': 'BLASTETH',
|
|
'XLM': 'XLM',
|
|
'RSK': 'RBTC',
|
|
'TON': 'TON',
|
|
'MNT': 'MNT',
|
|
# 'RUNE': 'BTCRUNES', probably, cant verify atm
|
|
'CELO': 'CELO',
|
|
'HBAR': 'HBAR',
|
|
# 'FTM': SONIC REBRAND, todo
|
|
'ZKSERA': 'ZKSERA',
|
|
'KLAY': 'KLAY',
|
|
'EOS': 'EOS',
|
|
'ACA': 'ACA',
|
|
# TLOS: ['TLOS', 'TLOSEVM']
|
|
# ASTR: ['ASTR', 'ASTREVM']
|
|
# CFX: ['CFX', 'CFXEVM']
|
|
'XTZ': 'XTZ',
|
|
'EGLD': 'EGLD',
|
|
'GLMR': 'GLMR',
|
|
'AURORA': 'AURORAEVM',
|
|
# others
|
|
'KON': 'KONET',
|
|
'GATECHAIN': 'GTEVM',
|
|
'KUSAMA': 'KSMSM',
|
|
'OKC': 'OKT',
|
|
'POLKADOT': 'DOTSM', # todo: DOT for main DOT
|
|
'LUNA': 'LUNC',
|
|
},
|
|
'networksById': {
|
|
'OPETH': 'OP',
|
|
'ETH': 'ERC20', # for GOlang
|
|
'ERC20': 'ERC20',
|
|
'TRX': 'TRC20',
|
|
'TRC20': 'TRC20',
|
|
'HT': 'HRC20',
|
|
'HECO': 'HRC20',
|
|
'BSC': 'BEP20',
|
|
'BEP20': 'BEP20',
|
|
'POLYGON': 'MATIC',
|
|
'POL': 'MATIC',
|
|
},
|
|
'timeInForce': {
|
|
'GTC': 'gtc',
|
|
'IOC': 'ioc',
|
|
'PO': 'poc',
|
|
'POC': 'poc',
|
|
'FOK': 'fok',
|
|
},
|
|
'accountsByType': {
|
|
'funding': 'spot',
|
|
'spot': 'spot',
|
|
'margin': 'margin',
|
|
'cross_margin': 'cross_margin',
|
|
'cross': 'cross_margin',
|
|
'isolated': 'margin',
|
|
'swap': 'futures',
|
|
'future': 'delivery',
|
|
'futures': 'futures',
|
|
'delivery': 'delivery',
|
|
'option': 'options',
|
|
'options': 'options',
|
|
},
|
|
'fetchMarkets': {
|
|
'types': ['spot', 'swap', 'future', 'option'],
|
|
},
|
|
'swap': {
|
|
'fetchMarkets': {
|
|
'settlementCurrencies': ['usdt', 'btc'],
|
|
},
|
|
},
|
|
'future': {
|
|
'fetchMarkets': {
|
|
'settlementCurrencies': ['usdt'],
|
|
},
|
|
},
|
|
},
|
|
'features': {
|
|
'default': {
|
|
'sandbox': True,
|
|
'createOrder': {
|
|
'marginMode': True,
|
|
'triggerPrice': True,
|
|
'triggerDirection': True, # todo: implementation edit needed
|
|
'triggerPriceType': None,
|
|
'stopLossPrice': True,
|
|
'takeProfitPrice': True,
|
|
'attachedStopLossTakeProfit': None,
|
|
'timeInForce': {
|
|
'IOC': True,
|
|
'FOK': True,
|
|
'PO': True,
|
|
'GTD': False,
|
|
},
|
|
'hedged': False,
|
|
'trailing': False,
|
|
'iceberg': True, # todo implement
|
|
'selfTradePrevention': True, # todo implement
|
|
'leverage': False,
|
|
'marketBuyByCost': True,
|
|
'marketBuyRequiresPrice': True,
|
|
},
|
|
'createOrders': {
|
|
'max': 40, # NOTE! max 10 per symbol
|
|
},
|
|
'fetchMyTrades': {
|
|
'marginMode': True,
|
|
'limit': 1000,
|
|
'daysBack': None,
|
|
'untilDays': 30,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOrder': {
|
|
'marginMode': False,
|
|
'trigger': True,
|
|
'trailing': False,
|
|
'symbolRequired': True,
|
|
},
|
|
'fetchOpenOrders': {
|
|
'marginMode': True,
|
|
'trigger': True,
|
|
'trailing': False,
|
|
'limit': 100,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOrders': None,
|
|
'fetchClosedOrders': {
|
|
'marginMode': True,
|
|
'trigger': True,
|
|
'trailing': False,
|
|
'limit': 100,
|
|
'untilDays': 30,
|
|
'daysBack': None,
|
|
'daysBackCanceled': None,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOHLCV': {
|
|
'limit': 1000,
|
|
},
|
|
},
|
|
'spot': {
|
|
'extends': 'default',
|
|
},
|
|
'forDerivatives': {
|
|
'extends': 'spot',
|
|
'createOrder': {
|
|
'marginMode': False,
|
|
'triggerPriceType': {
|
|
'last': True,
|
|
'mark': True,
|
|
'index': True,
|
|
},
|
|
},
|
|
'createOrders': {
|
|
'max': 10,
|
|
},
|
|
'fetchMyTrades': {
|
|
'marginMode': False,
|
|
'untilDays': None,
|
|
},
|
|
'fetchOpenOrders': {
|
|
'marginMode': False,
|
|
},
|
|
'fetchClosedOrders': {
|
|
'marginMode': False,
|
|
'untilDays': None,
|
|
'limit': 1000,
|
|
},
|
|
'fetchOHLCV': {
|
|
'limit': 1999,
|
|
},
|
|
},
|
|
'swap': {
|
|
'linear': {
|
|
'extends': 'forDerivatives',
|
|
},
|
|
'inverse': {
|
|
'extends': 'forDerivatives',
|
|
},
|
|
},
|
|
'future': {
|
|
'linear': {
|
|
'extends': 'forDerivatives',
|
|
},
|
|
'inverse': {
|
|
'extends': 'forDerivatives',
|
|
},
|
|
},
|
|
},
|
|
'precisionMode': TICK_SIZE,
|
|
'fees': {
|
|
'trading': {
|
|
'tierBased': True,
|
|
'feeSide': 'get',
|
|
'percentage': True,
|
|
'maker': self.parse_number('0.002'),
|
|
'taker': self.parse_number('0.002'),
|
|
'tiers': {
|
|
# volume is in BTC
|
|
'maker': [
|
|
[self.parse_number('0'), self.parse_number('0.002')],
|
|
[self.parse_number('1.5'), self.parse_number('0.00185')],
|
|
[self.parse_number('3'), self.parse_number('0.00175')],
|
|
[self.parse_number('6'), self.parse_number('0.00165')],
|
|
[self.parse_number('12.5'), self.parse_number('0.00155')],
|
|
[self.parse_number('25'), self.parse_number('0.00145')],
|
|
[self.parse_number('75'), self.parse_number('0.00135')],
|
|
[self.parse_number('200'), self.parse_number('0.00125')],
|
|
[self.parse_number('500'), self.parse_number('0.00115')],
|
|
[self.parse_number('1250'), self.parse_number('0.00105')],
|
|
[self.parse_number('2500'), self.parse_number('0.00095')],
|
|
[self.parse_number('3000'), self.parse_number('0.00085')],
|
|
[self.parse_number('6000'), self.parse_number('0.00075')],
|
|
[self.parse_number('11000'), self.parse_number('0.00065')],
|
|
[self.parse_number('20000'), self.parse_number('0.00055')],
|
|
[self.parse_number('40000'), self.parse_number('0.00055')],
|
|
[self.parse_number('75000'), self.parse_number('0.00055')],
|
|
],
|
|
'taker': [
|
|
[self.parse_number('0'), self.parse_number('0.002')],
|
|
[self.parse_number('1.5'), self.parse_number('0.00195')],
|
|
[self.parse_number('3'), self.parse_number('0.00185')],
|
|
[self.parse_number('6'), self.parse_number('0.00175')],
|
|
[self.parse_number('12.5'), self.parse_number('0.00165')],
|
|
[self.parse_number('25'), self.parse_number('0.00155')],
|
|
[self.parse_number('75'), self.parse_number('0.00145')],
|
|
[self.parse_number('200'), self.parse_number('0.00135')],
|
|
[self.parse_number('500'), self.parse_number('0.00125')],
|
|
[self.parse_number('1250'), self.parse_number('0.00115')],
|
|
[self.parse_number('2500'), self.parse_number('0.00105')],
|
|
[self.parse_number('3000'), self.parse_number('0.00095')],
|
|
[self.parse_number('6000'), self.parse_number('0.00085')],
|
|
[self.parse_number('11000'), self.parse_number('0.00075')],
|
|
[self.parse_number('20000'), self.parse_number('0.00065')],
|
|
[self.parse_number('40000'), self.parse_number('0.00065')],
|
|
[self.parse_number('75000'), self.parse_number('0.00065')],
|
|
],
|
|
},
|
|
},
|
|
'swap': {
|
|
'tierBased': True,
|
|
'feeSide': 'base',
|
|
'percentage': True,
|
|
'maker': self.parse_number('0.0'),
|
|
'taker': self.parse_number('0.0005'),
|
|
'tiers': {
|
|
'maker': [
|
|
[self.parse_number('0'), self.parse_number('0.0000')],
|
|
[self.parse_number('1.5'), self.parse_number('-0.00005')],
|
|
[self.parse_number('3'), self.parse_number('-0.00005')],
|
|
[self.parse_number('6'), self.parse_number('-0.00005')],
|
|
[self.parse_number('12.5'), self.parse_number('-0.00005')],
|
|
[self.parse_number('25'), self.parse_number('-0.00005')],
|
|
[self.parse_number('75'), self.parse_number('-0.00005')],
|
|
[self.parse_number('200'), self.parse_number('-0.00005')],
|
|
[self.parse_number('500'), self.parse_number('-0.00005')],
|
|
[self.parse_number('1250'), self.parse_number('-0.00005')],
|
|
[self.parse_number('2500'), self.parse_number('-0.00005')],
|
|
[self.parse_number('3000'), self.parse_number('-0.00008')],
|
|
[self.parse_number('6000'), self.parse_number('-0.01000')],
|
|
[self.parse_number('11000'), self.parse_number('-0.01002')],
|
|
[self.parse_number('20000'), self.parse_number('-0.01005')],
|
|
[self.parse_number('40000'), self.parse_number('-0.02000')],
|
|
[self.parse_number('75000'), self.parse_number('-0.02005')],
|
|
],
|
|
'taker': [
|
|
[self.parse_number('0'), self.parse_number('0.00050')],
|
|
[self.parse_number('1.5'), self.parse_number('0.00048')],
|
|
[self.parse_number('3'), self.parse_number('0.00046')],
|
|
[self.parse_number('6'), self.parse_number('0.00044')],
|
|
[self.parse_number('12.5'), self.parse_number('0.00042')],
|
|
[self.parse_number('25'), self.parse_number('0.00040')],
|
|
[self.parse_number('75'), self.parse_number('0.00038')],
|
|
[self.parse_number('200'), self.parse_number('0.00036')],
|
|
[self.parse_number('500'), self.parse_number('0.00034')],
|
|
[self.parse_number('1250'), self.parse_number('0.00032')],
|
|
[self.parse_number('2500'), self.parse_number('0.00030')],
|
|
[self.parse_number('3000'), self.parse_number('0.00030')],
|
|
[self.parse_number('6000'), self.parse_number('0.00030')],
|
|
[self.parse_number('11000'), self.parse_number('0.00030')],
|
|
[self.parse_number('20000'), self.parse_number('0.00030')],
|
|
[self.parse_number('40000'), self.parse_number('0.00030')],
|
|
[self.parse_number('75000'), self.parse_number('0.00030')],
|
|
],
|
|
},
|
|
},
|
|
},
|
|
# https://www.gate.com/docs/developers/apiv4/en/#label-list
|
|
'exceptions': {
|
|
'exact': {
|
|
'INVALID_PARAM_VALUE': BadRequest,
|
|
'INVALID_PROTOCOL': BadRequest,
|
|
'INVALID_ARGUMENT': BadRequest,
|
|
'INVALID_REQUEST_BODY': BadRequest,
|
|
'MISSING_REQUIRED_PARAM': ArgumentsRequired,
|
|
'BAD_REQUEST': BadRequest,
|
|
'INVALID_CONTENT_TYPE': BadRequest,
|
|
'NOT_ACCEPTABLE': BadRequest,
|
|
'METHOD_NOT_ALLOWED': BadRequest,
|
|
'NOT_FOUND': ExchangeError,
|
|
'AUTHENTICATION_FAILED': AuthenticationError,
|
|
'INVALID_CREDENTIALS': AuthenticationError,
|
|
'INVALID_KEY': AuthenticationError,
|
|
'IP_FORBIDDEN': AuthenticationError,
|
|
'READ_ONLY': PermissionDenied,
|
|
'INVALID_SIGNATURE': AuthenticationError,
|
|
'MISSING_REQUIRED_HEADER': AuthenticationError,
|
|
'REQUEST_EXPIRED': AuthenticationError,
|
|
'ACCOUNT_LOCKED': AccountSuspended,
|
|
'FORBIDDEN': PermissionDenied,
|
|
'SUB_ACCOUNT_NOT_FOUND': ExchangeError,
|
|
'SUB_ACCOUNT_LOCKED': AccountSuspended,
|
|
'MARGIN_BALANCE_EXCEPTION': ExchangeError,
|
|
'MARGIN_TRANSFER_FAILED': ExchangeError,
|
|
'TOO_MUCH_FUTURES_AVAILABLE': ExchangeError,
|
|
'FUTURES_BALANCE_NOT_ENOUGH': InsufficientFunds,
|
|
'ACCOUNT_EXCEPTION': ExchangeError,
|
|
'SUB_ACCOUNT_TRANSFER_FAILED': ExchangeError,
|
|
'ADDRESS_NOT_USED': ExchangeError,
|
|
'TOO_FAST': RateLimitExceeded,
|
|
'WITHDRAWAL_OVER_LIMIT': ExchangeError,
|
|
'API_WITHDRAW_DISABLED': ExchangeNotAvailable,
|
|
'INVALID_WITHDRAW_ID': ExchangeError,
|
|
'INVALID_WITHDRAW_CANCEL_STATUS': ExchangeError,
|
|
'INVALID_PRECISION': InvalidOrder,
|
|
'INVALID_CURRENCY': BadSymbol,
|
|
'INVALID_CURRENCY_PAIR': BadSymbol,
|
|
'POC_FILL_IMMEDIATELY': OrderImmediatelyFillable, # {"label":"POC_FILL_IMMEDIATELY","message":"Order would match and take immediately so its cancelled"}
|
|
'ORDER_NOT_FOUND': OrderNotFound,
|
|
'CLIENT_ID_NOT_FOUND': OrderNotFound,
|
|
'ORDER_CLOSED': InvalidOrder,
|
|
'ORDER_CANCELLED': InvalidOrder,
|
|
'QUANTITY_NOT_ENOUGH': InvalidOrder,
|
|
'BALANCE_NOT_ENOUGH': InsufficientFunds,
|
|
'MARGIN_NOT_SUPPORTED': InvalidOrder,
|
|
'MARGIN_BALANCE_NOT_ENOUGH': InsufficientFunds,
|
|
'AMOUNT_TOO_LITTLE': InvalidOrder,
|
|
'AMOUNT_TOO_MUCH': InvalidOrder,
|
|
'REPEATED_CREATION': InvalidOrder,
|
|
'LOAN_NOT_FOUND': OrderNotFound,
|
|
'LOAN_RECORD_NOT_FOUND': OrderNotFound,
|
|
'NO_MATCHED_LOAN': ExchangeError,
|
|
'NOT_MERGEABLE': ExchangeError,
|
|
'NO_CHANGE': ExchangeError,
|
|
'REPAY_TOO_MUCH': ExchangeError,
|
|
'TOO_MANY_CURRENCY_PAIRS': InvalidOrder,
|
|
'TOO_MANY_ORDERS': InvalidOrder,
|
|
'TOO_MANY_REQUESTS': RateLimitExceeded,
|
|
'MIXED_ACCOUNT_TYPE': InvalidOrder,
|
|
'AUTO_BORROW_TOO_MUCH': ExchangeError,
|
|
'TRADE_RESTRICTED': InsufficientFunds,
|
|
'USER_NOT_FOUND': AccountNotEnabled,
|
|
'CONTRACT_NO_COUNTER': ExchangeError,
|
|
'CONTRACT_NOT_FOUND': BadSymbol,
|
|
'RISK_LIMIT_EXCEEDED': ExchangeError,
|
|
'INSUFFICIENT_AVAILABLE': InsufficientFunds,
|
|
'LIQUIDATE_IMMEDIATELY': InvalidOrder,
|
|
'LEVERAGE_TOO_HIGH': InvalidOrder,
|
|
'LEVERAGE_TOO_LOW': InvalidOrder,
|
|
'ORDER_NOT_OWNED': ExchangeError,
|
|
'ORDER_FINISHED': ExchangeError,
|
|
'POSITION_CROSS_MARGIN': ExchangeError,
|
|
'POSITION_IN_LIQUIDATION': ExchangeError,
|
|
'POSITION_IN_CLOSE': ExchangeError,
|
|
'POSITION_EMPTY': InvalidOrder,
|
|
'REMOVE_TOO_MUCH': ExchangeError,
|
|
'RISK_LIMIT_NOT_MULTIPLE': ExchangeError,
|
|
'RISK_LIMIT_TOO_HIGH': ExchangeError,
|
|
'RISK_LIMIT_TOO_lOW': ExchangeError,
|
|
'PRICE_TOO_DEVIATED': InvalidOrder,
|
|
'SIZE_TOO_LARGE': InvalidOrder,
|
|
'SIZE_TOO_SMALL': InvalidOrder,
|
|
'PRICE_OVER_LIQUIDATION': InvalidOrder,
|
|
'PRICE_OVER_BANKRUPT': InvalidOrder,
|
|
'ORDER_POC_IMMEDIATE': OrderImmediatelyFillable, # {"label":"ORDER_POC_IMMEDIATE","detail":"order price 1700 while counter price 1793.55"}
|
|
'INCREASE_POSITION': InvalidOrder,
|
|
'CONTRACT_IN_DELISTING': ExchangeError,
|
|
'INTERNAL': ExchangeNotAvailable,
|
|
'SERVER_ERROR': ExchangeNotAvailable,
|
|
'TOO_BUSY': ExchangeNotAvailable,
|
|
'CROSS_ACCOUNT_NOT_FOUND': ExchangeError,
|
|
'RISK_LIMIT_TOO_LOW': BadRequest, # {"label":"RISK_LIMIT_TOO_LOW","detail":"limit 1000000"}
|
|
'AUTO_TRIGGER_PRICE_LESS_LAST': InvalidOrder, # {"label":"AUTO_TRIGGER_PRICE_LESS_LAST","message":"invalid argument: Trigger.Price must < last_price"}
|
|
'AUTO_TRIGGER_PRICE_GREATE_LAST': InvalidOrder, # {"label":"AUTO_TRIGGER_PRICE_GREATE_LAST","message":"invalid argument: Trigger.Price must > last_price"}
|
|
'POSITION_HOLDING': BadRequest,
|
|
'USER_LOAN_EXCEEDED': BadRequest, # {"label":"USER_LOAN_EXCEEDED","message":"Max loan amount per user would be exceeded"}
|
|
},
|
|
'broad': {},
|
|
},
|
|
})
|
|
|
|
def set_sandbox_mode(self, enable: bool):
|
|
super(gate, self).set_sandbox_mode(enable)
|
|
self.options['sandboxMode'] = enable
|
|
|
|
def load_unified_status(self, params={}):
|
|
"""
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
returns unifiedAccount so the user can check if the unified account is enabled
|
|
|
|
https://www.gate.com/docs/developers/apiv4/#get-account-detail
|
|
|
|
:returns boolean: True or False if the enabled unified account is enabled or not and sets the unifiedAccount option if it is None
|
|
"""
|
|
unifiedAccount = self.safe_bool(self.options, 'unifiedAccount')
|
|
if unifiedAccount is None:
|
|
try:
|
|
#
|
|
# {
|
|
# "user_id": 10406147,
|
|
# "ip_whitelist": [],
|
|
# "currency_pairs": [],
|
|
# "key": {
|
|
# "mode": 1
|
|
# },
|
|
# "tier": 0,
|
|
# "tier_expire_time": "0001-01-01T00:00:00Z",
|
|
# "copy_trading_role": 0
|
|
# }
|
|
#
|
|
response = self.privateAccountGetDetail(params)
|
|
result = self.safe_dict(response, 'key', {})
|
|
self.options['unifiedAccount'] = self.safe_integer(result, 'mode') == 2
|
|
except Exception as e:
|
|
# if the request fails, the unifiedAccount is disabled
|
|
self.options['unifiedAccount'] = False
|
|
return self.options['unifiedAccount']
|
|
|
|
def upgrade_unified_trade_account(self, params={}):
|
|
return self.privateUnifiedPutUnifiedMode(params)
|
|
|
|
def fetch_time(self, params={}) -> Int:
|
|
"""
|
|
fetches the current integer timestamp in milliseconds from the exchange server
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-server-current-time
|
|
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns int: the current integer timestamp in milliseconds from the exchange server
|
|
"""
|
|
response = self.publicSpotGetTime(params)
|
|
#
|
|
# {
|
|
# "server_time": 1731447921098
|
|
# }
|
|
#
|
|
return self.safe_integer(response, 'server_time')
|
|
|
|
def create_expired_option_market(self, symbol: str):
|
|
# support expired option contracts
|
|
quote = 'USDT'
|
|
settle = quote
|
|
optionParts = symbol.split('-')
|
|
symbolBase = symbol.split('/')
|
|
marketIdBase = symbol.split('_')
|
|
base = None
|
|
expiry = self.safe_string(optionParts, 1)
|
|
if symbol.find('/') > -1:
|
|
base = self.safe_string(symbolBase, 0)
|
|
else:
|
|
base = self.safe_string(marketIdBase, 0)
|
|
expiry = expiry[2:8] # convert 20230728 to 230728
|
|
strike = self.safe_string(optionParts, 2)
|
|
optionType = self.safe_string(optionParts, 3)
|
|
datetime = self.convert_expire_date(expiry)
|
|
timestamp = self.parse8601(datetime)
|
|
return {
|
|
'id': base + '_' + quote + '-' + '20' + expiry + '-' + strike + '-' + optionType,
|
|
'symbol': base + '/' + quote + ':' + settle + '-' + expiry + '-' + strike + '-' + optionType,
|
|
'base': base,
|
|
'quote': quote,
|
|
'settle': settle,
|
|
'baseId': base,
|
|
'quoteId': quote,
|
|
'settleId': settle,
|
|
'active': False,
|
|
'type': 'option',
|
|
'linear': None,
|
|
'inverse': None,
|
|
'spot': False,
|
|
'swap': False,
|
|
'future': False,
|
|
'option': True,
|
|
'margin': False,
|
|
'contract': True,
|
|
'contractSize': self.parse_number('1'),
|
|
'expiry': timestamp,
|
|
'expiryDatetime': datetime,
|
|
'optionType': 'call' if (optionType == 'C') else 'put',
|
|
'strike': self.parse_number(strike),
|
|
'precision': {
|
|
'amount': self.parse_number('1'),
|
|
'price': None,
|
|
},
|
|
'limits': {
|
|
'amount': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'price': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'cost': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
},
|
|
'info': None,
|
|
}
|
|
|
|
def safe_market(self, marketId: Str = None, market: Market = None, delimiter: Str = None, marketType: Str = None) -> MarketInterface:
|
|
isOption = (marketId is not None) and ((marketId.find('-C') > -1) or (marketId.find('-P') > -1))
|
|
if isOption and not (marketId in self.markets_by_id):
|
|
# handle expired option contracts
|
|
return self.create_expired_option_market(marketId)
|
|
return super(gate, self).safe_market(marketId, market, delimiter, marketType)
|
|
|
|
def fetch_markets(self, params={}) -> List[Market]:
|
|
"""
|
|
retrieves data on all markets for gate
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-currency-pairs-supported # spot
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-supported-currency-pairs-supported-in-margin-trading # margin
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-futures-contracts # swap
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-futures-contracts-2 # future
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-the-contracts-with-specified-underlying-and-expiration-time # option
|
|
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: an array of objects representing market data
|
|
"""
|
|
if self.options['adjustForTimeDifference']:
|
|
self.load_time_difference()
|
|
if self.check_required_credentials(False):
|
|
self.load_unified_status()
|
|
rawPromises = []
|
|
fetchMarketsOptions = self.safe_dict(self.options, 'fetchMarkets')
|
|
types = self.safe_list(fetchMarketsOptions, 'types', ['spot', 'swap', 'future', 'option'])
|
|
for i in range(0, len(types)):
|
|
marketType = types[i]
|
|
if marketType == 'spot':
|
|
# if not sandboxMode:
|
|
# gate doesn't have a sandbox for spot markets
|
|
rawPromises.append(self.fetch_spot_markets(params))
|
|
# }
|
|
elif marketType == 'swap':
|
|
rawPromises.append(self.fetch_swap_markets(params))
|
|
elif marketType == 'future':
|
|
rawPromises.append(self.fetch_future_markets(params))
|
|
elif marketType == 'option':
|
|
rawPromises.append(self.fetch_option_markets(params))
|
|
results = rawPromises
|
|
return self.arrays_concat(results)
|
|
|
|
def fetch_spot_markets(self, params={}):
|
|
marginPromise = self.publicMarginGetCurrencyPairs(params)
|
|
spotMarketsPromise = self.publicSpotGetCurrencyPairs(params)
|
|
marginResponse, spotMarketsResponse = [marginPromise, spotMarketsPromise]
|
|
marginMarkets = self.index_by(marginResponse, 'id')
|
|
#
|
|
# Spot
|
|
#
|
|
# [
|
|
# {
|
|
# "id": "QTUM_ETH",
|
|
# "base": "QTUM",
|
|
# "base_name": "Quantum",
|
|
# "quote": "ETH",
|
|
# "quote_name": "Ethereum",
|
|
# "fee": "0.2",
|
|
# "min_base_amount": "0.01",
|
|
# "min_quote_amount": "0.001",
|
|
# "max_quote_amount": "50000",
|
|
# "amount_precision": 3,
|
|
# "precision": 6,
|
|
# "trade_status": "tradable",
|
|
# "sell_start": 1607313600,
|
|
# "buy_start": 1700492400,
|
|
# "type": "normal",
|
|
# "trade_url": "https://www.gate.com/trade/QTUM_ETH",
|
|
# }
|
|
#
|
|
# Margin
|
|
#
|
|
# [
|
|
# {
|
|
# "id": "ETH_USDT",
|
|
# "base": "ETH",
|
|
# "quote": "USDT",
|
|
# "leverage": 3,
|
|
# "min_base_amount": "0.01",
|
|
# "min_quote_amount": "100",
|
|
# "max_quote_amount": "1000000"
|
|
# }
|
|
# ]
|
|
#
|
|
result = []
|
|
for i in range(0, len(spotMarketsResponse)):
|
|
spotMarket = spotMarketsResponse[i]
|
|
id = self.safe_string(spotMarket, 'id')
|
|
marginMarket = self.safe_value(marginMarkets, id)
|
|
market = self.deep_extend(marginMarket, spotMarket)
|
|
baseId, quoteId = id.split('_')
|
|
base = self.safe_currency_code(baseId)
|
|
quote = self.safe_currency_code(quoteId)
|
|
takerPercent = self.safe_string(market, 'fee')
|
|
makerPercent = self.safe_string(market, 'maker_fee_rate', takerPercent)
|
|
amountPrecision = self.parse_number(self.parse_precision(self.safe_string(market, 'amount_precision')))
|
|
tradeStatus = self.safe_string(market, 'trade_status')
|
|
leverage = self.safe_number(market, 'leverage')
|
|
margin = leverage is not None
|
|
buyStart = self.safe_integer_product(spotMarket, 'buy_start', 1000) # buy_start is the trading start time, while sell_start is offline orders start time
|
|
createdTs = buyStart if (buyStart != 0) else None
|
|
result.append({
|
|
'id': id,
|
|
'symbol': base + '/' + quote,
|
|
'base': base,
|
|
'quote': quote,
|
|
'settle': None,
|
|
'baseId': baseId,
|
|
'quoteId': quoteId,
|
|
'settleId': None,
|
|
'type': 'spot',
|
|
'spot': True,
|
|
'margin': margin,
|
|
'swap': False,
|
|
'future': False,
|
|
'option': False,
|
|
'active': (tradeStatus == 'tradable'),
|
|
'contract': False,
|
|
'linear': None,
|
|
'inverse': None,
|
|
# Fee is in %, so divide by 100
|
|
'taker': self.parse_number(Precise.string_div(takerPercent, '100')),
|
|
'maker': self.parse_number(Precise.string_div(makerPercent, '100')),
|
|
'contractSize': None,
|
|
'expiry': None,
|
|
'expiryDatetime': None,
|
|
'strike': None,
|
|
'optionType': None,
|
|
'precision': {
|
|
'amount': amountPrecision,
|
|
'price': self.parse_number(self.parse_precision(self.safe_string(market, 'precision'))),
|
|
},
|
|
'limits': {
|
|
'leverage': {
|
|
'min': self.parse_number('1'),
|
|
'max': self.safe_number(market, 'leverage', 1),
|
|
},
|
|
'amount': {
|
|
'min': self.safe_number(spotMarket, 'min_base_amount', amountPrecision),
|
|
'max': None,
|
|
},
|
|
'price': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'cost': {
|
|
'min': self.safe_number(market, 'min_quote_amount'),
|
|
'max': self.safe_number(market, 'max_quote_amount') if margin else None,
|
|
},
|
|
},
|
|
'created': createdTs,
|
|
'info': market,
|
|
})
|
|
return result
|
|
|
|
def fetch_swap_markets(self, params={}):
|
|
result = []
|
|
swapSettlementCurrencies = self.get_settlement_currencies('swap', 'fetchMarkets')
|
|
if self.options['sandboxMode']:
|
|
swapSettlementCurrencies = ['usdt'] # gate sandbox only has usdt-margined swaps
|
|
for c in range(0, len(swapSettlementCurrencies)):
|
|
settleId = swapSettlementCurrencies[c]
|
|
request: dict = {
|
|
'settle': settleId,
|
|
}
|
|
response = self.publicFuturesGetSettleContracts(self.extend(request, params))
|
|
for i in range(0, len(response)):
|
|
parsedMarket = self.parse_contract_market(response[i], settleId)
|
|
result.append(parsedMarket)
|
|
return result
|
|
|
|
def fetch_future_markets(self, params={}):
|
|
if self.options['sandboxMode']:
|
|
return [] # right now sandbox does not have inverse swaps
|
|
result = []
|
|
futureSettlementCurrencies = self.get_settlement_currencies('future', 'fetchMarkets')
|
|
for c in range(0, len(futureSettlementCurrencies)):
|
|
settleId = futureSettlementCurrencies[c]
|
|
request: dict = {
|
|
'settle': settleId,
|
|
}
|
|
response = self.publicDeliveryGetSettleContracts(self.extend(request, params))
|
|
for i in range(0, len(response)):
|
|
parsedMarket = self.parse_contract_market(response[i], settleId)
|
|
result.append(parsedMarket)
|
|
return result
|
|
|
|
def parse_contract_market(self, market, settleId):
|
|
#
|
|
# Perpetual swap
|
|
#
|
|
# {
|
|
# "name": "BTC_USDT",
|
|
# "type": "direct",
|
|
# "quanto_multiplier": "0.0001",
|
|
# "ref_discount_rate": "0",
|
|
# "order_price_deviate": "0.5",
|
|
# "maintenance_rate": "0.005",
|
|
# "mark_type": "index",
|
|
# "last_price": "38026",
|
|
# "mark_price": "37985.6",
|
|
# "index_price": "37954.92",
|
|
# "funding_rate_indicative": "0.000219",
|
|
# "mark_price_round": "0.01",
|
|
# "funding_offset": 0,
|
|
# "in_delisting": False,
|
|
# "risk_limit_base": "1000000",
|
|
# "interest_rate": "0.0003",
|
|
# "order_price_round": "0.1",
|
|
# "order_size_min": 1,
|
|
# "ref_rebate_rate": "0.2",
|
|
# "funding_interval": 28800,
|
|
# "risk_limit_step": "1000000",
|
|
# "leverage_min": "1",
|
|
# "leverage_max": "100",
|
|
# "risk_limit_max": "8000000",
|
|
# "maker_fee_rate": "-0.00025", # not actual value for regular users
|
|
# "taker_fee_rate": "0.00075", # not actual value for regular users
|
|
# "funding_rate": "0.002053",
|
|
# "order_size_max": 1000000,
|
|
# "funding_next_apply": 1610035200,
|
|
# "short_users": 977,
|
|
# "config_change_time": 1609899548,
|
|
# "create_time": 1609800048,
|
|
# "trade_size": 28530850594,
|
|
# "position_size": 5223816,
|
|
# "long_users": 455,
|
|
# "funding_impact_value": "60000",
|
|
# "orders_limit": 50,
|
|
# "trade_id": 10851092,
|
|
# "orderbook_id": 2129638396
|
|
# }
|
|
#
|
|
# Delivery Futures
|
|
#
|
|
# {
|
|
# "name": "BTC_USDT_20200814",
|
|
# "underlying": "BTC_USDT",
|
|
# "cycle": "WEEKLY",
|
|
# "type": "direct",
|
|
# "quanto_multiplier": "0.0001",
|
|
# "mark_type": "index",
|
|
# "last_price": "9017",
|
|
# "mark_price": "9019",
|
|
# "index_price": "9005.3",
|
|
# "basis_rate": "0.185095",
|
|
# "basis_value": "13.7",
|
|
# "basis_impact_value": "100000",
|
|
# "settle_price": "0",
|
|
# "settle_price_interval": 60,
|
|
# "settle_price_duration": 1800,
|
|
# "settle_fee_rate": "0.0015",
|
|
# "expire_time": 1593763200,
|
|
# "order_price_round": "0.1",
|
|
# "mark_price_round": "0.1",
|
|
# "leverage_min": "1",
|
|
# "leverage_max": "100",
|
|
# "maintenance_rate": "1000000",
|
|
# "risk_limit_base": "140.726652109199",
|
|
# "risk_limit_step": "1000000",
|
|
# "risk_limit_max": "8000000",
|
|
# "maker_fee_rate": "-0.00025", # not actual value for regular users
|
|
# "taker_fee_rate": "0.00075", # not actual value for regular users
|
|
# "ref_discount_rate": "0",
|
|
# "ref_rebate_rate": "0.2",
|
|
# "order_price_deviate": "0.5",
|
|
# "order_size_min": 1,
|
|
# "order_size_max": 1000000,
|
|
# "orders_limit": 50,
|
|
# "orderbook_id": 63,
|
|
# "trade_id": 26,
|
|
# "trade_size": 435,
|
|
# "position_size": 130,
|
|
# "config_change_time": 1593158867,
|
|
# "in_delisting": False
|
|
# }
|
|
#
|
|
id = self.safe_string(market, 'name')
|
|
parts = id.split('_')
|
|
baseId = self.safe_string(parts, 0)
|
|
quoteId = self.safe_string(parts, 1)
|
|
date = self.safe_string(parts, 2)
|
|
base = self.safe_currency_code(baseId)
|
|
quote = self.safe_currency_code(quoteId)
|
|
settle = self.safe_currency_code(settleId)
|
|
expiry = self.safe_timestamp(market, 'expire_time')
|
|
symbol = ''
|
|
marketType = 'swap'
|
|
if date is not None:
|
|
symbol = base + '/' + quote + ':' + settle + '-' + self.yymmdd(expiry, '')
|
|
marketType = 'future'
|
|
else:
|
|
symbol = base + '/' + quote + ':' + settle
|
|
priceDeviate = self.safe_string(market, 'order_price_deviate')
|
|
markPrice = self.safe_string(market, 'mark_price')
|
|
minMultiplier = Precise.string_sub('1', priceDeviate)
|
|
maxMultiplier = Precise.string_add('1', priceDeviate)
|
|
minPrice = Precise.string_mul(minMultiplier, markPrice)
|
|
maxPrice = Precise.string_mul(maxMultiplier, markPrice)
|
|
isLinear = quote == settle
|
|
contractSize = self.safe_string(market, 'quanto_multiplier')
|
|
# exception only for one market: https://api.gateio.ws/api/v4/futures/btc/contracts
|
|
if contractSize == '0':
|
|
contractSize = '1' # 1 USD in WEB: https://i.imgur.com/MBBUI04.png
|
|
return {
|
|
'id': id,
|
|
'symbol': symbol,
|
|
'base': base,
|
|
'quote': quote,
|
|
'settle': settle,
|
|
'baseId': baseId,
|
|
'quoteId': quoteId,
|
|
'settleId': settleId,
|
|
'type': marketType,
|
|
'spot': False,
|
|
'margin': False,
|
|
'swap': marketType == 'swap',
|
|
'future': marketType == 'future',
|
|
'option': marketType == 'option',
|
|
'active': True,
|
|
'contract': True,
|
|
'linear': isLinear,
|
|
'inverse': not isLinear,
|
|
'taker': None,
|
|
'maker': None,
|
|
'contractSize': self.parse_number(contractSize),
|
|
'expiry': expiry,
|
|
'expiryDatetime': self.iso8601(expiry),
|
|
'strike': None,
|
|
'optionType': None,
|
|
'precision': {
|
|
'amount': self.parse_number('1'), # all contracts have self step size
|
|
'price': self.safe_number(market, 'order_price_round'),
|
|
},
|
|
'limits': {
|
|
'leverage': {
|
|
'min': self.safe_number(market, 'leverage_min'),
|
|
'max': self.safe_number(market, 'leverage_max'),
|
|
},
|
|
'amount': {
|
|
'min': self.safe_number(market, 'order_size_min'),
|
|
'max': self.safe_number(market, 'order_size_max'),
|
|
},
|
|
'price': {
|
|
'min': self.parse_number(minPrice),
|
|
'max': self.parse_number(maxPrice),
|
|
},
|
|
'cost': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
},
|
|
'created': self.safe_integer_product(market, 'create_time', 1000),
|
|
'info': market,
|
|
}
|
|
|
|
def fetch_option_markets(self, params={}):
|
|
result = []
|
|
underlyings = self.fetch_option_underlyings()
|
|
for i in range(0, len(underlyings)):
|
|
underlying = underlyings[i]
|
|
query = self.extend({}, params)
|
|
query['underlying'] = underlying
|
|
response = self.publicOptionsGetContracts(query)
|
|
#
|
|
# [
|
|
# {
|
|
# "orders_limit": "50",
|
|
# "order_size_max": "100000",
|
|
# "mark_price_round": "0.1",
|
|
# "order_size_min": "1",
|
|
# "position_limit": "1000000",
|
|
# "orderbook_id": "575967",
|
|
# "order_price_deviate": "0.9",
|
|
# "is_call": True, # True means Call False means Put
|
|
# "last_price": "93.9",
|
|
# "bid1_size": "0",
|
|
# "bid1_price": "0",
|
|
# "taker_fee_rate": "0.0004",
|
|
# "underlying": "BTC_USDT",
|
|
# "create_time": "1646381188",
|
|
# "price_limit_fee_rate": "0.1",
|
|
# "maker_fee_rate": "0.0004",
|
|
# "trade_id": "727",
|
|
# "order_price_round": "0.1",
|
|
# "settle_fee_rate": "0.0001",
|
|
# "trade_size": "1982",
|
|
# "ref_rebate_rate": "0",
|
|
# "name": "BTC_USDT-20220311-44000-C",
|
|
# "underlying_price": "39194.26",
|
|
# "strike_price": "44000",
|
|
# "multiplier": "0.0001",
|
|
# "ask1_price": "0",
|
|
# "ref_discount_rate": "0",
|
|
# "expiration_time": "1646985600",
|
|
# "mark_price": "12.15",
|
|
# "position_size": "4",
|
|
# "ask1_size": "0",
|
|
# "tag": "WEEK"
|
|
# }
|
|
# ]
|
|
#
|
|
for j in range(0, len(response)):
|
|
market = response[j]
|
|
id = self.safe_string(market, 'name')
|
|
parts = underlying.split('_')
|
|
baseId = self.safe_string(parts, 0)
|
|
quoteId = self.safe_string(parts, 1)
|
|
base = self.safe_currency_code(baseId)
|
|
quote = self.safe_currency_code(quoteId)
|
|
symbol = base + '/' + quote
|
|
expiry = self.safe_timestamp(market, 'expiration_time')
|
|
strike = self.safe_string(market, 'strike_price')
|
|
isCall = self.safe_value(market, 'is_call')
|
|
optionLetter = 'C' if isCall else 'P'
|
|
optionType = 'call' if isCall else 'put'
|
|
symbol = symbol + ':' + quote + '-' + self.yymmdd(expiry) + '-' + strike + '-' + optionLetter
|
|
priceDeviate = self.safe_string(market, 'order_price_deviate')
|
|
markPrice = self.safe_string(market, 'mark_price')
|
|
minMultiplier = Precise.string_sub('1', priceDeviate)
|
|
maxMultiplier = Precise.string_add('1', priceDeviate)
|
|
minPrice = Precise.string_mul(minMultiplier, markPrice)
|
|
maxPrice = Precise.string_mul(maxMultiplier, markPrice)
|
|
result.append({
|
|
'id': id,
|
|
'symbol': symbol,
|
|
'base': base,
|
|
'quote': quote,
|
|
'settle': quote,
|
|
'baseId': baseId,
|
|
'quoteId': quoteId,
|
|
'settleId': quoteId,
|
|
'type': 'option',
|
|
'spot': False,
|
|
'margin': False,
|
|
'swap': False,
|
|
'future': False,
|
|
'option': True,
|
|
'active': True,
|
|
'contract': True,
|
|
'linear': True,
|
|
'inverse': False,
|
|
'taker': None,
|
|
'maker': None,
|
|
'contractSize': self.parse_number('1'),
|
|
'expiry': expiry,
|
|
'expiryDatetime': self.iso8601(expiry),
|
|
'strike': self.parse_number(strike),
|
|
'optionType': optionType,
|
|
'precision': {
|
|
'amount': self.parse_number('1'), # all options have self step size
|
|
'price': self.safe_number(market, 'order_price_round'),
|
|
},
|
|
'limits': {
|
|
'leverage': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'amount': {
|
|
'min': self.safe_number(market, 'order_size_min'),
|
|
'max': self.safe_number(market, 'order_size_max'),
|
|
},
|
|
'price': {
|
|
'min': self.parse_number(minPrice),
|
|
'max': self.parse_number(maxPrice),
|
|
},
|
|
'cost': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
},
|
|
'created': self.safe_timestamp(market, 'create_time'),
|
|
'info': market,
|
|
})
|
|
return result
|
|
|
|
def fetch_option_underlyings(self):
|
|
underlyingsResponse = self.publicOptionsGetUnderlyings()
|
|
#
|
|
# [
|
|
# {
|
|
# "index_time": "1646915796",
|
|
# "name": "BTC_USDT",
|
|
# "index_price": "39142.73"
|
|
# }
|
|
# ]
|
|
#
|
|
underlyings = []
|
|
for i in range(0, len(underlyingsResponse)):
|
|
underlying = underlyingsResponse[i]
|
|
name = self.safe_string(underlying, 'name')
|
|
if name is not None:
|
|
underlyings.append(name)
|
|
return underlyings
|
|
|
|
def prepare_request(self, market=None, type=None, params={}):
|
|
"""
|
|
@ignore
|
|
Fills request params contract, settle, currency_pair, market and account where applicable
|
|
:param dict market: CCXT market, required when type is None
|
|
:param str type: 'spot', 'swap', or 'future', required when market is None
|
|
:param dict [params]: request parameters
|
|
:returns: the api request object, and the new params object with non-needed parameters removed
|
|
"""
|
|
# * Do not call for multi spot order methods like cancelAllOrders and fetchOpenOrders. Use multiOrderSpotPrepareRequest instead
|
|
request: dict = {}
|
|
if market is not None:
|
|
if market['contract']:
|
|
request['contract'] = market['id']
|
|
if not market['option']:
|
|
request['settle'] = market['settleId']
|
|
else:
|
|
request['currency_pair'] = market['id']
|
|
else:
|
|
swap = type == 'swap'
|
|
future = type == 'future'
|
|
if swap or future:
|
|
defaultSettle = 'usdt' if swap else 'btc'
|
|
settle = self.safe_string_lower(params, 'settle', defaultSettle)
|
|
params = self.omit(params, 'settle')
|
|
request['settle'] = settle
|
|
return [request, params]
|
|
|
|
def spot_order_prepare_request(self, market=None, trigger=False, params={}):
|
|
"""
|
|
@ignore
|
|
Fills request params currency_pair, market and account where applicable for spot order methods like fetchOpenOrders, cancelAllOrders
|
|
:param dict market: CCXT market
|
|
:param bool trigger: True if for a trigger order
|
|
:param dict [params]: request parameters
|
|
:returns: the api request object, and the new params object with non-needed parameters removed
|
|
"""
|
|
marginMode, query = self.get_margin_mode(trigger, params)
|
|
request: dict = {}
|
|
if not trigger:
|
|
if market is None:
|
|
raise ArgumentsRequired(self.id + ' spotOrderPrepareRequest() requires a market argument for non-trigger orders')
|
|
request['account'] = marginMode
|
|
request['currency_pair'] = market['id'] # Should always be set for non-trigger
|
|
return [request, query]
|
|
|
|
def multi_order_spot_prepare_request(self, market=None, trigger=False, params={}):
|
|
"""
|
|
@ignore
|
|
Fills request params currency_pair, market and account where applicable for spot order methods like fetchOpenOrders, cancelAllOrders
|
|
:param dict market: CCXT market
|
|
:param bool trigger: True if for a trigger order
|
|
:param dict [params]: request parameters
|
|
:returns: the api request object, and the new params object with non-needed parameters removed
|
|
"""
|
|
marginMode, query = self.get_margin_mode(trigger, params)
|
|
request: dict = {
|
|
'account': marginMode,
|
|
}
|
|
if market is not None:
|
|
if trigger:
|
|
# gate spot and margin trigger orders use the term market instead of currency_pair, and normal instead of spot. Neither parameter is used when fetching/cancelling a single order. They are used for creating a single trigger order, but createOrder does not call self method
|
|
request['market'] = market['id']
|
|
else:
|
|
request['currency_pair'] = market['id']
|
|
return [request, query]
|
|
|
|
def get_margin_mode(self, trigger, params):
|
|
"""
|
|
@ignore
|
|
Gets the margin type for self api call
|
|
:param bool trigger: True if for a trigger order
|
|
:param dict [params]: Request params
|
|
:returns: The marginMode and the updated request params with marginMode removed, marginMode value is the value that can be read by the "account" property specified in gates api docs
|
|
"""
|
|
defaultMarginMode = self.safe_string_lower_2(self.options, 'defaultMarginMode', 'marginMode', 'spot') # 'margin' is isolated margin on gate's api
|
|
marginMode = self.safe_string_lower_2(params, 'marginMode', 'account', defaultMarginMode)
|
|
params = self.omit(params, ['marginMode', 'account'])
|
|
if marginMode == 'cross':
|
|
marginMode = 'cross_margin'
|
|
elif marginMode == 'isolated':
|
|
marginMode = 'margin'
|
|
elif marginMode == '':
|
|
marginMode = 'spot'
|
|
if trigger:
|
|
if marginMode == 'spot':
|
|
# gate spot trigger orders use the term normal instead of spot
|
|
marginMode = 'normal'
|
|
if marginMode == 'cross_margin':
|
|
raise BadRequest(self.id + ' getMarginMode() does not support trigger orders for cross margin')
|
|
isUnifiedAccount = False
|
|
isUnifiedAccount, params = self.handle_option_and_params(params, 'getMarginMode', 'unifiedAccount')
|
|
if isUnifiedAccount:
|
|
marginMode = 'unified'
|
|
return [marginMode, params]
|
|
|
|
def get_settlement_currencies(self, type, method):
|
|
options = self.safe_value(self.options, type, {}) # ['BTC', 'USDT'] unified codes
|
|
fetchMarketsContractOptions = self.safe_value(options, method, {})
|
|
defaultSettle = ['usdt'] if (type == 'swap') else ['btc']
|
|
return self.safe_value(fetchMarketsContractOptions, 'settlementCurrencies', defaultSettle)
|
|
|
|
def fetch_currencies(self, params={}) -> Currencies:
|
|
"""
|
|
fetches all available currencies on an exchange
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-currencies-details
|
|
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: an associative dictionary of currencies
|
|
"""
|
|
# sandbox/testnet only supports future markets
|
|
apiBackup = self.safe_value(self.urls, 'apiBackup')
|
|
if apiBackup is not None:
|
|
return {}
|
|
response = self.publicSpotGetCurrencies(params)
|
|
#
|
|
# [
|
|
# {
|
|
# "currency": "USDT",
|
|
# "name": "Tether",
|
|
# "delisted": False,
|
|
# "withdraw_disabled": False,
|
|
# "withdraw_delayed": False,
|
|
# "deposit_disabled": False,
|
|
# "trade_disabled": False,
|
|
# "fixed_rate": "",
|
|
# "chain": "ETH",
|
|
# "chains": [
|
|
# {
|
|
# "name": "ETH",
|
|
# "addr": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
# "withdraw_disabled": False,
|
|
# "withdraw_delayed": False,
|
|
# "deposit_disabled": False
|
|
# },
|
|
# {
|
|
# "name": "ARBEVM",
|
|
# "addr": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
|
# "withdraw_disabled": False,
|
|
# "withdraw_delayed": False,
|
|
# "deposit_disabled": False
|
|
# },
|
|
# {
|
|
# "name": "BSC",
|
|
# "addr": "0x55d398326f99059fF775485246999027B3197955",
|
|
# "withdraw_disabled": False,
|
|
# "withdraw_delayed": False,
|
|
# "deposit_disabled": False
|
|
# },
|
|
# ]
|
|
# },
|
|
# ]
|
|
#
|
|
indexedCurrencies = self.index_by(response, 'currency')
|
|
result: dict = {}
|
|
for i in range(0, len(response)):
|
|
entry = response[i]
|
|
currencyId = self.safe_string(entry, 'currency')
|
|
code = self.safe_currency_code(currencyId)
|
|
# check leveraged tokens(e.g. BTC3S, ETH5L)
|
|
type = 'leveraged' if self.is_leveraged_currency(currencyId, True, indexedCurrencies) else 'crypto'
|
|
chains = self.safe_list(entry, 'chains', [])
|
|
networks = {}
|
|
for j in range(0, len(chains)):
|
|
chain = chains[j]
|
|
networkId = self.safe_string(chain, 'name')
|
|
networkCode = self.network_id_to_code(networkId)
|
|
networks[networkCode] = {
|
|
'info': chain,
|
|
'id': networkId,
|
|
'network': networkCode,
|
|
'active': None,
|
|
'deposit': not self.safe_bool(chain, 'deposit_disabled'),
|
|
'withdraw': not self.safe_bool(chain, 'withdraw_disabled'),
|
|
'fee': None,
|
|
'precision': self.parse_number('0.0001'), # temporary safe default, because no value provided from API,
|
|
'limits': {
|
|
'deposit': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'withdraw': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
},
|
|
}
|
|
result[code] = self.safe_currency_structure({
|
|
'id': currencyId,
|
|
'code': code,
|
|
'name': self.safe_string(entry, 'name'),
|
|
'type': type,
|
|
'active': not self.safe_bool(entry, 'delisted'),
|
|
'deposit': not self.safe_bool(entry, 'deposit_disabled'),
|
|
'withdraw': not self.safe_bool(entry, 'withdraw_disabled'),
|
|
'fee': None,
|
|
'networks': networks,
|
|
'precision': self.parse_number('0.0001'),
|
|
'info': entry,
|
|
})
|
|
return result
|
|
|
|
def fetch_funding_rate(self, symbol: str, params={}) -> FundingRate:
|
|
"""
|
|
fetch the current funding rate
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-a-single-contract
|
|
|
|
:param str symbol: unified market symbol
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
if not market['swap']:
|
|
raise BadSymbol(self.id + ' fetchFundingRate() supports swap contracts only')
|
|
request, query = self.prepare_request(market, None, params)
|
|
response = self.publicFuturesGetSettleContractsContract(self.extend(request, query))
|
|
#
|
|
# [
|
|
# {
|
|
# "name": "BTC_USDT",
|
|
# "type": "direct",
|
|
# "quanto_multiplier": "0.0001",
|
|
# "ref_discount_rate": "0",
|
|
# "order_price_deviate": "0.5",
|
|
# "maintenance_rate": "0.005",
|
|
# "mark_type": "index",
|
|
# "last_price": "38026",
|
|
# "mark_price": "37985.6",
|
|
# "index_price": "37954.92",
|
|
# "funding_rate_indicative": "0.000219",
|
|
# "mark_price_round": "0.01",
|
|
# "funding_offset": 0,
|
|
# "in_delisting": False,
|
|
# "risk_limit_base": "1000000",
|
|
# "interest_rate": "0.0003",
|
|
# "order_price_round": "0.1",
|
|
# "order_size_min": 1,
|
|
# "ref_rebate_rate": "0.2",
|
|
# "funding_interval": 28800,
|
|
# "risk_limit_step": "1000000",
|
|
# "leverage_min": "1",
|
|
# "leverage_max": "100",
|
|
# "risk_limit_max": "8000000",
|
|
# "maker_fee_rate": "-0.00025",
|
|
# "taker_fee_rate": "0.00075",
|
|
# "funding_rate": "0.002053",
|
|
# "order_size_max": 1000000,
|
|
# "funding_next_apply": 1610035200,
|
|
# "short_users": 977,
|
|
# "config_change_time": 1609899548,
|
|
# "trade_size": 28530850594,
|
|
# "position_size": 5223816,
|
|
# "long_users": 455,
|
|
# "funding_impact_value": "60000",
|
|
# "orders_limit": 50,
|
|
# "trade_id": 10851092,
|
|
# "orderbook_id": 2129638396
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_funding_rate(response)
|
|
|
|
def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
|
|
"""
|
|
fetch the funding rate for multiple markets
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-futures-contracts
|
|
|
|
:param str[]|None symbols: list of unified market symbols
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rates-structure>`, indexed by market symbols
|
|
"""
|
|
self.load_markets()
|
|
symbols = self.market_symbols(symbols)
|
|
market = None
|
|
if symbols is not None:
|
|
firstSymbol = self.safe_string(symbols, 0)
|
|
market = self.market(firstSymbol)
|
|
request, query = self.prepare_request(market, 'swap', params)
|
|
response = self.publicFuturesGetSettleContracts(self.extend(request, query))
|
|
#
|
|
# [
|
|
# {
|
|
# "name": "BTC_USDT",
|
|
# "type": "direct",
|
|
# "quanto_multiplier": "0.0001",
|
|
# "ref_discount_rate": "0",
|
|
# "order_price_deviate": "0.5",
|
|
# "maintenance_rate": "0.005",
|
|
# "mark_type": "index",
|
|
# "last_price": "38026",
|
|
# "mark_price": "37985.6",
|
|
# "index_price": "37954.92",
|
|
# "funding_rate_indicative": "0.000219",
|
|
# "mark_price_round": "0.01",
|
|
# "funding_offset": 0,
|
|
# "in_delisting": False,
|
|
# "risk_limit_base": "1000000",
|
|
# "interest_rate": "0.0003",
|
|
# "order_price_round": "0.1",
|
|
# "order_size_min": 1,
|
|
# "ref_rebate_rate": "0.2",
|
|
# "funding_interval": 28800,
|
|
# "risk_limit_step": "1000000",
|
|
# "leverage_min": "1",
|
|
# "leverage_max": "100",
|
|
# "risk_limit_max": "8000000",
|
|
# "maker_fee_rate": "-0.00025",
|
|
# "taker_fee_rate": "0.00075",
|
|
# "funding_rate": "0.002053",
|
|
# "order_size_max": 1000000,
|
|
# "funding_next_apply": 1610035200,
|
|
# "short_users": 977,
|
|
# "config_change_time": 1609899548,
|
|
# "trade_size": 28530850594,
|
|
# "position_size": 5223816,
|
|
# "long_users": 455,
|
|
# "funding_impact_value": "60000",
|
|
# "orders_limit": 50,
|
|
# "trade_id": 10851092,
|
|
# "orderbook_id": 2129638396
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_funding_rates(response, symbols)
|
|
|
|
def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
|
|
#
|
|
# {
|
|
# "name": "BTC_USDT",
|
|
# "type": "direct",
|
|
# "quanto_multiplier": "0.0001",
|
|
# "ref_discount_rate": "0",
|
|
# "order_price_deviate": "0.5",
|
|
# "maintenance_rate": "0.005",
|
|
# "mark_type": "index",
|
|
# "last_price": "38026",
|
|
# "mark_price": "37985.6",
|
|
# "index_price": "37954.92",
|
|
# "funding_rate_indicative": "0.000219",
|
|
# "mark_price_round": "0.01",
|
|
# "funding_offset": 0,
|
|
# "in_delisting": False,
|
|
# "risk_limit_base": "1000000",
|
|
# "interest_rate": "0.0003",
|
|
# "order_price_round": "0.1",
|
|
# "order_size_min": 1,
|
|
# "ref_rebate_rate": "0.2",
|
|
# "funding_interval": 28800,
|
|
# "risk_limit_step": "1000000",
|
|
# "leverage_min": "1",
|
|
# "leverage_max": "100",
|
|
# "risk_limit_max": "8000000",
|
|
# "maker_fee_rate": "-0.00025",
|
|
# "taker_fee_rate": "0.00075",
|
|
# "funding_rate": "0.002053",
|
|
# "order_size_max": 1000000,
|
|
# "funding_next_apply": 1610035200,
|
|
# "short_users": 977,
|
|
# "config_change_time": 1609899548,
|
|
# "trade_size": 28530850594,
|
|
# "position_size": 5223816,
|
|
# "long_users": 455,
|
|
# "funding_impact_value": "60000",
|
|
# "orders_limit": 50,
|
|
# "trade_id": 10851092,
|
|
# "orderbook_id": 2129638396
|
|
# }
|
|
#
|
|
marketId = self.safe_string(contract, 'name')
|
|
symbol = self.safe_symbol(marketId, market, '_', 'swap')
|
|
markPrice = self.safe_number(contract, 'mark_price')
|
|
indexPrice = self.safe_number(contract, 'index_price')
|
|
interestRate = self.safe_number(contract, 'interest_rate')
|
|
fundingRate = self.safe_number(contract, 'funding_rate')
|
|
fundingTime = self.safe_timestamp(contract, 'funding_next_apply')
|
|
fundingRateIndicative = self.safe_number(contract, 'funding_rate_indicative')
|
|
fundingInterval = Precise.string_mul('1000', self.safe_string(contract, 'funding_interval'))
|
|
return {
|
|
'info': contract,
|
|
'symbol': symbol,
|
|
'markPrice': markPrice,
|
|
'indexPrice': indexPrice,
|
|
'interestRate': interestRate,
|
|
'estimatedSettlePrice': None,
|
|
'timestamp': None,
|
|
'datetime': None,
|
|
'fundingRate': fundingRate,
|
|
'fundingTimestamp': fundingTime,
|
|
'fundingDatetime': self.iso8601(fundingTime),
|
|
'nextFundingRate': fundingRateIndicative,
|
|
'nextFundingTimestamp': None,
|
|
'nextFundingDatetime': None,
|
|
'previousFundingRate': None,
|
|
'previousFundingTimestamp': None,
|
|
'previousFundingDatetime': None,
|
|
'interval': self.parse_funding_interval(fundingInterval),
|
|
}
|
|
|
|
def parse_funding_interval(self, interval):
|
|
intervals: dict = {
|
|
'3600000': '1h',
|
|
'14400000': '4h',
|
|
'28800000': '8h',
|
|
'57600000': '16h',
|
|
'86400000': '24h',
|
|
}
|
|
return self.safe_string(intervals, interval, interval)
|
|
|
|
def fetch_network_deposit_address(self, code: str, params={}):
|
|
self.load_markets()
|
|
currency = self.currency(code)
|
|
request: dict = {
|
|
'currency': currency['id'], # todo: currencies have network-junctions
|
|
}
|
|
response = self.privateWalletGetDepositAddress(self.extend(request, params))
|
|
addresses = self.safe_value(response, 'multichain_addresses')
|
|
currencyId = self.safe_string(response, 'currency')
|
|
code = self.safe_currency_code(currencyId)
|
|
result: dict = {}
|
|
for i in range(0, len(addresses)):
|
|
entry = addresses[i]
|
|
#
|
|
# {
|
|
# "chain": "ETH",
|
|
# "address": "0x359a697945E79C7e17b634675BD73B33324E9408",
|
|
# "payment_id": "",
|
|
# "payment_name": "",
|
|
# "obtain_failed": "0"
|
|
# }
|
|
#
|
|
obtainFailed = self.safe_integer(entry, 'obtain_failed')
|
|
if obtainFailed:
|
|
continue
|
|
network = self.safe_string(entry, 'chain')
|
|
address = self.safe_string(entry, 'address')
|
|
tag = self.safe_string(entry, 'payment_id')
|
|
result[network] = {
|
|
'info': entry,
|
|
'code': code, # kept here for backward-compatibility, but will be removed soon
|
|
'currency': code,
|
|
'address': address,
|
|
'tag': tag,
|
|
}
|
|
return result
|
|
|
|
def fetch_deposit_addresses_by_network(self, code: str, params={}) -> List[DepositAddress]:
|
|
"""
|
|
fetch a dictionary of addresses for a currency, indexed by network
|
|
:param str code: unified currency code of the currency for the deposit address
|
|
:param dict [params]: extra parameters specific to the api endpoint
|
|
:returns dict: a dictionary of `address structures <https://docs.ccxt.com/#/?id=address-structure>` indexed by the network
|
|
"""
|
|
self.load_markets()
|
|
currency = self.currency(code)
|
|
request = {
|
|
'currency': currency['id'],
|
|
}
|
|
response = self.privateWalletGetDepositAddress(self.extend(request, params))
|
|
chains = self.safe_value(response, 'multichain_addresses', [])
|
|
currencyId = self.safe_string(response, 'currency')
|
|
currency = self.safe_currency(currencyId, currency)
|
|
parsed = self.parse_deposit_addresses(chains, None, False)
|
|
return self.index_by(parsed, 'network')
|
|
|
|
def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
|
|
"""
|
|
fetch the deposit address for a currency associated with self account
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#generate-currency-deposit-address
|
|
|
|
:param str code: unified currency code
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.network]: unified network code(not used directly by gate.com but used by ccxt to filter the response)
|
|
:returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
|
|
"""
|
|
self.load_markets()
|
|
networkCode = None
|
|
networkCode, params = self.handle_network_code_and_params(params)
|
|
chainsIndexedById = self.fetch_deposit_addresses_by_network(code, params)
|
|
selectedNetworkIdOrCode = self.select_network_code_from_unified_networks(code, networkCode, chainsIndexedById)
|
|
return chainsIndexedById[selectedNetworkIdOrCode]
|
|
|
|
def parse_deposit_address(self, depositAddress, currency=None):
|
|
#
|
|
# {
|
|
# chain: "BTC",
|
|
# address: "1Nxu.......Ys",
|
|
# payment_id: "",
|
|
# payment_name: "",
|
|
# obtain_failed: "0",
|
|
# }
|
|
#
|
|
address = self.safe_string(depositAddress, 'address')
|
|
self.check_address(address)
|
|
return {
|
|
'info': depositAddress,
|
|
'currency': self.safe_string(currency, 'code'),
|
|
'address': address,
|
|
'tag': self.safe_string(depositAddress, 'payment_id'),
|
|
'network': self.network_id_to_code(self.safe_string(depositAddress, 'chain')),
|
|
}
|
|
|
|
def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
|
|
"""
|
|
fetch the trading fees for a market
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#retrieve-personal-trading-fee
|
|
|
|
:param str symbol: unified market symbol
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'currency_pair': market['id'],
|
|
}
|
|
response = self.privateWalletGetFee(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "user_id": 1486602,
|
|
# "taker_fee": "0.002",
|
|
# "maker_fee": "0.002",
|
|
# "gt_discount": True,
|
|
# "gt_taker_fee": "0.0015",
|
|
# "gt_maker_fee": "0.0015",
|
|
# "loan_fee": "0.18",
|
|
# "point_type": "0",
|
|
# "futures_taker_fee": "0.0005",
|
|
# "futures_maker_fee": "0"
|
|
# }
|
|
#
|
|
return self.parse_trading_fee(response, market)
|
|
|
|
def fetch_trading_fees(self, params={}) -> TradingFees:
|
|
"""
|
|
fetch the trading fees for multiple markets
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#retrieve-personal-trading-fee
|
|
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
|
|
"""
|
|
self.load_markets()
|
|
response = self.privateWalletGetFee(params)
|
|
#
|
|
# {
|
|
# "user_id": 1486602,
|
|
# "taker_fee": "0.002",
|
|
# "maker_fee": "0.002",
|
|
# "gt_discount": True,
|
|
# "gt_taker_fee": "0.0015",
|
|
# "gt_maker_fee": "0.0015",
|
|
# "loan_fee": "0.18",
|
|
# "point_type": "0",
|
|
# "futures_taker_fee": "0.0005",
|
|
# "futures_maker_fee": "0"
|
|
# }
|
|
#
|
|
return self.parse_trading_fees(response)
|
|
|
|
def parse_trading_fees(self, response):
|
|
result: dict = {}
|
|
for i in range(0, len(self.symbols)):
|
|
symbol = self.symbols[i]
|
|
market = self.market(symbol)
|
|
result[symbol] = self.parse_trading_fee(response, market)
|
|
return result
|
|
|
|
def parse_trading_fee(self, info, market: Market = None):
|
|
#
|
|
# {
|
|
# "user_id": 1486602,
|
|
# "taker_fee": "0.002",
|
|
# "maker_fee": "0.002",
|
|
# "gt_discount": True,
|
|
# "gt_taker_fee": "0.0015",
|
|
# "gt_maker_fee": "0.0015",
|
|
# "loan_fee": "0.18",
|
|
# "point_type": "0",
|
|
# "futures_taker_fee": "0.0005",
|
|
# "futures_maker_fee": "0"
|
|
# }
|
|
#
|
|
gtDiscount = self.safe_value(info, 'gt_discount')
|
|
taker = 'gt_taker_fee' if gtDiscount else 'taker_fee'
|
|
maker = 'gt_maker_fee' if gtDiscount else 'maker_fee'
|
|
contract = self.safe_value(market, 'contract')
|
|
takerKey = 'futures_taker_fee' if contract else taker
|
|
makerKey = 'futures_maker_fee' if contract else maker
|
|
return {
|
|
'info': info,
|
|
'symbol': self.safe_string(market, 'symbol'),
|
|
'maker': self.safe_number(info, makerKey),
|
|
'taker': self.safe_number(info, takerKey),
|
|
'percentage': None,
|
|
'tierBased': None,
|
|
}
|
|
|
|
def fetch_transaction_fees(self, codes: Strings = None, params={}):
|
|
"""
|
|
@deprecated
|
|
please use fetchDepositWithdrawFees instead
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#retrieve-withdrawal-status
|
|
|
|
:param str[]|None codes: list of unified currency codes
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a list of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>`
|
|
"""
|
|
self.load_markets()
|
|
response = self.privateWalletGetWithdrawStatus(params)
|
|
#
|
|
# {
|
|
# "currency": "MTN",
|
|
# "name": "Medicalchain",
|
|
# "name_cn": "Medicalchain",
|
|
# "deposit": "0",
|
|
# "withdraw_percent": "0%",
|
|
# "withdraw_fix": "900",
|
|
# "withdraw_day_limit": "500000",
|
|
# "withdraw_day_limit_remain": "500000",
|
|
# "withdraw_amount_mini": "900.1",
|
|
# "withdraw_eachtime_limit": "90000000000",
|
|
# "withdraw_fix_on_chains": {
|
|
# "ETH": "900"
|
|
# }
|
|
# }
|
|
#
|
|
result: dict = {}
|
|
withdrawFees = {}
|
|
for i in range(0, len(response)):
|
|
withdrawFees = {}
|
|
entry = response[i]
|
|
currencyId = self.safe_string(entry, 'currency')
|
|
code = self.safe_currency_code(currencyId)
|
|
if (codes is not None) and not self.in_array(code, codes):
|
|
continue
|
|
withdrawFixOnChains = self.safe_value(entry, 'withdraw_fix_on_chains')
|
|
if withdrawFixOnChains is None:
|
|
withdrawFees = self.safe_number(entry, 'withdraw_fix')
|
|
else:
|
|
networkIds = list(withdrawFixOnChains.keys())
|
|
for j in range(0, len(networkIds)):
|
|
networkId = networkIds[j]
|
|
networkCode = self.network_id_to_code(networkId)
|
|
withdrawFees[networkCode] = self.parse_number(withdrawFixOnChains[networkId])
|
|
result[code] = {
|
|
'withdraw': withdrawFees,
|
|
'deposit': None,
|
|
'info': entry,
|
|
}
|
|
return result
|
|
|
|
def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
|
|
"""
|
|
fetch deposit and withdraw fees
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#retrieve-withdrawal-status
|
|
|
|
:param str[]|None codes: list of unified currency codes
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a list of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>`
|
|
"""
|
|
self.load_markets()
|
|
response = self.privateWalletGetWithdrawStatus(params)
|
|
#
|
|
# [
|
|
# {
|
|
# "currency": "MTN",
|
|
# "name": "Medicalchain",
|
|
# "name_cn": "Medicalchain",
|
|
# "deposit": "0",
|
|
# "withdraw_percent": "0%",
|
|
# "withdraw_fix": "900",
|
|
# "withdraw_day_limit": "500000",
|
|
# "withdraw_day_limit_remain": "500000",
|
|
# "withdraw_amount_mini": "900.1",
|
|
# "withdraw_eachtime_limit": "90000000000",
|
|
# "withdraw_fix_on_chains": {
|
|
# "ETH": "900"
|
|
# }
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_deposit_withdraw_fees(response, codes, 'currency')
|
|
|
|
def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
|
|
#
|
|
# {
|
|
# "currency": "MTN",
|
|
# "name": "Medicalchain",
|
|
# "name_cn": "Medicalchain",
|
|
# "deposit": "0",
|
|
# "withdraw_percent": "0%",
|
|
# "withdraw_fix": "900",
|
|
# "withdraw_day_limit": "500000",
|
|
# "withdraw_day_limit_remain": "500000",
|
|
# "withdraw_amount_mini": "900.1",
|
|
# "withdraw_eachtime_limit": "90000000000",
|
|
# "withdraw_fix_on_chains": {
|
|
# "ETH": "900"
|
|
# }
|
|
# }
|
|
#
|
|
withdrawFixOnChains = self.safe_value(fee, 'withdraw_fix_on_chains')
|
|
result: dict = {
|
|
'info': fee,
|
|
'withdraw': {
|
|
'fee': self.safe_number(fee, 'withdraw_fix'),
|
|
'percentage': False,
|
|
},
|
|
'deposit': {
|
|
'fee': self.safe_number(fee, 'deposit'),
|
|
'percentage': False,
|
|
},
|
|
'networks': {},
|
|
}
|
|
if withdrawFixOnChains is not None:
|
|
chainKeys = list(withdrawFixOnChains.keys())
|
|
for i in range(0, len(chainKeys)):
|
|
chainKey = chainKeys[i]
|
|
networkCode = self.network_id_to_code(chainKey, self.safe_string(fee, 'currency'))
|
|
result['networks'][networkCode] = {
|
|
'withdraw': {
|
|
'fee': self.parse_number(withdrawFixOnChains[chainKey]),
|
|
'percentage': False,
|
|
},
|
|
'deposit': {
|
|
'fee': None,
|
|
'percentage': None,
|
|
},
|
|
}
|
|
return result
|
|
|
|
def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
fetch the history of funding payments paid and received on self account
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#query-account-book-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#query-account-book-3
|
|
|
|
:param str symbol: unified market symbol
|
|
:param int [since]: the earliest time in ms to fetch funding history for
|
|
:param int [limit]: the maximum number of funding history structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `funding history structure <https://docs.ccxt.com/#/?id=funding-history-structure>`
|
|
"""
|
|
self.load_markets()
|
|
# defaultType = 'future'
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
symbol = market['symbol']
|
|
type, query = self.handle_market_type_and_params('fetchFundingHistory', market, params)
|
|
request, requestParams = self.prepare_request(market, type, query)
|
|
request['type'] = 'fund' # 'dnw' 'pnl' 'fee' 'refr' 'fund' 'point_dnw' 'point_fee' 'point_refr'
|
|
if since is not None:
|
|
# from should be integer
|
|
request['from'] = self.parse_to_int(since / 1000)
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
response = None
|
|
if type == 'swap':
|
|
response = self.privateFuturesGetSettleAccountBook(self.extend(request, requestParams))
|
|
elif type == 'future':
|
|
response = self.privateDeliveryGetSettleAccountBook(self.extend(request, requestParams))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchFundingHistory() only support swap & future market type')
|
|
#
|
|
# [
|
|
# {
|
|
# "time": 1646899200,
|
|
# "change": "-0.027722",
|
|
# "balance": "11.653120591841",
|
|
# "text": "XRP_USDT",
|
|
# "type": "fund"
|
|
# },
|
|
# ...
|
|
# ]
|
|
#
|
|
return self.parse_funding_histories(response, symbol, since, limit)
|
|
|
|
def parse_funding_histories(self, response, symbol, since, limit) -> List[FundingHistory]:
|
|
result = []
|
|
for i in range(0, len(response)):
|
|
entry = response[i]
|
|
funding = self.parse_funding_history(entry)
|
|
result.append(funding)
|
|
sorted = self.sort_by(result, 'timestamp')
|
|
return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
|
|
|
|
def parse_funding_history(self, info, market: Market = None):
|
|
#
|
|
# {
|
|
# "time": 1646899200,
|
|
# "change": "-0.027722",
|
|
# "balance": "11.653120591841",
|
|
# "text": "XRP_USDT",
|
|
# "type": "fund"
|
|
# }
|
|
#
|
|
timestamp = self.safe_timestamp(info, 'time')
|
|
marketId = self.safe_string(info, 'text')
|
|
market = self.safe_market(marketId, market, '_', 'swap')
|
|
return {
|
|
'info': info,
|
|
'symbol': self.safe_string(market, 'symbol'),
|
|
'code': self.safe_string(market, 'settle'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'id': None,
|
|
'amount': self.safe_number(info, 'change'),
|
|
}
|
|
|
|
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://www.gate.com/docs/developers/apiv4/en/#retrieve-order-book
|
|
https://www.gate.com/docs/developers/apiv4/en/#futures-order-book
|
|
https://www.gate.com/docs/developers/apiv4/en/#futures-order-book-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#options-order-book
|
|
|
|
: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
|
|
: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)
|
|
#
|
|
# request: Dict = {
|
|
# 'currency_pair': market['id'],
|
|
# 'interval': '0', # depth, 0 means no aggregation is applied, default to 0
|
|
# 'limit': limit, # maximum number of order depth data in asks or bids
|
|
# 'with_id': True, # return order book ID
|
|
# }
|
|
#
|
|
request, query = self.prepare_request(market, market['type'], params)
|
|
if limit is not None:
|
|
if market['spot']:
|
|
limit = min(limit, 1000)
|
|
else:
|
|
limit = min(limit, 300)
|
|
request['limit'] = limit
|
|
request['with_id'] = True
|
|
response = None
|
|
if market['spot'] or market['margin']:
|
|
response = self.publicSpotGetOrderBook(self.extend(request, query))
|
|
elif market['swap']:
|
|
response = self.publicFuturesGetSettleOrderBook(self.extend(request, query))
|
|
elif market['future']:
|
|
response = self.publicDeliveryGetSettleOrderBook(self.extend(request, query))
|
|
elif market['option']:
|
|
response = self.publicOptionsGetOrderBook(self.extend(request, query))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchOrderBook() not support self market type')
|
|
#
|
|
# spot
|
|
#
|
|
# {
|
|
# "id": 6358770031
|
|
# "current": 1634345973275,
|
|
# "update": 1634345973271,
|
|
# "asks": [
|
|
# ["2.2241","12449.827"],
|
|
# ["2.2242","200"],
|
|
# ["2.2244","826.931"],
|
|
# ["2.2248","3876.107"],
|
|
# ["2.225","2377.252"],
|
|
# ["2.22509","439.484"],
|
|
# ["2.2251","1489.313"],
|
|
# ["2.2253","714.582"],
|
|
# ["2.2254","1349.784"],
|
|
# ["2.2256","234.701"]],
|
|
# "bids": [
|
|
# ["2.2236","32.465"],
|
|
# ["2.2232","243.983"],
|
|
# ["2.2231","32.207"],
|
|
# ["2.223","449.827"],
|
|
# ["2.2228","7.918"],
|
|
# ["2.2227","12703.482"],
|
|
# ["2.2226","143.033"],
|
|
# ["2.2225","143.027"],
|
|
# ["2.2224","1369.352"],
|
|
# ["2.2223","756.063"]
|
|
# ]
|
|
# }
|
|
#
|
|
# swap, future and option
|
|
#
|
|
# {
|
|
# "id": 6358770031
|
|
# "current": 1634350208.745,
|
|
# "asks": [
|
|
# {"s": 24909, "p": "61264.8"},
|
|
# {"s": 81, "p": "61266.6"},
|
|
# {"s": 2000, "p": "61267.6"},
|
|
# {"s": 490, "p": "61270.2"},
|
|
# {"s": 12, "p": "61270.4"},
|
|
# {"s": 11782, "p": "61273.2"},
|
|
# {"s": 14666, "p": "61273.3"},
|
|
# {"s": 22541, "p": "61273.4"},
|
|
# {"s": 33, "p": "61273.6"},
|
|
# {"s": 11980, "p": "61274.5"}
|
|
# ],
|
|
# "bids": [
|
|
# {"s": 41844, "p": "61264.7"},
|
|
# {"s": 13783, "p": "61263.3"},
|
|
# {"s": 1143, "p": "61259.8"},
|
|
# {"s": 81, "p": "61258.7"},
|
|
# {"s": 2471, "p": "61257.8"},
|
|
# {"s": 2471, "p": "61257.7"},
|
|
# {"s": 2471, "p": "61256.5"},
|
|
# {"s": 3, "p": "61254.2"},
|
|
# {"s": 114, "p": "61252.4"},
|
|
# {"s": 14372, "p": "61248.6"}
|
|
# ],
|
|
# "update": 1634350208.724
|
|
# }
|
|
#
|
|
timestamp = self.safe_integer(response, 'current')
|
|
if not market['spot']:
|
|
timestamp = timestamp * 1000
|
|
priceKey = 0 if market['spot'] else 'p'
|
|
amountKey = 1 if market['spot'] else 's'
|
|
nonce = self.safe_integer(response, 'id')
|
|
result = self.parse_order_book(response, symbol, timestamp, 'bids', 'asks', priceKey, amountKey)
|
|
result['nonce'] = nonce
|
|
return result
|
|
|
|
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://www.gate.com/docs/developers/apiv4/en/#get-details-of-a-specifc-order
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-futures-tickers
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-futures-tickers-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-tickers-of-options-contracts
|
|
|
|
: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)
|
|
request, query = self.prepare_request(market, None, params)
|
|
response = None
|
|
if market['spot'] or market['margin']:
|
|
response = self.publicSpotGetTickers(self.extend(request, query))
|
|
elif market['swap']:
|
|
response = self.publicFuturesGetSettleTickers(self.extend(request, query))
|
|
elif market['future']:
|
|
response = self.publicDeliveryGetSettleTickers(self.extend(request, query))
|
|
elif market['option']:
|
|
marketId = market['id']
|
|
optionParts = marketId.split('-')
|
|
request['underlying'] = self.safe_string(optionParts, 0)
|
|
response = self.publicOptionsGetTickers(self.extend(request, query))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchTicker() not support self market type')
|
|
ticker = None
|
|
if market['option']:
|
|
for i in range(0, len(response)):
|
|
entry = response[i]
|
|
if entry['name'] == market['id']:
|
|
ticker = entry
|
|
break
|
|
else:
|
|
ticker = self.safe_value(response, 0)
|
|
return self.parse_ticker(ticker, market)
|
|
|
|
def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
|
#
|
|
# SPOT
|
|
#
|
|
# {
|
|
# "currency_pair": "KFC_USDT",
|
|
# "last": "7.255",
|
|
# "lowest_ask": "7.298",
|
|
# "highest_bid": "7.218",
|
|
# "change_percentage": "-1.18",
|
|
# "base_volume": "1219.053687865",
|
|
# "quote_volume": "8807.40299875455",
|
|
# "high_24h": "7.262",
|
|
# "low_24h": "7.095"
|
|
# }
|
|
#
|
|
# LINEAR/DELIVERY
|
|
#
|
|
# {
|
|
# "contract": "BTC_USDT",
|
|
# "last": "6432",
|
|
# "low_24h": "6278",
|
|
# "high_24h": "6790",
|
|
# "change_percentage": "4.43",
|
|
# "total_size": "32323904",
|
|
# "volume_24h": "184040233284",
|
|
# "volume_24h_btc": "28613220",
|
|
# "volume_24h_usd": "184040233284",
|
|
# "volume_24h_base": "28613220",
|
|
# "volume_24h_quote": "184040233284",
|
|
# "volume_24h_settle": "28613220",
|
|
# "mark_price": "6534",
|
|
# "funding_rate": "0.0001",
|
|
# "funding_rate_indicative": "0.0001",
|
|
# "index_price": "6531"
|
|
# }
|
|
#
|
|
# bookTicker
|
|
# {
|
|
# "t": 1671363004228,
|
|
# "u": 9793320464,
|
|
# "s": "BTC_USDT",
|
|
# "b": "16716.8", # best bid price
|
|
# "B": "0.0134", # best bid size
|
|
# "a": "16716.9", # best ask price
|
|
# "A": "0.0353" # best ask size
|
|
# }
|
|
#
|
|
# option
|
|
#
|
|
# {
|
|
# "vega": "0.00002",
|
|
# "leverage": "12.277188268663",
|
|
# "ask_iv": "0",
|
|
# "delta": "-0.99999",
|
|
# "last_price": "0",
|
|
# "theta": "-0.00661",
|
|
# "bid1_price": "1096",
|
|
# "mark_iv": "0.7799",
|
|
# "name": "BTC_USDT-20230608-28500-P",
|
|
# "bid_iv": "0",
|
|
# "ask1_price": "2935",
|
|
# "mark_price": "2147.3",
|
|
# "position_size": 0,
|
|
# "bid1_size": 12,
|
|
# "ask1_size": -14,
|
|
# "gamma": "0"
|
|
# }
|
|
#
|
|
marketId = self.safe_string_n(ticker, ['currency_pair', 'contract', 'name'])
|
|
marketType = 'contract' if ('mark_price' in ticker) else 'spot'
|
|
symbol = self.safe_symbol(marketId, market, '_', marketType)
|
|
last = self.safe_string_2(ticker, 'last', 'last_price')
|
|
ask = self.safe_string_n(ticker, ['lowest_ask', 'a', 'ask1_price'])
|
|
bid = self.safe_string_n(ticker, ['highest_bid', 'b', 'bid1_price'])
|
|
high = self.safe_string(ticker, 'high_24h')
|
|
low = self.safe_string(ticker, 'low_24h')
|
|
bidVolume = self.safe_string_2(ticker, 'B', 'bid1_size')
|
|
askVolume = self.safe_string_2(ticker, 'A', 'ask1_size')
|
|
timestamp = self.safe_integer(ticker, 't')
|
|
baseVolume = self.safe_string_2(ticker, 'base_volume', 'volume_24h_base')
|
|
if baseVolume == 'nan':
|
|
baseVolume = '0'
|
|
quoteVolume = self.safe_string_2(ticker, 'quote_volume', 'volume_24h_quote')
|
|
if quoteVolume == 'nan':
|
|
quoteVolume = '0'
|
|
percentage = self.safe_string(ticker, 'change_percentage')
|
|
return self.safe_ticker({
|
|
'symbol': symbol,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'high': high,
|
|
'low': low,
|
|
'bid': bid,
|
|
'bidVolume': bidVolume,
|
|
'ask': ask,
|
|
'askVolume': askVolume,
|
|
'vwap': None,
|
|
'open': None,
|
|
'close': last,
|
|
'last': last,
|
|
'previousClose': None,
|
|
'change': None,
|
|
'percentage': percentage,
|
|
'average': None,
|
|
'baseVolume': baseVolume,
|
|
'quoteVolume': quoteVolume,
|
|
'markPrice': self.safe_string(ticker, 'mark_price'),
|
|
'indexPrice': self.safe_string(ticker, 'index_price'),
|
|
'info': ticker,
|
|
}, market)
|
|
|
|
def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
|
"""
|
|
fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-details-of-a-specifc-order
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-futures-tickers
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-futures-tickers-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-tickers-of-options-contracts
|
|
|
|
:param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
|
"""
|
|
self.load_markets()
|
|
symbols = self.market_symbols(symbols)
|
|
first = self.safe_string(symbols, 0)
|
|
market = None
|
|
if first is not None:
|
|
market = self.market(first)
|
|
type, query = self.handle_market_type_and_params('fetchTickers', market, params)
|
|
request, requestParams = self.prepare_request(None, type, query)
|
|
response = None
|
|
request['timezone'] = 'utc0' # default to utc
|
|
if type == 'spot' or type == 'margin':
|
|
response = self.publicSpotGetTickers(self.extend(request, requestParams))
|
|
elif type == 'swap':
|
|
response = self.publicFuturesGetSettleTickers(self.extend(request, requestParams))
|
|
elif type == 'future':
|
|
response = self.publicDeliveryGetSettleTickers(self.extend(request, requestParams))
|
|
elif type == 'option':
|
|
self.check_required_argument('fetchTickers', symbols, 'symbols')
|
|
marketId = market['id']
|
|
optionParts = marketId.split('-')
|
|
request['underlying'] = self.safe_string(optionParts, 0)
|
|
response = self.publicOptionsGetTickers(self.extend(request, requestParams))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchTickers() not support self market type, provide symbols or set params["defaultType"] to one from spot/margin/swap/future/option')
|
|
return self.parse_tickers(response, symbols)
|
|
|
|
def parse_balance_helper(self, entry):
|
|
account = self.account()
|
|
account['used'] = self.safe_string_2(entry, 'freeze', 'locked')
|
|
account['free'] = self.safe_string(entry, 'available')
|
|
account['total'] = self.safe_string(entry, 'total')
|
|
if 'borrowed' in entry:
|
|
account['debt'] = self.safe_string(entry, 'borrowed')
|
|
return account
|
|
|
|
def fetch_balance(self, params={}) -> Balances:
|
|
"""
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#margin-account-list
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-unified-account-information
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-spot-trading-accounts
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-futures-account
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-futures-account-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#query-account-information
|
|
|
|
:param dict [params]: exchange specific parameters
|
|
:param str [params.type]: spot, margin, swap or future, if not provided self.options['defaultType'] is used
|
|
:param str [params.settle]: 'btc' or 'usdt' - settle currency for perpetual swap and future - default="usdt" for swap and "btc" for future
|
|
:param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
|
|
:param str [params.symbol]: margin only - unified ccxt symbol
|
|
:param boolean [params.unifiedAccount]: default False, set to True for fetching the unified account balance
|
|
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
symbol = self.safe_string(params, 'symbol')
|
|
params = self.omit(params, 'symbol')
|
|
isUnifiedAccount = False
|
|
isUnifiedAccount, params = self.handle_option_and_params(params, 'fetchBalance', 'unifiedAccount')
|
|
type, query = self.handle_market_type_and_params('fetchBalance', None, params)
|
|
request, requestParams = self.prepare_request(None, type, query)
|
|
marginMode, requestQuery = self.get_margin_mode(False, requestParams)
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
request['currency_pair'] = market['id']
|
|
response = None
|
|
if isUnifiedAccount:
|
|
response = self.privateUnifiedGetAccounts(self.extend(request, params))
|
|
elif type == 'spot':
|
|
if marginMode == 'spot':
|
|
response = self.privateSpotGetAccounts(self.extend(request, requestQuery))
|
|
elif marginMode == 'margin':
|
|
response = self.privateMarginGetAccounts(self.extend(request, requestQuery))
|
|
elif marginMode == 'cross_margin':
|
|
response = self.privateMarginGetCrossAccounts(self.extend(request, requestQuery))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchBalance() not support self marginMode')
|
|
elif type == 'funding':
|
|
response = self.privateMarginGetFundingAccounts(self.extend(request, requestQuery))
|
|
elif type == 'swap':
|
|
response = self.privateFuturesGetSettleAccounts(self.extend(request, requestQuery))
|
|
elif type == 'future':
|
|
response = self.privateDeliveryGetSettleAccounts(self.extend(request, requestQuery))
|
|
elif type == 'option':
|
|
response = self.privateOptionsGetAccounts(self.extend(request, requestQuery))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchBalance() not support self market type')
|
|
contract = ((type == 'swap') or (type == 'future') or (type == 'option'))
|
|
if contract:
|
|
response = [response]
|
|
#
|
|
# Spot / margin funding
|
|
#
|
|
# [
|
|
# {
|
|
# "currency": "DBC",
|
|
# "available": "0",
|
|
# "locked": "0"
|
|
# "lent": "0", # margin funding only
|
|
# "total_lent": "0" # margin funding only
|
|
# },
|
|
# ...
|
|
# ]
|
|
#
|
|
# Margin
|
|
#
|
|
# [
|
|
# {
|
|
# "currency_pair": "DOGE_USDT",
|
|
# "locked": False,
|
|
# "risk": "9999.99",
|
|
# "base": {
|
|
# "currency": "DOGE",
|
|
# "available": "0",
|
|
# "locked": "0",
|
|
# "borrowed": "0",
|
|
# "interest": "0"
|
|
# },
|
|
# "quote": {
|
|
# "currency": "USDT",
|
|
# "available": "0.73402",
|
|
# "locked": "0",
|
|
# "borrowed": "0",
|
|
# "interest": "0"
|
|
# }
|
|
# },
|
|
# ...
|
|
# ]
|
|
#
|
|
# Cross margin
|
|
#
|
|
# {
|
|
# "user_id": 10406147,
|
|
# "locked": False,
|
|
# "balances": {
|
|
# "USDT": {
|
|
# "available": "1",
|
|
# "freeze": "0",
|
|
# "borrowed": "0",
|
|
# "interest": "0"
|
|
# }
|
|
# },
|
|
# "total": "1",
|
|
# "borrowed": "0",
|
|
# "interest": "0",
|
|
# "risk": "9999.99"
|
|
# }
|
|
#
|
|
# Perpetual Swap
|
|
#
|
|
# {
|
|
# "order_margin": "0",
|
|
# "point": "0",
|
|
# "bonus": "0",
|
|
# "history": {
|
|
# "dnw": "2.1321",
|
|
# "pnl": "11.5351",
|
|
# "refr": "0",
|
|
# "point_fee": "0",
|
|
# "fund": "-0.32340576684",
|
|
# "bonus_dnw": "0",
|
|
# "point_refr": "0",
|
|
# "bonus_offset": "0",
|
|
# "fee": "-0.20132775",
|
|
# "point_dnw": "0",
|
|
# },
|
|
# "unrealised_pnl": "13.315100000006",
|
|
# "total": "12.51345151332",
|
|
# "available": "0",
|
|
# "in_dual_mode": False,
|
|
# "currency": "USDT",
|
|
# "position_margin": "12.51345151332",
|
|
# "user": "6333333",
|
|
# }
|
|
#
|
|
# Delivery Future
|
|
#
|
|
# {
|
|
# "order_margin": "0",
|
|
# "point": "0",
|
|
# "history": {
|
|
# "dnw": "1",
|
|
# "pnl": "0",
|
|
# "refr": "0",
|
|
# "point_fee": "0",
|
|
# "point_dnw": "0",
|
|
# "settle": "0",
|
|
# "settle_fee": "0",
|
|
# "point_refr": "0",
|
|
# "fee": "0",
|
|
# },
|
|
# "unrealised_pnl": "0",
|
|
# "total": "1",
|
|
# "available": "1",
|
|
# "currency": "USDT",
|
|
# "position_margin": "0",
|
|
# "user": "6333333",
|
|
# }
|
|
#
|
|
# option
|
|
#
|
|
# {
|
|
# "order_margin": "0",
|
|
# "bid_order_margin": "0",
|
|
# "init_margin": "0",
|
|
# "history": {
|
|
# "dnw": "32",
|
|
# "set": "0",
|
|
# "point_fee": "0",
|
|
# "point_dnw": "0",
|
|
# "prem": "0",
|
|
# "point_refr": "0",
|
|
# "insur": "0",
|
|
# "fee": "0",
|
|
# "refr": "0"
|
|
# },
|
|
# "total": "32",
|
|
# "available": "32",
|
|
# "liq_triggered": False,
|
|
# "maint_margin": "0",
|
|
# "ask_order_margin": "0",
|
|
# "point": "0",
|
|
# "position_notional_limit": "2000000",
|
|
# "unrealised_pnl": "0",
|
|
# "equity": "32",
|
|
# "user": 5691076,
|
|
# "currency": "USDT",
|
|
# "short_enabled": False,
|
|
# "orders_limit": 10
|
|
# }
|
|
#
|
|
# unified
|
|
#
|
|
# {
|
|
# "user_id": 10001,
|
|
# "locked": False,
|
|
# "balances": {
|
|
# "ETH": {
|
|
# "available": "0",
|
|
# "freeze": "0",
|
|
# "borrowed": "0.075393666654",
|
|
# "negative_liab": "0",
|
|
# "futures_pos_liab": "0",
|
|
# "equity": "1016.1",
|
|
# "total_freeze": "0",
|
|
# "total_liab": "0"
|
|
# },
|
|
# "POINT": {
|
|
# "available": "9999999999.017023138734",
|
|
# "freeze": "0",
|
|
# "borrowed": "0",
|
|
# "negative_liab": "0",
|
|
# "futures_pos_liab": "0",
|
|
# "equity": "12016.1",
|
|
# "total_freeze": "0",
|
|
# "total_liab": "0"
|
|
# },
|
|
# "USDT": {
|
|
# "available": "0.00000062023",
|
|
# "freeze": "0",
|
|
# "borrowed": "0",
|
|
# "negative_liab": "0",
|
|
# "futures_pos_liab": "0",
|
|
# "equity": "16.1",
|
|
# "total_freeze": "0",
|
|
# "total_liab": "0"
|
|
# }
|
|
# },
|
|
# "total": "230.94621713",
|
|
# "borrowed": "161.66395521",
|
|
# "total_initial_margin": "1025.0524665088",
|
|
# "total_margin_balance": "3382495.944473949183",
|
|
# "total_maintenance_margin": "205.01049330176",
|
|
# "total_initial_margin_rate": "3299.827135672679",
|
|
# "total_maintenance_margin_rate": "16499.135678363399",
|
|
# "total_available_margin": "3381470.892007440383",
|
|
# "unified_account_total": "3381470.892007440383",
|
|
# "unified_account_total_liab": "0",
|
|
# "unified_account_total_equity": "100016.1",
|
|
# "leverage": "2"
|
|
# }
|
|
#
|
|
result: dict = {
|
|
'info': response,
|
|
}
|
|
isolated = marginMode == 'margin' and type == 'spot'
|
|
data = response
|
|
if 'balances' in data: # True for cross_margin and unified
|
|
flatBalances = []
|
|
balances = self.safe_value(data, 'balances', [])
|
|
# inject currency and create an artificial balance object
|
|
# so it can follow the existent flow
|
|
keys = list(balances.keys())
|
|
for i in range(0, len(keys)):
|
|
currencyId = keys[i]
|
|
content = balances[currencyId]
|
|
content['currency'] = currencyId
|
|
flatBalances.append(content)
|
|
data = flatBalances
|
|
for i in range(0, len(data)):
|
|
entry = data[i]
|
|
if isolated:
|
|
marketId = self.safe_string(entry, 'currency_pair')
|
|
symbolInner = self.safe_symbol(marketId, None, '_', 'margin')
|
|
base = self.safe_value(entry, 'base', {})
|
|
quote = self.safe_value(entry, 'quote', {})
|
|
baseCode = self.safe_currency_code(self.safe_string(base, 'currency'))
|
|
quoteCode = self.safe_currency_code(self.safe_string(quote, 'currency'))
|
|
subResult: dict = {}
|
|
subResult[baseCode] = self.parse_balance_helper(base)
|
|
subResult[quoteCode] = self.parse_balance_helper(quote)
|
|
result[symbolInner] = self.safe_balance(subResult)
|
|
else:
|
|
code = self.safe_currency_code(self.safe_string(entry, 'currency'))
|
|
result[code] = self.parse_balance_helper(entry)
|
|
returnResult = result if isolated else self.safe_balance(result)
|
|
return returnResult
|
|
|
|
def fetch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
|
"""
|
|
fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#market-candlesticks # spot
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-futures-candlesticks # swap
|
|
https://www.gate.com/docs/developers/apiv4/en/#market-candlesticks # future
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-options-candlesticks # option
|
|
|
|
: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, limit is conflicted with since and params["until"], If either since and params["until"] is specified, request will be rejected
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.price]: "mark" or "index" for mark price and index price candles
|
|
:param int [params.until]: timestamp in ms of the latest candle to fetch
|
|
:param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume(units in quote currency)
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate')
|
|
if paginate:
|
|
return self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 1000)
|
|
if market['option']:
|
|
return self.fetch_option_ohlcv(symbol, timeframe, since, limit, params)
|
|
price = self.safe_string(params, 'price')
|
|
request: dict = {}
|
|
request, params = self.prepare_request(market, None, params)
|
|
request['interval'] = self.safe_string(self.timeframes, timeframe, timeframe)
|
|
maxLimit = 1999 if market['contract'] else 1000
|
|
limit = maxLimit if (limit is None) else min(limit, maxLimit)
|
|
until = self.safe_integer(params, 'until')
|
|
if until is not None:
|
|
until = self.parse_to_int(until / 1000)
|
|
params = self.omit(params, 'until')
|
|
if since is not None:
|
|
duration = self.parse_timeframe(timeframe)
|
|
request['from'] = self.parse_to_int(since / 1000)
|
|
distance = (limit - 1) * duration
|
|
toTimestamp = self.sum(request['from'], distance)
|
|
currentTimestamp = self.seconds()
|
|
to = min(toTimestamp, currentTimestamp)
|
|
if until is not None:
|
|
request['to'] = min(to, until)
|
|
else:
|
|
request['to'] = to
|
|
else:
|
|
if until is not None:
|
|
request['to'] = until
|
|
request['limit'] = limit
|
|
response = None
|
|
if market['contract']:
|
|
isMark = (price == 'mark')
|
|
isIndex = (price == 'index')
|
|
if isMark or isIndex:
|
|
request['contract'] = price + '_' + market['id']
|
|
params = self.omit(params, 'price')
|
|
if market['future']:
|
|
response = self.publicDeliveryGetSettleCandlesticks(self.extend(request, params))
|
|
elif market['swap']:
|
|
response = self.publicFuturesGetSettleCandlesticks(self.extend(request, params))
|
|
else:
|
|
response = self.publicSpotGetCandlesticks(self.extend(request, params))
|
|
return self.parse_ohlcvs(response, market, timeframe, since, limit)
|
|
|
|
def fetch_option_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
|
# separated option logic because the from, to and limit parameters weren't functioning
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {}
|
|
request, params = self.prepare_request(market, None, params)
|
|
request['interval'] = self.safe_string(self.timeframes, timeframe, timeframe)
|
|
response = self.publicOptionsGetCandlesticks(self.extend(request, params))
|
|
return self.parse_ohlcvs(response, market, timeframe, since, limit)
|
|
|
|
def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
fetches historical funding rate prices
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#funding-rate-history
|
|
|
|
:param str symbol: unified symbol of the market to fetch the funding rate history for
|
|
:param int [since]: timestamp in ms of the earliest funding rate to fetch
|
|
:param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>` to fetch
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param int [params.until]: timestamp in ms of the latest funding rate to fetch
|
|
:param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
:returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>`
|
|
"""
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
|
|
self.load_markets()
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate')
|
|
if paginate:
|
|
return self.fetch_paginated_call_deterministic('fetchFundingRateHistory', symbol, since, limit, '8h', params)
|
|
market = self.market(symbol)
|
|
if not market['swap']:
|
|
raise BadSymbol(self.id + ' fetchFundingRateHistory() supports swap contracts only')
|
|
request: dict = {}
|
|
request, params = self.prepare_request(market, None, params)
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
if since is not None:
|
|
request['from'] = self.parse_to_int(since / 1000)
|
|
until = self.safe_integer(params, 'until')
|
|
if until is not None:
|
|
params = self.omit(params, 'until')
|
|
request['to'] = self.parse_to_int(until / 1000)
|
|
response = self.publicFuturesGetSettleFundingRate(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "r": "0.00063521",
|
|
# "t": "1621267200000",
|
|
# }
|
|
#
|
|
rates = []
|
|
for i in range(0, len(response)):
|
|
entry = response[i]
|
|
timestamp = self.safe_timestamp(entry, 't')
|
|
rates.append({
|
|
'info': entry,
|
|
'symbol': symbol,
|
|
'fundingRate': self.safe_number(entry, 'r'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
})
|
|
sorted = self.sort_by(rates, 'timestamp')
|
|
return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit)
|
|
|
|
def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
|
|
#
|
|
# Spot market candles
|
|
#
|
|
# [
|
|
# "1660957920", # timestamp
|
|
# "6227.070147198573", # quote volume
|
|
# "0.0000133485", # close
|
|
# "0.0000133615", # high
|
|
# "0.0000133347", # low
|
|
# "0.0000133468", # open
|
|
# "466641934.99" # base volume
|
|
# ]
|
|
#
|
|
#
|
|
# Swap, Future, Option, Mark and Index price candles
|
|
#
|
|
# {
|
|
# "t":1632873600, # Unix timestamp in seconds
|
|
# "o": "41025", # Open price
|
|
# "h": "41882.17", # Highest price
|
|
# "c": "41776.92", # Close price
|
|
# "l": "40783.94" # Lowest price
|
|
# }
|
|
#
|
|
if isinstance(ohlcv, list):
|
|
return [
|
|
self.safe_timestamp(ohlcv, 0), # unix timestamp in seconds
|
|
self.safe_number(ohlcv, 5), # open price
|
|
self.safe_number(ohlcv, 3), # highest price
|
|
self.safe_number(ohlcv, 4), # lowest price
|
|
self.safe_number(ohlcv, 2), # close price
|
|
self.safe_number(ohlcv, 6), # trading volume
|
|
]
|
|
else:
|
|
# Swap, Future, Option, Mark and Index price candles
|
|
return [
|
|
self.safe_timestamp(ohlcv, 't'), # unix timestamp in seconds
|
|
self.safe_number(ohlcv, 'o'), # open price
|
|
self.safe_number(ohlcv, 'h'), # highest price
|
|
self.safe_number(ohlcv, 'l'), # lowest price
|
|
self.safe_number(ohlcv, 'c'), # close price
|
|
self.safe_number(ohlcv, 'v'), # trading volume, None for mark or index price
|
|
]
|
|
|
|
def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
|
"""
|
|
get the list of most recent trades for a particular symbol
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#retrieve-market-trades
|
|
https://www.gate.com/docs/developers/apiv4/en/#futures-trading-history
|
|
https://www.gate.com/docs/developers/apiv4/en/#futures-trading-history-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#options-trade-history
|
|
|
|
:param str symbol: unified symbol of the market to fetch trades for
|
|
: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 exchange API endpoint
|
|
:param int [params.until]: timestamp in ms of the latest trade to fetch
|
|
:param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
|
"""
|
|
self.load_markets()
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchTrades', 'paginate')
|
|
if paginate:
|
|
return self.fetch_paginated_call_dynamic('fetchTrades', symbol, since, limit, params)
|
|
market = self.market(symbol)
|
|
#
|
|
# spot
|
|
#
|
|
# request: Dict = {
|
|
# 'currency_pair': market['id'],
|
|
# 'limit': limit, # maximum number of records to be returned in a single list
|
|
# 'last_id': 'id', # specify list staring point using the id of last record in previous list-query results
|
|
# 'reverse': False, # True to retrieve records where id is smaller than the specified last_id, False to retrieve records where id is larger than the specified last_id
|
|
# }
|
|
#
|
|
# swap, future
|
|
#
|
|
# request: Dict = {
|
|
# 'settle': market['settleId'],
|
|
# 'contract': market['id'],
|
|
# 'limit': limit, # maximum number of records to be returned in a single list
|
|
# 'last_id': 'id', # specify list staring point using the id of last record in previous list-query results
|
|
# 'from': since / 1000), # starting time in seconds, if not specified, to and limit will be used to limit response items
|
|
# 'to': self.seconds(), # end time in seconds, default to current time
|
|
# }
|
|
#
|
|
request, query = self.prepare_request(market, None, params)
|
|
until = self.safe_integer_2(params, 'to', 'until')
|
|
if until is not None:
|
|
params = self.omit(params, ['until'])
|
|
request['to'] = self.parse_to_int(until / 1000)
|
|
if limit is not None:
|
|
request['limit'] = min(limit, 1000) # default 100, max 1000
|
|
if since is not None and (market['contract']):
|
|
request['from'] = self.parse_to_int(since / 1000)
|
|
response = None
|
|
if market['type'] == 'spot' or market['type'] == 'margin':
|
|
response = self.publicSpotGetTrades(self.extend(request, query))
|
|
elif market['swap']:
|
|
response = self.publicFuturesGetSettleTrades(self.extend(request, query))
|
|
elif market['future']:
|
|
response = self.publicDeliveryGetSettleTrades(self.extend(request, query))
|
|
elif market['type'] == 'option':
|
|
response = self.publicOptionsGetTrades(self.extend(request, query))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchTrades() not support self market type.')
|
|
#
|
|
# spot
|
|
#
|
|
# [
|
|
# {
|
|
# "id": "1852958144",
|
|
# "create_time": "1634673259",
|
|
# "create_time_ms": "1634673259378.105000",
|
|
# "currency_pair": "ADA_USDT",
|
|
# "side": "sell",
|
|
# "amount": "307.078",
|
|
# "price": "2.104",
|
|
# }
|
|
# ]
|
|
#
|
|
# perpetual swap
|
|
#
|
|
# [
|
|
# {
|
|
# "size": "2",
|
|
# "id": "2522911",
|
|
# "create_time_ms": "1634673380.182",
|
|
# "create_time": "1634673380.182",
|
|
# "contract": "ADA_USDT",
|
|
# "price": "2.10486",
|
|
# }
|
|
# ]
|
|
#
|
|
# option
|
|
#
|
|
# [
|
|
# {
|
|
# "size": -5,
|
|
# "id": 25,
|
|
# "create_time": 1682378573,
|
|
# "contract": "ETH_USDT-20230526-2000-P",
|
|
# "price": "209.1"
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_trades(response, market, since, limit)
|
|
|
|
def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
fetch all the trades made from a single order
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-personal-trading-history
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-personal-trading-history-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-personal-trading-history-3
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-personal-trading-history-4
|
|
|
|
: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(self.id + ' fetchOrderTrades() requires a symbol argument')
|
|
self.load_markets()
|
|
#
|
|
# [
|
|
# {
|
|
# "id":"3711449544",
|
|
# "create_time":"1655486040",
|
|
# "create_time_ms":"1655486040177.599900",
|
|
# "currency_pair":"SHIB_USDT",
|
|
# "side":"buy",
|
|
# "role":"taker",
|
|
# "amount":"1360039",
|
|
# "price":"0.0000081084",
|
|
# "order_id":"169717399644",
|
|
# "fee":"2720.078",
|
|
# "fee_currency":"SHIB",
|
|
# "point_fee":"0",
|
|
# "gt_fee":"0"
|
|
# }
|
|
# ]
|
|
#
|
|
response = self.fetch_my_trades(symbol, since, limit, {'order_id': id})
|
|
return response
|
|
|
|
def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
Fetch personal trading history
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-personal-trading-history
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-personal-trading-history-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-personal-trading-history-3
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-personal-trading-history-4
|
|
|
|
: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 structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
|
|
:param str [params.type]: 'spot', 'swap', or 'future', if not provided self.options['defaultMarginMode'] is used
|
|
:param int [params.until]: The latest timestamp, in ms, that fetched trades were made
|
|
:param int [params.page]: *spot only* Page number
|
|
:param str [params.order_id]: *spot only* Filter trades with specified order ID. symbol is also required if self field is present
|
|
:param str [params.order]: *contract only* Futures order ID, return related data only if specified
|
|
:param int [params.offset]: *contract only* list offset, starting from 0
|
|
:param str [params.last_id]: *contract only* specify list staring point using the id of last record in previous list-query results
|
|
:param int [params.count_total]: *contract only* whether to return total number matched, default to 0(no return)
|
|
:param bool [params.unifiedAccount]: set to True for fetching trades in a unified account
|
|
:param bool [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchMyTrades', 'paginate')
|
|
if paginate:
|
|
return self.fetch_paginated_call_dynamic('fetchMyTrades', symbol, since, limit, params)
|
|
type = None
|
|
marginMode = None
|
|
request: dict = {}
|
|
market = self.market(symbol) if (symbol is not None) else None
|
|
until = self.safe_integer(params, 'until')
|
|
params = self.omit(params, ['until'])
|
|
type, params = self.handle_market_type_and_params('fetchMyTrades', market, params)
|
|
contract = (type == 'swap') or (type == 'future') or (type == 'option')
|
|
if contract:
|
|
request, params = self.prepare_request(market, type, params)
|
|
if type == 'option':
|
|
params = self.omit(params, 'order_id')
|
|
else:
|
|
if market is not None:
|
|
request['currency_pair'] = market['id'] # Should always be set for non-trigger
|
|
marginMode, params = self.get_margin_mode(False, params)
|
|
request['account'] = marginMode
|
|
if limit is not None:
|
|
request['limit'] = limit # default 100, max 1000
|
|
if since is not None:
|
|
request['from'] = self.parse_to_int(since / 1000)
|
|
if until is not None:
|
|
request['to'] = self.parse_to_int(until / 1000)
|
|
response = None
|
|
if type == 'spot' or type == 'margin':
|
|
response = self.privateSpotGetMyTrades(self.extend(request, params))
|
|
elif type == 'swap':
|
|
response = self.privateFuturesGetSettleMyTradesTimerange(self.extend(request, params))
|
|
elif type == 'future':
|
|
response = self.privateDeliveryGetSettleMyTrades(self.extend(request, params))
|
|
elif type == 'option':
|
|
response = self.privateOptionsGetMyTrades(self.extend(request, params))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchMyTrades() not support self market type.')
|
|
#
|
|
# spot
|
|
#
|
|
# [
|
|
# {
|
|
# "id": "2876130500",
|
|
# "create_time": "1645464610",
|
|
# "create_time_ms": "1645464610777.399200",
|
|
# "currency_pair": "DOGE_USDT",
|
|
# "side": "sell",
|
|
# "role": "taker",
|
|
# "amount": "10.97",
|
|
# "price": "0.137384",
|
|
# "order_id": "125924049993",
|
|
# "fee": "0.00301420496",
|
|
# "fee_currency": "USDT",
|
|
# "point_fee": "0",
|
|
# "gt_fee": "0"
|
|
# }
|
|
# ]
|
|
#
|
|
# perpetual swap
|
|
#
|
|
# [
|
|
# {
|
|
# "size": -5,
|
|
# "order_id": "130264979823",
|
|
# "id": 26884791,
|
|
# "role": "taker",
|
|
# "create_time": 1645465199.5472,
|
|
# "contract": "DOGE_USDT",
|
|
# "price": "0.136888"
|
|
# }
|
|
# ]
|
|
#
|
|
# future
|
|
#
|
|
# [
|
|
# {
|
|
# "id": 121234231,
|
|
# "create_time": 1514764800.123,
|
|
# "contract": "BTC_USDT",
|
|
# "order_id": "21893289839",
|
|
# "size": 100,
|
|
# "price": "100.123",
|
|
# "role": "taker"
|
|
# }
|
|
# ]
|
|
#
|
|
# option
|
|
#
|
|
# [
|
|
# {
|
|
# "underlying_price": "26817.84",
|
|
# "size": -1,
|
|
# "contract": "BTC_USDT-20230602-26500-C",
|
|
# "id": 16,
|
|
# "role": "taker",
|
|
# "create_time": 1685594770,
|
|
# "order_id": 2611026125,
|
|
# "price": "333"
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_trades(response, market, since, limit)
|
|
|
|
def parse_trade(self, trade: dict, market: Market = None) -> Trade:
|
|
#
|
|
# public
|
|
#
|
|
# spot:
|
|
# {
|
|
# "id": "1334253759",
|
|
# "create_time": "1626342738",
|
|
# "create_time_ms": "1626342738331.497000",
|
|
# "currency_pair": "BTC_USDT",
|
|
# "side": "sell",
|
|
# "amount": "0.0022",
|
|
# "price": "32452.16"
|
|
# }
|
|
#
|
|
# swap:
|
|
#
|
|
# {
|
|
# "id": "442288327",
|
|
# "contract": "BTC_USDT",
|
|
# "create_time": "1739814676.707",
|
|
# "create_time_ms": "1739814676.707",
|
|
# "size": "-105",
|
|
# "price": "95594.8"
|
|
# }
|
|
#
|
|
#
|
|
# public ws
|
|
#
|
|
# {
|
|
# "id": 221994511,
|
|
# "time": 1580311438.618647,
|
|
# "price": "9309",
|
|
# "amount": "0.0019",
|
|
# "type": "sell"
|
|
# }
|
|
#
|
|
# spot rest
|
|
#
|
|
# {
|
|
# "id": "2876130500",
|
|
# "create_time": "1645464610",
|
|
# "create_time_ms": "1645464610777.399200",
|
|
# "currency_pair": "DOGE_USDT",
|
|
# "side": "sell",
|
|
# "role": "taker",
|
|
# "amount": "10.97",
|
|
# "price": "0.137384",
|
|
# "order_id": "125924049993",
|
|
# "fee": "0.00301420496",
|
|
# "fee_currency": "USDT",
|
|
# "point_fee": "1.1",
|
|
# "gt_fee":"2.2"
|
|
# }
|
|
#
|
|
# perpetual swap rest
|
|
#
|
|
# {
|
|
# "size": -5,
|
|
# "order_id": "130264979823",
|
|
# "id": 26884791,
|
|
# "role": "taker",
|
|
# "create_time": 1645465199.5472,
|
|
# "contract": "DOGE_USDT",
|
|
# "price": "0.136888"
|
|
# }
|
|
#
|
|
# future rest
|
|
#
|
|
# {
|
|
# "id": 121234231,
|
|
# "create_time": 1514764800.123,
|
|
# "contract": "BTC_USDT",
|
|
# "order_id": "21893289839",
|
|
# "size": 100,
|
|
# "price": "100.123",
|
|
# "role": "taker"
|
|
# }
|
|
#
|
|
# fetchTrades: option
|
|
#
|
|
# {
|
|
# "size": -5,
|
|
# "id": 25,
|
|
# "create_time": 1682378573,
|
|
# "contract": "ETH_USDT-20230526-2000-P",
|
|
# "price": "209.1"
|
|
# }
|
|
#
|
|
# fetchMyTrades: option
|
|
#
|
|
# {
|
|
# "underlying_price": "26817.84",
|
|
# "size": -1,
|
|
# "contract": "BTC_USDT-20230602-26500-C",
|
|
# "id": 16,
|
|
# "role": "taker",
|
|
# "create_time": 1685594770,
|
|
# "order_id": 2611026125,
|
|
# "price": "333"
|
|
# }
|
|
#
|
|
id = self.safe_string_2(trade, 'id', 'trade_id')
|
|
timestamp: Int = None
|
|
msString = self.safe_string(trade, 'create_time_ms')
|
|
if msString is not None:
|
|
msString = Precise.string_mul(msString, '1000')
|
|
msString = msString[0:13]
|
|
timestamp = self.parse_to_int(msString)
|
|
else:
|
|
timestamp = self.safe_timestamp_2(trade, 'time', 'create_time')
|
|
marketId = self.safe_string_2(trade, 'currency_pair', 'contract')
|
|
marketType = 'contract' if ('contract' in trade) else 'spot'
|
|
market = self.safe_market(marketId, market, '_', marketType)
|
|
amountString = self.safe_string_2(trade, 'amount', 'size')
|
|
priceString = self.safe_string(trade, 'price')
|
|
contractSide = 'sell' if Precise.string_lt(amountString, '0') else 'buy'
|
|
amountString = Precise.string_abs(amountString)
|
|
side = self.safe_string_2(trade, 'side', 'type', contractSide)
|
|
orderId = self.safe_string(trade, 'order_id')
|
|
feeAmount = self.safe_string(trade, 'fee')
|
|
gtFee = self.omit_zero(self.safe_string(trade, 'gt_fee'))
|
|
pointFee = self.omit_zero(self.safe_string(trade, 'point_fee'))
|
|
fees = []
|
|
if feeAmount is not None:
|
|
feeCurrencyId = self.safe_string(trade, 'fee_currency')
|
|
feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
|
|
if feeCurrencyCode is None:
|
|
feeCurrencyCode = self.safe_string(market, 'settle')
|
|
fees.append({
|
|
'cost': feeAmount,
|
|
'currency': feeCurrencyCode,
|
|
})
|
|
if gtFee is not None:
|
|
fees.append({
|
|
'cost': gtFee,
|
|
'currency': 'GT',
|
|
})
|
|
if pointFee is not None:
|
|
fees.append({
|
|
'cost': pointFee,
|
|
'currency': 'GatePoint',
|
|
})
|
|
takerOrMaker = self.safe_string(trade, 'role')
|
|
return self.safe_trade({
|
|
'info': trade,
|
|
'id': id,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'symbol': market['symbol'],
|
|
'order': orderId,
|
|
'type': None,
|
|
'side': side,
|
|
'takerOrMaker': takerOrMaker,
|
|
'price': priceString,
|
|
'amount': amountString,
|
|
'cost': None,
|
|
'fee': None,
|
|
'fees': fees,
|
|
}, market)
|
|
|
|
def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
|
"""
|
|
fetch all deposits made to an account
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#retrieve-deposit-records
|
|
|
|
:param str code: unified currency code
|
|
:param int [since]: the earliest time in ms to fetch deposits for
|
|
:param int [limit]: the maximum number of deposits structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param int [params.until]: end time in ms
|
|
:param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
"""
|
|
self.load_markets()
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchDeposits', 'paginate')
|
|
if paginate:
|
|
return self.fetch_paginated_call_dynamic('fetchDeposits', code, since, limit, params)
|
|
request: dict = {}
|
|
currency = None
|
|
if code is not None:
|
|
currency = self.currency(code)
|
|
request['currency'] = currency['id'] # todo: currencies have network-junctions
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
if since is not None:
|
|
start = self.parse_to_int(since / 1000)
|
|
request['from'] = start
|
|
request['to'] = self.sum(start, 30 * 24 * 60 * 60)
|
|
request, params = self.handle_until_option('to', request, params, 0.001)
|
|
response = self.privateWalletGetDeposits(self.extend(request, params))
|
|
return self.parse_transactions(response, currency)
|
|
|
|
def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
|
"""
|
|
fetch all withdrawals made from an account
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#retrieve-withdrawal-records
|
|
|
|
:param str code: unified currency code
|
|
:param int [since]: the earliest time in ms to fetch withdrawals for
|
|
:param int [limit]: the maximum number of withdrawals structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param int [params.until]: end time in ms
|
|
:param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
"""
|
|
self.load_markets()
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchWithdrawals', 'paginate')
|
|
if paginate:
|
|
return self.fetch_paginated_call_dynamic('fetchWithdrawals', code, since, limit, params)
|
|
request: dict = {}
|
|
currency = None
|
|
if code is not None:
|
|
currency = self.currency(code)
|
|
request['currency'] = currency['id'] # todo: currencies have network-junctions
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
if since is not None:
|
|
start = self.parse_to_int(since / 1000)
|
|
request['from'] = start
|
|
request['to'] = self.sum(start, 30 * 24 * 60 * 60)
|
|
request, params = self.handle_until_option('to', request, params, 0.001)
|
|
response = self.privateWalletGetWithdrawals(self.extend(request, params))
|
|
return self.parse_transactions(response, currency)
|
|
|
|
def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction:
|
|
"""
|
|
make a withdrawal
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#withdraw
|
|
|
|
:param str code: unified currency code
|
|
:param float amount: the amount to withdraw
|
|
:param str address: the address to withdraw to
|
|
:param str tag:
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
"""
|
|
tag, params = self.handle_withdraw_tag_and_params(tag, params)
|
|
self.check_address(address)
|
|
self.load_markets()
|
|
currency = self.currency(code)
|
|
request: dict = {
|
|
'currency': currency['id'], # todo: currencies have network-junctions
|
|
'address': address,
|
|
'amount': self.currency_to_precision(code, amount),
|
|
}
|
|
if tag is not None:
|
|
request['memo'] = tag
|
|
networkCode = None
|
|
networkCode, params = self.handle_network_code_and_params(params)
|
|
if networkCode is not None:
|
|
request['chain'] = self.network_code_to_id(networkCode)
|
|
response = self.privateWithdrawalsPostWithdrawals(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "id": "w13389675",
|
|
# "currency": "USDT",
|
|
# "amount": "50",
|
|
# "address": "TUu2rLFrmzUodiWfYki7QCNtv1akL682p1",
|
|
# "memo": null
|
|
# }
|
|
#
|
|
return self.parse_transaction(response, currency)
|
|
|
|
def parse_transaction_status(self, status: Str):
|
|
statuses: dict = {
|
|
'PEND': 'pending',
|
|
'REQUEST': 'pending',
|
|
'DMOVE': 'pending',
|
|
'MANUAL': 'pending',
|
|
'VERIFY': 'pending',
|
|
'PROCES': 'pending',
|
|
'EXTPEND': 'pending',
|
|
'SPLITPEND': 'pending',
|
|
'CANCEL': 'canceled',
|
|
'FAIL': 'failed',
|
|
'INVALID': 'failed',
|
|
'DONE': 'ok',
|
|
'BCODE': 'ok', # GateCode withdrawal
|
|
}
|
|
return self.safe_string(statuses, status, status)
|
|
|
|
def parse_transaction_type(self, type):
|
|
types: dict = {
|
|
'd': 'deposit',
|
|
'w': 'withdrawal',
|
|
}
|
|
return self.safe_string(types, type, type)
|
|
|
|
def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
|
|
#
|
|
# fetchDeposits
|
|
#
|
|
# {
|
|
# "id": "d33361395",
|
|
# "currency": "USDT_TRX",
|
|
# "address": "TErdnxenuLtXfnMafLbfappYdHtnXQ5U4z",
|
|
# "amount": "100",
|
|
# "txid": "ae9374de34e558562fe18cbb1bf9ab4d9eb8aa7669d65541c9fa2a532c1474a0",
|
|
# "timestamp": "1626345819",
|
|
# "status": "DONE",
|
|
# "memo": ""
|
|
# }
|
|
#
|
|
# withdraw
|
|
#
|
|
# {
|
|
# "id":"w64413318",
|
|
# "currency":"usdt",
|
|
# "amount":"10150",
|
|
# "address":"0x0ab891497116f7f5532a4c2f4f7b1784488628e1",
|
|
# "memo":null,
|
|
# "status":"REQUEST",
|
|
# "chain":"eth",
|
|
# "withdraw_order_id":"",
|
|
# "fee_amount":"4.15000000"
|
|
# }
|
|
#
|
|
# fetchWithdrawals
|
|
#
|
|
# {
|
|
# "id": "210496",
|
|
# "timestamp": "1542000000",
|
|
# "withdraw_order_id": "order_123456",
|
|
# "currency": "USDT",
|
|
# "address": "1HkxtBAMrA3tP5ENnYY2CZortjZvFDH5Cs",
|
|
# "txid": "128988928203223323290",
|
|
# "block_number": "41575382",
|
|
# "amount": "222.61",
|
|
# "fee": "0.01",
|
|
# "memo": "",
|
|
# "status": "DONE",
|
|
# "chain": "TRX"
|
|
# }
|
|
#
|
|
# {
|
|
# "id": "w13389675",
|
|
# "currency": "USDT",
|
|
# "amount": "50",
|
|
# "address": "TUu2rLFrmzUodiWfYki7QCNtv1akL682p1",
|
|
# "memo": null
|
|
# }
|
|
#
|
|
# {
|
|
# "currency":"usdt",
|
|
# "address":"0x01c0A9b7b4CdE774AF0f3E47CB4f1c2CCdBa0806",
|
|
# "amount":"1880",
|
|
# "chain":"eth"
|
|
# }
|
|
#
|
|
id = self.safe_string(transaction, 'id')
|
|
type = None
|
|
amountString = self.safe_string(transaction, 'amount')
|
|
if id is not None:
|
|
if id[0] == 'b':
|
|
# GateCode handling
|
|
type = 'deposit' if Precise.string_gt(amountString, '0') else 'withdrawal'
|
|
amountString = Precise.string_abs(amountString)
|
|
else:
|
|
type = self.parse_transaction_type(id[0])
|
|
feeCostString = self.safe_string_2(transaction, 'fee', 'fee_amount')
|
|
if type == 'withdrawal':
|
|
amountString = Precise.string_sub(amountString, feeCostString)
|
|
networkId = self.safe_string_upper(transaction, 'chain')
|
|
currencyId = self.safe_string(transaction, 'currency')
|
|
code = self.safe_currency_code(currencyId)
|
|
txid = self.safe_string(transaction, 'txid')
|
|
rawStatus = self.safe_string(transaction, 'status')
|
|
status = self.parse_transaction_status(rawStatus)
|
|
address = self.safe_string(transaction, 'address')
|
|
tag = self.safe_string(transaction, 'memo')
|
|
timestamp = self.safe_timestamp(transaction, 'timestamp')
|
|
return {
|
|
'info': transaction,
|
|
'id': id,
|
|
'txid': txid,
|
|
'currency': code,
|
|
'amount': self.parse_number(amountString),
|
|
'network': self.network_id_to_code(networkId),
|
|
'address': address,
|
|
'addressTo': None,
|
|
'addressFrom': None,
|
|
'tag': tag,
|
|
'tagTo': None,
|
|
'tagFrom': None,
|
|
'status': status,
|
|
'type': type,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'updated': None,
|
|
'internal': None,
|
|
'comment': None,
|
|
'fee': {
|
|
'currency': code,
|
|
'cost': self.parse_number(feeCostString),
|
|
},
|
|
}
|
|
|
|
def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
"""
|
|
Create an order on the exchange
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-an-order
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-a-price-triggered-order
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-a-futures-order
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-a-price-triggered-order-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-a-futures-order-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-a-price-triggered-order-3
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-an-options-order
|
|
|
|
:param str symbol: Unified CCXT market symbol
|
|
:param str type: 'limit' or 'market' *"market" is contract only*
|
|
:param str side: 'buy' or 'sell'
|
|
:param float amount: the amount of currency to trade
|
|
:param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param float [params.triggerPrice]: The price at which a trigger order is triggered at
|
|
:param str [params.timeInForce]: "GTC", "IOC", or "PO"
|
|
:param float [params.stopLossPrice]: The price at which a stop loss order is triggered at
|
|
:param float [params.takeProfitPrice]: The price at which a take profit order is triggered at
|
|
:param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
|
|
:param int [params.iceberg]: Amount to display for the iceberg order, Null or 0 for normal orders, Set to -1 to hide the order completely
|
|
:param str [params.text]: User defined information
|
|
:param str [params.account]: *spot and margin only* "spot", "margin" or "cross_margin"
|
|
:param bool [params.auto_borrow]: *margin only* Used in margin or cross margin trading to allow automatic loan of insufficient amount if balance is not enough
|
|
:param str [params.settle]: *contract only* Unified Currency Code for settle currency
|
|
:param bool [params.reduceOnly]: *contract only* Indicates if self order is to reduce the size of a position
|
|
:param bool [params.close]: *contract only* Set to close the position, with size set to 0
|
|
:param bool [params.auto_size]: *contract only* Set side to close dual-mode position, close_long closes the long side, while close_short the short one, size also needs to be set to 0
|
|
:param int [params.price_type]: *contract only* 0 latest deal price, 1 mark price, 2 index price
|
|
:param float [params.cost]: *spot market buy only* the quote quantity that can be used alternative for the amount
|
|
:param bool [params.unifiedAccount]: set to True for creating an order in the unified account
|
|
:returns dict|None: `An order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
market = self.market(symbol)
|
|
trigger = self.safe_value(params, 'trigger')
|
|
triggerPrice = self.safe_value_2(params, 'triggerPrice', 'stopPrice')
|
|
stopLossPrice = self.safe_value(params, 'stopLossPrice', triggerPrice)
|
|
takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
|
|
isStopLossOrder = stopLossPrice is not None
|
|
isTakeProfitOrder = takeProfitPrice is not None
|
|
isTpsl = isStopLossOrder or isTakeProfitOrder
|
|
nonTriggerOrder = not isTpsl and (trigger is None)
|
|
orderRequest = self.create_order_request(symbol, type, side, amount, price, params)
|
|
response = None
|
|
if market['spot'] or market['margin']:
|
|
if nonTriggerOrder:
|
|
response = self.privateSpotPostOrders(orderRequest)
|
|
else:
|
|
response = self.privateSpotPostPriceOrders(orderRequest)
|
|
elif market['swap']:
|
|
if nonTriggerOrder:
|
|
response = self.privateFuturesPostSettleOrders(orderRequest)
|
|
else:
|
|
response = self.privateFuturesPostSettlePriceOrders(orderRequest)
|
|
elif market['future']:
|
|
if nonTriggerOrder:
|
|
response = self.privateDeliveryPostSettleOrders(orderRequest)
|
|
else:
|
|
response = self.privateDeliveryPostSettlePriceOrders(orderRequest)
|
|
else:
|
|
response = self.privateOptionsPostOrders(orderRequest)
|
|
# response = getattr(self, method)(self.deep_extend(request, params))
|
|
#
|
|
# spot
|
|
#
|
|
# {
|
|
# "id": "95282841887",
|
|
# "text": "apiv4",
|
|
# "create_time": "1637383156",
|
|
# "update_time": "1637383156",
|
|
# "create_time_ms": 1637383156017,
|
|
# "update_time_ms": 1637383156017,
|
|
# "status": "open",
|
|
# "currency_pair": "ETH_USDT",
|
|
# "type": "limit",
|
|
# "account": "spot",
|
|
# "side": "buy",
|
|
# "amount": "0.01",
|
|
# "price": "3500",
|
|
# "time_in_force": "gtc",
|
|
# "iceberg": "0",
|
|
# "left": "0.01",
|
|
# "fill_price": "0",
|
|
# "filled_total": "0",
|
|
# "fee": "0",
|
|
# "fee_currency": "ETH",
|
|
# "point_fee": "0",
|
|
# "gt_fee": "0",
|
|
# "gt_discount": False,
|
|
# "rebated_fee": "0",
|
|
# "rebated_fee_currency": "USDT"
|
|
# }
|
|
#
|
|
# spot conditional
|
|
#
|
|
# {"id": 5891843}
|
|
#
|
|
# futures, perpetual swaps and options
|
|
#
|
|
# {
|
|
# "id": 95938572327,
|
|
# "contract": "ETH_USDT",
|
|
# "mkfr": "0",
|
|
# "tkfr": "0.0005",
|
|
# "tif": "gtc",
|
|
# "is_reduce_only": False,
|
|
# "create_time": 1637384600.08,
|
|
# "price": "3000",
|
|
# "size": 1,
|
|
# "refr": "0",
|
|
# "left": 1,
|
|
# "text": "api",
|
|
# "fill_price": "0",
|
|
# "user": 2436035,
|
|
# "status": "open",
|
|
# "is_liq": False,
|
|
# "refu": 0,
|
|
# "is_close": False,
|
|
# "iceberg": 0
|
|
# }
|
|
#
|
|
# futures and perpetual swaps conditionals
|
|
#
|
|
# {"id": 7615567}
|
|
#
|
|
return self.parse_order(response, market)
|
|
|
|
def create_orders_request(self, orders: List[OrderRequest], params={}):
|
|
ordersRequests = []
|
|
orderSymbols = []
|
|
ordersLength = len(orders)
|
|
if ordersLength == 0:
|
|
raise BadRequest(self.id + ' createOrders() requires at least one order')
|
|
if ordersLength > 10:
|
|
raise BadRequest(self.id + ' createOrders() accepts a maximum of 10 orders at a time')
|
|
for i in range(0, len(orders)):
|
|
rawOrder = orders[i]
|
|
marketId = self.safe_string(rawOrder, 'symbol')
|
|
orderSymbols.append(marketId)
|
|
type = self.safe_string(rawOrder, 'type')
|
|
side = self.safe_string(rawOrder, 'side')
|
|
amount = self.safe_value(rawOrder, 'amount')
|
|
price = self.safe_value(rawOrder, 'price')
|
|
orderParams = self.safe_value(rawOrder, 'params', {})
|
|
extendedParams = self.extend(orderParams, params) # the request does not accept extra params since it's a list, so we're extending each order with the common params
|
|
triggerValue = self.safe_value_n(orderParams, ['triggerPrice', 'stopPrice', 'takeProfitPrice', 'stopLossPrice'])
|
|
if triggerValue is not None:
|
|
raise NotSupported(self.id + ' createOrders() does not support advanced order properties(stopPrice, takeProfitPrice, stopLossPrice)')
|
|
extendedParams['textIsRequired'] = True # the exchange requires a text parameter for each order here
|
|
orderRequest = self.create_order_request(marketId, type, side, amount, price, extendedParams)
|
|
ordersRequests.append(orderRequest)
|
|
symbols = self.market_symbols(orderSymbols, None, False, True, True)
|
|
market = self.market(symbols[0])
|
|
if market['future'] or market['option']:
|
|
raise NotSupported(self.id + ' createOrders() does not support futures or options markets')
|
|
return ordersRequests
|
|
|
|
def create_orders(self, orders: List[OrderRequest], params={}):
|
|
"""
|
|
create a list of trade orders
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-a-single-order-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-a-batch-of-orders
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-a-batch-of-futures-orders
|
|
|
|
:param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
ordersRequests = self.create_orders_request(orders, params)
|
|
firstOrder = orders[0]
|
|
market = self.market(firstOrder['symbol'])
|
|
response = None
|
|
if market['spot']:
|
|
response = self.privateSpotPostBatchOrders(ordersRequests)
|
|
elif market['swap']:
|
|
response = self.privateFuturesPostSettleBatchOrders(ordersRequests)
|
|
return self.parse_orders(response)
|
|
|
|
def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
market = self.market(symbol)
|
|
contract = market['contract']
|
|
trigger = self.safe_value(params, 'trigger')
|
|
triggerPrice = self.safe_value_2(params, 'triggerPrice', 'stopPrice')
|
|
stopLossPrice = self.safe_value(params, 'stopLossPrice', triggerPrice)
|
|
takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
|
|
isStopLossOrder = stopLossPrice is not None
|
|
isTakeProfitOrder = takeProfitPrice is not None
|
|
isTpsl = isStopLossOrder or isTakeProfitOrder
|
|
if isStopLossOrder and isTakeProfitOrder:
|
|
raise ExchangeError(self.id + ' createOrder() stopLossPrice and takeProfitPrice cannot both be defined')
|
|
reduceOnly = self.safe_value(params, 'reduceOnly')
|
|
exchangeSpecificTimeInForce = self.safe_string_lower_n(params, ['timeInForce', 'tif', 'time_in_force'])
|
|
postOnly = None
|
|
postOnly, params = self.handle_post_only(type == 'market', exchangeSpecificTimeInForce == 'poc', params)
|
|
timeInForce = self.handle_time_in_force(params)
|
|
if postOnly:
|
|
timeInForce = 'poc'
|
|
# we only omit the unified params here
|
|
# self is because the other params will get extended into the request
|
|
params = self.omit(params, ['stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'reduceOnly', 'timeInForce', 'postOnly'])
|
|
isLimitOrder = (type == 'limit')
|
|
isMarketOrder = (type == 'market')
|
|
if isLimitOrder and price is None:
|
|
raise ArgumentsRequired(self.id + ' createOrder() requires a price argument for ' + type + ' orders')
|
|
if isMarketOrder:
|
|
if (timeInForce == 'poc') or (timeInForce == 'gtc'):
|
|
raise ExchangeError(self.id + ' createOrder() timeInForce for market order can only be "FOK" or "IOC"')
|
|
else:
|
|
if timeInForce is None:
|
|
defaultTif = self.safe_string(self.options, 'defaultTimeInForce', 'IOC')
|
|
exchangeSpecificTif = self.safe_string(self.options['timeInForce'], defaultTif, 'ioc')
|
|
timeInForce = exchangeSpecificTif
|
|
if contract:
|
|
price = 0
|
|
if contract:
|
|
isClose = self.safe_value(params, 'close')
|
|
if isClose:
|
|
amount = 0
|
|
else:
|
|
amountToPrecision = self.amount_to_precision(symbol, amount)
|
|
signedAmount = Precise.string_neg(amountToPrecision) if (side == 'sell') else amountToPrecision
|
|
amount = int(signedAmount)
|
|
request = None
|
|
nonTriggerOrder = not isTpsl and (trigger is None)
|
|
if nonTriggerOrder:
|
|
if contract:
|
|
# contract order
|
|
request = {
|
|
'contract': market['id'], # filled in prepareRequest above
|
|
'size': amount, # int64, positive = bid, negative = ask
|
|
# 'iceberg': 0, # int64, display size for iceberg order, 0 for non-iceberg, note that you will have to pay the taker fee for the hidden size
|
|
# 'close': False, # True to close the position, with size set to 0
|
|
# 'reduce_only': False, # St to be reduce-only order
|
|
# 'tif': 'gtc', # gtc, ioc, poc PendingOrCancelled == postOnly order
|
|
# 'text': clientOrderId, # 't-abcdef1234567890',
|
|
# 'auto_size': '', # close_long, close_short, note size also needs to be set to 0
|
|
}
|
|
if not market['option']:
|
|
request['settle'] = market['settleId'] # filled in prepareRequest above
|
|
if isMarketOrder:
|
|
request['price'] = '0' # set to 0 for market orders
|
|
else:
|
|
request['price'] = '0' if (price == 0) else self.price_to_precision(symbol, price)
|
|
if reduceOnly is not None:
|
|
request['reduce_only'] = reduceOnly
|
|
if timeInForce is not None:
|
|
request['tif'] = timeInForce
|
|
else:
|
|
marginMode = None
|
|
marginMode, params = self.get_margin_mode(False, params)
|
|
# spot order
|
|
request = {
|
|
# 'text': clientOrderId, # 't-abcdef1234567890',
|
|
'currency_pair': market['id'], # filled in prepareRequest above
|
|
'type': type,
|
|
'account': marginMode, # spot, margin, cross_margin, unified
|
|
'side': side,
|
|
# 'time_in_force': 'gtc', # gtc, ioc, poc PendingOrCancelled == postOnly order
|
|
# 'iceberg': 0, # amount to display for the iceberg order, null or 0 for normal orders, set to -1 to hide the order completely
|
|
# 'auto_borrow': False, # used in margin or cross margin trading to allow automatic loan of insufficient amount if balance is not enough
|
|
# 'auto_repay': False, # automatic repayment for automatic borrow loan generated by cross margin order, diabled by default
|
|
}
|
|
if isMarketOrder and (side == 'buy'):
|
|
quoteAmount = None
|
|
createMarketBuyOrderRequiresPrice = True
|
|
createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
|
|
cost = self.safe_number(params, 'cost')
|
|
params = self.omit(params, 'cost')
|
|
if cost is not None:
|
|
quoteAmount = self.cost_to_precision(symbol, cost)
|
|
elif createMarketBuyOrderRequiresPrice:
|
|
if price is None:
|
|
raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend(quote quantity) in the amount argument')
|
|
else:
|
|
amountString = self.number_to_string(amount)
|
|
priceString = self.number_to_string(price)
|
|
costRequest = Precise.string_mul(amountString, priceString)
|
|
quoteAmount = self.cost_to_precision(symbol, costRequest)
|
|
else:
|
|
quoteAmount = self.cost_to_precision(symbol, amount)
|
|
request['amount'] = quoteAmount
|
|
else:
|
|
request['amount'] = self.amount_to_precision(symbol, amount)
|
|
if isLimitOrder:
|
|
request['price'] = self.price_to_precision(symbol, price)
|
|
if timeInForce is not None:
|
|
request['time_in_force'] = timeInForce
|
|
clientOrderId = self.safe_string_2(params, 'text', 'clientOrderId')
|
|
textIsRequired = self.safe_bool(params, 'textIsRequired', False)
|
|
if clientOrderId is not None:
|
|
# user-defined, must follow the rules if not empty
|
|
# prefixed with t-
|
|
# no longer than 28 bytes without t- prefix
|
|
# can only include 0-9, A-Z, a-z, underscores(_), hyphens(-) or dots(.)
|
|
if len(clientOrderId) > 28:
|
|
raise BadRequest(self.id + ' createOrder() clientOrderId or text param must be up to 28 characters')
|
|
params = self.omit(params, ['text', 'clientOrderId', 'textIsRequired'])
|
|
if clientOrderId[0] != 't':
|
|
clientOrderId = 't-' + clientOrderId
|
|
request['text'] = clientOrderId
|
|
else:
|
|
if textIsRequired:
|
|
# batchOrders requires text in the request
|
|
request['text'] = 't-' + self.uuid16()
|
|
else:
|
|
if market['option']:
|
|
raise NotSupported(self.id + ' createOrder() conditional option orders are not supported')
|
|
if contract:
|
|
# contract conditional order
|
|
request = {
|
|
'initial': {
|
|
'contract': market['id'],
|
|
'size': amount, # positive = buy, negative = sell, set to 0 to close the position
|
|
# 'price': '0' if (price == 0) else self.price_to_precision(symbol, price), # set to 0 to use market price
|
|
# 'close': False, # set to True if trying to close the position
|
|
# 'tif': 'gtc', # gtc, ioc, if using market price, only ioc is supported
|
|
# 'text': clientOrderId, # web, api, app
|
|
# 'reduce_only': False,
|
|
},
|
|
'settle': market['settleId'],
|
|
}
|
|
if type == 'market':
|
|
request['initial']['price'] = '0'
|
|
else:
|
|
request['initial']['price'] = '0' if (price == 0) else self.price_to_precision(symbol, price)
|
|
if trigger is None:
|
|
rule = None
|
|
triggerOrderPrice = None
|
|
if isStopLossOrder:
|
|
# we trigger orders be aliases for stopLoss orders because
|
|
# gateio doesn't accept conventional trigger orders for spot markets
|
|
rule = 1 if (side == 'buy') else 2
|
|
triggerOrderPrice = self.price_to_precision(symbol, stopLossPrice)
|
|
elif isTakeProfitOrder:
|
|
rule = 2 if (side == 'buy') else 1
|
|
triggerOrderPrice = self.price_to_precision(symbol, takeProfitPrice)
|
|
priceType = self.safe_integer(params, 'price_type', 0)
|
|
if priceType < 0 or priceType > 2:
|
|
raise BadRequest(self.id + ' createOrder() price_type should be 0 latest deal price, 1 mark price, 2 index price')
|
|
params = self.omit(params, ['price_type'])
|
|
request['trigger'] = {
|
|
# 'strategy_type': 0, # 0 = by price, 1 = by price gap, only 0 is supported currently
|
|
'price_type': priceType, # 0 latest deal price, 1 mark price, 2 index price
|
|
'price': self.price_to_precision(symbol, triggerOrderPrice), # price or gap
|
|
'rule': rule, # 1 means price_type >= price, 2 means price_type <= price
|
|
# 'expiration': expiration, how many seconds to wait for the condition to be triggered before cancelling the order
|
|
}
|
|
if reduceOnly is not None:
|
|
request['initial']['reduce_only'] = reduceOnly
|
|
if timeInForce is not None:
|
|
request['initial']['tif'] = timeInForce
|
|
else:
|
|
# spot conditional order
|
|
options = self.safe_value(self.options, 'createOrder', {})
|
|
marginMode = None
|
|
marginMode, params = self.get_margin_mode(True, params)
|
|
if timeInForce is None:
|
|
timeInForce = 'gtc'
|
|
request = {
|
|
'put': {
|
|
'type': type,
|
|
'side': side,
|
|
'price': self.price_to_precision(symbol, price),
|
|
'amount': self.amount_to_precision(symbol, amount),
|
|
'account': marginMode,
|
|
'time_in_force': timeInForce, # gtc, ioc(ioc is for taker only, so shouldnt't be in conditional order)
|
|
},
|
|
'market': market['id'],
|
|
}
|
|
if trigger is None:
|
|
defaultExpiration = self.safe_integer(options, 'expiration')
|
|
expiration = self.safe_integer(params, 'expiration', defaultExpiration)
|
|
rule = None
|
|
triggerOrderPrice = None
|
|
if isStopLossOrder:
|
|
# we trigger orders be aliases for stopLoss orders because
|
|
# gateio doesn't accept conventional trigger orders for spot markets
|
|
rule = '>=' if (side == 'buy') else '<='
|
|
triggerOrderPrice = self.price_to_precision(symbol, stopLossPrice)
|
|
elif isTakeProfitOrder:
|
|
rule = '<=' if (side == 'buy') else '>='
|
|
triggerOrderPrice = self.price_to_precision(symbol, takeProfitPrice)
|
|
request['trigger'] = {
|
|
'price': self.price_to_precision(symbol, triggerOrderPrice),
|
|
'rule': rule, # >= triggered when market price larger than or equal to price field, <= triggered when market price less than or equal to price field
|
|
'expiration': expiration, # required, how long(in seconds) to wait for the condition to be triggered before cancelling the order
|
|
}
|
|
return self.extend(request, params)
|
|
|
|
def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}):
|
|
"""
|
|
create a market buy order by providing the symbol and cost
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-an-order
|
|
|
|
:param str symbol: unified symbol of the market to create an order in
|
|
:param float cost: how much you want to trade in units of the quote currency
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param bool [params.unifiedAccount]: set to True for creating a unified account order
|
|
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
market = self.market(symbol)
|
|
if not market['spot']:
|
|
raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only')
|
|
params['createMarketBuyOrderRequiresPrice'] = False
|
|
return self.create_order(symbol, 'market', 'buy', cost, None, params)
|
|
|
|
def edit_order_request(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
|
|
market = self.market(symbol)
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('editOrder', market, params)
|
|
account = self.convert_type_to_account(marketType)
|
|
isUnifiedAccount = False
|
|
isUnifiedAccount, params = self.handle_option_and_params(params, 'editOrder', 'unifiedAccount')
|
|
if isUnifiedAccount:
|
|
account = 'unified'
|
|
isLimitOrder = (type == 'limit')
|
|
if account == 'spot':
|
|
if not isLimitOrder:
|
|
# exchange doesn't have market orders for spot
|
|
raise InvalidOrder(self.id + ' editOrder() does not support ' + type + ' orders for ' + marketType + ' markets')
|
|
request: dict = {
|
|
'order_id': str(id),
|
|
'currency_pair': market['id'],
|
|
'account': account,
|
|
}
|
|
if amount is not None:
|
|
if market['spot']:
|
|
request['amount'] = self.amount_to_precision(symbol, amount)
|
|
else:
|
|
if side == 'sell':
|
|
request['size'] = self.parse_to_numeric(Precise.string_neg(self.amount_to_precision(symbol, amount)))
|
|
else:
|
|
request['size'] = self.parse_to_numeric(self.amount_to_precision(symbol, amount))
|
|
if price is not None:
|
|
request['price'] = self.price_to_precision(symbol, price)
|
|
if not market['spot']:
|
|
request['settle'] = market['settleId']
|
|
return self.extend(request, params)
|
|
|
|
def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
|
|
"""
|
|
edit a trade order, gate currently only supports the modification of the price or amount fields
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#amend-an-order
|
|
https://www.gate.com/docs/developers/apiv4/en/#amend-an-order-2
|
|
|
|
:param str id: order id
|
|
:param str symbol: unified symbol of the market to create an order in
|
|
:param str type: 'market' or 'limit'
|
|
:param str side: 'buy' or 'sell'
|
|
:param float amount: how much of the currency you want to trade in units of the base currency
|
|
:param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param bool [params.unifiedAccount]: set to True for editing an order in a unified account
|
|
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
market = self.market(symbol)
|
|
extendedRequest = self.edit_order_request(id, symbol, type, side, amount, price, params)
|
|
response = None
|
|
if market['spot']:
|
|
response = self.privateSpotPatchOrdersOrderId(extendedRequest)
|
|
else:
|
|
response = self.privateFuturesPutSettleOrdersOrderId(extendedRequest)
|
|
#
|
|
# {
|
|
# "id": "243233276443",
|
|
# "text": "apiv4",
|
|
# "create_time": "1670908873",
|
|
# "update_time": "1670914102",
|
|
# "create_time_ms": 1670908873077,
|
|
# "update_time_ms": 1670914102241,
|
|
# "status": "open",
|
|
# "currency_pair": "ADA_USDT",
|
|
# "type": "limit",
|
|
# "account": "spot",
|
|
# "side": "sell",
|
|
# "amount": "10",
|
|
# "price": "0.6",
|
|
# "time_in_force": "gtc",
|
|
# "iceberg": "0",
|
|
# "left": "10",
|
|
# "fill_price": "0",
|
|
# "filled_total": "0",
|
|
# "fee": "0",
|
|
# "fee_currency": "USDT",
|
|
# "point_fee": "0",
|
|
# "gt_fee": "0",
|
|
# "gt_maker_fee": "0",
|
|
# "gt_taker_fee": "0",
|
|
# "gt_discount": False,
|
|
# "rebated_fee": "0",
|
|
# "rebated_fee_currency": "ADA"
|
|
# }
|
|
#
|
|
return self.parse_order(response, market)
|
|
|
|
def parse_order_status(self, status: Str):
|
|
statuses: dict = {
|
|
'open': 'open',
|
|
'_new': 'open',
|
|
'filled': 'closed',
|
|
'cancelled': 'canceled',
|
|
'liquidated': 'closed',
|
|
'ioc': 'canceled',
|
|
'failed': 'canceled',
|
|
'expired': 'canceled',
|
|
'finished': 'closed',
|
|
'finish': 'closed',
|
|
'succeeded': 'closed',
|
|
}
|
|
return self.safe_string(statuses, status, status)
|
|
|
|
def parse_order(self, order: dict, market: Market = None) -> Order:
|
|
#
|
|
# SPOT
|
|
# createOrder/cancelOrder/fetchOrder/editOrder
|
|
#
|
|
# {
|
|
# "id": "62364648575",
|
|
# "text": "apiv4",
|
|
# "create_time": "1626354834",
|
|
# "update_time": "1626354834",
|
|
# "create_time_ms": "1626354833544",
|
|
# "update_time_ms": "1626354833544",
|
|
# "status": "open",
|
|
# "currency_pair": "BTC_USDT",
|
|
# "type": "limit",
|
|
# "account": "spot",
|
|
# "side": "buy",
|
|
# "amount": "0.0001",
|
|
# "price": "30000",
|
|
# "time_in_force": "gtc",
|
|
# "iceberg": "0",
|
|
# "left": "0.0001",
|
|
# "fill_price": "0",
|
|
# "filled_total": "0",
|
|
# "fee": "0",
|
|
# "fee_currency": "BTC",
|
|
# "point_fee": "0",
|
|
# "gt_fee": "0",
|
|
# "gt_discount": True,
|
|
# "rebated_fee": "0",
|
|
# "rebated_fee_currency": "USDT"
|
|
# }
|
|
#
|
|
# SPOT TRIGGER ORDERS
|
|
# createOrder
|
|
#
|
|
# {
|
|
# "id": 12604556
|
|
# }
|
|
#
|
|
# fetchOrder/cancelOrder
|
|
#
|
|
# {
|
|
# "market": "ADA_USDT",
|
|
# "user": 6392049,
|
|
# "trigger": {
|
|
# "price": "1.08", # stopPrice
|
|
# "rule": "\u003e=",
|
|
# "expiration": 86400
|
|
# },
|
|
# "put": {
|
|
# "type": "limit",
|
|
# "side": "buy",
|
|
# "price": "1.08", # order price
|
|
# "amount": "1.00000000000000000000",
|
|
# "account": "normal",
|
|
# "time_in_force": "gtc"
|
|
# },
|
|
# "id": 71639298,
|
|
# "ctime": 1643945985,
|
|
# "status": "open"
|
|
# }
|
|
#
|
|
# FUTURE, SWAP AND OPTION
|
|
# createOrder/cancelOrder/fetchOrder
|
|
#
|
|
# {
|
|
# "id": 123028481731,
|
|
# "contract": "ADA_USDT",
|
|
# "mkfr": "-0.00005",
|
|
# "tkfr": "0.00048",
|
|
# "tif": "ioc",
|
|
# "is_reduce_only": False,
|
|
# "create_time": 1643950262.68,
|
|
# "finish_time": 1643950262.68,
|
|
# "price": "0",
|
|
# "size": 1,
|
|
# "refr": "0",
|
|
# "left":0,
|
|
# "text": "api",
|
|
# "fill_price": "1.05273",
|
|
# "user":6329238,
|
|
# "finish_as": "filled",
|
|
# "status": "finished",
|
|
# "is_liq": False,
|
|
# "refu":0,
|
|
# "is_close": False,
|
|
# "iceberg": 0
|
|
# }
|
|
#
|
|
# TRIGGER ORDERS(FUTURE AND SWAP)
|
|
# createOrder
|
|
#
|
|
# {
|
|
# "id": 12604556
|
|
# }
|
|
#
|
|
# fetchOrder/cancelOrder
|
|
#
|
|
# {
|
|
# "user": 6320300,
|
|
# "trigger": {
|
|
# "strategy_type": 0,
|
|
# "price_type": 0,
|
|
# "price": "1.03", # stopPrice
|
|
# "rule": 2,
|
|
# "expiration": 0
|
|
# },
|
|
# "initial": {
|
|
# "contract": "ADA_USDT",
|
|
# "size": -1,
|
|
# "price": "1.02",
|
|
# "tif": "gtc",
|
|
# "text": "",
|
|
# "iceberg": 0,
|
|
# "is_close": False,
|
|
# "is_reduce_only": False,
|
|
# "auto_size": ""
|
|
# },
|
|
# "id": 126393906,
|
|
# "trade_id": 0,
|
|
# "status": "open",
|
|
# "reason": "",
|
|
# "create_time": 1643953482,
|
|
# "finish_time": 1643953482,
|
|
# "is_stop_order": False,
|
|
# "stop_trigger": {
|
|
# "rule": 0,
|
|
# "trigger_price": "",
|
|
# "order_price": ""
|
|
# },
|
|
# "me_order_id": 0,
|
|
# "order_type": ""
|
|
# }
|
|
#
|
|
# {
|
|
# "text": "t-d18baf9ac44d82e2",
|
|
# "succeeded": False,
|
|
# "label": "BALANCE_NOT_ENOUGH",
|
|
# "message": "Not enough balance"
|
|
# }
|
|
#
|
|
# {"user_id":10406147,"id":"id","succeeded":false,"message":"INVALID_PROTOCOL","label":"INVALID_PROTOCOL"}
|
|
#
|
|
succeeded = self.safe_bool(order, 'succeeded', True)
|
|
if not succeeded:
|
|
# cancelOrders response
|
|
return self.safe_order({
|
|
'clientOrderId': self.safe_string(order, 'text'),
|
|
'info': order,
|
|
'status': 'rejected',
|
|
'id': self.safe_string(order, 'id'),
|
|
})
|
|
put = self.safe_value_2(order, 'put', 'initial', {})
|
|
trigger = self.safe_value(order, 'trigger', {})
|
|
contract = self.safe_string(put, 'contract')
|
|
type = self.safe_string(put, 'type')
|
|
timeInForce = self.safe_string_upper_2(put, 'time_in_force', 'tif')
|
|
amount = self.safe_string_2(put, 'amount', 'size')
|
|
side = self.safe_string(put, 'side')
|
|
price = self.safe_string(put, 'price')
|
|
contract = self.safe_string(order, 'contract', contract)
|
|
type = self.safe_string(order, 'type', type)
|
|
timeInForce = self.safe_string_upper_2(order, 'time_in_force', 'tif', timeInForce)
|
|
if timeInForce == 'POC':
|
|
timeInForce = 'PO'
|
|
postOnly = (timeInForce == 'PO')
|
|
amount = self.safe_string_2(order, 'amount', 'size', amount)
|
|
side = self.safe_string(order, 'side', side)
|
|
price = self.safe_string(order, 'price', price)
|
|
remainingString = self.safe_string(order, 'left')
|
|
cost = self.safe_string(order, 'filled_total')
|
|
triggerPrice = self.safe_number(trigger, 'price')
|
|
average = self.safe_number_2(order, 'avg_deal_price', 'fill_price')
|
|
if triggerPrice:
|
|
remainingString = amount
|
|
cost = '0'
|
|
if contract:
|
|
isMarketOrder = Precise.string_equals(price, '0') and (timeInForce == 'IOC')
|
|
type = 'market' if isMarketOrder else 'limit'
|
|
side = 'buy' if Precise.string_gt(amount, '0') else 'sell'
|
|
rawStatus = self.safe_string_n(order, ['finish_as', 'status', 'open'])
|
|
timestamp = self.safe_integer(order, 'create_time_ms')
|
|
if timestamp is None:
|
|
timestamp = self.safe_timestamp_2(order, 'create_time', 'ctime')
|
|
lastTradeTimestamp = self.safe_integer(order, 'update_time_ms')
|
|
if lastTradeTimestamp is None:
|
|
lastTradeTimestamp = self.safe_timestamp_2(order, 'update_time', 'finish_time')
|
|
marketType = 'contract'
|
|
if ('currency_pair' in order) or ('market' in order):
|
|
marketType = 'spot'
|
|
exchangeSymbol = self.safe_string_2(order, 'currency_pair', 'market', contract)
|
|
symbol = self.safe_symbol(exchangeSymbol, market, '_', marketType)
|
|
# Everything below self(above return) is related to fees
|
|
fees = []
|
|
gtFee = self.safe_string(order, 'gt_fee')
|
|
if gtFee is not None:
|
|
fees.append({
|
|
'currency': 'GT',
|
|
'cost': gtFee,
|
|
})
|
|
fee = self.safe_string(order, 'fee')
|
|
if fee is not None:
|
|
fees.append({
|
|
'currency': self.safe_currency_code(self.safe_string(order, 'fee_currency')),
|
|
'cost': fee,
|
|
})
|
|
rebate = self.safe_string(order, 'rebated_fee')
|
|
if rebate is not None:
|
|
fees.append({
|
|
'currency': self.safe_currency_code(self.safe_string(order, 'rebated_fee_currency')),
|
|
'cost': Precise.string_neg(rebate),
|
|
})
|
|
numFeeCurrencies = len(fees)
|
|
multipleFeeCurrencies = numFeeCurrencies > 1
|
|
status = self.parse_order_status(rawStatus)
|
|
remaining = Precise.string_abs(remainingString)
|
|
# handle spot market buy
|
|
account = self.safe_string(order, 'account') # using self instead of market type because of the conflicting ids
|
|
if account == 'spot':
|
|
averageString = self.safe_string(order, 'avg_deal_price')
|
|
average = self.parse_number(averageString)
|
|
if (type == 'market') and (side == 'buy'):
|
|
remaining = Precise.string_div(remainingString, averageString)
|
|
price = None # arrives
|
|
cost = amount
|
|
amount = Precise.string_div(amount, averageString)
|
|
return self.safe_order({
|
|
'id': self.safe_string(order, 'id'),
|
|
'clientOrderId': self.safe_string(order, 'text'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'lastTradeTimestamp': lastTradeTimestamp,
|
|
'status': status,
|
|
'symbol': symbol,
|
|
'type': type,
|
|
'timeInForce': timeInForce,
|
|
'postOnly': postOnly,
|
|
'reduceOnly': self.safe_value(order, 'is_reduce_only'),
|
|
'side': side,
|
|
'price': price,
|
|
'triggerPrice': triggerPrice,
|
|
'average': average,
|
|
'amount': Precise.string_abs(amount),
|
|
'cost': Precise.string_abs(cost),
|
|
'filled': None,
|
|
'remaining': remaining,
|
|
'fee': None if multipleFeeCurrencies else self.safe_value(fees, 0),
|
|
'fees': fees if multipleFeeCurrencies else [],
|
|
'trades': None,
|
|
'info': order,
|
|
}, market)
|
|
|
|
def fetch_order_request(self, id: str, symbol: Str = None, params={}):
|
|
market = None if (symbol is None) else self.market(symbol)
|
|
trigger = self.safe_bool_n(params, ['trigger', 'is_stop_order', 'stop'], False)
|
|
params = self.omit(params, ['is_stop_order', 'stop', 'trigger'])
|
|
clientOrderId = self.safe_string_2(params, 'text', 'clientOrderId')
|
|
orderId = id
|
|
if clientOrderId is not None:
|
|
params = self.omit(params, ['text', 'clientOrderId'])
|
|
if clientOrderId[0] != 't':
|
|
clientOrderId = 't-' + clientOrderId
|
|
orderId = clientOrderId
|
|
type, query = self.handle_market_type_and_params('fetchOrder', market, params)
|
|
contract = (type == 'swap') or (type == 'future') or (type == 'option')
|
|
request, requestParams = self.prepare_request(market, type, query) if contract else self.spot_order_prepare_request(market, trigger, query)
|
|
request['order_id'] = str(orderId)
|
|
return [request, requestParams]
|
|
|
|
def fetch_order(self, id: str, symbol: Str = None, params={}):
|
|
"""
|
|
Retrieves information on an order
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-a-single-order
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-a-single-order-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-a-single-order-3
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-a-single-order-4
|
|
|
|
:param str id: Order id
|
|
:param str symbol: Unified market symbol, *required for spot and margin*
|
|
:param dict [params]: Parameters specified by the exchange api
|
|
:param bool [params.trigger]: True if the order being fetched is a trigger order
|
|
:param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
|
|
:param str [params.type]: 'spot', 'swap', or 'future', if not provided self.options['defaultMarginMode'] is used
|
|
:param str [params.settle]: 'btc' or 'usdt' - settle currency for perpetual swap and future - market settle currency is used if symbol is not None, default="usdt" for swap and "btc" for future
|
|
:param bool [params.unifiedAccount]: set to True for fetching a unified account order
|
|
:returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
market = None if (symbol is None) else self.market(symbol)
|
|
result = self.handle_market_type_and_params('fetchOrder', market, params)
|
|
type = self.safe_string(result, 0)
|
|
trigger = self.safe_bool_n(params, ['trigger', 'is_stop_order', 'stop'], False)
|
|
request, requestParams = self.fetch_order_request(id, symbol, params)
|
|
response = None
|
|
if type == 'spot' or type == 'margin':
|
|
if trigger:
|
|
response = self.privateSpotGetPriceOrdersOrderId(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateSpotGetOrdersOrderId(self.extend(request, requestParams))
|
|
elif type == 'swap':
|
|
if trigger:
|
|
response = self.privateFuturesGetSettlePriceOrdersOrderId(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateFuturesGetSettleOrdersOrderId(self.extend(request, requestParams))
|
|
elif type == 'future':
|
|
if trigger:
|
|
response = self.privateDeliveryGetSettlePriceOrdersOrderId(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateDeliveryGetSettleOrdersOrderId(self.extend(request, requestParams))
|
|
elif type == 'option':
|
|
response = self.privateOptionsGetOrdersOrderId(self.extend(request, requestParams))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchOrder() not support self market type')
|
|
return self.parse_order(response, market)
|
|
|
|
def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
|
"""
|
|
fetch all unfilled currently open orders
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-open-orders
|
|
https://www.gate.com/docs/developers/apiv4/en/#retrieve-running-auto-order-list
|
|
|
|
:param str symbol: unified market symbol
|
|
:param int [since]: the earliest time in ms to fetch open orders for
|
|
:param int [limit]: the maximum number of open orders structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param bool [params.trigger]: True for fetching trigger orders
|
|
:param str [params.type]: spot, margin, swap or future, if not provided self.options['defaultType'] is used
|
|
:param str [params.marginMode]: 'cross' or 'isolated' - marginMode for type='margin', if not provided self.options['defaultMarginMode'] is used
|
|
:param bool [params.unifiedAccount]: set to True for fetching unified account orders
|
|
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
return self.fetch_orders_by_status('open', symbol, since, limit, params)
|
|
|
|
def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
|
"""
|
|
fetches information on multiple closed orders made by the user
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-orders
|
|
https://www.gate.com/docs/developers/apiv4/en/#retrieve-running-auto-order-list
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-futures-orders
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-auto-orders
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-futures-orders-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-auto-orders-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-options-orders
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-futures-orders-by-time-range
|
|
|
|
:param str symbol: unified market symbol of the market orders were made in
|
|
:param int [since]: the earliest time in ms to fetch orders for
|
|
:param int [limit]: the maximum number of order structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param bool [params.trigger]: True for fetching trigger orders
|
|
:param str [params.type]: spot, swap or future, if not provided self.options['defaultType'] is used
|
|
:param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
|
|
:param boolean [params.historical]: *swap only* True for using historical endpoint
|
|
:param bool [params.unifiedAccount]: set to True for fetching unified account orders
|
|
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
until = self.safe_integer(params, 'until')
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
symbol = market['symbol']
|
|
res = self.handle_market_type_and_params('fetchClosedOrders', market, params)
|
|
type = self.safe_string(res, 0)
|
|
useHistorical = False
|
|
useHistorical, params = self.handle_option_and_params(params, 'fetchClosedOrders', 'historical', False)
|
|
if not useHistorical and ((since is None and until is None) or (type != 'swap')):
|
|
return self.fetch_orders_by_status('finished', symbol, since, limit, params)
|
|
params = self.omit(params, 'type')
|
|
request = {}
|
|
request, params = self.prepare_request(market, type, params)
|
|
if since is not None:
|
|
request['from'] = self.parse_to_int(since / 1000)
|
|
if until is not None:
|
|
params = self.omit(params, 'until')
|
|
request['to'] = self.parse_to_int(until / 1000)
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
response = self.privateFuturesGetSettleOrdersTimerange(self.extend(request, params))
|
|
return self.parse_orders(response, market, since, limit)
|
|
|
|
def prepare_orders_by_status_request(self, status, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
symbol = market['symbol']
|
|
trigger: Bool = None
|
|
trigger, params = self.handle_param_bool_2(params, 'trigger', 'stop')
|
|
type: Str = None
|
|
type, params = self.handle_market_type_and_params('fetchOrdersByStatus', market, params)
|
|
spot = (type == 'spot') or (type == 'margin')
|
|
request: dict = {}
|
|
request, params = self.multi_order_spot_prepare_request(market, trigger, params) if spot else self.prepare_request(market, type, params)
|
|
if spot and trigger:
|
|
request = self.omit(request, 'account')
|
|
if status == 'closed':
|
|
status = 'finished'
|
|
request['status'] = status
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
if spot:
|
|
if since is not None:
|
|
request['from'] = self.parse_to_int(since / 1000)
|
|
until = self.safe_integer(params, 'until')
|
|
if until is not None:
|
|
params = self.omit(params, 'until')
|
|
request['to'] = self.parse_to_int(until / 1000)
|
|
lastId, finalParams = self.handle_param_string_2(params, 'lastId', 'last_id')
|
|
if lastId is not None:
|
|
request['last_id'] = lastId
|
|
return [request, finalParams]
|
|
|
|
def fetch_orders_by_status(self, status, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
symbol = market['symbol']
|
|
# don't omit here, omits done in prepareOrdersByStatusRequest
|
|
trigger: Bool = self.safe_bool_2(params, 'trigger', 'stop')
|
|
res = self.handle_market_type_and_params('fetchOrdersByStatus', market, params)
|
|
type = self.safe_string(res, 0)
|
|
request, requestParams = self.prepare_orders_by_status_request(status, symbol, since, limit, params)
|
|
spot = (type == 'spot') or (type == 'margin')
|
|
openStatus = (status == 'open')
|
|
openSpotOrders = spot and openStatus and not trigger
|
|
response = None
|
|
if spot:
|
|
if not trigger:
|
|
if openStatus:
|
|
response = self.privateSpotGetOpenOrders(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateSpotGetOrders(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateSpotGetPriceOrders(self.extend(request, requestParams))
|
|
elif type == 'swap':
|
|
if trigger:
|
|
response = self.privateFuturesGetSettlePriceOrders(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateFuturesGetSettleOrders(self.extend(request, requestParams))
|
|
elif type == 'future':
|
|
if trigger:
|
|
response = self.privateDeliveryGetSettlePriceOrders(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateDeliveryGetSettleOrders(self.extend(request, requestParams))
|
|
elif type == 'option':
|
|
response = self.privateOptionsGetOrders(self.extend(request, requestParams))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchOrders() not support self market type')
|
|
#
|
|
# spot open orders
|
|
#
|
|
# [
|
|
# {
|
|
# "currency_pair": "ADA_USDT",
|
|
# "total": 2,
|
|
# "orders": [
|
|
# {
|
|
# "id": "155498539874",
|
|
# "text": "apiv4",
|
|
# "create_time": "1652406843",
|
|
# "update_time": "1652406843",
|
|
# "create_time_ms": 1652406843295,
|
|
# "update_time_ms": 1652406843295,
|
|
# "status": "open",
|
|
# "currency_pair": "ADA_USDT",
|
|
# "type": "limit",
|
|
# "account": "spot",
|
|
# "side": "buy",
|
|
# "amount": "3",
|
|
# "price": "0.35",
|
|
# "time_in_force": "gtc",
|
|
# "iceberg": "0",
|
|
# "left": "3",
|
|
# "fill_price": "0",
|
|
# "filled_total": "0",
|
|
# "fee": "0",
|
|
# "fee_currency": "ADA",
|
|
# "point_fee": "0",
|
|
# "gt_fee": "0",
|
|
# "gt_discount": False,
|
|
# "rebated_fee": "0",
|
|
# "rebated_fee_currency": "USDT"
|
|
# },
|
|
# ...
|
|
# ]
|
|
# },
|
|
# ...
|
|
# ]
|
|
#
|
|
# spot
|
|
#
|
|
# [
|
|
# {
|
|
# "id": "8834234273",
|
|
# "text": "3",
|
|
# "create_time": "1635406193",
|
|
# "update_time": "1635406193",
|
|
# "create_time_ms": 1635406193361,
|
|
# "update_time_ms": 1635406193361,
|
|
# "status": "closed",
|
|
# "currency_pair": "BTC_USDT",
|
|
# "type": "limit",
|
|
# "account": "spot", # margin for margin orders
|
|
# "side": "sell",
|
|
# "amount": "0.0002",
|
|
# "price": "58904.01",
|
|
# "time_in_force": "gtc",
|
|
# "iceberg": "0",
|
|
# "left": "0.0000",
|
|
# "fill_price": "11.790516",
|
|
# "filled_total": "11.790516",
|
|
# "fee": "0.023581032",
|
|
# "fee_currency": "USDT",
|
|
# "point_fee": "0",
|
|
# "gt_fee": "0",
|
|
# "gt_discount": False,
|
|
# "rebated_fee_currency": "BTC"
|
|
# }
|
|
# ]
|
|
#
|
|
# spot trigger
|
|
#
|
|
# [
|
|
# {
|
|
# "market": "ADA_USDT",
|
|
# "user": 10406147,
|
|
# "trigger": {
|
|
# "price": "0.65",
|
|
# "rule": "\u003c=",
|
|
# "expiration": 86400
|
|
# },
|
|
# "put": {
|
|
# "type": "limit",
|
|
# "side": "sell",
|
|
# "price": "0.65",
|
|
# "amount": "2.00000000000000000000",
|
|
# "account": "normal", # margin for margin orders
|
|
# "time_in_force": "gtc"
|
|
# },
|
|
# "id": 8449909,
|
|
# "ctime": 1652188982,
|
|
# "status": "open"
|
|
# }
|
|
# ]
|
|
#
|
|
# swap
|
|
#
|
|
# [
|
|
# {
|
|
# "status": "finished",
|
|
# "size": -1,
|
|
# "left": 0,
|
|
# "id": 82750739203,
|
|
# "is_liq": False,
|
|
# "is_close": False,
|
|
# "contract": "BTC_USDT",
|
|
# "text": "web",
|
|
# "fill_price": "60721.3",
|
|
# "finish_as": "filled",
|
|
# "iceberg": 0,
|
|
# "tif": "ioc",
|
|
# "is_reduce_only": True,
|
|
# "create_time": 1635403475.412,
|
|
# "finish_time": 1635403475.4127,
|
|
# "price": "0"
|
|
# }
|
|
# ]
|
|
#
|
|
# option
|
|
#
|
|
# [
|
|
# {
|
|
# "id": 2593450699,
|
|
# "contract": "BTC_USDT-20230601-27500-C",
|
|
# "mkfr": "0.0003",
|
|
# "tkfr": "0.0003",
|
|
# "tif": "gtc",
|
|
# "is_reduce_only": False,
|
|
# "create_time": 1685503873,
|
|
# "price": "200",
|
|
# "size": 1,
|
|
# "refr": "0",
|
|
# "left": 1,
|
|
# "text": "api",
|
|
# "fill_price": "0",
|
|
# "user": 5691076,
|
|
# "status": "open",
|
|
# "is_liq": False,
|
|
# "refu": 0,
|
|
# "is_close": False,
|
|
# "iceberg": 0
|
|
# }
|
|
# ]
|
|
#
|
|
result = response
|
|
if openSpotOrders:
|
|
result = []
|
|
for i in range(0, len(response)):
|
|
ordersInner = self.safe_value(response[i], 'orders')
|
|
result = self.array_concat(result, ordersInner)
|
|
orders = self.parse_orders(result, market, since, limit)
|
|
return self.filter_by_symbol_since_limit(orders, symbol, since, limit)
|
|
|
|
def cancel_order(self, id: str, symbol: Str = None, params={}):
|
|
"""
|
|
Cancels an open order
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-a-single-order
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-a-single-order-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-a-single-order-3
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-a-single-order-4
|
|
|
|
:param str id: Order id
|
|
:param str symbol: Unified market symbol
|
|
:param dict [params]: Parameters specified by the exchange api
|
|
:param bool [params.trigger]: True if the order to be cancelled is a trigger order
|
|
:param bool [params.unifiedAccount]: set to True for canceling unified account orders
|
|
:returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
market = None if (symbol is None) else self.market(symbol)
|
|
trigger = self.safe_bool_n(params, ['is_stop_order', 'stop', 'trigger'], False)
|
|
params = self.omit(params, ['is_stop_order', 'stop', 'trigger'])
|
|
type, query = self.handle_market_type_and_params('cancelOrder', market, params)
|
|
request, requestParams = self.spot_order_prepare_request(market, trigger, query) if (type == 'spot' or type == 'margin') else self.prepare_request(market, type, query)
|
|
request['order_id'] = id
|
|
response = None
|
|
if type == 'spot' or type == 'margin':
|
|
if trigger:
|
|
response = self.privateSpotDeletePriceOrdersOrderId(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateSpotDeleteOrdersOrderId(self.extend(request, requestParams))
|
|
elif type == 'swap':
|
|
if trigger:
|
|
response = self.privateFuturesDeleteSettlePriceOrdersOrderId(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateFuturesDeleteSettleOrdersOrderId(self.extend(request, requestParams))
|
|
elif type == 'future':
|
|
if trigger:
|
|
response = self.privateDeliveryDeleteSettlePriceOrdersOrderId(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateDeliveryDeleteSettleOrdersOrderId(self.extend(request, requestParams))
|
|
elif type == 'option':
|
|
response = self.privateOptionsDeleteOrdersOrderId(self.extend(request, requestParams))
|
|
else:
|
|
raise NotSupported(self.id + ' cancelOrder() not support self market type')
|
|
#
|
|
# spot
|
|
#
|
|
# {
|
|
# "id": "95282841887",
|
|
# "text": "apiv4",
|
|
# "create_time": "1637383156",
|
|
# "update_time": "1637383235",
|
|
# "create_time_ms": 1637383156017,
|
|
# "update_time_ms": 1637383235085,
|
|
# "status": "cancelled",
|
|
# "currency_pair": "ETH_USDT",
|
|
# "type": "limit",
|
|
# "account": "spot",
|
|
# "side": "buy",
|
|
# "amount": "0.01",
|
|
# "price": "3500",
|
|
# "time_in_force": "gtc",
|
|
# "iceberg": "0",
|
|
# "left": "0.01",
|
|
# "fill_price": "0",
|
|
# "filled_total": "0",
|
|
# "fee": "0",
|
|
# "fee_currency": "ETH",
|
|
# "point_fee": "0",
|
|
# "gt_fee": "0",
|
|
# "gt_discount": False,
|
|
# "rebated_fee": "0",
|
|
# "rebated_fee_currency": "USDT"
|
|
# }
|
|
#
|
|
# spot conditional
|
|
#
|
|
# {
|
|
# "market": "ETH_USDT",
|
|
# "user": 2436035,
|
|
# "trigger": {
|
|
# "price": "3500",
|
|
# "rule": "\u003c=",
|
|
# "expiration": 86400
|
|
# },
|
|
# "put": {
|
|
# "type": "limit",
|
|
# "side": "buy",
|
|
# "price": "3500",
|
|
# "amount": "0.01000000000000000000",
|
|
# "account": "normal",
|
|
# "time_in_force": "gtc"
|
|
# },
|
|
# "id": 5891843,
|
|
# "ctime": 1637382379,
|
|
# "ftime": 1637382673,
|
|
# "status": "canceled"
|
|
# }
|
|
#
|
|
# swap, future and option
|
|
#
|
|
# {
|
|
# "id": "82241928192",
|
|
# "contract": "BTC_USDT",
|
|
# "mkfr": "0",
|
|
# "tkfr": "0.0005",
|
|
# "tif": "gtc",
|
|
# "is_reduce_only": False,
|
|
# "create_time": "1635196145.06",
|
|
# "finish_time": "1635196233.396",
|
|
# "price": "61000",
|
|
# "size": "4",
|
|
# "refr": "0",
|
|
# "left": "4",
|
|
# "text": "web",
|
|
# "fill_price": "0",
|
|
# "user": "6693577",
|
|
# "finish_as": "cancelled",
|
|
# "status": "finished",
|
|
# "is_liq": False,
|
|
# "refu": "0",
|
|
# "is_close": False,
|
|
# "iceberg": "0",
|
|
# }
|
|
#
|
|
return self.parse_order(response, market)
|
|
|
|
def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
|
|
"""
|
|
cancel multiple orders
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-a-batch-of-orders-with-an-id-list
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-a-batch-of-orders-with-an-id-list-2
|
|
|
|
:param str[] ids: order ids
|
|
:param str symbol: unified symbol of the market the order was made in
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param bool [params.unifiedAccount]: set to True for canceling unified account orders
|
|
:returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
type = None
|
|
defaultSettle = 'usdt' if (market is None) else market['settle']
|
|
settle = self.safe_string_lower(params, 'settle', defaultSettle)
|
|
type, params = self.handle_market_type_and_params('cancelOrders', market, params)
|
|
isSpot = (type == 'spot')
|
|
if isSpot and (symbol is None):
|
|
raise ArgumentsRequired(self.id + ' cancelOrders requires a symbol argument for spot markets')
|
|
if isSpot:
|
|
ordersRequests = []
|
|
for i in range(0, len(ids)):
|
|
id = ids[i]
|
|
orderItem: dict = {
|
|
'id': id,
|
|
'symbol': symbol,
|
|
}
|
|
ordersRequests.append(orderItem)
|
|
return self.cancel_orders_for_symbols(ordersRequests, params)
|
|
request = {
|
|
'settle': settle,
|
|
}
|
|
finalList = [request] # hacky but needs to be done here
|
|
for i in range(0, len(ids)):
|
|
finalList.append(ids[i])
|
|
response = self.privateFuturesPostSettleBatchCancelOrders(finalList)
|
|
return self.parse_orders(response)
|
|
|
|
def cancel_orders_for_symbols(self, orders: List[CancellationRequest], params={}):
|
|
"""
|
|
cancel multiple orders for multiple symbols
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-a-batch-of-orders-with-an-id-list
|
|
|
|
:param CancellationRequest[] orders: list of order ids with symbol, example [{"id": "a", "symbol": "BTC/USDT"}, {"id": "b", "symbol": "ETH/USDT"}]
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str[] [params.clientOrderIds]: client order ids
|
|
:param bool [params.unifiedAccount]: set to True for canceling unified account orders
|
|
:returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
ordersRequests = []
|
|
for i in range(0, len(orders)):
|
|
order = orders[i]
|
|
symbol = self.safe_string(order, 'symbol')
|
|
market = self.market(symbol)
|
|
if not market['spot']:
|
|
raise NotSupported(self.id + ' cancelOrdersForSymbols() supports only spot markets')
|
|
id = self.safe_string(order, 'id')
|
|
orderItem: dict = {
|
|
'id': id,
|
|
'currency_pair': market['id'],
|
|
}
|
|
ordersRequests.append(orderItem)
|
|
response = self.privateSpotPostCancelBatchOrders(ordersRequests)
|
|
#
|
|
# [
|
|
# {
|
|
# "currency_pair": "BTC_USDT",
|
|
# "id": "123456"
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_orders(response)
|
|
|
|
def cancel_all_orders(self, symbol: Str = None, params={}):
|
|
"""
|
|
cancel all open orders
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-all-open-orders-in-specified-currency-pair
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-all-open-orders-matched
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-all-open-orders-matched-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#cancel-all-open-orders-matched-3
|
|
|
|
:param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param bool [params.unifiedAccount]: set to True for canceling unified account orders
|
|
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
market = None if (symbol is None) else self.market(symbol)
|
|
trigger = self.safe_bool_2(params, 'stop', 'trigger')
|
|
params = self.omit(params, ['stop', 'trigger'])
|
|
type, query = self.handle_market_type_and_params('cancelAllOrders', market, params)
|
|
request, requestParams = self.multi_order_spot_prepare_request(market, trigger, query) if (type == 'spot') else self.prepare_request(market, type, query)
|
|
response = None
|
|
if type == 'spot' or type == 'margin':
|
|
if trigger:
|
|
response = self.privateSpotDeletePriceOrders(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateSpotDeleteOrders(self.extend(request, requestParams))
|
|
elif type == 'swap':
|
|
if trigger:
|
|
response = self.privateFuturesDeleteSettlePriceOrders(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateFuturesDeleteSettleOrders(self.extend(request, requestParams))
|
|
elif type == 'future':
|
|
if trigger:
|
|
response = self.privateDeliveryDeleteSettlePriceOrders(self.extend(request, requestParams))
|
|
else:
|
|
response = self.privateDeliveryDeleteSettleOrders(self.extend(request, requestParams))
|
|
elif type == 'option':
|
|
response = self.privateOptionsDeleteOrders(self.extend(request, requestParams))
|
|
else:
|
|
raise NotSupported(self.id + ' cancelAllOrders() not support self market type')
|
|
#
|
|
# [
|
|
# {
|
|
# "id": 139797004085,
|
|
# "contract": "ADA_USDT",
|
|
# "mkfr": "0",
|
|
# "tkfr": "0.0005",
|
|
# "tif": "gtc",
|
|
# "is_reduce_only": False,
|
|
# "create_time": 1647911169.343,
|
|
# "finish_time": 1647911226.849,
|
|
# "price": "0.8",
|
|
# "size": 1,
|
|
# "refr": "0.3",
|
|
# "left": 1,
|
|
# "text": "api",
|
|
# "fill_price": "0",
|
|
# "user": 6693577,
|
|
# "finish_as": "cancelled",
|
|
# "status": "finished",
|
|
# "is_liq": False,
|
|
# "refu": 2436035,
|
|
# "is_close": False,
|
|
# "iceberg": 0
|
|
# }
|
|
# ...
|
|
# ]
|
|
#
|
|
return self.parse_orders(response, market)
|
|
|
|
def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
|
|
"""
|
|
transfer currency internally between wallets on the same account
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#transfer-between-trading-accounts
|
|
|
|
:param str code: unified currency code for currency being transferred
|
|
:param float amount: the amount of currency to transfer
|
|
:param str fromAccount: the account to transfer currency from
|
|
:param str toAccount: the account to transfer currency to
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.symbol]: Unified market symbol *required for type == margin*
|
|
:returns: A `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
|
|
"""
|
|
self.load_markets()
|
|
currency = self.currency(code)
|
|
fromId = self.convert_type_to_account(fromAccount)
|
|
toId = self.convert_type_to_account(toAccount)
|
|
truncated = self.currency_to_precision(code, amount)
|
|
request: dict = {
|
|
'currency': currency['id'], # todo: currencies have network-junctions
|
|
'amount': truncated,
|
|
}
|
|
if not (fromId in self.options['accountsByType']):
|
|
request['from'] = 'margin'
|
|
request['currency_pair'] = fromId
|
|
else:
|
|
request['from'] = fromId
|
|
if not (toId in self.options['accountsByType']):
|
|
request['to'] = 'margin'
|
|
request['currency_pair'] = toId
|
|
else:
|
|
request['to'] = toId
|
|
if fromId == 'margin' or toId == 'margin':
|
|
symbol = self.safe_string_2(params, 'symbol', 'currency_pair')
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' transfer requires params["symbol"] for isolated margin transfers')
|
|
market = self.market(symbol)
|
|
request['currency_pair'] = market['id']
|
|
params = self.omit(params, 'symbol')
|
|
if (toId == 'futures') or (toId == 'delivery') or (fromId == 'futures') or (fromId == 'delivery'):
|
|
request['settle'] = currency['id'] # todo: currencies have network-junctions
|
|
response = self.privateWalletPostTransfers(self.extend(request, params))
|
|
#
|
|
# according to the docs(however actual response seems to be an empty string '')
|
|
#
|
|
# {
|
|
# "currency": "BTC",
|
|
# "from": "spot",
|
|
# "to": "margin",
|
|
# "amount": "1",
|
|
# "currency_pair": "BTC_USDT"
|
|
# }
|
|
#
|
|
return self.parse_transfer(response, currency)
|
|
|
|
def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
|
|
#
|
|
# {
|
|
# "currency": "BTC",
|
|
# "from": "spot",
|
|
# "to": "margin",
|
|
# "amount": "1",
|
|
# "currency_pair": "BTC_USDT"
|
|
# }
|
|
#
|
|
return {
|
|
'id': self.safe_string(transfer, 'tx_id'),
|
|
'timestamp': None,
|
|
'datetime': None,
|
|
'currency': self.safe_currency_code(None, currency),
|
|
'amount': None,
|
|
'fromAccount': None,
|
|
'toAccount': None,
|
|
'status': None,
|
|
'info': transfer,
|
|
}
|
|
|
|
def set_leverage(self, leverage: int, symbol: Str = None, params={}):
|
|
"""
|
|
set the level of leverage for a market
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#update-position-leverage
|
|
https://www.gate.com/docs/developers/apiv4/en/#update-position-leverage-2
|
|
|
|
:param float leverage: the rate of leverage
|
|
:param str symbol: unified market symbol
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: response from the exchange
|
|
"""
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
|
|
# WARNING: THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
|
|
# AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
|
|
if (leverage < 0) or (leverage > 100):
|
|
raise BadRequest(self.id + ' setLeverage() leverage should be between 1 and 100')
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request, query = self.prepare_request(market, None, params)
|
|
defaultMarginMode = self.safe_string_2(self.options, 'marginMode', 'defaultMarginMode')
|
|
crossLeverageLimit = self.safe_string(query, 'cross_leverage_limit')
|
|
marginMode = self.safe_string(query, 'marginMode', defaultMarginMode)
|
|
stringifiedMargin = self.number_to_string(leverage)
|
|
if crossLeverageLimit is not None:
|
|
marginMode = 'cross'
|
|
stringifiedMargin = crossLeverageLimit
|
|
if marginMode == 'cross' or marginMode == 'cross_margin':
|
|
request['cross_leverage_limit'] = stringifiedMargin
|
|
request['leverage'] = '0'
|
|
else:
|
|
request['leverage'] = stringifiedMargin
|
|
response = None
|
|
if market['swap']:
|
|
response = self.privateFuturesPostSettlePositionsContractLeverage(self.extend(request, query))
|
|
elif market['future']:
|
|
response = self.privateDeliveryPostSettlePositionsContractLeverage(self.extend(request, query))
|
|
else:
|
|
raise NotSupported(self.id + ' setLeverage() not support self market type')
|
|
#
|
|
# {
|
|
# "value": "0",
|
|
# "leverage": "5",
|
|
# "mode": "single",
|
|
# "realised_point": "0",
|
|
# "contract": "BTC_USDT",
|
|
# "entry_price": "0",
|
|
# "mark_price": "62035.86",
|
|
# "history_point": "0",
|
|
# "realised_pnl": "0",
|
|
# "close_order": null,
|
|
# "size": 0,
|
|
# "cross_leverage_limit": "0",
|
|
# "pending_orders": 0,
|
|
# "adl_ranking": 6,
|
|
# "maintenance_rate": "0.005",
|
|
# "unrealised_pnl": "0",
|
|
# "user": 2436035,
|
|
# "leverage_max": "100",
|
|
# "history_pnl": "0",
|
|
# "risk_limit": "1000000",
|
|
# "margin": "0",
|
|
# "last_close_pnl": "0",
|
|
# "liq_price": "0"
|
|
# }
|
|
#
|
|
return response
|
|
|
|
def parse_position(self, position: dict, market: Market = None):
|
|
#
|
|
# swap and future
|
|
#
|
|
# {
|
|
# "value": "4.60516",
|
|
# "leverage": "0",
|
|
# "mode": "single",
|
|
# "realised_point": "0",
|
|
# "contract": "BTC_USDT",
|
|
# "entry_price": "46030.3",
|
|
# "mark_price": "46051.6",
|
|
# "history_point": "0",
|
|
# "realised_pnl": "-0.002301515",
|
|
# "close_order": null,
|
|
# "size": 1,
|
|
# "cross_leverage_limit": "0",
|
|
# "pending_orders": 0,
|
|
# "adl_ranking": 5,
|
|
# "maintenance_rate": "0.004",
|
|
# "unrealised_pnl": "0.00213",
|
|
# "user": 5691076,
|
|
# "leverage_max": "125",
|
|
# "history_pnl": "0",
|
|
# "risk_limit": "1000000",
|
|
# "margin": "8.997698485",
|
|
# "last_close_pnl": "0",
|
|
# "liq_price": "0",
|
|
# "update_time": 1705034246,
|
|
# "update_id": 1,
|
|
# "initial_margin": "0",
|
|
# "maintenance_margin": "0",
|
|
# "open_time": 1705034246,
|
|
# "trade_max_size": "0"
|
|
# }
|
|
#
|
|
# option
|
|
#
|
|
# {
|
|
# "close_order": null,
|
|
# "size": 1,
|
|
# "vega": "5.29756",
|
|
# "theta": "-98.98917",
|
|
# "gamma": "0.00056",
|
|
# "delta": "0.68691",
|
|
# "contract": "BTC_USDT-20230602-26500-C",
|
|
# "entry_price": "529",
|
|
# "unrealised_pnl": "-1.0131",
|
|
# "user": 5691076,
|
|
# "mark_price": "427.69",
|
|
# "underlying_price": "26810.2",
|
|
# "underlying": "BTC_USDT",
|
|
# "realised_pnl": "-0.08042877",
|
|
# "mark_iv": "0.4224",
|
|
# "pending_orders": 0
|
|
# }
|
|
#
|
|
# fetchPositionsHistory(swap and future)
|
|
#
|
|
# {
|
|
# "contract": "SLERF_USDT", # Futures contract
|
|
# "text": "web", # Text of close order
|
|
# "long_price": "0.766306", # When 'side' is 'long,' it indicates the opening average price; when 'side' is 'short,' it indicates the closing average price.
|
|
# "pnl": "-23.41702352", # PNL
|
|
# "pnl_pnl": "-22.7187", # Position P/L
|
|
# "pnl_fee": "-0.06527125", # Transaction Fees
|
|
# "pnl_fund": "-0.63305227", # Funding Fees
|
|
# "accum_size": "100",
|
|
# "time": 1711279263, # Position close time
|
|
# "short_price": "0.539119", # When 'side' is 'long,' it indicates the opening average price; when 'side' is 'short,' it indicates the closing average price
|
|
# "side": "long", # Position side, long or short
|
|
# "max_size": "100", # Max Trade Size
|
|
# "first_open_time": 1711037985 # First Open Time
|
|
# }
|
|
#
|
|
contract = self.safe_string(position, 'contract')
|
|
market = self.safe_market(contract, market, '_', 'contract')
|
|
size = self.safe_string_2(position, 'size', 'accum_size')
|
|
side = self.safe_string(position, 'side')
|
|
if side is None:
|
|
if Precise.string_gt(size, '0'):
|
|
side = 'long'
|
|
elif Precise.string_lt(size, '0'):
|
|
side = 'short'
|
|
maintenanceRate = self.safe_string(position, 'maintenance_rate')
|
|
notional = self.safe_string(position, 'value')
|
|
leverage = self.safe_string(position, 'leverage')
|
|
marginMode = None
|
|
if leverage is not None:
|
|
if leverage == '0':
|
|
marginMode = 'cross'
|
|
else:
|
|
marginMode = 'isolated'
|
|
# Initial Position Margin = ( Position Value / Leverage ) + Close Position Fee
|
|
# *The default leverage under the full position is the highest leverage in the market.
|
|
# *Trading fee is charged Fee Rate(0.075%).
|
|
feePaid = self.safe_string(position, 'pnl_fee')
|
|
initialMarginString = None
|
|
if feePaid is None:
|
|
takerFee = '0.00075'
|
|
feePaid = Precise.string_mul(takerFee, notional)
|
|
initialMarginString = Precise.string_add(Precise.string_div(notional, leverage), feePaid)
|
|
timestamp = self.safe_timestamp_2(position, 'open_time', 'first_open_time')
|
|
if timestamp == 0:
|
|
timestamp = None
|
|
return self.safe_position({
|
|
'info': position,
|
|
'id': None,
|
|
'symbol': self.safe_string(market, 'symbol'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'lastUpdateTimestamp': self.safe_timestamp_2(position, 'update_time', 'time'),
|
|
'initialMargin': self.parse_number(initialMarginString),
|
|
'initialMarginPercentage': self.parse_number(Precise.string_div(initialMarginString, notional)),
|
|
'maintenanceMargin': self.parse_number(Precise.string_mul(maintenanceRate, notional)),
|
|
'maintenanceMarginPercentage': self.parse_number(maintenanceRate),
|
|
'entryPrice': self.safe_number(position, 'entry_price'),
|
|
'notional': self.parse_number(notional),
|
|
'leverage': self.safe_number(position, 'leverage'),
|
|
'unrealizedPnl': self.safe_number(position, 'unrealised_pnl'),
|
|
'realizedPnl': self.safe_number_2(position, 'realised_pnl', 'pnl'),
|
|
'contracts': self.parse_number(Precise.string_abs(size)),
|
|
'contractSize': self.safe_number(market, 'contractSize'),
|
|
'marginRatio': None,
|
|
'liquidationPrice': self.safe_number(position, 'liq_price'),
|
|
'markPrice': self.safe_number(position, 'mark_price'),
|
|
'lastPrice': None,
|
|
'collateral': self.safe_number(position, 'margin'),
|
|
'marginMode': marginMode,
|
|
'side': side,
|
|
'percentage': None,
|
|
'stopLossPrice': None,
|
|
'takeProfitPrice': None,
|
|
})
|
|
|
|
def fetch_position(self, symbol: str, params={}):
|
|
"""
|
|
fetch data on an open contract position
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-single-position
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-single-position-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-specified-contract-position
|
|
|
|
:param str symbol: unified market symbol of the market the position is held in
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `position structure <https://docs.ccxt.com/#/?id=position-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
if not market['contract']:
|
|
raise BadRequest(self.id + ' fetchPosition() supports contract markets only')
|
|
request: dict = {}
|
|
request, params = self.prepare_request(market, market['type'], params)
|
|
extendedRequest = self.extend(request, params)
|
|
response = None
|
|
if market['swap']:
|
|
response = self.privateFuturesGetSettlePositionsContract(extendedRequest)
|
|
elif market['future']:
|
|
response = self.privateDeliveryGetSettlePositionsContract(extendedRequest)
|
|
elif market['type'] == 'option':
|
|
response = self.privateOptionsGetPositionsContract(extendedRequest)
|
|
#
|
|
# swap and future
|
|
#
|
|
# {
|
|
# "value": "4.60516",
|
|
# "leverage": "0",
|
|
# "mode": "single",
|
|
# "realised_point": "0",
|
|
# "contract": "BTC_USDT",
|
|
# "entry_price": "46030.3",
|
|
# "mark_price": "46051.6",
|
|
# "history_point": "0",
|
|
# "realised_pnl": "-0.002301515",
|
|
# "close_order": null,
|
|
# "size": 1,
|
|
# "cross_leverage_limit": "0",
|
|
# "pending_orders": 0,
|
|
# "adl_ranking": 5,
|
|
# "maintenance_rate": "0.004",
|
|
# "unrealised_pnl": "0.00213",
|
|
# "user": 5691076,
|
|
# "leverage_max": "125",
|
|
# "history_pnl": "0",
|
|
# "risk_limit": "1000000",
|
|
# "margin": "8.997698485",
|
|
# "last_close_pnl": "0",
|
|
# "liq_price": "0",
|
|
# "update_time": 1705034246,
|
|
# "update_id": 1,
|
|
# "initial_margin": "0",
|
|
# "maintenance_margin": "0",
|
|
# "open_time": 1705034246,
|
|
# "trade_max_size": "0"
|
|
# }
|
|
#
|
|
# option
|
|
#
|
|
# {
|
|
# "close_order": null,
|
|
# "size": 1,
|
|
# "vega": "5.29756",
|
|
# "theta": "-98.98917",
|
|
# "gamma": "0.00056",
|
|
# "delta": "0.68691",
|
|
# "contract": "BTC_USDT-20230602-26500-C",
|
|
# "entry_price": "529",
|
|
# "unrealised_pnl": "-1.0131",
|
|
# "user": 5691076,
|
|
# "mark_price": "427.69",
|
|
# "underlying_price": "26810.2",
|
|
# "underlying": "BTC_USDT",
|
|
# "realised_pnl": "-0.08042877",
|
|
# "mark_iv": "0.4224",
|
|
# "pending_orders": 0
|
|
# }
|
|
#
|
|
return self.parse_position(response, market)
|
|
|
|
def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
|
|
"""
|
|
fetch all open positions
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-positions-of-a-user
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-positions-of-a-user-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-user-s-positions-of-specified-underlying
|
|
|
|
:param str[]|None symbols: Not used by gate, but parsed internally by CCXT
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.settle]: 'btc' or 'usdt' - settle currency for perpetual swap and future - default="usdt" for swap and "btc" for future
|
|
:param str [params.type]: swap, future or option, if not provided self.options['defaultType'] is used
|
|
:returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = None
|
|
symbols = self.market_symbols(symbols, None, True, True, True)
|
|
if symbols is not None:
|
|
symbolsLength = len(symbols)
|
|
if symbolsLength > 0:
|
|
market = self.market(symbols[0])
|
|
type = None
|
|
request: dict = {}
|
|
type, params = self.handle_market_type_and_params('fetchPositions', market, params)
|
|
if (type is None) or (type == 'spot'):
|
|
type = 'swap' # default to swap
|
|
if type == 'option':
|
|
if symbols is not None:
|
|
marketId = market['id']
|
|
optionParts = marketId.split('-')
|
|
request['underlying'] = self.safe_string(optionParts, 0)
|
|
else:
|
|
request, params = self.prepare_request(None, type, params)
|
|
response = None
|
|
if type == 'swap':
|
|
response = self.privateFuturesGetSettlePositions(self.extend(request, params))
|
|
elif type == 'future':
|
|
response = self.privateDeliveryGetSettlePositions(self.extend(request, params))
|
|
elif type == 'option':
|
|
response = self.privateOptionsGetPositions(self.extend(request, params))
|
|
#
|
|
# swap and future
|
|
#
|
|
# [
|
|
# {
|
|
# "value": "4.602828",
|
|
# "leverage": "0",
|
|
# "mode": "single",
|
|
# "realised_point": "0",
|
|
# "contract": "BTC_USDT",
|
|
# "entry_price": "46030.3",
|
|
# "mark_price": "46028.28",
|
|
# "history_point": "0",
|
|
# "realised_pnl": "-0.002301515",
|
|
# "close_order": null,
|
|
# "size": 1,
|
|
# "cross_leverage_limit": "0",
|
|
# "pending_orders": 0,
|
|
# "adl_ranking": 5,
|
|
# "maintenance_rate": "0.004",
|
|
# "unrealised_pnl": "-0.000202",
|
|
# "user": 5691076,
|
|
# "leverage_max": "125",
|
|
# "history_pnl": "0",
|
|
# "risk_limit": "1000000",
|
|
# "margin": "8.997698485",
|
|
# "last_close_pnl": "0",
|
|
# "liq_price": "0",
|
|
# "update_time": 1705034246,
|
|
# "update_id": 1,
|
|
# "initial_margin": "0",
|
|
# "maintenance_margin": "0",
|
|
# "open_time": 1705034246,
|
|
# "trade_max_size": "0"
|
|
# }
|
|
# ]
|
|
#
|
|
# option
|
|
#
|
|
# [
|
|
# {
|
|
# "close_order": null,
|
|
# "size": 0,
|
|
# "vega": "0.01907",
|
|
# "theta": "-3.04888",
|
|
# "gamma": "0.00001",
|
|
# "delta": "0.0011",
|
|
# "contract": "BTC_USDT-20230601-27500-C",
|
|
# "entry_price": "0",
|
|
# "unrealised_pnl": "0",
|
|
# "user": 5691076,
|
|
# "mark_price": "0.07",
|
|
# "underlying_price": "26817.27",
|
|
# "underlying": "BTC_USDT",
|
|
# "realised_pnl": "0",
|
|
# "mark_iv": "0.4339",
|
|
# "pending_orders": 0
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_positions(response, symbols)
|
|
|
|
def fetch_leverage_tiers(self, symbols: Strings = None, params={}) -> LeverageTiers:
|
|
"""
|
|
retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-futures-contracts
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-futures-contracts-2
|
|
|
|
:param str[] [symbols]: list of unified market symbols
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`, indexed by market symbols
|
|
"""
|
|
self.load_markets()
|
|
type, query = self.handle_market_type_and_params('fetchLeverageTiers', None, params)
|
|
request, requestParams = self.prepare_request(None, type, query)
|
|
if type != 'future' and type != 'swap':
|
|
raise BadRequest(self.id + ' fetchLeverageTiers only supports swap and future')
|
|
response = None
|
|
if type == 'swap':
|
|
response = self.publicFuturesGetSettleContracts(self.extend(request, requestParams))
|
|
elif type == 'future':
|
|
response = self.publicDeliveryGetSettleContracts(self.extend(request, requestParams))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchLeverageTiers() not support self market type')
|
|
#
|
|
# Perpetual swap
|
|
#
|
|
# [
|
|
# {
|
|
# "name": "BTC_USDT",
|
|
# "type": "direct",
|
|
# "quanto_multiplier": "0.0001",
|
|
# "ref_discount_rate": "0",
|
|
# "order_price_deviate": "0.5",
|
|
# "maintenance_rate": "0.005",
|
|
# "mark_type": "index",
|
|
# "last_price": "38026",
|
|
# "mark_price": "37985.6",
|
|
# "index_price": "37954.92",
|
|
# "funding_rate_indicative": "0.000219",
|
|
# "mark_price_round": "0.01",
|
|
# "funding_offset": 0,
|
|
# "in_delisting": False,
|
|
# "risk_limit_base": "1000000",
|
|
# "interest_rate": "0.0003",
|
|
# "order_price_round": "0.1",
|
|
# "order_size_min": 1,
|
|
# "ref_rebate_rate": "0.2",
|
|
# "funding_interval": 28800,
|
|
# "risk_limit_step": "1000000",
|
|
# "leverage_min": "1",
|
|
# "leverage_max": "100",
|
|
# "risk_limit_max": "8000000",
|
|
# "maker_fee_rate": "-0.00025",
|
|
# "taker_fee_rate": "0.00075",
|
|
# "funding_rate": "0.002053",
|
|
# "order_size_max": 1000000,
|
|
# "funding_next_apply": 1610035200,
|
|
# "short_users": 977,
|
|
# "config_change_time": 1609899548,
|
|
# "trade_size": 28530850594,
|
|
# "position_size": 5223816,
|
|
# "long_users": 455,
|
|
# "funding_impact_value": "60000",
|
|
# "orders_limit": 50,
|
|
# "trade_id": 10851092,
|
|
# "orderbook_id": 2129638396
|
|
# }
|
|
# ]
|
|
#
|
|
# Delivery Futures
|
|
#
|
|
# [
|
|
# {
|
|
# "name": "BTC_USDT_20200814",
|
|
# "underlying": "BTC_USDT",
|
|
# "cycle": "WEEKLY",
|
|
# "type": "direct",
|
|
# "quanto_multiplier": "0.0001",
|
|
# "mark_type": "index",
|
|
# "last_price": "9017",
|
|
# "mark_price": "9019",
|
|
# "index_price": "9005.3",
|
|
# "basis_rate": "0.185095",
|
|
# "basis_value": "13.7",
|
|
# "basis_impact_value": "100000",
|
|
# "settle_price": "0",
|
|
# "settle_price_interval": 60,
|
|
# "settle_price_duration": 1800,
|
|
# "settle_fee_rate": "0.0015",
|
|
# "expire_time": 1593763200,
|
|
# "order_price_round": "0.1",
|
|
# "mark_price_round": "0.1",
|
|
# "leverage_min": "1",
|
|
# "leverage_max": "100",
|
|
# "maintenance_rate": "1000000",
|
|
# "risk_limit_base": "140.726652109199",
|
|
# "risk_limit_step": "1000000",
|
|
# "risk_limit_max": "8000000",
|
|
# "maker_fee_rate": "-0.00025",
|
|
# "taker_fee_rate": "0.00075",
|
|
# "ref_discount_rate": "0",
|
|
# "ref_rebate_rate": "0.2",
|
|
# "order_price_deviate": "0.5",
|
|
# "order_size_min": 1,
|
|
# "order_size_max": 1000000,
|
|
# "orders_limit": 50,
|
|
# "orderbook_id": 63,
|
|
# "trade_id": 26,
|
|
# "trade_size": 435,
|
|
# "position_size": 130,
|
|
# "config_change_time": 1593158867,
|
|
# "in_delisting": False
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_leverage_tiers(response, symbols, 'name')
|
|
|
|
def fetch_market_leverage_tiers(self, symbol: str, params={}) -> List[LeverageTier]:
|
|
"""
|
|
retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes for a single market
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-risk-limit-tiers
|
|
|
|
:param str symbol: unified market symbol
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `leverage tiers structure <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
type, query = self.handle_market_type_and_params('fetchMarketLeverageTiers', market, params)
|
|
request, requestParams = self.prepare_request(market, type, query)
|
|
if type != 'future' and type != 'swap':
|
|
raise BadRequest(self.id + ' fetchMarketLeverageTiers only supports swap and future')
|
|
response = self.publicFuturesGetSettleRiskLimitTiers(self.extend(request, requestParams))
|
|
#
|
|
# [
|
|
# {
|
|
# "maintenance_rate": "0.004",
|
|
# "tier": 1,
|
|
# "initial_rate": "0.008",
|
|
# "leverage_max": "125",
|
|
# "risk_limit": "1000000"
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_market_leverage_tiers(response, market)
|
|
|
|
def parse_emulated_leverage_tiers(self, info, market=None) -> List[LeverageTier]:
|
|
marketId = self.safe_string(info, 'name')
|
|
maintenanceMarginUnit = self.safe_string(info, 'maintenance_rate') # '0.005',
|
|
leverageMax = self.safe_string(info, 'leverage_max') # '100',
|
|
riskLimitStep = self.safe_string(info, 'risk_limit_step') # '1000000',
|
|
riskLimitMax = self.safe_string(info, 'risk_limit_max') # '16000000',
|
|
initialMarginUnit = Precise.string_div('1', leverageMax)
|
|
maintenanceMarginRate = maintenanceMarginUnit
|
|
initialMarginRatio = initialMarginUnit
|
|
floor = '0'
|
|
tiers = []
|
|
while(Precise.string_lt(floor, riskLimitMax)):
|
|
cap = Precise.string_add(floor, riskLimitStep)
|
|
tiers.append({
|
|
'tier': self.parse_number(Precise.string_div(cap, riskLimitStep)),
|
|
'symbol': self.safe_symbol(marketId, market, None, 'contract'),
|
|
'currency': self.safe_string(market, 'settle'),
|
|
'minNotional': self.parse_number(floor),
|
|
'maxNotional': self.parse_number(cap),
|
|
'maintenanceMarginRate': self.parse_number(maintenanceMarginRate),
|
|
'maxLeverage': self.parse_number(Precise.string_div('1', initialMarginRatio)),
|
|
'info': info,
|
|
})
|
|
maintenanceMarginRate = Precise.string_add(maintenanceMarginRate, maintenanceMarginUnit)
|
|
initialMarginRatio = Precise.string_add(initialMarginRatio, initialMarginUnit)
|
|
floor = cap
|
|
return tiers
|
|
|
|
def parse_market_leverage_tiers(self, info, market: Market = None) -> List[LeverageTier]:
|
|
#
|
|
# [
|
|
# {
|
|
# "maintenance_rate": "0.004",
|
|
# "tier": 1,
|
|
# "initial_rate": "0.008",
|
|
# "leverage_max": "125",
|
|
# "risk_limit": "1000000"
|
|
# }
|
|
# ]
|
|
#
|
|
if not isinstance(info, list):
|
|
return self.parse_emulated_leverage_tiers(info, market)
|
|
minNotional = 0
|
|
tiers = []
|
|
for i in range(0, len(info)):
|
|
item = info[i]
|
|
maxNotional = self.safe_number(item, 'risk_limit')
|
|
tiers.append({
|
|
'tier': self.sum(i, 1),
|
|
'symbol': market['symbol'],
|
|
'currency': market['base'],
|
|
'minNotional': minNotional,
|
|
'maxNotional': maxNotional,
|
|
'maintenanceMarginRate': self.safe_number(item, 'maintenance_rate'),
|
|
'maxLeverage': self.safe_number(item, 'leverage_max'),
|
|
'info': item,
|
|
})
|
|
minNotional = maxNotional
|
|
return tiers
|
|
|
|
def repay_isolated_margin(self, symbol: str, code: str, amount, params={}):
|
|
"""
|
|
repay borrowed margin and interest
|
|
|
|
https://www.gate.com/docs/apiv4/en/#repay-a-loan
|
|
|
|
:param str symbol: unified market symbol
|
|
:param str code: unified currency code of the currency to repay
|
|
:param float amount: the amount to repay
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.mode]: 'all' or 'partial' payment mode, extra parameter required for isolated margin
|
|
:param str [params.id]: '34267567' loan id, extra parameter required for isolated margin
|
|
:returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
|
|
"""
|
|
self.load_markets()
|
|
currency = self.currency(code)
|
|
request: dict = {
|
|
'currency': currency['id'].upper(), # todo: currencies have network-junctions
|
|
'amount': self.currency_to_precision(code, amount),
|
|
}
|
|
market = self.market(symbol)
|
|
request['currency_pair'] = market['id']
|
|
request['type'] = 'repay'
|
|
response = self.privateMarginPostUniLoans(self.extend(request, params))
|
|
#
|
|
# empty response
|
|
#
|
|
return self.parse_margin_loan(response, currency)
|
|
|
|
def repay_cross_margin(self, code: str, amount, params={}):
|
|
"""
|
|
repay cross margin borrowed margin and interest
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#cross-margin-repayments
|
|
https://www.gate.com/docs/developers/apiv4/en/#borrow-or-repay
|
|
|
|
:param str code: unified currency code of the currency to repay
|
|
:param float amount: the amount to repay
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.mode]: 'all' or 'partial' payment mode, extra parameter required for isolated margin
|
|
:param str [params.id]: '34267567' loan id, extra parameter required for isolated margin
|
|
:param boolean [params.unifiedAccount]: set to True for repaying in the unified account
|
|
:returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
currency = self.currency(code)
|
|
request: dict = {
|
|
'currency': currency['id'].upper(), # todo: currencies have network-junctions
|
|
'amount': self.currency_to_precision(code, amount),
|
|
}
|
|
isUnifiedAccount = False
|
|
isUnifiedAccount, params = self.handle_option_and_params(params, 'repayCrossMargin', 'unifiedAccount')
|
|
response = None
|
|
if isUnifiedAccount:
|
|
request['type'] = 'repay'
|
|
response = self.privateUnifiedPostLoans(self.extend(request, params))
|
|
else:
|
|
response = self.privateMarginPostCrossRepayments(self.extend(request, params))
|
|
response = self.safe_dict(response, 0)
|
|
#
|
|
# [
|
|
# {
|
|
# "id": "17",
|
|
# "create_time": 1620381696159,
|
|
# "update_time": 1620381696159,
|
|
# "currency": "EOS",
|
|
# "amount": "110.553635",
|
|
# "text": "web",
|
|
# "status": 2,
|
|
# "repaid": "110.506649705159",
|
|
# "repaid_interest": "0.046985294841",
|
|
# "unpaid_interest": "0.0000074393366667"
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_margin_loan(response, currency)
|
|
|
|
def borrow_isolated_margin(self, symbol: str, code: str, amount: float, params={}):
|
|
"""
|
|
create a loan to borrow margin
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#marginuni
|
|
|
|
:param str symbol: unified market symbol, required for isolated margin
|
|
:param str code: unified currency code of the currency to borrow
|
|
:param float amount: the amount to borrow
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.rate]: '0.0002' or '0.002' extra parameter required for isolated margin
|
|
:returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
|
|
"""
|
|
self.load_markets()
|
|
currency = self.currency(code)
|
|
request: dict = {
|
|
'currency': currency['id'].upper(), # todo: currencies have network-junctions
|
|
'amount': self.currency_to_precision(code, amount),
|
|
}
|
|
response = None
|
|
market = self.market(symbol)
|
|
request['currency_pair'] = market['id']
|
|
request['type'] = 'borrow'
|
|
response = self.privateMarginPostUniLoans(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "id": "34267567",
|
|
# "create_time": "1656394778",
|
|
# "expire_time": "1657258778",
|
|
# "status": "loaned",
|
|
# "side": "borrow",
|
|
# "currency": "USDT",
|
|
# "rate": "0.0002",
|
|
# "amount": "100",
|
|
# "days": 10,
|
|
# "auto_renew": False,
|
|
# "currency_pair": "LTC_USDT",
|
|
# "left": "0",
|
|
# "repaid": "0",
|
|
# "paid_interest": "0",
|
|
# "unpaid_interest": "0.003333333333"
|
|
# }
|
|
#
|
|
return self.parse_margin_loan(response, currency)
|
|
|
|
def borrow_cross_margin(self, code: str, amount: float, params={}):
|
|
"""
|
|
create a loan to borrow margin
|
|
|
|
https://www.gate.com/docs/apiv4/en/#create-a-cross-margin-borrow-loan
|
|
https://www.gate.com/docs/developers/apiv4/en/#borrow-or-repay
|
|
|
|
:param str code: unified currency code of the currency to borrow
|
|
:param float amount: the amount to borrow
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.rate]: '0.0002' or '0.002' extra parameter required for isolated margin
|
|
:param boolean [params.unifiedAccount]: set to True for borrowing in the unified account
|
|
:returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
currency = self.currency(code)
|
|
request: dict = {
|
|
'currency': currency['id'].upper(), # todo: currencies have network-junctions
|
|
'amount': self.currency_to_precision(code, amount),
|
|
}
|
|
isUnifiedAccount = False
|
|
isUnifiedAccount, params = self.handle_option_and_params(params, 'borrowCrossMargin', 'unifiedAccount')
|
|
response = None
|
|
if isUnifiedAccount:
|
|
request['type'] = 'borrow'
|
|
response = self.privateUnifiedPostLoans(self.extend(request, params))
|
|
else:
|
|
response = self.privateMarginPostCrossLoans(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "id": "17",
|
|
# "create_time": 1620381696159,
|
|
# "update_time": 1620381696159,
|
|
# "currency": "EOS",
|
|
# "amount": "110.553635",
|
|
# "text": "web",
|
|
# "status": 2,
|
|
# "repaid": "110.506649705159",
|
|
# "repaid_interest": "0.046985294841",
|
|
# "unpaid_interest": "0.0000074393366667"
|
|
# }
|
|
#
|
|
return self.parse_margin_loan(response, currency)
|
|
|
|
def parse_margin_loan(self, info, currency: Currency = None):
|
|
#
|
|
# Cross
|
|
#
|
|
# {
|
|
# "id": "17",
|
|
# "create_time": 1620381696159,
|
|
# "update_time": 1620381696159,
|
|
# "currency": "EOS",
|
|
# "amount": "110.553635",
|
|
# "text": "web",
|
|
# "status": 2,
|
|
# "repaid": "110.506649705159",
|
|
# "repaid_interest": "0.046985294841",
|
|
# "unpaid_interest": "0.0000074393366667"
|
|
# }
|
|
#
|
|
# Isolated
|
|
#
|
|
# {
|
|
# "id": "34267567",
|
|
# "create_time": "1656394778",
|
|
# "expire_time": "1657258778",
|
|
# "status": "loaned",
|
|
# "side": "borrow",
|
|
# "currency": "USDT",
|
|
# "rate": "0.0002",
|
|
# "amount": "100",
|
|
# "days": 10,
|
|
# "auto_renew": False,
|
|
# "currency_pair": "LTC_USDT",
|
|
# "left": "0",
|
|
# "repaid": "0",
|
|
# "paid_interest": "0",
|
|
# "unpaid_interest": "0.003333333333"
|
|
# }
|
|
#
|
|
marginMode = self.safe_string_2(self.options, 'defaultMarginMode', 'marginMode', 'cross')
|
|
timestamp = self.safe_integer(info, 'create_time')
|
|
if marginMode == 'isolated':
|
|
timestamp = self.safe_timestamp(info, 'create_time')
|
|
currencyId = self.safe_string(info, 'currency')
|
|
marketId = self.safe_string(info, 'currency_pair')
|
|
return {
|
|
'id': self.safe_integer(info, 'id'),
|
|
'currency': self.safe_currency_code(currencyId, currency),
|
|
'amount': self.safe_number(info, 'amount'),
|
|
'symbol': self.safe_symbol(marketId, None, '_', 'margin'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'info': info,
|
|
}
|
|
|
|
def fetch_borrow_interest(self, code: Str = None, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[BorrowInterest]:
|
|
"""
|
|
fetch the interest owed by the user for borrowing currency for margin trading
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-interest-records
|
|
https://www.gate.com/docs/developers/apiv4/en/#interest-records-for-the-cross-margin-account
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-interest-records-2
|
|
|
|
:param str [code]: unified currency code
|
|
:param str [symbol]: unified market symbol when fetching interest in isolated markets
|
|
:param int [since]: the earliest time in ms to fetch borrow interest for
|
|
:param int [limit]: the maximum number of structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param boolean [params.unifiedAccount]: set to True for fetching borrow interest in the unified account
|
|
:returns dict[]: a list of `borrow interest structures <https://docs.ccxt.com/#/?id=borrow-interest-structure>`
|
|
"""
|
|
self.load_markets()
|
|
self.load_unified_status()
|
|
isUnifiedAccount = False
|
|
isUnifiedAccount, params = self.handle_option_and_params(params, 'fetchBorrowInterest', 'unifiedAccount')
|
|
request: dict = {}
|
|
request, params = self.handle_until_option('to', request, params)
|
|
currency = None
|
|
if code is not None:
|
|
currency = self.currency(code)
|
|
request['currency'] = currency['id']
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
if since is not None:
|
|
request['from'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
response = None
|
|
marginMode = None
|
|
marginMode, params = self.handle_margin_mode_and_params('fetchBorrowInterest', params, 'cross')
|
|
if isUnifiedAccount:
|
|
response = self.privateUnifiedGetInterestRecords(self.extend(request, params))
|
|
elif marginMode == 'isolated':
|
|
if market is not None:
|
|
request['currency_pair'] = market['id']
|
|
response = self.privateMarginGetUniInterestRecords(self.extend(request, params))
|
|
elif marginMode == 'cross':
|
|
response = self.privateMarginGetCrossInterestRecords(self.extend(request, params))
|
|
interest = self.parse_borrow_interests(response, market)
|
|
return self.filter_by_currency_since_limit(interest, code, since, limit)
|
|
|
|
def parse_borrow_interest(self, info: dict, market: Market = None) -> BorrowInterest:
|
|
marketId = self.safe_string(info, 'currency_pair')
|
|
market = self.safe_market(marketId, market)
|
|
marginMode = 'isolated' if (marketId is not None) else 'cross'
|
|
timestamp = self.safe_integer(info, 'create_time')
|
|
return {
|
|
'info': info,
|
|
'symbol': self.safe_string(market, 'symbol'),
|
|
'currency': self.safe_currency_code(self.safe_string(info, 'currency')),
|
|
'interest': self.safe_number(info, 'interest'),
|
|
'interestRate': self.safe_number(info, 'actual_rate'),
|
|
'amountBorrowed': None,
|
|
'marginMode': marginMode,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
}
|
|
|
|
def nonce(self):
|
|
return self.milliseconds() - self.options['timeDifference']
|
|
|
|
def sign(self, path, api=[], method='GET', params={}, headers=None, body=None):
|
|
authentication = api[0] # public, private
|
|
type = api[1] # spot, margin, future, delivery
|
|
query = self.omit(params, self.extract_params(path))
|
|
containsSettle = path.find('settle') > -1
|
|
if containsSettle and path.endswith('batch_cancel_orders'): # weird check to prevent $settle in php and converting {settle} to array(settle)
|
|
# special case where we need to extract the settle from the path
|
|
# but the body is an array of strings
|
|
settle = self.safe_dict(params, 0)
|
|
path = self.implode_params(path, settle)
|
|
# remove the first element from params
|
|
newParams = []
|
|
anyParams = params
|
|
for i in range(1, len(anyParams)):
|
|
newParams.append(params[i])
|
|
params = newParams
|
|
query = newParams
|
|
elif isinstance(params, list):
|
|
# endpoints like createOrders use an array instead of an object
|
|
# so we infer the settle from one of the elements
|
|
# they have to be all the same so relying on the first one is fine
|
|
first = self.safe_value(params, 0, {})
|
|
path = self.implode_params(path, first)
|
|
else:
|
|
path = self.implode_params(path, params)
|
|
endPart = '' if (path == '') else ('/' + path)
|
|
entirePath = '/' + type + endPart
|
|
if (type == 'subAccounts') or (type == 'withdrawals'):
|
|
entirePath = endPart
|
|
url = self.urls['api'][authentication][type]
|
|
if url is None:
|
|
raise NotSupported(self.id + ' does not have a testnet for the ' + type + ' market type.')
|
|
url += entirePath
|
|
if authentication == 'public':
|
|
if query:
|
|
url += '?' + self.urlencode(query)
|
|
else:
|
|
self.check_required_credentials()
|
|
queryString = ''
|
|
requiresURLEncoding = False
|
|
if ((type == 'futures') or (type == 'delivery')) and method == 'POST':
|
|
pathParts = path.split('/')
|
|
secondPart = self.safe_string(pathParts, 1, '')
|
|
requiresURLEncoding = (secondPart.find('dual') >= 0) or (secondPart.find('positions') >= 0)
|
|
if (method == 'GET') or (method == 'DELETE') or requiresURLEncoding or (method == 'PATCH'):
|
|
if query:
|
|
queryString = self.urlencode(query)
|
|
# https://github.com/ccxt/ccxt/issues/25570
|
|
if queryString.find('currencies=') >= 0 and queryString.find('%2C') >= 0:
|
|
queryString = queryString.replace('%2C', ',')
|
|
url += '?' + queryString
|
|
if method == 'PATCH':
|
|
body = self.json(query)
|
|
else:
|
|
urlQueryParams = self.safe_value(query, 'query', {})
|
|
if urlQueryParams:
|
|
queryString = self.urlencode(urlQueryParams)
|
|
url += '?' + queryString
|
|
query = self.omit(query, 'query')
|
|
body = self.json(query)
|
|
bodyPayload = '' if (body is None) else body
|
|
bodySignature = self.hash(self.encode(bodyPayload), 'sha512')
|
|
nonce = self.nonce()
|
|
timestamp = self.parse_to_int(nonce / 1000)
|
|
timestampString = str(timestamp)
|
|
signaturePath = '/api/' + self.version + entirePath
|
|
payloadArray = [method.upper(), signaturePath, queryString, bodySignature, timestampString]
|
|
# eslint-disable-next-line quotes
|
|
payload = "\n".join(payloadArray)
|
|
signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha512)
|
|
headers = {
|
|
'KEY': self.apiKey,
|
|
'Timestamp': timestampString,
|
|
'SIGN': signature,
|
|
'Content-Type': 'application/json',
|
|
}
|
|
return {'url': url, 'method': method, 'body': body, 'headers': headers}
|
|
|
|
def modify_margin_helper(self, symbol: str, amount, params={}):
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request, query = self.prepare_request(market, None, params)
|
|
request['change'] = self.number_to_string(amount)
|
|
response = None
|
|
if market['swap']:
|
|
response = self.privateFuturesPostSettlePositionsContractMargin(self.extend(request, query))
|
|
elif market['future']:
|
|
response = self.privateDeliveryPostSettlePositionsContractMargin(self.extend(request, query))
|
|
else:
|
|
raise NotSupported(self.id + ' modifyMarginHelper() not support self market type')
|
|
return self.parse_margin_modification(response, market)
|
|
|
|
def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
|
|
#
|
|
# {
|
|
# "value": "11.9257",
|
|
# "leverage": "5",
|
|
# "mode": "single",
|
|
# "realised_point": "0",
|
|
# "contract": "ETH_USDT",
|
|
# "entry_price": "1203.45",
|
|
# "mark_price": "1192.57",
|
|
# "history_point": "0",
|
|
# "realised_pnl": "-0.00577656",
|
|
# "close_order": null,
|
|
# "size": "1",
|
|
# "cross_leverage_limit": "0",
|
|
# "pending_orders": "0",
|
|
# "adl_ranking": "5",
|
|
# "maintenance_rate": "0.005",
|
|
# "unrealised_pnl": "-0.1088",
|
|
# "user": "1486602",
|
|
# "leverage_max": "100",
|
|
# "history_pnl": "0",
|
|
# "risk_limit": "1000000",
|
|
# "margin": "5.415925875",
|
|
# "last_close_pnl": "0",
|
|
# "liq_price": "665.69"
|
|
# }
|
|
#
|
|
contract = self.safe_string(data, 'contract')
|
|
market = self.safe_market(contract, market, '_', 'contract')
|
|
total = self.safe_number(data, 'margin')
|
|
return {
|
|
'info': data,
|
|
'symbol': market['symbol'],
|
|
'type': None,
|
|
'marginMode': 'isolated',
|
|
'amount': None,
|
|
'total': total,
|
|
'code': self.safe_value(market, 'quote'),
|
|
'status': 'ok',
|
|
'timestamp': None,
|
|
'datetime': None,
|
|
}
|
|
|
|
def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
|
|
"""
|
|
remove margin from a position
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#update-position-margin
|
|
https://www.gate.com/docs/developers/apiv4/en/#update-position-margin-2
|
|
|
|
:param str symbol: unified market symbol
|
|
:param float amount: the amount of margin to remove
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `margin structure <https://docs.ccxt.com/#/?id=reduce-margin-structure>`
|
|
"""
|
|
return self.modify_margin_helper(symbol, -amount, params)
|
|
|
|
def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
|
|
"""
|
|
add margin
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#update-position-margin
|
|
https://www.gate.com/docs/developers/apiv4/en/#update-position-margin-2
|
|
|
|
:param str symbol: unified market symbol
|
|
:param float amount: amount of margin to add
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `margin structure <https://docs.ccxt.com/#/?id=add-margin-structure>`
|
|
"""
|
|
return self.modify_margin_helper(symbol, amount, params)
|
|
|
|
def fetch_open_interest_history(self, symbol: str, timeframe='5m', since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
Retrieves the open interest of a currency
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#futures-stats
|
|
|
|
:param str symbol: Unified CCXT market symbol
|
|
:param str timeframe: "5m", "15m", "30m", "1h", "4h", "1d"
|
|
:param int [since]: the time(ms) of the earliest record to retrieve unix timestamp
|
|
:param int [limit]: default 30
|
|
:param dict [params]: exchange specific parameters
|
|
:param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
:returns dict} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure:
|
|
"""
|
|
self.load_markets()
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchOpenInterestHistory', 'paginate', False)
|
|
if paginate:
|
|
return self.fetch_paginated_call_deterministic('fetchOpenInterestHistory', symbol, since, limit, timeframe, params, 100)
|
|
market = self.market(symbol)
|
|
if not market['swap']:
|
|
raise BadRequest(self.id + ' fetchOpenInterest() supports swap markets only')
|
|
request: dict = {
|
|
'contract': market['id'],
|
|
'settle': market['settleId'],
|
|
'interval': self.safe_string(self.timeframes, timeframe, timeframe),
|
|
}
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
if since is not None:
|
|
request['from'] = since
|
|
response = self.publicFuturesGetSettleContractStats(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "long_liq_size": "0",
|
|
# "short_liq_size": "0",
|
|
# "short_liq_usd": "0",
|
|
# "lsr_account": "3.2808988764045",
|
|
# "mark_price": "0.34619",
|
|
# "top_lsr_size": "0",
|
|
# "time": "1674057000",
|
|
# "short_liq_amount": "0",
|
|
# "long_liq_amount": "0",
|
|
# "open_interest_usd": "9872386.7775",
|
|
# "top_lsr_account": "0",
|
|
# "open_interest": "2851725",
|
|
# "long_liq_usd": "0",
|
|
# "lsr_taker": "9.3765153315902"
|
|
# },
|
|
# ...
|
|
# ]
|
|
#
|
|
return self.parse_open_interests_history(response, market, since, limit)
|
|
|
|
def parse_open_interest(self, interest, market: Market = None):
|
|
#
|
|
# {
|
|
# "long_liq_size": "0",
|
|
# "short_liq_size": "0",
|
|
# "short_liq_usd": "0",
|
|
# "lsr_account": "3.2808988764045",
|
|
# "mark_price": "0.34619",
|
|
# "top_lsr_size": "0",
|
|
# "time": "1674057000",
|
|
# "short_liq_amount": "0",
|
|
# "long_liq_amount": "0",
|
|
# "open_interest_usd": "9872386.7775",
|
|
# "top_lsr_account": "0",
|
|
# "open_interest": "2851725",
|
|
# "long_liq_usd": "0",
|
|
# "lsr_taker": "9.3765153315902"
|
|
# }
|
|
#
|
|
timestamp = self.safe_timestamp(interest, 'time')
|
|
return {
|
|
'symbol': self.safe_string(market, 'symbol'),
|
|
'openInterestAmount': self.safe_number(interest, 'open_interest'),
|
|
'openInterestValue': self.safe_number(interest, 'open_interest_usd'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'info': interest,
|
|
}
|
|
|
|
def fetch_settlement_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
fetches historical settlement records
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-settlement-history-2
|
|
|
|
:param str symbol: unified market symbol of the settlement history, required on gate
|
|
:param int [since]: timestamp in ms
|
|
:param int [limit]: number of records
|
|
:param dict [params]: exchange specific params
|
|
:returns dict[]: a list of `settlement history objects <https://docs.ccxt.com/#/?id=settlement-history-structure>`
|
|
"""
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' fetchSettlementHistory() requires a symbol argument')
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
type = None
|
|
type, params = self.handle_market_type_and_params('fetchSettlementHistory', market, params)
|
|
if type != 'option':
|
|
raise NotSupported(self.id + ' fetchSettlementHistory() supports option markets only')
|
|
marketId = market['id']
|
|
optionParts = marketId.split('-')
|
|
request: dict = {
|
|
'underlying': self.safe_string(optionParts, 0),
|
|
}
|
|
if since is not None:
|
|
request['from'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
response = self.publicOptionsGetSettlements(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "time": 1685952000,
|
|
# "profit": "18.266806892718",
|
|
# "settle_price": "26826.68068927182",
|
|
# "fee": "0.040240021034",
|
|
# "contract": "BTC_USDT-20230605-25000-C",
|
|
# "strike_price": "25000"
|
|
# }
|
|
# ]
|
|
#
|
|
settlements = self.parse_settlements(response, market)
|
|
sorted = self.sort_by(settlements, 'timestamp')
|
|
return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
|
|
|
|
def fetch_my_settlement_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
fetches historical settlement records of the user
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-my-options-settlements
|
|
|
|
:param str symbol: unified market symbol of the settlement history
|
|
:param int [since]: timestamp in ms
|
|
:param int [limit]: number of records
|
|
:param dict [params]: exchange specific params
|
|
:returns dict[]: a list of [settlement history objects]
|
|
"""
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' fetchMySettlementHistory() requires a symbol argument')
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
type = None
|
|
type, params = self.handle_market_type_and_params('fetchMySettlementHistory', market, params)
|
|
if type != 'option':
|
|
raise NotSupported(self.id + ' fetchMySettlementHistory() supports option markets only')
|
|
marketId = market['id']
|
|
optionParts = marketId.split('-')
|
|
request: dict = {
|
|
'underlying': self.safe_string(optionParts, 0),
|
|
'contract': marketId,
|
|
}
|
|
if since is not None:
|
|
request['from'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
response = self.privateOptionsGetMySettlements(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "size": -1,
|
|
# "settle_profit": "0",
|
|
# "contract": "BTC_USDT-20220624-26000-C",
|
|
# "strike_price": "26000",
|
|
# "time": 1656057600,
|
|
# "settle_price": "20917.461281337048",
|
|
# "underlying": "BTC_USDT",
|
|
# "realised_pnl": "-0.00116042",
|
|
# "fee": "0"
|
|
# }
|
|
# ]
|
|
#
|
|
result = self.safe_value(response, 'result', {})
|
|
data = self.safe_value(result, 'list', [])
|
|
settlements = self.parse_settlements(data, market)
|
|
sorted = self.sort_by(settlements, 'timestamp')
|
|
return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit)
|
|
|
|
def parse_settlement(self, settlement, market):
|
|
#
|
|
# fetchSettlementHistory
|
|
#
|
|
# {
|
|
# "time": 1685952000,
|
|
# "profit": "18.266806892718",
|
|
# "settle_price": "26826.68068927182",
|
|
# "fee": "0.040240021034",
|
|
# "contract": "BTC_USDT-20230605-25000-C",
|
|
# "strike_price": "25000"
|
|
# }
|
|
#
|
|
# fetchMySettlementHistory
|
|
#
|
|
# {
|
|
# "size": -1,
|
|
# "settle_profit": "0",
|
|
# "contract": "BTC_USDT-20220624-26000-C",
|
|
# "strike_price": "26000",
|
|
# "time": 1656057600,
|
|
# "settle_price": "20917.461281337048",
|
|
# "underlying": "BTC_USDT",
|
|
# "realised_pnl": "-0.00116042",
|
|
# "fee": "0"
|
|
# }
|
|
#
|
|
timestamp = self.safe_timestamp(settlement, 'time')
|
|
marketId = self.safe_string(settlement, 'contract')
|
|
return {
|
|
'info': settlement,
|
|
'symbol': self.safe_symbol(marketId, market),
|
|
'price': self.safe_number(settlement, 'settle_price'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
}
|
|
|
|
def parse_settlements(self, settlements, market):
|
|
#
|
|
# fetchSettlementHistory
|
|
#
|
|
# [
|
|
# {
|
|
# "time": 1685952000,
|
|
# "profit": "18.266806892718",
|
|
# "settle_price": "26826.68068927182",
|
|
# "fee": "0.040240021034",
|
|
# "contract": "BTC_USDT-20230605-25000-C",
|
|
# "strike_price": "25000"
|
|
# }
|
|
# ]
|
|
#
|
|
# fetchMySettlementHistory
|
|
#
|
|
# [
|
|
# {
|
|
# "size": -1,
|
|
# "settle_profit": "0",
|
|
# "contract": "BTC_USDT-20220624-26000-C",
|
|
# "strike_price": "26000",
|
|
# "time": 1656057600,
|
|
# "settle_price": "20917.461281337048",
|
|
# "underlying": "BTC_USDT",
|
|
# "realised_pnl": "-0.00116042",
|
|
# "fee": "0"
|
|
# }
|
|
# ]
|
|
#
|
|
result = []
|
|
for i in range(0, len(settlements)):
|
|
result.append(self.parse_settlement(settlements[i], market))
|
|
return result
|
|
|
|
def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
|
|
"""
|
|
fetch the history of changes, actions done by the user or operations that altered the balance of the user
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#query-account-book
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-margin-account-balance-change-history
|
|
https://www.gate.com/docs/developers/apiv4/en/#query-account-book-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#query-account-book-3
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-account-changing-history
|
|
|
|
:param str [code]: unified currency code
|
|
:param int [since]: timestamp in ms of the earliest ledger entry
|
|
:param int [limit]: max number of ledger entries to return
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param int [params.until]: end time in ms
|
|
:param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
:returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
|
|
"""
|
|
self.load_markets()
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchLedger', 'paginate')
|
|
if paginate:
|
|
return self.fetch_paginated_call_dynamic('fetchLedger', code, since, limit, params)
|
|
type = None
|
|
currency = None
|
|
response = None
|
|
request: dict = {}
|
|
type, params = self.handle_market_type_and_params('fetchLedger', None, params)
|
|
if (type == 'spot') or (type == 'margin'):
|
|
if code is not None:
|
|
currency = self.currency(code)
|
|
request['currency'] = currency['id'] # todo: currencies have network-junctions
|
|
if (type == 'swap') or (type == 'future'):
|
|
defaultSettle = 'usdt' if (type == 'swap') else 'btc'
|
|
settle = self.safe_string_lower(params, 'settle', defaultSettle)
|
|
params = self.omit(params, 'settle')
|
|
request['settle'] = settle
|
|
if since is not None:
|
|
request['from'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
request, params = self.handle_until_option('to', request, params)
|
|
if type == 'spot':
|
|
response = self.privateSpotGetAccountBook(self.extend(request, params))
|
|
elif type == 'margin':
|
|
response = self.privateMarginGetAccountBook(self.extend(request, params))
|
|
elif type == 'swap':
|
|
response = self.privateFuturesGetSettleAccountBook(self.extend(request, params))
|
|
elif type == 'future':
|
|
response = self.privateDeliveryGetSettleAccountBook(self.extend(request, params))
|
|
elif type == 'option':
|
|
response = self.privateOptionsGetAccountBook(self.extend(request, params))
|
|
#
|
|
# spot
|
|
#
|
|
# [
|
|
# {
|
|
# "id": "123456",
|
|
# "time": 1547633726123,
|
|
# "currency": "BTC",
|
|
# "change": "1.03",
|
|
# "balance": "4.59316525194",
|
|
# "type": "margin_in"
|
|
# }
|
|
# ]
|
|
#
|
|
# margin
|
|
#
|
|
# [
|
|
# {
|
|
# "id": "123456",
|
|
# "time": "1547633726",
|
|
# "time_ms": 1547633726123,
|
|
# "currency": "BTC",
|
|
# "currency_pair": "BTC_USDT",
|
|
# "change": "1.03",
|
|
# "balance": "4.59316525194"
|
|
# }
|
|
# ]
|
|
#
|
|
# swap and future
|
|
#
|
|
# [
|
|
# {
|
|
# "time": 1682294400.123456,
|
|
# "change": "0.000010152188",
|
|
# "balance": "4.59316525194",
|
|
# "text": "ETH_USD:6086261",
|
|
# "type": "fee"
|
|
# }
|
|
# ]
|
|
#
|
|
# option
|
|
#
|
|
# [
|
|
# {
|
|
# "time": 1685594770,
|
|
# "change": "3.33",
|
|
# "balance": "29.87911771",
|
|
# "text": "BTC_USDT-20230602-26500-C:2611026125",
|
|
# "type": "prem"
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_ledger(response, currency, since, limit)
|
|
|
|
def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
|
|
#
|
|
# spot
|
|
#
|
|
# {
|
|
# "id": "123456",
|
|
# "time": 1547633726123,
|
|
# "currency": "BTC",
|
|
# "change": "1.03",
|
|
# "balance": "4.59316525194",
|
|
# "type": "margin_in"
|
|
# }
|
|
#
|
|
# margin
|
|
#
|
|
# {
|
|
# "id": "123456",
|
|
# "time": "1547633726",
|
|
# "time_ms": 1547633726123,
|
|
# "currency": "BTC",
|
|
# "currency_pair": "BTC_USDT",
|
|
# "change": "1.03",
|
|
# "balance": "4.59316525194"
|
|
# }
|
|
#
|
|
# swap and future
|
|
#
|
|
# {
|
|
# "time": 1682294400.123456,
|
|
# "change": "0.000010152188",
|
|
# "balance": "4.59316525194",
|
|
# "text": "ETH_USD:6086261",
|
|
# "type": "fee"
|
|
# }
|
|
#
|
|
# option
|
|
#
|
|
# {
|
|
# "time": 1685594770,
|
|
# "change": "3.33",
|
|
# "balance": "29.87911771",
|
|
# "text": "BTC_USDT-20230602-26500-C:2611026125",
|
|
# "type": "prem"
|
|
# }
|
|
#
|
|
direction = None
|
|
amount = self.safe_string(item, 'change')
|
|
if Precise.string_lt(amount, '0'):
|
|
direction = 'out'
|
|
amount = Precise.string_abs(amount)
|
|
else:
|
|
direction = 'in'
|
|
currencyId = self.safe_string(item, 'currency')
|
|
currency = self.safe_currency(currencyId, currency)
|
|
type = self.safe_string(item, 'type')
|
|
rawTimestamp = self.safe_string(item, 'time')
|
|
timestamp = None
|
|
if len(rawTimestamp) > 10:
|
|
timestamp = int(rawTimestamp)
|
|
else:
|
|
timestamp = int(rawTimestamp) * 1000
|
|
balanceString = self.safe_string(item, 'balance')
|
|
changeString = self.safe_string(item, 'change')
|
|
before = self.parse_number(Precise.string_sub(balanceString, changeString))
|
|
return self.safe_ledger_entry({
|
|
'info': item,
|
|
'id': self.safe_string(item, 'id'),
|
|
'direction': direction,
|
|
'account': None,
|
|
'referenceAccount': None,
|
|
'referenceId': None,
|
|
'type': self.parse_ledger_entry_type(type),
|
|
'currency': self.safe_currency_code(currencyId, currency),
|
|
'amount': self.parse_number(amount),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'before': before,
|
|
'after': self.safe_number(item, 'balance'),
|
|
'status': None,
|
|
'fee': None,
|
|
}, currency)
|
|
|
|
def parse_ledger_entry_type(self, type):
|
|
ledgerType: dict = {
|
|
'deposit': 'deposit',
|
|
'withdraw': 'withdrawal',
|
|
'sub_account_transfer': 'transfer',
|
|
'margin_in': 'transfer',
|
|
'margin_out': 'transfer',
|
|
'margin_funding_in': 'transfer',
|
|
'margin_funding_out': 'transfer',
|
|
'cross_margin_in': 'transfer',
|
|
'cross_margin_out': 'transfer',
|
|
'copy_trading_in': 'transfer',
|
|
'copy_trading_out': 'transfer',
|
|
'quant_in': 'transfer',
|
|
'quant_out': 'transfer',
|
|
'futures_in': 'transfer',
|
|
'futures_out': 'transfer',
|
|
'delivery_in': 'transfer',
|
|
'delivery_out': 'transfer',
|
|
'new_order': 'trade',
|
|
'order_fill': 'trade',
|
|
'referral_fee': 'rebate',
|
|
'order_fee': 'fee',
|
|
'interest': 'interest',
|
|
'lend': 'loan',
|
|
'redeem': 'loan',
|
|
'profit': 'interest',
|
|
'flash_swap_buy': 'trade',
|
|
'flash_swap_sell': 'trade',
|
|
'unknown': 'unknown',
|
|
'set': 'settlement',
|
|
'prem': 'trade',
|
|
'point_refr': 'rebate',
|
|
'point_fee': 'fee',
|
|
'point_dnw': 'deposit/withdraw',
|
|
'fund': 'fee',
|
|
'refr': 'rebate',
|
|
'fee': 'fee',
|
|
'pnl': 'trade',
|
|
'dnw': 'deposit/withdraw',
|
|
}
|
|
return self.safe_string(ledgerType, type, type)
|
|
|
|
def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
|
|
"""
|
|
set dual/hedged mode to True or False for a swap market, make sure all positions are closed and no orders are open before setting dual mode
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#enable-or-disable-dual-mode
|
|
|
|
:param bool hedged: set to True to enable dual mode
|
|
:param str|None symbol: if passed, dual mode is set for all markets with the same settle currency
|
|
:param dict params: extra parameters specific to the exchange API endpoint
|
|
:param str params['settle']: settle currency
|
|
:returns dict: response from the exchange
|
|
"""
|
|
market = self.market(symbol) if (symbol is not None) else None
|
|
request, query = self.prepare_request(market, 'swap', params)
|
|
request['dual_mode'] = hedged
|
|
return self.privateFuturesPostSettleDualMode(self.extend(request, query))
|
|
|
|
def fetch_underlying_assets(self, params={}):
|
|
"""
|
|
fetches the market ids of underlying assets for a specific contract market type
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-underlyings
|
|
|
|
:param dict [params]: exchange specific params
|
|
:param str [params.type]: the contract market type, 'option', 'swap' or 'future', the default is 'option'
|
|
:returns dict[]: a list of `underlying assets <https://docs.ccxt.com/#/?id=underlying-assets-structure>`
|
|
"""
|
|
self.load_markets()
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('fetchUnderlyingAssets', None, params)
|
|
if (marketType is None) or (marketType == 'spot'):
|
|
marketType = 'option'
|
|
if marketType != 'option':
|
|
raise NotSupported(self.id + ' fetchUnderlyingAssets() supports option markets only')
|
|
response = self.publicOptionsGetUnderlyings(params)
|
|
#
|
|
# [
|
|
# {
|
|
# "index_time": "1646915796",
|
|
# "name": "BTC_USDT",
|
|
# "index_price": "39142.73"
|
|
# }
|
|
# ]
|
|
#
|
|
underlyings = []
|
|
for i in range(0, len(response)):
|
|
underlying = response[i]
|
|
name = self.safe_string(underlying, 'name')
|
|
if name is not None:
|
|
underlyings.append(name)
|
|
return underlyings
|
|
|
|
def fetch_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
retrieves the public liquidations of a trading pair
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#retrieve-liquidation-history
|
|
|
|
:param str symbol: unified CCXT market symbol
|
|
:param int [since]: the earliest time in ms to fetch liquidations for
|
|
:param int [limit]: the maximum number of liquidation structures to retrieve
|
|
:param dict [params]: exchange specific parameters for the exchange API endpoint
|
|
:param int [params.until]: timestamp in ms of the latest liquidation
|
|
:returns dict: an array of `liquidation structures <https://docs.ccxt.com/#/?id=liquidation-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
if not market['swap']:
|
|
raise NotSupported(self.id + ' fetchLiquidations() supports swap markets only')
|
|
request: dict = {
|
|
'settle': market['settleId'],
|
|
'contract': market['id'],
|
|
}
|
|
if since is not None:
|
|
request['from'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
request, params = self.handle_until_option('to', request, params)
|
|
response = self.publicFuturesGetSettleLiqOrders(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "contract": "BTC_USDT",
|
|
# "left": 0,
|
|
# "size": -165,
|
|
# "fill_price": "28070",
|
|
# "order_price": "28225",
|
|
# "time": 1696736132
|
|
# },
|
|
# ]
|
|
#
|
|
return self.parse_liquidations(response, market, since, limit)
|
|
|
|
def fetch_my_liquidations(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
retrieves the users liquidated positions
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-liquidation-history
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-liquidation-history-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-user-s-liquidation-history-of-specified-underlying
|
|
|
|
:param str symbol: unified CCXT market symbol
|
|
:param int [since]: the earliest time in ms to fetch liquidations for
|
|
:param int [limit]: the maximum number of liquidation structures to retrieve
|
|
:param dict [params]: exchange specific parameters for the exchange API endpoint
|
|
:returns dict: an array of `liquidation structures <https://docs.ccxt.com/#/?id=liquidation-structure>`
|
|
"""
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' fetchMyLiquidations() requires a symbol argument')
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'contract': market['id'],
|
|
}
|
|
response = None
|
|
if (market['swap']) or (market['future']):
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
request['settle'] = market['settleId']
|
|
elif market['option']:
|
|
marketId = market['id']
|
|
optionParts = marketId.split('-')
|
|
request['underlying'] = self.safe_string(optionParts, 0)
|
|
if market['swap']:
|
|
response = self.privateFuturesGetSettleLiquidates(self.extend(request, params))
|
|
elif market['future']:
|
|
response = self.privateDeliveryGetSettleLiquidates(self.extend(request, params))
|
|
elif market['option']:
|
|
response = self.privateOptionsGetPositionClose(self.extend(request, params))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchMyLiquidations() does not support ' + market['type'] + ' orders')
|
|
#
|
|
# swap and future
|
|
#
|
|
# [
|
|
# {
|
|
# "time": 1548654951,
|
|
# "contract": "BTC_USDT",
|
|
# "size": 600,
|
|
# "leverage": "25",
|
|
# "margin": "0.006705256878",
|
|
# "entry_price": "3536.123",
|
|
# "liq_price": "3421.54",
|
|
# "mark_price": "3420.27",
|
|
# "order_id": 317393847,
|
|
# "order_price": "3405",
|
|
# "fill_price": "3424",
|
|
# "left": 0
|
|
# }
|
|
# ]
|
|
#
|
|
# option
|
|
#
|
|
# [
|
|
# {
|
|
# "time": 1631764800,
|
|
# "pnl": "-42914.291",
|
|
# "settle_size": "-10001",
|
|
# "side": "short",
|
|
# "contract": "BTC_USDT-20210916-5000-C",
|
|
# "text": "settled"
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_liquidations(response, market, since, limit)
|
|
|
|
def parse_liquidation(self, liquidation, market: Market = None):
|
|
#
|
|
# fetchLiquidations
|
|
#
|
|
# {
|
|
# "contract": "BTC_USDT",
|
|
# "left": 0,
|
|
# "size": -165,
|
|
# "fill_price": "28070",
|
|
# "order_price": "28225",
|
|
# "time": 1696736132
|
|
# }
|
|
#
|
|
# swap and future: fetchMyLiquidations
|
|
#
|
|
# {
|
|
# "time": 1548654951,
|
|
# "contract": "BTC_USDT",
|
|
# "size": 600,
|
|
# "leverage": "25",
|
|
# "margin": "0.006705256878",
|
|
# "entry_price": "3536.123",
|
|
# "liq_price": "3421.54",
|
|
# "mark_price": "3420.27",
|
|
# "order_id": 317393847,
|
|
# "order_price": "3405",
|
|
# "fill_price": "3424",
|
|
# "left": 0
|
|
# }
|
|
#
|
|
# option: fetchMyLiquidations
|
|
#
|
|
# {
|
|
# "time": 1631764800,
|
|
# "pnl": "-42914.291",
|
|
# "settle_size": "-10001",
|
|
# "side": "short",
|
|
# "contract": "BTC_USDT-20210916-5000-C",
|
|
# "text": "settled"
|
|
# }
|
|
#
|
|
marketId = self.safe_string(liquidation, 'contract')
|
|
timestamp = self.safe_timestamp(liquidation, 'time')
|
|
size = self.safe_string_2(liquidation, 'size', 'settle_size')
|
|
left = self.safe_string(liquidation, 'left', '0')
|
|
contractsString = Precise.string_abs(Precise.string_sub(size, left))
|
|
contractSizeString = self.safe_string(market, 'contractSize')
|
|
priceString = self.safe_string_2(liquidation, 'liq_price', 'fill_price')
|
|
baseValueString = Precise.string_mul(contractsString, contractSizeString)
|
|
quoteValueString = self.safe_string(liquidation, 'pnl')
|
|
if quoteValueString is None:
|
|
quoteValueString = Precise.string_mul(baseValueString, priceString)
|
|
# --- derive side ---
|
|
# 1) options payload has explicit 'side': 'long' | 'short'
|
|
optPos = self.safe_string_lower(liquidation, 'side')
|
|
side: Str = None
|
|
if optPos == 'long':
|
|
side = 'buy'
|
|
elif optPos == 'short':
|
|
side = 'sell'
|
|
else:
|
|
if size is not None: # 2) futures/perpetual(and fallback for options): infer from size
|
|
if Precise.string_gt(size, '0'):
|
|
side = 'buy'
|
|
elif Precise.string_lt(size, '0'):
|
|
side = 'sell'
|
|
return self.safe_liquidation({
|
|
'info': liquidation,
|
|
'symbol': self.safe_symbol(marketId, market),
|
|
'contracts': self.parse_number(contractsString),
|
|
'contractSize': self.parse_number(contractSizeString),
|
|
'price': self.parse_number(priceString),
|
|
'side': side,
|
|
'baseValue': self.parse_number(baseValueString),
|
|
'quoteValue': self.parse_number(Precise.string_abs(quoteValueString)),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
})
|
|
|
|
def fetch_greeks(self, symbol: str, params={}) -> Greeks:
|
|
"""
|
|
fetches an option contracts greeks, financial metrics used to measure the factors that affect the price of an options contract
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-tickers-of-options-contracts
|
|
|
|
:param str symbol: unified symbol of the market to fetch greeks for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `greeks structure <https://docs.ccxt.com/#/?id=greeks-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'underlying': market['info']['underlying'],
|
|
}
|
|
response = self.publicOptionsGetTickers(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "vega": "1.78992",
|
|
# "leverage": "6.2096777055417",
|
|
# "ask_iv": "0.6245",
|
|
# "delta": "-0.69397",
|
|
# "last_price": "0",
|
|
# "theta": "-2.5723",
|
|
# "bid1_price": "222.9",
|
|
# "mark_iv": "0.5909",
|
|
# "name": "ETH_USDT-20231201-2300-P",
|
|
# "bid_iv": "0.5065",
|
|
# "ask1_price": "243.6",
|
|
# "mark_price": "236.57",
|
|
# "position_size": 0,
|
|
# "bid1_size": 368,
|
|
# "ask1_size": -335,
|
|
# "gamma": "0.00116"
|
|
# },
|
|
# ]
|
|
#
|
|
marketId = market['id']
|
|
for i in range(0, len(response)):
|
|
entry = response[i]
|
|
entryMarketId = self.safe_string(entry, 'name')
|
|
if entryMarketId == marketId:
|
|
return self.parse_greeks(entry, market)
|
|
return None
|
|
|
|
def parse_greeks(self, greeks: dict, market: Market = None) -> Greeks:
|
|
#
|
|
# {
|
|
# "vega": "1.78992",
|
|
# "leverage": "6.2096777055417",
|
|
# "ask_iv": "0.6245",
|
|
# "delta": "-0.69397",
|
|
# "last_price": "0",
|
|
# "theta": "-2.5723",
|
|
# "bid1_price": "222.9",
|
|
# "mark_iv": "0.5909",
|
|
# "name": "ETH_USDT-20231201-2300-P",
|
|
# "bid_iv": "0.5065",
|
|
# "ask1_price": "243.6",
|
|
# "mark_price": "236.57",
|
|
# "position_size": 0,
|
|
# "bid1_size": 368,
|
|
# "ask1_size": -335,
|
|
# "gamma": "0.00116"
|
|
# }
|
|
#
|
|
marketId = self.safe_string(greeks, 'name')
|
|
symbol = self.safe_symbol(marketId, market)
|
|
return {
|
|
'symbol': symbol,
|
|
'timestamp': None,
|
|
'datetime': None,
|
|
'delta': self.safe_number(greeks, 'delta'),
|
|
'gamma': self.safe_number(greeks, 'gamma'),
|
|
'theta': self.safe_number(greeks, 'theta'),
|
|
'vega': self.safe_number(greeks, 'vega'),
|
|
'rho': None,
|
|
'bidSize': self.safe_number(greeks, 'bid1_size'),
|
|
'askSize': self.safe_number(greeks, 'ask1_size'),
|
|
'bidImpliedVolatility': self.safe_number(greeks, 'bid_iv'),
|
|
'askImpliedVolatility': self.safe_number(greeks, 'ask_iv'),
|
|
'markImpliedVolatility': self.safe_number(greeks, 'mark_iv'),
|
|
'bidPrice': self.safe_number(greeks, 'bid1_price'),
|
|
'askPrice': self.safe_number(greeks, 'ask1_price'),
|
|
'markPrice': self.safe_number(greeks, 'mark_price'),
|
|
'lastPrice': self.safe_number(greeks, 'last_price'),
|
|
'underlyingPrice': self.parse_number(market['info']['underlying_price']),
|
|
'info': greeks,
|
|
}
|
|
|
|
def close_position(self, symbol: str, side: OrderSide = None, params={}) -> Order:
|
|
"""
|
|
closes open positions for a market
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-a-futures-order
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-a-futures-order-2
|
|
https://www.gate.com/docs/developers/apiv4/en/#create-an-options-order
|
|
|
|
:param str symbol: Unified CCXT market symbol
|
|
:param str side: 'buy' or 'sell'
|
|
:param dict [params]: extra parameters specific to the okx api endpoint
|
|
:returns dict[]: `A list of position structures <https://docs.ccxt.com/#/?id=position-structure>`
|
|
"""
|
|
request: dict = {
|
|
'close': True,
|
|
}
|
|
params = self.extend(request, params)
|
|
if side is None:
|
|
side = '' # side is not used but needs to be present, otherwise crashes in php
|
|
return self.create_order(symbol, 'market', side, 0, None, params)
|
|
|
|
def fetch_leverage(self, symbol: str, params={}) -> Leverage:
|
|
"""
|
|
fetch the set leverage for a market
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-unified-account-information
|
|
https://www.gate.com/docs/developers/apiv4/en/#get-detail-of-lending-market
|
|
https://www.gate.com/docs/developers/apiv4/en/#query-one-single-margin-currency-pair-deprecated
|
|
|
|
:param str symbol: unified market symbol
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param boolean [params.unified]: default False, set to True for fetching the unified accounts leverage
|
|
:returns dict: a `leverage structure <https://docs.ccxt.com/#/?id=leverage-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = None
|
|
if symbol is not None:
|
|
# unified account does not require a symbol
|
|
market = self.market(symbol)
|
|
request: dict = {}
|
|
response = None
|
|
isUnified = self.safe_bool(params, 'unified')
|
|
params = self.omit(params, 'unified')
|
|
if market['spot']:
|
|
request['currency_pair'] = market['id']
|
|
if isUnified:
|
|
response = self.publicMarginGetUniCurrencyPairsCurrencyPair(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "currency_pair": "BTC_USDT",
|
|
# "base_min_borrow_amount": "0.0001",
|
|
# "quote_min_borrow_amount": "1",
|
|
# "leverage": "10"
|
|
# }
|
|
#
|
|
else:
|
|
response = self.publicMarginGetCurrencyPairsCurrencyPair(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "id": "BTC_USDT",
|
|
# "base": "BTC",
|
|
# "quote": "USDT",
|
|
# "leverage": 10,
|
|
# "min_base_amount": "0.0001",
|
|
# "min_quote_amount": "1",
|
|
# "max_quote_amount": "40000000",
|
|
# "status": 1
|
|
# }
|
|
#
|
|
elif isUnified:
|
|
response = self.privateUnifiedGetAccounts(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "user_id": 10001,
|
|
# "locked": False,
|
|
# "balances": {
|
|
# "ETH": {
|
|
# "available": "0",
|
|
# "freeze": "0",
|
|
# "borrowed": "0.075393666654",
|
|
# "negative_liab": "0",
|
|
# "futures_pos_liab": "0",
|
|
# "equity": "1016.1",
|
|
# "total_freeze": "0",
|
|
# "total_liab": "0"
|
|
# },
|
|
# "POINT": {
|
|
# "available": "9999999999.017023138734",
|
|
# "freeze": "0",
|
|
# "borrowed": "0",
|
|
# "negative_liab": "0",
|
|
# "futures_pos_liab": "0",
|
|
# "equity": "12016.1",
|
|
# "total_freeze": "0",
|
|
# "total_liab": "0"
|
|
# },
|
|
# "USDT": {
|
|
# "available": "0.00000062023",
|
|
# "freeze": "0",
|
|
# "borrowed": "0",
|
|
# "negative_liab": "0",
|
|
# "futures_pos_liab": "0",
|
|
# "equity": "16.1",
|
|
# "total_freeze": "0",
|
|
# "total_liab": "0"
|
|
# }
|
|
# },
|
|
# "total": "230.94621713",
|
|
# "borrowed": "161.66395521",
|
|
# "total_initial_margin": "1025.0524665088",
|
|
# "total_margin_balance": "3382495.944473949183",
|
|
# "total_maintenance_margin": "205.01049330176",
|
|
# "total_initial_margin_rate": "3299.827135672679",
|
|
# "total_maintenance_margin_rate": "16499.135678363399",
|
|
# "total_available_margin": "3381470.892007440383",
|
|
# "unified_account_total": "3381470.892007440383",
|
|
# "unified_account_total_liab": "0",
|
|
# "unified_account_total_equity": "100016.1",
|
|
# "leverage": "2"
|
|
# }
|
|
#
|
|
else:
|
|
raise NotSupported(self.id + ' fetchLeverage() does not support ' + market['type'] + ' markets')
|
|
return self.parse_leverage(response, market)
|
|
|
|
def fetch_leverages(self, symbols: Strings = None, params={}) -> Leverages:
|
|
"""
|
|
fetch the set leverage for all leverage markets, only spot margin is supported on gate
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-lending-markets
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-supported-currency-pairs-supported-in-margin-trading-deprecated
|
|
|
|
:param str[] symbols: a list of unified market symbols
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param boolean [params.unified]: default False, set to True for fetching unified account leverages
|
|
:returns dict: a list of `leverage structures <https://docs.ccxt.com/#/?id=leverage-structure>`
|
|
"""
|
|
self.load_markets()
|
|
symbols = self.market_symbols(symbols)
|
|
response = None
|
|
isUnified = self.safe_bool(params, 'unified')
|
|
params = self.omit(params, 'unified')
|
|
marketIdRequest = 'id'
|
|
if isUnified:
|
|
marketIdRequest = 'currency_pair'
|
|
response = self.publicMarginGetUniCurrencyPairs(params)
|
|
#
|
|
# [
|
|
# {
|
|
# "currency_pair": "1INCH_USDT",
|
|
# "base_min_borrow_amount": "8",
|
|
# "quote_min_borrow_amount": "1",
|
|
# "leverage": "3"
|
|
# },
|
|
# ]
|
|
#
|
|
else:
|
|
response = self.publicMarginGetCurrencyPairs(params)
|
|
#
|
|
# [
|
|
# {
|
|
# "id": "1CAT_USDT",
|
|
# "base": "1CAT",
|
|
# "quote": "USDT",
|
|
# "leverage": 3,
|
|
# "min_base_amount": "71",
|
|
# "min_quote_amount": "1",
|
|
# "max_quote_amount": "10000",
|
|
# "status": 1
|
|
# },
|
|
# ]
|
|
#
|
|
return self.parse_leverages(response, symbols, marketIdRequest, 'spot')
|
|
|
|
def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
|
|
marketId = self.safe_string_2(leverage, 'currency_pair', 'id')
|
|
leverageValue = self.safe_integer(leverage, 'leverage')
|
|
return {
|
|
'info': leverage,
|
|
'symbol': self.safe_symbol(marketId, market, '_', 'spot'),
|
|
'marginMode': None,
|
|
'longLeverage': leverageValue,
|
|
'shortLeverage': leverageValue,
|
|
}
|
|
|
|
def fetch_option(self, symbol: str, params={}) -> Option:
|
|
"""
|
|
fetches option data that is commonly found in an option chain
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#query-specified-contract-detail
|
|
|
|
:param str symbol: unified market symbol
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: an `option chain structure <https://docs.ccxt.com/#/?id=option-chain-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'contract': market['id'],
|
|
}
|
|
response = self.publicOptionsGetContractsContract(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "is_active": True,
|
|
# "mark_price_round": "0.01",
|
|
# "settle_fee_rate": "0.00015",
|
|
# "bid1_size": 30,
|
|
# "taker_fee_rate": "0.0003",
|
|
# "price_limit_fee_rate": "0.1",
|
|
# "order_price_round": "0.1",
|
|
# "tag": "month",
|
|
# "ref_rebate_rate": "0",
|
|
# "name": "ETH_USDT-20240628-4500-C",
|
|
# "strike_price": "4500",
|
|
# "ask1_price": "280.5",
|
|
# "ref_discount_rate": "0",
|
|
# "order_price_deviate": "0.2",
|
|
# "ask1_size": -19,
|
|
# "mark_price_down": "155.45",
|
|
# "orderbook_id": 11724695,
|
|
# "is_call": True,
|
|
# "last_price": "188.7",
|
|
# "mark_price": "274.26",
|
|
# "underlying": "ETH_USDT",
|
|
# "create_time": 1688024882,
|
|
# "settle_limit_fee_rate": "0.1",
|
|
# "orders_limit": 10,
|
|
# "mark_price_up": "403.83",
|
|
# "position_size": 80,
|
|
# "order_size_max": 10000,
|
|
# "position_limit": 100000,
|
|
# "multiplier": "0.01",
|
|
# "order_size_min": 1,
|
|
# "trade_size": 229,
|
|
# "underlying_price": "3326.6",
|
|
# "maker_fee_rate": "0.0003",
|
|
# "expiration_time": 1719561600,
|
|
# "trade_id": 15,
|
|
# "bid1_price": "269.3"
|
|
# }
|
|
#
|
|
return self.parse_option(response, None, market)
|
|
|
|
def fetch_option_chain(self, code: str, params={}) -> OptionChain:
|
|
"""
|
|
fetches data for an underlying asset that is commonly found in an option chain
|
|
|
|
https://www.gate.com/docs/developers/apiv4/en/#list-all-the-contracts-with-specified-underlying-and-expiration-time
|
|
|
|
:param str code: base currency to fetch an option chain for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.underlying]: the underlying asset, can be obtained from fetchUnderlyingAssets()
|
|
:param int [params.expiration]: unix timestamp of the expiration time
|
|
:returns dict: a list of `option chain structures <https://docs.ccxt.com/#/?id=option-chain-structure>`
|
|
"""
|
|
self.load_markets()
|
|
currency = self.currency(code)
|
|
request: dict = {
|
|
'underlying': currency['code'] + '_USDT', # todo: currency['id'].upper() & network junctions
|
|
}
|
|
response = self.publicOptionsGetContracts(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "is_active": True,
|
|
# "mark_price_round": "0.1",
|
|
# "settle_fee_rate": "0.00015",
|
|
# "bid1_size": 434,
|
|
# "taker_fee_rate": "0.0003",
|
|
# "price_limit_fee_rate": "0.1",
|
|
# "order_price_round": "1",
|
|
# "tag": "day",
|
|
# "ref_rebate_rate": "0",
|
|
# "name": "BTC_USDT-20240324-63500-P",
|
|
# "strike_price": "63500",
|
|
# "ask1_price": "387",
|
|
# "ref_discount_rate": "0",
|
|
# "order_price_deviate": "0.15",
|
|
# "ask1_size": -454,
|
|
# "mark_price_down": "124.3",
|
|
# "orderbook_id": 29600,
|
|
# "is_call": False,
|
|
# "last_price": "0",
|
|
# "mark_price": "366.6",
|
|
# "underlying": "BTC_USDT",
|
|
# "create_time": 1711118829,
|
|
# "settle_limit_fee_rate": "0.1",
|
|
# "orders_limit": 10,
|
|
# "mark_price_up": "630",
|
|
# "position_size": 0,
|
|
# "order_size_max": 10000,
|
|
# "position_limit": 10000,
|
|
# "multiplier": "0.01",
|
|
# "order_size_min": 1,
|
|
# "trade_size": 0,
|
|
# "underlying_price": "64084.65",
|
|
# "maker_fee_rate": "0.0003",
|
|
# "expiration_time": 1711267200,
|
|
# "trade_id": 0,
|
|
# "bid1_price": "307"
|
|
# },
|
|
# ]
|
|
#
|
|
return self.parse_option_chain(response, None, 'name')
|
|
|
|
def parse_option(self, chain: dict, currency: Currency = None, market: Market = None) -> Option:
|
|
#
|
|
# {
|
|
# "is_active": True,
|
|
# "mark_price_round": "0.1",
|
|
# "settle_fee_rate": "0.00015",
|
|
# "bid1_size": 434,
|
|
# "taker_fee_rate": "0.0003",
|
|
# "price_limit_fee_rate": "0.1",
|
|
# "order_price_round": "1",
|
|
# "tag": "day",
|
|
# "ref_rebate_rate": "0",
|
|
# "name": "BTC_USDT-20240324-63500-P",
|
|
# "strike_price": "63500",
|
|
# "ask1_price": "387",
|
|
# "ref_discount_rate": "0",
|
|
# "order_price_deviate": "0.15",
|
|
# "ask1_size": -454,
|
|
# "mark_price_down": "124.3",
|
|
# "orderbook_id": 29600,
|
|
# "is_call": False,
|
|
# "last_price": "0",
|
|
# "mark_price": "366.6",
|
|
# "underlying": "BTC_USDT",
|
|
# "create_time": 1711118829,
|
|
# "settle_limit_fee_rate": "0.1",
|
|
# "orders_limit": 10,
|
|
# "mark_price_up": "630",
|
|
# "position_size": 0,
|
|
# "order_size_max": 10000,
|
|
# "position_limit": 10000,
|
|
# "multiplier": "0.01",
|
|
# "order_size_min": 1,
|
|
# "trade_size": 0,
|
|
# "underlying_price": "64084.65",
|
|
# "maker_fee_rate": "0.0003",
|
|
# "expiration_time": 1711267200,
|
|
# "trade_id": 0,
|
|
# "bid1_price": "307"
|
|
# }
|
|
#
|
|
marketId = self.safe_string(chain, 'name')
|
|
market = self.safe_market(marketId, market)
|
|
timestamp = self.safe_timestamp(chain, 'create_time')
|
|
return {
|
|
'info': chain,
|
|
'currency': None,
|
|
'symbol': market['symbol'],
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'impliedVolatility': None,
|
|
'openInterest': None,
|
|
'bidPrice': self.safe_number(chain, 'bid1_price'),
|
|
'askPrice': self.safe_number(chain, 'ask1_price'),
|
|
'midPrice': None,
|
|
'markPrice': self.safe_number(chain, 'mark_price'),
|
|
'lastPrice': self.safe_number(chain, 'last_price'),
|
|
'underlyingPrice': self.safe_number(chain, 'underlying_price'),
|
|
'change': None,
|
|
'percentage': None,
|
|
'baseVolume': None,
|
|
'quoteVolume': None,
|
|
}
|
|
|
|
def fetch_positions_history(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
|
|
"""
|
|
fetches historical positions
|
|
|
|
https://www.gate.com/docs/developers/apiv4/#list-position-close-history
|
|
https://www.gate.com/docs/developers/apiv4/#list-position-close-history-2
|
|
|
|
:param str[] symbols: unified conract symbols, must all have the same settle currency and the same market type
|
|
:param int [since]: the earliest time in ms to fetch positions for
|
|
:param int [limit]: the maximum amount of records to fetch, default=1000
|
|
:param dict params: extra parameters specific to the exchange api endpoint
|
|
:param int [params.until]: the latest time in ms to fetch positions for
|
|
|
|
EXCHANGE SPECIFIC PARAMETERS
|
|
:param int [params.offset]: list offset, starting from 0
|
|
:param str [params.side]: long or short
|
|
:param str [params.pnl]: query profit or loss
|
|
:returns dict[]: a list of `position structures <https://docs.ccxt.com/#/?id=position-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = None
|
|
if symbols is not None:
|
|
symbolsLength = len(symbols)
|
|
if symbolsLength == 1:
|
|
market = self.market(symbols[0])
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('fetchPositionsHistory', market, params, 'swap')
|
|
until = self.safe_integer(params, 'until')
|
|
params = self.omit(params, 'until')
|
|
request: dict = {}
|
|
request, params = self.prepare_request(market, marketType, params)
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
if since is not None:
|
|
request['from'] = self.parse_to_int(since / 1000)
|
|
if until is not None:
|
|
request['to'] = self.parse_to_int(until / 1000)
|
|
response = None
|
|
if marketType == 'swap':
|
|
response = self.privateFuturesGetSettlePositionClose(self.extend(request, params))
|
|
elif marketType == 'future':
|
|
response = self.privateDeliveryGetSettlePositionClose(self.extend(request, params))
|
|
else:
|
|
raise NotSupported(self.id + ' fetchPositionsHistory() does not support markets of type ' + marketType)
|
|
#
|
|
# [
|
|
# {
|
|
# "contract": "SLERF_USDT",
|
|
# "text": "web",
|
|
# "long_price": "0.766306",
|
|
# "pnl": "-23.41702352",
|
|
# "pnl_pnl": "-22.7187",
|
|
# "pnl_fee": "-0.06527125",
|
|
# "pnl_fund": "-0.63305227",
|
|
# "accum_size": "100",
|
|
# "time": 1711279263,
|
|
# "short_price": "0.539119",
|
|
# "side": "long",
|
|
# "max_size": "100",
|
|
# "first_open_time": 1711037985
|
|
# },
|
|
# ...
|
|
# ]
|
|
#
|
|
return self.parse_positions(response, symbols, params)
|
|
|
|
def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
|
|
if response is None:
|
|
return None
|
|
#
|
|
# {"label": "ORDER_NOT_FOUND", "message": "Order not found"}
|
|
# {"label": "INVALID_PARAM_VALUE", "message": "invalid argument: status"}
|
|
# {"label": "INVALID_PARAM_VALUE", "message": "invalid argument: Trigger.rule"}
|
|
# {"label": "INVALID_PARAM_VALUE", "message": "invalid argument: trigger.expiration invalid range"}
|
|
# {"label": "INVALID_ARGUMENT", "detail": "invalid size"}
|
|
# {"user_id":10406147,"id":"id","succeeded":false,"message":"INVALID_PROTOCOL","label":"INVALID_PROTOCOL"}
|
|
#
|
|
label = self.safe_string(response, 'label')
|
|
if label is not None:
|
|
feedback = self.id + ' ' + body
|
|
self.throw_exactly_matched_exception(self.exceptions['exact'], label, feedback)
|
|
raise ExchangeError(feedback)
|
|
return None
|