2129 lines
99 KiB
Python
2129 lines
99 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.async_support.base.exchange import Exchange
|
||
from ccxt.abstract.coinsph import ImplicitAPI
|
||
import hashlib
|
||
from ccxt.base.types import Any, Balances, Currencies, Currency, DepositAddress, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, TradingFees, Transaction
|
||
from typing import List
|
||
from ccxt.base.errors import ExchangeError
|
||
from ccxt.base.errors import AuthenticationError
|
||
from ccxt.base.errors import PermissionDenied
|
||
from ccxt.base.errors import ArgumentsRequired
|
||
from ccxt.base.errors import BadRequest
|
||
from ccxt.base.errors import BadSymbol
|
||
from ccxt.base.errors import InsufficientFunds
|
||
from ccxt.base.errors import InvalidAddress
|
||
from ccxt.base.errors import InvalidOrder
|
||
from ccxt.base.errors import OrderNotFound
|
||
from ccxt.base.errors import OrderImmediatelyFillable
|
||
from ccxt.base.errors import DuplicateOrderId
|
||
from ccxt.base.errors import NotSupported
|
||
from ccxt.base.errors import RateLimitExceeded
|
||
from ccxt.base.errors import ExchangeNotAvailable
|
||
from ccxt.base.errors import BadResponse
|
||
from ccxt.base.decimal_to_precision import TICK_SIZE
|
||
from ccxt.base.precise import Precise
|
||
|
||
|
||
class coinsph(Exchange, ImplicitAPI):
|
||
|
||
def describe(self) -> Any:
|
||
return self.deep_extend(super(coinsph, self).describe(), {
|
||
'id': 'coinsph',
|
||
'name': 'Coins.ph',
|
||
'countries': ['PH'], # Philippines
|
||
'version': 'v1',
|
||
'rateLimit': 50, # 1200 per minute
|
||
'certified': False,
|
||
'pro': False,
|
||
'has': {
|
||
'CORS': None,
|
||
'spot': True,
|
||
'margin': False,
|
||
'swap': False,
|
||
'future': False,
|
||
'option': False,
|
||
'addMargin': False,
|
||
'borrowCrossMargin': False,
|
||
'borrowIsolatedMargin': False,
|
||
'borrowMargin': False,
|
||
'cancelAllOrders': True,
|
||
'cancelOrder': True,
|
||
'cancelOrders': False,
|
||
'closeAllPositions': False,
|
||
'closePosition': False,
|
||
'createDepositAddress': False,
|
||
'createMarketBuyOrderWithCost': True,
|
||
'createMarketOrderWithCost': False,
|
||
'createMarketSellOrderWithCost': False,
|
||
'createOrder': True,
|
||
'createOrderWithTakeProfitAndStopLoss': False,
|
||
'createOrderWithTakeProfitAndStopLossWs': False,
|
||
'createPostOnlyOrder': False,
|
||
'createReduceOnlyOrder': False,
|
||
'createStopLimitOrder': True,
|
||
'createStopMarketOrder': True,
|
||
'createStopOrder': True,
|
||
'deposit': True,
|
||
'editOrder': False,
|
||
'fetchAccounts': False,
|
||
'fetchBalance': True,
|
||
'fetchBidsAsks': False,
|
||
'fetchBorrowInterest': False,
|
||
'fetchBorrowRate': False,
|
||
'fetchBorrowRateHistories': False,
|
||
'fetchBorrowRateHistory': False,
|
||
'fetchBorrowRates': False,
|
||
'fetchBorrowRatesPerSymbol': False,
|
||
'fetchCanceledOrders': False,
|
||
'fetchClosedOrder': False,
|
||
'fetchClosedOrders': True,
|
||
'fetchCrossBorrowRate': False,
|
||
'fetchCrossBorrowRates': False,
|
||
'fetchCurrencies': True,
|
||
'fetchDeposit': None,
|
||
'fetchDepositAddress': True,
|
||
'fetchDepositAddresses': False,
|
||
'fetchDepositAddressesByNetwork': False,
|
||
'fetchDeposits': True,
|
||
'fetchDepositWithdrawFee': False,
|
||
'fetchDepositWithdrawFees': False,
|
||
'fetchFundingHistory': False,
|
||
'fetchFundingInterval': False,
|
||
'fetchFundingIntervals': False,
|
||
'fetchFundingRate': False,
|
||
'fetchFundingRateHistory': False,
|
||
'fetchFundingRates': False,
|
||
'fetchGreeks': False,
|
||
'fetchIndexOHLCV': False,
|
||
'fetchIsolatedBorrowRate': False,
|
||
'fetchIsolatedBorrowRates': False,
|
||
'fetchIsolatedPositions': False,
|
||
'fetchL3OrderBook': False,
|
||
'fetchLedger': False,
|
||
'fetchLeverage': False,
|
||
'fetchLeverages': False,
|
||
'fetchLeverageTiers': False,
|
||
'fetchLiquidations': False,
|
||
'fetchLongShortRatio': False,
|
||
'fetchLongShortRatioHistory': False,
|
||
'fetchMarginAdjustmentHistory': False,
|
||
'fetchMarginMode': False,
|
||
'fetchMarginModes': False,
|
||
'fetchMarketLeverageTiers': False,
|
||
'fetchMarkets': True,
|
||
'fetchMarkOHLCV': False,
|
||
'fetchMarkPrices': False,
|
||
'fetchMyLiquidations': False,
|
||
'fetchMySettlementHistory': False,
|
||
'fetchMyTrades': True,
|
||
'fetchOHLCV': True,
|
||
'fetchOpenInterest': False,
|
||
'fetchOpenInterestHistory': False,
|
||
'fetchOpenInterests': False,
|
||
'fetchOpenOrder': None,
|
||
'fetchOpenOrders': True,
|
||
'fetchOption': False,
|
||
'fetchOptionChain': False,
|
||
'fetchOrder': True,
|
||
'fetchOrderBook': True,
|
||
'fetchOrderBooks': False,
|
||
'fetchOrders': False,
|
||
'fetchOrderTrades': True,
|
||
'fetchPosition': False,
|
||
'fetchPositionHistory': False,
|
||
'fetchPositionMode': False,
|
||
'fetchPositions': False,
|
||
'fetchPositionsForSymbol': False,
|
||
'fetchPositionsHistory': False,
|
||
'fetchPositionsRisk': False,
|
||
'fetchPremiumIndexOHLCV': False,
|
||
'fetchSettlementHistory': False,
|
||
'fetchStatus': True,
|
||
'fetchTicker': True,
|
||
'fetchTickers': True,
|
||
'fetchTime': True,
|
||
'fetchTrades': True,
|
||
'fetchTradingFee': True,
|
||
'fetchTradingFees': True,
|
||
'fetchTradingLimits': False,
|
||
'fetchTransactionFee': False,
|
||
'fetchTransactionFees': False,
|
||
'fetchTransactions': False,
|
||
'fetchTransfers': False,
|
||
'fetchVolatilityHistory': False,
|
||
'fetchWithdrawal': None,
|
||
'fetchWithdrawals': True,
|
||
'fetchWithdrawalWhitelist': False,
|
||
'reduceMargin': False,
|
||
'repayCrossMargin': False,
|
||
'repayIsolatedMargin': False,
|
||
'repayMargin': False,
|
||
'setLeverage': False,
|
||
'setMargin': False,
|
||
'setMarginMode': False,
|
||
'setPositionMode': False,
|
||
'signIn': False,
|
||
'transfer': False,
|
||
'withdraw': True,
|
||
'ws': False,
|
||
},
|
||
'timeframes': {
|
||
'1m': '1m',
|
||
'3m': '3m',
|
||
'5m': '5m',
|
||
'15m': '15m',
|
||
'30m': '30m',
|
||
'1h': '1h',
|
||
'2h': '2h',
|
||
'4h': '4h',
|
||
'6h': '6h',
|
||
'8h': '8h',
|
||
'12h': '12h',
|
||
'1d': '1d',
|
||
'3d': '3d',
|
||
'1w': '1w',
|
||
'1M': '1M',
|
||
},
|
||
'urls': {
|
||
'logo': 'https://user-images.githubusercontent.com/1294454/225719995-48ab2026-4ddb-496c-9da7-0d7566617c9b.jpg',
|
||
'api': {
|
||
'public': 'https://api.pro.coins.ph',
|
||
'private': 'https://api.pro.coins.ph',
|
||
},
|
||
'www': 'https://coins.ph/',
|
||
'doc': [
|
||
'https://coins-docs.github.io/rest-api',
|
||
],
|
||
'fees': 'https://support.coins.ph/hc/en-us/sections/4407198694681-Limits-Fees',
|
||
},
|
||
'api': {
|
||
'public': {
|
||
'get': {
|
||
'openapi/v1/ping': 1,
|
||
'openapi/v1/time': 1,
|
||
# cost 1 if 'symbol' param defined(one market symbol) or if 'symbols' param is a list of 1-20 market symbols
|
||
# cost 20 if 'symbols' param is a list of 21-100 market symbols
|
||
# cost 40 if 'symbols' param is a list of 101 or more market symbols or if both 'symbol' and 'symbols' params are omited
|
||
'openapi/quote/v1/ticker/24hr': {'cost': 1, 'noSymbolAndNoSymbols': 40, 'byNumberOfSymbols': [[101, 40], [21, 20], [0, 1]]},
|
||
# cost 1 if 'symbol' param defined(one market symbol)
|
||
# cost 2 if 'symbols' param is a list of 1 or more market symbols or if both 'symbol' and 'symbols' params are omited
|
||
'openapi/quote/v1/ticker/price': {'cost': 1, 'noSymbol': 2},
|
||
# cost 1 if 'symbol' param defined(one market symbol)
|
||
# cost 2 if 'symbols' param is a list of 1 or more market symbols or if both 'symbol' and 'symbols' params are omited
|
||
'openapi/quote/v1/ticker/bookTicker': {'cost': 1, 'noSymbol': 2},
|
||
'openapi/v1/exchangeInfo': 10,
|
||
# cost 1 if limit <= 100; 5 if limit > 100.
|
||
'openapi/quote/v1/depth': {'cost': 1, 'byLimit': [[101, 5], [0, 1]]},
|
||
'openapi/quote/v1/klines': 1, # default limit 500; max 1000.
|
||
'openapi/quote/v1/trades': 1, # default limit 500; max 1000. if limit <=0 or > 1000 then return 1000
|
||
'openapi/v1/pairs': 1,
|
||
'openapi/quote/v1/avgPrice': 1,
|
||
},
|
||
},
|
||
'private': {
|
||
'get': {
|
||
'openapi/wallet/v1/config/getall': 10,
|
||
'openapi/wallet/v1/deposit/address': 10,
|
||
'openapi/wallet/v1/deposit/history': 1,
|
||
'openapi/wallet/v1/withdraw/history': 1,
|
||
'openapi/v1/account': 10,
|
||
# cost 3 for a single symbol; 40 when the symbol parameter is omitted
|
||
'openapi/v1/openOrders': {'cost': 3, 'noSymbol': 40},
|
||
'openapi/v1/asset/tradeFee': 1,
|
||
'openapi/v1/order': 2,
|
||
# cost 10 with symbol, 40 when the symbol parameter is omitted
|
||
'openapi/v1/historyOrders': {'cost': 10, 'noSymbol': 40},
|
||
'openapi/v1/myTrades': 10,
|
||
'openapi/v1/capital/deposit/history': 1,
|
||
'openapi/v1/capital/withdraw/history': 1,
|
||
'openapi/v3/payment-request/get-payment-request': 1,
|
||
'merchant-api/v1/get-invoices': 1,
|
||
'openapi/account/v3/crypto-accounts': 1,
|
||
'openapi/transfer/v3/transfers/{id}': 1,
|
||
},
|
||
'post': {
|
||
'openapi/wallet/v1/withdraw/apply': 600,
|
||
'openapi/v1/order/test': 1,
|
||
'openapi/v1/order': 1,
|
||
'openapi/v1/capital/withdraw/apply': 1,
|
||
'openapi/v1/capital/deposit/apply': 1,
|
||
'openapi/v3/payment-request/payment-requests': 1,
|
||
'openapi/v3/payment-request/delete-payment-request': 1,
|
||
'openapi/v3/payment-request/payment-request-reminder': 1,
|
||
'openapi/v1/userDataStream': 1,
|
||
'merchant-api/v1/invoices': 1,
|
||
'merchant-api/v1/invoices-cancel': 1,
|
||
'openapi/convert/v1/get-supported-trading-pairs': 1,
|
||
'openapi/convert/v1/get-quote': 1,
|
||
'openapi/convert/v1/accpet-quote': 1,
|
||
'openapi/fiat/v1/support-channel': 1,
|
||
'openapi/fiat/v1/cash-out': 1,
|
||
'openapi/fiat/v1/history': 1,
|
||
'openapi/migration/v4/sellorder': 1,
|
||
'openapi/migration/v4/validate-field': 1,
|
||
'openapi/transfer/v3/transfers': 1,
|
||
},
|
||
'delete': {
|
||
'openapi/v1/order': 1,
|
||
'openapi/v1/openOrders': 1,
|
||
'openapi/v1/userDataStream': 1,
|
||
},
|
||
},
|
||
},
|
||
'fees': {
|
||
# todo: zero fees for USDT, ETH and BTC markets till 2023-04-02
|
||
'trading': {
|
||
'feeSide': 'get',
|
||
'tierBased': True,
|
||
'percentage': True,
|
||
'maker': self.parse_number('0.0025'),
|
||
'taker': self.parse_number('0.003'),
|
||
'tiers': {
|
||
'taker': [
|
||
[self.parse_number('0'), self.parse_number('0.003')],
|
||
[self.parse_number('500000'), self.parse_number('0.0027')],
|
||
[self.parse_number('1000000'), self.parse_number('0.0024')],
|
||
[self.parse_number('2500000'), self.parse_number('0.002')],
|
||
[self.parse_number('5000000'), self.parse_number('0.0018')],
|
||
[self.parse_number('10000000'), self.parse_number('0.0015')],
|
||
[self.parse_number('100000000'), self.parse_number('0.0012')],
|
||
[self.parse_number('500000000'), self.parse_number('0.0009')],
|
||
[self.parse_number('1000000000'), self.parse_number('0.0007')],
|
||
[self.parse_number('2500000000'), self.parse_number('0.0005')],
|
||
],
|
||
'maker': [
|
||
[self.parse_number('0'), self.parse_number('0.0025')],
|
||
[self.parse_number('500000'), self.parse_number('0.0022')],
|
||
[self.parse_number('1000000'), self.parse_number('0.0018')],
|
||
[self.parse_number('2500000'), self.parse_number('0.0015')],
|
||
[self.parse_number('5000000'), self.parse_number('0.0012')],
|
||
[self.parse_number('10000000'), self.parse_number('0.001')],
|
||
[self.parse_number('100000000'), self.parse_number('0.0008')],
|
||
[self.parse_number('500000000'), self.parse_number('0.0007')],
|
||
[self.parse_number('1000000000'), self.parse_number('0.0006')],
|
||
[self.parse_number('2500000000'), self.parse_number('0.0005')],
|
||
],
|
||
},
|
||
},
|
||
},
|
||
'precisionMode': TICK_SIZE,
|
||
# exchange-specific options
|
||
'options': {
|
||
'createMarketBuyOrderRequiresPrice': True, # True or False
|
||
'withdraw': {
|
||
'warning': False,
|
||
},
|
||
'deposit': {
|
||
'warning': False,
|
||
},
|
||
'createOrder': {
|
||
'timeInForce': 'GTC', # FOK, IOC
|
||
'newOrderRespType': {
|
||
'market': 'FULL', # FULL, RESULT. ACK
|
||
'limit': 'FULL', # we change it from 'ACK' by default to 'FULL'
|
||
},
|
||
},
|
||
'fetchTicker': {
|
||
'method': 'publicGetOpenapiQuoteV1Ticker24hr', # publicGetOpenapiQuoteV1TickerPrice, publicGetOpenapiQuoteV1TickerBookTicker
|
||
},
|
||
'fetchTickers': {
|
||
'method': 'publicGetOpenapiQuoteV1Ticker24hr', # publicGetOpenapiQuoteV1TickerPrice, publicGetOpenapiQuoteV1TickerBookTicker
|
||
},
|
||
'networks': {
|
||
# all networks: 'ETH', 'TRX', 'BSC', 'ARBITRUM', 'RON', 'BTC', 'XRP'
|
||
# you can call api privateGetOpenapiWalletV1ConfigGetall to check which network is supported for the currency
|
||
'TRC20': 'TRX',
|
||
'ERC20': 'ETH',
|
||
'BEP20': 'BSC',
|
||
'ARB': 'ARBITRUM',
|
||
},
|
||
},
|
||
'features': {
|
||
'spot': {
|
||
'sandbox': False,
|
||
'fetchCurrencies': {
|
||
'private': True,
|
||
},
|
||
'createOrder': {
|
||
'marginMode': False,
|
||
'triggerPrice': True,
|
||
'triggerPriceType': None,
|
||
'triggerDirection': False,
|
||
'stopLossPrice': False, # todo
|
||
'takeProfitPrice': False, # todo
|
||
'attachedStopLossTakeProfit': None,
|
||
'timeInForce': {
|
||
'IOC': True,
|
||
'FOK': True,
|
||
'PO': False,
|
||
'GTD': False,
|
||
},
|
||
'hedged': False,
|
||
'trailing': False,
|
||
'leverage': False,
|
||
'marketBuyByCost': True,
|
||
'marketBuyRequiresPrice': False,
|
||
'selfTradePrevention': True, # todo implement
|
||
'iceberg': False,
|
||
},
|
||
'createOrders': None,
|
||
'fetchMyTrades': {
|
||
'marginMode': False,
|
||
'limit': 1000,
|
||
'daysBack': 100000,
|
||
'untilDays': 100000, # todo implement
|
||
'symbolRequired': True,
|
||
},
|
||
'fetchOrder': {
|
||
'marginMode': False,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchOpenOrders': {
|
||
'marginMode': False,
|
||
'limit': None,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchOrders': None,
|
||
'fetchClosedOrders': {
|
||
'marginMode': False,
|
||
'limit': 1000,
|
||
'daysBack': 100000,
|
||
'daysBackCanceled': 1,
|
||
'untilDays': 100000,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': True,
|
||
},
|
||
'fetchOHLCV': {
|
||
'limit': 1000,
|
||
},
|
||
},
|
||
'swap': {
|
||
'linear': None,
|
||
'inverse': None,
|
||
},
|
||
'future': {
|
||
'linear': None,
|
||
'inverse': None,
|
||
},
|
||
},
|
||
# https://coins-docs.github.io/errors/
|
||
'exceptions': {
|
||
'exact': {
|
||
'-1000': BadRequest, # An unknown error occured while processing the request.
|
||
'-1001': BadRequest, # {"code":-1001,"msg":"Internal error."}
|
||
'-1002': AuthenticationError, # You are not authorized to execute self request. Request need API Key included in . We suggest that API Key be included in any request.
|
||
'-1003': RateLimitExceeded, # Too many requests; please use the websocket for live updates. Too many requests; current limit is %s requests per minute. Please use the websocket for live updates to avoid polling the API. Way too many requests; IP banned until %s. Please use the websocket for live updates to avoid bans.
|
||
'-1004': InvalidOrder, # {"code":-1004,"msg":"Missing required parameter \u0027symbol\u0027"}
|
||
'-1006': BadResponse, # An unexpected response was received from the message bus. Execution status unknown. OPEN API server find some exception in execute request .Please report to Customer service.
|
||
'-1007': BadResponse, # Timeout waiting for response from backend server. Send status unknown; execution status unknown.
|
||
'-1014': InvalidOrder, # Unsupported order combination.
|
||
'-1015': RateLimitExceeded, # Reach the rate limit .Please slow down your request speed. Too many new orders. Too many new orders; current limit is %s orders per %s.
|
||
'-1016': NotSupported, # This service is no longer available.
|
||
'-1020': NotSupported, # This operation is not supported.
|
||
'-1021': BadRequest, # {"code":-1021,"msg":"Timestamp for self request is outside of the recvWindow."}
|
||
'-1022': BadRequest, # {"code":-1022,"msg":"Signature for self request is not valid."}
|
||
'-1023': AuthenticationError, # Please set IP whitelist before using API.
|
||
'-1024': BadRequest, # {"code":-1024,"msg":"recvWindow is not valid."}
|
||
'-1025': BadRequest, # {"code":-1025,"msg":"recvWindow cannot be greater than 60000"}
|
||
'-1030': ExchangeError, # Business error.
|
||
'-1100': BadRequest, # Illegal characters found in a parameter. Illegal characters found in parameter ‘%s’; legal range is ‘%s’.
|
||
'-1101': BadRequest, # Too many parameters sent for self endpoint. Too many parameters; expected ‘%s’ and received ‘%s’. Duplicate values for a parameter detected.
|
||
'-1102': BadRequest, # A mandatory parameter was not sent, was empty/null, or malformed. Mandatory parameter ‘%s’ was not sent, was empty/null, or malformed. Param ‘%s’ or ‘%s’ must be sent, but both were empty/null!
|
||
'-1103': BadRequest, # An unknown parameter was sent. In BHEx Open Api , each request requires at least one parameter. {Timestamp}.
|
||
'-1104': BadRequest, # Not all sent parameters were read. Not all sent parameters were read; read ‘%s’ parameter(s) but was sent ‘%s’.
|
||
'-1105': BadRequest, # {"code":-1105,"msg":"Parameter \u0027orderId and origClientOrderId\u0027 is empty."}
|
||
'-1106': BadRequest, # A parameter was sent when not required. Parameter ‘%s’ sent when not required.
|
||
'-1111': BadRequest, # Precision is over the maximum defined for self asset.
|
||
'-1112': BadResponse, # No orders on book for symbol.
|
||
'-1114': BadRequest, # TimeInForce parameter sent when not required.
|
||
'-1115': InvalidOrder, # {"code":-1115,"msg":"Invalid timeInForce."}
|
||
'-1116': InvalidOrder, # {"code":-1116,"msg":"Invalid orderType."}
|
||
'-1117': InvalidOrder, # {"code":-1117,"msg":"Invalid side."}
|
||
'-1118': InvalidOrder, # New client order ID was empty.
|
||
'-1119': InvalidOrder, # Original client order ID was empty.
|
||
'-1120': BadRequest, # Invalid interval.
|
||
'-1121': BadSymbol, # Invalid symbol.
|
||
'-1122': InvalidOrder, # Invalid newOrderRespType.
|
||
'-1125': BadRequest, # This listenKey does not exist.
|
||
'-1127': BadRequest, # Lookup interval is too big. More than %s hours between startTime and endTime.
|
||
'-1128': BadRequest, # Combination of optional parameters invalid.
|
||
'-1130': BadRequest, # Invalid data sent for a parameter. Data sent for paramter ‘%s’ is not valid.
|
||
'-1131': InsufficientFunds, # {"code":-1131,"msg":"Balance insufficient "}
|
||
'-1132': InvalidOrder, # Order price too high.
|
||
'-1133': InvalidOrder, # Order price lower than the minimum,please check general broker info.
|
||
'-1134': InvalidOrder, # Order price decimal too long,please check general broker info.
|
||
'-1135': InvalidOrder, # Order quantity too large.
|
||
'-1136': InvalidOrder, # Order quantity lower than the minimum.
|
||
'-1137': InvalidOrder, # Order quantity decimal too long.
|
||
'-1138': InvalidOrder, # Order price exceeds permissible range.
|
||
'-1139': InvalidOrder, # Order has been filled.
|
||
'-1140': InvalidOrder, # {"code":-1140,"msg":"Transaction amount lower than the minimum."}
|
||
'-1141': DuplicateOrderId, # {"code":-1141,"msg":"Duplicate clientOrderId"}
|
||
'-1142': InvalidOrder, # {"code":-1142,"msg":"Order has been canceled"}
|
||
'-1143': OrderNotFound, # Cannot be found on order book
|
||
'-1144': InvalidOrder, # Order has been locked
|
||
'-1145': InvalidOrder, # This order type does not support cancellation
|
||
'-1146': InvalidOrder, # Order creation timeout
|
||
'-1147': InvalidOrder, # Order cancellation timeout
|
||
'-1148': InvalidOrder, # Market order amount decimal too long
|
||
'-1149': InvalidOrder, # Create order failed
|
||
'-1150': InvalidOrder, # Cancel order failed
|
||
'-1151': BadSymbol, # The trading pair is not open yet
|
||
'-1152': NotSupported, # Coming soon
|
||
'-1153': AuthenticationError, # User not exist
|
||
'-1154': BadRequest, # Invalid price type
|
||
'-1155': BadRequest, # Invalid position side
|
||
'-1156': InvalidOrder, # Order quantity invalid
|
||
'-1157': BadSymbol, # The trading pair is not available for api trading
|
||
'-1158': InvalidOrder, # create limit maker order failed
|
||
'-1159': InvalidOrder, # {"code":-1159,"msg":"STOP_LOSS/TAKE_PROFIT order is not allowed to trade immediately"}
|
||
'-1160': BadRequest, # Modify futures margin error
|
||
'-1161': BadRequest, # Reduce margin forbidden
|
||
'-2010': InvalidOrder, # {"code":-2010,"msg":"New order rejected."}
|
||
'-2013': OrderNotFound, # {"code":-2013,"msg":"Order does not exist."}
|
||
'-2011': BadRequest, # CANCEL_REJECTED
|
||
'-2014': BadRequest, # API-key format invalid.
|
||
'-2015': AuthenticationError, # {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."}
|
||
'-2016': BadResponse, # No trading window could be found for the symbol. Try ticker/24hrs instead
|
||
'-3126': InvalidOrder, # {"code":-3126,"msg":"Order price lower than 72005.93415"}
|
||
'-3127': InvalidOrder, # {"code":-3127,"msg":"Order price higher than 1523.192"}
|
||
'-4001': BadRequest, # {"code":-4001,"msg":"start time must less than end time"}
|
||
'-100011': BadSymbol, # {"code":-100011,"msg":"Not supported symbols"}
|
||
'-100012': BadSymbol, # {"code":-100012,"msg":"Parameter symbol [str] missing!"}
|
||
'-30008': InsufficientFunds, # {"code":-30008,"msg":"withdraw balance insufficient"}
|
||
'-30036': InsufficientFunds, # {"code":-30036,"msg":"Available balance not enough!"}
|
||
'403': ExchangeNotAvailable,
|
||
},
|
||
'broad': {
|
||
'Unknown order sent': OrderNotFound, # The order(by either orderId, clOrdId, origClOrdId) could not be found
|
||
'Duplicate order sent': DuplicateOrderId, # The clOrdId is already in use
|
||
'Market is closed': BadSymbol, # The symbol is not trading
|
||
'Account has insufficient balance for requested action': InsufficientFunds, # Not enough funds to complete the action
|
||
'Market orders are not supported for self symbol': BadSymbol, # MARKET is not enabled on the symbol
|
||
'Iceberg orders are not supported for self symbol': BadSymbol, # icebergQty is not enabled on the symbol
|
||
'Stop loss orders are not supported for self symbol': BadSymbol, # STOP_LOSS is not enabled on the symbol
|
||
'Stop loss limit orders are not supported for self symbol': BadSymbol, # STOP_LOSS_LIMIT is not enabled on the symbol
|
||
'Take profit orders are not supported for self symbol': BadSymbol, # TAKE_PROFIT is not enabled on the symbol
|
||
'Take profit limit orders are not supported for self symbol': BadSymbol, # TAKE_PROFIT_LIMIT is not enabled on the symbol
|
||
'Price* QTY is zero or less': BadRequest, # price* quantity is too low
|
||
'IcebergQty exceeds QTY': BadRequest, # icebergQty must be less than the order quantity
|
||
'This action disabled is on self account': PermissionDenied, # Contact customer support; some actions have been disabled on the account.
|
||
'Unsupported order combination': InvalidOrder, # The orderType, timeInForce, stopPrice, and or icebergQty combination isn’t allowed.
|
||
'Order would trigger immediately': InvalidOrder, # The order’s stop price is not valid when compared to the last traded price.
|
||
'Cancel order is invalid. Check origClOrdId and orderId': InvalidOrder, # No origClOrdId or orderId was sent in.
|
||
'Order would immediately match and take': OrderImmediatelyFillable, # LIMIT_MAKER order type would immediately match and trade, and not be a pure maker order.
|
||
'PRICE_FILTER': InvalidOrder, # price is too high, too low, and or not following the tick size rule for the symbol.
|
||
'LOT_SIZE': InvalidOrder, # quantity is too high, too low, and or not following the step size rule for the symbol.
|
||
'MIN_NOTIONAL': InvalidOrder, # price* quantity is too low to be a valid order for the symbol.
|
||
'MAX_NUM_ORDERS': InvalidOrder, # Account has too many open orders on the symbol.
|
||
'MAX_ALGO_ORDERS': InvalidOrder, # Account has too many open stop loss and or take profit orders on the symbol.
|
||
'BROKER_MAX_NUM_ORDERS': InvalidOrder, # Account has too many open orders on the broker.
|
||
'BROKER_MAX_ALGO_ORDERS': InvalidOrder, # Account has too many open stop loss and or take profit orders on the broker.
|
||
'ICEBERG_PARTS': BadRequest, # Iceberg order would break into too many parts; icebergQty is too small.
|
||
},
|
||
},
|
||
})
|
||
|
||
async def fetch_currencies(self, params={}) -> Currencies:
|
||
"""
|
||
fetches all available currencies on an exchange
|
||
|
||
https://docs.coins.ph/rest-api/#all-coins-information-user_data
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: an associative dictionary of currencies
|
||
"""
|
||
if not self.check_required_credentials(False):
|
||
return {}
|
||
response = await self.privateGetOpenapiWalletV1ConfigGetall(params)
|
||
#
|
||
# [
|
||
# {
|
||
# "coin": "PHP",
|
||
# "name": "PHP",
|
||
# "depositAllEnable": False,
|
||
# "withdrawAllEnable": False,
|
||
# "free": "0",
|
||
# "locked": "0",
|
||
# "transferPrecision": "2",
|
||
# "transferMinQuantity": "0",
|
||
# "networkList": [],
|
||
# "legalMoney": True
|
||
# },
|
||
# {
|
||
# "coin": "USDT",
|
||
# "name": "USDT",
|
||
# "depositAllEnable": True,
|
||
# "withdrawAllEnable": True,
|
||
# "free": "0",
|
||
# "locked": "0",
|
||
# "transferPrecision": "8",
|
||
# "transferMinQuantity": "0",
|
||
# "networkList": [
|
||
# {
|
||
# "addressRegex": "^0x[0-9a-fA-F]{40}$",
|
||
# "memoRegex": " ",
|
||
# "network": "ETH",
|
||
# "name": "Ethereum(ERC20)",
|
||
# "depositEnable": True,
|
||
# "minConfirm": "12",
|
||
# "unLockConfirm": "-1",
|
||
# "withdrawDesc": "",
|
||
# "withdrawEnable": True,
|
||
# "withdrawFee": "6",
|
||
# "withdrawIntegerMultiple": "0.000001",
|
||
# "withdrawMax": "500000",
|
||
# "withdrawMin": "10",
|
||
# "sameAddress": False
|
||
# },
|
||
# {
|
||
# "addressRegex": "^T[0-9a-zA-Z]{33}$",
|
||
# "memoRegex": "",
|
||
# "network": "TRX",
|
||
# "name": "TRON",
|
||
# "depositEnable": True,
|
||
# "minConfirm": "19",
|
||
# "unLockConfirm": "-1",
|
||
# "withdrawDesc": "",
|
||
# "withdrawEnable": True,
|
||
# "withdrawFee": "3",
|
||
# "withdrawIntegerMultiple": "0.000001",
|
||
# "withdrawMax": "1000000",
|
||
# "withdrawMin": "20",
|
||
# "sameAddress": False
|
||
# }
|
||
# ],
|
||
# "legalMoney": False
|
||
# }
|
||
# ]
|
||
#
|
||
result: dict = {}
|
||
for i in range(0, len(response)):
|
||
entry = response[i]
|
||
id = self.safe_string(entry, 'coin')
|
||
code = self.safe_currency_code(id)
|
||
isFiat = self.safe_bool(entry, 'isLegalMoney')
|
||
networkList = self.safe_list(entry, 'networkList', [])
|
||
networks: dict = {}
|
||
for j in range(0, len(networkList)):
|
||
networkItem = networkList[j]
|
||
network = self.safe_string(networkItem, 'network')
|
||
networkCode = self.network_id_to_code(network)
|
||
networks[networkCode] = {
|
||
'info': networkItem,
|
||
'id': network,
|
||
'network': networkCode,
|
||
'active': None,
|
||
'deposit': self.safe_bool(networkItem, 'depositEnable'),
|
||
'withdraw': self.safe_bool(networkItem, 'withdrawEnable'),
|
||
'fee': self.safe_number(networkItem, 'withdrawFee'),
|
||
'precision': self.safe_number(networkItem, 'withdrawIntegerMultiple'),
|
||
'limits': {
|
||
'withdraw': {
|
||
'min': self.safe_number(networkItem, 'withdrawMin'),
|
||
'max': self.safe_number(networkItem, 'withdrawMax'),
|
||
},
|
||
'deposit': {
|
||
'min': None,
|
||
'max': None,
|
||
},
|
||
},
|
||
}
|
||
result[code] = self.safe_currency_structure({
|
||
'id': id,
|
||
'name': self.safe_string(entry, 'name'),
|
||
'code': code,
|
||
'type': 'fiat' if isFiat else 'crypto',
|
||
'precision': self.parse_number(self.parse_precision(self.safe_string(entry, 'transferPrecision'))),
|
||
'info': entry,
|
||
'active': None,
|
||
'deposit': self.safe_bool(entry, 'depositAllEnable'),
|
||
'withdraw': self.safe_bool(entry, 'withdrawAllEnable'),
|
||
'networks': networks,
|
||
'fee': None,
|
||
'fees': None,
|
||
'limits': {},
|
||
})
|
||
return result
|
||
|
||
def calculate_rate_limiter_cost(self, api, method, path, params, config={}):
|
||
if ('noSymbol' in config) and not ('symbol' in params):
|
||
return config['noSymbol']
|
||
elif ('noSymbolAndNoSymbols' in config) and not ('symbol' in params) and not ('symbols' in params):
|
||
return config['noSymbolAndNoSymbols']
|
||
elif ('byNumberOfSymbols' in config) and ('symbols' in params):
|
||
symbols = params['symbols']
|
||
symbolsAmount = len(symbols)
|
||
byNumberOfSymbols = config['byNumberOfSymbols']
|
||
for i in range(0, len(byNumberOfSymbols)):
|
||
entry = byNumberOfSymbols[i]
|
||
if symbolsAmount >= entry[0]:
|
||
return entry[1]
|
||
elif ('byLimit' in config) and ('limit' in params):
|
||
limit = params['limit']
|
||
byLimit = config['byLimit']
|
||
for i in range(0, len(byLimit)):
|
||
entry = byLimit[i]
|
||
if limit >= entry[0]:
|
||
return entry[1]
|
||
return self.safe_value(config, 'cost', 1)
|
||
|
||
async def fetch_status(self, params={}):
|
||
"""
|
||
the latest known information on the availability of the exchange API
|
||
|
||
https://coins-docs.github.io/rest-api/#test-connectivity
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `status structure <https://docs.ccxt.com/#/?id=exchange-status-structure>`
|
||
"""
|
||
response = await self.publicGetOpenapiV1Ping(params)
|
||
return {
|
||
'status': 'ok', # if there's no Errors, status = 'ok'
|
||
'updated': None,
|
||
'eta': None,
|
||
'url': None,
|
||
'info': response,
|
||
}
|
||
|
||
async def fetch_time(self, params={}) -> Int:
|
||
"""
|
||
fetches the current integer timestamp in milliseconds from the exchange server
|
||
|
||
https://coins-docs.github.io/rest-api/#check-server-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 = await self.publicGetOpenapiV1Time(params)
|
||
#
|
||
# {"serverTime":1677705408268}
|
||
#
|
||
return self.safe_integer(response, 'serverTime')
|
||
|
||
async def fetch_markets(self, params={}) -> List[Market]:
|
||
"""
|
||
retrieves data on all markets for coinsph
|
||
|
||
https://coins-docs.github.io/rest-api/#exchange-information
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict[]: an array of objects representing market data
|
||
"""
|
||
response = await self.publicGetOpenapiV1ExchangeInfo(params)
|
||
#
|
||
# {
|
||
# "timezone": "UTC",
|
||
# "serverTime": "1677449496897",
|
||
# "exchangeFilters": [],
|
||
# "symbols": [
|
||
# {
|
||
# "symbol": "XRPPHP",
|
||
# "status": "TRADING",
|
||
# "baseAsset": "XRP",
|
||
# "baseAssetPrecision": "2",
|
||
# "quoteAsset": "PHP",
|
||
# "quoteAssetPrecision": "4",
|
||
# "orderTypes": [
|
||
# "LIMIT",
|
||
# "MARKET",
|
||
# "LIMIT_MAKER",
|
||
# "STOP_LOSS_LIMIT",
|
||
# "STOP_LOSS",
|
||
# "TAKE_PROFIT_LIMIT",
|
||
# "TAKE_PROFIT"
|
||
# ],
|
||
# "filters": [
|
||
# {
|
||
# "minPrice": "0.01",
|
||
# "maxPrice": "99999999.00000000",
|
||
# "tickSize": "0.01",
|
||
# "filterType": "PRICE_FILTER"
|
||
# },
|
||
# {
|
||
# "minQty": "0.01",
|
||
# "maxQty": "99999999999.00000000",
|
||
# "stepSize": "0.01",
|
||
# "filterType": "LOT_SIZE"
|
||
# },
|
||
# {minNotional: "50", filterType: "NOTIONAL"},
|
||
# {minNotional: "50", filterType: "MIN_NOTIONAL"},
|
||
# {
|
||
# "priceUp": "99999999",
|
||
# "priceDown": "0.01",
|
||
# "filterType": "STATIC_PRICE_RANGE"
|
||
# },
|
||
# {
|
||
# "multiplierUp": "1.1",
|
||
# "multiplierDown": "0.9",
|
||
# "filterType": "PERCENT_PRICE_INDEX"
|
||
# },
|
||
# {
|
||
# "multiplierUp": "1.1",
|
||
# "multiplierDown": "0.9",
|
||
# "filterType": "PERCENT_PRICE_ORDER_SIZE"
|
||
# },
|
||
# {maxNumOrders: "200", filterType: "MAX_NUM_ORDERS"},
|
||
# {maxNumAlgoOrders: "5", filterType: "MAX_NUM_ALGO_ORDERS"}
|
||
# ]
|
||
# },
|
||
# ]
|
||
# }
|
||
#
|
||
markets = self.safe_list(response, 'symbols', [])
|
||
result = []
|
||
for i in range(0, len(markets)):
|
||
market = markets[i]
|
||
id = self.safe_string(market, 'symbol')
|
||
baseId = self.safe_string(market, 'baseAsset')
|
||
quoteId = self.safe_string(market, 'quoteAsset')
|
||
base = self.safe_currency_code(baseId)
|
||
quote = self.safe_currency_code(quoteId)
|
||
limits = self.index_by(self.safe_list(market, 'filters', []), 'filterType')
|
||
amountLimits = self.safe_value(limits, 'LOT_SIZE', {})
|
||
priceLimits = self.safe_value(limits, 'PRICE_FILTER', {})
|
||
costLimits = self.safe_value(limits, 'NOTIONAL', {})
|
||
result.append({
|
||
'id': id,
|
||
'symbol': base + '/' + quote,
|
||
'base': base,
|
||
'quote': quote,
|
||
'settle': None,
|
||
'baseId': baseId,
|
||
'quoteId': quoteId,
|
||
'settleId': None,
|
||
'type': 'spot',
|
||
'spot': True,
|
||
'margin': False,
|
||
'swap': False,
|
||
'future': False,
|
||
'option': False,
|
||
'active': self.safe_string_lower(market, 'status') == 'trading',
|
||
'contract': False,
|
||
'linear': None,
|
||
'inverse': None,
|
||
'taker': None,
|
||
'maker': None,
|
||
'contractSize': None,
|
||
'expiry': None,
|
||
'expiryDatetime': None,
|
||
'strike': None,
|
||
'optionType': None,
|
||
'precision': {
|
||
'amount': self.parse_number(self.safe_string(amountLimits, 'stepSize')),
|
||
'price': self.parse_number(self.safe_string(priceLimits, 'tickSize')),
|
||
},
|
||
'limits': {
|
||
'leverage': {
|
||
'min': None,
|
||
'max': None,
|
||
},
|
||
'amount': {
|
||
'min': self.parse_number(self.safe_string(amountLimits, 'minQty')),
|
||
'max': self.parse_number(self.safe_string(amountLimits, 'maxQty')),
|
||
},
|
||
'price': {
|
||
'min': self.parse_number(self.safe_string(priceLimits, 'minPrice')),
|
||
'max': self.parse_number(self.safe_string(priceLimits, 'maxPrice')),
|
||
},
|
||
'cost': {
|
||
'min': self.parse_number(self.safe_string(costLimits, 'minNotional')),
|
||
'max': None,
|
||
},
|
||
},
|
||
'created': None,
|
||
'info': market,
|
||
})
|
||
self.set_markets(result)
|
||
return result
|
||
|
||
async 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://coins-docs.github.io/rest-api/#24hr-ticker-price-change-statistics
|
||
https://coins-docs.github.io/rest-api/#symbol-price-ticker
|
||
https://coins-docs.github.io/rest-api/#symbol-order-book-ticker
|
||
|
||
: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>`
|
||
"""
|
||
await self.load_markets()
|
||
request: dict = {}
|
||
if symbols is not None:
|
||
ids = []
|
||
for i in range(0, len(symbols)):
|
||
market = self.market(symbols[i])
|
||
id = market['id']
|
||
ids.append(id)
|
||
request['symbols'] = ids
|
||
defaultMethod = 'publicGetOpenapiQuoteV1Ticker24hr'
|
||
options = self.safe_dict(self.options, 'fetchTickers', {})
|
||
method = self.safe_string(options, 'method', defaultMethod)
|
||
tickers = None
|
||
if method == 'publicGetOpenapiQuoteV1TickerPrice':
|
||
tickers = await self.publicGetOpenapiQuoteV1TickerPrice(self.extend(request, params))
|
||
elif method == 'publicGetOpenapiQuoteV1TickerBookTicker':
|
||
tickers = await self.publicGetOpenapiQuoteV1TickerBookTicker(self.extend(request, params))
|
||
else:
|
||
tickers = await self.publicGetOpenapiQuoteV1Ticker24hr(self.extend(request, params))
|
||
return self.parse_tickers(tickers, symbols, params)
|
||
|
||
async 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://coins-docs.github.io/rest-api/#24hr-ticker-price-change-statistics
|
||
https://coins-docs.github.io/rest-api/#symbol-price-ticker
|
||
https://coins-docs.github.io/rest-api/#symbol-order-book-ticker
|
||
|
||
: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>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
defaultMethod = 'publicGetOpenapiQuoteV1Ticker24hr'
|
||
options = self.safe_dict(self.options, 'fetchTicker', {})
|
||
method = self.safe_string(options, 'method', defaultMethod)
|
||
ticker = None
|
||
if method == 'publicGetOpenapiQuoteV1TickerPrice':
|
||
ticker = await self.publicGetOpenapiQuoteV1TickerPrice(self.extend(request, params))
|
||
elif method == 'publicGetOpenapiQuoteV1TickerBookTicker':
|
||
ticker = await self.publicGetOpenapiQuoteV1TickerBookTicker(self.extend(request, params))
|
||
else:
|
||
ticker = await self.publicGetOpenapiQuoteV1Ticker24hr(self.extend(request, params))
|
||
return self.parse_ticker(ticker, market)
|
||
|
||
def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
||
#
|
||
# publicGetOpenapiQuoteV1Ticker24hr
|
||
# {
|
||
# "symbol": "ETHUSDT",
|
||
# "priceChange": "41.440000000000000000",
|
||
# "priceChangePercent": "0.0259",
|
||
# "weightedAvgPrice": "1631.169825783972125436",
|
||
# "prevClosePrice": "1601.520000000000000000",
|
||
# "lastPrice": "1642.96",
|
||
# "lastQty": "0.000001000000000000",
|
||
# "bidPrice": "1638.790000000000000000",
|
||
# "bidQty": "0.280075000000000000",
|
||
# "askPrice": "1647.340000000000000000",
|
||
# "askQty": "0.165183000000000000",
|
||
# "openPrice": "1601.52",
|
||
# "highPrice": "1648.28",
|
||
# "lowPrice": "1601.52",
|
||
# "volume": "0.000287",
|
||
# "quoteVolume": "0.46814574",
|
||
# "openTime": "1677417000000",
|
||
# "closeTime": "1677503415200",
|
||
# "firstId": "1364680572697591809",
|
||
# "lastId": "1365389809203560449",
|
||
# "count": "100"
|
||
# }
|
||
#
|
||
# publicGetOpenapiQuoteV1TickerPrice
|
||
# {"symbol": "ETHUSDT", "price": "1599.68"}
|
||
#
|
||
# publicGetOpenapiQuoteV1TickerBookTicker
|
||
# {
|
||
# "symbol": "ETHUSDT",
|
||
# "bidPrice": "1596.57",
|
||
# "bidQty": "0.246405",
|
||
# "askPrice": "1605.12",
|
||
# "askQty": "0.242681"
|
||
# }
|
||
#
|
||
marketId = self.safe_string(ticker, 'symbol')
|
||
market = self.safe_market(marketId, market)
|
||
timestamp = self.safe_integer(ticker, 'closeTime')
|
||
bid = self.safe_string(ticker, 'bidPrice')
|
||
ask = self.safe_string(ticker, 'askPrice')
|
||
bidVolume = self.safe_string(ticker, 'bidQty')
|
||
askVolume = self.safe_string(ticker, 'askQty')
|
||
baseVolume = self.safe_string(ticker, 'volume')
|
||
quoteVolume = self.safe_string(ticker, 'quoteVolume')
|
||
open = self.safe_string(ticker, 'openPrice')
|
||
high = self.safe_string(ticker, 'highPrice')
|
||
low = self.safe_string(ticker, 'lowPrice')
|
||
prevClose = self.safe_string(ticker, 'prevClosePrice')
|
||
vwap = self.safe_string(ticker, 'weightedAvgPrice')
|
||
changeValue = self.safe_string(ticker, 'priceChange')
|
||
changePcnt = self.safe_string(ticker, 'priceChangePercent')
|
||
changePcnt = Precise.string_mul(changePcnt, '100')
|
||
return self.safe_ticker({
|
||
'symbol': market['symbol'],
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'open': open,
|
||
'high': high,
|
||
'low': low,
|
||
'close': self.safe_string_2(ticker, 'lastPrice', 'price'),
|
||
'bid': bid,
|
||
'bidVolume': bidVolume,
|
||
'ask': ask,
|
||
'askVolume': askVolume,
|
||
'vwap': vwap,
|
||
'previousClose': prevClose,
|
||
'change': changeValue,
|
||
'percentage': changePcnt,
|
||
'average': None,
|
||
'baseVolume': baseVolume,
|
||
'quoteVolume': quoteVolume,
|
||
'info': ticker,
|
||
}, market)
|
||
|
||
async 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://coins-docs.github.io/rest-api/#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(default 100, max 200)
|
||
: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
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
if limit is not None:
|
||
request['limit'] = limit
|
||
response = await self.publicGetOpenapiQuoteV1Depth(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "lastUpdateId": "1667022157000699400",
|
||
# "bids": [
|
||
# ['1651.810000000000000000', '0.214556000000000000'],
|
||
# ['1651.730000000000000000', '0.257343000000000000'],
|
||
# ],
|
||
# "asks": [
|
||
# ['1660.510000000000000000', '0.299092000000000000'],
|
||
# ['1660.600000000000000000', '0.253667000000000000'],
|
||
# ]
|
||
# }
|
||
#
|
||
orderbook = self.parse_order_book(response, symbol)
|
||
orderbook['nonce'] = self.safe_integer(response, 'lastUpdateId')
|
||
return orderbook
|
||
|
||
async 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://coins-docs.github.io/rest-api/#klinecandlestick-data
|
||
|
||
: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(default 500, max 1000)
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param int [params.until]: timestamp in ms of the latest candle to fetch
|
||
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
interval = self.safe_string(self.timeframes, timeframe)
|
||
until = self.safe_integer(params, 'until')
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
'interval': interval,
|
||
}
|
||
if limit is None:
|
||
limit = 1000
|
||
if since is not None:
|
||
request['startTime'] = since
|
||
# since work properly only when it is "younger" than last "limit" candle
|
||
if until is not None:
|
||
request['endTime'] = until
|
||
else:
|
||
duration = self.parse_timeframe(timeframe) * 1000
|
||
endTimeByLimit = self.sum(since, duration * (limit - 1))
|
||
now = self.milliseconds()
|
||
request['endTime'] = min(endTimeByLimit, now)
|
||
elif until is not None:
|
||
request['endTime'] = until
|
||
# since work properly only when it is "younger" than last "limit" candle
|
||
duration = self.parse_timeframe(timeframe) * 1000
|
||
request['startTime'] = until - (duration * (limit - 1))
|
||
request['limit'] = limit
|
||
params = self.omit(params, 'until')
|
||
response = await self.publicGetOpenapiQuoteV1Klines(self.extend(request, params))
|
||
#
|
||
# [
|
||
# [
|
||
# 1499040000000, # Open time
|
||
# "0.01634790", # Open
|
||
# "0.80000000", # High
|
||
# "0.01575800", # Low
|
||
# "0.01577100", # Close
|
||
# "148976.11427815", # Volume
|
||
# 1499644799999, # Close time
|
||
# "2434.19055334", # Quote asset volume
|
||
# 308, # Number of trades
|
||
# "1756.87402397", # Taker buy base asset volume
|
||
# "28.46694368" # Taker buy quote asset volume
|
||
# ]
|
||
# ]
|
||
#
|
||
return self.parse_ohlcvs(response, market, timeframe, since, limit)
|
||
|
||
def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
|
||
return [
|
||
self.safe_integer(ohlcv, 0),
|
||
self.safe_number(ohlcv, 1),
|
||
self.safe_number(ohlcv, 2),
|
||
self.safe_number(ohlcv, 3),
|
||
self.safe_number(ohlcv, 4),
|
||
self.safe_number(ohlcv, 5),
|
||
]
|
||
|
||
async 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://coins-docs.github.io/rest-api/#recent-trades-list
|
||
|
||
: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(default 500, max 1000)
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
if since is not None:
|
||
# since work properly only when it is "younger" than last 'limit' trade
|
||
request['limit'] = 1000
|
||
else:
|
||
if limit is not None:
|
||
request['limit'] = limit
|
||
response = await self.publicGetOpenapiQuoteV1Trades(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "price": "89685.8",
|
||
# "id": "1365561108437680129",
|
||
# "qty": "0.000004",
|
||
# "quoteQty": "0.000004000000000000",
|
||
# "time": "1677523569575",
|
||
# "isBuyerMaker": False,
|
||
# "isBestMatch": True
|
||
# },
|
||
# ]
|
||
#
|
||
return self.parse_trades(response, market, since, limit)
|
||
|
||
async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
||
"""
|
||
fetch all trades made by the user
|
||
|
||
https://coins-docs.github.io/rest-api/#account-trade-list-user_data
|
||
|
||
: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(default 500, max 1000)
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
|
||
"""
|
||
if symbol is None:
|
||
raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
if since is not None:
|
||
request['startTime'] = since
|
||
# since work properly only when it is "younger" than last 'limit' trade
|
||
request['limit'] = 1000
|
||
elif limit is not None:
|
||
request['limit'] = limit
|
||
response = await self.privateGetOpenapiV1MyTrades(self.extend(request, params))
|
||
return self.parse_trades(response, market, since, limit)
|
||
|
||
async 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://coins-docs.github.io/rest-api/#account-trade-list-user_data
|
||
|
||
: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')
|
||
request: dict = {
|
||
'orderId': id,
|
||
}
|
||
return await self.fetch_my_trades(symbol, since, limit, self.extend(request, params))
|
||
|
||
def parse_trade(self, trade: dict, market: Market = None) -> Trade:
|
||
#
|
||
# fetchTrades
|
||
# {
|
||
# "price": "89685.8",
|
||
# "id": "1365561108437680129",
|
||
# "qty": "0.000004",
|
||
# "quoteQty": "0.000004000000000000", # warning: report to exchange - self is not quote quantity, self is base quantity
|
||
# "time": "1677523569575",
|
||
# "isBuyerMaker": False,
|
||
# "isBestMatch": True
|
||
# },
|
||
#
|
||
# fetchMyTrades
|
||
# {
|
||
# "symbol": "ETHUSDT",
|
||
# "id": 1375426310524125185,
|
||
# "orderId": 1375426310415879614,
|
||
# "price": "1580.91",
|
||
# "qty": "0.01",
|
||
# "quoteQty": "15.8091",
|
||
# "commission": "0",
|
||
# "commissionAsset": "USDT",
|
||
# "time": 1678699593307,
|
||
# "isBuyer": False,
|
||
# "isMaker":false,
|
||
# "isBestMatch":false
|
||
# }
|
||
#
|
||
# createOrder
|
||
# {
|
||
# "price": "1579.51",
|
||
# "qty": "0.001899",
|
||
# "commission": "0",
|
||
# "commissionAsset": "ETH",
|
||
# "tradeId":1375445992035598337
|
||
# }
|
||
#
|
||
marketId = self.safe_string(trade, 'symbol')
|
||
market = self.safe_market(marketId, market)
|
||
symbol = market['symbol']
|
||
id = self.safe_string_2(trade, 'id', 'tradeId')
|
||
orderId = self.safe_string(trade, 'orderId')
|
||
timestamp = self.safe_integer(trade, 'time')
|
||
priceString = self.safe_string(trade, 'price')
|
||
amountString = self.safe_string(trade, 'qty')
|
||
type = None
|
||
fee = None
|
||
feeCost = self.safe_string(trade, 'commission')
|
||
if feeCost is not None:
|
||
feeCurrencyId = self.safe_string(trade, 'commissionAsset')
|
||
fee = {
|
||
'cost': feeCost,
|
||
'currency': self.safe_currency_code(feeCurrencyId),
|
||
}
|
||
isBuyer = self.safe_bool_2(trade, 'isBuyer', 'isBuyerMaker', None)
|
||
side = None
|
||
if isBuyer is not None:
|
||
side = 'buy' if (isBuyer is True) else 'sell'
|
||
isMaker = self.safe_string_2(trade, 'isMaker', None)
|
||
takerOrMaker = None
|
||
if isMaker is not None:
|
||
takerOrMaker = 'maker' if (isMaker == 'true') else 'taker'
|
||
costString = None
|
||
if orderId is not None:
|
||
costString = self.safe_string(trade, 'quoteQty')
|
||
return self.safe_trade({
|
||
'id': id,
|
||
'order': orderId,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'symbol': symbol,
|
||
'type': type,
|
||
'side': side,
|
||
'takerOrMaker': takerOrMaker,
|
||
'price': priceString,
|
||
'amount': amountString,
|
||
'cost': costString,
|
||
'fee': fee,
|
||
'info': trade,
|
||
}, market)
|
||
|
||
async def fetch_balance(self, params={}) -> Balances:
|
||
"""
|
||
query for balance and get the amount of funds available for trading or funds locked in orders
|
||
|
||
https://coins-docs.github.io/rest-api/#accept-the-quote
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
response = await self.privateGetOpenapiV1Account(params)
|
||
#
|
||
# {
|
||
# "accountType": "SPOT",
|
||
# "balances": [
|
||
# {
|
||
# "asset": "BTC",
|
||
# "free": "4723846.89208129",
|
||
# "locked": "0.00000000"
|
||
# },
|
||
# {
|
||
# "asset": "LTC",
|
||
# "free": "4763368.68006011",
|
||
# "locked": "0.00000000"
|
||
# }
|
||
# ],
|
||
# "canDeposit": True,
|
||
# "canTrade": True,
|
||
# "canWithdraw": True,
|
||
# "updateTime": "1677430932528"
|
||
# }
|
||
#
|
||
return self.parse_balance(response)
|
||
|
||
def parse_balance(self, response) -> Balances:
|
||
balances = self.safe_list(response, 'balances', [])
|
||
result: dict = {
|
||
'info': response,
|
||
'timestamp': None,
|
||
'datetime': None,
|
||
}
|
||
for i in range(0, len(balances)):
|
||
balance = balances[i]
|
||
currencyId = self.safe_string(balance, 'asset')
|
||
code = self.safe_currency_code(currencyId)
|
||
account = self.account()
|
||
account['free'] = self.safe_string(balance, 'free')
|
||
account['used'] = self.safe_string(balance, 'locked')
|
||
result[code] = account
|
||
return self.safe_balance(result)
|
||
|
||
async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
||
"""
|
||
create a trade order
|
||
|
||
https://coins-docs.github.io/rest-api/#new-order--trade
|
||
|
||
:param str symbol: unified symbol of the market to create an order in
|
||
:param str type: 'market', 'limit', 'stop_loss', 'take_profit', 'stop_loss_limit', 'take_profit_limit' or 'limit_maker'
|
||
:param str side: 'buy' or 'sell'
|
||
:param float amount: how much of currency you want to trade in units of 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 float [params.cost]: the quote quantity that can be used alternative for the amount for market buy orders
|
||
:param bool [params.test]: set to True to test an order, no order will be created but the request will be validated
|
||
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
# todo: add test order low priority
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
testOrder = self.safe_bool(params, 'test', False)
|
||
params = self.omit(params, 'test')
|
||
orderType = self.safe_string(params, 'type', type)
|
||
orderType = self.encode_order_type(orderType)
|
||
params = self.omit(params, 'type')
|
||
orderSide = self.encode_order_side(side)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
'type': orderType,
|
||
'side': orderSide,
|
||
}
|
||
options = self.safe_value(self.options, 'createOrder', {})
|
||
newOrderRespType = self.safe_value(options, 'newOrderRespType', {})
|
||
# if limit order
|
||
if orderType == 'LIMIT' or orderType == 'STOP_LOSS_LIMIT' or orderType == 'TAKE_PROFIT_LIMIT' or orderType == 'LIMIT_MAKER':
|
||
if price is None:
|
||
raise ArgumentsRequired(self.id + ' createOrder() requires a price argument for a ' + type + ' order')
|
||
newOrderRespType = self.safe_string(newOrderRespType, 'limit', 'FULL')
|
||
request['price'] = self.price_to_precision(symbol, price)
|
||
request['quantity'] = self.amount_to_precision(symbol, amount)
|
||
if orderType != 'LIMIT_MAKER':
|
||
request['timeInForce'] = self.safe_string(options, 'timeInForce', 'GTC')
|
||
# if market order
|
||
elif orderType == 'MARKET' or orderType == 'STOP_LOSS' or orderType == 'TAKE_PROFIT':
|
||
newOrderRespType = self.safe_string(newOrderRespType, 'market', 'FULL')
|
||
if orderSide == 'SELL':
|
||
request['quantity'] = self.amount_to_precision(symbol, amount)
|
||
elif orderSide == 'BUY':
|
||
quoteAmount = None
|
||
createMarketBuyOrderRequiresPrice = True
|
||
createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
|
||
cost = self.safe_number_2(params, 'cost', 'quoteOrderQty')
|
||
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 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['quoteOrderQty'] = quoteAmount
|
||
if orderType == 'STOP_LOSS' or orderType == 'STOP_LOSS_LIMIT' or orderType == 'TAKE_PROFIT' or orderType == 'TAKE_PROFIT_LIMIT':
|
||
triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
|
||
if triggerPrice is None:
|
||
raise InvalidOrder(self.id + ' createOrder() requires a triggerPrice or stopPrice param for stop_loss, take_profit, stop_loss_limit, and take_profit_limit orders')
|
||
request['stopPrice'] = self.price_to_precision(symbol, triggerPrice)
|
||
request['newOrderRespType'] = newOrderRespType
|
||
params = self.omit(params, 'price', 'stopPrice', 'triggerPrice', 'quantity', 'quoteOrderQty')
|
||
response = None
|
||
if testOrder:
|
||
response = await self.privatePostOpenapiV1OrderTest(self.extend(request, params))
|
||
else:
|
||
response = await self.privatePostOpenapiV1Order(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "symbol": "ETHUSDT",
|
||
# "orderId": "1375407140139731486",
|
||
# "clientOrderId": "1375407140139733169",
|
||
# "transactTime": "1678697308023",
|
||
# "price": "1600",
|
||
# "origQty": "0.02",
|
||
# "executedQty": "0.02",
|
||
# "cummulativeQuoteQty": "31.9284",
|
||
# "status": "FILLED",
|
||
# "timeInForce": "GTC",
|
||
# "type": "LIMIT",
|
||
# "side": "BUY",
|
||
# "stopPrice": "0",
|
||
# "origQuoteOrderQty": "0",
|
||
# "fills": [
|
||
# {
|
||
# "price": "1596.42",
|
||
# "qty": "0.02",
|
||
# "commission": "0",
|
||
# "commissionAsset": "ETH",
|
||
# "tradeId": "1375407140281532417"
|
||
# }
|
||
# ]
|
||
# },
|
||
#
|
||
return self.parse_order(response, market)
|
||
|
||
async def fetch_order(self, id: str, symbol: Str = None, params={}):
|
||
"""
|
||
fetches information on an order made by the user
|
||
|
||
https://coins-docs.github.io/rest-api/#query-order-user_data
|
||
|
||
:param int|str id: order id
|
||
:param str symbol: not used by coinsph fetchOrder()
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
request: dict = {}
|
||
clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
|
||
if clientOrderId is not None:
|
||
request['origClientOrderId'] = clientOrderId
|
||
else:
|
||
request['orderId'] = id
|
||
params = self.omit(params, ['clientOrderId', 'origClientOrderId'])
|
||
response = await self.privateGetOpenapiV1Order(self.extend(request, params))
|
||
return self.parse_order(response)
|
||
|
||
async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
||
"""
|
||
fetch all unfilled currently open orders
|
||
|
||
https://coins-docs.github.io/rest-api/#current-open-orders-user_data
|
||
|
||
: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
|
||
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
market = None
|
||
request: dict = {}
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
request['symbol'] = market['id']
|
||
response = await self.privateGetOpenapiV1OpenOrders(self.extend(request, params))
|
||
return self.parse_orders(response, market, since, limit)
|
||
|
||
async 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://coins-docs.github.io/rest-api/#history-orders-user_data
|
||
|
||
: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(default 500, max 1000)
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
if symbol is None:
|
||
raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
if since is not None:
|
||
request['startTime'] = since
|
||
# since work properly only when it is "younger" than last 'limit' order
|
||
request['limit'] = 1000
|
||
elif limit is not None:
|
||
request['limit'] = limit
|
||
response = await self.privateGetOpenapiV1HistoryOrders(self.extend(request, params))
|
||
return self.parse_orders(response, market, since, limit)
|
||
|
||
async def cancel_order(self, id: str, symbol: Str = None, params={}):
|
||
"""
|
||
cancels an open order
|
||
|
||
https://coins-docs.github.io/rest-api/#cancel-order-trade
|
||
|
||
:param str id: order id
|
||
:param str symbol: not used by coinsph cancelOrder()
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
request: dict = {}
|
||
clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
|
||
if clientOrderId is not None:
|
||
request['origClientOrderId'] = clientOrderId
|
||
else:
|
||
request['orderId'] = id
|
||
params = self.omit(params, ['clientOrderId', 'origClientOrderId'])
|
||
response = await self.privateDeleteOpenapiV1Order(self.extend(request, params))
|
||
return self.parse_order(response)
|
||
|
||
async def cancel_all_orders(self, symbol: Str = None, params={}):
|
||
"""
|
||
cancel open orders of market
|
||
|
||
https://coins-docs.github.io/rest-api/#cancel-all-open-orders-on-a-symbol-trade
|
||
|
||
:param str symbol: unified market symbol
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
if symbol is None:
|
||
raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
|
||
await self.load_markets()
|
||
market = None
|
||
request: dict = {}
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
request['symbol'] = market['id']
|
||
response = await self.privateDeleteOpenapiV1OpenOrders(self.extend(request, params))
|
||
return self.parse_orders(response, market)
|
||
|
||
def parse_order(self, order: dict, market: Market = None) -> Order:
|
||
#
|
||
# createOrder POST /openapi/v1/order
|
||
# {
|
||
# "symbol": "ETHUSDT",
|
||
# "orderId": 1375445991893797391,
|
||
# "clientOrderId": "1375445991893799115",
|
||
# "transactTime": 1678701939513,
|
||
# "price": "0",
|
||
# "origQty": "0",
|
||
# "executedQty": "0.001899",
|
||
# "cummulativeQuoteQty": "2.99948949",
|
||
# "status": "FILLED",
|
||
# "timeInForce": "GTC",
|
||
# "type": "MARKET",
|
||
# "side": "BUY",
|
||
# "stopPrice": "0",
|
||
# "origQuoteOrderQty": "3",
|
||
# "fills": [
|
||
# {
|
||
# "price": "1579.51",
|
||
# "qty": "0.001899",
|
||
# "commission": "0",
|
||
# "commissionAsset": "ETH",
|
||
# "tradeId":1375445992035598337
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
# fetchOrder GET /openapi/v1/order
|
||
# fetchOpenOrders GET /openapi/v1/openOrders
|
||
# fetchClosedOrders GET /openapi/v1/historyOrders
|
||
# cancelAllOrders DELETE /openapi/v1/openOrders
|
||
# {
|
||
# "symbol": "DOGEPHP",
|
||
# "orderId":1375465375097982423,
|
||
# "clientOrderId": "1375465375098001241",
|
||
# "price": "0",
|
||
# "origQty": "0",
|
||
# "executedQty": "13",
|
||
# "cummulativeQuoteQty": "49.621",
|
||
# "status": "FILLED",
|
||
# "timeInForce": "GTC",
|
||
# "type": "MARKET",
|
||
# "side": "BUY",
|
||
# "stopPrice": "0",
|
||
# "time":1678704250171,
|
||
# "updateTime":1678704250256,
|
||
# "isWorking":false,
|
||
# "origQuoteOrderQty": "50"
|
||
# }
|
||
#
|
||
# cancelOrder DELETE /openapi/v1/order
|
||
# {
|
||
# "symbol": "ETHPHP",
|
||
# "orderId":1375609441915774332,
|
||
# "clientOrderId": "1375609441915899557",
|
||
# "price": "96000",
|
||
# "origQty": "0.001",
|
||
# "executedQty": "0",
|
||
# "cummulativeQuoteQty": "0",
|
||
# "status": "CANCELED",
|
||
# "timeInForce": "GTC",
|
||
# "type": "LIMIT",
|
||
# "side": "SELL",
|
||
# "stopPrice": "0",
|
||
# "origQuoteOrderQty": "0"
|
||
# }
|
||
#
|
||
id = self.safe_string(order, 'orderId')
|
||
marketId = self.safe_string(order, 'symbol')
|
||
market = self.safe_market(marketId, market)
|
||
timestamp = self.safe_integer_2(order, 'time', 'transactTime')
|
||
trades = self.safe_value(order, 'fills', None)
|
||
triggerPrice = self.safe_string(order, 'stopPrice')
|
||
if Precise.string_eq(triggerPrice, '0'):
|
||
triggerPrice = None
|
||
return self.safe_order({
|
||
'id': id,
|
||
'clientOrderId': self.safe_string(order, 'clientOrderId'),
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'lastTradeTimestamp': None,
|
||
'status': self.parse_order_status(self.safe_string(order, 'status')),
|
||
'symbol': market['symbol'],
|
||
'type': self.parse_order_type(self.safe_string(order, 'type')),
|
||
'timeInForce': self.parse_order_time_in_force(self.safe_string(order, 'timeInForce')),
|
||
'side': self.parse_order_side(self.safe_string(order, 'side')),
|
||
'price': self.safe_string(order, 'price'),
|
||
'triggerPrice': triggerPrice,
|
||
'average': None,
|
||
'amount': self.safe_string(order, 'origQty'),
|
||
'cost': self.safe_string(order, 'cummulativeQuoteQty'),
|
||
'filled': self.safe_string(order, 'executedQty'),
|
||
'remaining': None,
|
||
'fee': None,
|
||
'fees': None,
|
||
'trades': trades,
|
||
'info': order,
|
||
}, market)
|
||
|
||
def parse_order_side(self, status):
|
||
statuses: dict = {
|
||
'BUY': 'buy',
|
||
'SELL': 'sell',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
def encode_order_side(self, status):
|
||
statuses: dict = {
|
||
'buy': 'BUY',
|
||
'sell': 'SELL',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
def parse_order_type(self, status):
|
||
statuses: dict = {
|
||
'MARKET': 'market',
|
||
'LIMIT': 'limit',
|
||
'LIMIT_MAKER': 'limit',
|
||
'STOP_LOSS': 'market',
|
||
'STOP_LOSS_LIMIT': 'limit',
|
||
'TAKE_PROFIT': 'market',
|
||
'TAKE_PROFIT_LIMIT': 'limit',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
def encode_order_type(self, status):
|
||
statuses: dict = {
|
||
'market': 'MARKET',
|
||
'limit': 'LIMIT',
|
||
'limit_maker': 'LIMIT_MAKER',
|
||
'stop_loss': 'STOP_LOSS',
|
||
'stop_loss_limit': 'STOP_LOSS_LIMIT',
|
||
'take_profit': 'TAKE_PROFIT',
|
||
'take_profit_limit': 'TAKE_PROFIT_LIMIT',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
def parse_order_status(self, status: Str):
|
||
statuses: dict = {
|
||
'NEW': 'open',
|
||
'FILLED': 'closed',
|
||
'CANCELED': 'canceled',
|
||
'PARTIALLY_FILLED': 'open',
|
||
'PARTIALLY_CANCELED': 'canceled',
|
||
'REJECTED': 'rejected',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
def parse_order_time_in_force(self, status):
|
||
statuses: dict = {
|
||
'GTC': 'GTC',
|
||
'FOK': 'FOK',
|
||
'IOC': 'IOC',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
async def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
|
||
"""
|
||
fetch the trading fees for a market
|
||
|
||
https://coins-docs.github.io/rest-api/#trade-fee-user_data
|
||
|
||
: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>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
response = await self.privateGetOpenapiV1AssetTradeFee(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "symbol": "ETHUSDT",
|
||
# "makerCommission": "0.0025",
|
||
# "takerCommission": "0.003"
|
||
# }
|
||
# ]
|
||
#
|
||
tradingFee = self.safe_dict(response, 0, {})
|
||
return self.parse_trading_fee(tradingFee, market)
|
||
|
||
async def fetch_trading_fees(self, params={}) -> TradingFees:
|
||
"""
|
||
fetch the trading fees for multiple markets
|
||
|
||
https://coins-docs.github.io/rest-api/#trade-fee-user_data
|
||
|
||
: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
|
||
"""
|
||
await self.load_markets()
|
||
response = await self.privateGetOpenapiV1AssetTradeFee(params)
|
||
#
|
||
# [
|
||
# {
|
||
# "symbol": "ETHPHP",
|
||
# "makerCommission": "0.0025",
|
||
# "takerCommission": "0.003"
|
||
# },
|
||
# {
|
||
# "symbol": "UNIPHP",
|
||
# "makerCommission": "0.0025",
|
||
# "takerCommission": "0.003"
|
||
# },
|
||
# ]
|
||
#
|
||
result: dict = {}
|
||
for i in range(0, len(response)):
|
||
fee = self.parse_trading_fee(response[i])
|
||
symbol = fee['symbol']
|
||
result[symbol] = fee
|
||
return result
|
||
|
||
def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
|
||
#
|
||
# {
|
||
# "symbol": "ETHUSDT",
|
||
# "makerCommission": "0.0025",
|
||
# "takerCommission": "0.003"
|
||
# }
|
||
#
|
||
marketId = self.safe_string(fee, 'symbol')
|
||
market = self.safe_market(marketId, market)
|
||
symbol = market['symbol']
|
||
return {
|
||
'info': fee,
|
||
'symbol': symbol,
|
||
'maker': self.safe_number(fee, 'makerCommission'),
|
||
'taker': self.safe_number(fee, 'takerCommission'),
|
||
'percentage': None,
|
||
'tierBased': None,
|
||
}
|
||
|
||
async def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction:
|
||
"""
|
||
make a withdrawal to coins_ph account
|
||
|
||
https://coins-docs.github.io/rest-api/#withdrawuser_data
|
||
|
||
:param str code: unified currency code
|
||
:param float amount: the amount to withdraw
|
||
:param str address: not used by coinsph withdraw()
|
||
: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>`
|
||
"""
|
||
options = self.safe_value(self.options, 'withdraw')
|
||
warning = self.safe_bool(options, 'warning', True)
|
||
if warning:
|
||
raise InvalidAddress(self.id + " withdraw() makes a withdrawals only to coins_ph account, add .options['withdraw']['warning'] = False to make a withdrawal to your coins_ph account")
|
||
networkCode = self.safe_string(params, 'network')
|
||
networkId = self.network_code_to_id(networkCode, code)
|
||
if networkId is None:
|
||
raise BadRequest(self.id + ' withdraw() require network parameter')
|
||
await self.load_markets()
|
||
currency = self.currency(code)
|
||
request: dict = {
|
||
'coin': currency['id'],
|
||
'amount': self.number_to_string(amount),
|
||
'network': networkId,
|
||
'address': address,
|
||
}
|
||
if tag is not None:
|
||
request['withdrawOrderId'] = tag
|
||
params = self.omit(params, 'network')
|
||
response = await self.privatePostOpenapiWalletV1WithdrawApply(self.extend(request, params))
|
||
return self.parse_transaction(response, currency)
|
||
|
||
async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
||
"""
|
||
fetch all deposits made to an account
|
||
|
||
https://coins-docs.github.io/rest-api/#deposit-history-user_data
|
||
|
||
: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
|
||
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
||
"""
|
||
# todo: returns an empty array - find out why
|
||
await self.load_markets()
|
||
currency = None
|
||
request: dict = {}
|
||
if code is not None:
|
||
currency = self.currency(code)
|
||
request['coin'] = currency['id']
|
||
if since is not None:
|
||
request['startTime'] = since
|
||
if limit is not None:
|
||
request['limit'] = limit
|
||
response = await self.privateGetOpenapiWalletV1DepositHistory(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "id": "d_769800519366885376",
|
||
# "amount": "0.001",
|
||
# "coin": "BNB",
|
||
# "network": "BNB",
|
||
# "status": 0,
|
||
# "address": "bnb136ns6lfw4zs5hg4n85vdthaad7hq5m4gtkgf23",
|
||
# "addressTag": "101764890",
|
||
# "txId": "98A3EA560C6B3336D348B6C83F0F95ECE4F1F5919E94BD006E5BF3BF264FACFC",
|
||
# "insertTime": 1661493146000,
|
||
# "confirmNo": 10,
|
||
# },
|
||
# {
|
||
# "id": "d_769754833590042625",
|
||
# "amount":"0.5",
|
||
# "coin":"IOTA",
|
||
# "network":"IOTA",
|
||
# "status":1,
|
||
# "address":"SIZ9VLMHWATXKV99LH99CIGFJFUMLEHGWVZVNNZXRJJVWBPHYWPPBOSDORZ9EQSHCZAMPVAPGFYQAUUV9DROOXJLNW",
|
||
# "addressTag":"",
|
||
# "txId":"ESBFVQUTPIWQNJSPXFNHNYHSQNTGKRVKPRABQWTAXCDWOAKDKYWPTVG9BGXNVNKTLEJGESAVXIKIZ9999",
|
||
# "insertTime":1599620082000,
|
||
# "confirmNo": 20,
|
||
# }
|
||
# ]
|
||
#
|
||
return self.parse_transactions(response, currency, since, limit)
|
||
|
||
async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
||
"""
|
||
fetch all withdrawals made from an account
|
||
|
||
https://coins-docs.github.io/rest-api/#withdraw-history-user_data
|
||
|
||
: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
|
||
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
||
"""
|
||
# todo: returns an empty array - find out why
|
||
await self.load_markets()
|
||
currency = None
|
||
request: dict = {}
|
||
if code is not None:
|
||
currency = self.currency(code)
|
||
request['coin'] = currency['id']
|
||
if since is not None:
|
||
request['startTime'] = since
|
||
if limit is not None:
|
||
request['limit'] = limit
|
||
response = await self.privateGetOpenapiWalletV1WithdrawHistory(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "id": "459890698271244288",
|
||
# "amount": "0.01",
|
||
# "transactionFee": "0",
|
||
# "coin": "ETH",
|
||
# "status": 1,
|
||
# "address": "0x386AE30AE2dA293987B5d51ddD03AEb70b21001F",
|
||
# "addressTag": "",
|
||
# "txId": "0x4ae2fed36a90aada978fc31c38488e8b60d7435cfe0b4daed842456b4771fcf7",
|
||
# "applyTime": 1673601139000,
|
||
# "network": "ETH",
|
||
# "withdrawOrderId": "thomas123",
|
||
# "info": "",
|
||
# "confirmNo": 100
|
||
# },
|
||
# {
|
||
# "id": "451899190746456064",
|
||
# "amount": "0.00063",
|
||
# "transactionFee": "0.00037",
|
||
# "coin": "ETH",
|
||
# "status": 1,
|
||
# "address": "0x386AE30AE2dA293987B5d51ddD03AEb70b21001F",
|
||
# "addressTag": "",
|
||
# "txId": "0x62690ca4f9d6a8868c258e2ce613805af614d9354dda7b39779c57b2e4da0260",
|
||
# "applyTime": 1671695815000,
|
||
# "network": "ETH",
|
||
# "withdrawOrderId": "",
|
||
# "info": "",
|
||
# "confirmNo": 100
|
||
# }
|
||
# ]
|
||
#
|
||
return self.parse_transactions(response, currency, since, limit)
|
||
|
||
def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
|
||
#
|
||
# fetchDeposits
|
||
# {
|
||
# "coin": "PHP",
|
||
# "address": "Internal Transfer",
|
||
# "addressTag": "Internal Transfer",
|
||
# "amount": "0.02",
|
||
# "id": "31312321312312312312322",
|
||
# "network": "Internal",
|
||
# "transferType": "0",
|
||
# "status": 3,
|
||
# "confirmTimes": "",
|
||
# "unlockConfirm": "",
|
||
# "txId": "Internal Transfer",
|
||
# "insertTime": 1657623798000,
|
||
# "depositOrderId": "the deposit id which created by client"
|
||
# }
|
||
#
|
||
# fetchWithdrawals
|
||
# {
|
||
# "coin": "BTC",
|
||
# "address": "Internal Transfer",
|
||
# "amount": "0.1",
|
||
# "id": "1201515362324421632",
|
||
# "withdrawOrderId": null,
|
||
# "network": "Internal",
|
||
# "transferType": "0",
|
||
# "status": 0,
|
||
# "transactionFee": "0",
|
||
# "confirmNo": 0,
|
||
# "info": "{}",
|
||
# "txId": "Internal Transfer",
|
||
# "applyTime": 1657967792000
|
||
# }
|
||
#
|
||
# todo: self is in progress
|
||
id = self.safe_string(transaction, 'id')
|
||
address = self.safe_string(transaction, 'address')
|
||
tag = self.safe_string(transaction, 'addressTag')
|
||
if tag is not None:
|
||
if len(tag) < 1:
|
||
tag = None
|
||
txid = self.safe_string(transaction, 'txId')
|
||
currencyId = self.safe_string(transaction, 'coin')
|
||
code = self.safe_currency_code(currencyId, currency)
|
||
timestamp = None
|
||
timestamp = self.safe_integer_2(transaction, 'insertTime', 'applyTime')
|
||
updated = None
|
||
type = None
|
||
withdrawOrderId = self.safe_string(transaction, 'withdrawOrderId')
|
||
depositOrderId = self.safe_string(transaction, 'depositOrderId')
|
||
if withdrawOrderId is not None:
|
||
type = 'withdrawal'
|
||
elif depositOrderId is not None:
|
||
type = 'deposit'
|
||
status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
|
||
amount = self.safe_number(transaction, 'amount')
|
||
feeCost = self.safe_number(transaction, 'transactionFee')
|
||
fee = None
|
||
if feeCost is not None:
|
||
fee = {'currency': code, 'cost': feeCost}
|
||
network = self.safe_string(transaction, 'network')
|
||
internal = network == 'Internal'
|
||
return {
|
||
'info': transaction,
|
||
'id': id,
|
||
'txid': txid,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'network': network,
|
||
'address': address,
|
||
'addressTo': address,
|
||
'addressFrom': None,
|
||
'tag': tag,
|
||
'tagTo': tag,
|
||
'tagFrom': None,
|
||
'type': type,
|
||
'amount': amount,
|
||
'currency': code,
|
||
'status': status,
|
||
'updated': updated,
|
||
'internal': internal,
|
||
'comment': None,
|
||
'fee': fee,
|
||
}
|
||
|
||
def parse_transaction_status(self, status: Str):
|
||
statuses: dict = {
|
||
'0': 'pending',
|
||
'1': 'ok',
|
||
'2': 'failed',
|
||
'3': 'pending',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
|
||
"""
|
||
fetch the deposit address for a currency associated with self account
|
||
|
||
https://coins-docs.github.io/rest-api/#deposit-address-user_data
|
||
|
||
:param str code: unified currency code
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param str [params.network]: network for fetch deposit address
|
||
:returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
|
||
"""
|
||
networkCode = self.safe_string(params, 'network')
|
||
networkId = self.network_code_to_id(networkCode, code)
|
||
if networkId is None:
|
||
raise BadRequest(self.id + ' fetchDepositAddress() require network parameter')
|
||
await self.load_markets()
|
||
currency = self.currency(code)
|
||
request: dict = {
|
||
'coin': currency['id'],
|
||
'network': networkId,
|
||
}
|
||
params = self.omit(params, 'network')
|
||
response = await self.privateGetOpenapiWalletV1DepositAddress(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "coin": "ETH",
|
||
# "address": "0xfe98628173830bf79c59f04585ce41f7de168784",
|
||
# "addressTag": ""
|
||
# }
|
||
#
|
||
return self.parse_deposit_address(response, currency)
|
||
|
||
def parse_deposit_address(self, depositAddress, currency: Currency = None) -> DepositAddress:
|
||
#
|
||
# {
|
||
# "coin": "ETH",
|
||
# "address": "0xfe98628173830bf79c59f04585ce41f7de168784",
|
||
# "addressTag": ""
|
||
# }
|
||
#
|
||
currencyId = self.safe_string(depositAddress, 'coin')
|
||
parsedCurrency = self.safe_currency_code(currencyId, currency)
|
||
return {
|
||
'info': depositAddress,
|
||
'currency': parsedCurrency,
|
||
'network': None,
|
||
'address': self.safe_string(depositAddress, 'address'),
|
||
'tag': self.safe_string(depositAddress, 'addressTag'),
|
||
}
|
||
|
||
def url_encode_query(self, query={}):
|
||
encodedArrayParams = ''
|
||
keys = list(query.keys())
|
||
for i in range(0, len(keys)):
|
||
key = keys[i]
|
||
if isinstance(query[key], list):
|
||
if i != 0:
|
||
encodedArrayParams += '&'
|
||
innerArray = query[key]
|
||
query = self.omit(query, key)
|
||
encodedArrayParam = self.parse_array_param(innerArray, key)
|
||
encodedArrayParams += encodedArrayParam
|
||
encodedQuery = self.urlencode(query)
|
||
if len(encodedQuery) != 0:
|
||
return encodedQuery + '&' + encodedArrayParams
|
||
else:
|
||
return encodedArrayParams
|
||
|
||
def parse_array_param(self, array, key):
|
||
stringifiedArray = self.json(array)
|
||
stringifiedArray = stringifiedArray.replace('[', '%5B')
|
||
stringifiedArray = stringifiedArray.replace(']', '%5D')
|
||
urlEncodedParam = key + '=' + stringifiedArray
|
||
return urlEncodedParam
|
||
|
||
def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
|
||
url = self.urls['api'][api]
|
||
query = self.omit(params, self.extract_params(path))
|
||
endpoint = self.implode_params(path, params)
|
||
url = url + '/' + endpoint
|
||
if api == 'private':
|
||
self.check_required_credentials()
|
||
query['timestamp'] = self.milliseconds()
|
||
recvWindow = self.safe_integer(query, 'recvWindow')
|
||
if recvWindow is None:
|
||
defaultRecvWindow = self.safe_integer(self.options, 'recvWindow')
|
||
if defaultRecvWindow is not None:
|
||
query['recvWindow'] = defaultRecvWindow
|
||
query = self.url_encode_query(query)
|
||
signature = self.hmac(self.encode(query), self.encode(self.secret), hashlib.sha256)
|
||
url = url + '?' + query + '&signature=' + signature
|
||
headers = {
|
||
'X-COINS-APIKEY': self.apiKey,
|
||
}
|
||
else:
|
||
query = self.url_encode_query(query)
|
||
if len(query) != 0:
|
||
url += '?' + query
|
||
return {'url': url, 'method': method, 'body': body, 'headers': headers}
|
||
|
||
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
|
||
responseCode = self.safe_string(response, 'code', None)
|
||
if (responseCode is not None) and (responseCode != '200') and (responseCode != '0'):
|
||
feedback = self.id + ' ' + body
|
||
self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
|
||
self.throw_exactly_matched_exception(self.exceptions['exact'], responseCode, feedback)
|
||
raise ExchangeError(feedback)
|
||
return None
|