1864 lines
78 KiB
Python
1864 lines
78 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
|
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
|
|
|
from ccxt.async_support.base.exchange import Exchange
|
|
from ccxt.abstract.probit import ImplicitAPI
|
|
import math
|
|
from ccxt.base.types import Any, Balances, Currencies, Currency, DepositAddress, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, Transaction
|
|
from typing import List
|
|
from ccxt.base.errors import ExchangeError
|
|
from ccxt.base.errors import AuthenticationError
|
|
from ccxt.base.errors import ArgumentsRequired
|
|
from ccxt.base.errors import BadRequest
|
|
from ccxt.base.errors import BadSymbol
|
|
from ccxt.base.errors import MarketClosed
|
|
from ccxt.base.errors import InsufficientFunds
|
|
from ccxt.base.errors import InvalidAddress
|
|
from ccxt.base.errors import InvalidOrder
|
|
from ccxt.base.errors import DDoSProtection
|
|
from ccxt.base.errors import RateLimitExceeded
|
|
from ccxt.base.errors import ExchangeNotAvailable
|
|
from ccxt.base.errors import BadResponse
|
|
from ccxt.base.decimal_to_precision import TRUNCATE
|
|
from ccxt.base.decimal_to_precision import TICK_SIZE
|
|
from ccxt.base.precise import Precise
|
|
|
|
|
|
class probit(Exchange, ImplicitAPI):
|
|
|
|
def describe(self) -> Any:
|
|
return self.deep_extend(super(probit, self).describe(), {
|
|
'id': 'probit',
|
|
'name': 'ProBit',
|
|
'countries': ['SC', 'KR'], # Seychelles, South Korea
|
|
'rateLimit': 50, # ms
|
|
'pro': True,
|
|
'has': {
|
|
'CORS': True,
|
|
'spot': True,
|
|
'margin': False,
|
|
'swap': False,
|
|
'future': False,
|
|
'option': False,
|
|
'addMargin': False,
|
|
'borrowCrossMargin': False,
|
|
'borrowIsolatedMargin': False,
|
|
'borrowMargin': False,
|
|
'cancelOrder': True,
|
|
'closeAllPositions': False,
|
|
'closePosition': False,
|
|
'createMarketBuyOrderWithCost': True,
|
|
'createMarketOrder': True,
|
|
'createMarketOrderWithCost': False,
|
|
'createMarketSellOrderWithCost': False,
|
|
'createOrder': True,
|
|
'createReduceOnlyOrder': False,
|
|
'createStopLimitOrder': False,
|
|
'createStopMarketOrder': False,
|
|
'createStopOrder': False,
|
|
'fetchAllGreeks': False,
|
|
'fetchBalance': True,
|
|
'fetchBorrowInterest': False,
|
|
'fetchBorrowRate': False,
|
|
'fetchBorrowRateHistories': False,
|
|
'fetchBorrowRateHistory': False,
|
|
'fetchBorrowRates': False,
|
|
'fetchBorrowRatesPerSymbol': False,
|
|
'fetchClosedOrders': True,
|
|
'fetchCrossBorrowRate': False,
|
|
'fetchCrossBorrowRates': False,
|
|
'fetchCurrencies': True,
|
|
'fetchDepositAddress': True,
|
|
'fetchDepositAddresses': True,
|
|
'fetchDepositAddressesByNetwork': False,
|
|
'fetchDeposits': True,
|
|
'fetchDepositsWithdrawals': True,
|
|
'fetchFundingHistory': False,
|
|
'fetchFundingInterval': False,
|
|
'fetchFundingIntervals': False,
|
|
'fetchFundingRate': False,
|
|
'fetchFundingRateHistory': False,
|
|
'fetchFundingRates': False,
|
|
'fetchGreeks': False,
|
|
'fetchIndexOHLCV': False,
|
|
'fetchIsolatedBorrowRate': False,
|
|
'fetchIsolatedBorrowRates': False,
|
|
'fetchIsolatedPositions': False,
|
|
'fetchLeverage': False,
|
|
'fetchLeverages': False,
|
|
'fetchLeverageTiers': False,
|
|
'fetchLiquidations': False,
|
|
'fetchLongShortRatio': False,
|
|
'fetchLongShortRatioHistory': False,
|
|
'fetchMarginAdjustmentHistory': False,
|
|
'fetchMarginMode': False,
|
|
'fetchMarginModes': False,
|
|
'fetchMarketLeverageTiers': False,
|
|
'fetchMarkets': True,
|
|
'fetchMarkOHLCV': False,
|
|
'fetchMarkPrice': False,
|
|
'fetchMarkPrices': False,
|
|
'fetchMyLiquidations': False,
|
|
'fetchMySettlementHistory': False,
|
|
'fetchMyTrades': True,
|
|
'fetchOHLCV': True,
|
|
'fetchOpenInterest': False,
|
|
'fetchOpenInterestHistory': False,
|
|
'fetchOpenInterests': False,
|
|
'fetchOpenOrders': True,
|
|
'fetchOption': False,
|
|
'fetchOptionChain': False,
|
|
'fetchOrder': True,
|
|
'fetchOrderBook': True,
|
|
'fetchPosition': False,
|
|
'fetchPositionHistory': False,
|
|
'fetchPositionMode': False,
|
|
'fetchPositions': False,
|
|
'fetchPositionsForSymbol': False,
|
|
'fetchPositionsHistory': False,
|
|
'fetchPositionsRisk': False,
|
|
'fetchPremiumIndexOHLCV': False,
|
|
'fetchSettlementHistory': False,
|
|
'fetchTicker': True,
|
|
'fetchTickers': True,
|
|
'fetchTime': True,
|
|
'fetchTrades': True,
|
|
'fetchTradingFee': False,
|
|
'fetchTradingFees': False,
|
|
'fetchTransactions': 'emulated',
|
|
'fetchTransfer': False,
|
|
'fetchTransfers': False,
|
|
'fetchUnderlyingAssets': False,
|
|
'fetchVolatilityHistory': False,
|
|
'fetchWithdrawal': False,
|
|
'fetchWithdrawals': True,
|
|
'reduceMargin': False,
|
|
'repayCrossMargin': False,
|
|
'repayIsolatedMargin': False,
|
|
'sandbox': False,
|
|
'setLeverage': False,
|
|
'setMargin': False,
|
|
'setMarginMode': False,
|
|
'setPositionMode': False,
|
|
'signIn': True,
|
|
'transfer': False,
|
|
'withdraw': True,
|
|
},
|
|
'timeframes': {
|
|
'1m': '1m',
|
|
'3m': '3m',
|
|
'5m': '5m',
|
|
'10m': '10m',
|
|
'15m': '15m',
|
|
'30m': '30m',
|
|
'1h': '1h',
|
|
'4h': '4h',
|
|
'6h': '6h',
|
|
'12h': '12h',
|
|
'1d': '1D',
|
|
'1w': '1W',
|
|
'1M': '1M',
|
|
},
|
|
'version': 'v1',
|
|
'urls': {
|
|
'logo': 'https://user-images.githubusercontent.com/51840849/79268032-c4379480-7ea2-11ea-80b3-dd96bb29fd0d.jpg',
|
|
'api': {
|
|
'accounts': 'https://accounts.probit.com',
|
|
'public': 'https://api.probit.com/api/exchange',
|
|
'private': 'https://api.probit.com/api/exchange',
|
|
},
|
|
'www': 'https://www.probit.com',
|
|
'doc': [
|
|
'https://docs-en.probit.com',
|
|
'https://docs-ko.probit.com',
|
|
],
|
|
'fees': 'https://support.probit.com/hc/en-us/articles/360020968611-Trading-Fees',
|
|
'referral': 'https://www.probit.com/r/34608773',
|
|
},
|
|
'api': {
|
|
'public': {
|
|
'get': {
|
|
'market': 1,
|
|
'currency': 1,
|
|
'currency_with_platform': 1,
|
|
'time': 1,
|
|
'ticker': 1,
|
|
'order_book': 1,
|
|
'trade': 1,
|
|
'candle': 1,
|
|
},
|
|
},
|
|
'private': {
|
|
'post': {
|
|
'new_order': 2,
|
|
'cancel_order': 1,
|
|
'withdrawal': 2,
|
|
},
|
|
'get': {
|
|
'balance': 1,
|
|
'order': 1,
|
|
'open_order': 1,
|
|
'order_history': 1,
|
|
'trade_history': 1,
|
|
'deposit_address': 1,
|
|
'transfer/payment': 1,
|
|
},
|
|
},
|
|
'accounts': {
|
|
'post': {
|
|
'token': 1,
|
|
},
|
|
},
|
|
},
|
|
'fees': {
|
|
'trading': {
|
|
'tierBased': False,
|
|
'percentage': True,
|
|
'maker': self.parse_number('0.002'),
|
|
'taker': self.parse_number('0.002'),
|
|
},
|
|
},
|
|
'features': {
|
|
'spot': {
|
|
'sandbox': False,
|
|
'createOrder': {
|
|
'marginMode': False,
|
|
'triggerPrice': False,
|
|
'triggerDirection': False,
|
|
'triggerPriceType': None,
|
|
'stopLossPrice': False,
|
|
'takeProfitPrice': False,
|
|
'attachedStopLossTakeProfit': None,
|
|
# todo
|
|
'timeInForce': {
|
|
'IOC': True,
|
|
'FOK': True,
|
|
'PO': False,
|
|
'GTD': False,
|
|
},
|
|
'hedged': False,
|
|
'trailing': False,
|
|
'leverage': False,
|
|
'marketBuyByCost': True,
|
|
'marketBuyRequiresPrice': False,
|
|
'selfTradePrevention': False,
|
|
'iceberg': False,
|
|
},
|
|
'createOrders': None,
|
|
'fetchMyTrades': {
|
|
'marginMode': False,
|
|
'limit': 1000,
|
|
'daysBack': 100000, # todo
|
|
'untilDays': 100000, # todo
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOrder': {
|
|
'marginMode': False,
|
|
'trigger': False,
|
|
'trailing': False,
|
|
'symbolRequired': True,
|
|
},
|
|
'fetchOpenOrders': {
|
|
'marginMode': False,
|
|
'limit': None,
|
|
'trigger': False,
|
|
'trailing': False,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOrders': None,
|
|
'fetchClosedOrders': {
|
|
'marginMode': False,
|
|
'limit': 1000,
|
|
'daysBack': 100000, # todo
|
|
'daysBackCanceled': 1, # todo
|
|
'untilDays': 90,
|
|
'trigger': False,
|
|
'trailing': False,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOHLCV': {
|
|
'limit': 4000,
|
|
},
|
|
},
|
|
'swap': {
|
|
'linear': None,
|
|
'inverse': None,
|
|
},
|
|
'future': {
|
|
'linear': None,
|
|
'inverse': None,
|
|
},
|
|
},
|
|
'exceptions': {
|
|
'exact': {
|
|
'UNAUTHORIZED': AuthenticationError,
|
|
'INVALID_ARGUMENT': BadRequest, # Parameters are not a valid format, parameters are empty, or out of range, or a parameter was sent when not required.
|
|
'TRADING_UNAVAILABLE': ExchangeNotAvailable,
|
|
'NOT_ENOUGH_BALANCE': InsufficientFunds,
|
|
'NOT_ALLOWED_COMBINATION': BadRequest,
|
|
'INVALID_ORDER': InvalidOrder, # Requested order does not exist, or it is not your order
|
|
'RATE_LIMIT_EXCEEDED': RateLimitExceeded, # You are sending requests too frequently. Please try it later.
|
|
'MARKET_UNAVAILABLE': ExchangeNotAvailable, # Market is closed today
|
|
'INVALID_MARKET': BadSymbol, # Requested market is not exist
|
|
'MARKET_CLOSED': MarketClosed, # {"errorCode":"MARKET_CLOSED"}
|
|
'MARKET_NOT_FOUND': BadSymbol, # {"errorCode":"MARKET_NOT_FOUND","message":"8e2b8496-0a1e-5beb-b990-a205b902eabe","details":{}}
|
|
'INVALID_CURRENCY': BadRequest, # Requested currency is not exist on ProBit system
|
|
'TOO_MANY_OPEN_ORDERS': DDoSProtection, # Too many open orders
|
|
'DUPLICATE_ADDRESS': InvalidAddress, # Address already exists in withdrawal address list
|
|
'invalid_grant': AuthenticationError, # {"error":"invalid_grant"}
|
|
},
|
|
},
|
|
'requiredCredentials': {
|
|
'apiKey': True,
|
|
'secret': True,
|
|
},
|
|
'precisionMode': TICK_SIZE,
|
|
'options': {
|
|
'createMarketBuyOrderRequiresPrice': True,
|
|
'timeInForce': {
|
|
'limit': 'gtc',
|
|
'market': 'ioc',
|
|
},
|
|
'networks': {
|
|
'BEP20': 'BSC',
|
|
'ERC20': 'ETH',
|
|
'TRC20': 'TRON',
|
|
},
|
|
},
|
|
'commonCurrencies': {
|
|
'BB': 'Baby Bali',
|
|
'CBC': 'CryptoBharatCoin',
|
|
'CTK': 'Cryptyk',
|
|
'CTT': 'Castweet',
|
|
'DKT': 'DAKOTA',
|
|
'EGC': 'EcoG9coin',
|
|
'EPS': 'Epanus', # conflict with EPS Ellipsis https://github.com/ccxt/ccxt/issues/8909
|
|
'FX': 'Fanzy',
|
|
'GM': 'GM Holding',
|
|
'GOGOL': 'GOL',
|
|
'GOL': 'Goldofir',
|
|
'HUSL': 'The Hustle App',
|
|
'LAND': 'Landbox',
|
|
'SST': 'SocialSwap',
|
|
'TCT': 'Top Coin Token',
|
|
'TOR': 'Torex',
|
|
'UNI': 'UNICORN Token',
|
|
'UNISWAP': 'UNI',
|
|
},
|
|
})
|
|
|
|
async def fetch_markets(self, params={}) -> List[Market]:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/market
|
|
|
|
retrieves data on all markets for probit
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: an array of objects representing market data
|
|
"""
|
|
response = await self.publicGetMarket(params)
|
|
#
|
|
# {
|
|
# "data":[
|
|
# {
|
|
# "id":"MONA-USDT",
|
|
# "base_currency_id":"MONA",
|
|
# "quote_currency_id":"USDT",
|
|
# "min_price":"0.001",
|
|
# "max_price":"9999999999999999",
|
|
# "price_increment":"0.001",
|
|
# "min_quantity":"0.0001",
|
|
# "max_quantity":"9999999999999999",
|
|
# "quantity_precision":4,
|
|
# "min_cost":"1",
|
|
# "max_cost":"9999999999999999",
|
|
# "cost_precision":8,
|
|
# "taker_fee_rate":"0.2",
|
|
# "maker_fee_rate":"0.2",
|
|
# "show_in_ui":true,
|
|
# "closed":false
|
|
# },
|
|
# ]
|
|
# }
|
|
#
|
|
markets = self.safe_value(response, 'data', [])
|
|
return self.parse_markets(markets)
|
|
|
|
def parse_market(self, market: dict) -> Market:
|
|
id = self.safe_string(market, 'id')
|
|
baseId = self.safe_string(market, 'base_currency_id')
|
|
quoteId = self.safe_string(market, 'quote_currency_id')
|
|
base = self.safe_currency_code(baseId)
|
|
quote = self.safe_currency_code(quoteId)
|
|
closed = self.safe_bool(market, 'closed', False)
|
|
showInUI = self.safe_bool(market, 'show_in_ui', True)
|
|
active = not closed and showInUI
|
|
takerFeeRate = self.safe_string(market, 'taker_fee_rate')
|
|
taker = Precise.string_div(takerFeeRate, '100')
|
|
makerFeeRate = self.safe_string(market, 'maker_fee_rate')
|
|
maker = Precise.string_div(makerFeeRate, '100')
|
|
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,
|
|
'taker': self.parse_number(taker),
|
|
'maker': self.parse_number(maker),
|
|
'contractSize': None,
|
|
'expiry': None,
|
|
'expiryDatetime': None,
|
|
'strike': None,
|
|
'optionType': None,
|
|
'precision': {
|
|
'amount': self.parse_number(self.parse_precision(self.safe_string(market, 'quantity_precision'))),
|
|
'price': self.safe_number(market, 'price_increment'),
|
|
'cost': self.parse_number(self.parse_precision(self.safe_string(market, 'cost_precision'))),
|
|
},
|
|
'limits': {
|
|
'leverage': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'amount': {
|
|
'min': self.safe_number(market, 'min_quantity'),
|
|
'max': self.safe_number(market, 'max_quantity'),
|
|
},
|
|
'price': {
|
|
'min': self.safe_number(market, 'min_price'),
|
|
'max': self.safe_number(market, 'max_price'),
|
|
},
|
|
'cost': {
|
|
'min': self.safe_number(market, 'min_cost'),
|
|
'max': self.safe_number(market, 'max_cost'),
|
|
},
|
|
},
|
|
'created': None,
|
|
'info': market,
|
|
}
|
|
|
|
async def fetch_currencies(self, params={}) -> Currencies:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/currency
|
|
|
|
fetches all available currencies on an exchange
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: an associative dictionary of currencies
|
|
"""
|
|
response = await self.publicGetCurrencyWithPlatform(params)
|
|
#
|
|
# {
|
|
# "data":[
|
|
# {
|
|
# "id":"USDT",
|
|
# "display_name":{"ko-kr":"테더","en-us":"Tether"},
|
|
# "show_in_ui":true,
|
|
# "platform":[
|
|
# {
|
|
# "id":"ETH",
|
|
# "priority":1,
|
|
# "deposit":true,
|
|
# "withdrawal":true,
|
|
# "currency_id":"USDT",
|
|
# "precision":6,
|
|
# "min_confirmation_count":15,
|
|
# "require_destination_tag":false,
|
|
# "display_name":{"name":{"ko-kr":"ERC-20","en-us":"ERC-20"}},
|
|
# "min_deposit_amount":"0",
|
|
# "min_withdrawal_amount":"1",
|
|
# "withdrawal_fee":[
|
|
# {"amount":"0.01","priority":2,"currency_id":"ETH"},
|
|
# {"amount":"1.5","priority":1,"currency_id":"USDT"},
|
|
# ],
|
|
# "deposit_fee":{},
|
|
# "suspended_reason":"",
|
|
# "deposit_suspended":false,
|
|
# "withdrawal_suspended":false
|
|
# },
|
|
# {
|
|
# "id":"OMNI",
|
|
# "priority":2,
|
|
# "deposit":true,
|
|
# "withdrawal":true,
|
|
# "currency_id":"USDT",
|
|
# "precision":6,
|
|
# "min_confirmation_count":3,
|
|
# "require_destination_tag":false,
|
|
# "display_name":{"name":{"ko-kr":"OMNI","en-us":"OMNI"}},
|
|
# "min_deposit_amount":"0",
|
|
# "min_withdrawal_amount":"5",
|
|
# "withdrawal_fee":[{"amount":"5","priority":1,"currency_id":"USDT"}],
|
|
# "deposit_fee":{},
|
|
# "suspended_reason":"wallet_maintenance",
|
|
# "deposit_suspended":false,
|
|
# "withdrawal_suspended":false
|
|
# }
|
|
# ],
|
|
# "stakeable":false,
|
|
# "unstakeable":false,
|
|
# "auto_stake":false,
|
|
# "auto_stake_amount":"0"
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
currencies = self.safe_list(response, 'data', [])
|
|
result: dict = {}
|
|
for i in range(0, len(currencies)):
|
|
currency = currencies[i]
|
|
id = self.safe_string(currency, 'id')
|
|
code = self.safe_currency_code(id)
|
|
displayName = self.safe_dict(currency, 'display_name')
|
|
name = self.safe_string(displayName, 'en-us')
|
|
platforms = self.safe_list(currency, 'platform', [])
|
|
platformsByPriority = self.sort_by(platforms, 'priority')
|
|
networkList: dict = {}
|
|
for j in range(0, len(platformsByPriority)):
|
|
network = platformsByPriority[j]
|
|
idInner = self.safe_string(network, 'id')
|
|
networkCode = self.network_id_to_code(idInner)
|
|
withdrawFee = self.safe_list(network, 'withdrawal_fee', [])
|
|
networkFee = self.safe_dict(withdrawFee, 0, {})
|
|
for k in range(0, len(withdrawFee)):
|
|
withdrawPlatform = withdrawFee[k]
|
|
feeCurrencyId = self.safe_string(withdrawPlatform, 'currency_id')
|
|
if feeCurrencyId == id:
|
|
networkFee = withdrawPlatform
|
|
break
|
|
networkList[networkCode] = {
|
|
'id': idInner,
|
|
'network': networkCode,
|
|
'active': None,
|
|
'deposit': not self.safe_bool(network, 'deposit_suspended'),
|
|
'withdraw': not self.safe_bool(network, 'withdrawal_suspended'),
|
|
'fee': self.safe_number(networkFee, 'amount'),
|
|
'precision': self.parse_number(self.parse_precision(self.safe_string(network, 'precision'))),
|
|
'limits': {
|
|
'withdraw': {
|
|
'min': self.safe_number(network, 'min_withdrawal_amount'),
|
|
'max': None,
|
|
},
|
|
'deposit': {
|
|
'min': self.safe_number(network, 'min_deposit_amount'),
|
|
'max': None,
|
|
},
|
|
},
|
|
'info': network,
|
|
}
|
|
result[code] = self.safe_currency_structure({
|
|
'id': id,
|
|
'code': code,
|
|
'info': currency,
|
|
'name': name,
|
|
'active': None,
|
|
'deposit': None,
|
|
'withdraw': None,
|
|
'type': 'crypto',
|
|
'fee': None,
|
|
'precision': None,
|
|
'limits': {
|
|
'amount': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'deposit': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'withdraw': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
},
|
|
'networks': networkList,
|
|
})
|
|
return result
|
|
|
|
def parse_balance(self, response) -> Balances:
|
|
result: dict = {
|
|
'info': response,
|
|
'timestamp': None,
|
|
'datetime': None,
|
|
}
|
|
data = self.safe_value(response, 'data', [])
|
|
for i in range(0, len(data)):
|
|
balance = data[i]
|
|
currencyId = self.safe_string(balance, 'currency_id')
|
|
code = self.safe_currency_code(currencyId)
|
|
account = self.account()
|
|
account['total'] = self.safe_string(balance, 'total')
|
|
account['free'] = self.safe_string(balance, 'available')
|
|
result[code] = account
|
|
return self.safe_balance(result)
|
|
|
|
async def fetch_balance(self, params={}) -> Balances:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/balance
|
|
|
|
query for balance and get the amount of funds available for trading or funds locked in orders
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
response = await self.privateGetBalance(params)
|
|
#
|
|
# {
|
|
# "data": [
|
|
# {
|
|
# "currency_id":"XRP",
|
|
# "total":"100",
|
|
# "available":"0",
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
return self.parse_balance(response)
|
|
|
|
async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/order_book
|
|
|
|
fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
|
:param str symbol: unified symbol of the market to fetch the order book for
|
|
:param int [limit]: the maximum amount of order book entries to return
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
|
"""
|
|
await self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'market_id': market['id'],
|
|
}
|
|
response = await self.publicGetOrderBook(self.extend(request, params))
|
|
#
|
|
# {
|
|
# data: [
|
|
# {side: 'buy', price: '0.000031', quantity: '10'},
|
|
# {side: 'buy', price: '0.00356007', quantity: '4.92156877'},
|
|
# {side: 'sell', price: '0.1857', quantity: '0.17'},
|
|
# ]
|
|
# }
|
|
#
|
|
data = self.safe_value(response, 'data', [])
|
|
dataBySide = self.group_by(data, 'side')
|
|
return self.parse_order_book(dataBySide, market['symbol'], None, 'buy', 'sell', 'price', 'quantity')
|
|
|
|
async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/ticker
|
|
|
|
fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
|
:param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
request: dict = {}
|
|
if symbols is not None:
|
|
marketIds = self.market_ids(symbols)
|
|
request['market_ids'] = ','.join(marketIds)
|
|
response = await self.publicGetTicker(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "data":[
|
|
# {
|
|
# "last":"0.022902",
|
|
# "low":"0.021693",
|
|
# "high":"0.024093",
|
|
# "change":"-0.000047",
|
|
# "base_volume":"15681.986",
|
|
# "quote_volume":"360.514403624",
|
|
# "market_id":"ETH-BTC",
|
|
# "time":"2020-04-12T18:43:38.000Z"
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
data = self.safe_list(response, 'data', [])
|
|
return self.parse_tickers(data, symbols)
|
|
|
|
async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/ticker
|
|
|
|
fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
|
:param str symbol: unified symbol of the market to fetch the ticker for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'market_ids': market['id'],
|
|
}
|
|
response = await self.publicGetTicker(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "data":[
|
|
# {
|
|
# "last":"0.022902",
|
|
# "low":"0.021693",
|
|
# "high":"0.024093",
|
|
# "change":"-0.000047",
|
|
# "base_volume":"15681.986",
|
|
# "quote_volume":"360.514403624",
|
|
# "market_id":"ETH-BTC",
|
|
# "time":"2020-04-12T18:43:38.000Z"
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
data = self.safe_value(response, 'data', [])
|
|
ticker = self.safe_value(data, 0)
|
|
if ticker is None:
|
|
raise BadResponse(self.id + ' fetchTicker() returned an empty response')
|
|
return self.parse_ticker(ticker, market)
|
|
|
|
def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
|
#
|
|
# {
|
|
# "last":"0.022902",
|
|
# "low":"0.021693",
|
|
# "high":"0.024093",
|
|
# "change":"-0.000047",
|
|
# "base_volume":"15681.986",
|
|
# "quote_volume":"360.514403624",
|
|
# "market_id":"ETH-BTC",
|
|
# "time":"2020-04-12T18:43:38.000Z"
|
|
# }
|
|
#
|
|
timestamp = self.parse8601(self.safe_string(ticker, 'time'))
|
|
marketId = self.safe_string(ticker, 'market_id')
|
|
symbol = self.safe_symbol(marketId, market, '-')
|
|
close = self.safe_string(ticker, 'last')
|
|
change = self.safe_string(ticker, 'change')
|
|
baseVolume = self.safe_string(ticker, 'base_volume')
|
|
quoteVolume = self.safe_string(ticker, 'quote_volume')
|
|
return self.safe_ticker({
|
|
'symbol': symbol,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'high': self.safe_string(ticker, 'high'),
|
|
'low': self.safe_string(ticker, 'low'),
|
|
'bid': None,
|
|
'bidVolume': None,
|
|
'ask': None,
|
|
'askVolume': None,
|
|
'vwap': None,
|
|
'open': None,
|
|
'close': close,
|
|
'last': close,
|
|
'previousClose': None, # previous day close
|
|
'change': change,
|
|
'percentage': None,
|
|
'average': None,
|
|
'baseVolume': baseVolume,
|
|
'quoteVolume': quoteVolume,
|
|
'info': ticker,
|
|
}, market)
|
|
|
|
async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/trade
|
|
|
|
fetch all trades made by the user
|
|
: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
|
|
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
market: Market = None
|
|
now = self.milliseconds()
|
|
request: dict = {
|
|
'limit': 100,
|
|
'start_time': self.iso8601(now - 31536000000), # -365 days
|
|
'end_time': self.iso8601(now),
|
|
}
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
request['market_id'] = market['id']
|
|
if since is not None:
|
|
request['start_time'] = self.iso8601(since)
|
|
request['end_time'] = self.iso8601(min(now, since + 31536000000))
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
response = await self.privateGetTradeHistory(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "data": [
|
|
# {
|
|
# "id":"BTC-USDT:183566",
|
|
# "order_id":"17209376",
|
|
# "side":"sell",
|
|
# "fee_amount":"0.657396569175",
|
|
# "fee_currency_id":"USDT",
|
|
# "status":"settled",
|
|
# "price":"6573.96569175",
|
|
# "quantity":"0.1",
|
|
# "cost":"657.396569175",
|
|
# "time":"2018-08-10T06:06:46.000Z",
|
|
# "market_id":"BTC-USDT"
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
data = self.safe_list(response, 'data', [])
|
|
return self.parse_trades(data, market, since, limit)
|
|
|
|
async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/trade-1
|
|
|
|
get the list of most recent trades for a particular symbol
|
|
:param str symbol: unified symbol of the market to fetch trades for
|
|
:param int [since]: timestamp in ms of the earliest trade to fetch
|
|
:param int [limit]: the maximum amount of trades to fetch
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
|
"""
|
|
await self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'market_id': market['id'],
|
|
'start_time': '1970-01-01T00:00:00.000Z',
|
|
'end_time': self.iso8601(self.milliseconds()),
|
|
}
|
|
if since is not None:
|
|
request['start_time'] = self.iso8601(since)
|
|
if limit is not None:
|
|
request['limit'] = min(limit, 1000)
|
|
else:
|
|
request['limit'] = 1000 # required to set any value
|
|
response = await self.publicGetTrade(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "data":[
|
|
# {
|
|
# "id":"ETH-BTC:3331886",
|
|
# "price":"0.022982",
|
|
# "quantity":"12.337",
|
|
# "time":"2020-04-12T20:55:42.371Z",
|
|
# "side":"sell",
|
|
# "tick_direction":"down"
|
|
# },
|
|
# {
|
|
# "id":"ETH-BTC:3331885",
|
|
# "price":"0.022982",
|
|
# "quantity":"6.472",
|
|
# "time":"2020-04-12T20:55:39.652Z",
|
|
# "side":"sell",
|
|
# "tick_direction":"down"
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
data = self.safe_list(response, 'data', [])
|
|
return self.parse_trades(data, market, since, limit)
|
|
|
|
def parse_trade(self, trade: dict, market: Market = None) -> Trade:
|
|
#
|
|
# fetchTrades(public)
|
|
#
|
|
# {
|
|
# "id":"ETH-BTC:3331886",
|
|
# "price":"0.022981",
|
|
# "quantity":"12.337",
|
|
# "time":"2020-04-12T20:55:42.371Z",
|
|
# "side":"sell",
|
|
# "tick_direction":"down"
|
|
# }
|
|
#
|
|
# fetchMyTrades(private)
|
|
#
|
|
# {
|
|
# "id":"BTC-USDT:183566",
|
|
# "order_id":"17209376",
|
|
# "side":"sell",
|
|
# "fee_amount":"0.657396569175",
|
|
# "fee_currency_id":"USDT",
|
|
# "status":"settled",
|
|
# "price":"6573.96569175",
|
|
# "quantity":"0.1",
|
|
# "cost":"657.396569175",
|
|
# "time":"2018-08-10T06:06:46.000Z",
|
|
# "market_id":"BTC-USDT"
|
|
# }
|
|
#
|
|
timestamp = self.parse8601(self.safe_string(trade, 'time'))
|
|
id = self.safe_string(trade, 'id')
|
|
marketId: Str = None
|
|
if id is not None:
|
|
parts = id.split(':')
|
|
marketId = self.safe_string(parts, 0)
|
|
marketId = self.safe_string(trade, 'market_id', marketId)
|
|
symbol = self.safe_symbol(marketId, market, '-')
|
|
side = self.safe_string(trade, 'side')
|
|
priceString = self.safe_string(trade, 'price')
|
|
amountString = self.safe_string(trade, 'quantity')
|
|
orderId = self.safe_string(trade, 'order_id')
|
|
feeCostString = self.safe_string(trade, 'fee_amount')
|
|
fee = None
|
|
if feeCostString is not None:
|
|
feeCurrencyId = self.safe_string(trade, 'fee_currency_id')
|
|
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': None,
|
|
'side': side,
|
|
'takerOrMaker': None,
|
|
'price': priceString,
|
|
'amount': amountString,
|
|
'cost': None,
|
|
'fee': fee,
|
|
}, market)
|
|
|
|
async def fetch_time(self, params={}) -> Int:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/time
|
|
|
|
fetches the current integer timestamp in milliseconds from the exchange server
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns int: the current integer timestamp in milliseconds from the exchange server
|
|
"""
|
|
response = await self.publicGetTime(params)
|
|
#
|
|
# {"data":"2020-04-12T18:54:25.390Z"}
|
|
#
|
|
timestamp = self.parse8601(self.safe_string(response, 'data'))
|
|
return timestamp
|
|
|
|
def normalize_ohlcv_timestamp(self, timestamp, timeframe, after=False):
|
|
duration = self.parse_timeframe(timeframe)
|
|
if timeframe == '1M':
|
|
iso8601 = self.iso8601(timestamp)
|
|
parts = iso8601.split('-')
|
|
year = self.safe_string(parts, 0)
|
|
month = self.safe_integer(parts, 1)
|
|
monthString: Str = None
|
|
if after:
|
|
monthString = self.sum(month, str(1))
|
|
if month < 10:
|
|
monthString = '0' + str(month)
|
|
return year + '-' + monthString + '-01T00:00:00.000Z'
|
|
elif timeframe == '1w':
|
|
timestamp = self.parse_to_int(timestamp / 1000)
|
|
firstSunday = 259200 # 1970-01-04T00:00:00.000Z
|
|
difference = timestamp - firstSunday
|
|
numWeeks = int(math.floor(difference / duration))
|
|
previousSunday = self.sum(firstSunday, numWeeks * duration)
|
|
if after:
|
|
previousSunday = self.sum(previousSunday, duration)
|
|
return self.iso8601(previousSunday * 1000)
|
|
else:
|
|
timestamp = self.parse_to_int(timestamp / 1000)
|
|
timestamp = duration * self.parse_to_int(timestamp / duration)
|
|
if after:
|
|
timestamp = self.sum(timestamp, duration)
|
|
return self.iso8601(timestamp * 1000)
|
|
|
|
async def fetch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/candle
|
|
|
|
fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
: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 str [params.until]: timestamp in ms of the earliest candle to fetch
|
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
|
"""
|
|
await self.load_markets()
|
|
market = self.market(symbol)
|
|
interval = self.safe_string(self.timeframes, timeframe, timeframe)
|
|
limit = 100 if (limit is None) else limit
|
|
requestLimit = self.sum(limit, 1)
|
|
requestLimit = min(1000, requestLimit) # max 1000
|
|
request: dict = {
|
|
'market_ids': market['id'],
|
|
'interval': interval,
|
|
'sort': 'asc', # 'asc' will always include the start_time, 'desc' will always include end_time
|
|
'limit': requestLimit, # max 1000
|
|
}
|
|
now = self.milliseconds()
|
|
until = self.safe_integer(params, 'until')
|
|
durationMilliseconds = self.parse_timeframe(timeframe) * 1000
|
|
startTime = since
|
|
endTime = until - durationMilliseconds if (until is not None) else now
|
|
if since is None:
|
|
if limit is None:
|
|
limit = requestLimit
|
|
startLimit = limit - 1
|
|
startTime = endTime - startLimit * durationMilliseconds
|
|
else:
|
|
if limit is not None:
|
|
endByLimit = self.sum(since, limit * durationMilliseconds)
|
|
endTime = min(endTime, endByLimit)
|
|
startTimeNormalized = self.normalize_ohlcv_timestamp(startTime, timeframe)
|
|
endTimeNormalized = self.normalize_ohlcv_timestamp(endTime, timeframe, True)
|
|
request['start_time'] = startTimeNormalized
|
|
request['end_time'] = endTimeNormalized
|
|
response = await self.publicGetCandle(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "data":[
|
|
# {
|
|
# "market_id":"ETH-BTC",
|
|
# "open":"0.02811",
|
|
# "close":"0.02811",
|
|
# "low":"0.02811",
|
|
# "high":"0.02811",
|
|
# "base_volume":"0.0005",
|
|
# "quote_volume":"0.000014055",
|
|
# "start_time":"2018-11-30T18:19:00.000Z",
|
|
# "end_time":"2018-11-30T18:20:00.000Z"
|
|
# },
|
|
# ]
|
|
# }
|
|
#
|
|
data = self.safe_list(response, 'data', [])
|
|
return self.parse_ohlcvs(data, market, timeframe, since, limit)
|
|
|
|
def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
|
|
#
|
|
# {
|
|
# "market_id":"ETH-BTC",
|
|
# "open":"0.02811",
|
|
# "close":"0.02811",
|
|
# "low":"0.02811",
|
|
# "high":"0.02811",
|
|
# "base_volume":"0.0005",
|
|
# "quote_volume":"0.000014055",
|
|
# "start_time":"2018-11-30T18:19:00.000Z",
|
|
# "end_time":"2018-11-30T18:20:00.000Z"
|
|
# }
|
|
#
|
|
return [
|
|
self.parse8601(self.safe_string(ohlcv, 'start_time')),
|
|
self.safe_number(ohlcv, 'open'),
|
|
self.safe_number(ohlcv, 'high'),
|
|
self.safe_number(ohlcv, 'low'),
|
|
self.safe_number(ohlcv, 'close'),
|
|
self.safe_number(ohlcv, 'base_volume'),
|
|
]
|
|
|
|
async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/open_order-1
|
|
|
|
fetch all unfilled currently open 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
|
|
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
since = self.parse8601(since)
|
|
request: dict = {}
|
|
market: Market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
request['market_id'] = market['id']
|
|
response = await self.privateGetOpenOrder(self.extend(request, params))
|
|
data = self.safe_list(response, 'data')
|
|
return self.parse_orders(data, market, since, limit)
|
|
|
|
async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/order
|
|
|
|
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
|
|
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
request: dict = {
|
|
'start_time': self.iso8601(0),
|
|
'end_time': self.iso8601(self.milliseconds()),
|
|
'limit': 100,
|
|
}
|
|
market: Market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
request['market_id'] = market['id']
|
|
if since:
|
|
request['start_time'] = self.iso8601(since)
|
|
if limit:
|
|
request['limit'] = limit
|
|
response = await self.privateGetOrderHistory(self.extend(request, params))
|
|
data = self.safe_list(response, 'data')
|
|
return self.parse_orders(data, market, since, limit)
|
|
|
|
async def fetch_order(self, id: str, symbol: Str = None, params={}):
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/order-3
|
|
|
|
fetches information on an order made by the user
|
|
:param str id: the order id
|
|
:param str symbol: unified symbol of the market the order was made in
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
|
|
await self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'market_id': market['id'],
|
|
}
|
|
clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_order_id')
|
|
if clientOrderId is not None:
|
|
request['client_order_id'] = clientOrderId
|
|
else:
|
|
request['order_id'] = id
|
|
query = self.omit(params, ['clientOrderId', 'client_order_id'])
|
|
response = await self.privateGetOrder(self.extend(request, query))
|
|
data = self.safe_value(response, 'data', [])
|
|
order = self.safe_dict(data, 0)
|
|
return self.parse_order(order, market)
|
|
|
|
def parse_order_status(self, status: Str):
|
|
statuses: dict = {
|
|
'open': 'open',
|
|
'cancelled': 'canceled',
|
|
'filled': 'closed',
|
|
}
|
|
return self.safe_string(statuses, status, status)
|
|
|
|
def parse_order(self, order: dict, market: Market = None) -> Order:
|
|
#
|
|
# {
|
|
# id,
|
|
# user_id,
|
|
# market_id,
|
|
# "type": "orderType",
|
|
# "side": "side",
|
|
# quantity,
|
|
# limit_price,
|
|
# "time_in_force": "timeInForce",
|
|
# filled_cost,
|
|
# filled_quantity,
|
|
# open_quantity,
|
|
# cancelled_quantity,
|
|
# "status": "orderStatus",
|
|
# "time": "date",
|
|
# client_order_id,
|
|
# }
|
|
#
|
|
status = self.parse_order_status(self.safe_string(order, 'status'))
|
|
id = self.safe_string(order, 'id')
|
|
type = self.safe_string(order, 'type')
|
|
side = self.safe_string(order, 'side')
|
|
marketId = self.safe_string(order, 'market_id')
|
|
symbol = self.safe_symbol(marketId, market, '-')
|
|
timestamp = self.parse8601(self.safe_string(order, 'time'))
|
|
price = self.safe_string(order, 'limit_price')
|
|
filled = self.safe_string(order, 'filled_quantity')
|
|
remaining = self.safe_string(order, 'open_quantity')
|
|
canceledAmount = self.safe_string(order, 'cancelled_quantity')
|
|
if canceledAmount is not None:
|
|
remaining = Precise.string_add(remaining, canceledAmount)
|
|
amount = self.safe_string(order, 'quantity', Precise.string_add(filled, remaining))
|
|
cost = self.safe_string_2(order, 'filled_cost', 'cost')
|
|
if type == 'market':
|
|
price = None
|
|
clientOrderId = self.safe_string(order, 'client_order_id')
|
|
timeInForce = self.safe_string_upper(order, 'time_in_force')
|
|
return self.safe_order({
|
|
'id': id,
|
|
'info': order,
|
|
'clientOrderId': clientOrderId,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'lastTradeTimestamp': None,
|
|
'symbol': symbol,
|
|
'type': type,
|
|
'timeInForce': timeInForce,
|
|
'side': side,
|
|
'status': status,
|
|
'price': price,
|
|
'triggerPrice': None,
|
|
'amount': amount,
|
|
'filled': filled,
|
|
'remaining': remaining,
|
|
'average': None,
|
|
'cost': cost,
|
|
'fee': None,
|
|
'trades': None,
|
|
}, market)
|
|
|
|
def cost_to_precision(self, symbol, cost):
|
|
return self.decimal_to_precision(cost, TRUNCATE, self.markets[symbol]['precision']['cost'], self.precisionMode)
|
|
|
|
async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
"""
|
|
create a trade order
|
|
|
|
https://docs-en.probit.com/reference/order-1
|
|
|
|
: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 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.cost]: the quote quantity that can be used alternative for the amount for market buy orders
|
|
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
market = self.market(symbol)
|
|
options = self.safe_value(self.options, 'timeInForce')
|
|
defaultTimeInForce = self.safe_value(options, type)
|
|
timeInForce = self.safe_string_2(params, 'timeInForce', 'time_in_force', defaultTimeInForce)
|
|
request: dict = {
|
|
'market_id': market['id'],
|
|
'type': type,
|
|
'side': side,
|
|
'time_in_force': timeInForce,
|
|
}
|
|
clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_order_id')
|
|
if clientOrderId is not None:
|
|
request['client_order_id'] = clientOrderId
|
|
quoteAmount = None
|
|
if type == 'limit':
|
|
request['limit_price'] = self.price_to_precision(symbol, price)
|
|
request['quantity'] = self.amount_to_precision(symbol, amount)
|
|
elif type == 'market':
|
|
# for market buy it requires the amount of quote currency to spend
|
|
if side == 'buy':
|
|
createMarketBuyOrderRequiresPrice = True
|
|
createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
|
|
cost = self.safe_string(params, 'cost')
|
|
params = self.omit(params, 'cost')
|
|
if cost is not None:
|
|
quoteAmount = self.cost_to_precision(symbol, cost)
|
|
elif createMarketBuyOrderRequiresPrice:
|
|
if price is None:
|
|
raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend in the amount argument')
|
|
else:
|
|
amountString = self.number_to_string(amount)
|
|
priceString = self.number_to_string(price)
|
|
costRequest = Precise.string_mul(amountString, priceString)
|
|
quoteAmount = self.cost_to_precision(symbol, costRequest)
|
|
else:
|
|
quoteAmount = self.cost_to_precision(symbol, amount)
|
|
request['cost'] = quoteAmount
|
|
else:
|
|
request['quantity'] = self.amount_to_precision(symbol, amount)
|
|
query = self.omit(params, ['timeInForce', 'time_in_force', 'clientOrderId', 'client_order_id'])
|
|
response = await self.privatePostNewOrder(self.extend(request, query))
|
|
#
|
|
# {
|
|
# "data": {
|
|
# id,
|
|
# user_id,
|
|
# market_id,
|
|
# "type": "orderType",
|
|
# "side": "side",
|
|
# quantity,
|
|
# limit_price,
|
|
# "time_in_force": "timeInForce",
|
|
# filled_cost,
|
|
# filled_quantity,
|
|
# open_quantity,
|
|
# cancelled_quantity,
|
|
# "status": "orderStatus",
|
|
# "time": "date",
|
|
# client_order_id,
|
|
# }
|
|
# }
|
|
#
|
|
data = self.safe_value(response, 'data')
|
|
order = self.parse_order(data, market)
|
|
# a workaround for incorrect huge amounts
|
|
# returned by the exchange on market buys
|
|
if (type == 'market') and (side == 'buy'):
|
|
order['amount'] = None
|
|
order['cost'] = self.parse_number(quoteAmount)
|
|
order['remaining'] = None
|
|
return order
|
|
|
|
async def cancel_order(self, id: str, symbol: Str = None, params={}):
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/order-2
|
|
|
|
cancels an open order
|
|
:param str id: order id
|
|
:param str symbol: unified symbol of the market the order was made in
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
"""
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
|
|
await self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'market_id': market['id'],
|
|
'order_id': id,
|
|
}
|
|
response = await self.privatePostCancelOrder(self.extend(request, params))
|
|
data = self.safe_dict(response, 'data')
|
|
return self.parse_order(data)
|
|
|
|
def parse_deposit_address(self, depositAddress, currency: Currency = None) -> DepositAddress:
|
|
address = self.safe_string(depositAddress, 'address')
|
|
tag = self.safe_string(depositAddress, 'destination_tag')
|
|
currencyId = self.safe_string(depositAddress, 'currency_id')
|
|
currency = self.safe_currency(currencyId, currency)
|
|
code = currency['code']
|
|
network = self.safe_string(depositAddress, 'platform_id')
|
|
self.check_address(address)
|
|
return {
|
|
'info': depositAddress,
|
|
'currency': code,
|
|
'network': network,
|
|
'address': address,
|
|
'tag': tag,
|
|
}
|
|
|
|
async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/deposit_address
|
|
|
|
fetch the deposit address for a currency associated with self account
|
|
:param str code: unified currency code
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
currency = self.currency(code)
|
|
request: dict = {
|
|
'currency_id': currency['id'],
|
|
# 'platform_id': 'TRON',(undocumented)
|
|
}
|
|
networks = self.safe_value(self.options, 'networks', {})
|
|
network = self.safe_string_upper(params, 'network') # self line allows the user to specify either ERC20 or ETH
|
|
network = self.safe_string(networks, network, network) # handle ERC20>ETH alias
|
|
if network is not None:
|
|
request['platform_id'] = network
|
|
params = self.omit(params, 'platform_id')
|
|
response = await self.privateGetDepositAddress(self.extend(request, params))
|
|
#
|
|
# without 'platform_id'
|
|
# {
|
|
# "data":[
|
|
# {
|
|
# "currency_id":"ETH",
|
|
# "address":"0x12e2caf3c4051ba1146e612f532901a423a9898a",
|
|
# "destination_tag":null
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
# with 'platform_id'
|
|
# {
|
|
# "data":[
|
|
# {
|
|
# "platform_id":"TRON",
|
|
# "address":"TDQLMxBTa6MzuoZ6deSGZkqET3Ek8v7uC6",
|
|
# "destination_tag":null
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
data = self.safe_value(response, 'data', [])
|
|
firstAddress = self.safe_value(data, 0)
|
|
if firstAddress is None:
|
|
raise InvalidAddress(self.id + ' fetchDepositAddress() returned an empty response')
|
|
return self.parse_deposit_address(firstAddress, currency)
|
|
|
|
async def fetch_deposit_addresses(self, codes: Strings = None, params={}) -> List[DepositAddress]:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/deposit_address
|
|
|
|
fetch deposit addresses for multiple currencies and chain types
|
|
:param str[]|None codes: list of unified currency codes, default is None
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a list of `address structures <https://docs.ccxt.com/#/?id=address-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
request: dict = {}
|
|
if codes:
|
|
currencyIds = []
|
|
for i in range(0, len(codes)):
|
|
currency = self.currency(codes[i])
|
|
currencyIds.append(currency['id'])
|
|
request['currency_id'] = ','.join(codes)
|
|
response = await self.privateGetDepositAddress(self.extend(request, params))
|
|
data = self.safe_list(response, 'data', [])
|
|
return self.parse_deposit_addresses(data, codes)
|
|
|
|
async def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction:
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/withdrawal
|
|
|
|
make a withdrawal
|
|
:param str code: unified currency code
|
|
:param float amount: the amount to withdraw
|
|
:param str address: the address to withdraw to
|
|
:param str tag:
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
"""
|
|
tag, params = self.handle_withdraw_tag_and_params(tag, params)
|
|
# In order to use self method
|
|
# you need to allow API withdrawal from the API Settings Page, and
|
|
# and register the list of withdrawal addresses and destination tags on the API Settings page
|
|
# you can only withdraw to the registered addresses using the API
|
|
self.check_address(address)
|
|
await self.load_markets()
|
|
currency = self.currency(code)
|
|
if tag is None:
|
|
tag = ''
|
|
request: dict = {
|
|
'currency_id': currency['id'],
|
|
# 'platform_id': 'ETH', # if omitted it will use the default platform for the currency
|
|
'address': address,
|
|
'destination_tag': tag,
|
|
'amount': self.number_to_string(amount),
|
|
# which currency to pay the withdrawal fees
|
|
# only applicable for currencies that accepts multiple withdrawal fee options
|
|
# 'fee_currency_id': 'ETH', # if omitted it will use the default fee policy for each currency
|
|
# whether the amount field includes fees
|
|
# 'include_fee': False, # makes sense only when fee_currency_id is equal to currency_id
|
|
}
|
|
networks = self.safe_value(self.options, 'networks', {})
|
|
network = self.safe_string_upper(params, 'network') # self line allows the user to specify either ERC20 or ETH
|
|
network = self.safe_string(networks, network, network) # handle ERC20>ETH alias
|
|
if network is not None:
|
|
request['platform_id'] = network
|
|
params = self.omit(params, 'network')
|
|
response = await self.privatePostWithdrawal(self.extend(request, params))
|
|
data = self.safe_dict(response, 'data')
|
|
return self.parse_transaction(data, currency)
|
|
|
|
async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
|
"""
|
|
fetch all deposits made to an account
|
|
: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 transaction structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
"""
|
|
request: dict = {
|
|
'type': 'deposit',
|
|
}
|
|
result = await self.fetch_transactions(code, since, limit, self.extend(request, params))
|
|
return result
|
|
|
|
async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
|
"""
|
|
fetch all withdrawals made to an account
|
|
: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 transaction structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
"""
|
|
request: dict = {
|
|
'type': 'withdrawal',
|
|
}
|
|
result = await self.fetch_transactions(code, since, limit, self.extend(request, params))
|
|
return result
|
|
|
|
async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
fetch history of deposits and withdrawals
|
|
|
|
https://docs-en.probit.com/reference/transferpayment
|
|
|
|
:param str code: unified currency code
|
|
:param int [since]: the earliest time in ms to fetch transactions for
|
|
:param int [limit]: the maximum number of transaction 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 transactions for
|
|
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
currency: Currency = None
|
|
request: dict = {}
|
|
if code is not None:
|
|
currency = self.currency(code)
|
|
request['currency_id'] = currency['id']
|
|
if since is not None:
|
|
request['start_time'] = self.iso8601(since)
|
|
else:
|
|
request['start_time'] = self.iso8601(1)
|
|
until = self.safe_integer(params, 'until')
|
|
if until is not None:
|
|
request['end_time'] = self.iso8601(until)
|
|
params = self.omit(params, ['until'])
|
|
else:
|
|
request['end_time'] = self.iso8601(self.milliseconds())
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
else:
|
|
request['limit'] = 100
|
|
response = await self.privateGetTransferPayment(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "data": [
|
|
# {
|
|
# "id": "01211d4b-0e68-41d6-97cb-298bfe2cab67",
|
|
# "type": "deposit",
|
|
# "status": "done",
|
|
# "amount": "0.01",
|
|
# "address": "0x9e7430fc0bdd14745bd00a1b92ed25133a7c765f",
|
|
# "time": "2023-06-14T12:03:11.000Z",
|
|
# "hash": "0x0ff5bedc9e378f9529acc6b9840fa8c2ef00fd0275e0bac7fa0589a9b5d1712e",
|
|
# "currency_id": "ETH",
|
|
# "confirmations":0,
|
|
# "fee": "0",
|
|
# "destination_tag": null,
|
|
# "platform_id": "ETH",
|
|
# "fee_currency_id": "ETH",
|
|
# "payment_service_name":null,
|
|
# "payment_service_display_name":null,
|
|
# "crypto":null
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
data = self.safe_list(response, 'data', [])
|
|
return self.parse_transactions(data, currency, since, limit)
|
|
|
|
def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
|
|
#
|
|
# {
|
|
# "id": "01211d4b-0e68-41d6-97cb-298bfe2cab67",
|
|
# "type": "deposit",
|
|
# "status": "done",
|
|
# "amount": "0.01",
|
|
# "address": "0x9e7430fc0bdd14745bd00a1b92ed25133a7c765f",
|
|
# "time": "2023-06-14T12:03:11.000Z",
|
|
# "hash": "0x0ff5bedc9e378f9529acc6b9840fa8c2ef00fd0275e0bac7fa0589a9b5d1712e",
|
|
# "currency_id": "ETH",
|
|
# "confirmations":0,
|
|
# "fee": "0",
|
|
# "destination_tag": null,
|
|
# "platform_id": "ETH",
|
|
# "fee_currency_id": "ETH",
|
|
# "payment_service_name":null,
|
|
# "payment_service_display_name":null,
|
|
# "crypto":null
|
|
# }
|
|
#
|
|
id = self.safe_string(transaction, 'id')
|
|
networkId = self.safe_string(transaction, 'platform_id')
|
|
networkCode = self.network_id_to_code(networkId)
|
|
amount = self.safe_number(transaction, 'amount')
|
|
address = self.safe_string(transaction, 'address')
|
|
tag = self.safe_string(transaction, 'destination_tag')
|
|
txid = self.safe_string(transaction, 'hash')
|
|
timestamp = self.parse8601(self.safe_string(transaction, 'time'))
|
|
type = self.safe_string(transaction, 'type')
|
|
currencyId = self.safe_string(transaction, 'currency_id')
|
|
code = self.safe_currency_code(currencyId)
|
|
status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
|
|
feeCostString = self.safe_string(transaction, 'fee')
|
|
fee = None
|
|
if feeCostString is not None and feeCostString != '0':
|
|
fee = {
|
|
'currency': code,
|
|
'cost': self.parse_number(feeCostString),
|
|
}
|
|
return {
|
|
'id': id,
|
|
'currency': code,
|
|
'amount': amount,
|
|
'network': networkCode,
|
|
'addressFrom': None,
|
|
'address': address,
|
|
'addressTo': address,
|
|
'tagFrom': None,
|
|
'tag': tag,
|
|
'tagTo': tag,
|
|
'status': status,
|
|
'type': type,
|
|
'txid': txid,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'updated': None,
|
|
'internal': None,
|
|
'comment': None,
|
|
'fee': fee,
|
|
'info': transaction,
|
|
}
|
|
|
|
def parse_transaction_status(self, status: Str):
|
|
statuses: dict = {
|
|
'requested': 'pending',
|
|
'pending': 'pending',
|
|
'confirming': 'pending',
|
|
'confirmed': 'pending',
|
|
'applying': 'pending',
|
|
'done': 'ok',
|
|
'cancelled': 'canceled',
|
|
'cancelling': 'canceled',
|
|
}
|
|
return self.safe_string(statuses, status, status)
|
|
|
|
async def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/currency
|
|
|
|
fetch deposit and withdraw fees
|
|
:param str[]|None codes: list of unified currency codes
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `fees structures <https://docs.ccxt.com/#/?id=fee-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
response = await self.publicGetCurrencyWithPlatform(params)
|
|
#
|
|
# {
|
|
# "data": [
|
|
# {
|
|
# "id": "AFX",
|
|
# "display_name": {
|
|
# "ko-kr": "아프릭스",
|
|
# "en-us": "Afrix"
|
|
# },
|
|
# "show_in_ui": True,
|
|
# "platform": [
|
|
# {
|
|
# "id": "ZYN",
|
|
# "priority": 1,
|
|
# "deposit": True,
|
|
# "withdrawal": True,
|
|
# "currency_id": "AFX",
|
|
# "precision": 18,
|
|
# "min_confirmation_count": 60,
|
|
# "require_destination_tag": False,
|
|
# "allow_withdrawal_destination_tag": False,
|
|
# "display_name": {
|
|
# "name": {
|
|
# "ko-kr": "지네코인",
|
|
# "en-us": "Wethio"
|
|
# }
|
|
# },
|
|
# "min_deposit_amount": "0",
|
|
# "min_withdrawal_amount": "0",
|
|
# "withdrawal_fee": [
|
|
# {
|
|
# "currency_id": "ZYN",
|
|
# "amount": "0.5",
|
|
# "priority": 1
|
|
# }
|
|
# ],
|
|
# "deposit_fee": {},
|
|
# "suspended_reason": "",
|
|
# "deposit_suspended": False,
|
|
# "withdrawal_suspended": False,
|
|
# "platform_currency_display_name": {}
|
|
# }
|
|
# ],
|
|
# "internal_transfer": {
|
|
# "suspended_reason": null,
|
|
# "suspended": False
|
|
# },
|
|
# "stakeable": False,
|
|
# "unstakeable": False,
|
|
# "auto_stake": False,
|
|
# "auto_stake_amount": "0"
|
|
# },
|
|
# ]
|
|
# }
|
|
#
|
|
data = self.safe_list(response, 'data')
|
|
return self.parse_deposit_withdraw_fees(data, codes, 'id')
|
|
|
|
def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
|
|
#
|
|
# {
|
|
# "id": "USDT",
|
|
# "display_name": {"ko-kr": '테더', "en-us": "Tether"},
|
|
# "show_in_ui": True,
|
|
# "platform": [
|
|
# {
|
|
# "id": "ETH",
|
|
# "priority": "1",
|
|
# "deposit": True,
|
|
# "withdrawal": True,
|
|
# "currency_id": "USDT",
|
|
# "precision": "6",
|
|
# "min_confirmation_count": "15",
|
|
# "require_destination_tag": False,
|
|
# "allow_withdrawal_destination_tag": False,
|
|
# "display_name": [Object],
|
|
# "min_deposit_amount": "0",
|
|
# "min_withdrawal_amount": "1",
|
|
# "withdrawal_fee": [Array],
|
|
# "deposit_fee": {},
|
|
# "suspended_reason": '',
|
|
# "deposit_suspended": False,
|
|
# "withdrawal_suspended": False,
|
|
# "platform_currency_display_name": [Object]
|
|
# },
|
|
# ],
|
|
# "internal_transfer": {suspended_reason: null, suspended: False},
|
|
# "stakeable": False,
|
|
# "unstakeable": False,
|
|
# "auto_stake": False,
|
|
# "auto_stake_amount": "0"
|
|
# }
|
|
#
|
|
depositWithdrawFee = self.deposit_withdraw_fee({})
|
|
platforms = self.safe_value(fee, 'platform', [])
|
|
depositResult: dict = {
|
|
'fee': None,
|
|
'percentage': None,
|
|
}
|
|
for i in range(0, len(platforms)):
|
|
network = platforms[i]
|
|
networkId = self.safe_string(network, 'id')
|
|
networkCode = self.network_id_to_code(networkId, currency['code'])
|
|
withdrawalFees = self.safe_value(network, 'withdrawal_fee', {})
|
|
withdrawFee = self.safe_number(withdrawalFees[0], 'amount')
|
|
if len(withdrawalFees):
|
|
withdrawResult: dict = {
|
|
'fee': withdrawFee,
|
|
'percentage': False if (withdrawFee is not None) else None,
|
|
}
|
|
if i == 0:
|
|
depositWithdrawFee['withdraw'] = withdrawResult
|
|
depositWithdrawFee['networks'][networkCode] = {
|
|
'withdraw': withdrawResult,
|
|
'deposit': depositResult,
|
|
}
|
|
depositWithdrawFee['info'] = fee
|
|
return depositWithdrawFee
|
|
|
|
def nonce(self):
|
|
return self.milliseconds()
|
|
|
|
def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
|
|
url = self.urls['api'][api] + '/'
|
|
query = self.omit(params, self.extract_params(path))
|
|
if api == 'accounts':
|
|
self.check_required_credentials()
|
|
url += self.implode_params(path, params)
|
|
auth = self.apiKey + ':' + self.secret
|
|
auth64 = self.string_to_base64(auth)
|
|
headers = {
|
|
'Authorization': 'Basic ' + auth64,
|
|
'Content-Type': 'application/json',
|
|
}
|
|
if query:
|
|
body = self.json(query)
|
|
else:
|
|
url += self.version + '/'
|
|
if api == 'public':
|
|
url += self.implode_params(path, params)
|
|
if query:
|
|
url += '?' + self.urlencode(query)
|
|
elif api == 'private':
|
|
now = self.milliseconds()
|
|
self.check_required_credentials()
|
|
expires = self.safe_integer(self.options, 'expires')
|
|
if (expires is None) or (expires < now):
|
|
raise AuthenticationError(self.id + ' access token expired, call signIn() method')
|
|
accessToken = self.safe_string(self.options, 'accessToken')
|
|
headers = {
|
|
'Authorization': 'Bearer ' + accessToken,
|
|
}
|
|
url += self.implode_params(path, params)
|
|
if method == 'GET':
|
|
if query:
|
|
url += '?' + self.urlencode(query)
|
|
elif query:
|
|
body = self.json(query)
|
|
headers['Content-Type'] = 'application/json'
|
|
return {'url': url, 'method': method, 'body': body, 'headers': headers}
|
|
|
|
async def sign_in(self, params={}):
|
|
"""
|
|
|
|
https://docs-en.probit.com/reference/token
|
|
|
|
sign in, must be called prior to using other authenticated methods
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns: response from exchange
|
|
"""
|
|
self.check_required_credentials()
|
|
request: dict = {
|
|
'grant_type': 'client_credentials', # the only supported value
|
|
}
|
|
response = await self.accountsPostToken(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "access_token": "0ttDv/2hTTn3bLi8GP1gKaneiEQ6+0hOBenPrxNQt2s=",
|
|
# "token_type": "bearer",
|
|
# "expires_in": 900
|
|
# }
|
|
#
|
|
expiresIn = self.safe_integer(response, 'expires_in')
|
|
accessToken = self.safe_string(response, 'access_token')
|
|
self.options['accessToken'] = accessToken
|
|
self.options['expires'] = self.sum(self.milliseconds(), expiresIn * 1000)
|
|
return response
|
|
|
|
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 # fallback to default error handler
|
|
if 'errorCode' in response:
|
|
errorCode = self.safe_string(response, 'errorCode')
|
|
if errorCode is not None:
|
|
errMessage = self.safe_string(response, 'message', '')
|
|
details = self.safe_value(response, 'details')
|
|
feedback = self.id + ' ' + errorCode + ' ' + errMessage + ' ' + self.json(details)
|
|
if 'exact' in self.exceptions:
|
|
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
|
|
if 'broad' in self.exceptions:
|
|
self.throw_broadly_matched_exception(self.exceptions['broad'], errMessage, feedback)
|
|
raise ExchangeError(feedback)
|
|
return None
|