ccxt-look 1.81.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/.cache/eslintcache +1 -0
  2. package/.dockerignore +6 -0
  3. package/.eslintignore +1 -0
  4. package/.gitattributes +5 -0
  5. package/.readthedocs.yaml +16 -0
  6. package/CONTRIBUTING.md +1049 -0
  7. package/LICENSE.txt +21 -0
  8. package/README.md +537 -0
  9. package/SECURITY.md +5 -0
  10. package/build/cleanup-old-tags.js +94 -0
  11. package/build/countries.js +256 -0
  12. package/build/export-exchanges.js +520 -0
  13. package/build/fs.js +51 -0
  14. package/build/transpile.js +1772 -0
  15. package/build/vss.js +78 -0
  16. package/ccxt.browser.js +7 -0
  17. package/ccxt.d.ts +692 -0
  18. package/ccxt.js +171 -0
  19. package/cleanup.sh +2 -0
  20. package/composer-install.sh +20 -0
  21. package/dist/ccxt.browser.js +208383 -0
  22. package/gource.sh +3 -0
  23. package/index.html +7 -0
  24. package/js/.eslintrc +87 -0
  25. package/js/aax.js +2686 -0
  26. package/js/ascendex.js +2584 -0
  27. package/js/base/.eslintrc.js +43 -0
  28. package/js/base/Exchange.js +2371 -0
  29. package/js/base/Precise.js +283 -0
  30. package/js/base/errorHierarchy.js +47 -0
  31. package/js/base/errors.js +55 -0
  32. package/js/base/functions/crypto.js +158 -0
  33. package/js/base/functions/encode.js +118 -0
  34. package/js/base/functions/generic.js +270 -0
  35. package/js/base/functions/misc.js +138 -0
  36. package/js/base/functions/number.js +329 -0
  37. package/js/base/functions/platform.js +38 -0
  38. package/js/base/functions/string.js +21 -0
  39. package/js/base/functions/throttle.js +79 -0
  40. package/js/base/functions/time.js +210 -0
  41. package/js/base/functions/type.js +66 -0
  42. package/js/base/functions.js +28 -0
  43. package/js/bequant.js +32 -0
  44. package/js/bibox.js +1407 -0
  45. package/js/bigone.js +1366 -0
  46. package/js/binance.js +5652 -0
  47. package/js/binancecoinm.js +46 -0
  48. package/js/binanceus.js +46 -0
  49. package/js/binanceusdm.js +49 -0
  50. package/js/bit2c.js +535 -0
  51. package/js/bitbank.js +842 -0
  52. package/js/bitbay.js +16 -0
  53. package/js/bitbns.js +1073 -0
  54. package/js/bitcoincom.js +15 -0
  55. package/js/bitfinex.js +1433 -0
  56. package/js/bitfinex2.js +2025 -0
  57. package/js/bitflyer.js +840 -0
  58. package/js/bitforex.js +614 -0
  59. package/js/bitget.js +2397 -0
  60. package/js/bithumb.js +980 -0
  61. package/js/bitmart.js +2516 -0
  62. package/js/bitmex.js +1809 -0
  63. package/js/bitopro.js +1443 -0
  64. package/js/bitpanda.js +1782 -0
  65. package/js/bitrue.js +1747 -0
  66. package/js/bitso.js +1062 -0
  67. package/js/bitstamp.js +1757 -0
  68. package/js/bitstamp1.js +343 -0
  69. package/js/bittrex.js +1876 -0
  70. package/js/bitvavo.js +1579 -0
  71. package/js/bkex.js +1233 -0
  72. package/js/bl3p.js +346 -0
  73. package/js/blockchaincom.js +969 -0
  74. package/js/btcalpha.js +680 -0
  75. package/js/btcbox.js +477 -0
  76. package/js/btcmarkets.js +1022 -0
  77. package/js/btctradeua.js +466 -0
  78. package/js/btcturk.js +734 -0
  79. package/js/buda.js +946 -0
  80. package/js/bw.js +1265 -0
  81. package/js/bybit.js +3372 -0
  82. package/js/bytetrade.js +1336 -0
  83. package/js/cdax.js +1646 -0
  84. package/js/cex.js +1410 -0
  85. package/js/coinbase.js +1342 -0
  86. package/js/coinbaseprime.js +31 -0
  87. package/js/coinbasepro.js +1466 -0
  88. package/js/coincheck.js +755 -0
  89. package/js/coinex.js +3400 -0
  90. package/js/coinfalcon.js +880 -0
  91. package/js/coinmate.js +794 -0
  92. package/js/coinone.js +816 -0
  93. package/js/coinspot.js +345 -0
  94. package/js/crex24.js +1636 -0
  95. package/js/cryptocom.js +1832 -0
  96. package/js/currencycom.js +1748 -0
  97. package/js/delta.js +1547 -0
  98. package/js/deribit.js +2148 -0
  99. package/js/digifinex.js +1585 -0
  100. package/js/eqonex.js +1660 -0
  101. package/js/exmo.js +1670 -0
  102. package/js/fairdesk.js +1231 -0
  103. package/js/flowbtc.js +35 -0
  104. package/js/fmfwio.js +34 -0
  105. package/js/ftx.js +2751 -0
  106. package/js/ftxus.js +38 -0
  107. package/js/gateio.js +4174 -0
  108. package/js/gemini.js +1397 -0
  109. package/js/hitbtc.js +1343 -0
  110. package/js/hitbtc3.js +2329 -0
  111. package/js/hollaex.js +1486 -0
  112. package/js/huobi.js +5706 -0
  113. package/js/huobijp.js +1710 -0
  114. package/js/huobipro.js +18 -0
  115. package/js/idex.js +1439 -0
  116. package/js/independentreserve.js +649 -0
  117. package/js/indodax.js +742 -0
  118. package/js/itbit.js +722 -0
  119. package/js/kraken.js +2179 -0
  120. package/js/kucoin.js +2571 -0
  121. package/js/kucoinfutures.js +1771 -0
  122. package/js/kuna.js +809 -0
  123. package/js/latoken.js +1445 -0
  124. package/js/lbank.js +760 -0
  125. package/js/liquid.js +1432 -0
  126. package/js/luno.js +873 -0
  127. package/js/lykke.js +1147 -0
  128. package/js/mercado.js +771 -0
  129. package/js/mexc.js +3151 -0
  130. package/js/ndax.js +2233 -0
  131. package/js/novadax.js +1318 -0
  132. package/js/oceanex.js +816 -0
  133. package/js/okcoin.js +3841 -0
  134. package/js/okex.js +16 -0
  135. package/js/okex5.js +16 -0
  136. package/js/okx.js +4795 -0
  137. package/js/paymium.js +498 -0
  138. package/js/phemex.js +2957 -0
  139. package/js/poloniex.js +1674 -0
  140. package/js/probit.js +1346 -0
  141. package/js/qtrade.js +1588 -0
  142. package/js/ripio.js +1061 -0
  143. package/js/static_dependencies/BN/bn.js +3526 -0
  144. package/js/static_dependencies/README.md +1 -0
  145. package/js/static_dependencies/crypto-js/crypto-js.js +5988 -0
  146. package/js/static_dependencies/elliptic/lib/elliptic/curve/base.js +375 -0
  147. package/js/static_dependencies/elliptic/lib/elliptic/curve/edwards.js +433 -0
  148. package/js/static_dependencies/elliptic/lib/elliptic/curve/index.js +8 -0
  149. package/js/static_dependencies/elliptic/lib/elliptic/curve/mont.js +180 -0
  150. package/js/static_dependencies/elliptic/lib/elliptic/curve/short.js +938 -0
  151. package/js/static_dependencies/elliptic/lib/elliptic/curves.js +204 -0
  152. package/js/static_dependencies/elliptic/lib/elliptic/ec/index.js +240 -0
  153. package/js/static_dependencies/elliptic/lib/elliptic/ec/key.js +119 -0
  154. package/js/static_dependencies/elliptic/lib/elliptic/ec/signature.js +24 -0
  155. package/js/static_dependencies/elliptic/lib/elliptic/eddsa/index.js +145 -0
  156. package/js/static_dependencies/elliptic/lib/elliptic/eddsa/key.js +100 -0
  157. package/js/static_dependencies/elliptic/lib/elliptic/eddsa/signature.js +65 -0
  158. package/js/static_dependencies/elliptic/lib/elliptic/precomputed/secp256k1.js +780 -0
  159. package/js/static_dependencies/elliptic/lib/elliptic/utils.js +214 -0
  160. package/js/static_dependencies/elliptic/lib/elliptic.js +22 -0
  161. package/js/static_dependencies/elliptic/lib/hmac-drbg/hmac-drbg.js +114 -0
  162. package/js/static_dependencies/fetch-ponyfill/fetch-node.js +39 -0
  163. package/js/static_dependencies/node-fetch/index.js +1564 -0
  164. package/js/static_dependencies/node-rsa/NodeRSA.js +223 -0
  165. package/js/static_dependencies/node-rsa/asn1/ber/errors.js +13 -0
  166. package/js/static_dependencies/node-rsa/asn1/ber/index.js +21 -0
  167. package/js/static_dependencies/node-rsa/asn1/ber/reader.js +262 -0
  168. package/js/static_dependencies/node-rsa/asn1/ber/types.js +36 -0
  169. package/js/static_dependencies/node-rsa/asn1/index.js +17 -0
  170. package/js/static_dependencies/node-rsa/encryptEngines/js.js +34 -0
  171. package/js/static_dependencies/node-rsa/formats/components.js +71 -0
  172. package/js/static_dependencies/node-rsa/formats/formats.js +31 -0
  173. package/js/static_dependencies/node-rsa/formats/pkcs1.js +148 -0
  174. package/js/static_dependencies/node-rsa/formats/pkcs8.js +187 -0
  175. package/js/static_dependencies/node-rsa/libs/jsbn.js +1252 -0
  176. package/js/static_dependencies/node-rsa/libs/rsa.js +147 -0
  177. package/js/static_dependencies/node-rsa/schemes/pkcs1.js +176 -0
  178. package/js/static_dependencies/node-rsa/schemes/schemes.js +21 -0
  179. package/js/static_dependencies/node-rsa/utils.js +98 -0
  180. package/js/static_dependencies/qs/formats.js +18 -0
  181. package/js/static_dependencies/qs/index.js +11 -0
  182. package/js/static_dependencies/qs/parse.js +242 -0
  183. package/js/static_dependencies/qs/stringify.js +269 -0
  184. package/js/static_dependencies/qs/utils.js +230 -0
  185. package/js/stex.js +1925 -0
  186. package/js/test/.eslintrc.js +42 -0
  187. package/js/test/Exchange/test.balance.js +61 -0
  188. package/js/test/Exchange/test.borrowRate.js +32 -0
  189. package/js/test/Exchange/test.currency.js +52 -0
  190. package/js/test/Exchange/test.fetchBalance.js +23 -0
  191. package/js/test/Exchange/test.fetchBorrowInterest.js +59 -0
  192. package/js/test/Exchange/test.fetchBorrowRate.js +32 -0
  193. package/js/test/Exchange/test.fetchBorrowRates.js +28 -0
  194. package/js/test/Exchange/test.fetchClosedOrders.js +32 -0
  195. package/js/test/Exchange/test.fetchCurrencies.js +35 -0
  196. package/js/test/Exchange/test.fetchDeposits.js +31 -0
  197. package/js/test/Exchange/test.fetchFundingFees.js +19 -0
  198. package/js/test/Exchange/test.fetchFundingRateHistory.js +40 -0
  199. package/js/test/Exchange/test.fetchL2OrderBook.js +23 -0
  200. package/js/test/Exchange/test.fetchLedger.js +42 -0
  201. package/js/test/Exchange/test.fetchLeverageTiers.js +33 -0
  202. package/js/test/Exchange/test.fetchMarketLeverageTiers.js +22 -0
  203. package/js/test/Exchange/test.fetchMarkets.js +33 -0
  204. package/js/test/Exchange/test.fetchMyTrades.js +42 -0
  205. package/js/test/Exchange/test.fetchOHLCV.js +46 -0
  206. package/js/test/Exchange/test.fetchOpenOrders.js +36 -0
  207. package/js/test/Exchange/test.fetchOrderBook.js +25 -0
  208. package/js/test/Exchange/test.fetchOrderBooks.js +35 -0
  209. package/js/test/Exchange/test.fetchOrders.js +41 -0
  210. package/js/test/Exchange/test.fetchPositions.js +47 -0
  211. package/js/test/Exchange/test.fetchStatus.js +35 -0
  212. package/js/test/Exchange/test.fetchTicker.js +38 -0
  213. package/js/test/Exchange/test.fetchTickers.js +49 -0
  214. package/js/test/Exchange/test.fetchTrades.js +39 -0
  215. package/js/test/Exchange/test.fetchTradingFee.js +18 -0
  216. package/js/test/Exchange/test.fetchTradingFees.js +22 -0
  217. package/js/test/Exchange/test.fetchTransactions.js +31 -0
  218. package/js/test/Exchange/test.fetchWithdrawals.js +31 -0
  219. package/js/test/Exchange/test.ledgerItem.js +46 -0
  220. package/js/test/Exchange/test.leverageTier.js +33 -0
  221. package/js/test/Exchange/test.loadMarkets.js +35 -0
  222. package/js/test/Exchange/test.market.js +129 -0
  223. package/js/test/Exchange/test.ohlcv.js +33 -0
  224. package/js/test/Exchange/test.order.js +62 -0
  225. package/js/test/Exchange/test.orderbook.js +61 -0
  226. package/js/test/Exchange/test.position.js +21 -0
  227. package/js/test/Exchange/test.throttle.js +94 -0
  228. package/js/test/Exchange/test.ticker.js +95 -0
  229. package/js/test/Exchange/test.trade.js +68 -0
  230. package/js/test/Exchange/test.tradingFee.js +34 -0
  231. package/js/test/Exchange/test.transaction.js +35 -0
  232. package/js/test/base/.eslintrc +38 -0
  233. package/js/test/base/functions/test.crypto.js +110 -0
  234. package/js/test/base/functions/test.datetime.js +62 -0
  235. package/js/test/base/functions/test.generic.js +152 -0
  236. package/js/test/base/functions/test.number.js +362 -0
  237. package/js/test/base/functions/test.time.js +56 -0
  238. package/js/test/base/functions/test.type.js +53 -0
  239. package/js/test/base/test.base.js +193 -0
  240. package/js/test/errors/test.InsufficientFunds.js +86 -0
  241. package/js/test/errors/test.InvalidNonce.js +64 -0
  242. package/js/test/errors/test.InvalidOrder.js +35 -0
  243. package/js/test/errors/test.OrderNotFound.js +39 -0
  244. package/js/test/test.js +426 -0
  245. package/js/test/test.timeout_hang.js +12 -0
  246. package/js/therock.js +1431 -0
  247. package/js/tidebit.js +632 -0
  248. package/js/tidex.js +939 -0
  249. package/js/timex.js +1283 -0
  250. package/js/upbit.js +1622 -0
  251. package/js/vcc.js +1353 -0
  252. package/js/wavesexchange.js +2185 -0
  253. package/js/wazirx.js +732 -0
  254. package/js/whitebit.js +1352 -0
  255. package/js/woo.js +1577 -0
  256. package/js/xena.js +1948 -0
  257. package/js/yobit.js +1129 -0
  258. package/js/zaif.js +647 -0
  259. package/js/zb.js +4088 -0
  260. package/js/zipmex.js +40 -0
  261. package/js/zonda.js +1497 -0
  262. package/multilang.sh +159 -0
  263. package/package.json +591 -0
  264. package/postinstall.js +103 -0
