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/liquid.js ADDED
@@ -0,0 +1,1432 @@
1
+ 'use strict';
2
+
3
+ // ---------------------------------------------------------------------------
4
+
5
+ const Exchange = require ('./base/Exchange');
6
+ const { TICK_SIZE } = require ('./base/functions/number');
7
+ const { ExchangeError, ArgumentsRequired, InvalidNonce, OrderNotFound, InvalidOrder, InsufficientFunds, AuthenticationError, DDoSProtection, NotSupported, BadSymbol } = require ('./base/errors');
8
+ const Precise = require ('./base/Precise');
9
+
10
+ // ---------------------------------------------------------------------------
11
+
12
+ module.exports = class liquid extends Exchange {
13
+ describe () {
14
+ return this.deepExtend (super.describe (), {
15
+ 'id': 'liquid',
16
+ 'name': 'Liquid',
17
+ 'countries': [ 'JP', 'CN', 'TW' ],
18
+ 'version': '2',
19
+ 'rateLimit': 1000,
20
+ 'has': {
21
+ 'CORS': undefined,
22
+ 'spot': true,
23
+ 'margin': undefined, // has but not fully implemented
24
+ 'swap': undefined, // has but not fully implemented
25
+ 'future': false,
26
+ 'option': false,
27
+ 'cancelOrder': true,
28
+ 'createOrder': true,
29
+ 'editOrder': true,
30
+ 'fetchBalance': true,
31
+ 'fetchClosedOrders': true,
32
+ 'fetchCurrencies': true,
33
+ 'fetchMarkets': true,
34
+ 'fetchMyTrades': true,
35
+ 'fetchOpenOrders': true,
36
+ 'fetchOrder': true,
37
+ 'fetchOrderBook': true,
38
+ 'fetchOrders': true,
39
+ 'fetchTicker': true,
40
+ 'fetchTickers': true,
41
+ 'fetchTrades': true,
42
+ 'fetchTradingFee': true,
43
+ 'fetchTradingFees': true,
44
+ 'fetchWithdrawals': true,
45
+ 'transfer': false,
46
+ 'withdraw': true,
47
+ },
48
+ 'urls': {
49
+ 'logo': 'https://user-images.githubusercontent.com/1294454/45798859-1a872600-bcb4-11e8-8746-69291ce87b04.jpg',
50
+ 'api': 'https://api.liquid.com',
51
+ 'www': 'https://www.liquid.com',
52
+ 'doc': [
53
+ 'https://developers.liquid.com',
54
+ ],
55
+ 'fees': 'https://help.liquid.com/getting-started-with-liquid/the-platform/fee-structure',
56
+ 'referral': 'https://www.liquid.com/sign-up/?affiliate=SbzC62lt30976',
57
+ },
58
+ 'api': {
59
+ 'public': {
60
+ 'get': [
61
+ 'currencies',
62
+ 'products',
63
+ 'products/{id}',
64
+ 'products/{id}/price_levels',
65
+ 'executions',
66
+ 'ir_ladders/{currency}',
67
+ 'fees', // add fetchFees, fetchTradingFees, fetchFundingFees
68
+ ],
69
+ },
70
+ 'private': {
71
+ 'get': [
72
+ 'accounts', // undocumented https://github.com/ccxt/ccxt/pull/7493
73
+ 'accounts/balance',
74
+ 'accounts/main_asset',
75
+ 'accounts/{id}',
76
+ 'accounts/{currency}/reserved_balance_details',
77
+ 'crypto_accounts', // add fetchAccounts
78
+ 'crypto_withdrawal',
79
+ 'crypto_withdrawals',
80
+ 'crypto_withdrawals/crypto_networks',
81
+ 'executions/me',
82
+ 'fiat_accounts', // add fetchAccounts
83
+ 'fund_infos', // add fetchDeposits
84
+ 'loan_bids',
85
+ 'loans',
86
+ 'orders',
87
+ 'orders/{id}',
88
+ 'orders/{id}/trades', // add fetchOrderTrades
89
+ 'trades',
90
+ 'trades/{id}/loans',
91
+ 'trading_accounts',
92
+ 'trading_accounts/{id}',
93
+ 'transactions',
94
+ 'withdrawals', // add fetchWithdrawals
95
+ 'user/fee_tier',
96
+ 'user/fees',
97
+ 'trading_accounts/{id}',
98
+ 'bank_accounts',
99
+ 'accounts/{currency}/reserved_balance_details',
100
+ ],
101
+ 'post': [
102
+ 'crypto_withdrawals',
103
+ 'fund_infos',
104
+ 'fiat_accounts',
105
+ 'loan_bids',
106
+ 'orders',
107
+ 'withdrawals',
108
+ 'fees/estimate',
109
+ ],
110
+ 'put': [
111
+ 'crypto_withdrawal/{id}/cancel',
112
+ 'loan_bids/{id}/close',
113
+ 'loans/{id}',
114
+ 'orders/{id}', // add editOrder
115
+ 'orders/{id}/cancel',
116
+ 'trades/{id}',
117
+ 'trades/{id}/adjust_margin',
118
+ 'trades/{id}/close',
119
+ 'trades/close_all',
120
+ 'trading_accounts/{id}',
121
+ 'withdrawals/{id}/cancel',
122
+ ],
123
+ },
124
+ },
125
+ 'fees': {
126
+ 'trading': {
127
+ 'tierBased': true,
128
+ 'percentage': true,
129
+ 'taker': 0.0030,
130
+ 'maker': 0.0000,
131
+ 'tiers': {
132
+ 'perpetual': {
133
+ 'maker': [
134
+ [ 0, 0.0000 ],
135
+ [ 25000, 0.0000 ],
136
+ [ 50000, -0.00025 ],
137
+ [ 100000, -0.00025 ],
138
+ [ 1000000, -0.00025 ],
139
+ [ 10000000, -0.00025 ],
140
+ [ 25000000, -0.00025 ],
141
+ [ 50000000, -0.00025 ],
142
+ [ 75000000, -0.00025 ],
143
+ [ 100000000, -0.00025 ],
144
+ [ 200000000, -0.00025 ],
145
+ [ 300000000, -0.00025 ],
146
+ ],
147
+ 'taker': [
148
+ [ 0, 0.00120 ],
149
+ [ 25000, 0.00115 ],
150
+ [ 50000, 0.00110 ],
151
+ [ 100000, 0.00105 ],
152
+ [ 1000000, 0.00100 ],
153
+ [ 10000000, 0.00095 ],
154
+ [ 25000000, 0.00090 ],
155
+ [ 50000000, 0.00085 ],
156
+ [ 75000000, 0.00080 ],
157
+ [ 100000000, 0.00075 ],
158
+ [ 200000000, 0.00070 ],
159
+ [ 300000000, 0.00065 ],
160
+ ],
161
+ },
162
+ 'spot': {
163
+ 'taker': [
164
+ [ 0, 0.003 ],
165
+ [ 10000, 0.0029 ],
166
+ [ 20000, 0.0028 ],
167
+ [ 50000, 0.0026 ],
168
+ [ 100000, 0.0020 ],
169
+ [ 1000000, 0.0016 ],
170
+ [ 5000000, 0.0012 ],
171
+ [ 10000000, 0.0010 ],
172
+ [ 25000000, 0.0009 ],
173
+ [ 50000000, 0.0008 ],
174
+ [ 100000000, 0.0007 ],
175
+ [ 200000000, 0.0006 ],
176
+ [ 500000000, 0.0004 ],
177
+ [ 1000000000, 0.0003 ],
178
+ ],
179
+ 'maker': [
180
+ [ 0, 0.0000 ],
181
+ [ 10000, 0.0020 ],
182
+ [ 20000, 0.0019 ],
183
+ [ 50000, 0.0018 ],
184
+ [ 100000, 0.0016 ],
185
+ [ 1000000, 0.0008 ],
186
+ [ 5000000, 0.0007 ],
187
+ [ 10000000, 0.0005 ],
188
+ [ 25000000, 0.0000 ],
189
+ [ 50000000, 0.0000 ],
190
+ [ 100000000, 0.0000 ],
191
+ [ 200000000, 0.0000 ],
192
+ [ 500000000, 0.0000 ],
193
+ [ 1000000000, 0.0000 ],
194
+ ],
195
+ },
196
+ },
197
+ },
198
+ },
199
+ 'precisionMode': TICK_SIZE,
200
+ 'exceptions': {
201
+ 'API rate limit exceeded. Please retry after 300s': DDoSProtection,
202
+ 'API Authentication failed': AuthenticationError,
203
+ 'Nonce is too small': InvalidNonce,
204
+ 'Order not found': OrderNotFound,
205
+ 'Can not update partially filled order': InvalidOrder,
206
+ 'Can not update non-live order': OrderNotFound,
207
+ 'not_enough_free_balance': InsufficientFunds,
208
+ 'must_be_positive': InvalidOrder,
209
+ 'less_than_order_size': InvalidOrder,
210
+ 'price_too_high': InvalidOrder,
211
+ 'price_too_small': InvalidOrder, // {"errors":{"order":["price_too_small"]}}
212
+ 'product_disabled': BadSymbol, // {"errors":{"order":["product_disabled"]}}
213
+ },
214
+ 'commonCurrencies': {
215
+ 'BIFI': 'BIFIF',
216
+ 'HOT': 'HOT Token',
217
+ 'MIOTA': 'IOTA', // https://github.com/ccxt/ccxt/issues/7487
218
+ 'P-BTC': 'BTC',
219
+ 'TON': 'Tokamak Network',
220
+ },
221
+ 'options': {
222
+ 'cancelOrderException': true,
223
+ 'networks': {
224
+ 'ETH': 'ERC20',
225
+ 'TRX': 'TRC20',
226
+ 'XLM': 'Stellar',
227
+ 'ALGO': 'Algorand',
228
+ },
229
+ 'swap': {
230
+ 'fetchMarkets': {
231
+ 'settlementCurrencies': [ 'BTC', 'ETH', 'XRP', 'QASH', 'USD', 'JPY', 'EUR', 'SGD', 'AUD' ],
232
+ },
233
+ },
234
+ },
235
+ });
236
+ }
237
+
238
+ async fetchCurrencies (params = {}) {
239
+ const response = await this.publicGetCurrencies (params);
240
+ //
241
+ // [
242
+ // {
243
+ // currency_type: 'fiat',
244
+ // currency: 'USD',
245
+ // symbol: '$',
246
+ // assets_precision: 2,
247
+ // quoting_precision: 5,
248
+ // minimum_withdrawal: '15.0',
249
+ // withdrawal_fee: 5,
250
+ // minimum_fee: null,
251
+ // minimum_order_quantity: null,
252
+ // display_precision: 2,
253
+ // depositable: true,
254
+ // withdrawable: true,
255
+ // discount_fee: 0.5,
256
+ // credit_card_fundable: false,
257
+ // lendable: false,
258
+ // position_fundable: true,
259
+ // has_memo: false,
260
+ // stable_currency: null,
261
+ // root_currency: 'USD',
262
+ // minimum_loan_bid_quantity: '0.0',
263
+ // maximum_order_taker_quantity: null,
264
+ // name: 'United States Dollar'
265
+ // },
266
+ // ]
267
+ //
268
+ const result = {};
269
+ for (let i = 0; i < response.length; i++) {
270
+ const currency = response[i];
271
+ const id = this.safeString (currency, 'currency');
272
+ const code = this.safeCurrencyCode (id);
273
+ const name = this.safeString (currency, 'name');
274
+ const depositable = this.safeValue (currency, 'depositable');
275
+ const withdrawable = this.safeValue (currency, 'withdrawable');
276
+ const active = depositable && withdrawable;
277
+ const amountPrecision = this.safeInteger (currency, 'assets_precision');
278
+ result[code] = {
279
+ 'id': id,
280
+ 'code': code,
281
+ 'info': currency,
282
+ 'name': name,
283
+ 'active': active,
284
+ 'deposit': depositable,
285
+ 'withdraw': withdrawable,
286
+ 'fee': this.safeNumber (currency, 'withdrawal_fee'),
287
+ 'precision': amountPrecision,
288
+ 'limits': {
289
+ 'amount': {
290
+ 'min': Math.pow (10, -amountPrecision),
291
+ 'max': Math.pow (10, amountPrecision),
292
+ },
293
+ 'withdraw': {
294
+ 'min': this.safeNumber (currency, 'minimum_withdrawal'),
295
+ 'max': undefined,
296
+ },
297
+ },
298
+ };
299
+ }
300
+ return result;
301
+ }
302
+
303
+ async fetchMarkets (params = {}) {
304
+ const spot = await this.publicGetProducts (params);
305
+ //
306
+ // [
307
+ // {
308
+ // "id":"637",
309
+ // "product_type":"CurrencyPair",
310
+ // "code":"CASH",
311
+ // "name":null,
312
+ // "market_ask":"0.00000797",
313
+ // "market_bid":"0.00000727",
314
+ // "indicator":null,
315
+ // "currency":"BTC",
316
+ // "currency_pair_code":"TFTBTC",
317
+ // "symbol":null,
318
+ // "btc_minimum_withdraw":null,
319
+ // "fiat_minimum_withdraw":null,
320
+ // "pusher_channel":"product_cash_tftbtc_637",
321
+ // "taker_fee":"0.0",
322
+ // "maker_fee":"0.0",
323
+ // "low_market_bid":"0.00000685",
324
+ // "high_market_ask":"0.00000885",
325
+ // "volume_24h":"3696.0755956",
326
+ // "last_price_24h":"0.00000716",
327
+ // "last_traded_price":"0.00000766",
328
+ // "last_traded_quantity":"1748.0377978",
329
+ // "average_price":null,
330
+ // "quoted_currency":"BTC",
331
+ // "base_currency":"TFT",
332
+ // "tick_size":"0.00000001",
333
+ // "disabled":false,
334
+ // "margin_enabled":false,
335
+ // "cfd_enabled":false,
336
+ // "perpetual_enabled":false,
337
+ // "last_event_timestamp":"1596962820.000797146",
338
+ // "timestamp":"1596962820.000797146",
339
+ // "multiplier_up":"9.0",
340
+ // "multiplier_down":"0.1",
341
+ // "average_time_interval":null
342
+ // },
343
+ // ]
344
+ //
345
+ const perpetual = await this.publicGetProducts ({ 'perpetual': '1' });
346
+ //
347
+ // [
348
+ // {
349
+ // "id":"604",
350
+ // "product_type":"Perpetual",
351
+ // "code":"CASH",
352
+ // "name":null,
353
+ // "market_ask":"11721.5",
354
+ // "market_bid":"11719.0",
355
+ // "indicator":null,
356
+ // "currency":"USD",
357
+ // "currency_pair_code":"P-BTCUSD",
358
+ // "symbol":"$",
359
+ // "btc_minimum_withdraw":null,
360
+ // "fiat_minimum_withdraw":null,
361
+ // "pusher_channel":"product_cash_p-btcusd_604",
362
+ // "taker_fee":"0.0012",
363
+ // "maker_fee":"0.0",
364
+ // "low_market_bid":"11624.5",
365
+ // "high_market_ask":"11859.0",
366
+ // "volume_24h":"0.271",
367
+ // "last_price_24h":"11621.5",
368
+ // "last_traded_price":"11771.5",
369
+ // "last_traded_quantity":"0.09",
370
+ // "average_price":"11771.5",
371
+ // "quoted_currency":"USD",
372
+ // "base_currency":"P-BTC",
373
+ // "tick_size":"0.5",
374
+ // "disabled":false,
375
+ // "margin_enabled":false,
376
+ // "cfd_enabled":false,
377
+ // "perpetual_enabled":true,
378
+ // "last_event_timestamp":"1596963309.418853092",
379
+ // "timestamp":"1596963309.418853092",
380
+ // "multiplier_up":null,
381
+ // "multiplier_down":"0.1",
382
+ // "average_time_interval":300,
383
+ // "index_price":"11682.8124",
384
+ // "mark_price":"11719.96781",
385
+ // "funding_rate":"0.00273",
386
+ // "fair_price":"11720.2745"
387
+ // },
388
+ // ]
389
+ //
390
+ const currencies = await this.fetchCurrencies ();
391
+ const currenciesByCode = this.indexBy (currencies, 'code');
392
+ const result = [];
393
+ const markets = this.arrayConcat (spot, perpetual);
394
+ for (let i = 0; i < markets.length; i++) {
395
+ const market = markets[i];
396
+ const id = this.safeString (market, 'id');
397
+ const baseId = this.safeString (market, 'base_currency');
398
+ const quoteId = this.safeString (market, 'quoted_currency');
399
+ const productType = this.safeString (market, 'product_type');
400
+ const swap = (productType === 'Perpetual');
401
+ const type = swap ? 'swap' : 'spot';
402
+ const spot = !swap;
403
+ const base = this.safeCurrencyCode (baseId);
404
+ const quote = this.safeCurrencyCode (quoteId);
405
+ const disabled = this.safeValue (market, 'disabled', false);
406
+ const baseCurrency = this.safeValue (currenciesByCode, base);
407
+ let minAmount = undefined;
408
+ if (baseCurrency !== undefined) {
409
+ minAmount = this.safeNumber (baseCurrency['info'], 'minimum_order_quantity');
410
+ }
411
+ const lastPrice = this.safeNumber (market, 'last_traded_price');
412
+ let minPrice = undefined;
413
+ let maxPrice = undefined;
414
+ if (lastPrice) {
415
+ const multiplierDown = this.safeNumber (market, 'multiplier_down');
416
+ const multiplierUp = this.safeNumber (market, 'multiplier_up');
417
+ if (multiplierDown !== undefined) {
418
+ minPrice = lastPrice * multiplierDown;
419
+ }
420
+ if (multiplierUp !== undefined) {
421
+ maxPrice = lastPrice * multiplierUp;
422
+ }
423
+ }
424
+ const margin = this.safeValue (market, 'margin_enabled');
425
+ const symbol = base + '/' + quote;
426
+ const maker = this.fees['trading']['maker'];
427
+ const taker = this.fees['trading']['taker'];
428
+ const parsedMarket = {
429
+ 'id': id,
430
+ 'symbol': symbol,
431
+ 'base': base,
432
+ 'quote': quote,
433
+ 'settle': undefined,
434
+ 'baseId': baseId,
435
+ 'quoteId': quoteId,
436
+ 'settleId': undefined,
437
+ 'type': type,
438
+ 'spot': spot,
439
+ 'margin': spot && margin,
440
+ 'swap': swap,
441
+ 'future': false,
442
+ 'option': false,
443
+ 'active': !disabled,
444
+ 'contract': swap,
445
+ 'linear': undefined,
446
+ 'inverse': undefined,
447
+ 'taker': taker,
448
+ 'maker': maker,
449
+ 'contractSize': undefined,
450
+ 'expiry': undefined,
451
+ 'expiryDatetime': undefined,
452
+ 'strike': undefined,
453
+ 'optionType': undefined,
454
+ 'precision': {
455
+ 'amount': this.parseNumber ('0.00000001'),
456
+ 'price': this.safeNumber (market, 'tick_size'),
457
+ },
458
+ 'limits': {
459
+ 'leverage': {
460
+ 'min': undefined,
461
+ 'max': undefined,
462
+ },
463
+ 'amount': {
464
+ 'min': minAmount,
465
+ 'max': undefined,
466
+ },
467
+ 'price': {
468
+ 'min': minPrice,
469
+ 'max': maxPrice,
470
+ },
471
+ 'cost': {
472
+ 'min': undefined,
473
+ 'max': undefined,
474
+ },
475
+ },
476
+ 'info': market,
477
+ };
478
+ if (swap) {
479
+ const settlementCurrencies = this.options['fetchMarkets']['settlementCurrencies'];
480
+ for (let i = 0; i < settlementCurrencies.length; i++) {
481
+ const settle = settlementCurrencies[i];
482
+ parsedMarket['settle'] = settle;
483
+ parsedMarket['symbol'] = symbol + ':' + settle;
484
+ parsedMarket['linear'] = quote === settle;
485
+ parsedMarket['inverse'] = base === settle;
486
+ parsedMarket['taker'] = this.safeNumber (market, 'taker_fee', taker);
487
+ parsedMarket['maker'] = this.safeNumber (market, 'maker_fee', maker);
488
+ parsedMarket['contractSize'] = this.parseNumber ('1');
489
+ result.push (parsedMarket);
490
+ }
491
+ } else {
492
+ result.push (parsedMarket);
493
+ }
494
+ }
495
+ return result;
496
+ }
497
+
498
+ parseBalance (response) {
499
+ const result = {
500
+ 'info': response,
501
+ 'timestamp': undefined,
502
+ 'datetime': undefined,
503
+ };
504
+ const crypto = this.safeValue (response, 'crypto_accounts', []);
505
+ const fiat = this.safeValue (response, 'fiat_accounts', []);
506
+ for (let i = 0; i < crypto.length; i++) {
507
+ const balance = crypto[i];
508
+ const currencyId = this.safeString (balance, 'currency');
509
+ const code = this.safeCurrencyCode (currencyId);
510
+ const account = this.account ();
511
+ account['total'] = this.safeString (balance, 'balance');
512
+ account['used'] = this.safeString (balance, 'reserved_balance');
513
+ result[code] = account;
514
+ }
515
+ for (let i = 0; i < fiat.length; i++) {
516
+ const balance = fiat[i];
517
+ const currencyId = this.safeString (balance, 'currency');
518
+ const code = this.safeCurrencyCode (currencyId);
519
+ const account = this.account ();
520
+ account['total'] = this.safeString (balance, 'balance');
521
+ account['used'] = this.safeString (balance, 'reserved_balance');
522
+ result[code] = account;
523
+ }
524
+ return this.safeBalance (result);
525
+ }
526
+
527
+ async fetchBalance (params = {}) {
528
+ await this.loadMarkets ();
529
+ const response = await this.privateGetAccounts (params);
530
+ //
531
+ // {
532
+ // crypto_accounts: [
533
+ // {
534
+ // id: 2221179,
535
+ // currency: 'USDT',
536
+ // balance: '0.0',
537
+ // reserved_balance: '0.0',
538
+ // pusher_channel: 'user_xxxxx_account_usdt',
539
+ // lowest_offer_interest_rate: null,
540
+ // highest_offer_interest_rate: null,
541
+ // address: '0',
542
+ // currency_symbol: 'USDT',
543
+ // minimum_withdraw: null,
544
+ // currency_type: 'crypto'
545
+ // },
546
+ // ],
547
+ // fiat_accounts: [
548
+ // {
549
+ // id: 1112734,
550
+ // currency: 'USD',
551
+ // balance: '0.0',
552
+ // reserved_balance: '0.0',
553
+ // pusher_channel: 'user_xxxxx_account_usd',
554
+ // lowest_offer_interest_rate: null,
555
+ // highest_offer_interest_rate: null,
556
+ // currency_symbol: '$',
557
+ // send_to_btc_address: null,
558
+ // exchange_rate: '1.0',
559
+ // currency_type: 'fiat'
560
+ // }
561
+ // ]
562
+ // }
563
+ //
564
+ return this.parseBalance (response);
565
+ }
566
+
567
+ async fetchOrderBook (symbol, limit = undefined, params = {}) {
568
+ await this.loadMarkets ();
569
+ const request = {
570
+ 'id': this.marketId (symbol),
571
+ };
572
+ const response = await this.publicGetProductsIdPriceLevels (this.extend (request, params));
573
+ return this.parseOrderBook (response, symbol, undefined, 'buy_price_levels', 'sell_price_levels');
574
+ }
575
+
576
+ parseTicker (ticker, market = undefined) {
577
+ const timestamp = this.milliseconds ();
578
+ let last = undefined;
579
+ if ('last_traded_price' in ticker) {
580
+ if (ticker['last_traded_price']) {
581
+ const length = ticker['last_traded_price'].length;
582
+ if (length > 0) {
583
+ last = this.safeString (ticker, 'last_traded_price');
584
+ }
585
+ }
586
+ }
587
+ const marketId = this.safeString (ticker, 'id');
588
+ market = this.safeMarket (marketId, market);
589
+ let symbol = market['symbol'];
590
+ const baseId = this.safeString (ticker, 'base_currency');
591
+ const quoteId = this.safeString (ticker, 'quoted_currency');
592
+ if ((baseId !== undefined) && (quoteId !== undefined)) {
593
+ symbol = this.safeCurrencyCode (baseId) + '/' + this.safeCurrencyCode (quoteId);
594
+ }
595
+ const open = this.safeString (ticker, 'last_price_24h');
596
+ return this.safeTicker ({
597
+ 'symbol': symbol,
598
+ 'timestamp': timestamp,
599
+ 'datetime': this.iso8601 (timestamp),
600
+ 'high': this.safeString (ticker, 'high_market_ask'),
601
+ 'low': this.safeString (ticker, 'low_market_bid'),
602
+ 'bid': this.safeString (ticker, 'market_bid'),
603
+ 'bidVolume': undefined,
604
+ 'ask': this.safeString (ticker, 'market_ask'),
605
+ 'askVolume': undefined,
606
+ 'vwap': undefined,
607
+ 'open': open,
608
+ 'close': last,
609
+ 'last': last,
610
+ 'previousClose': undefined,
611
+ 'change': undefined,
612
+ 'percentage': undefined,
613
+ 'average': undefined,
614
+ 'baseVolume': this.safeString (ticker, 'volume_24h'),
615
+ 'quoteVolume': undefined,
616
+ 'info': ticker,
617
+ }, market, false);
618
+ }
619
+
620
+ async fetchTickers (symbols = undefined, params = {}) {
621
+ await this.loadMarkets ();
622
+ const response = await this.publicGetProducts (params);
623
+ const result = {};
624
+ for (let i = 0; i < response.length; i++) {
625
+ const ticker = this.parseTicker (response[i]);
626
+ const symbol = ticker['symbol'];
627
+ result[symbol] = ticker;
628
+ }
629
+ return this.filterByArray (result, 'symbol', symbols);
630
+ }
631
+
632
+ async fetchTicker (symbol, params = {}) {
633
+ await this.loadMarkets ();
634
+ const market = this.market (symbol);
635
+ const request = {
636
+ 'id': market['id'],
637
+ };
638
+ const response = await this.publicGetProductsId (this.extend (request, params));
639
+ return this.parseTicker (response, market);
640
+ }
641
+
642
+ parseTrade (trade, market = undefined) {
643
+ // { id: 12345,
644
+ // quantity: "6.789",
645
+ // price: "98765.4321",
646
+ // taker_side: "sell",
647
+ // created_at: 1512345678,
648
+ // my_side: "buy" }
649
+ const timestamp = this.safeTimestamp (trade, 'created_at');
650
+ const orderId = this.safeString (trade, 'order_id');
651
+ // 'taker_side' gets filled for both fetchTrades and fetchMyTrades
652
+ const takerSide = this.safeString (trade, 'taker_side');
653
+ // 'my_side' gets filled for fetchMyTrades only and may differ from 'taker_side'
654
+ const mySide = this.safeString (trade, 'my_side');
655
+ const side = (mySide !== undefined) ? mySide : takerSide;
656
+ let takerOrMaker = undefined;
657
+ if (mySide !== undefined) {
658
+ takerOrMaker = (takerSide === mySide) ? 'taker' : 'maker';
659
+ }
660
+ const price = this.safeString (trade, 'price');
661
+ const amount = this.safeString (trade, 'quantity');
662
+ const id = this.safeString (trade, 'id');
663
+ market = this.safeMarket (undefined, market);
664
+ return this.safeTrade ({
665
+ 'info': trade,
666
+ 'id': id,
667
+ 'order': orderId,
668
+ 'timestamp': timestamp,
669
+ 'datetime': this.iso8601 (timestamp),
670
+ 'symbol': market['symbol'],
671
+ 'type': undefined,
672
+ 'side': side,
673
+ 'takerOrMaker': takerOrMaker,
674
+ 'price': price,
675
+ 'amount': amount,
676
+ 'cost': undefined,
677
+ 'fee': undefined,
678
+ }, market);
679
+ }
680
+
681
+ async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
682
+ await this.loadMarkets ();
683
+ const market = this.market (symbol);
684
+ const request = {
685
+ 'product_id': market['id'],
686
+ };
687
+ if (limit !== undefined) {
688
+ request['limit'] = limit;
689
+ }
690
+ if (since !== undefined) {
691
+ // timestamp should be in seconds, whereas we use milliseconds in since and everywhere
692
+ request['timestamp'] = parseInt (since / 1000);
693
+ }
694
+ const response = await this.publicGetExecutions (this.extend (request, params));
695
+ const result = (since !== undefined) ? response : response['models'];
696
+ return this.parseTrades (result, market, since, limit);
697
+ }
698
+
699
+ async fetchTradingFee (symbol, params = {}) {
700
+ await this.loadMarkets ();
701
+ const market = this.market (symbol);
702
+ const request = {
703
+ 'id': market['id'],
704
+ };
705
+ const response = await this.publicGetProductsId (this.extend (request, params));
706
+ //
707
+ // {
708
+ // "id":"637",
709
+ // "product_type":"CurrencyPair",
710
+ // "code":"CASH",
711
+ // "name":null,
712
+ // "market_ask":"0.00000797",
713
+ // "market_bid":"0.00000727",
714
+ // "indicator":null,
715
+ // "currency":"BTC",
716
+ // "currency_pair_code":"TFTBTC",
717
+ // "symbol":null,
718
+ // "btc_minimum_withdraw":null,
719
+ // "fiat_minimum_withdraw":null,
720
+ // "pusher_channel":"product_cash_tftbtc_637",
721
+ // "taker_fee":"0.0",
722
+ // "maker_fee":"0.0",
723
+ // "low_market_bid":"0.00000685",
724
+ // "high_market_ask":"0.00000885",
725
+ // "volume_24h":"3696.0755956",
726
+ // "last_price_24h":"0.00000716",
727
+ // "last_traded_price":"0.00000766",
728
+ // "last_traded_quantity":"1748.0377978",
729
+ // "average_price":null,
730
+ // "quoted_currency":"BTC",
731
+ // "base_currency":"TFT",
732
+ // "tick_size":"0.00000001",
733
+ // "disabled":false,
734
+ // "margin_enabled":false,
735
+ // "cfd_enabled":false,
736
+ // "perpetual_enabled":false,
737
+ // "last_event_timestamp":"1596962820.000797146",
738
+ // "timestamp":"1596962820.000797146",
739
+ // "multiplier_up":"9.0",
740
+ // "multiplier_down":"0.1",
741
+ // "average_time_interval":null
742
+ // }
743
+ //
744
+ return this.parseTradingFee (response, market);
745
+ }
746
+
747
+ parseTradingFee (fee, market = undefined) {
748
+ const marketId = this.safeString (fee, 'id');
749
+ const symbol = this.safeSymbol (marketId, market);
750
+ return {
751
+ 'info': fee,
752
+ 'symbol': symbol,
753
+ 'maker': this.safeNumber (fee, 'maker_fee'),
754
+ 'taker': this.safeNumber (fee, 'taker_fee'),
755
+ 'percentage': true,
756
+ 'tierBased': true,
757
+ };
758
+ }
759
+
760
+ async fetchTradingFees (params = {}) {
761
+ await this.loadMarkets ();
762
+ const spot = await this.publicGetProducts (params);
763
+ //
764
+ // [
765
+ // {
766
+ // "id":"637",
767
+ // "product_type":"CurrencyPair",
768
+ // "code":"CASH",
769
+ // "name":null,
770
+ // "market_ask":"0.00000797",
771
+ // "market_bid":"0.00000727",
772
+ // "indicator":null,
773
+ // "currency":"BTC",
774
+ // "currency_pair_code":"TFTBTC",
775
+ // "symbol":null,
776
+ // "btc_minimum_withdraw":null,
777
+ // "fiat_minimum_withdraw":null,
778
+ // "pusher_channel":"product_cash_tftbtc_637",
779
+ // "taker_fee":"0.0",
780
+ // "maker_fee":"0.0",
781
+ // "low_market_bid":"0.00000685",
782
+ // "high_market_ask":"0.00000885",
783
+ // "volume_24h":"3696.0755956",
784
+ // "last_price_24h":"0.00000716",
785
+ // "last_traded_price":"0.00000766",
786
+ // "last_traded_quantity":"1748.0377978",
787
+ // "average_price":null,
788
+ // "quoted_currency":"BTC",
789
+ // "base_currency":"TFT",
790
+ // "tick_size":"0.00000001",
791
+ // "disabled":false,
792
+ // "margin_enabled":false,
793
+ // "cfd_enabled":false,
794
+ // "perpetual_enabled":false,
795
+ // "last_event_timestamp":"1596962820.000797146",
796
+ // "timestamp":"1596962820.000797146",
797
+ // "multiplier_up":"9.0",
798
+ // "multiplier_down":"0.1",
799
+ // "average_time_interval":null
800
+ // },
801
+ // ]
802
+ //
803
+ const perpetual = await this.publicGetProducts ({ 'perpetual': '1' });
804
+ //
805
+ // [
806
+ // {
807
+ // "id":"604",
808
+ // "product_type":"Perpetual",
809
+ // "code":"CASH",
810
+ // "name":null,
811
+ // "market_ask":"11721.5",
812
+ // "market_bid":"11719.0",
813
+ // "indicator":null,
814
+ // "currency":"USD",
815
+ // "currency_pair_code":"P-BTCUSD",
816
+ // "symbol":"$",
817
+ // "btc_minimum_withdraw":null,
818
+ // "fiat_minimum_withdraw":null,
819
+ // "pusher_channel":"product_cash_p-btcusd_604",
820
+ // "taker_fee":"0.0012",
821
+ // "maker_fee":"0.0",
822
+ // "low_market_bid":"11624.5",
823
+ // "high_market_ask":"11859.0",
824
+ // "volume_24h":"0.271",
825
+ // "last_price_24h":"11621.5",
826
+ // "last_traded_price":"11771.5",
827
+ // "last_traded_quantity":"0.09",
828
+ // "average_price":"11771.5",
829
+ // "quoted_currency":"USD",
830
+ // "base_currency":"P-BTC",
831
+ // "tick_size":"0.5",
832
+ // "disabled":false,
833
+ // "margin_enabled":false,
834
+ // "cfd_enabled":false,
835
+ // "perpetual_enabled":true,
836
+ // "last_event_timestamp":"1596963309.418853092",
837
+ // "timestamp":"1596963309.418853092",
838
+ // "multiplier_up":null,
839
+ // "multiplier_down":"0.1",
840
+ // "average_time_interval":300,
841
+ // "index_price":"11682.8124",
842
+ // "mark_price":"11719.96781",
843
+ // "funding_rate":"0.00273",
844
+ // "fair_price":"11720.2745"
845
+ // },
846
+ // ]
847
+ //
848
+ const markets = this.arrayConcat (spot, perpetual);
849
+ const result = {};
850
+ for (let i = 0; i < markets.length; i++) {
851
+ const market = markets[i];
852
+ const marketId = this.safeString (market, 'id');
853
+ const symbol = this.safeSymbol (marketId, market);
854
+ result[symbol] = this.parseTradingFee (market);
855
+ }
856
+ return result;
857
+ }
858
+
859
+ async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
860
+ await this.loadMarkets ();
861
+ const market = this.market (symbol);
862
+ // the `with_details` param is undocumented - it adds the order_id to the results
863
+ const request = {
864
+ 'product_id': market['id'],
865
+ 'with_details': true,
866
+ };
867
+ if (limit !== undefined) {
868
+ request['limit'] = limit;
869
+ }
870
+ const response = await this.privateGetExecutionsMe (this.extend (request, params));
871
+ return this.parseTrades (response['models'], market, since, limit);
872
+ }
873
+
874
+ async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
875
+ await this.loadMarkets ();
876
+ const clientOrderId = this.safeString2 (params, 'clientOrderId', 'client_order_id');
877
+ params = this.omit (params, [ 'clientOrderId', 'client_order_id' ]);
878
+ const request = {
879
+ 'order_type': type,
880
+ 'product_id': this.marketId (symbol),
881
+ 'side': side,
882
+ 'quantity': this.amountToPrecision (symbol, amount),
883
+ };
884
+ if (clientOrderId !== undefined) {
885
+ request['client_order_id'] = clientOrderId;
886
+ }
887
+ if ((type === 'limit') || (type === 'limit_post_only') || (type === 'market_with_range') || (type === 'stop')) {
888
+ request['price'] = this.priceToPrecision (symbol, price);
889
+ }
890
+ const response = await this.privatePostOrders (this.extend (request, params));
891
+ //
892
+ // {
893
+ // "id": 2157474,
894
+ // "order_type": "limit",
895
+ // "quantity": "0.01",
896
+ // "disc_quantity": "0.0",
897
+ // "iceberg_total_quantity": "0.0",
898
+ // "side": "sell",
899
+ // "filled_quantity": "0.0",
900
+ // "price": "500.0",
901
+ // "created_at": 1462123639,
902
+ // "updated_at": 1462123639,
903
+ // "status": "live",
904
+ // "leverage_level": 1,
905
+ // "source_exchange": "QUOINE",
906
+ // "product_id": 1,
907
+ // "product_code": "CASH",
908
+ // "funding_currency": "USD",
909
+ // "currency_pair_code": "BTCUSD",
910
+ // "order_fee": "0.0",
911
+ // "client_order_id": null,
912
+ // }
913
+ //
914
+ return this.parseOrder (response);
915
+ }
916
+
917
+ async cancelOrder (id, symbol = undefined, params = {}) {
918
+ await this.loadMarkets ();
919
+ const request = {
920
+ 'id': id,
921
+ };
922
+ const response = await this.privatePutOrdersIdCancel (this.extend (request, params));
923
+ const order = this.parseOrder (response);
924
+ if (order['status'] === 'closed') {
925
+ if (this.options['cancelOrderException']) {
926
+ throw new OrderNotFound (this.id + ' order closed already: ' + this.json (response));
927
+ }
928
+ }
929
+ return order;
930
+ }
931
+
932
+ async editOrder (id, symbol, type, side, amount, price = undefined, params = {}) {
933
+ await this.loadMarkets ();
934
+ if (price === undefined) {
935
+ throw new ArgumentsRequired (this.id + ' editOrder() requires the price argument');
936
+ }
937
+ const request = {
938
+ 'order': {
939
+ 'quantity': this.amountToPrecision (symbol, amount),
940
+ 'price': this.priceToPrecision (symbol, price),
941
+ },
942
+ 'id': id,
943
+ };
944
+ const response = await this.privatePutOrdersId (this.extend (request, params));
945
+ return this.parseOrder (response);
946
+ }
947
+
948
+ parseOrderStatus (status) {
949
+ const statuses = {
950
+ 'live': 'open',
951
+ 'filled': 'closed',
952
+ 'cancelled': 'canceled',
953
+ };
954
+ return this.safeString (statuses, status, status);
955
+ }
956
+
957
+ parseOrder (order, market = undefined) {
958
+ //
959
+ // createOrder
960
+ //
961
+ // {
962
+ // "id": 2157474,
963
+ // "order_type": "limit",
964
+ // "quantity": "0.01",
965
+ // "disc_quantity": "0.0",
966
+ // "iceberg_total_quantity": "0.0",
967
+ // "side": "sell",
968
+ // "filled_quantity": "0.0",
969
+ // "price": "500.0",
970
+ // "created_at": 1462123639,
971
+ // "updated_at": 1462123639,
972
+ // "status": "live",
973
+ // "leverage_level": 1,
974
+ // "source_exchange": "QUOINE",
975
+ // "product_id": 1,
976
+ // "product_code": "CASH",
977
+ // "funding_currency": "USD",
978
+ // "currency_pair_code": "BTCUSD",
979
+ // "order_fee": "0.0"
980
+ // "client_order_id": null,
981
+ // }
982
+ //
983
+ // fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders
984
+ //
985
+ // {
986
+ // "id": 2157479,
987
+ // "order_type": "limit",
988
+ // "quantity": "0.01",
989
+ // "disc_quantity": "0.0",
990
+ // "iceberg_total_quantity": "0.0",
991
+ // "side": "sell",
992
+ // "filled_quantity": "0.01",
993
+ // "price": "500.0",
994
+ // "created_at": 1462123639,
995
+ // "updated_at": 1462123639,
996
+ // "status": "filled",
997
+ // "leverage_level": 2,
998
+ // "source_exchange": "QUOINE",
999
+ // "product_id": 1,
1000
+ // "product_code": "CASH",
1001
+ // "funding_currency": "USD",
1002
+ // "currency_pair_code": "BTCUSD",
1003
+ // "order_fee": "0.0",
1004
+ // "executions": [
1005
+ // {
1006
+ // "id": 4566133,
1007
+ // "quantity": "0.01",
1008
+ // "price": "500.0",
1009
+ // "taker_side": "buy",
1010
+ // "my_side": "sell",
1011
+ // "created_at": 1465396785
1012
+ // }
1013
+ // ]
1014
+ // }
1015
+ //
1016
+ const orderId = this.safeString (order, 'id');
1017
+ const timestamp = this.safeTimestamp (order, 'created_at');
1018
+ const marketId = this.safeString (order, 'product_id');
1019
+ market = this.safeValue (this.markets_by_id, marketId);
1020
+ const status = this.parseOrderStatus (this.safeString (order, 'status'));
1021
+ const amount = this.safeNumber (order, 'quantity');
1022
+ let filled = this.safeNumber (order, 'filled_quantity');
1023
+ const price = this.safeNumber (order, 'price');
1024
+ const type = this.safeString (order, 'order_type');
1025
+ let tradeCost = 0;
1026
+ let tradeFilled = 0;
1027
+ let average = this.safeNumber (order, 'average_price');
1028
+ const trades = this.parseTrades (this.safeValue (order, 'executions', []), market, undefined, undefined, {
1029
+ 'order': orderId,
1030
+ 'type': type,
1031
+ });
1032
+ const numTrades = trades.length;
1033
+ for (let i = 0; i < numTrades; i++) {
1034
+ // php copies values upon assignment, but not references them
1035
+ // todo rewrite this (shortly)
1036
+ const trade = trades[i];
1037
+ trade['order'] = orderId;
1038
+ trade['type'] = type;
1039
+ tradeFilled = this.sum (tradeFilled, trade['amount']);
1040
+ tradeCost = this.sum (tradeCost, trade['cost']);
1041
+ }
1042
+ let cost = undefined;
1043
+ let lastTradeTimestamp = undefined;
1044
+ if (numTrades > 0) {
1045
+ lastTradeTimestamp = trades[numTrades - 1]['timestamp'];
1046
+ if (!average && (tradeFilled > 0)) {
1047
+ average = tradeCost / tradeFilled;
1048
+ }
1049
+ if (cost === undefined) {
1050
+ cost = tradeCost;
1051
+ }
1052
+ if (filled === undefined) {
1053
+ filled = tradeFilled;
1054
+ }
1055
+ }
1056
+ let remaining = undefined;
1057
+ if (amount !== undefined && filled !== undefined) {
1058
+ remaining = amount - filled;
1059
+ }
1060
+ const side = this.safeString (order, 'side');
1061
+ const clientOrderId = this.safeString (order, 'client_order_id');
1062
+ return {
1063
+ 'id': orderId,
1064
+ 'clientOrderId': clientOrderId,
1065
+ 'timestamp': timestamp,
1066
+ 'datetime': this.iso8601 (timestamp),
1067
+ 'lastTradeTimestamp': lastTradeTimestamp,
1068
+ 'type': type,
1069
+ 'timeInForce': undefined,
1070
+ 'postOnly': undefined,
1071
+ 'status': status,
1072
+ 'symbol': market['symbol'],
1073
+ 'side': side,
1074
+ 'price': price,
1075
+ 'stopPrice': undefined,
1076
+ 'amount': amount,
1077
+ 'filled': filled,
1078
+ 'cost': cost,
1079
+ 'remaining': remaining,
1080
+ 'average': average,
1081
+ 'trades': trades,
1082
+ 'fee': {
1083
+ 'currency': market['quote'],
1084
+ 'cost': this.safeNumber (order, 'order_fee'),
1085
+ },
1086
+ 'info': order,
1087
+ };
1088
+ }
1089
+
1090
+ async fetchOrder (id, symbol = undefined, params = {}) {
1091
+ await this.loadMarkets ();
1092
+ const request = {
1093
+ 'id': id,
1094
+ };
1095
+ const response = await this.privateGetOrdersId (this.extend (request, params));
1096
+ return this.parseOrder (response);
1097
+ }
1098
+
1099
+ async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
1100
+ await this.loadMarkets ();
1101
+ let market = undefined;
1102
+ const request = {
1103
+ // 'funding_currency': market['quoteId'], // filter orders based on "funding" currency (quote currency)
1104
+ // 'product_id': market['id'],
1105
+ // 'status': 'live', // 'filled', 'cancelled'
1106
+ // 'trading_type': 'spot', // 'margin', 'cfd'
1107
+ 'with_details': 1, // return full order details including executions
1108
+ };
1109
+ if (symbol !== undefined) {
1110
+ market = this.market (symbol);
1111
+ request['product_id'] = market['id'];
1112
+ }
1113
+ if (limit !== undefined) {
1114
+ request['limit'] = limit;
1115
+ }
1116
+ const response = await this.privateGetOrders (this.extend (request, params));
1117
+ //
1118
+ // {
1119
+ // "models": [
1120
+ // {
1121
+ // "id": 2157474,
1122
+ // "order_type": "limit",
1123
+ // "quantity": "0.01",
1124
+ // "disc_quantity": "0.0",
1125
+ // "iceberg_total_quantity": "0.0",
1126
+ // "side": "sell",
1127
+ // "filled_quantity": "0.0",
1128
+ // "price": "500.0",
1129
+ // "created_at": 1462123639,
1130
+ // "updated_at": 1462123639,
1131
+ // "status": "live",
1132
+ // "leverage_level": 1,
1133
+ // "source_exchange": "QUOINE",
1134
+ // "product_id": 1,
1135
+ // "product_code": "CASH",
1136
+ // "funding_currency": "USD",
1137
+ // "currency_pair_code": "BTCUSD",
1138
+ // "order_fee": "0.0",
1139
+ // "executions": [], // optional
1140
+ // }
1141
+ // ],
1142
+ // "current_page": 1,
1143
+ // "total_pages": 1
1144
+ // }
1145
+ //
1146
+ const orders = this.safeValue (response, 'models', []);
1147
+ return this.parseOrders (orders, market, since, limit);
1148
+ }
1149
+
1150
+ async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
1151
+ const request = { 'status': 'live' };
1152
+ return await this.fetchOrders (symbol, since, limit, this.extend (request, params));
1153
+ }
1154
+
1155
+ async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
1156
+ const request = { 'status': 'filled' };
1157
+ return await this.fetchOrders (symbol, since, limit, this.extend (request, params));
1158
+ }
1159
+
1160
+ async withdraw (code, amount, address, tag = undefined, params = {}) {
1161
+ [ tag, params ] = this.handleWithdrawTagAndParams (tag, params);
1162
+ this.checkAddress (address);
1163
+ await this.loadMarkets ();
1164
+ const currency = this.currency (code);
1165
+ const request = {
1166
+ // 'auth_code': '', // optional 2fa code
1167
+ 'crypto_withdrawal': {
1168
+ 'currency': currency['id'],
1169
+ 'address': address,
1170
+ 'amount': amount,
1171
+ // 'payment_id': tag, // for XRP only
1172
+ // 'memo_type': 'text', // 'text', 'id' or 'hash', for XLM only
1173
+ // 'memo_value': tag, // for XLM only
1174
+ },
1175
+ };
1176
+ if (tag !== undefined) {
1177
+ if (code === 'XRP') {
1178
+ request['crypto_withdrawal']['payment_id'] = tag;
1179
+ } else if (code === 'XLM') {
1180
+ request['crypto_withdrawal']['memo_type'] = 'text'; // overrideable via params
1181
+ request['crypto_withdrawal']['memo_value'] = tag;
1182
+ } else {
1183
+ throw new NotSupported (this.id + ' withdraw() only supports a tag along the address for XRP or XLM');
1184
+ }
1185
+ }
1186
+ const networks = this.safeValue (this.options, 'networks', {});
1187
+ let network = this.safeStringUpper (params, 'network'); // this line allows the user to specify either ERC20 or ETH
1188
+ if (network === undefined) {
1189
+ const paramsCwArray = this.safeValue (params, 'crypto_withdrawal', {});
1190
+ network = this.safeStringUpper (paramsCwArray, 'network');
1191
+ }
1192
+ network = this.safeString (networks, network, network); // handle ERC20>ETH alias
1193
+ if (network !== undefined) {
1194
+ request['crypto_withdrawal']['network'] = network;
1195
+ params = this.omit (params, 'network');
1196
+ params['crypto_withdrawal'] = this.omit (params['crypto_withdrawal'], 'network');
1197
+ }
1198
+ const response = await this.privatePostCryptoWithdrawals (this.deepExtend (request, params));
1199
+ //
1200
+ // {
1201
+ // "id": 1353,
1202
+ // "address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2",
1203
+ // "amount": 1.0,
1204
+ // "state": "pending",
1205
+ // "currency": "BTC",
1206
+ // "withdrawal_fee": 0.0,
1207
+ // "created_at": 1568016450,
1208
+ // "updated_at": 1568016450,
1209
+ // "payment_id": null
1210
+ // }
1211
+ //
1212
+ return this.parseTransaction (response, currency);
1213
+ }
1214
+
1215
+ async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
1216
+ await this.loadMarkets ();
1217
+ const request = {
1218
+ // state: 'processed', // optional: pending, filed, cancelled, processing, processed, reverted to_be_reviewed, declined, broadcasted
1219
+ };
1220
+ let currency = undefined;
1221
+ if (code !== undefined) {
1222
+ currency = this.currency (code);
1223
+ }
1224
+ const response = await this.privateGetCryptoWithdrawals (this.extend (request, params));
1225
+ //
1226
+ // {
1227
+ // models: [
1228
+ // {
1229
+ // id: '2',
1230
+ // address: '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
1231
+ // amount: '0.01',
1232
+ // state: 'processed',
1233
+ // currency: 'BTC',
1234
+ // withdrawal_fee: '0.0005',
1235
+ // created_at: '1614718276',
1236
+ // updated_at: '1614720926',
1237
+ // payment_id: null,
1238
+ // transaction_hash: 'xxxxxxxx...',
1239
+ // broadcasted_at: '1614720762',
1240
+ // wallet_label: 'btc',
1241
+ // chain_name: 'Bitcoin',
1242
+ // network: null
1243
+ // },
1244
+ // ],
1245
+ // current_page: '1',
1246
+ // total_pages: '1'
1247
+ // }
1248
+ //
1249
+ const transactions = this.safeValue (response, 'models', []);
1250
+ return this.parseTransactions (transactions, currency, since, limit);
1251
+ }
1252
+
1253
+ parseTransactionStatus (status) {
1254
+ const statuses = {
1255
+ 'pending': 'pending',
1256
+ 'cancelled': 'canceled',
1257
+ 'approved': 'ok',
1258
+ 'processing': 'pending',
1259
+ 'processed': 'ok',
1260
+ 'reverted': 'failed',
1261
+ 'to_be_reviewed': 'pending',
1262
+ 'declined': 'failed',
1263
+ 'broadcasted': 'ok',
1264
+ };
1265
+ return this.safeString (statuses, status, status);
1266
+ }
1267
+
1268
+ parseTransaction (transaction, currency = undefined) {
1269
+ //
1270
+ // withdraw
1271
+ //
1272
+ // {
1273
+ // id: '1',
1274
+ // address: '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
1275
+ // amount: '0.01',
1276
+ // state: 'pending',
1277
+ // currency: 'BTC',
1278
+ // withdrawal_fee: '0.0007',
1279
+ // created_at: '1626000533',
1280
+ // updated_at: '1626000533',
1281
+ // payment_id: null,
1282
+ // transaction_hash: null,
1283
+ // broadcasted_at: null,
1284
+ // wallet_label: null,
1285
+ // chain_name: 'Bitcoin',
1286
+ // network: null
1287
+ // },
1288
+ //
1289
+ // fetchWithdrawals
1290
+ //
1291
+ // {
1292
+ // id: '2',
1293
+ // address: '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
1294
+ // amount: '0.01',
1295
+ // state: 'processed',
1296
+ // currency: 'BTC',
1297
+ // withdrawal_fee: '0.0005',
1298
+ // created_at: '1614718276',
1299
+ // updated_at: '1614720926',
1300
+ // payment_id: '',
1301
+ // transaction_hash: 'xxxxxxxx...',
1302
+ // broadcasted_at: '1614720762',
1303
+ // wallet_label: 'btc',
1304
+ // chain_name: 'Bitcoin',
1305
+ // network: null
1306
+ // },
1307
+ //
1308
+ // fetchDeposits
1309
+ //
1310
+ // ...
1311
+ //
1312
+ const id = this.safeString (transaction, 'id');
1313
+ const address = this.safeString (transaction, 'address');
1314
+ const tag = this.safeString2 (transaction, 'payment_id', 'memo_value');
1315
+ const txid = this.safeString (transaction, 'transaction_hash');
1316
+ const currencyId = this.safeString2 (transaction, 'currency', 'asset');
1317
+ const code = this.safeCurrencyCode (currencyId, currency);
1318
+ const timestamp = this.safeTimestamp (transaction, 'created_at');
1319
+ const updated = this.safeTimestamp (transaction, 'updated_at');
1320
+ const type = 'withdrawal';
1321
+ const status = this.parseTransactionStatus (this.safeString (transaction, 'state'));
1322
+ const amountString = this.safeString (transaction, 'amount');
1323
+ const feeCostString = this.safeString (transaction, 'withdrawal_fee');
1324
+ const amount = this.parseNumber (Precise.stringSub (amountString, feeCostString));
1325
+ const network = this.safeString (transaction, 'chain_name');
1326
+ return {
1327
+ 'info': transaction,
1328
+ 'id': id,
1329
+ 'txid': txid,
1330
+ 'timestamp': timestamp,
1331
+ 'datetime': this.iso8601 (timestamp),
1332
+ 'network': network,
1333
+ 'address': address,
1334
+ 'addressTo': undefined,
1335
+ 'addressFrom': undefined,
1336
+ 'tag': tag,
1337
+ 'tagTo': undefined,
1338
+ 'tagFrom': undefined,
1339
+ 'type': type,
1340
+ 'amount': amount,
1341
+ 'currency': code,
1342
+ 'status': status,
1343
+ 'updated': updated,
1344
+ 'fee': {
1345
+ 'currency': code,
1346
+ 'cost': this.parseNumber (feeCostString),
1347
+ },
1348
+ };
1349
+ }
1350
+
1351
+ nonce () {
1352
+ return this.milliseconds ();
1353
+ }
1354
+
1355
+ sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
1356
+ let url = '/' + this.implodeParams (path, params);
1357
+ const query = this.omit (params, this.extractParams (path));
1358
+ headers = {
1359
+ 'X-Quoine-API-Version': this.version,
1360
+ 'Content-Type': 'application/json',
1361
+ };
1362
+ if (api === 'private') {
1363
+ this.checkRequiredCredentials ();
1364
+ if (method === 'GET') {
1365
+ if (Object.keys (query).length) {
1366
+ url += '?' + this.urlencode (query);
1367
+ }
1368
+ } else if (Object.keys (query).length) {
1369
+ body = this.json (query);
1370
+ }
1371
+ const nonce = this.nonce ();
1372
+ const request = {
1373
+ 'path': url,
1374
+ 'token_id': this.apiKey,
1375
+ 'iat': Math.floor (nonce / 1000), // issued at
1376
+ };
1377
+ if (!('client_order_id' in query)) {
1378
+ request['nonce'] = nonce;
1379
+ }
1380
+ headers['X-Quoine-Auth'] = this.jwt (request, this.encode (this.secret));
1381
+ } else {
1382
+ if (Object.keys (query).length) {
1383
+ url += '?' + this.urlencode (query);
1384
+ }
1385
+ }
1386
+ url = this.urls['api'] + url;
1387
+ return { 'url': url, 'method': method, 'body': body, 'headers': headers };
1388
+ }
1389
+
1390
+ handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
1391
+ if (code >= 200 && code < 300) {
1392
+ return;
1393
+ }
1394
+ if (code === 401) {
1395
+ // expected non-json response
1396
+ this.throwExactlyMatchedException (this.exceptions, body, body);
1397
+ return;
1398
+ }
1399
+ if (code === 429) {
1400
+ throw new DDoSProtection (this.id + ' ' + body);
1401
+ }
1402
+ if (response === undefined) {
1403
+ return;
1404
+ }
1405
+ const feedback = this.id + ' ' + body;
1406
+ const message = this.safeString (response, 'message');
1407
+ const errors = this.safeValue (response, 'errors');
1408
+ if (message !== undefined) {
1409
+ //
1410
+ // { "message": "Order not found" }
1411
+ //
1412
+ this.throwExactlyMatchedException (this.exceptions, message, feedback);
1413
+ } else if (errors !== undefined) {
1414
+ //
1415
+ // { "errors": { "user": ["not_enough_free_balance"] }}
1416
+ // { "errors": { "quantity": ["less_than_order_size"] }}
1417
+ // { "errors": { "order": ["Can not update partially filled order"] }}
1418
+ //
1419
+ const types = Object.keys (errors);
1420
+ for (let i = 0; i < types.length; i++) {
1421
+ const type = types[i];
1422
+ const errorMessages = errors[type];
1423
+ for (let j = 0; j < errorMessages.length; j++) {
1424
+ const message = errorMessages[j];
1425
+ this.throwExactlyMatchedException (this.exceptions, message, feedback);
1426
+ }
1427
+ }
1428
+ } else {
1429
+ throw new ExchangeError (feedback);
1430
+ }
1431
+ }
1432
+ };