3553 lines
152 KiB
Python
3553 lines
152 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.poloniex import ImplicitAPI
|
||
import hashlib
|
||
from ccxt.base.types import Any, Balances, Bool, Currencies, Currency, DepositAddress, Int, Leverage, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction, 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 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 NotSupported
|
||
from ccxt.base.errors import ExchangeNotAvailable
|
||
from ccxt.base.errors import OnMaintenance
|
||
from ccxt.base.errors import RequestTimeout
|
||
from ccxt.base.decimal_to_precision import TICK_SIZE
|
||
from ccxt.base.precise import Precise
|
||
|
||
|
||
class poloniex(Exchange, ImplicitAPI):
|
||
|
||
def describe(self) -> Any:
|
||
return self.deep_extend(super(poloniex, self).describe(), {
|
||
'id': 'poloniex',
|
||
'name': 'Poloniex',
|
||
'countries': ['US'],
|
||
# 200 requests per second for some unauthenticated market endpoints => 1000ms / 200 = 5ms between requests
|
||
'rateLimit': 5,
|
||
'certified': False,
|
||
'pro': True,
|
||
'has': {
|
||
'CORS': None,
|
||
'spot': True,
|
||
'margin': None, # has but not fully implemented
|
||
'swap': True,
|
||
'future': True,
|
||
'option': False,
|
||
'addMargin': True,
|
||
'cancelAllOrders': True,
|
||
'cancelOrder': True,
|
||
'cancelOrders': None, # not yet implemented, because RL is worse than cancelOrder
|
||
'createDepositAddress': True,
|
||
'createMarketBuyOrderWithCost': True,
|
||
'createMarketOrderWithCost': False,
|
||
'createMarketSellOrderWithCost': False,
|
||
'createOrder': True,
|
||
'createOrders': None, # not yet implemented, because RL is worse than createOrder
|
||
'createStopOrder': True,
|
||
'createTriggerOrder': True,
|
||
'editOrder': True,
|
||
'fetchBalance': True,
|
||
'fetchClosedOrder': False,
|
||
'fetchClosedOrders': True,
|
||
'fetchCurrencies': True,
|
||
'fetchDepositAddress': True,
|
||
'fetchDepositAddresses': False,
|
||
'fetchDepositAddressesByNetwork': False,
|
||
'fetchDeposits': True,
|
||
'fetchDepositsWithdrawals': True,
|
||
'fetchDepositWithdrawFee': 'emulated',
|
||
'fetchDepositWithdrawFees': True,
|
||
'fetchFundingHistory': False,
|
||
'fetchFundingInterval': False,
|
||
'fetchFundingIntervals': False,
|
||
'fetchFundingRate': False,
|
||
'fetchFundingRateHistory': False,
|
||
'fetchFundingRates': None, # has but not implemented
|
||
'fetchLedger': None, # has but not implemented
|
||
'fetchLeverage': True,
|
||
'fetchLiquidations': None, # has but not implemented
|
||
'fetchMarginMode': False,
|
||
'fetchMarkets': True,
|
||
'fetchMyTrades': True,
|
||
'fetchOHLCV': True,
|
||
'fetchOpenInterestHistory': False,
|
||
'fetchOpenOrder': False,
|
||
'fetchOpenOrders': True,
|
||
'fetchOrder': True,
|
||
'fetchOrderBook': True,
|
||
'fetchOrderBooks': False,
|
||
'fetchOrderTrades': True,
|
||
'fetchPosition': False,
|
||
'fetchPositionMode': True,
|
||
'fetchPositions': True,
|
||
'fetchTicker': True,
|
||
'fetchTickers': True,
|
||
'fetchTime': True,
|
||
'fetchTrades': True,
|
||
'fetchTradingFee': False,
|
||
'fetchTradingFees': True,
|
||
'fetchTransactions': 'emulated',
|
||
'fetchTransfer': False,
|
||
'fetchTransfers': False,
|
||
'fetchWithdrawals': True,
|
||
'reduceMargin': True,
|
||
'sandbox': True,
|
||
'setLeverage': True,
|
||
'setPositionMode': True,
|
||
'transfer': True,
|
||
'withdraw': True,
|
||
},
|
||
'timeframes': {
|
||
'1m': 'MINUTE_1',
|
||
'5m': 'MINUTE_5',
|
||
'10m': 'MINUTE_10', # not in swap
|
||
'15m': 'MINUTE_15',
|
||
'30m': 'MINUTE_30',
|
||
'1h': 'HOUR_1',
|
||
'2h': 'HOUR_2',
|
||
'4h': 'HOUR_4',
|
||
'6h': 'HOUR_6', # not in swap
|
||
'12h': 'HOUR_12',
|
||
'1d': 'DAY_1',
|
||
'3d': 'DAY_3',
|
||
'1w': 'WEEK_1',
|
||
'1M': 'MONTH_1', # not in swap
|
||
},
|
||
'urls': {
|
||
'logo': 'https://user-images.githubusercontent.com/1294454/27766817-e9456312-5ee6-11e7-9b3c-b628ca5626a5.jpg',
|
||
'api': {
|
||
'spot': 'https://api.poloniex.com',
|
||
'swap': 'https://api.poloniex.com',
|
||
},
|
||
'test': {
|
||
'spot': 'https://sand-spot-api-gateway.poloniex.com',
|
||
},
|
||
'www': 'https://www.poloniex.com',
|
||
'doc': 'https://api-docs.poloniex.com/spot/',
|
||
'fees': 'https://poloniex.com/fees',
|
||
'referral': 'https://poloniex.com/signup?c=UBFZJRPJ',
|
||
},
|
||
'api': {
|
||
'public': {
|
||
'get': {
|
||
'markets': 20,
|
||
'markets/{symbol}': 1,
|
||
'currencies': 20,
|
||
'currencies/{currency}': 20,
|
||
'v2/currencies': 20,
|
||
'v2/currencies/{currency}': 20,
|
||
'timestamp': 1,
|
||
'markets/price': 1,
|
||
'markets/{symbol}/price': 1,
|
||
'markets/markPrice': 1,
|
||
'markets/{symbol}/markPrice': 1,
|
||
'markets/{symbol}/markPriceComponents': 1,
|
||
'markets/{symbol}/orderBook': 1,
|
||
'markets/{symbol}/candles': 1,
|
||
'markets/{symbol}/trades': 20,
|
||
'markets/ticker24h': 20,
|
||
'markets/{symbol}/ticker24h': 20,
|
||
'markets/collateralInfo': 1,
|
||
'markets/{currency}/collateralInfo': 1,
|
||
'markets/borrowRatesInfo': 1,
|
||
},
|
||
},
|
||
'private': {
|
||
'get': {
|
||
'accounts': 4,
|
||
'accounts/balances': 4,
|
||
'accounts/{id}/balances': 4,
|
||
'accounts/activity': 20,
|
||
'accounts/transfer': 20,
|
||
'accounts/transfer/{id}': 4,
|
||
'feeinfo': 20,
|
||
'accounts/interest/history': 1,
|
||
'subaccounts': 4,
|
||
'subaccounts/balances': 20,
|
||
'subaccounts/{id}/balances': 4,
|
||
'subaccounts/transfer': 20,
|
||
'subaccounts/transfer/{id}': 4,
|
||
'wallets/addresses': 20,
|
||
'wallets/addresses/{currency}': 20,
|
||
'wallets/activity': 20,
|
||
'margin/accountMargin': 4,
|
||
'margin/borrowStatus': 4,
|
||
'margin/maxSize': 4,
|
||
'orders': 20,
|
||
'orders/{id}': 4,
|
||
'orders/killSwitchStatus': 4,
|
||
'smartorders': 20,
|
||
'smartorders/{id}': 4,
|
||
'orders/history': 20,
|
||
'smartorders/history': 20,
|
||
'trades': 20,
|
||
'orders/{id}/trades': 4,
|
||
},
|
||
'post': {
|
||
'accounts/transfer': 4,
|
||
'subaccounts/transfer': 20,
|
||
'wallets/address': 20,
|
||
'wallets/withdraw': 20,
|
||
'v2/wallets/withdraw': 20,
|
||
'orders': 4,
|
||
'orders/batch': 20,
|
||
'orders/killSwitch': 4,
|
||
'smartorders': 4,
|
||
},
|
||
'delete': {
|
||
'orders/{id}': 4,
|
||
'orders/cancelByIds': 20,
|
||
'orders': 20,
|
||
'smartorders/{id}': 4,
|
||
'smartorders/cancelByIds': 20,
|
||
'smartorders': 20,
|
||
},
|
||
'put': {
|
||
'orders/{id}': 20,
|
||
'smartorders/{id}': 20,
|
||
},
|
||
},
|
||
'swapPublic': {
|
||
'get': {
|
||
# 300 calls / second
|
||
'v3/market/allInstruments': 2 / 3,
|
||
'v3/market/instruments': 2 / 3,
|
||
'v3/market/orderBook': 2 / 3,
|
||
'v3/market/candles': 10, # candles have differnt RL
|
||
'v3/market/indexPriceCandlesticks': 10,
|
||
'v3/market/premiumIndexCandlesticks': 10,
|
||
'v3/market/markPriceCandlesticks': 10,
|
||
'v3/market/trades': 2 / 3,
|
||
'v3/market/liquidationOrder': 2 / 3,
|
||
'v3/market/tickers': 2 / 3,
|
||
'v3/market/markPrice': 2 / 3,
|
||
'v3/market/indexPrice': 2 / 3,
|
||
'v3/market/indexPriceComponents': 2 / 3,
|
||
'v3/market/fundingRate': 2 / 3,
|
||
'v3/market/openInterest': 2 / 3,
|
||
'v3/market/insurance': 2 / 3,
|
||
'v3/market/riskLimit': 2 / 3,
|
||
},
|
||
},
|
||
'swapPrivate': {
|
||
'get': {
|
||
'v3/account/balance': 4,
|
||
'v3/account/bills': 20,
|
||
'v3/trade/order/opens': 20,
|
||
'v3/trade/order/trades': 20,
|
||
'v3/trade/order/history': 20,
|
||
'v3/trade/position/opens': 20,
|
||
'v3/trade/position/history': 20, # todo: method for self
|
||
'v3/position/leverages': 20,
|
||
'v3/position/mode': 20,
|
||
},
|
||
'post': {
|
||
'v3/trade/order': 4,
|
||
'v3/trade/orders': 40,
|
||
'v3/trade/position': 20,
|
||
'v3/trade/positionAll': 100,
|
||
'v3/position/leverage': 20,
|
||
'v3/position/mode': 20,
|
||
'v3/trade/position/margin': 20,
|
||
},
|
||
'delete': {
|
||
'v3/trade/order': 2,
|
||
'v3/trade/batchOrders': 20,
|
||
'v3/trade/allOrders': 20,
|
||
},
|
||
},
|
||
},
|
||
'fees': {
|
||
'trading': {
|
||
'feeSide': 'get',
|
||
# starting from Jan 8 2020
|
||
'maker': self.parse_number('0.0009'),
|
||
'taker': self.parse_number('0.0009'),
|
||
},
|
||
'funding': {},
|
||
},
|
||
'commonCurrencies': {
|
||
'AIR': 'AirCoin',
|
||
'APH': 'AphroditeCoin',
|
||
'BCC': 'BTCtalkcoin',
|
||
'BCHABC': 'BCHABC',
|
||
'BDG': 'Badgercoin',
|
||
'BTM': 'Bitmark',
|
||
'CON': 'Coino',
|
||
'ETHTRON': 'ETH',
|
||
'GOLD': 'GoldEagles',
|
||
'GPUC': 'GPU',
|
||
'HOT': 'Hotcoin',
|
||
'ITC': 'Information Coin',
|
||
'KEY': 'KEYCoin',
|
||
'MASK': 'NFTX Hashmasks Index', # conflict with Mask Network
|
||
'MEME': 'Degenerator Meme', # Degenerator Meme migrated to Meme Inu, self exchange still has the old price
|
||
'PLX': 'ParallaxCoin',
|
||
'REPV2': 'REP',
|
||
'STR': 'XLM',
|
||
'SOC': 'SOCC',
|
||
'TRADE': 'Unitrade',
|
||
'TRXETH': 'TRX',
|
||
'XAP': 'API Coin',
|
||
# self is not documented in the API docs for Poloniex
|
||
# https://github.com/ccxt/ccxt/issues/7084
|
||
# when the user calls withdraw('USDT', amount, address, tag, params)
|
||
# with params = {'currencyToWithdrawAs': 'USDTTRON'}
|
||
# or params = {'currencyToWithdrawAs': 'USDTETH'}
|
||
# fetchWithdrawals('USDT') returns the corresponding withdrawals
|
||
# with a USDTTRON or a USDTETH currency id, respectfully
|
||
# therefore we have map them back to the original code USDT
|
||
# otherwise the returned withdrawals are filtered out
|
||
'USDTBSC': 'USDT',
|
||
'USDTTRON': 'USDT',
|
||
'USDTETH': 'USDT',
|
||
'UST': 'USTC',
|
||
},
|
||
'options': {
|
||
'defaultType': 'spot',
|
||
'createMarketBuyOrderRequiresPrice': True,
|
||
'networks': {
|
||
'BEP20': 'BSC',
|
||
'ERC20': 'ETH',
|
||
'TRC20': 'TRON',
|
||
'TRX': 'TRON',
|
||
},
|
||
'networksById': {
|
||
'TRX': 'TRC20',
|
||
'TRON': 'TRC20',
|
||
},
|
||
'limits': {
|
||
'cost': {
|
||
'min': {
|
||
'BTC': 0.0001,
|
||
'ETH': 0.0001,
|
||
'USDT': 1.0,
|
||
'TRX': 100,
|
||
'BNB': 0.06,
|
||
'USDC': 1.0,
|
||
'USDJ': 1.0,
|
||
'TUSD': 0.0001,
|
||
'DAI': 1.0,
|
||
'PAX': 1.0,
|
||
'BUSD': 1.0,
|
||
},
|
||
},
|
||
},
|
||
'accountsByType': {
|
||
'spot': 'spot',
|
||
'future': 'futures',
|
||
},
|
||
'accountsById': {
|
||
'exchange': 'spot',
|
||
'futures': 'future',
|
||
},
|
||
},
|
||
'features': {
|
||
'default': {
|
||
'sandbox': True,
|
||
'createOrder': {
|
||
'marginMode': True, # todo
|
||
'triggerPrice': True,
|
||
'triggerPriceType': None,
|
||
'triggerDirection': False,
|
||
'stopLossPrice': False, # todo
|
||
'takeProfitPrice': False, # todo
|
||
'attachedStopLossTakeProfit': None,
|
||
'timeInForce': {
|
||
'IOC': True,
|
||
'FOK': True,
|
||
'PO': True,
|
||
'GTD': False,
|
||
},
|
||
'hedged': False,
|
||
'leverage': False,
|
||
'marketBuyByCost': True,
|
||
'marketBuyRequiresPrice': False,
|
||
'selfTradePrevention': True, # todo, only for non-trigger orders
|
||
'trailing': False,
|
||
'iceberg': False,
|
||
},
|
||
'createOrders': {
|
||
'max': 20,
|
||
},
|
||
'fetchMyTrades': {
|
||
'marginMode': False,
|
||
'limit': 1000,
|
||
'daysBack': 100000,
|
||
'untilDays': 100000,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchOrder': {
|
||
'marginMode': False,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchOpenOrders': {
|
||
'marginMode': False,
|
||
'limit': 2000,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchOrders': None,
|
||
'fetchClosedOrders': None, # todo implement
|
||
'fetchOHLCV': {
|
||
'limit': 500,
|
||
},
|
||
},
|
||
'spot': {
|
||
'extends': 'default',
|
||
},
|
||
'forContracts': {
|
||
'extends': 'default',
|
||
'createOrder': {
|
||
'marginMode': True,
|
||
'triggerPrice': False,
|
||
'hedged': True,
|
||
'stpMode': True, # todo
|
||
'marketBuyByCost': False,
|
||
},
|
||
'createOrders': {
|
||
'max': 10,
|
||
},
|
||
'fetchOpenOrders': {
|
||
'limit': 100,
|
||
},
|
||
'fetchClosedOrders': {
|
||
'marginMode': False,
|
||
'limit': 100,
|
||
'daysBack': None,
|
||
'daysBackCanceled': 1 / 6,
|
||
'untilDays': None,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchMyTrades': {
|
||
'limit': 100,
|
||
'untilDays': 90,
|
||
},
|
||
},
|
||
'swap': {
|
||
'linear': {
|
||
'extends': 'forContracts',
|
||
},
|
||
'inverse': {
|
||
'extends': 'forContracts',
|
||
},
|
||
},
|
||
'future': {
|
||
'linear': {
|
||
'extends': 'forContracts',
|
||
},
|
||
'inverse': {
|
||
'extends': 'forContracts',
|
||
},
|
||
},
|
||
},
|
||
'precisionMode': TICK_SIZE,
|
||
'exceptions': {
|
||
'exact': {
|
||
# General
|
||
'500': ExchangeNotAvailable, # Internal System Error
|
||
'603': RequestTimeout, # Internal Request Timeout
|
||
'601': BadRequest, # Invalid Parameter
|
||
'415': ExchangeError, # System Error
|
||
'602': ArgumentsRequired, # Missing Required Parameters
|
||
# Accounts
|
||
'21604': BadRequest, # Invalid UserId
|
||
'21600': AuthenticationError, # Account Not Found
|
||
'21605': AuthenticationError, # Invalid Account Type
|
||
'21102': ExchangeError, # Invalid Currency
|
||
'21100': AuthenticationError, # Invalid account
|
||
'21704': AuthenticationError, # Missing UserId and/or AccountId
|
||
'21700': BadRequest, # Error updating accounts
|
||
'21705': BadRequest, # Invalid currency type
|
||
'21707': ExchangeError, # Internal accounts Error
|
||
'21708': BadRequest, # Currency not available to User
|
||
'21601': AccountSuspended, # Account locked. Contact support
|
||
'21711': ExchangeError, # Currency locked. Contact support
|
||
'21709': InsufficientFunds, # Insufficient balance
|
||
'250000': ExchangeError, # Transfer error. Try again later
|
||
'250001': BadRequest, # Invalid toAccount for transfer
|
||
'250002': BadRequest, # Invalid fromAccount for transfer
|
||
'250003': BadRequest, # Invalid transfer amount
|
||
'250004': BadRequest, # Transfer is not supported
|
||
'250005': InsufficientFunds, # Insufficient transfer balance
|
||
'250008': BadRequest, # Invalid transfer currency
|
||
'250012': ExchangeError, # Futures account is not valid
|
||
# Trading
|
||
'21110': BadRequest, # Invalid quote currency
|
||
'10040': BadSymbol, # Invalid symbol
|
||
'10060': ExchangeError, # Symbol setup error
|
||
'10020': BadSymbol, # Invalid currency
|
||
'10041': BadSymbol, # Symbol frozen for trading
|
||
'21340': OnMaintenance, # No order creation/cancelation is allowed is in Maintenane Mode
|
||
'21341': InvalidOrder, # Post-only orders type allowed is in Post Only Mode
|
||
'21342': InvalidOrder, # Price is higher than highest bid is in Maintenance Mode
|
||
'21343': InvalidOrder, # Price is lower than lowest bid is in Maintenance Mode
|
||
'21351': AccountSuspended, # Trading for self account is frozen. Contact support
|
||
'21352': BadSymbol, # Trading for self currency is frozen
|
||
'21353': PermissionDenied, # Trading for US customers is not supported
|
||
'21354': PermissionDenied, # Account needs to be verified via email before trading is enabled. Contact support
|
||
'21359': OrderNotFound, # {"code" : 21359, "message" : "Order was already canceled or filled."}
|
||
'21360': InvalidOrder, # {"code" : 21360, "message" : "Order size exceeds the limit.Please enter a smaller amount and try again."}
|
||
'24106': BadRequest, # Invalid market depth
|
||
'24201': ExchangeNotAvailable, # Service busy. Try again later
|
||
# Orders
|
||
'21301': OrderNotFound, # Order not found
|
||
'21302': ExchangeError, # Batch cancel order error
|
||
'21304': ExchangeError, # Order is filled
|
||
'21305': OrderNotFound, # Order is canceled
|
||
'21307': ExchangeError, # Error during Order Cancelation
|
||
'21309': InvalidOrder, # Order price must be greater than 0
|
||
'21310': InvalidOrder, # Order price must be less than max price
|
||
'21311': InvalidOrder, # Order price must be greater than min price
|
||
'21312': InvalidOrder, # Client orderId already exists
|
||
'21314': InvalidOrder, # Max limit of open orders(2000) exceeded
|
||
'21315': InvalidOrder, # Client orderId exceeded max length of 17 digits
|
||
'21317': InvalidOrder, # Amount must be greater than 0
|
||
'21319': InvalidOrder, # Invalid order side
|
||
'21320': InvalidOrder, # Invalid order type
|
||
'21321': InvalidOrder, # Invalid timeInForce value
|
||
'21322': InvalidOrder, # Amount is less than minAmount trade limit
|
||
'21324': BadRequest, # Invalid account type
|
||
'21327': InvalidOrder, # Order pice must be greater than 0
|
||
'21328': InvalidOrder, # Order quantity must be greater than 0
|
||
'21330': InvalidOrder, # Quantity is less than minQuantity trade limit
|
||
'21335': InvalidOrder, # Invalid priceScale for self symbol
|
||
'21336': InvalidOrder, # Invalid quantityScale for self symbol
|
||
'21337': InvalidOrder, # Invalid amountScale for self symbol
|
||
'21344': InvalidOrder, # Value of limit param is greater than max value of 100
|
||
'21345': InvalidOrder, # Value of limit param value must be greater than 0
|
||
'21346': InvalidOrder, # Order Id must be of type Long
|
||
'21348': InvalidOrder, # Order type must be LIMIT_MAKER
|
||
'21347': InvalidOrder, # Stop price must be greater than 0
|
||
'21349': InvalidOrder, # Order value is too large
|
||
'21350': InvalidOrder, # Amount must be greater than 1 USDT
|
||
'21355': ExchangeError, # Interval between startTime and endTime in trade/order history has exceeded 7 day limit
|
||
'21356': BadRequest, # Order size would cause too much price movement. Reduce order size.
|
||
'21721': InsufficientFunds,
|
||
'24101': BadSymbol, # Invalid symbol
|
||
'24102': InvalidOrder, # Invalid K-line type
|
||
'24103': InvalidOrder, # Invalid endTime
|
||
'24104': InvalidOrder, # Invalid amount
|
||
'24105': InvalidOrder, # Invalid startTime
|
||
'25020': InvalidOrder, # No active kill switch
|
||
# Smartorders
|
||
'25000': InvalidOrder, # Invalid userId
|
||
'25001': InvalidOrder, # Invalid parameter
|
||
'25002': InvalidOrder, # Invalid userId.
|
||
'25003': ExchangeError, # Unable to place order
|
||
'25004': InvalidOrder, # Client orderId already exists
|
||
'25005': ExchangeError, # Unable to place smart order
|
||
'25006': InvalidOrder, # OrderId and clientOrderId already exists
|
||
'25007': InvalidOrder, # Invalid orderid
|
||
'25008': InvalidOrder, # Both orderId and clientOrderId are required
|
||
'25009': ExchangeError, # Failed to cancel order
|
||
'25010': PermissionDenied, # Unauthorized to cancel order
|
||
'25011': InvalidOrder, # Failed to cancel due to invalid paramters
|
||
'25012': ExchangeError, # Failed to cancel
|
||
'25013': OrderNotFound, # Failed to cancel were not found
|
||
'25014': OrderNotFound, # Failed to cancel were not found
|
||
'25015': OrderNotFound, # Failed to cancel orders exist
|
||
'25016': ExchangeError, # Failed to cancel to release funds
|
||
'25017': ExchangeError, # No orders were canceled
|
||
'25018': BadRequest, # Invalid accountType
|
||
'25019': BadSymbol, # Invalid symbol
|
||
},
|
||
'broad': {
|
||
},
|
||
},
|
||
})
|
||
|
||
def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
|
||
#
|
||
# spot:
|
||
#
|
||
# [
|
||
# [
|
||
# "22814.01",
|
||
# "22937.42",
|
||
# "22832.57",
|
||
# "22937.42",
|
||
# "3916.58764051",
|
||
# "0.171199",
|
||
# "2982.64647063",
|
||
# "0.130295",
|
||
# 33,
|
||
# 0,
|
||
# "22877.449915304470460711",
|
||
# "MINUTE_5",
|
||
# 1659664800000,
|
||
# 1659665099999
|
||
# ]
|
||
# ]
|
||
#
|
||
# contract:
|
||
#
|
||
# [
|
||
# "84207.02",
|
||
# "84320.85",
|
||
# "84207.02",
|
||
# "84253.83",
|
||
# "3707.5395",
|
||
# "44",
|
||
# "14",
|
||
# "1740770040000",
|
||
# "1740770099999",
|
||
# ],
|
||
#
|
||
ohlcvLength = len(ohlcv)
|
||
isContract = ohlcvLength == 9
|
||
if isContract:
|
||
return [
|
||
self.safe_integer(ohlcv, 7),
|
||
self.safe_number(ohlcv, 2),
|
||
self.safe_number(ohlcv, 1),
|
||
self.safe_number(ohlcv, 0),
|
||
self.safe_number(ohlcv, 3),
|
||
self.safe_number(ohlcv, 5),
|
||
]
|
||
return [
|
||
self.safe_integer(ohlcv, 12),
|
||
self.safe_number(ohlcv, 2),
|
||
self.safe_number(ohlcv, 1),
|
||
self.safe_number(ohlcv, 0),
|
||
self.safe_number(ohlcv, 3),
|
||
self.safe_number(ohlcv, 5),
|
||
]
|
||
|
||
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://api-docs.poloniex.com/spot/api/public/market-data#candles
|
||
https://api-docs.poloniex.com/v3/futures/api/market/get-kline-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
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param int [params.until]: timestamp 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 int[][]: A list of candles ordered, open, high, low, close, volume
|
||
"""
|
||
self.load_markets()
|
||
paginate = False
|
||
paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate', False)
|
||
if paginate:
|
||
return self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 500)
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
'interval': self.safe_string(self.timeframes, timeframe, timeframe),
|
||
}
|
||
keyStart = 'startTime' if market['spot'] else 'sTime'
|
||
keyEnd = 'endTime' if market['spot'] else 'eTime'
|
||
if since is not None:
|
||
request[keyStart] = since
|
||
if limit is not None:
|
||
# limit should in between 100 and 500
|
||
request['limit'] = limit
|
||
request, params = self.handle_until_option(keyEnd, request, params)
|
||
if market['contract']:
|
||
if self.in_array(timeframe, ['10m', '1M']):
|
||
raise NotSupported(self.id + ' ' + timeframe + ' ' + market['type'] + ' fetchOHLCV is not supported')
|
||
responseRaw = self.swapPublicGetV3MarketCandles(self.extend(request, params))
|
||
#
|
||
# {
|
||
# code: "200",
|
||
# msg: "Success",
|
||
# data: [
|
||
# [
|
||
# "84207.02",
|
||
# "84320.85",
|
||
# "84207.02",
|
||
# "84253.83",
|
||
# "3707.5395",
|
||
# "44",
|
||
# "14",
|
||
# "1740770040000",
|
||
# "1740770099999",
|
||
# ],
|
||
#
|
||
data = self.safe_list(responseRaw, 'data')
|
||
return self.parse_ohlcvs(data, market, timeframe, since, limit)
|
||
response = self.publicGetMarketsSymbolCandles(self.extend(request, params))
|
||
#
|
||
# [
|
||
# [
|
||
# "22814.01",
|
||
# "22937.42",
|
||
# "22832.57",
|
||
# "22937.42",
|
||
# "3916.58764051",
|
||
# "0.171199",
|
||
# "2982.64647063",
|
||
# "0.130295",
|
||
# 33,
|
||
# 0,
|
||
# "22877.449915304470460711",
|
||
# "MINUTE_5",
|
||
# 1659664800000,
|
||
# 1659665099999
|
||
# ]
|
||
# ]
|
||
#
|
||
return self.parse_ohlcvs(response, market, timeframe, since, limit)
|
||
|
||
def load_markets(self, reload=False, params={}):
|
||
markets = super(poloniex, self).load_markets(reload, params)
|
||
currenciesByNumericId = self.safe_value(self.options, 'currenciesByNumericId')
|
||
if (currenciesByNumericId is None) or reload:
|
||
self.options['currenciesByNumericId'] = self.index_by(self.currencies, 'numericId')
|
||
return markets
|
||
|
||
def fetch_markets(self, params={}) -> List[Market]:
|
||
"""
|
||
retrieves data on all markets for poloniex
|
||
|
||
https://api-docs.poloniex.com/spot/api/public/reference-data#symbol-information
|
||
https://api-docs.poloniex.com/v3/futures/api/market/get-all-product-info
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict[]: an array of objects representing market data
|
||
"""
|
||
promises = [self.fetch_spot_markets(params), self.fetch_swap_markets(params)]
|
||
results = promises
|
||
return self.array_concat(results[0], results[1])
|
||
|
||
def fetch_spot_markets(self, params={}) -> List[Market]:
|
||
markets = self.publicGetMarkets(params)
|
||
#
|
||
# [
|
||
# {
|
||
# "symbol" : "BTS_BTC",
|
||
# "baseCurrencyName" : "BTS",
|
||
# "quoteCurrencyName" : "BTC",
|
||
# "displayName" : "BTS/BTC",
|
||
# "state" : "NORMAL",
|
||
# "visibleStartTime" : 1659018816626,
|
||
# "tradableStartTime" : 1659018816626,
|
||
# "symbolTradeLimit" : {
|
||
# "symbol" : "BTS_BTC",
|
||
# "priceScale" : 10,
|
||
# "quantityScale" : 0,
|
||
# "amountScale" : 8,
|
||
# "minQuantity" : "100",
|
||
# "minAmount" : "0.00001",
|
||
# "highestBid" : "0",
|
||
# "lowestAsk" : "0"
|
||
# }
|
||
# }
|
||
# ]
|
||
#
|
||
return self.parse_markets(markets)
|
||
|
||
def fetch_swap_markets(self, params={}) -> List[Market]:
|
||
# do similar per https://api-docs.poloniex.com/v3/futures/api/market/get-product-info
|
||
response = self.swapPublicGetV3MarketAllInstruments(params)
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "Success",
|
||
# "data": [
|
||
# {
|
||
# "symbol": "BNB_USDT_PERP",
|
||
# "bAsset": ".PBNBUSDT",
|
||
# "bCcy": "BNB",
|
||
# "qCcy": "USDT",
|
||
# "visibleStartTime": "1620390600000",
|
||
# "tradableStartTime": "1620390600000",
|
||
# "sCcy": "USDT",
|
||
# "tSz": "0.001",
|
||
# "pxScale": "0.001,0.01,0.1,1,10",
|
||
# "lotSz": "1",
|
||
# "minSz": "1",
|
||
# "ctVal": "0.1",
|
||
# "status": "OPEN",
|
||
# "oDate": "1620287590000",
|
||
# "maxPx": "1000000",
|
||
# "minPx": "0.001",
|
||
# "maxQty": "1000000",
|
||
# "minQty": "1",
|
||
# "maxLever": "50",
|
||
# "lever": "10",
|
||
# "ctType": "LINEAR",
|
||
# "alias": "",
|
||
# "iM": "0.02",
|
||
# "mM": "0.0115",
|
||
# "mR": "2000",
|
||
# "buyLmt": "",
|
||
# "sellLmt": "",
|
||
# "ordPxRange": "0.05",
|
||
# "marketMaxQty": "2800",
|
||
# "limitMaxQty": "1000000"
|
||
# },
|
||
#
|
||
markets = self.safe_list(response, 'data')
|
||
return self.parse_markets(markets)
|
||
|
||
def parse_market(self, market: dict) -> Market:
|
||
if 'ctType' in market:
|
||
return self.parse_swap_market(market)
|
||
else:
|
||
return self.parse_spot_market(market)
|
||
|
||
def parse_spot_market(self, market: dict) -> Market:
|
||
id = self.safe_string(market, 'symbol')
|
||
baseId = self.safe_string(market, 'baseCurrencyName')
|
||
quoteId = self.safe_string(market, 'quoteCurrencyName')
|
||
base = self.safe_currency_code(baseId)
|
||
quote = self.safe_currency_code(quoteId)
|
||
state = self.safe_string(market, 'state')
|
||
active = state == 'NORMAL'
|
||
symbolTradeLimit = self.safe_value(market, 'symbolTradeLimit')
|
||
# these are known defaults
|
||
return {
|
||
'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': active,
|
||
'contract': False,
|
||
'linear': None,
|
||
'inverse': None,
|
||
'contractSize': None,
|
||
'expiry': None,
|
||
'expiryDatetime': None,
|
||
'strike': None,
|
||
'optionType': None,
|
||
'precision': {
|
||
'amount': self.parse_number(self.parse_precision(self.safe_string(symbolTradeLimit, 'quantityScale'))),
|
||
'price': self.parse_number(self.parse_precision(self.safe_string(symbolTradeLimit, 'priceScale'))),
|
||
},
|
||
'limits': {
|
||
'amount': {
|
||
'min': self.safe_number(symbolTradeLimit, 'minQuantity'),
|
||
'max': None,
|
||
},
|
||
'price': {
|
||
'min': None,
|
||
'max': None,
|
||
},
|
||
'cost': {
|
||
'min': self.safe_number(symbolTradeLimit, 'minAmount'),
|
||
'max': None,
|
||
},
|
||
},
|
||
'created': self.safe_integer(market, 'tradableStartTime'),
|
||
'info': market,
|
||
}
|
||
|
||
def parse_swap_market(self, market: dict) -> Market:
|
||
#
|
||
# {
|
||
# "symbol": "BNB_USDT_PERP",
|
||
# "bAsset": ".PBNBUSDT",
|
||
# "bCcy": "BNB",
|
||
# "qCcy": "USDT",
|
||
# "visibleStartTime": "1620390600000",
|
||
# "tradableStartTime": "1620390600000",
|
||
# "sCcy": "USDT",
|
||
# "tSz": "0.001",
|
||
# "pxScale": "0.001,0.01,0.1,1,10",
|
||
# "lotSz": "1",
|
||
# "minSz": "1",
|
||
# "ctVal": "0.1",
|
||
# "status": "OPEN",
|
||
# "oDate": "1620287590000",
|
||
# "maxPx": "1000000",
|
||
# "minPx": "0.001",
|
||
# "maxQty": "1000000",
|
||
# "minQty": "1",
|
||
# "maxLever": "50",
|
||
# "lever": "10",
|
||
# "ctType": "LINEAR",
|
||
# "alias": "",
|
||
# "iM": "0.02",
|
||
# "mM": "0.0115",
|
||
# "mR": "2000",
|
||
# "buyLmt": "",
|
||
# "sellLmt": "",
|
||
# "ordPxRange": "0.05",
|
||
# "marketMaxQty": "2800",
|
||
# "limitMaxQty": "1000000"
|
||
# },
|
||
#
|
||
id = self.safe_string(market, 'symbol')
|
||
baseId = self.safe_string(market, 'bCcy')
|
||
quoteId = self.safe_string(market, 'qCcy')
|
||
settleId = self.safe_string(market, 'sCcy')
|
||
base = self.safe_currency_code(baseId)
|
||
quote = self.safe_currency_code(quoteId)
|
||
settle = self.safe_currency_code(settleId)
|
||
status = self.safe_string(market, 'status')
|
||
active = status == 'OPEN'
|
||
linear = market['ctType'] == 'LINEAR'
|
||
symbol = base + '/' + quote
|
||
if linear:
|
||
symbol += ':' + settle
|
||
else:
|
||
# actually, exchange does not have any inverse future now
|
||
symbol += ':' + base
|
||
alias = self.safe_string(market, 'alias')
|
||
type = 'swap'
|
||
if alias is not None:
|
||
type = 'future'
|
||
return {
|
||
'id': id,
|
||
'symbol': symbol,
|
||
'base': base,
|
||
'quote': quote,
|
||
'settle': settle,
|
||
'baseId': baseId,
|
||
'quoteId': quoteId,
|
||
'settleId': settleId,
|
||
'type': 'future' if (type == 'future') else 'swap',
|
||
'spot': False,
|
||
'margin': False,
|
||
'swap': type == 'swap',
|
||
'future': type == 'future',
|
||
'option': False,
|
||
'active': active,
|
||
'contract': True,
|
||
'linear': linear,
|
||
'inverse': not linear,
|
||
'contractSize': self.safe_number(market, 'ctVal'),
|
||
'expiry': None,
|
||
'expiryDatetime': None,
|
||
'strike': None,
|
||
'optionType': None,
|
||
'taker': self.safe_number(market, 'tFee'),
|
||
'maker': self.safe_number(market, 'mFee'),
|
||
'precision': {
|
||
'amount': self.safe_number(market, 'lotSz'),
|
||
'price': self.safe_number(market, 'tSz'),
|
||
},
|
||
'limits': {
|
||
'amount': {
|
||
'min': self.safe_number(market, 'minSz'),
|
||
'max': self.safe_number(market, 'limitMaxQty'),
|
||
},
|
||
'price': {
|
||
'min': self.safe_number(market, 'minPx'),
|
||
'max': self.safe_number(market, 'maxPx'),
|
||
},
|
||
'cost': {
|
||
'min': None,
|
||
'max': None,
|
||
},
|
||
'leverage': {
|
||
'max': self.safe_number(market, 'maxLever'),
|
||
'min': None,
|
||
},
|
||
},
|
||
'created': self.safe_integer(market, 'oDate'),
|
||
'info': market,
|
||
}
|
||
|
||
def fetch_time(self, params={}) -> Int:
|
||
"""
|
||
fetches the current integer timestamp in milliseconds from the exchange server
|
||
|
||
https://api-docs.poloniex.com/spot/api/public/reference-data#system-timestamp
|
||
|
||
: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.publicGetTimestamp(params)
|
||
return self.safe_integer(response, 'serverTime')
|
||
|
||
def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
||
#
|
||
# spot:
|
||
#
|
||
# {
|
||
# "symbol" : "BTC_USDT",
|
||
# "open" : "26053.33",
|
||
# "low" : "26053.33",
|
||
# "high" : "26798.02",
|
||
# "close" : "26447.58",
|
||
# "quantity" : "6116.210188",
|
||
# "amount" : "161082122.88450926",
|
||
# "tradeCount" : "134709",
|
||
# "startTime" : "1692784440000",
|
||
# "closeTime" : "1692870839630",
|
||
# "displayName" : "BTC/USDT",
|
||
# "dailyChange" : "0.0151",
|
||
# "bid" : "26447.57",
|
||
# "bidQuantity" : "0.016313",
|
||
# "ask" : "26447.58",
|
||
# "askQuantity" : "0.068307",
|
||
# "ts" : "1692870845446",
|
||
# "markPrice" : "26444.11"
|
||
# }
|
||
#
|
||
# swap:
|
||
#
|
||
# {
|
||
# "s": "XRP_USDT_PERP",
|
||
# "o": "2.0503",
|
||
# "l": "2.0066",
|
||
# "h": "2.216",
|
||
# "c": "2.1798",
|
||
# "qty": "21090",
|
||
# "amt": "451339.65",
|
||
# "tC": "3267",
|
||
# "sT": "1740736380000",
|
||
# "cT": "1740822777559",
|
||
# "dN": "XRP/USDT/PERP",
|
||
# "dC": "0.0632",
|
||
# "bPx": "2.175",
|
||
# "bSz": "3",
|
||
# "aPx": "2.1831",
|
||
# "aSz": "111",
|
||
# "mPx": "2.1798",
|
||
# "iPx": "2.1834"
|
||
# },
|
||
#
|
||
timestamp = self.safe_integer_2(ticker, 'ts', 'cT')
|
||
marketId = self.safe_string_2(ticker, 'symbol', 's')
|
||
market = self.safe_market(marketId)
|
||
relativeChange = self.safe_string_2(ticker, 'dailyChange', 'dc')
|
||
percentage = Precise.string_mul(relativeChange, '100')
|
||
return self.safe_ticker({
|
||
'id': marketId,
|
||
'symbol': market['symbol'],
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'high': self.safe_string_2(ticker, 'high', 'h'),
|
||
'low': self.safe_string_2(ticker, 'low', 'l'),
|
||
'bid': self.safe_string_2(ticker, 'bid', 'bPx'),
|
||
'bidVolume': self.safe_string_2(ticker, 'bidQuantity', 'bSz'),
|
||
'ask': self.safe_string_2(ticker, 'ask', 'aPx'),
|
||
'askVolume': self.safe_string_2(ticker, 'askQuantity', 'aSz'),
|
||
'vwap': None,
|
||
'open': self.safe_string_2(ticker, 'open', 'o'),
|
||
'close': self.safe_string_2(ticker, 'close', 'c'),
|
||
'previousClose': None,
|
||
'change': None,
|
||
'percentage': percentage,
|
||
'average': None,
|
||
'baseVolume': self.safe_string_2(ticker, 'quantity', 'qty'),
|
||
'quoteVolume': self.safe_string_2(ticker, 'amount', 'amt'),
|
||
'markPrice': self.safe_string_2(ticker, 'markPrice', 'mPx'),
|
||
'indexPrice': self.safe_string(ticker, 'iPx'),
|
||
'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://api-docs.poloniex.com/spot/api/public/market-data#ticker
|
||
https://api-docs.poloniex.com/v3/futures/api/market/get-market-info
|
||
|
||
: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()
|
||
market = None
|
||
request: dict = {}
|
||
if symbols is not None:
|
||
symbols = self.market_symbols(symbols, None, True, True, False)
|
||
symbolsLength = len(symbols)
|
||
if symbolsLength > 0:
|
||
market = self.market(symbols[0])
|
||
if symbolsLength == 1:
|
||
request['symbol'] = market['id']
|
||
marketType = None
|
||
marketType, params = self.handle_market_type_and_params('fetchTickers', market, params)
|
||
if marketType == 'swap':
|
||
responseRaw = self.swapPublicGetV3MarketTickers(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "Success",
|
||
# "data": [
|
||
# {
|
||
# "s": "XRP_USDT_PERP",
|
||
# "o": "2.0503",
|
||
# "l": "2.0066",
|
||
# "h": "2.216",
|
||
# "c": "2.1798",
|
||
# "qty": "21090",
|
||
# "amt": "451339.65",
|
||
# "tC": "3267",
|
||
# "sT": "1740736380000",
|
||
# "cT": "1740822777559",
|
||
# "dN": "XRP/USDT/PERP",
|
||
# "dC": "0.0632",
|
||
# "bPx": "2.175",
|
||
# "bSz": "3",
|
||
# "aPx": "2.1831",
|
||
# "aSz": "111",
|
||
# "mPx": "2.1798",
|
||
# "iPx": "2.1834"
|
||
# },
|
||
#
|
||
data = self.safe_list(responseRaw, 'data')
|
||
return self.parse_tickers(data, symbols)
|
||
response = self.publicGetMarketsTicker24h(params)
|
||
#
|
||
# [
|
||
# {
|
||
# "symbol" : "BTC_USDT",
|
||
# "open" : "26053.33",
|
||
# "low" : "26053.33",
|
||
# "high" : "26798.02",
|
||
# "close" : "26447.58",
|
||
# "quantity" : "6116.210188",
|
||
# "amount" : "161082122.88450926",
|
||
# "tradeCount" : "134709",
|
||
# "startTime" : "1692784440000",
|
||
# "closeTime" : "1692870839630",
|
||
# "displayName" : "BTC/USDT",
|
||
# "dailyChange" : "0.0151",
|
||
# "bid" : "26447.57",
|
||
# "bidQuantity" : "0.016313",
|
||
# "ask" : "26447.58",
|
||
# "askQuantity" : "0.068307",
|
||
# "ts" : "1692870845446",
|
||
# "markPrice" : "26444.11"
|
||
# }
|
||
# ]
|
||
#
|
||
return self.parse_tickers(response, symbols)
|
||
|
||
def fetch_currencies(self, params={}) -> Currencies:
|
||
"""
|
||
fetches all available currencies on an exchange
|
||
|
||
https://api-docs.poloniex.com/spot/api/public/reference-data#currency-information
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: an associative dictionary of currencies
|
||
"""
|
||
response = self.publicGetCurrencies(self.extend(params, {'includeMultiChainCurrencies': True}))
|
||
#
|
||
# [
|
||
# {
|
||
# "USDT": {
|
||
# "id": 214,
|
||
# "name": "Tether USD",
|
||
# "description": "Sweep to Main Account",
|
||
# "type": "address",
|
||
# "withdrawalFee": "0.00000000",
|
||
# "minConf": 2,
|
||
# "depositAddress": null,
|
||
# "blockchain": "OMNI",
|
||
# "delisted": False,
|
||
# "tradingState": "NORMAL",
|
||
# "walletState": "DISABLED",
|
||
# "walletDepositState": "DISABLED",
|
||
# "walletWithdrawalState": "DISABLED",
|
||
# "supportCollateral": True,
|
||
# "supportBorrow": True,
|
||
# "parentChain": null,
|
||
# "isMultiChain": True,
|
||
# "isChildChain": False,
|
||
# "childChains": [
|
||
# "USDTBSC",
|
||
# "USDTETH",
|
||
# "USDTSOL",
|
||
# "USDTTRON"
|
||
# ]
|
||
# }
|
||
# },
|
||
# ...
|
||
# {
|
||
# "USDTBSC": {
|
||
# "id": 582,
|
||
# "name": "Binance-Peg BSC-USD",
|
||
# "description": "Sweep to Main Account",
|
||
# "type": "address",
|
||
# "withdrawalFee": "0.00000000",
|
||
# "minConf": 15,
|
||
# "depositAddress": null,
|
||
# "blockchain": "BSC",
|
||
# "delisted": False,
|
||
# "tradingState": "OFFLINE",
|
||
# "walletState": "ENABLED",
|
||
# "walletDepositState": "ENABLED",
|
||
# "walletWithdrawalState": "DISABLED",
|
||
# "supportCollateral": False,
|
||
# "supportBorrow": False,
|
||
# "parentChain": "USDT",
|
||
# "isMultiChain": True,
|
||
# "isChildChain": True,
|
||
# "childChains": []
|
||
# }
|
||
# },
|
||
# ...
|
||
# ]
|
||
#
|
||
result: dict = {}
|
||
# poloniex has a complicated structure of currencies, so we handle them differently
|
||
# at first, turn the response into a normal dictionary
|
||
currenciesDict = {}
|
||
for i in range(0, len(response)):
|
||
item = self.safe_dict(response, i)
|
||
ids = list(item.keys())
|
||
id = self.safe_string(ids, 0)
|
||
currenciesDict[id] = item[id]
|
||
keys = list(currenciesDict.keys())
|
||
for i in range(0, len(keys)):
|
||
id = keys[i]
|
||
entry = currenciesDict[id]
|
||
code = self.safe_currency_code(id)
|
||
# skip childChains, are collected in parentChain loop
|
||
if self.safe_bool(entry, 'isChildChain'):
|
||
continue
|
||
allChainEntries = []
|
||
childChains = self.safe_list(entry, 'childChains', [])
|
||
if childChains is not None:
|
||
for j in range(0, len(childChains)):
|
||
childChainId = childChains[j]
|
||
childNetworkEntry = self.safe_dict(currenciesDict, childChainId)
|
||
allChainEntries.append(childNetworkEntry)
|
||
allChainEntries.append(entry)
|
||
networks: dict = {}
|
||
for j in range(0, len(allChainEntries)):
|
||
chainEntry = allChainEntries[j]
|
||
networkName = self.safe_string(chainEntry, 'blockchain')
|
||
networkCode = self.network_id_to_code(networkName, code)
|
||
specialNetworkId = self.safe_string(childChains, j, id) # in case it's primary chain, defeault to ID
|
||
networks[networkCode] = {
|
||
'info': chainEntry,
|
||
'id': specialNetworkId, # we need self for deposit/withdrawal, instead of friendly name
|
||
'numericId': self.safe_integer(chainEntry, 'id'),
|
||
'network': networkCode,
|
||
'active': self.safe_bool(chainEntry, 'walletState'),
|
||
'deposit': self.safe_string(chainEntry, 'walletDepositState') == 'ENABLED',
|
||
'withdraw': self.safe_string(chainEntry, 'walletWithdrawalState') == 'ENABLED',
|
||
'fee': self.safe_number(chainEntry, 'withdrawalFee'),
|
||
'precision': None,
|
||
'limits': {
|
||
'withdraw': {
|
||
'min': None,
|
||
'max': None,
|
||
},
|
||
'deposit': {
|
||
'min': None,
|
||
'max': None,
|
||
},
|
||
},
|
||
}
|
||
result[code] = self.safe_currency_structure({
|
||
'info': entry,
|
||
'code': code,
|
||
'id': id,
|
||
'numericId': self.safe_integer(entry, 'id'),
|
||
'type': 'crypto',
|
||
'name': self.safe_string(entry, 'name'),
|
||
'active': None,
|
||
'deposit': None,
|
||
'withdraw': None,
|
||
'fee': None,
|
||
'precision': None,
|
||
'limits': {
|
||
'amount': {
|
||
'min': None,
|
||
'max': None,
|
||
},
|
||
'withdraw': {
|
||
'min': None,
|
||
'max': None,
|
||
},
|
||
'deposit': {
|
||
'min': None,
|
||
'max': None,
|
||
},
|
||
},
|
||
'networks': networks,
|
||
})
|
||
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://api-docs.poloniex.com/spot/api/public/market-data#ticker
|
||
https://api-docs.poloniex.com/v3/futures/api/market/get-market-info
|
||
|
||
: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: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
if market['contract']:
|
||
tickers = self.fetch_tickers([market['symbol']], params)
|
||
return self.safe_dict(tickers, symbol)
|
||
response = self.publicGetMarketsSymbolTicker24h(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "symbol" : "BTC_USDT",
|
||
# "open" : "26053.33",
|
||
# "low" : "26053.33",
|
||
# "high" : "26798.02",
|
||
# "close" : "26447.58",
|
||
# "quantity" : "6116.210188",
|
||
# "amount" : "161082122.88450926",
|
||
# "tradeCount" : "134709",
|
||
# "startTime" : "1692784440000",
|
||
# "closeTime" : "1692870839630",
|
||
# "displayName" : "BTC/USDT",
|
||
# "dailyChange" : "0.0151",
|
||
# "bid" : "26447.57",
|
||
# "bidQuantity" : "0.016313",
|
||
# "ask" : "26447.58",
|
||
# "askQuantity" : "0.068307",
|
||
# "ts" : "1692870845446",
|
||
# "markPrice" : "26444.11"
|
||
# }
|
||
#
|
||
return self.parse_ticker(response, market)
|
||
|
||
def parse_trade(self, trade: dict, market: Market = None) -> Trade:
|
||
#
|
||
# fetchTrades
|
||
#
|
||
# spot:
|
||
#
|
||
# {
|
||
# "id" : "60014521",
|
||
# "price" : "23162.94",
|
||
# "quantity" : "0.00009",
|
||
# "amount" : "2.0846646",
|
||
# "takerSide" : "SELL",
|
||
# "ts" : 1659684602042,
|
||
# "createTime" : 1659684602036
|
||
# }
|
||
#
|
||
# swap:
|
||
#
|
||
# {
|
||
# "id": "105807376",
|
||
# "side": "buy",
|
||
# "px": "84410.57",
|
||
# "qty": "1",
|
||
# "amt": "84.41057",
|
||
# "cT": "1740777563557",
|
||
# }
|
||
#
|
||
# fetchMyTrades
|
||
#
|
||
# spot:
|
||
#
|
||
# {
|
||
# "id": "32164924331503616",
|
||
# "symbol": "LINK_USDT",
|
||
# "accountType": "SPOT",
|
||
# "orderId": "32164923987566592",
|
||
# "side": "SELL",
|
||
# "type": "MARKET",
|
||
# "matchRole": "TAKER",
|
||
# "createTime": 1648635115525,
|
||
# "price": "11",
|
||
# "quantity": "0.5",
|
||
# "amount": "5.5",
|
||
# "feeCurrency": "USDT",
|
||
# "feeAmount": "0.007975",
|
||
# "pageId": "32164924331503616",
|
||
# "clientOrderId": "myOwnId-321"
|
||
# }
|
||
#
|
||
# swap:
|
||
#
|
||
# {
|
||
# "symbol": "BTC_USDT_PERP",
|
||
# "trdId": "105813553",
|
||
# "side": "SELL",
|
||
# "type": "TRADE",
|
||
# "mgnMode": "CROSS",
|
||
# "ordType": "MARKET",
|
||
# "clOrdId": "polo418912106147315112",
|
||
# "role": "TAKER",
|
||
# "px": "84704.9",
|
||
# "qty": "1",
|
||
# "cTime": "1740842829430",
|
||
# "uTime": "1740842829450",
|
||
# "feeCcy": "USDT",
|
||
# "feeAmt": "0.04235245",
|
||
# "deductCcy": "",
|
||
# "deductAmt": "0",
|
||
# "feeRate": "0.0005",
|
||
# "id": "418912106342654592",
|
||
# "posSide": "BOTH",
|
||
# "ordId": "418912106147315112",
|
||
# "qCcy": "USDT",
|
||
# "value": "84.7049",
|
||
# "actType": "TRADING"
|
||
# },
|
||
#
|
||
# fetchOrderTrades(taker trades)
|
||
#
|
||
# {
|
||
# "id": "30341456333942784",
|
||
# "symbol": "LINK_USDT",
|
||
# "accountType": "SPOT",
|
||
# "orderId": "30249408733945856",
|
||
# "side": "BUY",
|
||
# "type": "LIMIT",
|
||
# "matchRole": "MAKER",
|
||
# "createTime": 1648200366864,
|
||
# "price": "3.1",
|
||
# "quantity": "1",
|
||
# "amount": "3.1",
|
||
# "feeCurrency": "LINK",
|
||
# "feeAmount": "0.00145",
|
||
# "pageId": "30341456333942784",
|
||
# "clientOrderId": ""
|
||
# }
|
||
#
|
||
id = self.safe_string_n(trade, ['id', 'tradeID', 'trdId'])
|
||
orderId = self.safe_string_2(trade, 'orderId', 'ordId')
|
||
timestamp = self.safe_integer_n(trade, ['ts', 'createTime', 'cT', 'cTime'])
|
||
marketId = self.safe_string(trade, 'symbol')
|
||
market = self.safe_market(marketId, market, '_')
|
||
symbol = market['symbol']
|
||
side = self.safe_string_lower_2(trade, 'side', 'takerSide')
|
||
fee = None
|
||
priceString = self.safe_string_2(trade, 'price', 'px')
|
||
amountString = self.safe_string_2(trade, 'quantity', 'qty')
|
||
costString = self.safe_string_2(trade, 'amount', 'amt')
|
||
feeCurrencyId = self.safe_string_2(trade, 'feeCurrency', 'feeCcy')
|
||
feeCostString = self.safe_string_2(trade, 'feeAmount', 'feeAmt')
|
||
if feeCostString is not None:
|
||
feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
|
||
fee = {
|
||
'cost': feeCostString,
|
||
'currency': feeCurrencyCode,
|
||
}
|
||
return self.safe_trade({
|
||
'id': id,
|
||
'info': trade,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'symbol': symbol,
|
||
'order': orderId,
|
||
'type': self.safe_string_lower_2(trade, 'ordType', 'type'), # ordType should take precedence
|
||
'side': side,
|
||
'takerOrMaker': self.safe_string_lower_2(trade, 'matchRole', 'role'),
|
||
'price': priceString,
|
||
'amount': amountString,
|
||
'cost': costString,
|
||
'fee': fee,
|
||
}, market)
|
||
|
||
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://api-docs.poloniex.com/spot/api/public/market-data#trades
|
||
https://api-docs.poloniex.com/v3/futures/api/market/get-execution-info
|
||
|
||
: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
|
||
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
||
"""
|
||
self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
if limit is not None:
|
||
request['limit'] = limit # max 1000, for spot & swap
|
||
if market['contract']:
|
||
response = self.swapPublicGetV3MarketTrades(self.extend(request, params))
|
||
#
|
||
# {
|
||
# code: "200",
|
||
# msg: "Success",
|
||
# data: [
|
||
# {
|
||
# id: "105807320", # descending order
|
||
# side: "sell",
|
||
# px: "84383.93",
|
||
# qty: "1",
|
||
# amt: "84.38393",
|
||
# cT: "1740777074704",
|
||
# },
|
||
#
|
||
tradesList = self.safe_list(response, 'data')
|
||
return self.parse_trades(tradesList, market, since, limit)
|
||
trades = self.publicGetMarketsSymbolTrades(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "id" : "60014521",
|
||
# "price" : "23162.94",
|
||
# "quantity" : "0.00009",
|
||
# "amount" : "2.0846646",
|
||
# "takerSide" : "SELL",
|
||
# "ts" : 1659684602042,
|
||
# "createTime" : 1659684602036
|
||
# }
|
||
# ]
|
||
#
|
||
return self.parse_trades(trades, market, since, limit)
|
||
|
||
def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
||
"""
|
||
fetch all trades made by the user
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/trade#trade-history
|
||
https://api-docs.poloniex.com/v3/futures/api/trade/get-execution-details
|
||
|
||
: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 int [params.until]: the latest time in ms to fetch entries for
|
||
: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=trade-structure>`
|
||
"""
|
||
self.load_markets()
|
||
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)
|
||
market: Market = None
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
marketType = None
|
||
marketType, params = self.handle_market_type_and_params('fetchMyTrades', market, params)
|
||
isContract = self.in_array(marketType, ['swap', 'future'])
|
||
request: dict = {
|
||
# 'from': 12345678, # A 'trade Id'. The query begins at ‘from'.
|
||
# 'direction': 'PRE', # PRE, NEXT The direction before or after ‘from'.
|
||
}
|
||
startKey = 'sTime' if isContract else 'startTime'
|
||
endKey = 'eTime' if isContract else 'endTime'
|
||
if since is not None:
|
||
request[startKey] = since
|
||
if limit is not None:
|
||
request['limit'] = limit
|
||
if isContract and symbol is not None:
|
||
request['symbol'] = market['id']
|
||
request, params = self.handle_until_option(endKey, request, params)
|
||
if isContract:
|
||
raw = self.swapPrivateGetV3TradeOrderTrades(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "",
|
||
# "data": [
|
||
# {
|
||
# "symbol": "BTC_USDT_PERP",
|
||
# "trdId": "105813553",
|
||
# "side": "SELL",
|
||
# "type": "TRADE",
|
||
# "mgnMode": "CROSS",
|
||
# "ordType": "MARKET",
|
||
# "clOrdId": "polo418912106147315112",
|
||
# "role": "TAKER",
|
||
# "px": "84704.9",
|
||
# "qty": "1",
|
||
# "cTime": "1740842829430",
|
||
# "uTime": "1740842829450",
|
||
# "feeCcy": "USDT",
|
||
# "feeAmt": "0.04235245",
|
||
# "deductCcy": "",
|
||
# "deductAmt": "0",
|
||
# "feeRate": "0.0005",
|
||
# "id": "418912106342654592",
|
||
# "posSide": "BOTH",
|
||
# "ordId": "418912106147315112",
|
||
# "qCcy": "USDT",
|
||
# "value": "84.7049",
|
||
# "actType": "TRADING"
|
||
# },
|
||
#
|
||
data = self.safe_list(raw, 'data')
|
||
return self.parse_trades(data, market, since, limit)
|
||
response = self.privateGetTrades(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "id": "32164924331503616",
|
||
# "symbol": "LINK_USDT",
|
||
# "accountType": "SPOT",
|
||
# "orderId": "32164923987566592",
|
||
# "side": "SELL",
|
||
# "type": "MARKET",
|
||
# "matchRole": "TAKER",
|
||
# "createTime": 1648635115525,
|
||
# "price": "11",
|
||
# "quantity": "0.5",
|
||
# "amount": "5.5",
|
||
# "feeCurrency": "USDT",
|
||
# "feeAmount": "0.007975",
|
||
# "pageId": "32164924331503616",
|
||
# "clientOrderId": "myOwnId-321"
|
||
# }
|
||
# ]
|
||
#
|
||
result = self.parse_trades(response, market, since, limit)
|
||
return result
|
||
|
||
def parse_order_status(self, status: Str):
|
||
statuses: dict = {
|
||
'NEW': 'open',
|
||
'PARTIALLY_FILLED': 'open',
|
||
'FILLED': 'closed',
|
||
'PENDING_CANCEL': 'canceled',
|
||
'PARTIALLY_CANCELED': 'canceled',
|
||
'CANCELED': 'canceled',
|
||
'FAILED': 'canceled',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
def parse_order(self, order: dict, market: Market = None) -> Order:
|
||
#
|
||
# fetchOpenOrder
|
||
#
|
||
# {
|
||
# "id" : "7xxxxxxxxxxxxxxx6",
|
||
# "clientOrderId" : "",
|
||
# "symbol" : "ETH_USDT",
|
||
# "state" : "NEW",
|
||
# "accountType" : "SPOT",
|
||
# "side" : "BUY",
|
||
# "type" : "LIMIT",
|
||
# "timeInForce" : "GTC",
|
||
# "quantity" : "0.001",
|
||
# "price" : "1600",
|
||
# "avgPrice" : "0",
|
||
# "amount" : "0",
|
||
# "filledQuantity" : "0",
|
||
# "filledAmount" : "0",
|
||
# "createTime" : 16xxxxxxxxx26,
|
||
# "updateTime" : 16xxxxxxxxx36
|
||
# }
|
||
#
|
||
# fetchOpenOrders(and fetchClosedOrders same for contracts)
|
||
#
|
||
# spot:
|
||
#
|
||
# {
|
||
# "id": "24993088082542592",
|
||
# "clientOrderId": "",
|
||
# "symbol": "ELON_USDC",
|
||
# "state": "NEW",
|
||
# "accountType": "SPOT",
|
||
# "side": "SELL",
|
||
# "type": "MARKET",
|
||
# "timeInForce": "GTC",
|
||
# "quantity": "1.00",
|
||
# "price": "0.00",
|
||
# "avgPrice": "0.00",
|
||
# "amount": "0.00",
|
||
# "filledQuantity": "0.00",
|
||
# "filledAmount": "0.00",
|
||
# "createTime": 1646925216548,
|
||
# "updateTime": 1646925216548
|
||
# }
|
||
#
|
||
# contract:
|
||
#
|
||
# {
|
||
# "symbol": "BTC_USDT_PERP",
|
||
# "side": "BUY",
|
||
# "type": "LIMIT",
|
||
# "ordId": "418890767248232148",
|
||
# "clOrdId": "polo418890767248232148",
|
||
# "mgnMode": "CROSS",
|
||
# "px": "81130.13",
|
||
# "reduceOnly": False,
|
||
# "lever": "20",
|
||
# "state": "NEW",
|
||
# "source": "WEB",
|
||
# "timeInForce": "GTC",
|
||
# "tpTrgPx": "",
|
||
# "tpPx": "",
|
||
# "tpTrgPxType": "",
|
||
# "slTrgPx": "",
|
||
# "slPx": "",
|
||
# "slTrgPxType": "",
|
||
# "avgPx": "0",
|
||
# "execQty": "0",
|
||
# "execAmt": "0",
|
||
# "feeCcy": "",
|
||
# "feeAmt": "0",
|
||
# "deductCcy": "0",
|
||
# "deductAmt": "0",
|
||
# "stpMode": "NONE", # todo: selfTradePrevention
|
||
# "cTime": "1740837741523",
|
||
# "uTime": "1740840846882",
|
||
# "sz": "1",
|
||
# "posSide": "BOTH",
|
||
# "qCcy": "USDT"
|
||
# "cancelReason": "", # self field can only be in closed orders
|
||
# },
|
||
#
|
||
# createOrder, editOrder
|
||
#
|
||
# spot:
|
||
#
|
||
# {
|
||
# "id": "29772698821328896",
|
||
# "clientOrderId": "1234Abc"
|
||
# }
|
||
#
|
||
# contract:
|
||
#
|
||
# {
|
||
# "ordId":"418876147745775616",
|
||
# "clOrdId":"polo418876147745775616"
|
||
# }
|
||
#
|
||
timestamp = self.safe_integer_n(order, ['timestamp', 'createTime', 'cTime'])
|
||
if timestamp is None:
|
||
timestamp = self.parse8601(self.safe_string(order, 'date'))
|
||
marketId = self.safe_string(order, 'symbol')
|
||
market = self.safe_market(marketId, market, '_')
|
||
symbol = market['symbol']
|
||
resultingTrades = self.safe_value(order, 'resultingTrades')
|
||
if resultingTrades is not None:
|
||
if not isinstance(resultingTrades, list):
|
||
resultingTrades = self.safe_value(resultingTrades, self.safe_string(market, 'id', marketId))
|
||
price = self.safe_string_n(order, ['price', 'rate', 'px'])
|
||
amount = self.safe_string_2(order, 'quantity', 'sz')
|
||
filled = self.safe_string_2(order, 'filledQuantity', 'execQty')
|
||
status = self.parse_order_status(self.safe_string(order, 'state'))
|
||
side = self.safe_string_lower(order, 'side')
|
||
rawType = self.safe_string(order, 'type')
|
||
type = self.parse_order_type(rawType)
|
||
id = self.safe_string_n(order, ['orderNumber', 'id', 'orderId', 'ordId'])
|
||
fee = None
|
||
feeCurrency = self.safe_string_2(order, 'tokenFeeCurrency', 'feeCcy')
|
||
feeCost: Str = None
|
||
feeCurrencyCode: Str = None
|
||
rate = self.safe_string(order, 'fee')
|
||
if feeCurrency is None:
|
||
feeCurrencyCode = market['base'] if (side == 'buy') else market['quote']
|
||
else:
|
||
# poloniex accepts a 30% discount to pay fees in TRX
|
||
feeCurrencyCode = self.safe_currency_code(feeCurrency)
|
||
feeCost = self.safe_string_2(order, 'tokenFee', 'feeAmt')
|
||
if feeCost is not None:
|
||
fee = {
|
||
'rate': rate,
|
||
'cost': feeCost,
|
||
'currency': feeCurrencyCode,
|
||
}
|
||
clientOrderId = self.safe_string_2(order, 'clientOrderId', 'clOrdId')
|
||
marginMode = self.safe_string_lower(order, 'mgnMode')
|
||
reduceOnly = self.safe_bool(order, 'reduceOnly')
|
||
leverage = self.safe_integer(order, 'lever')
|
||
hedged = self.safe_string(order, 'posSide') != 'BOTH'
|
||
return self.safe_order({
|
||
'info': order,
|
||
'id': id,
|
||
'clientOrderId': clientOrderId,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'lastTradeTimestamp': self.safe_integer(order, 'updateTime'),
|
||
'status': status,
|
||
'symbol': symbol,
|
||
'type': type,
|
||
'timeInForce': self.safe_string(order, 'timeInForce'),
|
||
'postOnly': rawType == 'LIMIT_MAKER',
|
||
'side': side,
|
||
'price': price,
|
||
'triggerPrice': self.safe_string_2(order, 'triggerPrice', 'stopPrice'),
|
||
'cost': self.safe_string(order, 'execAmt'),
|
||
'average': self.safe_string_2(order, 'avgPrice', 'avgPx'),
|
||
'amount': amount,
|
||
'filled': filled,
|
||
'remaining': None,
|
||
'trades': resultingTrades,
|
||
'fee': fee,
|
||
'marginMode': marginMode,
|
||
'reduceOnly': reduceOnly,
|
||
'leverage': leverage,
|
||
'hedged': hedged,
|
||
}, market)
|
||
|
||
def parse_order_type(self, status):
|
||
statuses: dict = {
|
||
'MARKET': 'market',
|
||
'LIMIT': 'limit',
|
||
'LIMIT_MAKER': 'limit',
|
||
'STOP-LIMIT': 'limit',
|
||
'STOP-MARKET': 'market',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
def parse_open_orders(self, orders, market, result):
|
||
for i in range(0, len(orders)):
|
||
order = orders[i]
|
||
extended = self.extend(order, {
|
||
'status': 'open',
|
||
'type': 'limit',
|
||
'side': order['type'],
|
||
'price': order['rate'],
|
||
})
|
||
result.append(self.parse_order(extended, market))
|
||
return result
|
||
|
||
def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
||
"""
|
||
fetch all unfilled currently open orders
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/order#open-orders
|
||
https://api-docs.poloniex.com/spot/api/private/smart-order#open-orders # trigger orders
|
||
https://api-docs.poloniex.com/v3/futures/api/trade/get-current-orders
|
||
|
||
: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 boolean [params.trigger]: set True to fetch trigger orders instead of regular orders
|
||
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
self.load_markets()
|
||
market: Market = None
|
||
request: dict = {}
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
request['symbol'] = market['id']
|
||
marketType = None
|
||
marketType, params = self.handle_market_type_and_params('fetchOpenOrders', market, params)
|
||
if limit is not None:
|
||
max = 2000 if (marketType == 'spot') else 100
|
||
request['limit'] = max(limit, max)
|
||
isTrigger = self.safe_value_2(params, 'trigger', 'stop')
|
||
params = self.omit(params, ['trigger', 'stop'])
|
||
response = None
|
||
if marketType != 'spot':
|
||
raw = self.swapPrivateGetV3TradeOrderOpens(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "",
|
||
# "data": [
|
||
# {
|
||
# "symbol": "BTC_USDT_PERP",
|
||
# "side": "BUY",
|
||
# "type": "LIMIT",
|
||
# "ordId": "418890767248232148",
|
||
# "clOrdId": "polo418890767248232148",
|
||
# "mgnMode": "CROSS",
|
||
# "px": "81130.13",
|
||
# "reduceOnly": False,
|
||
# "lever": "20",
|
||
# "state": "NEW",
|
||
# "source": "WEB",
|
||
# "timeInForce": "GTC",
|
||
# "tpTrgPx": "",
|
||
# "tpPx": "",
|
||
# "tpTrgPxType": "",
|
||
# "slTrgPx": "",
|
||
# "slPx": "",
|
||
# "slTrgPxType": "",
|
||
# "avgPx": "0",
|
||
# "execQty": "0",
|
||
# "execAmt": "0",
|
||
# "feeCcy": "",
|
||
# "feeAmt": "0",
|
||
# "deductCcy": "0",
|
||
# "deductAmt": "0",
|
||
# "stpMode": "NONE",
|
||
# "cTime": "1740837741523",
|
||
# "uTime": "1740840846882",
|
||
# "sz": "1",
|
||
# "posSide": "BOTH",
|
||
# "qCcy": "USDT"
|
||
# },
|
||
#
|
||
response = self.safe_list(raw, 'data')
|
||
elif isTrigger:
|
||
response = self.privateGetSmartorders(self.extend(request, params))
|
||
else:
|
||
response = self.privateGetOrders(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "id" : "7xxxxxxxxxxxxxxx6",
|
||
# "clientOrderId" : "",
|
||
# "symbol" : "ETH_USDT",
|
||
# "state" : "NEW",
|
||
# "accountType" : "SPOT",
|
||
# "side" : "BUY",
|
||
# "type" : "LIMIT",
|
||
# "timeInForce" : "GTC",
|
||
# "quantity" : "0.001",
|
||
# "price" : "1600",
|
||
# "avgPrice" : "0",
|
||
# "amount" : "0",
|
||
# "filledQuantity" : "0",
|
||
# "filledAmount" : "0",
|
||
# "stopPrice": "3750.00", # for trigger orders
|
||
# "createTime" : 16xxxxxxxxx26,
|
||
# "updateTime" : 16xxxxxxxxx36
|
||
# }
|
||
# ]
|
||
#
|
||
extension: dict = {'status': 'open'}
|
||
return self.parse_orders(response, market, since, limit, extension)
|
||
|
||
def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
||
"""
|
||
|
||
https://api-docs.poloniex.com/v3/futures/api/trade/get-order-history
|
||
|
||
fetches information on multiple closed orders made by the user
|
||
: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 int [params.until]: timestamp in ms of the latest entry
|
||
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
self.load_markets()
|
||
market = None
|
||
request: dict = {}
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
request['symbol'] = market['id']
|
||
marketType = None
|
||
marketType, params = self.handle_market_type_and_params('fetchClosedOrders', market, params, 'swap')
|
||
if marketType == 'spot':
|
||
raise NotSupported(self.id + ' fetchClosedOrders() is not supported for spot markets yet')
|
||
if limit is not None:
|
||
request['limit'] = min(200, limit)
|
||
if since is not None:
|
||
request['sTime'] = since
|
||
request, params = self.handle_until_option('eTime', request, params)
|
||
response = self.swapPrivateGetV3TradeOrderHistory(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "",
|
||
# "data": [
|
||
# {
|
||
# "symbol": "BTC_USDT_PERP",
|
||
# "side": "SELL",
|
||
# "type": "MARKET",
|
||
# "ordId": "418912106147315712",
|
||
# "clOrdId": "polo418912106147315712",
|
||
# "mgnMode": "CROSS",
|
||
# "px": "0",
|
||
# "sz": "2",
|
||
# "lever": "20",
|
||
# "state": "FILLED",
|
||
# "cancelReason": "",
|
||
# "source": "WEB",
|
||
# "reduceOnly": "true",
|
||
# "timeInForce": "GTC",
|
||
# "tpTrgPx": "",
|
||
# "tpPx": "",
|
||
# "tpTrgPxType": "",
|
||
# "slTrgPx": "",
|
||
# "slPx": "",
|
||
# "slTrgPxType": "",
|
||
# "avgPx": "84705.56",
|
||
# "execQty": "2",
|
||
# "execAmt": "169.41112",
|
||
# "feeCcy": "USDT",
|
||
# "feeAmt": "0.08470556",
|
||
# "deductCcy": "0",
|
||
# "deductAmt": "0",
|
||
# "stpMode": "NONE",
|
||
# "cTime": "1740842829116",
|
||
# "uTime": "1740842829130",
|
||
# "posSide": "BOTH",
|
||
# "qCcy": "USDT"
|
||
# },
|
||
#
|
||
data = self.safe_list(response, 'data', [])
|
||
return self.parse_orders(data, market, since, limit)
|
||
|
||
def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
||
"""
|
||
create a trade order
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/order#create-order
|
||
https://api-docs.poloniex.com/spot/api/private/smart-order#create-order # trigger orders
|
||
|
||
: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 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.triggerPrice]: the price at which a trigger order is triggered at
|
||
:param float [params.cost]: *spot market buy only* the quote quantity that can be used alternative for the amount
|
||
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
'side': side.upper(), # uppercase, both for spot & swap
|
||
# 'timeInForce': timeInForce, # matches unified values
|
||
# 'accountType': 'SPOT',
|
||
# 'amount': amount,
|
||
}
|
||
triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
|
||
request, params = self.order_request(symbol, type, side, amount, request, price, params)
|
||
response = None
|
||
if market['swap'] or market['future']:
|
||
responseInitial = self.swapPrivatePostV3TradeOrder(self.extend(request, params))
|
||
#
|
||
# {"code":200,"msg":"Success","data":{"ordId":"418876147745775616","clOrdId":"polo418876147745775616"}}
|
||
#
|
||
response = self.safe_dict(responseInitial, 'data')
|
||
elif triggerPrice is not None:
|
||
response = self.privatePostSmartorders(self.extend(request, params))
|
||
else:
|
||
response = self.privatePostOrders(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "id" : "78923648051920896",
|
||
# "clientOrderId" : ""
|
||
# }
|
||
#
|
||
return self.parse_order(response, market)
|
||
|
||
def order_request(self, symbol, type, side, amount, request, price=None, params={}):
|
||
triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
|
||
market = self.market(symbol)
|
||
if market['contract']:
|
||
marginMode = None
|
||
marginMode, params = self.handle_param_string(params, 'marginMode')
|
||
if marginMode is not None:
|
||
self.check_required_argument('createOrder', marginMode, 'marginMode', ['cross', 'isolated'])
|
||
request['mgnMode'] = marginMode.upper()
|
||
hedged = None
|
||
hedged, params = self.handle_param_string(params, 'hedged')
|
||
if hedged:
|
||
if marginMode is None:
|
||
raise ArgumentsRequired(self.id + ' createOrder() requires a marginMode parameter "cross" or "isolated" for hedged orders')
|
||
if not ('posSide' in params):
|
||
raise ArgumentsRequired(self.id + ' createOrder() requires a posSide parameter "LONG" or "SHORT" for hedged orders')
|
||
upperCaseType = type.upper()
|
||
isMarket = upperCaseType == 'MARKET'
|
||
isPostOnly = self.is_post_only(isMarket, upperCaseType == 'LIMIT_MAKER', params)
|
||
params = self.omit(params, ['postOnly', 'triggerPrice', 'stopPrice'])
|
||
if triggerPrice is not None:
|
||
if not market['spot']:
|
||
raise InvalidOrder(self.id + ' createOrder() does not support trigger orders for ' + market['type'] + ' markets')
|
||
upperCaseType = 'STOP' if (price is None) else 'STOP_LIMIT'
|
||
request['stopPrice'] = triggerPrice
|
||
elif isPostOnly:
|
||
upperCaseType = 'LIMIT_MAKER'
|
||
request['type'] = upperCaseType
|
||
if isMarket:
|
||
if 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 and market['spot']:
|
||
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)
|
||
amountKey = 'amount' if market['spot'] else 'sz'
|
||
request[amountKey] = quoteAmount
|
||
else:
|
||
amountKey = 'quantity' if market['spot'] else 'sz'
|
||
request[amountKey] = self.amount_to_precision(symbol, amount)
|
||
else:
|
||
amountKey = 'quantity' if market['spot'] else 'sz'
|
||
request[amountKey] = self.amount_to_precision(symbol, amount)
|
||
priceKey = 'price' if market['spot'] else 'px'
|
||
request[priceKey] = self.price_to_precision(symbol, price)
|
||
clientOrderId = self.safe_string(params, 'clientOrderId')
|
||
if clientOrderId is not None:
|
||
request['clientOrderId'] = clientOrderId
|
||
params = self.omit(params, 'clientOrderId')
|
||
# remember the timestamp before issuing the request
|
||
return [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
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/order#cancel-replace-order
|
||
https://api-docs.poloniex.com/spot/api/private/smart-order#cancel-replace-order
|
||
|
||
: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 float [params.triggerPrice]: The price at which a trigger order is triggered at
|
||
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
self.load_markets()
|
||
market = self.market(symbol)
|
||
if not market['spot']:
|
||
raise NotSupported(self.id + ' editOrder() does not support ' + market['type'] + ' orders, only spot orders are accepted')
|
||
request: dict = {
|
||
'id': id,
|
||
# 'timeInForce': timeInForce,
|
||
}
|
||
triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
|
||
request, params = self.order_request(symbol, type, side, amount, request, price, params)
|
||
response = None
|
||
if triggerPrice is not None:
|
||
response = self.privatePutSmartordersId(self.extend(request, params))
|
||
else:
|
||
response = self.privatePutOrdersId(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "id" : "78923648051920896",
|
||
# "clientOrderId" : ""
|
||
# }
|
||
#
|
||
response = self.extend(response, {
|
||
'side': side,
|
||
'type': type,
|
||
})
|
||
return self.parse_order(response, market)
|
||
|
||
def cancel_order(self, id: str, symbol: Str = None, params={}):
|
||
#
|
||
# @method
|
||
# @name poloniex#cancelOrder
|
||
# @description cancels an open order
|
||
# @see https://api-docs.poloniex.com/spot/api/private/order#cancel-order-by-id
|
||
# @see https://api-docs.poloniex.com/spot/api/private/smart-order#cancel-order-by-id # trigger orders
|
||
# @param {string} id order id
|
||
# @param {string} symbol unified symbol of the market the order was made in
|
||
# @param {object} [params] extra parameters specific to the exchange API endpoint
|
||
# @param {boolean} [params.trigger] True if canceling a trigger order
|
||
# @returns {object} An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
#
|
||
self.load_markets()
|
||
if symbol is None:
|
||
raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
|
||
market = self.market(symbol)
|
||
request: dict = {}
|
||
if not market['spot']:
|
||
request['symbol'] = market['id']
|
||
request['ordId'] = id
|
||
raw = self.swapPrivateDeleteV3TradeOrder(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "Success",
|
||
# "data": {
|
||
# "ordId": "418886099910612040",
|
||
# "clOrdId": "polo418886099910612040"
|
||
# }
|
||
# }
|
||
#
|
||
return self.parse_order(self.safe_dict(raw, 'data'))
|
||
clientOrderId = self.safe_value(params, 'clientOrderId')
|
||
if clientOrderId is not None:
|
||
id = clientOrderId
|
||
request['id'] = id
|
||
isTrigger = self.safe_value_2(params, 'trigger', 'stop')
|
||
params = self.omit(params, ['clientOrderId', 'trigger', 'stop'])
|
||
response = None
|
||
if isTrigger:
|
||
response = self.privateDeleteSmartordersId(self.extend(request, params))
|
||
else:
|
||
response = self.privateDeleteOrdersId(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "orderId":"210832697138888704",
|
||
# "clientOrderId":"",
|
||
# "state":"PENDING_CANCEL",
|
||
# "code":200,
|
||
# "message":""
|
||
# }
|
||
#
|
||
return self.parse_order(response)
|
||
|
||
def cancel_all_orders(self, symbol: Str = None, params={}):
|
||
"""
|
||
cancel all open orders
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/order#cancel-all-orders
|
||
https://api-docs.poloniex.com/spot/api/private/smart-order#cancel-all-orders # trigger orders
|
||
https://api-docs.poloniex.com/v3/futures/api/trade/cancel-all-orders - contract markets
|
||
|
||
: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 boolean [params.trigger]: True if canceling trigger orders
|
||
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
self.load_markets()
|
||
request: dict = {
|
||
# 'accountTypes': 'SPOT',
|
||
'symbols': [],
|
||
}
|
||
market: Market = None
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
request['symbols'] = [
|
||
market['id'],
|
||
]
|
||
response = None
|
||
marketType = None
|
||
marketType, params = self.handle_market_type_and_params('cancelAllOrders', market, params)
|
||
if marketType == 'swap' or marketType == 'future':
|
||
raw = self.swapPrivateDeleteV3TradeAllOrders(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "Success",
|
||
# "data": [
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "Success",
|
||
# "ordId": "418885787866388511",
|
||
# "clOrdId": "polo418885787866388511"
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
response = self.safe_list(raw, 'data')
|
||
return self.parse_orders(response, market)
|
||
isTrigger = self.safe_value_2(params, 'trigger', 'stop')
|
||
params = self.omit(params, ['trigger', 'stop'])
|
||
if isTrigger:
|
||
response = self.privateDeleteSmartorders(self.extend(request, params))
|
||
else:
|
||
response = self.privateDeleteOrders(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "orderId" : "78xxxxxxxx80",
|
||
# "clientOrderId" : "",
|
||
# "state" : "NEW",
|
||
# "code" : 200,
|
||
# "message" : ""
|
||
# }, {
|
||
# "orderId" : "78xxxxxxxxx80",
|
||
# "clientOrderId" : "",
|
||
# "state" : "NEW",
|
||
# "code" : 200,
|
||
# "message" : ""
|
||
# }
|
||
# ]
|
||
#
|
||
return self.parse_orders(response, market)
|
||
|
||
def fetch_order(self, id: str, symbol: Str = None, params={}):
|
||
"""
|
||
fetch an order by it's id
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/order#order-details
|
||
https://api-docs.poloniex.com/spot/api/private/smart-order#open-orders # trigger orders
|
||
|
||
:param str id: order id
|
||
:param str symbol: unified market symbol, default is None
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param boolean [params.trigger]: True if fetching a trigger order
|
||
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
self.load_markets()
|
||
id = str(id)
|
||
request: dict = {
|
||
'id': id,
|
||
}
|
||
market = None
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
request['symbol'] = market['id']
|
||
marketType = None
|
||
marketType, params = self.handle_market_type_and_params('fetchOrder', market, params)
|
||
if marketType != 'spot':
|
||
raise NotSupported(self.id + ' fetchOrder() is not supported for ' + marketType + ' markets yet')
|
||
isTrigger = self.safe_value_2(params, 'trigger', 'stop')
|
||
params = self.omit(params, ['trigger', 'stop'])
|
||
response = None
|
||
if isTrigger:
|
||
response = self.privateGetSmartordersId(self.extend(request, params))
|
||
response = self.safe_value(response, 0)
|
||
else:
|
||
response = self.privateGetOrdersId(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "id": "21934611974062080",
|
||
# "clientOrderId": "123",
|
||
# "symbol": "TRX_USDC",
|
||
# "state": "NEW",
|
||
# "accountType": "SPOT",
|
||
# "side": "SELL",
|
||
# "type": "LIMIT",
|
||
# "timeInForce": "GTC",
|
||
# "quantity": "1.00",
|
||
# "price": "10.00",
|
||
# "avgPrice": "0.00",
|
||
# "amount": "0.00",
|
||
# "filledQuantity": "0.00",
|
||
# "filledAmount": "0.00",
|
||
# "stopPrice": "3750.00", # for trigger orders
|
||
# "createTime": 1646196019020,
|
||
# "updateTime": 1646196019020
|
||
# }
|
||
#
|
||
order = self.parse_order(response)
|
||
order['id'] = id
|
||
return order
|
||
|
||
def fetch_order_status(self, id: str, symbol: Str = None, params={}):
|
||
self.load_markets()
|
||
orders = self.fetch_open_orders(symbol, None, None, params)
|
||
indexed = self.index_by(orders, 'id')
|
||
return 'open' if (id in indexed) else 'closed'
|
||
|
||
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://api-docs.poloniex.com/spot/api/private/trade#trades-by-order-id
|
||
|
||
: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>`
|
||
"""
|
||
self.load_markets()
|
||
request: dict = {
|
||
'id': id,
|
||
}
|
||
trades = self.privateGetOrdersIdTrades(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "id": "30341456333942784",
|
||
# "symbol": "LINK_USDT",
|
||
# "accountType": "SPOT",
|
||
# "orderId": "30249408733945856",
|
||
# "side": "BUY",
|
||
# "type": "LIMIT",
|
||
# "matchRole": "MAKER",
|
||
# "createTime": 1648200366864,
|
||
# "price": "3.1",
|
||
# "quantity": "1",
|
||
# "amount": "3.1",
|
||
# "feeCurrency": "LINK",
|
||
# "feeAmount": "0.00145",
|
||
# "pageId": "30341456333942784",
|
||
# "clientOrderId": ""
|
||
# }
|
||
# ]
|
||
#
|
||
return self.parse_trades(trades)
|
||
|
||
def parse_balance(self, response) -> Balances:
|
||
result: dict = {
|
||
'info': response,
|
||
'timestamp': None,
|
||
'datetime': None,
|
||
}
|
||
# for swap
|
||
if not isinstance(response, list):
|
||
ts = self.safe_integer(response, 'uTime')
|
||
result['timestamp'] = ts
|
||
result['datetime'] = self.iso8601(ts)
|
||
details = self.safe_list(response, 'details', [])
|
||
for i in range(0, len(details)):
|
||
balance = details[i]
|
||
currencyId = self.safe_string(balance, 'ccy')
|
||
code = self.safe_currency_code(currencyId)
|
||
account = self.account()
|
||
account['total'] = self.safe_string(balance, 'avail')
|
||
account['used'] = self.safe_string(balance, 'im')
|
||
result[code] = account
|
||
return self.safe_balance(result)
|
||
# for spot
|
||
for i in range(0, len(response)):
|
||
account = self.safe_value(response, i, {})
|
||
balances = self.safe_value(account, 'balances')
|
||
for j in range(0, len(balances)):
|
||
balance = self.safe_value(balances, j)
|
||
currencyId = self.safe_string(balance, 'currency')
|
||
code = self.safe_currency_code(currencyId)
|
||
newAccount = self.account()
|
||
newAccount['free'] = self.safe_string(balance, 'available')
|
||
newAccount['used'] = self.safe_string(balance, 'hold')
|
||
result[code] = newAccount
|
||
return self.safe_balance(result)
|
||
|
||
def fetch_balance(self, params={}) -> Balances:
|
||
"""
|
||
query for balance and get the amount of funds available for trading or funds locked in orders
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/account#all-account-balances
|
||
https://api-docs.poloniex.com/v3/futures/api/account/balance
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
|
||
"""
|
||
self.load_markets()
|
||
marketType = None
|
||
marketType, params = self.handle_market_type_and_params('fetchBalance', None, params)
|
||
if marketType != 'spot':
|
||
responseRaw = self.swapPrivateGetV3AccountBalance(params)
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "",
|
||
# "data": {
|
||
# "state": "NORMAL",
|
||
# "eq": "9.98571622",
|
||
# "isoEq": "0",
|
||
# "im": "0",
|
||
# "mm": "0",
|
||
# "mmr": "0",
|
||
# "upl": "0",
|
||
# "availMgn": "9.98571622",
|
||
# "cTime": "1738093601775",
|
||
# "uTime": "1740829116236",
|
||
# "details": [
|
||
# {
|
||
# "ccy": "USDT",
|
||
# "eq": "9.98571622",
|
||
# "isoEq": "0",
|
||
# "avail": "9.98571622",
|
||
# "trdHold": "0",
|
||
# "upl": "0",
|
||
# "isoAvail": "0",
|
||
# "isoHold": "0",
|
||
# "isoUpl": "0",
|
||
# "im": "0",
|
||
# "mm": "0",
|
||
# "mmr": "0",
|
||
# "imr": "0",
|
||
# "cTime": "1740829116236",
|
||
# "uTime": "1740829116236"
|
||
# }
|
||
# ]
|
||
# }
|
||
# }
|
||
#
|
||
data = self.safe_dict(responseRaw, 'data', {})
|
||
return self.parse_balance(data)
|
||
request: dict = {
|
||
'accountType': 'SPOT',
|
||
}
|
||
response = self.privateGetAccountsBalances(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "accountId" : "7xxxxxxxxxx8",
|
||
# "accountType" : "SPOT",
|
||
# "balances" : [
|
||
# {
|
||
# "currencyId" : "214",
|
||
# "currency" : "USDT",
|
||
# "available" : "2.00",
|
||
# "hold" : "0.00"
|
||
# }
|
||
# ]
|
||
# }
|
||
# ]
|
||
#
|
||
return self.parse_balance(response)
|
||
|
||
def fetch_trading_fees(self, params={}) -> TradingFees:
|
||
"""
|
||
fetch the trading fees for multiple markets
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/account#fee-info
|
||
|
||
: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.privateGetFeeinfo(params)
|
||
#
|
||
# {
|
||
# "trxDiscount" : False,
|
||
# "makerRate" : "0.00145",
|
||
# "takerRate" : "0.00155",
|
||
# "volume30D" : "0.00"
|
||
# }
|
||
#
|
||
result: dict = {}
|
||
for i in range(0, len(self.symbols)):
|
||
symbol = self.symbols[i]
|
||
result[symbol] = {
|
||
'info': response,
|
||
'symbol': symbol,
|
||
'maker': self.safe_number(response, 'makerRate'),
|
||
'taker': self.safe_number(response, 'takerRate'),
|
||
'percentage': True,
|
||
'tierBased': True,
|
||
}
|
||
return result
|
||
|
||
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://api-docs.poloniex.com/spot/api/public/market-data#order-book
|
||
https://api-docs.poloniex.com/v3/futures/api/market/get-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 = {
|
||
'symbol': market['id'],
|
||
}
|
||
if limit is not None:
|
||
request['limit'] = limit # The default value of limit is 10. Valid limit values are: 5, 10, 20, 50, 100, 150.
|
||
if market['contract']:
|
||
request['limit'] = self.find_nearest_ceiling([5, 10, 20, 100, 150], limit)
|
||
if market['contract']:
|
||
responseRaw = self.swapPublicGetV3MarketOrderBook(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "code": 200,
|
||
# "data": {
|
||
# "asks": [["58700", "9934"], ..],
|
||
# "bids": [["58600", "9952"], ..],
|
||
# "s": "100",
|
||
# "ts": 1719974138333
|
||
# },
|
||
# "msg": "Success"
|
||
# }
|
||
#
|
||
data = self.safe_dict(responseRaw, 'data', {})
|
||
ts = self.safe_integer(data, 'ts')
|
||
return self.parse_order_book(data, symbol, ts)
|
||
response = self.publicGetMarketsSymbolOrderBook(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "time" : 1659695219507,
|
||
# "scale" : "-1",
|
||
# "asks" : ["23139.82", "0.317981", "23140", "0.191091", "23170.06", "0.01", "23200", "0.107758", "23230.55", "0.01", "23247.2", "0.154", "23254", "0.005121", "23263", "0.038", "23285.4", "0.308", "23300", "0.108896"],
|
||
# "bids" : ["23139.74", "0.432092", "23139.73", "0.198592", "23123.21", "0.000886", "23123.2", "0.308", "23121.4", "0.154", "23105", "0.000789", "23100", "0.078175", "23069.1", "0.026276", "23068.83", "0.001329", "23051", "0.000048"],
|
||
# "ts" : 1659695219512
|
||
# }
|
||
#
|
||
timestamp = self.safe_integer(response, 'time')
|
||
asks = self.safe_value(response, 'asks')
|
||
bids = self.safe_value(response, 'bids')
|
||
asksResult = []
|
||
bidsResult = []
|
||
for i in range(0, len(asks)):
|
||
if (i % 2) < 1:
|
||
price = self.safe_number(asks, i)
|
||
amount = self.safe_number(asks, self.sum(i, 1))
|
||
asksResult.append([price, amount])
|
||
for i in range(0, len(bids)):
|
||
if (i % 2) < 1:
|
||
price = self.safe_number(bids, i)
|
||
amount = self.safe_number(bids, self.sum(i, 1))
|
||
bidsResult.append([price, amount])
|
||
return {
|
||
'symbol': market['symbol'],
|
||
'bids': self.sort_by(bidsResult, 0, True),
|
||
'asks': self.sort_by(asksResult, 0),
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'nonce': None,
|
||
}
|
||
|
||
def create_deposit_address(self, code: str, params={}) -> DepositAddress:
|
||
"""
|
||
create a currency deposit address
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/wallet#deposit-addresses
|
||
|
||
:param str code: unified currency code of the currency for the deposit address
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
|
||
"""
|
||
self.load_markets()
|
||
request, extraParams, currency, networkEntry = self.prepare_request_for_deposit_address(code, params)
|
||
params = extraParams
|
||
response = self.privatePostWalletsAddress(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "address" : "0xfxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxf"
|
||
# }
|
||
#
|
||
return self.parse_deposit_address_special(response, currency, networkEntry)
|
||
|
||
def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
|
||
"""
|
||
fetch the deposit address for a currency associated with self account
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/wallet#deposit-addresses
|
||
|
||
:param str code: unified currency code
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
|
||
"""
|
||
self.load_markets()
|
||
request, extraParams, currency, networkEntry = self.prepare_request_for_deposit_address(code, params)
|
||
params = extraParams
|
||
response = self.privateGetWalletsAddresses(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "USDTTRON" : "Txxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxp"
|
||
# }
|
||
#
|
||
keys = list(response.keys())
|
||
length = len(keys)
|
||
if length < 1:
|
||
raise ExchangeError(self.id + ' fetchDepositAddress() returned an empty response, you might need to try "createDepositAddress" at first and then use "fetchDepositAddress"')
|
||
return self.parse_deposit_address_special(response, currency, networkEntry)
|
||
|
||
def prepare_request_for_deposit_address(self, code: str, params: dict = {}) -> Any:
|
||
if not (code in self.currencies):
|
||
raise BadSymbol(self.id + ' fetchDepositAddress(): can not recognize ' + code + ' currency, you might try using unified currency-code and add provide specific "network" parameter, like: fetchDepositAddress("USDT", {"network": "TRC20"})')
|
||
currency = self.currency(code)
|
||
networkCode = None
|
||
networkCode, params = self.handle_network_code_and_params(params)
|
||
if networkCode is None:
|
||
# we need to know the network to find out the currency-junction
|
||
raise ArgumentsRequired(self.id + ' fetchDepositAddress requires a network parameter for ' + code + '.')
|
||
exchangeNetworkId = None
|
||
networkCode = self.network_id_to_code(networkCode, code)
|
||
networkEntry = self.safe_dict(currency['networks'], networkCode)
|
||
if networkEntry is not None:
|
||
exchangeNetworkId = networkEntry['id']
|
||
else:
|
||
exchangeNetworkId = networkCode
|
||
request = {
|
||
'currency': exchangeNetworkId,
|
||
}
|
||
return [request, params, currency, networkEntry]
|
||
|
||
def parse_deposit_address_special(self, response, currency, networkEntry) -> DepositAddress:
|
||
address = self.safe_string(response, 'address')
|
||
if address is None:
|
||
address = self.safe_string(response, networkEntry['id'])
|
||
tag: Str = None
|
||
self.check_address(address)
|
||
if networkEntry is not None:
|
||
depositAddress = self.safe_string(networkEntry['info'], 'depositAddress')
|
||
if depositAddress is not None:
|
||
tag = address
|
||
address = depositAddress
|
||
return {
|
||
'info': response,
|
||
'currency': currency['code'],
|
||
'network': self.safe_string(networkEntry, 'network'),
|
||
'address': address,
|
||
'tag': tag,
|
||
}
|
||
|
||
def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
|
||
"""
|
||
transfer currency internally between wallets on the same account
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/account#accounts-transfer
|
||
|
||
:param str code: unified currency code
|
||
:param float amount: amount to transfer
|
||
:param str fromAccount: account to transfer from
|
||
:param str toAccount: account to transfer to
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
|
||
"""
|
||
self.load_markets()
|
||
currency = self.currency(code)
|
||
accountsByType = self.safe_value(self.options, 'accountsByType', {})
|
||
fromId = self.safe_string(accountsByType, fromAccount, fromAccount)
|
||
toId = self.safe_string(accountsByType, toAccount, fromAccount)
|
||
request: dict = {
|
||
'amount': self.currency_to_precision(code, amount),
|
||
'currency': currency['id'],
|
||
'fromAccount': fromId,
|
||
'toAccount': toId,
|
||
}
|
||
response = self.privatePostAccountsTransfer(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "transferId" : "168041074"
|
||
# }
|
||
#
|
||
return self.parse_transfer(response, currency)
|
||
|
||
def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
|
||
#
|
||
# {
|
||
# "transferId" : "168041074"
|
||
# }
|
||
#
|
||
return {
|
||
'info': transfer,
|
||
'id': self.safe_string(transfer, 'transferId'),
|
||
'timestamp': None,
|
||
'datetime': None,
|
||
'currency': self.safe_string(currency, 'id'),
|
||
'amount': None,
|
||
'fromAccount': None,
|
||
'toAccount': None,
|
||
'status': None,
|
||
}
|
||
|
||
def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction:
|
||
"""
|
||
make a withdrawal
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/wallet#withdraw-currency
|
||
|
||
: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)
|
||
request, extraParams, currency, networkEntry = self.prepare_request_for_deposit_address(code, params)
|
||
params = extraParams
|
||
request['amount'] = self.currency_to_precision(code, amount)
|
||
request['address'] = address
|
||
if tag is not None:
|
||
request['paymentId'] = tag
|
||
response = self.privatePostWalletsWithdraw(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "response": "Withdrew 1.00000000 USDT.",
|
||
# "email2FA": False,
|
||
# "withdrawalNumber": 13449869
|
||
# }
|
||
#
|
||
withdrawResponse = {
|
||
'response': response,
|
||
'withdrawNetworkEntry': networkEntry,
|
||
}
|
||
return self.parse_transaction(withdrawResponse, currency)
|
||
|
||
def fetch_transactions_helper(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
||
self.load_markets()
|
||
year = 31104000 # 60 * 60 * 24 * 30 * 12 = one year of history, why not
|
||
now = self.seconds()
|
||
start = self.parse_to_int(since / 1000) if (since is not None) else now - 10 * year
|
||
request: dict = {
|
||
'start': start, # UNIX timestamp, required
|
||
'end': now, # UNIX timestamp, required
|
||
}
|
||
response = self.privateGetWalletsActivity(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "adjustments":[],
|
||
# "deposits":[
|
||
# {
|
||
# "currency": "BTC",
|
||
# "address": "1MEtiqJWru53FhhHrfJPPvd2tC3TPDVcmW",
|
||
# "amount": "0.01063000",
|
||
# "confirmations": 1,
|
||
# "txid": "952b0e1888d6d491591facc0d37b5ebec540ac1efb241fdbc22bcc20d1822fb6",
|
||
# "timestamp": 1507916888,
|
||
# "status": "COMPLETE"
|
||
# },
|
||
# {
|
||
# "currency": "ETH",
|
||
# "address": "0x20108ba20b65c04d82909e91df06618107460197",
|
||
# "amount": "4.00000000",
|
||
# "confirmations": 38,
|
||
# "txid": "0x4be260073491fe63935e9e0da42bd71138fdeb803732f41501015a2d46eb479d",
|
||
# "timestamp": 1525060430,
|
||
# "status": "COMPLETE"
|
||
# }
|
||
# ],
|
||
# "withdrawals":[
|
||
# {
|
||
# "withdrawalNumber":13449869,
|
||
# "currency":"USDTTRON", # not documented in API docs, see commonCurrencies in describe()
|
||
# "address":"TXGaqPW23JdRWhsVwS2mRsGsegbdnAd3Rw",
|
||
# "amount":"1.00000000",
|
||
# "fee":"0.00000000",
|
||
# "timestamp":1591573420,
|
||
# "status":"COMPLETE: dadf427224b3d44b38a2c13caa4395e4666152556ca0b2f67dbd86a95655150f",
|
||
# "ipAddress":"x.x.x.x",
|
||
# "canCancel":0,
|
||
# "canResendEmail":0,
|
||
# "paymentID":null,
|
||
# "scope":"crypto"
|
||
# },
|
||
# {
|
||
# "withdrawalNumber": 8224394,
|
||
# "currency": "EMC2",
|
||
# "address": "EYEKyCrqTNmVCpdDV8w49XvSKRP9N3EUyF",
|
||
# "amount": "63.10796020",
|
||
# "fee": "0.01000000",
|
||
# "timestamp": 1510819838,
|
||
# "status": "COMPLETE: d37354f9d02cb24d98c8c4fc17aa42f475530b5727effdf668ee5a43ce667fd6",
|
||
# "ipAddress": "x.x.x.x"
|
||
# },
|
||
# {
|
||
# "withdrawalNumber": 9290444,
|
||
# "currency": "ETH",
|
||
# "address": "0x191015ff2e75261d50433fbd05bd57e942336149",
|
||
# "amount": "0.15500000",
|
||
# "fee": "0.00500000",
|
||
# "timestamp": 1514099289,
|
||
# "status": "COMPLETE: 0x12d444493b4bca668992021fd9e54b5292b8e71d9927af1f076f554e4bea5b2d",
|
||
# "ipAddress": "x.x.x.x"
|
||
# },
|
||
# {
|
||
# "withdrawalNumber": 11518260,
|
||
# "currency": "BTC",
|
||
# "address": "8JoDXAmE1GY2LRK8jD1gmAmgRPq54kXJ4t",
|
||
# "amount": "0.20000000",
|
||
# "fee": "0.00050000",
|
||
# "timestamp": 1527918155,
|
||
# "status": "COMPLETE: 1864f4ebb277d90b0b1ff53259b36b97fa1990edc7ad2be47c5e0ab41916b5ff",
|
||
# "ipAddress": "x.x.x.x"
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
return response
|
||
|
||
def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
||
"""
|
||
fetch history of deposits and withdrawals
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/wallet#wallets-activity-records
|
||
|
||
:param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
|
||
:param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
|
||
:param int [limit]: max number of deposit/withdrawals to return, default is None
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a list of `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
|
||
"""
|
||
self.load_markets()
|
||
response = self.fetch_transactions_helper(code, since, limit, params)
|
||
currency: Currency = None
|
||
if code is not None:
|
||
currency = self.currency(code)
|
||
withdrawals = self.safe_value(response, 'withdrawals', [])
|
||
deposits = self.safe_value(response, 'deposits', [])
|
||
withdrawalTransactions = self.parse_transactions(withdrawals, currency, since, limit)
|
||
depositTransactions = self.parse_transactions(deposits, currency, since, limit)
|
||
transactions = self.array_concat(depositTransactions, withdrawalTransactions)
|
||
return self.filter_by_currency_since_limit(self.sort_by(transactions, 'timestamp'), code, since, limit)
|
||
|
||
def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
||
"""
|
||
fetch all withdrawals made from an account
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/wallet#wallets-activity-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
|
||
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
||
"""
|
||
response = self.fetch_transactions_helper(code, since, limit, params)
|
||
currency: Currency = None
|
||
if code is not None:
|
||
currency = self.currency(code)
|
||
withdrawals = self.safe_value(response, 'withdrawals', [])
|
||
transactions = self.parse_transactions(withdrawals, currency, since, limit)
|
||
return self.filter_by_currency_since_limit(transactions, code, since, limit)
|
||
|
||
def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
|
||
"""
|
||
fetch deposit and withdraw fees
|
||
|
||
https://api-docs.poloniex.com/spot/api/public/reference-data#currency-information
|
||
|
||
: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 `fees structures <https://docs.ccxt.com/#/?id=fee-structure>`
|
||
"""
|
||
self.load_markets()
|
||
response = self.publicGetCurrencies(self.extend(params, {'includeMultiChainCurrencies': True}))
|
||
#
|
||
# [
|
||
# {
|
||
# "1CR": {
|
||
# "id": 1,
|
||
# "name": "1CRedit",
|
||
# "description": "BTC Clone",
|
||
# "type": "address",
|
||
# "withdrawalFee": "0.01000000",
|
||
# "minConf": 10000,
|
||
# "depositAddress": null,
|
||
# "blockchain": "1CR",
|
||
# "delisted": False,
|
||
# "tradingState": "NORMAL",
|
||
# "walletState": "DISABLED",
|
||
# "parentChain": null,
|
||
# "isMultiChain": False,
|
||
# "isChildChain": False,
|
||
# "childChains": []
|
||
# }
|
||
# }
|
||
# ]
|
||
#
|
||
data: dict = {}
|
||
for i in range(0, len(response)):
|
||
entry = response[i]
|
||
currencies = list(entry.keys())
|
||
currencyId = self.safe_string(currencies, 0)
|
||
data[currencyId] = entry[currencyId]
|
||
return self.parse_deposit_withdraw_fees(data, codes)
|
||
|
||
def parse_deposit_withdraw_fees(self, response, codes=None, currencyIdKey=None):
|
||
#
|
||
# {
|
||
# "1CR": {
|
||
# "id": 1,
|
||
# "name": "1CRedit",
|
||
# "description": "BTC Clone",
|
||
# "type": "address",
|
||
# "withdrawalFee": "0.01000000",
|
||
# "minConf": 10000,
|
||
# "depositAddress": null,
|
||
# "blockchain": "1CR",
|
||
# "delisted": False,
|
||
# "tradingState": "NORMAL",
|
||
# "walletState": "DISABLED",
|
||
# "parentChain": null,
|
||
# "isMultiChain": False,
|
||
# "isChildChain": False,
|
||
# "childChains": []
|
||
# },
|
||
# }
|
||
#
|
||
depositWithdrawFees: dict = {}
|
||
codes = self.market_codes(codes)
|
||
responseKeys = list(response.keys())
|
||
for i in range(0, len(responseKeys)):
|
||
currencyId = responseKeys[i]
|
||
code = self.safe_currency_code(currencyId)
|
||
feeInfo = response[currencyId]
|
||
if (codes is None) or (self.in_array(code, codes)):
|
||
currency = self.currency(code)
|
||
depositWithdrawFees[code] = self.parse_deposit_withdraw_fee(feeInfo, currency)
|
||
childChains = self.safe_value(feeInfo, 'childChains')
|
||
chainsLength = len(childChains)
|
||
if chainsLength > 0:
|
||
for j in range(0, len(childChains)):
|
||
networkId = childChains[j]
|
||
networkId = networkId.replace(code, '')
|
||
networkCode = self.network_id_to_code(networkId)
|
||
networkInfo = self.safe_value(response, networkId)
|
||
networkObject: dict = {}
|
||
withdrawFee = self.safe_number(networkInfo, 'withdrawalFee')
|
||
networkObject[networkCode] = {
|
||
'withdraw': {
|
||
'fee': withdrawFee,
|
||
'percentage': False if (withdrawFee is not None) else None,
|
||
},
|
||
'deposit': {
|
||
'fee': None,
|
||
'percentage': None,
|
||
},
|
||
}
|
||
depositWithdrawFees[code]['networks'] = self.extend(depositWithdrawFees[code]['networks'], networkObject)
|
||
return depositWithdrawFees
|
||
|
||
def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
|
||
depositWithdrawFee = self.deposit_withdraw_fee({})
|
||
depositWithdrawFee['info'][currency['code']] = fee
|
||
networkId = self.safe_string(fee, 'blockchain')
|
||
withdrawFee = self.safe_number(fee, 'withdrawalFee')
|
||
withdrawResult: dict = {
|
||
'fee': withdrawFee,
|
||
'percentage': False if (withdrawFee is not None) else None,
|
||
}
|
||
depositResult: dict = {
|
||
'fee': None,
|
||
'percentage': None,
|
||
}
|
||
depositWithdrawFee['withdraw'] = withdrawResult
|
||
depositWithdrawFee['deposit'] = depositResult
|
||
networkCode = self.network_id_to_code(networkId)
|
||
depositWithdrawFee['networks'][networkCode] = {
|
||
'withdraw': withdrawResult,
|
||
'deposit': depositResult,
|
||
}
|
||
return depositWithdrawFee
|
||
|
||
def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
||
"""
|
||
fetch all deposits made to an account
|
||
|
||
https://api-docs.poloniex.com/spot/api/private/wallet#wallets-activity-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
|
||
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
||
"""
|
||
response = self.fetch_transactions_helper(code, since, limit, params)
|
||
currency = None
|
||
if code is not None:
|
||
currency = self.currency(code)
|
||
deposits = self.safe_value(response, 'deposits', [])
|
||
transactions = self.parse_transactions(deposits, currency, since, limit)
|
||
return self.filter_by_currency_since_limit(transactions, code, since, limit)
|
||
|
||
def parse_transaction_status(self, status: Str):
|
||
statuses: dict = {
|
||
'COMPLETE': 'ok',
|
||
'COMPLETED': 'ok',
|
||
'AWAITING APPROVAL': 'pending',
|
||
'AWAITING_APPROVAL': 'pending',
|
||
'PENDING': 'pending',
|
||
'PROCESSING': 'pending',
|
||
'COMPLETE ERROR': 'failed',
|
||
'COMPLETE_ERROR': 'failed',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
|
||
#
|
||
# deposits
|
||
#
|
||
# {
|
||
# "txid": "f49d489616911db44b740612d19464521179c76ebe9021af85b6de1e2f8d68cd",
|
||
# "amount": "49798.01987021",
|
||
# "status": "COMPLETE",
|
||
# "address": "DJVJZ58tJC8UeUv9Tqcdtn6uhWobouxFLT",
|
||
# "currency": "DOGE",
|
||
# "timestamp": 1524321838,
|
||
# "confirmations": 3371,
|
||
# "depositNumber": 134587098
|
||
# }
|
||
#
|
||
# withdrawals
|
||
#
|
||
# {
|
||
# "withdrawalRequestsId": 7397527,
|
||
# "currency": "ETC",
|
||
# "address": "0x26419a62055af459d2cd69bb7392f5100b75e304",
|
||
# "amount": "13.19951600",
|
||
# "fee": "0.01000000",
|
||
# "timestamp": 1506010932,
|
||
# "status": "COMPLETED",
|
||
# "txid": "343346392f82ac16e8c2604f2a604b7b2382d0e9d8030f673821f8de4b5f5bk",
|
||
# "ipAddress": "1.2.3.4",
|
||
# "paymentID": null
|
||
# }
|
||
#
|
||
# withdraw
|
||
#
|
||
# {
|
||
# "withdrawalRequestsId": 33485231
|
||
# }
|
||
#
|
||
# if it's being parsed from "withdraw()" method, get the original response
|
||
if 'withdrawNetworkEntry' in transaction:
|
||
transaction = transaction['response']
|
||
timestamp = self.safe_timestamp(transaction, 'timestamp')
|
||
currencyId = self.safe_string(transaction, 'currency')
|
||
code = self.safe_currency_code(currencyId)
|
||
status = self.safe_string(transaction, 'status', 'pending')
|
||
status = self.parse_transaction_status(status)
|
||
txid = self.safe_string(transaction, 'txid')
|
||
type = 'withdrawal' if ('withdrawalRequestsId' in transaction) else 'deposit'
|
||
id = self.safe_string_2(transaction, 'withdrawalRequestsId', 'depositNumber')
|
||
address = self.safe_string(transaction, 'address')
|
||
tag = self.safe_string(transaction, 'paymentID')
|
||
amountString = self.safe_string(transaction, 'amount')
|
||
feeCostString = self.safe_string(transaction, 'fee')
|
||
if type == 'withdrawal':
|
||
amountString = Precise.string_sub(amountString, feeCostString)
|
||
return {
|
||
'info': transaction,
|
||
'id': id,
|
||
'currency': code,
|
||
'amount': self.parse_number(amountString),
|
||
'network': None,
|
||
'address': address,
|
||
'addressTo': None,
|
||
'addressFrom': None,
|
||
'tag': tag,
|
||
'tagTo': None,
|
||
'tagFrom': None,
|
||
'status': status,
|
||
'type': type,
|
||
'updated': None,
|
||
'txid': txid,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'comment': None,
|
||
'internal': None,
|
||
'fee': {
|
||
'currency': code,
|
||
'cost': self.parse_number(feeCostString),
|
||
'rate': None,
|
||
},
|
||
}
|
||
|
||
def set_leverage(self, leverage: int, symbol: Str = None, params={}):
|
||
"""
|
||
set the level of leverage for a market
|
||
|
||
https://api-docs.poloniex.com/v3/futures/api/positions/set-leverage
|
||
|
||
:param int leverage: the rate of leverage
|
||
:param str symbol: unified market symbol
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param str [params.marginMode]: 'cross' or 'isolated'
|
||
:returns dict: response from the exchange
|
||
"""
|
||
if symbol is None:
|
||
raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
|
||
self.load_markets()
|
||
market = self.market(symbol)
|
||
marginMode = None
|
||
marginMode, params = self.handle_margin_mode_and_params('setLeverage', params)
|
||
if marginMode is None:
|
||
raise ArgumentsRequired(self.id + ' setLeverage() requires a marginMode parameter "cross" or "isolated"')
|
||
hedged: Bool = None
|
||
hedged, params = self.handle_param_bool(params, 'hedged', False)
|
||
if hedged:
|
||
if not ('posSide' in params):
|
||
raise ArgumentsRequired(self.id + ' setLeverage() requires a posSide parameter for hedged mode: "LONG" or "SHORT"')
|
||
request: dict = {
|
||
'lever': leverage,
|
||
'mgnMode': marginMode.upper(),
|
||
'symbol': market['id'],
|
||
}
|
||
response = self.swapPrivatePostV3PositionLeverage(self.extend(request, params))
|
||
return response
|
||
|
||
def fetch_leverage(self, symbol: str, params={}) -> Leverage:
|
||
"""
|
||
fetch the set leverage for a market
|
||
|
||
https://api-docs.poloniex.com/v3/futures/api/positions/get-leverages
|
||
|
||
:param str symbol: unified market symbol
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `leverage structure <https://docs.ccxt.com/#/?id=leverage-structure>`
|
||
"""
|
||
self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
marginMode = None
|
||
marginMode, params = self.handle_margin_mode_and_params('fetchLeverage', params)
|
||
if marginMode is None:
|
||
raise ArgumentsRequired(self.id + ' fetchLeverage() requires a marginMode parameter "cross" or "isolated"')
|
||
request['mgnMode'] = marginMode.upper()
|
||
response = self.swapPrivateGetV3PositionLeverages(self.extend(request, params))
|
||
#
|
||
# for one-way mode:
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "",
|
||
# "data": [
|
||
# {
|
||
# "symbol": "BTC_USDT_PERP",
|
||
# "lever": "10",
|
||
# "mgnMode": "CROSS",
|
||
# "posSide": "BOTH"
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
# for hedge:
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "",
|
||
# "data": [
|
||
# {
|
||
# "symbol": "BTC_USDT_PERP",
|
||
# "lever": "20",
|
||
# "mgnMode": "CROSS",
|
||
# "posSide": "SHORT"
|
||
# },
|
||
# {
|
||
# "symbol": "BTC_USDT_PERP",
|
||
# "lever": "20",
|
||
# "mgnMode": "CROSS",
|
||
# "posSide": "LONG"
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
return self.parse_leverage(response, market)
|
||
|
||
def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
|
||
shortLeverage: Int = None
|
||
longLeverage: Int = None
|
||
marketId: Str = None
|
||
marginMode: Str = None
|
||
data = self.safe_list(leverage, 'data')
|
||
for i in range(0, len(data)):
|
||
entry = data[i]
|
||
marketId = self.safe_string(entry, 'symbol')
|
||
marginMode = self.safe_string(entry, 'mgnMode')
|
||
lever = self.safe_integer(entry, 'lever')
|
||
posSide = self.safe_string(entry, 'posSide')
|
||
if posSide == 'LONG':
|
||
longLeverage = lever
|
||
elif posSide == 'SHORT':
|
||
shortLeverage = lever
|
||
else:
|
||
longLeverage = lever
|
||
shortLeverage = lever
|
||
return {
|
||
'info': leverage,
|
||
'symbol': self.safe_symbol(marketId, market),
|
||
'marginMode': marginMode,
|
||
'longLeverage': longLeverage,
|
||
'shortLeverage': shortLeverage,
|
||
}
|
||
|
||
def fetch_position_mode(self, symbol: Str = None, params={}):
|
||
"""
|
||
fetchs the position mode, hedged or one way, hedged for binance is set identically for all linear markets or all inverse markets
|
||
|
||
https://api-docs.poloniex.com/v3/futures/api/positions/position-mode-switch
|
||
|
||
:param str symbol: unified symbol of the market to fetch the order book for
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: an object detailing whether the market is in hedged or one-way mode
|
||
"""
|
||
response = self.swapPrivateGetV3PositionMode(params)
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "Success",
|
||
# "data": {
|
||
# "posMode": "ONE_WAY"
|
||
# }
|
||
# }
|
||
#
|
||
data = self.safe_dict(response, 'data', {})
|
||
posMode = self.safe_string(data, 'posMode')
|
||
hedged = posMode == 'HEDGE'
|
||
return {
|
||
'info': response,
|
||
'hedged': hedged,
|
||
}
|
||
|
||
def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
|
||
"""
|
||
set hedged to True or False for a market
|
||
|
||
https://api-docs.poloniex.com/v3/futures/api/positions/position-mode-switch
|
||
|
||
:param bool hedged: set to True to use dualSidePosition
|
||
:param str symbol: not used by binance setPositionMode()
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: response from the exchange
|
||
"""
|
||
mode = 'HEDGE' if hedged else 'ONE_WAY'
|
||
request: dict = {
|
||
'posMode': mode,
|
||
}
|
||
response = self.swapPrivatePostV3PositionMode(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "Success",
|
||
# "data": {}
|
||
# }
|
||
#
|
||
return response
|
||
|
||
def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
|
||
"""
|
||
fetch all open positions
|
||
|
||
https://api-docs.poloniex.com/v3/futures/api/positions/get-current-position
|
||
|
||
:param str[]|None symbols: list of unified market symbols
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param boolean [params.standard]: whether to fetch standard contract positions
|
||
:returns dict[]: a list of `position structures <https://docs.ccxt.com/#/?id=position-structure>`
|
||
"""
|
||
self.load_markets()
|
||
symbols = self.market_symbols(symbols)
|
||
response = self.swapPrivateGetV3TradePositionOpens(params)
|
||
#
|
||
# {
|
||
# "code": "200",
|
||
# "msg": "",
|
||
# "data": [
|
||
# {
|
||
# "symbol": "BTC_USDT_PERP",
|
||
# "posSide": "LONG",
|
||
# "side": "BUY",
|
||
# "mgnMode": "CROSS",
|
||
# "openAvgPx": "94193.42",
|
||
# "qty": "1",
|
||
# "availQty": "1",
|
||
# "lever": "20",
|
||
# "adl": "0.3007",
|
||
# "liqPx": "84918.201844064386317906",
|
||
# "im": "4.7047795",
|
||
# "mm": "0.56457354",
|
||
# "upl": "-0.09783",
|
||
# "uplRatio": "-0.0207",
|
||
# "pnl": "0",
|
||
# "markPx": "94095.59",
|
||
# "mgnRatio": "0.0582",
|
||
# "state": "NORMAL",
|
||
# "cTime": "1740950344401",
|
||
# "uTime": "1740950344401",
|
||
# "mgn": "4.7047795",
|
||
# "actType": "TRADING",
|
||
# "maxWAmt": "0",
|
||
# "tpTrgPx": "",
|
||
# "slTrgPx": ""
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
positions = self.safe_list(response, 'data', [])
|
||
return self.parse_positions(positions, symbols)
|
||
|
||
def parse_position(self, position: dict, market: Market = None):
|
||
#
|
||
# {
|
||
# "symbol": "BTC_USDT_PERP",
|
||
# "posSide": "LONG",
|
||
# "side": "BUY",
|
||
# "mgnMode": "CROSS",
|
||
# "openAvgPx": "94193.42",
|
||
# "qty": "1",
|
||
# "availQty": "1",
|
||
# "lever": "20",
|
||
# "adl": "0.3007",
|
||
# "liqPx": "84918.201844064386317906",
|
||
# "im": "4.7047795",
|
||
# "mm": "0.56457354",
|
||
# "upl": "-0.09783",
|
||
# "uplRatio": "-0.0207",
|
||
# "pnl": "0",
|
||
# "markPx": "94095.59",
|
||
# "mgnRatio": "0.0582",
|
||
# "state": "NORMAL",
|
||
# "cTime": "1740950344401",
|
||
# "uTime": "1740950344401",
|
||
# "mgn": "4.7047795",
|
||
# "actType": "TRADING",
|
||
# "maxWAmt": "0",
|
||
# "tpTrgPx": "",
|
||
# "slTrgPx": ""
|
||
# }
|
||
#
|
||
marketId = self.safe_string(position, 'symbol')
|
||
market = self.safe_market(marketId, market)
|
||
timestamp = self.safe_integer(position, 'cTime')
|
||
marginMode = self.safe_string_lower(position, 'mgnMode')
|
||
leverage = self.safe_string(position, 'lever')
|
||
initialMargin = self.safe_string(position, 'im')
|
||
notional = Precise.string_mul(leverage, initialMargin)
|
||
qty = self.safe_string(position, 'qty')
|
||
avgPrice = self.safe_string(position, 'openAvgPx')
|
||
collateral = Precise.string_mul(qty, avgPrice)
|
||
# todo: some more fields
|
||
return self.safe_position({
|
||
'info': position,
|
||
'id': None,
|
||
'symbol': market['symbol'],
|
||
'notional': notional,
|
||
'marginMode': marginMode,
|
||
'liquidationPrice': self.safe_number(position, 'liqPx'),
|
||
'entryPrice': self.safe_number(position, 'openAvgPx'),
|
||
'unrealizedPnl': self.safe_number(position, 'upl'),
|
||
'percentage': None,
|
||
'contracts': self.safe_number(position, 'qty'),
|
||
'contractSize': None,
|
||
'markPrice': self.safe_number(position, 'markPx'),
|
||
'lastPrice': None,
|
||
'side': self.safe_string_lower(position, 'posSide'),
|
||
'hedged': None,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'lastUpdateTimestamp': None,
|
||
'maintenanceMargin': self.safe_number(position, 'mm'),
|
||
'maintenanceMarginPercentage': None,
|
||
'collateral': collateral,
|
||
'initialMargin': initialMargin,
|
||
'initialMarginPercentage': None,
|
||
'leverage': int(leverage),
|
||
'marginRatio': self.safe_number(position, 'mgnRatio'),
|
||
'stopLossPrice': self.safe_number(position, 'slTrgPx'),
|
||
'takeProfitPrice': self.safe_number(position, 'tpTrgPx'),
|
||
})
|
||
|
||
def modify_margin_helper(self, symbol: str, amount, type, params={}) -> MarginModification:
|
||
self.load_markets()
|
||
market = self.market(symbol)
|
||
amount = self.amount_to_precision(symbol, amount)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
'amt': Precise.string_abs(amount),
|
||
'type': type.upper(), # 'ADD' or 'REDUCE'
|
||
}
|
||
# todo: hedged handling, tricky
|
||
if not ('posMode' in params):
|
||
request['posMode'] = 'BOTH'
|
||
response = self.swapPrivatePostV3TradePositionMargin(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "code": 200,
|
||
# "data": {
|
||
# "amt": "50",
|
||
# "lever": "20",
|
||
# "symbol": "DOT_USDT_PERP",
|
||
# "posSide": "BOTH",
|
||
# "type": "ADD"
|
||
# },
|
||
# "msg": "Success"
|
||
# }
|
||
#
|
||
if type == 'reduce':
|
||
amount = Precise.string_abs(amount)
|
||
data = self.safe_dict(response, 'data')
|
||
return self.parse_margin_modification(data, market)
|
||
|
||
def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
|
||
marketId = self.safe_string(data, 'symbol')
|
||
market = self.safe_market(marketId, market)
|
||
rawType = self.safe_string(data, 'type')
|
||
type = 'add' if (rawType == 'ADD') else 'reduce'
|
||
return {
|
||
'info': data,
|
||
'symbol': market['symbol'],
|
||
'type': type,
|
||
'marginMode': None,
|
||
'amount': self.safe_number(data, 'amt'),
|
||
'total': None,
|
||
'code': None,
|
||
'status': 'ok',
|
||
'timestamp': None,
|
||
'datetime': None,
|
||
}
|
||
|
||
def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
|
||
"""
|
||
remove margin from a position
|
||
: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, 'reduce', params)
|
||
|
||
def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
|
||
"""
|
||
add margin
|
||
: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, 'add', params)
|
||
|
||
def nonce(self):
|
||
return self.milliseconds()
|
||
|
||
def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
|
||
url = self.urls['api']['spot']
|
||
if self.in_array(api, ['swapPublic', 'swapPrivate']):
|
||
url = self.urls['api']['swap']
|
||
if 'symbol' in params:
|
||
params['symbol'] = self.encode_uri_component(params['symbol']) # handle symbols like 索拉拉/USDT'
|
||
query = self.omit(params, self.extract_params(path))
|
||
implodedPath = self.implode_params(path, params)
|
||
if api == 'public' or api == 'swapPublic':
|
||
url += '/' + implodedPath
|
||
if query:
|
||
url += '?' + self.urlencode(query)
|
||
else:
|
||
self.check_required_credentials()
|
||
timestamp = str(self.nonce())
|
||
auth = method + "\n" # eslint-disable-line quotes
|
||
url += '/' + implodedPath
|
||
auth += '/' + implodedPath
|
||
if (method == 'POST') or (method == 'PUT') or (method == 'DELETE'):
|
||
auth += "\n" # eslint-disable-line quotes
|
||
if query:
|
||
body = self.json(query)
|
||
auth += 'requestBody=' + body + '&'
|
||
auth += 'signTimestamp=' + timestamp
|
||
else:
|
||
sortedQuery = self.extend({'signTimestamp': timestamp}, query)
|
||
sortedQuery = self.keysort(sortedQuery)
|
||
auth += "\n" + self.urlencode(sortedQuery) # eslint-disable-line quotes
|
||
if query:
|
||
url += '?' + self.urlencode(query)
|
||
signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256, 'base64')
|
||
headers = {
|
||
'Content-Type': 'application/json',
|
||
'key': self.apiKey,
|
||
'signTimestamp': timestamp,
|
||
'signature': signature,
|
||
}
|
||
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
|
||
#
|
||
# {
|
||
# "code" : 21709,
|
||
# "message" : "Low available balance"
|
||
# }
|
||
#
|
||
responseCode = self.safe_string(response, 'code')
|
||
if (responseCode is not None) and (responseCode != '200'):
|
||
codeInner = response['code']
|
||
message = self.safe_string(response, 'message')
|
||
feedback = self.id + ' ' + body
|
||
self.throw_exactly_matched_exception(self.exceptions['exact'], codeInner, feedback)
|
||
self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
|
||
raise ExchangeError(feedback) # unknown message
|
||
return None
|