package/js/exmo.js ADDED
@@ -0,0 +1,1670 @@
1
+ 'use strict';
2
+
3
+ // ---------------------------------------------------------------------------
4
+
5
+ const Exchange = require ('./base/Exchange');
6
+ const { ArgumentsRequired, ExchangeError, OrderNotFound, AuthenticationError, InsufficientFunds, InvalidOrder, InvalidNonce, OnMaintenance, RateLimitExceeded, BadRequest, PermissionDenied } = require ('./base/errors');
7
+ const Precise = require ('./base/Precise');
8
+
9
+ // ---------------------------------------------------------------------------
10
+
11
+ module.exports = class exmo extends Exchange {
12
+ describe () {
13
+ return this.deepExtend (super.describe (), {
14
+ 'id': 'exmo',
15
+ 'name': 'EXMO',
16
+ 'countries': [ 'LT' ], // Lithuania
17
+ 'rateLimit': 350, // once every 350 ms ≈ 180 requests per minute ≈ 3 requests per second
18
+ 'version': 'v1.1',
19
+ 'has': {
20
+ 'CORS': undefined,
21
+ 'spot': true,
22
+ 'margin': undefined, // has but unimplemented
23
+ 'swap': false,
24
+ 'future': false,
25
+ 'option': false,
26
+ 'cancelOrder': true,
27
+ 'createOrder': true,
28
+ 'createStopLimitOrder': true,
29
+ 'createStopMarketOrder': true,
30
+ 'createStopOrder': true,
31
+ 'fetchBalance': true,
32
+ 'fetchCurrencies': true,
33
+ 'fetchDepositAddress': true,
34
+ 'fetchFundingFees': true,
35
+ 'fetchFundingHistory': false,
36
+ 'fetchFundingRate': false,
37
+ 'fetchFundingRateHistory': false,
38
+ 'fetchFundingRates': false,
39
+ 'fetchIndexOHLCV': false,
40
+ 'fetchMarkets': true,
41
+ 'fetchMarkOHLCV': false,
42
+ 'fetchMyTrades': true,
43
+ 'fetchOHLCV': true,
44
+ 'fetchOpenOrders': true,
45
+ 'fetchOrder': 'emulated',
46
+ 'fetchOrderBook': true,
47
+ 'fetchOrderBooks': true,
48
+ 'fetchOrderTrades': true,
49
+ 'fetchPremiumIndexOHLCV': false,
50
+ 'fetchTicker': true,
51
+ 'fetchTickers': true,
52
+ 'fetchTrades': true,
53
+ 'fetchTradingFee': false,
54
+ 'fetchTradingFees': true,
55
+ 'fetchTransactions': true,
56
+ 'fetchTransfer': false,
57
+ 'fetchTransfers': false,
58
+ 'fetchWithdrawals': true,
59
+ 'transfer': false,
60
+ 'withdraw': true,
61
+ },
62
+ 'timeframes': {
63
+ '1m': '1',
64
+ '5m': '5',
65
+ '15m': '15',
66
+ '30m': '30',
67
+ '45m': '45',
68
+ '1h': '60',
69
+ '2h': '120',
70
+ '3h': '180',
71
+ '4h': '240',
72
+ '1d': 'D',
73
+ '1w': 'W',
74
+ '1M': 'M',
75
+ },
76
+ 'urls': {
77
+ 'logo': 'https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg',
78
+ 'api': {
79
+ 'public': 'https://api.exmo.com',
80
+ 'private': 'https://api.exmo.com',
81
+ 'web': 'https://exmo.me',
82
+ },
83
+ 'www': 'https://exmo.me',
84
+ 'referral': 'https://exmo.me/?ref=131685',
85
+ 'doc': [
86
+ 'https://exmo.me/en/api_doc?ref=131685',
87
+ ],
88
+ 'fees': 'https://exmo.com/en/docs/fees',
89
+ },
90
+ 'api': {
91
+ 'web': {
92
+ 'get': [
93
+ 'ctrl/feesAndLimits',
94
+ 'en/docs/fees',
95
+ ],
96
+ },
97
+ 'public': {
98
+ 'get': [
99
+ 'currency',
100
+ 'currency/list/extended',
101
+ 'order_book',
102
+ 'pair_settings',
103
+ 'ticker',
104
+ 'trades',
105
+ 'candles_history',
106
+ 'required_amount',
107
+ 'payments/providers/crypto/list',
108
+ ],
109
+ },
110
+ 'private': {
111
+ 'post': [
112
+ 'user_info',
113
+ 'order_create',
114
+ 'order_cancel',
115
+ 'stop_market_order_create',
116
+ 'stop_market_order_cancel',
117
+ 'user_open_orders',
118
+ 'user_trades',
119
+ 'user_cancelled_orders',
120
+ 'order_trades',
121
+ 'deposit_address',
122
+ 'withdraw_crypt',
123
+ 'withdraw_get_txid',
124
+ 'excode_create',
125
+ 'excode_load',
126
+ 'code_check',
127
+ 'wallet_history',
128
+ 'wallet_operations',
129
+ 'margin/user/order/create',
130
+ 'margin/user/order/update',
131
+ 'margin/user/order/cancel',
132
+ 'margin/user/position/close',
133
+ 'margin/user/position/margin_add',
134
+ 'margin/user/position/margin_remove',
135
+ 'margin/currency/list',
136
+ 'margin/pair/list',
137
+ 'margin/settings',
138
+ 'margin/funding/list',
139
+ 'margin/user/info',
140
+ 'margin/user/order/list',
141
+ 'margin/user/order/history',
142
+ 'margin/user/order/trades',
143
+ 'margin/user/order/max_quantity',
144
+ 'margin/user/position/list',
145
+ 'margin/user/position/margin_remove_info',
146
+ 'margin/user/position/margin_add_info',
147
+ 'margin/user/wallet/list',
148
+ 'margin/user/wallet/history',
149
+ 'margin/user/trade/list',
150
+ 'margin/trades',
151
+ 'margin/liquidation/feed',
152
+ ],
153
+ },
154
+ },
155
+ 'fees': {
156
+ 'trading': {
157
+ 'feeSide': 'get',
158
+ 'tierBased': true,
159
+ 'percentage': true,
160
+ 'maker': this.parseNumber ('0.004'),
161
+ 'taker': this.parseNumber ('0.004'),
162
+ },
163
+ 'funding': {
164
+ 'tierBased': false,
165
+ 'percentage': false, // fixed funding fees for crypto, see fetchFundingFees below
166
+ },
167
+ },
168
+ 'options': {
169
+ 'networks': {
170
+ 'ETH': 'ERC20',
171
+ 'TRX': 'TRC20',
172
+ },
173
+ 'fetchTradingFees': {
174
+ 'method': 'fetchPrivateTradingFees', // or 'fetchPublicTradingFees'
175
+ },
176
+ },
177
+ 'commonCurrencies': {
178
+ 'GMT': 'GMT Token',
179
+ },
180
+ 'exceptions': {
181
+ 'exact': {
182
+ '40005': AuthenticationError, // Authorization error, incorrect signature
183
+ '40009': InvalidNonce, //
184
+ '40015': ExchangeError, // API function do not exist
185
+ '40016': OnMaintenance, // {"result":false,"error":"Error 40016: Maintenance work in progress"}
186
+ '40017': AuthenticationError, // Wrong API Key
187
+ '40032': PermissionDenied, // {"result":false,"error":"Error 40032: Access is denied for this API key"}
188
+ '40033': PermissionDenied, // {"result":false,"error":"Error 40033: Access is denied, this resources are temporarily blocked to user"}
189
+ '40034': RateLimitExceeded, // {"result":false,"error":"Error 40034: Access is denied, rate limit is exceeded"}
190
+ '50052': InsufficientFunds,
191
+ '50054': InsufficientFunds,
192
+ '50304': OrderNotFound, // "Order was not found '123456789'" (fetching order trades for an order that does not have trades yet)
193
+ '50173': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order)
194
+ '50277': InvalidOrder,
195
+ '50319': InvalidOrder, // Price by order is less than permissible minimum for this pair
196
+ '50321': InvalidOrder, // Price by order is more than permissible maximum for this pair
197
+ '50381': InvalidOrder, // {"result":false,"error":"Error 50381: More than 2 decimal places are not permitted for pair BTC_USD"}
198
+ },
199
+ 'broad': {
200
+ 'range period is too long': BadRequest,
201
+ 'invalid syntax': BadRequest,
202
+ 'API rate limit exceeded': RateLimitExceeded, // {"result":false,"error":"API rate limit exceeded for x.x.x.x. Retry after 60 sec.","history":[],"begin":1579392000,"end":1579478400}
203
+ },
204
+ },
205
+ });
206
+ }
207
+
208
+ async fetchTradingFees (params = {}) {
209
+ let method = this.safeString (params, 'method');
210
+ params = this.omit (params, 'method');
211
+ if (method === undefined) {
212
+ const options = this.safeValue (this.options, 'fetchTradingFees', {});
213
+ method = this.safeString (options, 'method', 'fetchPrivateTradingFees');
214
+ }
215
+ return await this[method] (params);
216
+ }
217
+
218
+ async fetchPrivateTradingFees (params = {}) {
219
+ await this.loadMarkets ();
220
+ const response = await this.privatePostMarginPairList (params);
221
+ //
222
+ // {
223
+ // pairs: [{
224
+ // name: 'EXM_USD',
225
+ // buy_price: '0.02728391',
226
+ // sell_price: '0.0276',
227
+ // last_trade_price: '0.0276',
228
+ // ticker_updated: '1646956050056696046',
229
+ // is_fair_price: true,
230
+ // max_price_precision: '8',
231
+ // min_order_quantity: '1',
232
+ // max_order_quantity: '50000',
233
+ // min_order_price: '0.00000001',
234
+ // max_order_price: '1000',
235
+ // max_position_quantity: '50000',
236
+ // trade_taker_fee: '0.05',
237
+ // trade_maker_fee: '0',
238
+ // liquidation_fee: '0.5',
239
+ // max_leverage: '3',
240
+ // default_leverage: '3',
241
+ // liquidation_level: '5',
242
+ // margin_call_level: '7.5',
243
+ // position: '1',
244
+ // updated: '1638976144797807397'
245
+ // }
246
+ // ...
247
+ // ]
248
+ // }
249
+ //
250
+ const pairs = this.safeValue (response, 'pairs', []);
251
+ const result = {};
252
+ for (let i = 0; i < pairs.length; i++) {
253
+ const pair = pairs[i];
254
+ const marketId = this.safeString (pair, 'name');
255
+ const symbol = this.safeSymbol (marketId, undefined, '_');
256
+ const makerString = this.safeString (pair, 'trade_maker_fee');
257
+ const takerString = this.safeString (pair, 'trade_taker_fee');
258
+ const maker = this.parseNumber (Precise.stringDiv (makerString, '100'));
259
+ const taker = this.parseNumber (Precise.stringDiv (takerString, '100'));
260
+ result[symbol] = {
261
+ 'info': pair,
262
+ 'symbol': symbol,
263
+ 'maker': maker,
264
+ 'taker': taker,
265
+ 'percentage': true,
266
+ 'tierBased': true,
267
+ };
268
+ }
269
+ return result;
270
+ }
271
+
272
+ async fetchPublicTradingFees (params = {}) {
273
+ await this.loadMarkets ();
274
+ const response = await this.publicGetPairSettings (params);
275
+ //
276
+ // {
277
+ // BTC_USD: {
278
+ // min_quantity: '0.00002',
279
+ // max_quantity: '1000',
280
+ // min_price: '1',
281
+ // max_price: '150000',
282
+ // max_amount: '500000',
283
+ // min_amount: '1',
284
+ // price_precision: '2',
285
+ // commission_taker_percent: '0.3',
286
+ // commission_maker_percent: '0.3'
287
+ // },
288
+ // }
289
+ //
290
+ const result = {};
291
+ for (let i = 0; i < this.symbols.length; i++) {
292
+ const symbol = this.symbols[i];
293
+ const market = this.market (symbol);
294
+ const fee = this.safeValue (response, market['id'], {});
295
+ const makerString = this.safeString (fee, 'commission_maker_percent');
296
+ const takerString = this.safeString (fee, 'commission_taker_percent');
297
+ const maker = this.parseNumber (Precise.stringDiv (makerString, '100'));
298
+ const taker = this.parseNumber (Precise.stringDiv (takerString, '100'));
299
+ result[symbol] = {
300
+ 'info': fee,
301
+ 'symbol': symbol,
302
+ 'maker': maker,
303
+ 'taker': taker,
304
+ 'percentage': true,
305
+ 'tierBased': true,
306
+ };
307
+ }
308
+ return result;
309
+ }
310
+
311
+ parseFixedFloatValue (input) {
312
+ if ((input === undefined) || (input === '-')) {
313
+ return undefined;
314
+ }
315
+ if (input === '') {
316
+ return 0;
317
+ }
318
+ const isPercentage = (input.indexOf ('%') >= 0);
319
+ const parts = input.split (' ');
320
+ const value = parts[0].replace ('%', '');
321
+ const result = parseFloat (value);
322
+ if ((result > 0) && isPercentage) {
323
+ throw new ExchangeError (this.id + ' parseFixedFloatValue() detected an unsupported non-zero percentage-based fee ' + input);
324
+ }
325
+ return result;
326
+ }
327
+
328
+ async fetchFundingFees (params = {}) {
329
+ await this.loadMarkets ();
330
+ const currencyList = await this.publicGetCurrencyListExtended (params);
331
+ //
332
+ // [
333
+ // {"name":"VLX","description":"Velas"},
334
+ // {"name":"RUB","description":"Russian Ruble"},
335
+ // {"name":"BTC","description":"Bitcoin"},
336
+ // {"name":"USD","description":"US Dollar"}
337
+ // ]
338
+ //
339
+ const cryptoList = await this.publicGetPaymentsProvidersCryptoList (params);
340
+ //
341
+ // {
342
+ // "BTC":[
343
+ // { "type":"deposit", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.001 BTC. We do not support BSC and BEP20 network, please consider this when sending funds", "commission_desc":"0%", "currency_confirmations":1 },
344
+ // { "type":"withdraw", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"350", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address as your account will not be credited with tokens from such sales.", "commission_desc":"0.0005 BTC", "currency_confirmations":6 }
345
+ // ],
346
+ // "ETH":[
347
+ // { "type":"withdraw", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"500", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address as your account will not be credited with tokens from such sales.", "commission_desc":"0.004 ETH", "currency_confirmations":4 },
348
+ // { "type":"deposit", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.01 ETH. We do not support BSC and BEP20 network, please consider this when sending funds", "commission_desc":"0%", "currency_confirmations":1 }
349
+ // ],
350
+ // "USDT":[
351
+ // { "type":"deposit", "name":"USDT (OMNI)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":false,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2 },
352
+ // { "type":"withdraw", "name":"USDT (OMNI)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":false,"comment":"Do not withdraw directly to the Crowdfunding or ICO address as your account will not be credited with tokens from such sales.", "commission_desc":"5 USDT", "currency_confirmations":6 },
353
+ // { "type":"deposit", "name":"USDT (ERC20)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2 },
354
+ // {
355
+ // "type":"withdraw",
356
+ // "name":"USDT (ERC20)",
357
+ // "currency_name":"USDT",
358
+ // "min":"55",
359
+ // "max":"200000",
360
+ // "enabled":true,
361
+ // "comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, as your account will not be credited with tokens from such sales. Recommendation: Due to the high load of ERC20 network, using TRC20 address for withdrawal is recommended.",
362
+ // "commission_desc":"10 USDT",
363
+ // "currency_confirmations":6
364
+ // },
365
+ // { "type":"deposit", "name":"USDT (TRC20)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":true,"comment":"Minimum deposit amount is 10 USDT. Only TRON main network supported", "commission_desc":"0%", "currency_confirmations":2 },
366
+ // { "type":"withdraw", "name":"USDT (TRC20)", "currency_name":"USDT", "min":"10", "max":"150000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, as your account will not be credited with tokens from such sales. Only TRON main network supported.", "commission_desc":"1 USDT", "currency_confirmations":6 }
367
+ // ],
368
+ // "XLM":[
369
+ // { "type":"deposit", "name":"XLM", "currency_name":"XLM", "min":"1", "max":"1000000", "enabled":true,"comment":"Attention! A deposit without memo(invoice) will not be credited. Minimum deposit amount is 1 XLM. We do not support BSC and BEP20 network, please consider this when sending funds", "commission_desc":"0%", "currency_confirmations":1 },
370
+ // { "type":"withdraw", "name":"XLM", "currency_name":"XLM", "min":"21", "max":"1000000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, as your account will not be credited with tokens from such sales.", "commission_desc":"0.01 XLM", "currency_confirmations":1 }
371
+ // ],
372
+ // }
373
+ //
374
+ const result = {
375
+ 'info': cryptoList,
376
+ 'withdraw': {},
377
+ 'deposit': {},
378
+ };
379
+ for (let i = 0; i < currencyList.length; i++) {
380
+ const currency = currencyList[i];
381
+ const currencyId = this.safeString (currency, 'name');
382
+ const code = this.safeCurrencyCode (currencyId);
383
+ const providers = this.safeValue (cryptoList, currencyId, []);
384
+ for (let j = 0; j < providers.length; j++) {
385
+ const provider = providers[j];
386
+ const type = this.safeString (provider, 'type');
387
+ const commissionDesc = this.safeString (provider, 'commission_desc');
388
+ const newFee = this.parseFixedFloatValue (commissionDesc);
389
+ const previousFee = this.safeNumber (result[type], code);
390
+ if ((previousFee === undefined) || ((newFee !== undefined) && (newFee < previousFee))) {
391
+ result[type][code] = newFee;
392
+ }
393
+ }
394
+ }
395
+ // cache them for later use
396
+ this.options['fundingFees'] = result;
397
+ return result;
398
+ }
399
+
400
+ async fetchCurrencies (params = {}) {
401
+ //
402
+ const currencyList = await this.publicGetCurrencyListExtended (params);
403
+ //
404
+ // [
405
+ // {"name":"VLX","description":"Velas"},
406
+ // {"name":"RUB","description":"Russian Ruble"},
407
+ // {"name":"BTC","description":"Bitcoin"},
408
+ // {"name":"USD","description":"US Dollar"}
409
+ // ]
410
+ //
411
+ const cryptoList = await this.publicGetPaymentsProvidersCryptoList (params);
412
+ //
413
+ // {
414
+ // "BTC":[
415
+ // { "type":"deposit", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.001 BTC. We do not support BSC and BEP20 network, please consider this when sending funds", "commission_desc":"0%", "currency_confirmations":1 },
416
+ // { "type":"withdraw", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"350", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address as your account will not be credited with tokens from such sales.", "commission_desc":"0.0005 BTC", "currency_confirmations":6 }
417
+ // ],
418
+ // "ETH":[
419
+ // { "type":"withdraw", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"500", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address as your account will not be credited with tokens from such sales.", "commission_desc":"0.004 ETH", "currency_confirmations":4 },
420
+ // { "type":"deposit", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.01 ETH. We do not support BSC and BEP20 network, please consider this when sending funds", "commission_desc":"0%", "currency_confirmations":1 }
421
+ // ],
422
+ // "USDT":[
423
+ // { "type":"deposit", "name":"USDT (OMNI)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":false,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2 },
424
+ // { "type":"withdraw", "name":"USDT (OMNI)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":false,"comment":"Do not withdraw directly to the Crowdfunding or ICO address as your account will not be credited with tokens from such sales.", "commission_desc":"5 USDT", "currency_confirmations":6 },
425
+ // { "type":"deposit", "name":"USDT (ERC20)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2 },
426
+ // {
427
+ // "type":"withdraw",
428
+ // "name":"USDT (ERC20)",
429
+ // "currency_name":"USDT",
430
+ // "min":"55",
431
+ // "max":"200000",
432
+ // "enabled":true,
433
+ // "comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, as your account will not be credited with tokens from such sales. Recommendation: Due to the high load of ERC20 network, using TRC20 address for withdrawal is recommended.",
434
+ // "commission_desc":"10 USDT",
435
+ // "currency_confirmations":6
436
+ // },
437
+ // { "type":"deposit", "name":"USDT (TRC20)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":true,"comment":"Minimum deposit amount is 10 USDT. Only TRON main network supported", "commission_desc":"0%", "currency_confirmations":2 },
438
+ // { "type":"withdraw", "name":"USDT (TRC20)", "currency_name":"USDT", "min":"10", "max":"150000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, as your account will not be credited with tokens from such sales. Only TRON main network supported.", "commission_desc":"1 USDT", "currency_confirmations":6 }
439
+ // ],
440
+ // "XLM":[
441
+ // { "type":"deposit", "name":"XLM", "currency_name":"XLM", "min":"1", "max":"1000000", "enabled":true,"comment":"Attention! A deposit without memo(invoice) will not be credited. Minimum deposit amount is 1 XLM. We do not support BSC and BEP20 network, please consider this when sending funds", "commission_desc":"0%", "currency_confirmations":1 },
442
+ // { "type":"withdraw", "name":"XLM", "currency_name":"XLM", "min":"21", "max":"1000000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, as your account will not be credited with tokens from such sales.", "commission_desc":"0.01 XLM", "currency_confirmations":1 }
443
+ // ],
444
+ // }
445
+ //
446
+ const result = {};
447
+ for (let i = 0; i < currencyList.length; i++) {
448
+ const currency = currencyList[i];
449
+ const currencyId = this.safeString (currency, 'name');
450
+ const name = this.safeString (currency, 'description');
451
+ const providers = this.safeValue (cryptoList, currencyId);
452
+ let active = false;
453
+ let type = 'crypto';
454
+ const limits = {
455
+ 'deposit': {
456
+ 'min': undefined,
457
+ 'max': undefined,
458
+ },
459
+ 'withdraw': {
460
+ 'min': undefined,
461
+ 'max': undefined,
462
+ },
463
+ };
464
+ let fee = undefined;
465
+ let depositEnabled = undefined;
466
+ let withdrawEnabled = undefined;
467
+ if (providers === undefined) {
468
+ active = true;
469
+ type = 'fiat';
470
+ } else {
471
+ for (let j = 0; j < providers.length; j++) {
472
+ const provider = providers[j];
473
+ const type = this.safeString (provider, 'type');
474
+ const minValue = this.safeNumber (provider, 'min');
475
+ let maxValue = this.safeNumber (provider, 'max');
476
+ if (maxValue === 0.0) {
477
+ maxValue = undefined;
478
+ }
479
+ const activeProvider = this.safeValue (provider, 'enabled');
480
+ if (type === 'deposit') {
481
+ if (activeProvider && !depositEnabled) {
482
+ depositEnabled = true;
483
+ } else if (!activeProvider) {
484
+ depositEnabled = false;
485
+ }
486
+ } else if (type === 'withdraw') {
487
+ if (activeProvider && !withdrawEnabled) {
488
+ withdrawEnabled = true;
489
+ } else if (!activeProvider) {
490
+ withdrawEnabled = false;
491
+ }
492
+ }
493
+ if (activeProvider) {
494
+ active = true;
495
+ if ((limits[type]['min'] === undefined) || (minValue < limits[type]['min'])) {
496
+ limits[type]['min'] = minValue;
497
+ limits[type]['max'] = maxValue;
498
+ if (type === 'withdraw') {
499
+ const commissionDesc = this.safeString (provider, 'commission_desc');
500
+ fee = this.parseFixedFloatValue (commissionDesc);
501
+ }
502
+ }
503
+ }
504
+ }
505
+ }
506
+ const code = this.safeCurrencyCode (currencyId);
507
+ result[code] = {
508
+ 'id': currencyId,
509
+ 'code': code,
510
+ 'name': name,
511
+ 'type': type,
512
+ 'active': active,
513
+ 'deposit': depositEnabled,
514
+ 'withdraw': withdrawEnabled,
515
+ 'fee': fee,
516
+ 'precision': 8,
517
+ 'limits': limits,
518
+ 'info': providers,
519
+ };
520
+ }
521
+ return result;
522
+ }
523
+
524
+ async fetchMarkets (params = {}) {
525
+ const response = await this.publicGetPairSettings (params);
526
+ //
527
+ // {
528
+ // "BTC_USD":{
529
+ // "min_quantity":"0.0001",
530
+ // "max_quantity":"1000",
531
+ // "min_price":"1",
532
+ // "max_price":"30000",
533
+ // "max_amount":"500000",
534
+ // "min_amount":"1",
535
+ // "price_precision":8,
536
+ // "commission_taker_percent":"0.4",
537
+ // "commission_maker_percent":"0.4"
538
+ // },
539
+ // }
540
+ //
541
+ const keys = Object.keys (response);
542
+ const result = [];
543
+ for (let i = 0; i < keys.length; i++) {
544
+ const id = keys[i];
545
+ const market = response[id];
546
+ const symbol = id.replace ('_', '/');
547
+ const [ baseId, quoteId ] = symbol.split ('/');
548
+ const base = this.safeCurrencyCode (baseId);
549
+ const quote = this.safeCurrencyCode (quoteId);
550
+ const takerString = this.safeString (market, 'commission_taker_percent');
551
+ const makerString = this.safeString (market, 'commission_maker_percent');
552
+ result.push ({
553
+ 'id': id,
554
+ 'symbol': symbol,
555
+ 'base': base,
556
+ 'quote': quote,
557
+ 'settle': undefined,
558
+ 'baseId': baseId,
559
+ 'quoteId': quoteId,
560
+ 'settleId': undefined,
561
+ 'type': 'spot',
562
+ 'spot': true,
563
+ 'margin': false,
564
+ 'swap': false,
565
+ 'future': false,
566
+ 'option': false,
567
+ 'active': true,
568
+ 'contract': false,
569
+ 'linear': undefined,
570
+ 'inverse': undefined,
571
+ 'taker': this.parseNumber (Precise.stringDiv (takerString, '100')),
572
+ 'maker': this.parseNumber (Precise.stringDiv (makerString, '100')),
573
+ 'contractSize': undefined,
574
+ 'expiry': undefined,
575
+ 'expiryDatetime': undefined,
576
+ 'strike': undefined,
577
+ 'optionType': undefined,
578
+ 'precision': {
579
+ 'amount': parseInt ('8'),
580
+ 'price': this.safeInteger (market, 'price_precision'),
581
+ },
582
+ 'limits': {
583
+ 'leverage': {
584
+ 'min': undefined,
585
+ 'max': undefined,
586
+ },
587
+ 'amount': {
588
+ 'min': this.safeNumber (market, 'min_quantity'),
589
+ 'max': this.safeNumber (market, 'max_quantity'),
590
+ },
591
+ 'price': {
592
+ 'min': this.safeNumber (market, 'min_price'),
593
+ 'max': this.safeNumber (market, 'max_price'),
594
+ },
595
+ 'cost': {
596
+ 'min': this.safeNumber (market, 'min_amount'),
597
+ 'max': this.safeNumber (market, 'max_amount'),
598
+ },
599
+ },
600
+ 'info': market,
601
+ });
602
+ }
603
+ return result;
604
+ }
605
+
606
+ async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
607
+ await this.loadMarkets ();
608
+ const market = this.market (symbol);
609
+ const request = {
610
+ 'symbol': market['id'],
611
+ 'resolution': this.timeframes[timeframe],
612
+ };
613
+ const options = this.safeValue (this.options, 'fetchOHLCV');
614
+ const maxLimit = this.safeInteger (options, 'maxLimit', 3000);
615
+ const duration = this.parseTimeframe (timeframe);
616
+ const now = this.milliseconds ();
617
+ if (since === undefined) {
618
+ if (limit === undefined) {
619
+ throw new ArgumentsRequired (this.id + ' fetchOHLCV() requires a since argument or a limit argument');
620
+ } else {
621
+ if (limit > maxLimit) {
622
+ throw new BadRequest (this.id + ' fetchOHLCV() will serve ' + maxLimit.toString () + ' candles at most');
623
+ }
624
+ request['from'] = parseInt (now / 1000) - limit * duration - 1;
625
+ request['to'] = parseInt (now / 1000);
626
+ }
627
+ } else {
628
+ request['from'] = parseInt (since / 1000) - 1;
629
+ if (limit === undefined) {
630
+ request['to'] = parseInt (now / 1000);
631
+ } else {
632
+ if (limit > maxLimit) {
633
+ throw new BadRequest (this.id + ' fetchOHLCV() will serve ' + maxLimit.toString () + ' candles at most');
634
+ }
635
+ const to = this.sum (since, limit * duration * 1000);
636
+ request['to'] = parseInt (to / 1000);
637
+ }
638
+ }
639
+ const response = await this.publicGetCandlesHistory (this.extend (request, params));
640
+ //
641
+ // {
642
+ // "candles":[
643
+ // {"t":1584057600000,"o":0.02235144,"c":0.02400233,"h":0.025171,"l":0.02221,"v":5988.34031761},
644
+ // {"t":1584144000000,"o":0.0240373,"c":0.02367413,"h":0.024399,"l":0.0235,"v":2027.82522329},
645
+ // {"t":1584230400000,"o":0.02363458,"c":0.02319242,"h":0.0237948,"l":0.02223196,"v":1707.96944997},
646
+ // ]
647
+ // }
648
+ //
649
+ const candles = this.safeValue (response, 'candles', []);
650
+ return this.parseOHLCVs (candles, market, timeframe, since, limit);
651
+ }
652
+
653
+ parseOHLCV (ohlcv, market = undefined) {
654
+ //
655
+ // {
656
+ // "t":1584057600000,
657
+ // "o":0.02235144,
658
+ // "c":0.02400233,
659
+ // "h":0.025171,
660
+ // "l":0.02221,
661
+ // "v":5988.34031761
662
+ // }
663
+ //
664
+ return [
665
+ this.safeInteger (ohlcv, 't'),
666
+ this.safeNumber (ohlcv, 'o'),
667
+ this.safeNumber (ohlcv, 'h'),
668
+ this.safeNumber (ohlcv, 'l'),
669
+ this.safeNumber (ohlcv, 'c'),
670
+ this.safeNumber (ohlcv, 'v'),
671
+ ];
672
+ }
673
+
674
+ parseBalance (response) {
675
+ const result = { 'info': response };
676
+ const free = this.safeValue (response, 'balances', {});
677
+ const used = this.safeValue (response, 'reserved', {});
678
+ const currencyIds = Object.keys (free);
679
+ for (let i = 0; i < currencyIds.length; i++) {
680
+ const currencyId = currencyIds[i];
681
+ const code = this.safeCurrencyCode (currencyId);
682
+ const account = this.account ();
683
+ if (currencyId in free) {
684
+ account['free'] = this.safeString (free, currencyId);
685
+ }
686
+ if (currencyId in used) {
687
+ account['used'] = this.safeString (used, currencyId);
688
+ }
689
+ result[code] = account;
690
+ }
691
+ return this.safeBalance (result);
692
+ }
693
+
694
+ async fetchBalance (params = {}) {
695
+ await this.loadMarkets ();
696
+ const response = await this.privatePostUserInfo (params);
697
+ //
698
+ // {
699
+ // "uid":131685,
700
+ // "server_date":1628999600,
701
+ // "balances":{
702
+ // "EXM":"0",
703
+ // "USD":"0",
704
+ // "EUR":"0",
705
+ // "GBP":"0",
706
+ // },
707
+ // }
708
+ //
709
+ return this.parseBalance (response);
710
+ }
711
+
712
+ async fetchOrderBook (symbol, limit = undefined, params = {}) {
713
+ await this.loadMarkets ();
714
+ const market = this.market (symbol);
715
+ const request = {
716
+ 'pair': market['id'],
717
+ };
718
+ if (limit !== undefined) {
719
+ request['limit'] = limit;
720
+ }
721
+ const response = await this.publicGetOrderBook (this.extend (request, params));
722
+ const result = this.safeValue (response, market['id']);
723
+ return this.parseOrderBook (result, symbol, undefined, 'bid', 'ask');
724
+ }
725
+
726
+ async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) {
727
+ await this.loadMarkets ();
728
+ let ids = undefined;
729
+ if (symbols === undefined) {
730
+ ids = this.ids.join (',');
731
+ // max URL length is 2083 symbols, including http schema, hostname, tld, etc...
732
+ if (ids.length > 2048) {
733
+ const numIds = this.ids.length;
734
+ throw new ExchangeError (this.id + ' fetchOrderBooks() has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks');
735
+ }
736
+ } else {
737
+ ids = this.marketIds (symbols);
738
+ ids = ids.join (',');
739
+ }
740
+ const request = {
741
+ 'pair': ids,
742
+ };
743
+ if (limit !== undefined) {
744
+ request['limit'] = limit;
745
+ }
746
+ const response = await this.publicGetOrderBook (this.extend (request, params));
747
+ const result = {};
748
+ const marketIds = Object.keys (response);
749
+ for (let i = 0; i < marketIds.length; i++) {
750
+ const marketId = marketIds[i];
751
+ let symbol = marketId;
752
+ if (marketId in this.markets_by_id) {
753
+ const market = this.markets_by_id[marketId];
754
+ symbol = market['symbol'];
755
+ }
756
+ result[symbol] = this.parseOrderBook (response[marketId], symbol, undefined, 'bid', 'ask');
757
+ }
758
+ return result;
759
+ }
760
+
761
+ parseTicker (ticker, market = undefined) {
762
+ //
763
+ // {
764
+ // "buy_price":"0.00002996",
765
+ // "sell_price":"0.00003002",
766
+ // "last_trade":"0.00002992",
767
+ // "high":"0.00003028",
768
+ // "low":"0.00002935",
769
+ // "avg":"0.00002963",
770
+ // "vol":"1196546.3163222",
771
+ // "vol_curr":"35.80066578",
772
+ // "updated":1642291733
773
+ // }
774
+ //
775
+ const timestamp = this.safeTimestamp (ticker, 'updated');
776
+ market = this.safeMarket (undefined, market);
777
+ const last = this.safeString (ticker, 'last_trade');
778
+ return this.safeTicker ({
779
+ 'symbol': market['symbol'],
780
+ 'timestamp': timestamp,
781
+ 'datetime': this.iso8601 (timestamp),
782
+ 'high': this.safeString (ticker, 'high'),
783
+ 'low': this.safeString (ticker, 'low'),
784
+ 'bid': this.safeString (ticker, 'buy_price'),
785
+ 'bidVolume': undefined,
786
+ 'ask': this.safeString (ticker, 'sell_price'),
787
+ 'askVolume': undefined,
788
+ 'vwap': undefined,
789
+ 'open': undefined,
790
+ 'close': last,
791
+ 'last': last,
792
+ 'previousClose': undefined,
793
+ 'change': undefined,
794
+ 'percentage': undefined,
795
+ 'average': this.safeString (ticker, 'avg'),
796
+ 'baseVolume': this.safeString (ticker, 'vol'),
797
+ 'quoteVolume': this.safeString (ticker, 'vol_curr'),
798
+ 'info': ticker,
799
+ }, market, false);
800
+ }
801
+
802
+ async fetchTickers (symbols = undefined, params = {}) {
803
+ await this.loadMarkets ();
804
+ const response = await this.publicGetTicker (params);
805
+ //
806
+ // {
807
+ // "ADA_BTC":{
808
+ // "buy_price":"0.00002996",
809
+ // "sell_price":"0.00003002",
810
+ // "last_trade":"0.00002992",
811
+ // "high":"0.00003028",
812
+ // "low":"0.00002935",
813
+ // "avg":"0.00002963",
814
+ // "vol":"1196546.3163222",
815
+ // "vol_curr":"35.80066578",
816
+ // "updated":1642291733
817
+ // }
818
+ // }
819
+ //
820
+ const result = {};
821
+ const marketIds = Object.keys (response);
822
+ for (let i = 0; i < marketIds.length; i++) {
823
+ const marketId = marketIds[i];
824
+ const market = this.safeMarket (marketId, undefined, '_');
825
+ const symbol = market['symbol'];
826
+ const ticker = this.safeValue (response, marketId);
827
+ result[symbol] = this.parseTicker (ticker, market);
828
+ }
829
+ return this.filterByArray (result, 'symbol', symbols);
830
+ }
831
+
832
+ async fetchTicker (symbol, params = {}) {
833
+ await this.loadMarkets ();
834
+ const response = await this.publicGetTicker (params);
835
+ const market = this.market (symbol);
836
+ return this.parseTicker (response[market['id']], market);
837
+ }
838
+
839
+ parseTrade (trade, market = undefined) {
840
+ //
841
+ // fetchTrades (public)
842
+ //
843
+ // {
844
+ // "trade_id":165087520,
845
+ // "date":1587470005,
846
+ // "type":"buy",
847
+ // "quantity":"1.004",
848
+ // "price":"0.02491461",
849
+ // "amount":"0.02501426"
850
+ // },
851
+ //
852
+ // fetchMyTrades, fetchOrderTrades
853
+ //
854
+ // {
855
+ // "trade_id": 3,
856
+ // "date": 1435488248,
857
+ // "type": "buy",
858
+ // "pair": "BTC_USD",
859
+ // "order_id": 12345,
860
+ // "quantity": 1,
861
+ // "price": 100,
862
+ // "amount": 100,
863
+ // "exec_type": "taker",
864
+ // "commission_amount": "0.02",
865
+ // "commission_currency": "BTC",
866
+ // "commission_percent": "0.2"
867
+ // }
868
+ //
869
+ const timestamp = this.safeTimestamp (trade, 'date');
870
+ let symbol = undefined;
871
+ const id = this.safeString (trade, 'trade_id');
872
+ const orderId = this.safeString (trade, 'order_id');
873
+ const priceString = this.safeString (trade, 'price');
874
+ const amountString = this.safeString (trade, 'quantity');
875
+ const costString = this.safeString (trade, 'amount');
876
+ const side = this.safeString (trade, 'type');
877
+ const type = undefined;
878
+ const marketId = this.safeString (trade, 'pair');
879
+ if (marketId !== undefined) {
880
+ if (marketId in this.markets_by_id) {
881
+ market = this.markets_by_id[marketId];
882
+ } else {
883
+ const [ baseId, quoteId ] = marketId.split ('_');
884
+ const base = this.safeCurrencyCode (baseId);
885
+ const quote = this.safeCurrencyCode (quoteId);
886
+ symbol = base + '/' + quote;
887
+ }
888
+ }
889
+ if ((symbol === undefined) && (market !== undefined)) {
890
+ symbol = market['symbol'];
891
+ }
892
+ const takerOrMaker = this.safeString (trade, 'exec_type');
893
+ let fee = undefined;
894
+ const feeCostString = this.safeString (trade, 'commission_amount');
895
+ if (feeCostString !== undefined) {
896
+ const feeCurrencyId = this.safeString (trade, 'commission_currency');
897
+ const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
898
+ let feeRateString = this.safeString (trade, 'commission_percent');
899
+ if (feeRateString !== undefined) {
900
+ feeRateString = Precise.stringDiv (feeRateString, '1000', 18);
901
+ }
902
+ fee = {
903
+ 'cost': feeCostString,
904
+ 'currency': feeCurrencyCode,
905
+ 'rate': feeRateString,
906
+ };
907
+ }
908
+ return this.safeTrade ({
909
+ 'id': id,
910
+ 'info': trade,
911
+ 'timestamp': timestamp,
912
+ 'datetime': this.iso8601 (timestamp),
913
+ 'symbol': symbol,
914
+ 'order': orderId,
915
+ 'type': type,
916
+ 'side': side,
917
+ 'takerOrMaker': takerOrMaker,
918
+ 'price': priceString,
919
+ 'amount': amountString,
920
+ 'cost': costString,
921
+ 'fee': fee,
922
+ }, market);
923
+ }
924
+
925
+ async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
926
+ await this.loadMarkets ();
927
+ const market = this.market (symbol);
928
+ const request = {
929
+ 'pair': market['id'],
930
+ };
931
+ const response = await this.publicGetTrades (this.extend (request, params));
932
+ //
933
+ // {
934
+ // "ETH_BTC":[
935
+ // {
936
+ // "trade_id":165087520,
937
+ // "date":1587470005,
938
+ // "type":"buy",
939
+ // "quantity":"1.004",
940
+ // "price":"0.02491461",
941
+ // "amount":"0.02501426"
942
+ // },
943
+ // {
944
+ // "trade_id":165087369,
945
+ // "date":1587469938,
946
+ // "type":"buy",
947
+ // "quantity":"0.94",
948
+ // "price":"0.02492348",
949
+ // "amount":"0.02342807"
950
+ // }
951
+ // ]
952
+ // }
953
+ //
954
+ const data = this.safeValue (response, market['id'], []);
955
+ return this.parseTrades (data, market, since, limit);
956
+ }
957
+
958
+ async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
959
+ // a symbol is required but it can be a single string, or a non-empty array
960
+ if (symbol === undefined) {
961
+ throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument (a single symbol or an array)');
962
+ }
963
+ await this.loadMarkets ();
964
+ let pair = undefined;
965
+ let market = undefined;
966
+ if (Array.isArray (symbol)) {
967
+ const numSymbols = symbol.length;
968
+ if (numSymbols < 1) {
969
+ throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a non-empty symbol array');
970
+ }
971
+ const marketIds = this.marketIds (symbol);
972
+ pair = marketIds.join (',');
973
+ } else {
974
+ market = this.market (symbol);
975
+ pair = market['id'];
976
+ }
977
+ const request = {
978
+ 'pair': pair,
979
+ };
980
+ if (limit !== undefined) {
981
+ request['limit'] = limit;
982
+ }
983
+ const response = await this.privatePostUserTrades (this.extend (request, params));
984
+ let result = [];
985
+ const marketIds = Object.keys (response);
986
+ for (let i = 0; i < marketIds.length; i++) {
987
+ const marketId = marketIds[i];
988
+ let symbol = undefined;
989
+ if (marketId in this.markets_by_id) {
990
+ market = this.markets_by_id[marketId];
991
+ symbol = market['symbol'];
992
+ } else {
993
+ const [ baseId, quoteId ] = marketId.split ('_');
994
+ const base = this.safeCurrencyCode (baseId);
995
+ const quote = this.safeCurrencyCode (quoteId);
996
+ symbol = base + '/' + quote;
997
+ }
998
+ const items = response[marketId];
999
+ const trades = this.parseTrades (items, market, since, limit, {
1000
+ 'symbol': symbol,
1001
+ });
1002
+ result = this.arrayConcat (result, trades);
1003
+ }
1004
+ return this.filterBySinceLimit (result, since, limit);
1005
+ }
1006
+
1007
+ async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
1008
+ await this.loadMarkets ();
1009
+ const market = this.market (symbol);
1010
+ const prefix = (type === 'market') ? (type + '_') : '';
1011
+ const orderType = prefix + side;
1012
+ let orderPrice = price;
1013
+ if ((type === 'market') && (price === undefined)) {
1014
+ orderPrice = 0;
1015
+ }
1016
+ const request = {
1017
+ 'pair': market['id'],
1018
+ // 'leverage': 2,
1019
+ 'quantity': this.amountToPrecision (symbol, amount),
1020
+ // spot - buy, sell, market_buy, market_sell, market_buy_total, market_sell_total
1021
+ // margin - limit_buy, limit_sell, market_buy, market_sell, stop_buy, stop_sell, stop_limit_buy, stop_limit_sell, trailing_stop_buy, trailing_stop_sell
1022
+ 'type': orderType,
1023
+ 'price': this.priceToPrecision (symbol, orderPrice),
1024
+ // 'stop_price': this.priceToPrecision (symbol, stopPrice),
1025
+ // 'distance': 0, // distance for trailing stop orders
1026
+ // 'expire': 0, // expiration timestamp in UTC timezone for the order, unless expire is 0
1027
+ // 'client_id': 123, // optional, must be a positive integer
1028
+ // 'comment': '', // up to 50 latin symbols, whitespaces, underscores
1029
+ };
1030
+ let method = 'privatePostOrderCreate';
1031
+ let clientOrderId = this.safeValue2 (params, 'client_id', 'clientOrderId');
1032
+ if (clientOrderId !== undefined) {
1033
+ clientOrderId = this.safeInteger2 (params, 'client_id', 'clientOrderId');
1034
+ if (clientOrderId === undefined) {
1035
+ throw new BadRequest (this.id + ' createOrder() client order id must be an integer / numeric literal');
1036
+ } else {
1037
+ request['client_id'] = clientOrderId;
1038
+ }
1039
+ params = this.omit (params, [ 'client_id', 'clientOrderId' ]);
1040
+ }
1041
+ if ((type === 'stop') || (type === 'stop_limit') || (type === 'trailing_stop')) {
1042
+ const stopPrice = this.safeNumber2 (params, 'stop_price', 'stopPrice');
1043
+ if (stopPrice === undefined) {
1044
+ throw new InvalidOrder (this.id + ' createOrder() requires a stopPrice extra param for a ' + type + ' order');
1045
+ } else {
1046
+ params = this.omit (params, [ 'stopPrice', 'stop_price' ]);
1047
+ request['stop_price'] = this.priceToPrecision (symbol, stopPrice);
1048
+ method = 'privatePostMarginUserOrderCreate';
1049
+ }
1050
+ }
1051
+ const response = await this[method] (this.extend (request, params));
1052
+ const id = this.safeString (response, 'order_id');
1053
+ const timestamp = this.milliseconds ();
1054
+ const status = 'open';
1055
+ return {
1056
+ 'id': id,
1057
+ 'info': response,
1058
+ 'timestamp': timestamp,
1059
+ 'datetime': this.iso8601 (timestamp),
1060
+ 'lastTradeTimestamp': undefined,
1061
+ 'status': status,
1062
+ 'symbol': symbol,
1063
+ 'type': type,
1064
+ 'side': side,
1065
+ 'price': price,
1066
+ 'cost': undefined,
1067
+ 'amount': amount,
1068
+ 'remaining': amount,
1069
+ 'filled': 0.0,
1070
+ 'fee': undefined,
1071
+ 'trades': undefined,
1072
+ 'clientOrderId': clientOrderId,
1073
+ 'average': undefined,
1074
+ };
1075
+ }
1076
+
1077
+ async cancelOrder (id, symbol = undefined, params = {}) {
1078
+ await this.loadMarkets ();
1079
+ const request = { 'order_id': id };
1080
+ return await this.privatePostOrderCancel (this.extend (request, params));
1081
+ }
1082
+
1083
+ async fetchOrder (id, symbol = undefined, params = {}) {
1084
+ await this.loadMarkets ();
1085
+ const request = {
1086
+ 'order_id': id.toString (),
1087
+ };
1088
+ const response = await this.privatePostOrderTrades (this.extend (request, params));
1089
+ //
1090
+ // {
1091
+ // "type": "buy",
1092
+ // "in_currency": "BTC",
1093
+ // "in_amount": "1",
1094
+ // "out_currency": "USD",
1095
+ // "out_amount": "100",
1096
+ // "trades": [
1097
+ // {
1098
+ // "trade_id": 3,
1099
+ // "date": 1435488248,
1100
+ // "type": "buy",
1101
+ // "pair": "BTC_USD",
1102
+ // "order_id": 12345,
1103
+ // "quantity": 1,
1104
+ // "price": 100,
1105
+ // "amount": 100
1106
+ // }
1107
+ // ]
1108
+ // }
1109
+ //
1110
+ const order = this.parseOrder (response);
1111
+ return this.extend (order, {
1112
+ 'id': id.toString (),
1113
+ });
1114
+ }
1115
+
1116
+ async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
1117
+ let market = undefined;
1118
+ if (symbol !== undefined) {
1119
+ market = this.market (symbol);
1120
+ }
1121
+ const request = {
1122
+ 'order_id': id.toString (),
1123
+ };
1124
+ const response = await this.privatePostOrderTrades (this.extend (request, params));
1125
+ //
1126
+ // {
1127
+ // "type": "buy",
1128
+ // "in_currency": "BTC",
1129
+ // "in_amount": "1",
1130
+ // "out_currency": "USD",
1131
+ // "out_amount": "100",
1132
+ // "trades": [
1133
+ // {
1134
+ // "trade_id": 3,
1135
+ // "date": 1435488248,
1136
+ // "type": "buy",
1137
+ // "pair": "BTC_USD",
1138
+ // "order_id": 12345,
1139
+ // "quantity": 1,
1140
+ // "price": 100,
1141
+ // "amount": 100,
1142
+ // "exec_type": "taker",
1143
+ // "commission_amount": "0.02",
1144
+ // "commission_currency": "BTC",
1145
+ // "commission_percent": "0.2"
1146
+ // }
1147
+ // ]
1148
+ // }
1149
+ //
1150
+ const trades = this.safeValue (response, 'trades');
1151
+ return this.parseTrades (trades, market, since, limit);
1152
+ }
1153
+
1154
+ async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
1155
+ await this.loadMarkets ();
1156
+ if (symbol !== undefined) {
1157
+ const market = this.market (symbol);
1158
+ symbol = market['symbol'];
1159
+ }
1160
+ const response = await this.privatePostUserOpenOrders (params);
1161
+ const marketIds = Object.keys (response);
1162
+ let orders = [];
1163
+ for (let i = 0; i < marketIds.length; i++) {
1164
+ const marketId = marketIds[i];
1165
+ let market = undefined;
1166
+ if (marketId in this.markets_by_id) {
1167
+ market = this.markets_by_id[marketId];
1168
+ }
1169
+ const parsedOrders = this.parseOrders (response[marketId], market);
1170
+ orders = this.arrayConcat (orders, parsedOrders);
1171
+ }
1172
+ return this.filterBySymbolSinceLimit (orders, symbol, since, limit);
1173
+ }
1174
+
1175
+ parseOrder (order, market = undefined) {
1176
+ //
1177
+ // fetchOrders, fetchOpenOrders, fetchClosedOrders
1178
+ //
1179
+ // {
1180
+ // "order_id": "14",
1181
+ // "created": "1435517311",
1182
+ // "type": "buy",
1183
+ // "pair": "BTC_USD",
1184
+ // "price": "100",
1185
+ // "quantity": "1",
1186
+ // "amount": "100"
1187
+ // }
1188
+ //
1189
+ // fetchOrder
1190
+ //
1191
+ // {
1192
+ // "type": "buy",
1193
+ // "in_currency": "BTC",
1194
+ // "in_amount": "1",
1195
+ // "out_currency": "USD",
1196
+ // "out_amount": "100",
1197
+ // "trades": [
1198
+ // {
1199
+ // "trade_id": 3,
1200
+ // "date": 1435488248,
1201
+ // "type": "buy",
1202
+ // "pair": "BTC_USD",
1203
+ // "order_id": 12345,
1204
+ // "quantity": 1,
1205
+ // "price": 100,
1206
+ // "amount": 100
1207
+ // }
1208
+ // ]
1209
+ // }
1210
+ //
1211
+ let id = this.safeString (order, 'order_id');
1212
+ let timestamp = this.safeTimestamp (order, 'created');
1213
+ let symbol = undefined;
1214
+ const side = this.safeString (order, 'type');
1215
+ if (market === undefined) {
1216
+ let marketId = undefined;
1217
+ if ('pair' in order) {
1218
+ marketId = order['pair'];
1219
+ } else if (('in_currency' in order) && ('out_currency' in order)) {
1220
+ if (side === 'buy') {
1221
+ marketId = order['in_currency'] + '_' + order['out_currency'];
1222
+ } else {
1223
+ marketId = order['out_currency'] + '_' + order['in_currency'];
1224
+ }
1225
+ }
1226
+ if ((marketId !== undefined) && (marketId in this.markets_by_id)) {
1227
+ market = this.markets_by_id[marketId];
1228
+ }
1229
+ }
1230
+ let amount = this.safeNumber (order, 'quantity');
1231
+ if (amount === undefined) {
1232
+ const amountField = (side === 'buy') ? 'in_amount' : 'out_amount';
1233
+ amount = this.safeNumber (order, amountField);
1234
+ }
1235
+ let price = this.safeNumber (order, 'price');
1236
+ let cost = this.safeNumber (order, 'amount');
1237
+ let filled = 0.0;
1238
+ const trades = [];
1239
+ const transactions = this.safeValue (order, 'trades', []);
1240
+ let feeCost = undefined;
1241
+ let lastTradeTimestamp = undefined;
1242
+ let average = undefined;
1243
+ const numTransactions = transactions.length;
1244
+ if (numTransactions > 0) {
1245
+ feeCost = 0;
1246
+ for (let i = 0; i < numTransactions; i++) {
1247
+ const trade = this.parseTrade (transactions[i], market);
1248
+ if (id === undefined) {
1249
+ id = trade['order'];
1250
+ }
1251
+ if (timestamp === undefined) {
1252
+ timestamp = trade['timestamp'];
1253
+ }
1254
+ if (timestamp > trade['timestamp']) {
1255
+ timestamp = trade['timestamp'];
1256
+ }
1257
+ filled = this.sum (filled, trade['amount']);
1258
+ feeCost = this.sum (feeCost, trade['fee']['cost']);
1259
+ trades.push (trade);
1260
+ }
1261
+ lastTradeTimestamp = trades[numTransactions - 1]['timestamp'];
1262
+ }
1263
+ let status = this.safeString (order, 'status'); // in case we need to redefine it for canceled orders
1264
+ let remaining = undefined;
1265
+ if (amount !== undefined) {
1266
+ remaining = amount - filled;
1267
+ if (filled >= amount) {
1268
+ status = 'closed';
1269
+ } else {
1270
+ status = 'open';
1271
+ }
1272
+ }
1273
+ if (market === undefined) {
1274
+ market = this.getMarketFromTrades (trades);
1275
+ }
1276
+ let feeCurrency = undefined;
1277
+ if (market !== undefined) {
1278
+ symbol = market['symbol'];
1279
+ feeCurrency = market['quote'];
1280
+ }
1281
+ if (cost === undefined) {
1282
+ if (price !== undefined) {
1283
+ cost = price * filled;
1284
+ }
1285
+ } else {
1286
+ if (filled > 0) {
1287
+ if (average === undefined) {
1288
+ average = cost / filled;
1289
+ }
1290
+ if (price === undefined) {
1291
+ price = cost / filled;
1292
+ }
1293
+ }
1294
+ }
1295
+ const fee = {
1296
+ 'cost': feeCost,
1297
+ 'currency': feeCurrency,
1298
+ };
1299
+ const clientOrderId = this.safeInteger (order, 'client_id');
1300
+ return {
1301
+ 'id': id,
1302
+ 'clientOrderId': clientOrderId,
1303
+ 'datetime': this.iso8601 (timestamp),
1304
+ 'timestamp': timestamp,
1305
+ 'lastTradeTimestamp': lastTradeTimestamp,
1306
+ 'status': status,
1307
+ 'symbol': symbol,
1308
+ 'type': 'limit',
1309
+ 'timeInForce': undefined,
1310
+ 'postOnly': undefined,
1311
+ 'side': side,
1312
+ 'price': price,
1313
+ 'stopPrice': undefined,
1314
+ 'cost': cost,
1315
+ 'amount': amount,
1316
+ 'filled': filled,
1317
+ 'remaining': remaining,
1318
+ 'average': average,
1319
+ 'trades': trades,
1320
+ 'fee': fee,
1321
+ 'info': order,
1322
+ };
1323
+ }
1324
+
1325
+ async fetchDepositAddress (code, params = {}) {
1326
+ await this.loadMarkets ();
1327
+ const response = await this.privatePostDepositAddress (params);
1328
+ const depositAddress = this.safeString (response, code);
1329
+ let address = undefined;
1330
+ let tag = undefined;
1331
+ if (depositAddress) {
1332
+ const addressAndTag = depositAddress.split (',');
1333
+ address = addressAndTag[0];
1334
+ const numParts = addressAndTag.length;
1335
+ if (numParts > 1) {
1336
+ tag = addressAndTag[1];
1337
+ }
1338
+ }
1339
+ this.checkAddress (address);
1340
+ return {
1341
+ 'currency': code,
1342
+ 'address': address,
1343
+ 'tag': tag,
1344
+ 'network': undefined,
1345
+ 'info': response,
1346
+ };
1347
+ }
1348
+
1349
+ getMarketFromTrades (trades) {
1350
+ const tradesBySymbol = this.indexBy (trades, 'pair');
1351
+ const symbols = Object.keys (tradesBySymbol);
1352
+ const numSymbols = symbols.length;
1353
+ if (numSymbols === 1) {
1354
+ return this.markets[symbols[0]];
1355
+ }
1356
+ return undefined;
1357
+ }
1358
+
1359
+ async withdraw (code, amount, address, tag = undefined, params = {}) {
1360
+ [ tag, params ] = this.handleWithdrawTagAndParams (tag, params);
1361
+ await this.loadMarkets ();
1362
+ const currency = this.currency (code);
1363
+ const request = {
1364
+ 'amount': amount,
1365
+ 'currency': currency['id'],
1366
+ 'address': address,
1367
+ };
1368
+ if (tag !== undefined) {
1369
+ request['invoice'] = tag;
1370
+ }
1371
+ const networks = this.safeValue (this.options, 'networks', {});
1372
+ let network = this.safeStringUpper (params, 'network'); // this line allows the user to specify either ERC20 or ETH
1373
+ network = this.safeString (networks, network, network); // handle ERC20>ETH alias
1374
+ if (network !== undefined) {
1375
+ request['transport'] = network;
1376
+ params = this.omit (params, 'network');
1377
+ }
1378
+ const response = await this.privatePostWithdrawCrypt (this.extend (request, params));
1379
+ return this.parseTransaction (response, currency);
1380
+ }
1381
+
1382
+ parseTransactionStatus (status) {
1383
+ const statuses = {
1384
+ 'transferred': 'ok',
1385
+ 'paid': 'ok',
1386
+ 'pending': 'pending',
1387
+ 'processing': 'pending',
1388
+ 'verifying': 'pending',
1389
+ };
1390
+ return this.safeString (statuses, status, status);
1391
+ }
1392
+
1393
+ parseTransaction (transaction, currency = undefined) {
1394
+ //
1395
+ // fetchTransactions
1396
+ //
1397
+ // {
1398
+ // "dt": 1461841192,
1399
+ // "type": "deposit",
1400
+ // "curr": "RUB",
1401
+ // "status": "processing",
1402
+ // "provider": "Qiwi (LA) [12345]",
1403
+ // "amount": "1",
1404
+ // "account": "",
1405
+ // "txid": "ec46f784ad976fd7f7539089d1a129fe46...",
1406
+ // }
1407
+ //
1408
+ // fetchWithdrawals
1409
+ //
1410
+ // {
1411
+ // "operation_id": 47412538520634344,
1412
+ // "created": 1573760013,
1413
+ // "updated": 1573760013,
1414
+ // "type": "withdraw",
1415
+ // "currency": "DOGE",
1416
+ // "status": "Paid",
1417
+ // "amount": "300",
1418
+ // "provider": "DOGE",
1419
+ // "commission": "0",
1420
+ // "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
1421
+ // "order_id": 69670170,
1422
+ // "provider_type": "crypto",
1423
+ // "crypto_address": "DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
1424
+ // "card_number": "",
1425
+ // "wallet_address": "",
1426
+ // "email": "",
1427
+ // "phone": "",
1428
+ // "extra": {
1429
+ // "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
1430
+ // "confirmations": null,
1431
+ // "excode": "",
1432
+ // "invoice": ""
1433
+ // },
1434
+ // "error": ""
1435
+ // },
1436
+ //
1437
+ const id = this.safeString2 (transaction, 'order_id', 'task_id');
1438
+ const timestamp = this.safeTimestamp2 (transaction, 'dt', 'created');
1439
+ const updated = this.safeTimestamp (transaction, 'updated');
1440
+ let amount = this.safeNumber (transaction, 'amount');
1441
+ if (amount !== undefined) {
1442
+ amount = Math.abs (amount);
1443
+ }
1444
+ const status = this.parseTransactionStatus (this.safeStringLower (transaction, 'status'));
1445
+ let txid = this.safeString (transaction, 'txid');
1446
+ if (txid === undefined) {
1447
+ const extra = this.safeValue (transaction, 'extra', {});
1448
+ const extraTxid = this.safeString (extra, 'txid');
1449
+ if (extraTxid !== '') {
1450
+ txid = extraTxid;
1451
+ }
1452
+ }
1453
+ const type = this.safeString (transaction, 'type');
1454
+ const currencyId = this.safeString2 (transaction, 'curr', 'currency');
1455
+ const code = this.safeCurrencyCode (currencyId, currency);
1456
+ let address = undefined;
1457
+ const tag = undefined;
1458
+ let comment = undefined;
1459
+ const account = this.safeString (transaction, 'account');
1460
+ if (type === 'deposit') {
1461
+ comment = account;
1462
+ } else if (type === 'withdrawal') {
1463
+ address = account;
1464
+ if (address !== undefined) {
1465
+ const parts = address.split (':');
1466
+ const numParts = parts.length;
1467
+ if (numParts === 2) {
1468
+ address = this.safeString (parts, 1);
1469
+ address = address.replace (' ', '');
1470
+ }
1471
+ }
1472
+ }
1473
+ let fee = undefined;
1474
+ // fixed funding fees only (for now)
1475
+ if (!this.fees['funding']['percentage']) {
1476
+ const key = (type === 'withdrawal') ? 'withdraw' : 'deposit';
1477
+ let feeCost = this.safeNumber (transaction, 'commission');
1478
+ if (feeCost === undefined) {
1479
+ feeCost = this.safeNumber (this.options['fundingFees'][key], code);
1480
+ }
1481
+ // users don't pay for cashbacks, no fees for that
1482
+ const provider = this.safeString (transaction, 'provider');
1483
+ if (provider === 'cashback') {
1484
+ feeCost = 0;
1485
+ }
1486
+ if (feeCost !== undefined) {
1487
+ // withdrawal amount includes the fee
1488
+ if (type === 'withdrawal') {
1489
+ amount = amount - feeCost;
1490
+ }
1491
+ fee = {
1492
+ 'cost': feeCost,
1493
+ 'currency': code,
1494
+ 'rate': undefined,
1495
+ };
1496
+ }
1497
+ }
1498
+ const network = this.safeString (transaction, 'provider');
1499
+ return {
1500
+ 'info': transaction,
1501
+ 'id': id,
1502
+ 'timestamp': timestamp,
1503
+ 'datetime': this.iso8601 (timestamp),
1504
+ 'currency': code,
1505
+ 'amount': amount,
1506
+ 'network': network,
1507
+ 'address': address,
1508
+ 'addressTo': address,
1509
+ 'addressFrom': undefined,
1510
+ 'tag': tag,
1511
+ 'tagTo': tag,
1512
+ 'tagFrom': undefined,
1513
+ 'status': status,
1514
+ 'type': type,
1515
+ 'updated': updated,
1516
+ 'comment': comment,
1517
+ 'txid': txid,
1518
+ 'fee': fee,
1519
+ };
1520
+ }
1521
+
1522
+ async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) {
1523
+ await this.loadMarkets ();
1524
+ const request = {};
1525
+ if (since !== undefined) {
1526
+ request['date'] = parseInt (since / 1000);
1527
+ }
1528
+ let currency = undefined;
1529
+ if (code !== undefined) {
1530
+ currency = this.currency (code);
1531
+ }
1532
+ const response = await this.privatePostWalletHistory (this.extend (request, params));
1533
+ //
1534
+ // {
1535
+ // "result": true,
1536
+ // "error": "",
1537
+ // "begin": "1493942400",
1538
+ // "end": "1494028800",
1539
+ // "history": [
1540
+ // {
1541
+ // "dt": 1461841192,
1542
+ // "type": "deposit",
1543
+ // "curr": "RUB",
1544
+ // "status": "processing",
1545
+ // "provider": "Qiwi (LA) [12345]",
1546
+ // "amount": "1",
1547
+ // "account": "",
1548
+ // "txid": "ec46f784ad976fd7f7539089d1a129fe46...",
1549
+ // },
1550
+ // {
1551
+ // "dt": 1463414785,
1552
+ // "type": "withdrawal",
1553
+ // "curr": "USD",
1554
+ // "status": "paid",
1555
+ // "provider": "EXCODE",
1556
+ // "amount": "-1",
1557
+ // "account": "EX-CODE_19371_USDda...",
1558
+ // "txid": "",
1559
+ // },
1560
+ // ],
1561
+ // }
1562
+ //
1563
+ return this.parseTransactions (response['history'], currency, since, limit);
1564
+ }
1565
+
1566
+ async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
1567
+ await this.loadMarkets ();
1568
+ let currency = undefined;
1569
+ const request = {
1570
+ 'type': 'withdraw',
1571
+ };
1572
+ if (limit !== undefined) {
1573
+ request['limit'] = limit; // default: 100, maximum: 100
1574
+ }
1575
+ if (code !== undefined) {
1576
+ currency = this.currency (code);
1577
+ request['currency'] = currency['id'];
1578
+ }
1579
+ const response = await this.privatePostWalletOperations (this.extend (request, params));
1580
+ //
1581
+ // {
1582
+ // "items": [
1583
+ // {
1584
+ // "operation_id": 47412538520634344,
1585
+ // "created": 1573760013,
1586
+ // "updated": 1573760013,
1587
+ // "type": "withdraw",
1588
+ // "currency": "DOGE",
1589
+ // "status": "Paid",
1590
+ // "amount": "300",
1591
+ // "provider": "DOGE",
1592
+ // "commission": "0",
1593
+ // "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
1594
+ // "order_id": 69670170,
1595
+ // "extra": {
1596
+ // "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
1597
+ // "excode": "",
1598
+ // "invoice": ""
1599
+ // },
1600
+ // "error": ""
1601
+ // },
1602
+ // ],
1603
+ // "count": 23
1604
+ // }
1605
+ //
1606
+ return this.parseTransactions (response['items'], currency, since, limit);
1607
+ }
1608
+
1609
+ sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
1610
+ let url = this.urls['api'][api] + '/';
1611
+ if (api !== 'web') {
1612
+ url += this.version + '/';
1613
+ }
1614
+ url += path;
1615
+ if ((api === 'public') || (api === 'web')) {
1616
+ if (Object.keys (params).length) {
1617
+ url += '?' + this.urlencode (params);
1618
+ }
1619
+ } else if (api === 'private') {
1620
+ this.checkRequiredCredentials ();
1621
+ const nonce = this.nonce ();
1622
+ body = this.urlencode (this.extend ({ 'nonce': nonce }, params));
1623
+ headers = {
1624
+ 'Content-Type': 'application/x-www-form-urlencoded',
1625
+ 'Key': this.apiKey,
1626
+ 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'),
1627
+ };
1628
+ }
1629
+ return { 'url': url, 'method': method, 'body': body, 'headers': headers };
1630
+ }
1631
+
1632
+ nonce () {
1633
+ return this.milliseconds ();
1634
+ }
1635
+
1636
+ handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
1637
+ if (response === undefined) {
1638
+ return; // fallback to default error handler
1639
+ }
1640
+ if (('result' in response) || ('errmsg' in response)) {
1641
+ //
1642
+ // {"result":false,"error":"Error 50052: Insufficient funds"}
1643
+ // {"s":"error","errmsg":"strconv.ParseInt: parsing \"\": invalid syntax"}
1644
+ //
1645
+ let success = this.safeValue (response, 'result', false);
1646
+ if (typeof success === 'string') {
1647
+ if ((success === 'true') || (success === '1')) {
1648
+ success = true;
1649
+ } else {
1650
+ success = false;
1651
+ }
1652
+ }
1653
+ if (!success) {
1654
+ let code = undefined;
1655
+ const message = this.safeString2 (response, 'error', 'errmsg');
1656
+ const errorParts = message.split (':');
1657
+ const numParts = errorParts.length;
1658
+ if (numParts > 1) {
1659
+ const errorSubParts = errorParts[0].split (' ');
1660
+ const numSubParts = errorSubParts.length;
1661
+ code = (numSubParts > 1) ? errorSubParts[1] : errorSubParts[0];
1662
+ }
1663
+ const feedback = this.id + ' ' + body;
1664
+ this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback);
1665
+ this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback);
1666
+ throw new ExchangeError (feedback);
1667
+ }
1668
+ }
1669
+ }
1670
+ };