# -*- 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` # 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` 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 ` 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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 ` """ 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