ccxt-ir 4.3.46.0.2__py2.py3-none-any.whl → 4.5.0__py2.py3-none-any.whl

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 (528) hide show
  1. ccxt/__init__.py +39 -35
  2. ccxt/abantether.py +9 -9
  3. ccxt/abstract/alpaca.py +4 -0
  4. ccxt/abstract/apex.py +31 -0
  5. ccxt/abstract/bigone.py +1 -1
  6. ccxt/abstract/binance.py +106 -48
  7. ccxt/abstract/binancecoinm.py +106 -48
  8. ccxt/abstract/binanceus.py +141 -83
  9. ccxt/abstract/binanceusdm.py +106 -48
  10. ccxt/abstract/bingx.py +50 -1
  11. ccxt/abstract/bitbank.py +5 -0
  12. ccxt/abstract/bitfinex.py +136 -65
  13. ccxt/abstract/bitflyer.py +1 -0
  14. ccxt/abstract/bitget.py +67 -0
  15. ccxt/abstract/bitmart.py +19 -1
  16. ccxt/abstract/bitopro.py +1 -0
  17. ccxt/abstract/bitrue.py +68 -68
  18. ccxt/abstract/bitstamp.py +1 -0
  19. ccxt/abstract/blofin.py +30 -0
  20. ccxt/abstract/btcbox.py +2 -0
  21. ccxt/abstract/bybit.py +28 -13
  22. ccxt/abstract/cex.py +28 -29
  23. ccxt/abstract/coinbaseexchange.py +1 -0
  24. ccxt/abstract/coinbaseinternational.py +1 -1
  25. ccxt/abstract/cryptocom.py +16 -0
  26. ccxt/abstract/cryptomus.py +20 -0
  27. ccxt/abstract/defx.py +69 -0
  28. ccxt/abstract/deribit.py +1 -0
  29. ccxt/abstract/derive.py +117 -0
  30. ccxt/abstract/digifinex.py +1 -0
  31. ccxt/abstract/ellipx.py +25 -0
  32. ccxt/abstract/foxbit.py +26 -0
  33. ccxt/abstract/gate.py +19 -0
  34. ccxt/abstract/gateio.py +19 -0
  35. ccxt/abstract/gemini.py +1 -0
  36. ccxt/abstract/hibachi.py +26 -0
  37. ccxt/abstract/hyperliquid.py +1 -1
  38. ccxt/abstract/independentreserve.py +6 -0
  39. ccxt/abstract/kraken.py +1 -0
  40. ccxt/abstract/krakenfutures.py +4 -0
  41. ccxt/abstract/kucoin.py +10 -0
  42. ccxt/abstract/kucoinfutures.py +18 -0
  43. ccxt/abstract/lbank.py +2 -1
  44. ccxt/abstract/luno.py +1 -0
  45. ccxt/abstract/mexc.py +2 -0
  46. ccxt/abstract/modetrade.py +119 -0
  47. ccxt/abstract/myokx.py +349 -0
  48. ccxt/abstract/oceanex.py +5 -0
  49. ccxt/abstract/okx.py +25 -0
  50. ccxt/abstract/okxus.py +349 -0
  51. ccxt/abstract/onetrading.py +0 -12
  52. ccxt/abstract/paradex.py +23 -0
  53. ccxt/abstract/phemex.py +2 -0
  54. ccxt/abstract/poloniex.py +36 -0
  55. ccxt/abstract/tradeogre.py +3 -1
  56. ccxt/abstract/upbit.py +51 -34
  57. ccxt/abstract/whitebit.py +16 -0
  58. ccxt/abstract/woo.py +64 -6
  59. ccxt/abstract/xt.py +10 -5
  60. ccxt/afratether.py +7 -7
  61. ccxt/alpaca.py +828 -51
  62. ccxt/apex.py +1875 -0
  63. ccxt/arzinja.py +7 -7
  64. ccxt/arzplus.py +9 -9
  65. ccxt/ascendex.py +501 -306
  66. ccxt/async_support/__init__.py +39 -35
  67. ccxt/async_support/abantether.py +10 -10
  68. ccxt/async_support/afratether.py +9 -9
  69. ccxt/async_support/alpaca.py +828 -51
  70. ccxt/async_support/apex.py +1875 -0
  71. ccxt/async_support/arzinja.py +10 -10
  72. ccxt/async_support/arzplus.py +12 -12
  73. ccxt/async_support/ascendex.py +502 -306
  74. ccxt/async_support/base/exchange.py +303 -89
  75. ccxt/async_support/base/ws/cache.py +9 -3
  76. ccxt/async_support/base/ws/client.py +173 -38
  77. ccxt/async_support/base/ws/future.py +25 -37
  78. ccxt/async_support/bequant.py +5 -3
  79. ccxt/async_support/bigone.py +279 -144
  80. ccxt/async_support/binance.py +2347 -1158
  81. ccxt/async_support/binancecoinm.py +9 -3
  82. ccxt/async_support/binanceus.py +17 -3
  83. ccxt/async_support/binanceusdm.py +9 -4
  84. ccxt/async_support/bingx.py +2962 -920
  85. ccxt/async_support/bit2c.py +147 -27
  86. ccxt/async_support/bitbank.py +151 -23
  87. ccxt/async_support/bitbns.py +104 -30
  88. ccxt/async_support/bitfinex.py +3291 -1113
  89. ccxt/async_support/bitflyer.py +202 -27
  90. ccxt/async_support/bitget.py +3683 -1538
  91. ccxt/async_support/bithumb.py +195 -38
  92. ccxt/async_support/bitimen.py +12 -12
  93. ccxt/async_support/bitir.py +38 -38
  94. ccxt/async_support/bitmart.py +1288 -350
  95. ccxt/async_support/bitmex.py +260 -75
  96. ccxt/async_support/bitopro.py +262 -62
  97. ccxt/async_support/bitpin.py +17 -16
  98. ccxt/async_support/bitrue.py +459 -290
  99. ccxt/async_support/bitso.py +199 -54
  100. ccxt/async_support/bitstamp.py +230 -96
  101. ccxt/async_support/bitteam.py +167 -25
  102. ccxt/async_support/{huobijp.py → bittrade.py} +158 -30
  103. ccxt/async_support/bitvavo.py +213 -49
  104. ccxt/async_support/blockchaincom.py +160 -46
  105. ccxt/async_support/blofin.py +502 -120
  106. ccxt/async_support/btcalpha.py +169 -31
  107. ccxt/async_support/btcbox.py +292 -23
  108. ccxt/async_support/btcmarkets.py +211 -58
  109. ccxt/async_support/btcturk.py +161 -38
  110. ccxt/async_support/bybit.py +1775 -1030
  111. ccxt/async_support/cex.py +1440 -1303
  112. ccxt/async_support/coinbase.py +724 -212
  113. ccxt/async_support/coinbaseadvanced.py +2 -1
  114. ccxt/async_support/coinbaseexchange.py +388 -89
  115. ccxt/async_support/coinbaseinternational.py +412 -57
  116. ccxt/async_support/coincatch.py +177 -78
  117. ccxt/async_support/coincheck.py +135 -19
  118. ccxt/async_support/coinex.py +606 -232
  119. ccxt/async_support/coinmate.py +189 -63
  120. ccxt/async_support/coinmetro.py +195 -54
  121. ccxt/async_support/coinone.py +158 -51
  122. ccxt/async_support/coinsph.py +336 -61
  123. ccxt/async_support/coinspot.py +151 -52
  124. ccxt/async_support/cryptocom.py +661 -111
  125. ccxt/async_support/cryptomus.py +1137 -0
  126. ccxt/async_support/defx.py +2071 -0
  127. ccxt/async_support/delta.py +299 -99
  128. ccxt/async_support/deribit.py +348 -126
  129. ccxt/async_support/derive.py +2572 -0
  130. ccxt/async_support/digifinex.py +430 -214
  131. ccxt/async_support/ellipx.py +2029 -0
  132. ccxt/async_support/eterex.py +10 -10
  133. ccxt/async_support/excoino.py +31 -31
  134. ccxt/async_support/exir.py +14 -14
  135. ccxt/async_support/exmo.py +344 -131
  136. ccxt/async_support/exnovin.py +10 -10
  137. ccxt/async_support/farhadexchange.py +12 -12
  138. ccxt/async_support/fmfwio.py +2 -1
  139. ccxt/async_support/foxbit.py +1935 -0
  140. ccxt/async_support/gate.py +1351 -529
  141. ccxt/async_support/gateio.py +2 -1
  142. ccxt/async_support/gemini.py +144 -39
  143. ccxt/async_support/hashkey.py +152 -109
  144. ccxt/async_support/hibachi.py +2080 -0
  145. ccxt/async_support/hitbtc.py +395 -167
  146. ccxt/async_support/hitobit.py +12 -12
  147. ccxt/async_support/hollaex.py +307 -119
  148. ccxt/async_support/htx.py +851 -383
  149. ccxt/async_support/huobi.py +2 -1
  150. ccxt/async_support/hyperliquid.py +1848 -536
  151. ccxt/async_support/independentreserve.py +288 -15
  152. ccxt/async_support/indodax.py +190 -33
  153. ccxt/async_support/jibitex.py +12 -12
  154. ccxt/async_support/kraken.py +795 -351
  155. ccxt/async_support/krakenfutures.py +214 -62
  156. ccxt/async_support/kucoin.py +715 -396
  157. ccxt/async_support/kucoinfutures.py +652 -89
  158. ccxt/async_support/latoken.py +217 -113
  159. ccxt/async_support/lbank.py +425 -97
  160. ccxt/async_support/luno.py +382 -35
  161. ccxt/async_support/mercado.py +113 -6
  162. ccxt/async_support/mexc.py +874 -437
  163. ccxt/async_support/modetrade.py +2818 -0
  164. ccxt/async_support/myokx.py +54 -0
  165. ccxt/async_support/ndax.py +221 -64
  166. ccxt/async_support/nobitex.py +31 -37
  167. ccxt/async_support/novadax.py +190 -34
  168. ccxt/async_support/oceanex.py +217 -28
  169. ccxt/async_support/okcoin.py +253 -145
  170. ccxt/async_support/okexchange.py +11 -11
  171. ccxt/async_support/okx.py +1088 -351
  172. ccxt/async_support/okxus.py +54 -0
  173. ccxt/async_support/ompfinex.py +25 -24
  174. ccxt/async_support/onetrading.py +213 -392
  175. ccxt/async_support/oxfun.py +245 -166
  176. ccxt/async_support/p2b.py +151 -29
  177. ccxt/async_support/paradex.py +562 -49
  178. ccxt/async_support/paymium.py +82 -19
  179. ccxt/async_support/phemex.py +713 -172
  180. ccxt/async_support/poloniex.py +1602 -283
  181. ccxt/async_support/probit.py +224 -95
  182. ccxt/async_support/ramzinex.py +30 -27
  183. ccxt/async_support/sarmayex.py +9 -9
  184. ccxt/async_support/sarrafex.py +13 -13
  185. ccxt/async_support/tabdeal.py +14 -13
  186. ccxt/async_support/tetherland.py +9 -9
  187. ccxt/async_support/timex.py +210 -51
  188. ccxt/async_support/tokocrypto.py +167 -47
  189. ccxt/async_support/tradeogre.py +266 -31
  190. ccxt/async_support/twox.py +9 -9
  191. ccxt/async_support/ubitex.py +12 -12
  192. ccxt/async_support/upbit.py +568 -165
  193. ccxt/async_support/vertex.py +160 -32
  194. ccxt/async_support/wallex.py +12 -12
  195. ccxt/async_support/wavesexchange.py +165 -30
  196. ccxt/async_support/whitebit.py +975 -127
  197. ccxt/async_support/woo.py +1918 -1016
  198. ccxt/async_support/woofipro.py +433 -141
  199. ccxt/async_support/xt.py +649 -193
  200. ccxt/async_support/yobit.py +195 -70
  201. ccxt/async_support/zaif.py +91 -15
  202. ccxt/async_support/zonda.py +151 -36
  203. ccxt/base/decimal_to_precision.py +14 -10
  204. ccxt/base/errors.py +49 -18
  205. ccxt/base/exchange.py +1556 -450
  206. ccxt/base/precise.py +10 -0
  207. ccxt/base/types.py +114 -6
  208. ccxt/bequant.py +5 -3
  209. ccxt/bigone.py +279 -144
  210. ccxt/binance.py +2347 -1158
  211. ccxt/binancecoinm.py +9 -3
  212. ccxt/binanceus.py +17 -3
  213. ccxt/binanceusdm.py +9 -4
  214. ccxt/bingx.py +2962 -920
  215. ccxt/bit2c.py +147 -27
  216. ccxt/bitbank.py +151 -23
  217. ccxt/bitbns.py +104 -30
  218. ccxt/bitfinex.py +3290 -1113
  219. ccxt/bitflyer.py +202 -27
  220. ccxt/bitget.py +3683 -1538
  221. ccxt/bithumb.py +194 -38
  222. ccxt/bitimen.py +9 -9
  223. ccxt/bitir.py +35 -35
  224. ccxt/bitmart.py +1288 -350
  225. ccxt/bitmex.py +260 -75
  226. ccxt/bitopro.py +262 -62
  227. ccxt/bitpin.py +15 -14
  228. ccxt/bitrue.py +459 -290
  229. ccxt/bitso.py +199 -54
  230. ccxt/bitstamp.py +230 -96
  231. ccxt/bitteam.py +167 -25
  232. ccxt/{huobijp.py → bittrade.py} +158 -30
  233. ccxt/bitvavo.py +213 -49
  234. ccxt/blockchaincom.py +160 -46
  235. ccxt/blofin.py +502 -120
  236. ccxt/btcalpha.py +169 -31
  237. ccxt/btcbox.py +291 -23
  238. ccxt/btcmarkets.py +211 -58
  239. ccxt/btcturk.py +161 -38
  240. ccxt/bybit.py +1775 -1030
  241. ccxt/cex.py +1439 -1303
  242. ccxt/coinbase.py +724 -212
  243. ccxt/coinbaseadvanced.py +2 -1
  244. ccxt/coinbaseexchange.py +388 -89
  245. ccxt/coinbaseinternational.py +412 -57
  246. ccxt/coincatch.py +177 -78
  247. ccxt/coincheck.py +135 -19
  248. ccxt/coinex.py +606 -232
  249. ccxt/coinmate.py +189 -63
  250. ccxt/coinmetro.py +194 -54
  251. ccxt/coinone.py +158 -51
  252. ccxt/coinsph.py +336 -61
  253. ccxt/coinspot.py +151 -52
  254. ccxt/cryptocom.py +661 -111
  255. ccxt/cryptomus.py +1137 -0
  256. ccxt/defx.py +2070 -0
  257. ccxt/delta.py +299 -99
  258. ccxt/deribit.py +348 -126
  259. ccxt/derive.py +2571 -0
  260. ccxt/digifinex.py +430 -214
  261. ccxt/ellipx.py +2029 -0
  262. ccxt/eterex.py +7 -7
  263. ccxt/excoino.py +29 -29
  264. ccxt/exir.py +11 -11
  265. ccxt/exmo.py +343 -131
  266. ccxt/exnovin.py +8 -8
  267. ccxt/farhadexchange.py +10 -10
  268. ccxt/fmfwio.py +2 -1
  269. ccxt/foxbit.py +1935 -0
  270. ccxt/gate.py +1351 -529
  271. ccxt/gateio.py +2 -1
  272. ccxt/gemini.py +144 -39
  273. ccxt/hashkey.py +152 -109
  274. ccxt/hibachi.py +2079 -0
  275. ccxt/hitbtc.py +395 -167
  276. ccxt/hitobit.py +9 -9
  277. ccxt/hollaex.py +307 -119
  278. ccxt/htx.py +851 -383
  279. ccxt/huobi.py +2 -1
  280. ccxt/hyperliquid.py +1848 -536
  281. ccxt/independentreserve.py +287 -15
  282. ccxt/indodax.py +190 -33
  283. ccxt/jibitex.py +9 -9
  284. ccxt/kraken.py +794 -351
  285. ccxt/krakenfutures.py +214 -62
  286. ccxt/kucoin.py +715 -396
  287. ccxt/kucoinfutures.py +652 -89
  288. ccxt/latoken.py +217 -113
  289. ccxt/lbank.py +425 -97
  290. ccxt/luno.py +382 -35
  291. ccxt/mercado.py +113 -6
  292. ccxt/mexc.py +873 -437
  293. ccxt/modetrade.py +2818 -0
  294. ccxt/myokx.py +54 -0
  295. ccxt/ndax.py +221 -64
  296. ccxt/nobitex.py +29 -35
  297. ccxt/novadax.py +190 -34
  298. ccxt/oceanex.py +217 -28
  299. ccxt/okcoin.py +253 -145
  300. ccxt/okexchange.py +9 -9
  301. ccxt/okx.py +1088 -351
  302. ccxt/okxus.py +54 -0
  303. ccxt/ompfinex.py +22 -21
  304. ccxt/onetrading.py +213 -392
  305. ccxt/oxfun.py +245 -166
  306. ccxt/p2b.py +151 -29
  307. ccxt/paradex.py +562 -49
  308. ccxt/paymium.py +82 -19
  309. ccxt/phemex.py +712 -172
  310. ccxt/poloniex.py +1601 -283
  311. ccxt/pro/__init__.py +76 -17
  312. ccxt/pro/alpaca.py +21 -6
  313. ccxt/pro/apex.py +984 -0
  314. ccxt/pro/ascendex.py +58 -10
  315. ccxt/pro/bequant.py +6 -1
  316. ccxt/pro/binance.py +728 -156
  317. ccxt/pro/binancecoinm.py +6 -2
  318. ccxt/pro/binanceus.py +8 -4
  319. ccxt/pro/binanceusdm.py +7 -2
  320. ccxt/pro/bingx.py +333 -142
  321. ccxt/pro/bitfinex.py +727 -262
  322. ccxt/pro/bitget.py +570 -79
  323. ccxt/pro/bithumb.py +20 -6
  324. ccxt/pro/bitmart.py +216 -87
  325. ccxt/pro/bitmex.py +47 -9
  326. ccxt/pro/bitopro.py +26 -14
  327. ccxt/pro/bitrue.py +22 -22
  328. ccxt/pro/bitstamp.py +54 -21
  329. ccxt/pro/{huobijp.py → bittrade.py} +7 -6
  330. ccxt/pro/bitvavo.py +191 -67
  331. ccxt/pro/blockchaincom.py +21 -8
  332. ccxt/pro/blofin.py +9 -1
  333. ccxt/pro/bybit.py +632 -245
  334. ccxt/pro/cex.py +59 -24
  335. ccxt/pro/coinbase.py +102 -73
  336. ccxt/pro/coinbaseadvanced.py +2 -1
  337. ccxt/pro/coinbaseexchange.py +8 -8
  338. ccxt/pro/coinbaseinternational.py +181 -25
  339. ccxt/pro/coincatch.py +6 -7
  340. ccxt/pro/coincheck.py +11 -6
  341. ccxt/pro/coinex.py +967 -665
  342. ccxt/pro/coinone.py +16 -9
  343. ccxt/pro/cryptocom.py +448 -45
  344. ccxt/pro/defx.py +831 -0
  345. ccxt/pro/deribit.py +150 -14
  346. ccxt/pro/derive.py +704 -0
  347. ccxt/pro/exmo.py +239 -6
  348. ccxt/pro/gate.py +623 -65
  349. ccxt/pro/gateio.py +2 -1
  350. ccxt/pro/gemini.py +27 -11
  351. ccxt/pro/hashkey.py +2 -2
  352. ccxt/pro/hitbtc.py +196 -91
  353. ccxt/pro/hollaex.py +23 -7
  354. ccxt/pro/htx.py +51 -14
  355. ccxt/pro/huobi.py +2 -1
  356. ccxt/pro/hyperliquid.py +591 -27
  357. ccxt/pro/independentreserve.py +9 -6
  358. ccxt/pro/kraken.py +640 -320
  359. ccxt/pro/krakenfutures.py +62 -35
  360. ccxt/pro/kucoin.py +267 -46
  361. ccxt/pro/kucoinfutures.py +165 -21
  362. ccxt/pro/lbank.py +102 -21
  363. ccxt/pro/luno.py +12 -8
  364. ccxt/pro/mexc.py +877 -111
  365. ccxt/pro/modetrade.py +1271 -0
  366. ccxt/pro/myokx.py +38 -0
  367. ccxt/pro/ndax.py +15 -2
  368. ccxt/pro/okcoin.py +23 -4
  369. ccxt/pro/okx.py +573 -98
  370. ccxt/pro/okxus.py +38 -0
  371. ccxt/pro/onetrading.py +30 -13
  372. ccxt/pro/oxfun.py +131 -27
  373. ccxt/pro/p2b.py +88 -22
  374. ccxt/pro/paradex.py +3 -3
  375. ccxt/pro/phemex.py +75 -21
  376. ccxt/pro/poloniex.py +124 -41
  377. ccxt/pro/probit.py +87 -80
  378. ccxt/pro/tradeogre.py +272 -0
  379. ccxt/pro/upbit.py +152 -12
  380. ccxt/pro/vertex.py +8 -3
  381. ccxt/pro/whitebit.py +58 -5
  382. ccxt/pro/woo.py +228 -37
  383. ccxt/pro/woofipro.py +106 -18
  384. ccxt/pro/xt.py +111 -5
  385. ccxt/probit.py +224 -95
  386. ccxt/protobuf/__init__.py +0 -0
  387. ccxt/protobuf/mexc/PrivateAccountV3Api_pb2.py +37 -0
  388. ccxt/protobuf/mexc/PrivateDealsV3Api_pb2.py +37 -0
  389. ccxt/protobuf/mexc/PrivateOrdersV3Api_pb2.py +37 -0
  390. ccxt/protobuf/mexc/PublicAggreBookTickerV3Api_pb2.py +37 -0
  391. ccxt/protobuf/mexc/PublicAggreDealsV3Api_pb2.py +39 -0
  392. ccxt/protobuf/mexc/PublicAggreDepthsV3Api_pb2.py +39 -0
  393. ccxt/protobuf/mexc/PublicBookTickerBatchV3Api_pb2.py +38 -0
  394. ccxt/protobuf/mexc/PublicBookTickerV3Api_pb2.py +37 -0
  395. ccxt/protobuf/mexc/PublicDealsV3Api_pb2.py +39 -0
  396. ccxt/protobuf/mexc/PublicIncreaseDepthsBatchV3Api_pb2.py +38 -0
  397. ccxt/protobuf/mexc/PublicIncreaseDepthsV3Api_pb2.py +39 -0
  398. ccxt/protobuf/mexc/PublicLimitDepthsV3Api_pb2.py +39 -0
  399. ccxt/protobuf/mexc/PublicMiniTickerV3Api_pb2.py +37 -0
  400. ccxt/protobuf/mexc/PublicMiniTickersV3Api_pb2.py +38 -0
  401. ccxt/protobuf/mexc/PublicSpotKlineV3Api_pb2.py +37 -0
  402. ccxt/protobuf/mexc/PushDataV3ApiWrapper_pb2.py +52 -0
  403. ccxt/protobuf/mexc/__init__.py +0 -0
  404. ccxt/ramzinex.py +28 -25
  405. ccxt/sarmayex.py +7 -7
  406. ccxt/sarrafex.py +10 -10
  407. ccxt/static_dependencies/__init__.py +1 -1
  408. ccxt/static_dependencies/lark/py.typed +0 -0
  409. ccxt/static_dependencies/marshmallow/py.typed +0 -0
  410. ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
  411. ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
  412. ccxt/tabdeal.py +12 -11
  413. ccxt/test/tests_async.py +261 -57
  414. ccxt/test/tests_helpers.py +1 -3
  415. ccxt/test/tests_init.py +4 -3
  416. ccxt/test/tests_sync.py +261 -57
  417. ccxt/tetherland.py +7 -7
  418. ccxt/timex.py +210 -51
  419. ccxt/tokocrypto.py +167 -47
  420. ccxt/tradeogre.py +266 -31
  421. ccxt/twox.py +7 -7
  422. ccxt/ubitex.py +9 -9
  423. ccxt/upbit.py +568 -165
  424. ccxt/vertex.py +160 -32
  425. ccxt/wallex.py +9 -9
  426. ccxt/wavesexchange.py +165 -30
  427. ccxt/whitebit.py +975 -127
  428. ccxt/woo.py +1917 -1016
  429. ccxt/woofipro.py +432 -141
  430. ccxt/xt.py +649 -193
  431. ccxt/yobit.py +194 -70
  432. ccxt/zaif.py +91 -15
  433. ccxt/zonda.py +151 -36
  434. {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info}/METADATA +225 -73
  435. ccxt_ir-4.5.0.dist-info/RECORD +743 -0
  436. {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info}/WHEEL +1 -1
  437. ccxt/abstract/ace.py +0 -15
  438. ccxt/abstract/bitbay.py +0 -53
  439. ccxt/abstract/bitcoincom.py +0 -115
  440. ccxt/abstract/bitfinex2.py +0 -139
  441. ccxt/abstract/bitpanda.py +0 -35
  442. ccxt/abstract/bl3p.py +0 -19
  443. ccxt/abstract/coinlist.py +0 -54
  444. ccxt/abstract/currencycom.py +0 -68
  445. ccxt/abstract/hitbtc3.py +0 -115
  446. ccxt/abstract/idex.py +0 -26
  447. ccxt/abstract/kuna.py +0 -182
  448. ccxt/abstract/lykke.py +0 -29
  449. ccxt/abstract/poloniexfutures.py +0 -48
  450. ccxt/abstract/wazirx.py +0 -30
  451. ccxt/ace.py +0 -1012
  452. ccxt/async_support/ace.py +0 -1012
  453. ccxt/async_support/base/ws/aiohttp_client.py +0 -125
  454. ccxt/async_support/base/ws/fast_client.py +0 -96
  455. ccxt/async_support/bitbay.py +0 -17
  456. ccxt/async_support/bitcoincom.py +0 -17
  457. ccxt/async_support/bitfinex2.py +0 -3552
  458. ccxt/async_support/bitpanda.py +0 -16
  459. ccxt/async_support/bl3p.py +0 -485
  460. ccxt/async_support/coinlist.py +0 -2243
  461. ccxt/async_support/currencycom.py +0 -1950
  462. ccxt/async_support/hitbtc3.py +0 -16
  463. ccxt/async_support/idex.py +0 -1766
  464. ccxt/async_support/kuna.py +0 -1841
  465. ccxt/async_support/lykke.py +0 -1270
  466. ccxt/async_support/poloniexfutures.py +0 -1717
  467. ccxt/async_support/wazirx.py +0 -1224
  468. ccxt/bitbay.py +0 -17
  469. ccxt/bitcoincom.py +0 -17
  470. ccxt/bitfinex2.py +0 -3552
  471. ccxt/bitpanda.py +0 -16
  472. ccxt/bl3p.py +0 -485
  473. ccxt/coinlist.py +0 -2243
  474. ccxt/currencycom.py +0 -1950
  475. ccxt/hitbtc3.py +0 -16
  476. ccxt/idex.py +0 -1766
  477. ccxt/kuna.py +0 -1841
  478. ccxt/lykke.py +0 -1270
  479. ccxt/poloniexfutures.py +0 -1717
  480. ccxt/pro/bitcoincom.py +0 -34
  481. ccxt/pro/bitfinex2.py +0 -1083
  482. ccxt/pro/bitpanda.py +0 -15
  483. ccxt/pro/currencycom.py +0 -536
  484. ccxt/pro/idex.py +0 -672
  485. ccxt/pro/poloniexfutures.py +0 -990
  486. ccxt/pro/wazirx.py +0 -749
  487. ccxt/test/base/__init__.py +0 -29
  488. ccxt/test/base/test_account.py +0 -26
  489. ccxt/test/base/test_balance.py +0 -56
  490. ccxt/test/base/test_borrow_interest.py +0 -35
  491. ccxt/test/base/test_borrow_rate.py +0 -32
  492. ccxt/test/base/test_calculate_fee.py +0 -51
  493. ccxt/test/base/test_crypto.py +0 -127
  494. ccxt/test/base/test_currency.py +0 -76
  495. ccxt/test/base/test_datetime.py +0 -109
  496. ccxt/test/base/test_decimal_to_precision.py +0 -392
  497. ccxt/test/base/test_deep_extend.py +0 -68
  498. ccxt/test/base/test_deposit_withdrawal.py +0 -50
  499. ccxt/test/base/test_exchange_datetime_functions.py +0 -76
  500. ccxt/test/base/test_funding_rate_history.py +0 -29
  501. ccxt/test/base/test_last_price.py +0 -31
  502. ccxt/test/base/test_ledger_entry.py +0 -45
  503. ccxt/test/base/test_ledger_item.py +0 -48
  504. ccxt/test/base/test_leverage_tier.py +0 -33
  505. ccxt/test/base/test_liquidation.py +0 -50
  506. ccxt/test/base/test_margin_mode.py +0 -24
  507. ccxt/test/base/test_margin_modification.py +0 -35
  508. ccxt/test/base/test_market.py +0 -193
  509. ccxt/test/base/test_number.py +0 -411
  510. ccxt/test/base/test_ohlcv.py +0 -33
  511. ccxt/test/base/test_open_interest.py +0 -32
  512. ccxt/test/base/test_order.py +0 -64
  513. ccxt/test/base/test_order_book.py +0 -69
  514. ccxt/test/base/test_position.py +0 -60
  515. ccxt/test/base/test_shared_methods.py +0 -353
  516. ccxt/test/base/test_status.py +0 -24
  517. ccxt/test/base/test_throttle.py +0 -126
  518. ccxt/test/base/test_ticker.py +0 -92
  519. ccxt/test/base/test_trade.py +0 -47
  520. ccxt/test/base/test_trading_fee.py +0 -26
  521. ccxt/test/base/test_transaction.py +0 -39
  522. ccxt/test/test_async.py +0 -1649
  523. ccxt/test/test_sync.py +0 -1648
  524. ccxt/wazirx.py +0 -1224
  525. ccxt_ir-4.3.46.0.2.dist-info/RECORD +0 -772
  526. /ccxt/abstract/{huobijp.py → bittrade.py} +0 -0
  527. {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info/licenses}/LICENSE.txt +0 -0
  528. {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info}/top_level.txt +0 -0
ccxt/test/test_async.py DELETED
@@ -1,1649 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- import argparse
4
- import json
5
- # import logging
6
- import os
7
- import sys
8
- from traceback import format_tb, format_exception
9
-
10
- import importlib # noqa: E402
11
- import re
12
-
13
- # ------------------------------------------------------------------------------
14
- # logging.basicConfig(level=logging.INFO)
15
- # ------------------------------------------------------------------------------
16
- DIR_NAME = os.path.dirname(os.path.abspath(__file__))
17
- root = os.path.dirname(os.path.dirname(DIR_NAME))
18
- sys.path.append(root)
19
-
20
- import ccxt.async_support as ccxt # noqa: E402
21
- import ccxt.pro as ccxtpro # noqa: E402
22
-
23
- # ------------------------------------------------------------------------------
24
- import asyncio
25
- # from typing import Optional
26
- # from typing import List
27
- from ccxt.base.errors import NotSupported
28
- from ccxt.base.errors import ProxyError
29
- from ccxt.base.errors import OperationFailed
30
- # from ccxt.base.errors import ExchangeError
31
- from ccxt.base.errors import ExchangeNotAvailable
32
- from ccxt.base.errors import OnMaintenance
33
- from ccxt.base.errors import AuthenticationError
34
-
35
- # ------------------------------------------------------------------------------
36
-
37
- class Argv(object):
38
- id_tests = False
39
- static_tests = False
40
- ws_tests = False
41
- request_tests = False
42
- response_tests = False
43
- token_bucket = False
44
- sandbox = False
45
- privateOnly = False
46
- private = False
47
- ws = False
48
- verbose = False
49
- nonce = None
50
- exchange = None
51
- symbol = None
52
- info = False
53
- pass
54
-
55
-
56
- argv = Argv()
57
- parser = argparse.ArgumentParser()
58
- parser.add_argument('--token_bucket', action='store_true', help='enable token bucket experimental test')
59
- parser.add_argument('--sandbox', action='store_true', help='enable sandbox mode')
60
- parser.add_argument('--privateOnly', action='store_true', help='run private tests only')
61
- parser.add_argument('--private', action='store_true', help='run private tests')
62
- parser.add_argument('--verbose', action='store_true', help='enable verbose output')
63
- parser.add_argument('--ws', action='store_true', help='websockets version')
64
- parser.add_argument('--info', action='store_true', help='enable info output')
65
- parser.add_argument('--static', action='store_true', help='run static tests')
66
- parser.add_argument('--useProxy', action='store_true', help='run static tests')
67
- parser.add_argument('--idTests', action='store_true', help='run brokerId tests')
68
- parser.add_argument('--responseTests', action='store_true', help='run response tests')
69
- parser.add_argument('--requestTests', action='store_true', help='run response tests')
70
- parser.add_argument('--nonce', type=int, help='integer')
71
- parser.add_argument('exchange', type=str, help='exchange id in lowercase', nargs='?')
72
- parser.add_argument('symbol', type=str, help='symbol in uppercase', nargs='?')
73
- parser.parse_args(namespace=argv)
74
-
75
- # ------------------------------------------------------------------------------
76
-
77
- path = os.path.dirname(ccxt.__file__)
78
- if 'site-packages' in os.path.dirname(ccxt.__file__):
79
- raise Exception("You are running test_async.py/test.py against a globally-installed version of the library! It was previously installed into your site-packages folder by pip or pip3. To ensure testing against the local folder uninstall it first with pip uninstall ccxt or pip3 uninstall ccxt")
80
-
81
- # ------------------------------------------------------------------------------
82
-
83
- Error = Exception
84
-
85
- # # print an error string
86
- # def dump_error(*args):
87
- # string = ' '.join([str(arg) for arg in args])
88
- # print(string)
89
- # sys.stderr.write(string + "\n")
90
- # sys.stderr.flush()
91
-
92
-
93
- def handle_all_unhandled_exceptions(type, value, traceback):
94
- dump((type), (value), '\n<UNHANDLED EXCEPTION>\n' + ('\n'.join(format_tb(traceback))))
95
- exit(1) # unrecoverable crash
96
-
97
-
98
- sys.excepthook = handle_all_unhandled_exceptions
99
- # ------------------------------------------------------------------------------
100
-
101
- # non-transpiled part, but shared names among langs
102
-
103
- is_synchronous = 'async' not in os.path.basename(__file__)
104
-
105
- rootDir = DIR_NAME + '/../../../'
106
- rootDirForSkips = DIR_NAME + '/../../../'
107
- envVars = os.environ
108
- LOG_CHARS_LENGTH = 10000
109
- ext = 'py'
110
- proxyTestFileName = 'proxies'
111
-
112
-
113
- def get_cli_arg_value(arg):
114
- arg_exists = getattr(argv, arg) if hasattr(argv, arg) else False
115
- with_hyphen = '--' + arg
116
- arg_exists_with_hyphen = getattr(argv, with_hyphen) if hasattr(argv, with_hyphen) else False
117
- without_hyphen = arg.replace('--', '')
118
- arg_exists_wo_hyphen = getattr(argv, without_hyphen) if hasattr(argv, without_hyphen) else False
119
- return arg_exists or arg_exists_with_hyphen or arg_exists_wo_hyphen
120
-
121
- isWsTests = get_cli_arg_value('--ws')
122
-
123
-
124
- class baseMainTestClass():
125
- lang = 'PY'
126
- is_synchronous = is_synchronous
127
- request_tests_failed = False
128
- response_tests_failed = False
129
- response_tests = False
130
- ws_tests = False
131
- load_keys = False
132
- skipped_settings_for_exchange = {}
133
- skipped_methods = {}
134
- check_public_tests = {}
135
- test_files = {}
136
- public_tests = {}
137
- new_line = '\n'
138
- root_dir = rootDir
139
- env_vars = envVars
140
- ext = ext
141
- root_dir_for_skips = rootDirForSkips
142
- only_specific_tests = []
143
- proxy_test_file_name = proxyTestFileName
144
- pass
145
-
146
-
147
- def dump(*args):
148
- print(' '.join([str(arg) for arg in args]))
149
-
150
-
151
- def convert_ascii(str):
152
- return str # stub
153
-
154
- def json_parse(elem):
155
- return json.loads(elem)
156
-
157
-
158
- def json_stringify(elem):
159
- return json.dumps(elem)
160
-
161
-
162
- def convert_to_snake_case(content):
163
- res = re.sub(r'(?<!^)(?=[A-Z])', '_', content).lower()
164
- return res.replace('o_h_l_c_v', 'ohlcv')
165
-
166
-
167
- def get_test_name(methodName):
168
- # stub
169
- return methodName
170
-
171
-
172
- def io_file_exists(path):
173
- return os.path.isfile(path)
174
-
175
-
176
- def io_file_read(path, decode=True):
177
- fs = open(path, "r", encoding="utf-8")
178
- content = fs.read()
179
- if decode:
180
- return json.loads(content)
181
- else:
182
- return content
183
-
184
-
185
- def io_dir_read(path):
186
- return os.listdir(path)
187
-
188
-
189
- async def call_method(test_files, methodName, exchange, skippedProperties, args):
190
- methodNameToCall = 'test_' + convert_to_snake_case(methodName)
191
- return await getattr(test_files[methodName], methodNameToCall)(exchange, skippedProperties, *args)
192
-
193
-
194
- async def call_exchange_method_dynamically(exchange, methodName, args):
195
- return await getattr(exchange, methodName)(*args)
196
-
197
- async def call_overriden_method(exchange, methodName, args):
198
- # needed for php
199
- return await call_exchange_method_dynamically(exchange, methodName, args)
200
-
201
- def exception_message(exc):
202
- message = '[' + type(exc).__name__ + '] ' + "".join(format_exception(type(exc), exc, exc.__traceback__, limit=6))
203
- if len(message) > LOG_CHARS_LENGTH:
204
- # Accessing out of range element causes error
205
- message = message[0:LOG_CHARS_LENGTH]
206
- return message
207
-
208
-
209
- def exit_script(code=0):
210
- exit(code)
211
-
212
-
213
- def get_exchange_prop(exchange, prop, defaultValue=None):
214
- if hasattr(exchange, prop):
215
- res = getattr(exchange, prop)
216
- if res is not None and res != '':
217
- return res
218
- return defaultValue
219
-
220
-
221
- def set_exchange_prop(exchange, prop, value):
222
- setattr(exchange, prop, value)
223
- # set snake case too
224
- setattr(exchange, convert_to_snake_case(prop), value)
225
-
226
-
227
- def init_exchange(exchangeId, args, is_ws=False):
228
- if (is_ws):
229
- return getattr(ccxtpro, exchangeId)(args)
230
- return getattr(ccxt, exchangeId)(args)
231
-
232
-
233
- async def get_test_files(properties, ws=False):
234
- tests = {}
235
- finalPropList = properties + [proxyTestFileName]
236
- for i in range(0, len(finalPropList)):
237
- methodName = finalPropList[i]
238
- name_snake_case = convert_to_snake_case(methodName)
239
- prefix = 'async' if not is_synchronous else 'sync'
240
- dir_to_test = DIR_NAME + '/' + prefix + '/'
241
- module_string = 'ccxt.test.' + prefix + '.test_' + name_snake_case
242
- if (ws):
243
- prefix = 'pro'
244
- dir_to_test = DIR_NAME + '/../' + prefix + '/test/Exchange/'
245
- module_string = 'ccxt.pro.test.Exchange.test_' + name_snake_case
246
- filePathWithExt = dir_to_test + 'test_' + name_snake_case + '.py'
247
- if (io_file_exists (filePathWithExt)):
248
- imp = importlib.import_module(module_string)
249
- tests[methodName] = imp # getattr(imp, finalName)
250
- return tests
251
-
252
- async def close(exchange):
253
- if (not is_synchronous and hasattr(exchange, 'close')):
254
- await exchange.close()
255
-
256
- def is_null_value(value):
257
- return value is None
258
-
259
- def set_fetch_response(exchange: ccxt.Exchange, data):
260
- async def fetch(url, method='GET', headers=None, body=None):
261
- return data
262
- exchange.fetch = fetch
263
- return exchange
264
-
265
- # *********************************
266
- # ***** AUTO-TRANSPILER-START *****
267
- class testMainClass(baseMainTestClass):
268
- def parse_cli_args(self):
269
- self.response_tests = get_cli_arg_value('--responseTests')
270
- self.id_tests = get_cli_arg_value('--idTests')
271
- self.request_tests = get_cli_arg_value('--requestTests')
272
- self.info = get_cli_arg_value('--info')
273
- self.verbose = get_cli_arg_value('--verbose')
274
- self.debug = get_cli_arg_value('--debug')
275
- self.private_test = get_cli_arg_value('--private')
276
- self.private_test_only = get_cli_arg_value('--privateOnly')
277
- self.sandbox = get_cli_arg_value('--sandbox')
278
- self.load_keys = get_cli_arg_value('--loadKeys')
279
- self.ws_tests = get_cli_arg_value('--ws')
280
-
281
- async def init(self, exchange_id, symbol_argv, method_argv):
282
- self.parse_cli_args()
283
- if self.request_tests and self.response_tests:
284
- await self.run_static_request_tests(exchange_id, symbol_argv)
285
- await self.run_static_response_tests(exchange_id, symbol_argv)
286
- return
287
- if self.response_tests:
288
- await self.run_static_response_tests(exchange_id, symbol_argv)
289
- return
290
- if self.request_tests:
291
- await self.run_static_request_tests(exchange_id, symbol_argv) # symbol here is the testname
292
- return
293
- if self.id_tests:
294
- await self.run_broker_id_tests()
295
- return
296
- dump(self.new_line + '' + self.new_line + '' + '[INFO] TESTING ', self.ext, {
297
- 'exchange': exchange_id,
298
- 'symbol': symbol_argv,
299
- 'method': method_argv,
300
- 'isWs': self.ws_tests,
301
- }, self.new_line)
302
- exchange_args = {
303
- 'verbose': self.verbose,
304
- 'debug': self.debug,
305
- 'enableRateLimit': True,
306
- 'timeout': 30000,
307
- }
308
- exchange = init_exchange(exchange_id, exchange_args, self.ws_tests)
309
- if exchange.alias:
310
- exit_script(0)
311
- await self.import_files(exchange)
312
- assert len(list(self.test_files.keys())) > 0, 'Test files were not loaded' # ensure test files are found & filled
313
- self.expand_settings(exchange)
314
- self.check_if_specific_test_is_chosen(method_argv)
315
- await self.start_test(exchange, symbol_argv)
316
- exit_script(0) # needed to be explicitly finished for WS tests
317
-
318
- def check_if_specific_test_is_chosen(self, method_argv):
319
- if method_argv is not None:
320
- test_file_names = list(self.test_files.keys())
321
- possible_method_names = method_argv.split(',') # i.e. `test.ts binance fetchBalance,fetchDeposits`
322
- if len(possible_method_names) >= 1:
323
- for i in range(0, len(test_file_names)):
324
- test_file_name = test_file_names[i]
325
- for j in range(0, len(possible_method_names)):
326
- method_name = possible_method_names[j]
327
- method_name = method_name.replace('()', '')
328
- if test_file_name == method_name:
329
- self.only_specific_tests.append(test_file_name)
330
-
331
- async def import_files(self, exchange):
332
- properties = list(exchange.has.keys())
333
- properties.append('loadMarkets')
334
- self.test_files = await get_test_files(properties, self.ws_tests)
335
-
336
- def load_credentials_from_env(self, exchange):
337
- exchange_id = exchange.id
338
- req_creds = get_exchange_prop(exchange, 're' + 'quiredCredentials') # dont glue the r-e-q-u-i-r-e phrase, because leads to messed up transpilation
339
- objkeys = list(req_creds.keys())
340
- for i in range(0, len(objkeys)):
341
- credential = objkeys[i]
342
- is_required = req_creds[credential]
343
- if is_required and get_exchange_prop(exchange, credential) is None:
344
- full_key = exchange_id + '_' + credential
345
- credential_env_name = full_key.upper() # example: KRAKEN_APIKEY
346
- credential_value = self.env_vars[credential_env_name] if (credential_env_name in self.env_vars) else None
347
- if credential_value:
348
- set_exchange_prop(exchange, credential, credential_value)
349
-
350
- def expand_settings(self, exchange):
351
- exchange_id = exchange.id
352
- keys_global = self.root_dir + 'keys.json'
353
- keys_local = self.root_dir + 'keys.local.json'
354
- keys_global_exists = io_file_exists(keys_global)
355
- keys_local_exists = io_file_exists(keys_local)
356
- global_settings = io_file_read(keys_global) if keys_global_exists else {}
357
- local_settings = io_file_read(keys_local) if keys_local_exists else {}
358
- all_settings = exchange.deep_extend(global_settings, local_settings)
359
- exchange_settings = exchange.safe_value(all_settings, exchange_id, {})
360
- if exchange_settings:
361
- setting_keys = list(exchange_settings.keys())
362
- for i in range(0, len(setting_keys)):
363
- key = setting_keys[i]
364
- if exchange_settings[key]:
365
- final_value = None
366
- if isinstance(exchange_settings[key], dict):
367
- existing = get_exchange_prop(exchange, key, {})
368
- final_value = exchange.deep_extend(existing, exchange_settings[key])
369
- else:
370
- final_value = exchange_settings[key]
371
- set_exchange_prop(exchange, key, final_value)
372
- # credentials
373
- if self.load_keys:
374
- self.load_credentials_from_env(exchange)
375
- # skipped tests
376
- skipped_file = self.root_dir_for_skips + 'skip-tests.json'
377
- skipped_settings = io_file_read(skipped_file)
378
- self.skipped_settings_for_exchange = exchange.safe_value(skipped_settings, exchange_id, {})
379
- skipped_settings_for_exchange = self.skipped_settings_for_exchange
380
- # others
381
- timeout = exchange.safe_value(skipped_settings_for_exchange, 'timeout')
382
- if timeout is not None:
383
- exchange.timeout = exchange.parse_to_int(timeout)
384
- if get_cli_arg_value('--useProxy'):
385
- exchange.http_proxy = exchange.safe_string(skipped_settings_for_exchange, 'httpProxy')
386
- exchange.https_proxy = exchange.safe_string(skipped_settings_for_exchange, 'httpsProxy')
387
- exchange.ws_proxy = exchange.safe_string(skipped_settings_for_exchange, 'wsProxy')
388
- exchange.wss_proxy = exchange.safe_string(skipped_settings_for_exchange, 'wssProxy')
389
- self.skipped_methods = exchange.safe_value(skipped_settings_for_exchange, 'skipMethods', {})
390
- self.checked_public_tests = {}
391
-
392
- def add_padding(self, message, size):
393
- # has to be transpilable
394
- res = ''
395
- message_length = len(message) # avoid php transpilation issue
396
- missing_space = size - message_length - 0 # - 0 is added just to trick transpile to treat the .length as a string for php
397
- if missing_space > 0:
398
- for i in range(0, missing_space):
399
- res += ' '
400
- return message + res
401
-
402
- def exchange_hint(self, exchange, market=None):
403
- market_type = exchange.safe_string_2(exchange.options, 'defaultType', 'type', '')
404
- market_sub_type = exchange.safe_string_2(exchange.options, 'defaultSubType', 'subType')
405
- if market is not None:
406
- market_type = market['type']
407
- if market['linear']:
408
- market_sub_type = 'linear'
409
- elif market['inverse']:
410
- market_sub_type = 'inverse'
411
- elif exchange.safe_value(market, 'quanto'):
412
- market_sub_type = 'quanto'
413
- is_ws = ('ws' in exchange.has)
414
- ws_flag = '(WS)' if is_ws else ''
415
- result = exchange.id + ' ' + ws_flag + ' ' + market_type
416
- if market_sub_type is not None:
417
- result = result + ' [subType: ' + market_sub_type + '] '
418
- return result
419
-
420
- async def test_method(self, method_name, exchange, args, is_public):
421
- # todo: temporary skip for c#
422
- if 'OrderBook' in method_name and self.ext == 'cs':
423
- exchange.options['checksum'] = False
424
- # todo: temporary skip for php
425
- if 'OrderBook' in method_name and self.ext == 'php':
426
- return
427
- skipped_properties_for_method = self.get_skips(exchange, method_name)
428
- is_load_markets = (method_name == 'loadMarkets')
429
- is_fetch_currencies = (method_name == 'fetchCurrencies')
430
- is_proxy_test = (method_name == self.proxy_test_file_name)
431
- # if this is a private test, and the implementation was already tested in public, then no need to re-test it in private test (exception is fetchCurrencies, because our approach in base exchange)
432
- if not is_public and (method_name in self.checked_public_tests) and not is_fetch_currencies:
433
- return
434
- skip_message = None
435
- supported_by_exchange = (method_name in exchange.has) and exchange.has[method_name]
436
- if not is_load_markets and (len(self.only_specific_tests) > 0 and not exchange.in_array(method_name, self.only_specific_tests)):
437
- skip_message = '[INFO] IGNORED_TEST'
438
- elif not is_load_markets and not supported_by_exchange and not is_proxy_test:
439
- skip_message = '[INFO] UNSUPPORTED_TEST' # keep it aligned with the longest message
440
- elif isinstance(skipped_properties_for_method, str):
441
- skip_message = '[INFO] SKIPPED_TEST'
442
- elif not (method_name in self.test_files):
443
- skip_message = '[INFO] UNIMPLEMENTED_TEST'
444
- # exceptionally for `loadMarkets` call, we call it before it's even checked for "skip" as we need it to be called anyway (but can skip "test.loadMarket" for it)
445
- if is_load_markets:
446
- await exchange.load_markets(True)
447
- if skip_message:
448
- if self.info:
449
- dump(self.add_padding(skip_message, 25), self.exchange_hint(exchange), method_name)
450
- return
451
- if self.info:
452
- args_stringified = '(' + exchange.json(args) + ')' # args.join() breaks when we provide a list of symbols or multidimensional array; "args.toString()" breaks bcz of "array to string conversion"
453
- dump(self.add_padding('[INFO] TESTING', 25), self.exchange_hint(exchange), method_name, args_stringified)
454
- await call_method(self.test_files, method_name, exchange, skipped_properties_for_method, args)
455
- # if it was passed successfully, add to the list of successfull tests
456
- if is_public:
457
- self.checked_public_tests[method_name] = True
458
- return
459
-
460
- def get_skips(self, exchange, method_name):
461
- final_skips = {}
462
- # check the exact method (i.e. `fetchTrades`) and language-specific (i.e. `fetchTrades.php`)
463
- method_names = [method_name, method_name + '.' + self.ext]
464
- for i in range(0, len(method_names)):
465
- m_name = method_names[i]
466
- if m_name in self.skipped_methods:
467
- # if whole method is skipped, by assigning a string to it, i.e. "fetchOrders":"blabla"
468
- if isinstance(self.skipped_methods[m_name], str):
469
- return self.skipped_methods[m_name]
470
- else:
471
- final_skips = exchange.deep_extend(final_skips, self.skipped_methods[m_name])
472
- # get "object-specific" skips
473
- object_skips = {
474
- 'orderBook': ['fetchOrderBook', 'fetchOrderBooks', 'fetchL2OrderBook', 'watchOrderBook', 'watchOrderBookForSymbols'],
475
- 'ticker': ['fetchTicker', 'fetchTickers', 'watchTicker', 'watchTickers'],
476
- 'trade': ['fetchTrades', 'watchTrades', 'watchTradesForSymbols'],
477
- 'ohlcv': ['fetchOHLCV', 'watchOHLCV', 'watchOHLCVForSymbols'],
478
- 'ledger': ['fetchLedger', 'fetchLedgerEntry'],
479
- 'depositWithdraw': ['fetchDepositsWithdrawals', 'fetchDeposits', 'fetchWithdrawals'],
480
- 'depositWithdrawFee': ['fetchDepositWithdrawFee', 'fetchDepositWithdrawFees'],
481
- }
482
- object_names = list(object_skips.keys())
483
- for i in range(0, len(object_names)):
484
- object_name = object_names[i]
485
- object_methods = object_skips[object_name]
486
- if exchange.in_array(method_name, object_methods):
487
- # if whole object is skipped, by assigning a string to it, i.e. "orderBook":"blabla"
488
- if (object_name in self.skipped_methods) and (isinstance(self.skipped_methods[object_name], str)):
489
- return self.skipped_methods[object_name]
490
- extra_skips = exchange.safe_dict(self.skipped_methods, object_name, {})
491
- final_skips = exchange.deep_extend(final_skips, extra_skips)
492
- # extend related skips
493
- # - if 'timestamp' is skipped, we should do so for 'datetime' too
494
- # - if 'bid' is skipped, skip 'ask' too
495
- if ('timestamp' in final_skips) and not ('datetime' in final_skips):
496
- final_skips['datetime'] = final_skips['timestamp']
497
- if ('bid' in final_skips) and not ('ask' in final_skips):
498
- final_skips['ask'] = final_skips['bid']
499
- if ('baseVolume' in final_skips) and not ('quoteVolume' in final_skips):
500
- final_skips['quoteVolume'] = final_skips['baseVolume']
501
- return final_skips
502
-
503
- async def test_safe(self, method_name, exchange, args=[], is_public=False):
504
- # `testSafe` method does not throw an exception, instead mutes it. The reason we
505
- # mute the thrown exceptions here is because we don't want to stop the whole
506
- # tests queue if any single test-method fails. Instead, they are echoed with
507
- # formatted message "[TEST_FAILURE] ..." and that output is then regex-matched by
508
- # run-tests.js, so the exceptions are still printed out to console from there.
509
- max_retries = 3
510
- args_stringified = exchange.json(args) # args.join() breaks when we provide a list of symbols | "args.toString()" breaks bcz of "array to string conversion"
511
- for i in range(0, max_retries):
512
- try:
513
- await self.test_method(method_name, exchange, args, is_public)
514
- return True
515
- except Exception as e:
516
- is_load_markets = (method_name == 'loadMarkets')
517
- is_auth_error = (isinstance(e, AuthenticationError))
518
- is_not_supported = (isinstance(e, NotSupported))
519
- is_operation_failed = (isinstance(e, OperationFailed)) # includes "DDoSProtection", "RateLimitExceeded", "RequestTimeout", "ExchangeNotAvailable", "OperationFailed", "InvalidNonce", ...
520
- if is_operation_failed:
521
- # if last retry was gone with same `tempFailure` error, then let's eventually return false
522
- if i == max_retries - 1:
523
- is_on_maintenance = (isinstance(e, OnMaintenance))
524
- is_exchange_not_available = (isinstance(e, ExchangeNotAvailable))
525
- should_fail = None
526
- return_success = None
527
- if is_load_markets:
528
- # if "loadMarkets" does not succeed, we must return "false" to caller method, to stop tests continual
529
- return_success = False
530
- # we might not break exchange tests, if exchange is on maintenance at this moment
531
- if is_on_maintenance:
532
- should_fail = False
533
- else:
534
- should_fail = True
535
- else:
536
- # for any other method tests:
537
- if is_exchange_not_available and not is_on_maintenance:
538
- # break exchange tests if "ExchangeNotAvailable" exception is thrown, but it's not maintenance
539
- should_fail = True
540
- return_success = False
541
- else:
542
- # in all other cases of OperationFailed, show Warning, but don't mark test as failed
543
- should_fail = False
544
- return_success = True
545
- # output the message
546
- fail_type = '[TEST_FAILURE]' if should_fail else '[TEST_WARNING]'
547
- dump(fail_type, 'Method could not be tested due to a repeated Network/Availability issues', ' | ', self.exchange_hint(exchange), method_name, args_stringified, exception_message(e))
548
- return return_success
549
- else:
550
- # wait and retry again
551
- # (increase wait time on every retry)
552
- await exchange.sleep(i * 1000)
553
- continue
554
- else:
555
- # if it's loadMarkets, then fail test, because it's mandatory for tests
556
- if is_load_markets:
557
- dump('[TEST_FAILURE]', 'Exchange can not load markets', exception_message(e), self.exchange_hint(exchange), method_name, args_stringified)
558
- return False
559
- # if the specific arguments to the test method throws "NotSupported" exception
560
- # then let's don't fail the test
561
- if is_not_supported:
562
- if self.info:
563
- dump('[INFO] NOT_SUPPORTED', exception_message(e), self.exchange_hint(exchange), method_name, args_stringified)
564
- return True
565
- # If public test faces authentication error, we don't break (see comments under `testSafe` method)
566
- if is_public and is_auth_error:
567
- if self.info:
568
- dump('[INFO]', 'Authentication problem for public method', exception_message(e), self.exchange_hint(exchange), method_name, args_stringified)
569
- return True
570
- else:
571
- dump('[TEST_FAILURE]', exception_message(e), self.exchange_hint(exchange), method_name, args_stringified)
572
- return False
573
- return True
574
-
575
- async def run_public_tests(self, exchange, symbol):
576
- tests = {
577
- 'fetchCurrencies': [],
578
- 'fetchTicker': [symbol],
579
- 'fetchTickers': [symbol],
580
- 'fetchLastPrices': [symbol],
581
- 'fetchOHLCV': [symbol],
582
- 'fetchTrades': [symbol],
583
- 'fetchOrderBook': [symbol],
584
- 'fetchL2OrderBook': [symbol],
585
- 'fetchOrderBooks': [],
586
- 'fetchBidsAsks': [],
587
- 'fetchStatus': [],
588
- 'fetchTime': [],
589
- }
590
- if self.ws_tests:
591
- tests = {
592
- 'watchOHLCV': [symbol],
593
- 'watchOHLCVForSymbols': [symbol],
594
- 'watchTicker': [symbol],
595
- 'watchTickers': [symbol],
596
- 'watchBidsAsks': [symbol],
597
- 'watchOrderBook': [symbol],
598
- 'watchOrderBookForSymbols': [[symbol]],
599
- 'watchTrades': [symbol],
600
- 'watchTradesForSymbols': [[symbol]],
601
- }
602
- market = exchange.market(symbol)
603
- is_spot = market['spot']
604
- if not self.ws_tests:
605
- if is_spot:
606
- tests['fetchCurrencies'] = []
607
- else:
608
- tests['fetchFundingRates'] = [symbol]
609
- tests['fetchFundingRate'] = [symbol]
610
- tests['fetchFundingRateHistory'] = [symbol]
611
- tests['fetchIndexOHLCV'] = [symbol]
612
- tests['fetchMarkOHLCV'] = [symbol]
613
- tests['fetchPremiumIndexOHLCV'] = [symbol]
614
- self.public_tests = tests
615
- await self.run_tests(exchange, tests, True)
616
-
617
- async def run_tests(self, exchange, tests, is_public_test):
618
- test_names = list(tests.keys())
619
- promises = []
620
- for i in range(0, len(test_names)):
621
- test_name = test_names[i]
622
- test_args = tests[test_name]
623
- promises.append(self.test_safe(test_name, exchange, test_args, is_public_test))
624
- # todo - not yet ready in other langs too
625
- # promises.push (testThrottle ());
626
- results = await asyncio.gather(*promises)
627
- # now count which test-methods retuned `false` from "testSafe" and dump that info below
628
- failed_methods = []
629
- for i in range(0, len(test_names)):
630
- test_name = test_names[i]
631
- test_returned_value = results[i]
632
- if not test_returned_value:
633
- failed_methods.append(test_name)
634
- test_prefix_string = 'PUBLIC_TESTS' if is_public_test else 'PRIVATE_TESTS'
635
- if len(failed_methods):
636
- errors_string = ', '.join(failed_methods)
637
- dump('[TEST_FAILURE]', self.exchange_hint(exchange), test_prefix_string, 'Failed methods : ' + errors_string)
638
- if self.info:
639
- dump(self.add_padding('[INFO] END ' + test_prefix_string + ' ' + self.exchange_hint(exchange), 25))
640
-
641
- async def load_exchange(self, exchange):
642
- result = await self.test_safe('loadMarkets', exchange, [], True)
643
- if not result:
644
- return False
645
- exchange_symbols_length = len(exchange.symbols)
646
- dump('[INFO:MAIN] Exchange loaded', exchange_symbols_length, 'symbols')
647
- return True
648
-
649
- def get_test_symbol(self, exchange, is_spot, symbols):
650
- symbol = None
651
- preferred_spot_symbol = exchange.safe_string(self.skipped_settings_for_exchange, 'preferredSpotSymbol')
652
- preferred_swap_symbol = exchange.safe_string(self.skipped_settings_for_exchange, 'preferredSwapSymbol')
653
- if is_spot and preferred_spot_symbol:
654
- return preferred_spot_symbol
655
- elif not is_spot and preferred_swap_symbol:
656
- return preferred_swap_symbol
657
- for i in range(0, len(symbols)):
658
- s = symbols[i]
659
- market = exchange.safe_value(exchange.markets, s)
660
- if market is not None:
661
- active = exchange.safe_value(market, 'active')
662
- if active or (active is None):
663
- symbol = s
664
- break
665
- return symbol
666
-
667
- def get_exchange_code(self, exchange, codes=None):
668
- if codes is None:
669
- codes = ['BTC', 'ETH', 'XRP', 'LTC', 'BCH', 'EOS', 'BNB', 'BSV', 'USDT']
670
- code = codes[0]
671
- for i in range(0, len(codes)):
672
- if codes[i] in exchange.currencies:
673
- return codes[i]
674
- return code
675
-
676
- def get_markets_from_exchange(self, exchange, spot=True):
677
- res = {}
678
- markets = exchange.markets
679
- keys = list(markets.keys())
680
- for i in range(0, len(keys)):
681
- key = keys[i]
682
- market = markets[key]
683
- if spot and market['spot']:
684
- res[market['symbol']] = market
685
- elif not spot and not market['spot']:
686
- res[market['symbol']] = market
687
- return res
688
-
689
- def get_valid_symbol(self, exchange, spot=True):
690
- current_type_markets = self.get_markets_from_exchange(exchange, spot)
691
- codes = ['BTC', 'ETH', 'XRP', 'LTC', 'BNB', 'DASH', 'DOGE', 'ETC', 'TRX', 'USDT', 'USDC', 'USD', 'EUR', 'TUSD', 'CNY', 'JPY', 'BRL']
692
- spot_symbols = ['BTC/USDT', 'BTC/USDC', 'BTC/USD', 'BTC/CNY', 'BTC/EUR', 'BTC/AUD', 'BTC/BRL', 'BTC/JPY', 'ETH/USDT', 'ETH/USDC', 'ETH/USD', 'ETH/CNY', 'ETH/EUR', 'ETH/AUD', 'ETH/BRL', 'ETH/JPY', 'EUR/USDT', 'EUR/USD', 'EUR/USDC', 'USDT/EUR', 'USD/EUR', 'USDC/EUR', 'BTC/ETH', 'ETH/BTC']
693
- swap_symbols = ['BTC/USDT:USDT', 'BTC/USDC:USDC', 'BTC/USD:USD', 'ETH/USDT:USDT', 'ETH/USDC:USDC', 'ETH/USD:USD', 'BTC/USD:BTC', 'ETH/USD:ETH']
694
- target_symbols = spot_symbols if spot else swap_symbols
695
- symbol = self.get_test_symbol(exchange, spot, target_symbols)
696
- # if symbols wasn't found from above hardcoded list, then try to locate any symbol which has our target hardcoded 'base' code
697
- if symbol is None:
698
- for i in range(0, len(codes)):
699
- current_code = codes[i]
700
- markets_array_for_current_code = exchange.filter_by(current_type_markets, 'base', current_code)
701
- indexed_mkts = exchange.index_by(markets_array_for_current_code, 'symbol')
702
- symbols_array_for_current_code = list(indexed_mkts.keys())
703
- symbols_length = len(symbols_array_for_current_code)
704
- if symbols_length:
705
- symbol = self.get_test_symbol(exchange, spot, symbols_array_for_current_code)
706
- break
707
- # if there wasn't found any symbol with our hardcoded 'base' code, then just try to find symbols that are 'active'
708
- if symbol is None:
709
- active_markets = exchange.filter_by(current_type_markets, 'active', True)
710
- active_symbols = []
711
- for i in range(0, len(active_markets)):
712
- active_symbols.append(active_markets[i]['symbol'])
713
- symbol = self.get_test_symbol(exchange, spot, active_symbols)
714
- if symbol is None:
715
- values = list(current_type_markets.values())
716
- values_length = len(values)
717
- if values_length > 0:
718
- first = values[0]
719
- if first is not None:
720
- symbol = first['symbol']
721
- return symbol
722
-
723
- async def test_exchange(self, exchange, provided_symbol=None):
724
- spot_symbol = None
725
- swap_symbol = None
726
- if provided_symbol is not None:
727
- market = exchange.market(provided_symbol)
728
- if market['spot']:
729
- spot_symbol = provided_symbol
730
- else:
731
- swap_symbol = provided_symbol
732
- else:
733
- if exchange.has['spot']:
734
- spot_symbol = self.get_valid_symbol(exchange, True)
735
- if exchange.has['swap']:
736
- swap_symbol = self.get_valid_symbol(exchange, False)
737
- if spot_symbol is not None:
738
- dump('[INFO:MAIN] Selected SPOT SYMBOL:', spot_symbol)
739
- if swap_symbol is not None:
740
- dump('[INFO:MAIN] Selected SWAP SYMBOL:', swap_symbol)
741
- if not self.private_test_only:
742
- # note, spot & swap tests should run sequentially, because of conflicting `exchange.options['defaultType']` setting
743
- if exchange.has['spot'] and spot_symbol is not None:
744
- if self.info:
745
- dump('[INFO] ### SPOT TESTS ###')
746
- exchange.options['defaultType'] = 'spot'
747
- await self.run_public_tests(exchange, spot_symbol)
748
- if exchange.has['swap'] and swap_symbol is not None:
749
- if self.info:
750
- dump('[INFO] ### SWAP TESTS ###')
751
- exchange.options['defaultType'] = 'swap'
752
- await self.run_public_tests(exchange, swap_symbol)
753
- if self.private_test or self.private_test_only:
754
- if exchange.has['spot'] and spot_symbol is not None:
755
- exchange.options['defaultType'] = 'spot'
756
- await self.run_private_tests(exchange, spot_symbol)
757
- if exchange.has['swap'] and swap_symbol is not None:
758
- exchange.options['defaultType'] = 'swap'
759
- await self.run_private_tests(exchange, swap_symbol)
760
-
761
- async def run_private_tests(self, exchange, symbol):
762
- if not exchange.check_required_credentials(False):
763
- dump('[INFO] Skipping private tests', 'Keys not found')
764
- return
765
- code = self.get_exchange_code(exchange)
766
- # if (exchange.deepExtendedTest) {
767
- # await test ('InvalidNonce', exchange, symbol);
768
- # await test ('OrderNotFound', exchange, symbol);
769
- # await test ('InvalidOrder', exchange, symbol);
770
- # await test ('InsufficientFunds', exchange, symbol, balance); # danger zone - won't execute with non-empty balance
771
- # }
772
- tests = {
773
- 'signIn': [],
774
- 'fetchBalance': [],
775
- 'fetchAccounts': [],
776
- 'fetchTransactionFees': [],
777
- 'fetchTradingFees': [],
778
- 'fetchStatus': [],
779
- 'fetchOrders': [symbol],
780
- 'fetchOpenOrders': [symbol],
781
- 'fetchClosedOrders': [symbol],
782
- 'fetchMyTrades': [symbol],
783
- 'fetchLeverageTiers': [[symbol]],
784
- 'fetchLedger': [code],
785
- 'fetchTransactions': [code],
786
- 'fetchDeposits': [code],
787
- 'fetchWithdrawals': [code],
788
- 'fetchBorrowInterest': [code, symbol],
789
- 'cancelAllOrders': [symbol],
790
- 'fetchCanceledOrders': [symbol],
791
- 'fetchMarginModes': [symbol],
792
- 'fetchPosition': [symbol],
793
- 'fetchDeposit': [code],
794
- 'createDepositAddress': [code],
795
- 'fetchDepositAddress': [code],
796
- 'fetchDepositAddresses': [code],
797
- 'fetchDepositAddressesByNetwork': [code],
798
- 'fetchBorrowRateHistory': [code],
799
- 'fetchLedgerEntry': [code],
800
- }
801
- if self.ws_tests:
802
- tests = {
803
- 'watchBalance': [code],
804
- 'watchMyTrades': [symbol],
805
- 'watchOrders': [symbol],
806
- 'watchPosition': [symbol],
807
- 'watchPositions': [symbol],
808
- }
809
- market = exchange.market(symbol)
810
- is_spot = market['spot']
811
- if not self.ws_tests:
812
- if is_spot:
813
- tests['fetchCurrencies'] = []
814
- else:
815
- # derivatives only
816
- tests['fetchPositions'] = [symbol] # this test fetches all positions for 1 symbol
817
- tests['fetchPosition'] = [symbol]
818
- tests['fetchPositionRisk'] = [symbol]
819
- tests['setPositionMode'] = [symbol]
820
- tests['setMarginMode'] = [symbol]
821
- tests['fetchOpenInterestHistory'] = [symbol]
822
- tests['fetchFundingRateHistory'] = [symbol]
823
- tests['fetchFundingHistory'] = [symbol]
824
- # const combinedTests = exchange.deepExtend (this.publicTests, privateTests);
825
- await self.run_tests(exchange, tests, False)
826
-
827
- async def test_proxies(self, exchange):
828
- # these tests should be synchronously executed, because of conflicting nature of proxy settings
829
- proxy_test_name = self.proxy_test_file_name
830
- # todo: temporary skip for sync py
831
- if self.ext == 'py' and self.is_synchronous:
832
- return
833
- # try proxy several times
834
- max_retries = 3
835
- exception = None
836
- for j in range(0, max_retries):
837
- try:
838
- await self.test_method(proxy_test_name, exchange, [], True)
839
- break # if successfull, then break
840
- except Exception as e:
841
- exception = e
842
- # if exception was set, then throw it
843
- if exception:
844
- error_message = '[TEST_FAILURE] Failed ' + proxy_test_name + ' : ' + exception_message(exception)
845
-
846
- async def start_test(self, exchange, symbol):
847
- # we do not need to test aliases
848
- if exchange.alias:
849
- return
850
- if self.sandbox or get_exchange_prop(exchange, 'sandbox'):
851
- exchange.set_sandbox_mode(True)
852
- try:
853
- result = await self.load_exchange(exchange)
854
- if not result:
855
- await close(exchange)
856
- return
857
- # if (exchange.id === 'binance') {
858
- # # we test proxies functionality just for one random exchange on each build, because proxy functionality is not exchange-specific, instead it's all done from base methods, so just one working sample would mean it works for all ccxt exchanges
859
- # # await this.testProxies (exchange);
860
- # }
861
- await self.test_exchange(exchange, symbol)
862
- await close(exchange)
863
- except Exception as e:
864
- await close(exchange)
865
- raise e
866
-
867
- def assert_static_error(self, cond, message, calculated_output, stored_output, key=None):
868
- # -----------------------------------------------------------------------------
869
- # --- Init of static tests functions------------------------------------------
870
- # -----------------------------------------------------------------------------
871
- calculated_string = json_stringify(calculated_output)
872
- stored_string = json_stringify(stored_output)
873
- error_message = message + ' computed ' + stored_string + ' stored: ' + calculated_string
874
- if key is not None:
875
- error_message = ' | ' + key + ' | ' + 'computed value: ' + stored_string + ' stored value: ' + calculated_string
876
- assert cond, error_message
877
-
878
- def load_markets_from_file(self, id):
879
- # load markets from file
880
- # to make this test as fast as possible
881
- # and basically independent from the exchange
882
- # so we can run it offline
883
- filename = self.root_dir + './ts/src/test/static/markets/' + id + '.json'
884
- content = io_file_read(filename)
885
- return content
886
-
887
- def load_currencies_from_file(self, id):
888
- filename = self.root_dir + './ts/src/test/static/currencies/' + id + '.json'
889
- content = io_file_read(filename)
890
- return content
891
-
892
- def load_static_data(self, folder, target_exchange=None):
893
- result = {}
894
- if target_exchange:
895
- # read a single exchange
896
- path = folder + target_exchange + '.json'
897
- if not io_file_exists(path):
898
- dump('[WARN] tests not found: ' + path)
899
- return None
900
- result[target_exchange] = io_file_read(path)
901
- return result
902
- files = io_dir_read(folder)
903
- for i in range(0, len(files)):
904
- file = files[i]
905
- exchange_name = file.replace('.json', '')
906
- content = io_file_read(folder + file)
907
- result[exchange_name] = content
908
- return result
909
-
910
- def remove_hostnamefrom_url(self, url):
911
- if url is None:
912
- return None
913
- url_parts = url.split('/')
914
- res = ''
915
- for i in range(0, len(url_parts)):
916
- if i > 2:
917
- current = url_parts[i]
918
- if current.find('?') > -1:
919
- # handle urls like this: /v1/account/accounts?AccessK
920
- current_parts = current.split('?')
921
- res += '/'
922
- res += current_parts[0]
923
- break
924
- res += '/'
925
- res += current
926
- return res
927
-
928
- def urlencoded_to_dict(self, url):
929
- result = {}
930
- parts = url.split('&')
931
- for i in range(0, len(parts)):
932
- part = parts[i]
933
- key_value = part.split('=')
934
- keys_length = len(key_value)
935
- if keys_length != 2:
936
- continue
937
- key = key_value[0]
938
- value = key_value[1]
939
- if (value is not None) and ((value.startswith('[')) or (value.startswith('{'))):
940
- # some exchanges might return something like this: timestamp=1699382693405&batchOrders=[{\"symbol\":\"LTCUSDT\",\"side\":\"BUY\",\"newClientOrderI
941
- value = json_parse(value)
942
- result[key] = value
943
- return result
944
-
945
- def assert_new_and_stored_output(self, exchange, skip_keys, new_output, stored_output, strict_type_check=True, asserting_key=None):
946
- if is_null_value(new_output) and is_null_value(stored_output):
947
- return True
948
- if not new_output and not stored_output:
949
- return True
950
- if (isinstance(stored_output, dict)) and (isinstance(new_output, dict)):
951
- stored_output_keys = list(stored_output.keys())
952
- new_output_keys = list(new_output.keys())
953
- stored_keys_length = len(stored_output_keys)
954
- new_keys_length = len(new_output_keys)
955
- self.assert_static_error(stored_keys_length == new_keys_length, 'output length mismatch', stored_output, new_output)
956
- # iterate over the keys
957
- for i in range(0, len(stored_output_keys)):
958
- key = stored_output_keys[i]
959
- if exchange.in_array(key, skip_keys):
960
- continue
961
- if not (exchange.in_array(key, new_output_keys)):
962
- self.assert_static_error(False, 'output key missing: ' + key, stored_output, new_output)
963
- stored_value = stored_output[key]
964
- new_value = new_output[key]
965
- self.assert_new_and_stored_output(exchange, skip_keys, new_value, stored_value, strict_type_check, key)
966
- elif isinstance(stored_output, list) and (isinstance(new_output, list)):
967
- stored_array_length = len(stored_output)
968
- new_array_length = len(new_output)
969
- self.assert_static_error(stored_array_length == new_array_length, 'output length mismatch', stored_output, new_output)
970
- for i in range(0, len(stored_output)):
971
- stored_item = stored_output[i]
972
- new_item = new_output[i]
973
- self.assert_new_and_stored_output(exchange, skip_keys, new_item, stored_item, strict_type_check)
974
- else:
975
- # built-in types like strings, numbers, booleans
976
- sanitized_new_output = None if (not new_output) else new_output # we store undefined as nulls in the json file so we need to convert it back
977
- sanitized_stored_output = None if (not stored_output) else stored_output
978
- new_output_string = str(sanitized_new_output) if sanitized_new_output else 'undefined'
979
- stored_output_string = str(sanitized_stored_output) if sanitized_stored_output else 'undefined'
980
- message_error = 'output value mismatch:' + new_output_string + ' != ' + stored_output_string
981
- if strict_type_check and (self.lang != 'C#'):
982
- # upon building the request we want strict type check to make sure all the types are correct
983
- # when comparing the response we want to allow some flexibility, because a 50.0 can be equal to 50 after saving it to the json file
984
- self.assert_static_error(sanitized_new_output == sanitized_stored_output, message_error, stored_output, new_output, asserting_key)
985
- else:
986
- is_boolean = (isinstance(sanitized_new_output, bool)) or (isinstance(sanitized_stored_output, bool))
987
- is_string = (isinstance(sanitized_new_output, str)) or (isinstance(sanitized_stored_output, str))
988
- is_undefined = (sanitized_new_output is None) or (sanitized_stored_output is None) # undefined is a perfetly valid value
989
- if is_boolean or is_string or is_undefined:
990
- if self.lang == 'C#':
991
- # tmp c# number comparsion
992
- is_number = False
993
- try:
994
- exchange.parse_to_numeric(sanitized_new_output)
995
- is_number = True
996
- except Exception as e:
997
- # if we can't parse it to number, then it's not a number
998
- is_number = False
999
- if is_number:
1000
- self.assert_static_error(exchange.parse_to_numeric(sanitized_new_output) == exchange.parse_to_numeric(sanitized_stored_output), message_error, stored_output, new_output, asserting_key)
1001
- return True
1002
- else:
1003
- self.assert_static_error(convert_ascii(new_output_string) == convert_ascii(stored_output_string), message_error, stored_output, new_output, asserting_key)
1004
- return True
1005
- else:
1006
- self.assert_static_error(convert_ascii(new_output_string) == convert_ascii(stored_output_string), message_error, stored_output, new_output, asserting_key)
1007
- return True
1008
- else:
1009
- if self.lang == 'C#':
1010
- stringified_new_output = exchange.number_to_string(sanitized_new_output)
1011
- stringified_stored_output = exchange.number_to_string(sanitized_stored_output)
1012
- self.assert_static_error(str(stringified_new_output) == str(stringified_stored_output), message_error, stored_output, new_output, asserting_key)
1013
- else:
1014
- numeric_new_output = exchange.parse_to_numeric(new_output_string)
1015
- numeric_stored_output = exchange.parse_to_numeric(stored_output_string)
1016
- self.assert_static_error(numeric_new_output == numeric_stored_output, message_error, stored_output, new_output, asserting_key)
1017
- return True # c# requ
1018
-
1019
- def assert_static_request_output(self, exchange, type, skip_keys, stored_url, request_url, stored_output, new_output):
1020
- if stored_url != request_url:
1021
- # remove the host part from the url
1022
- first_path = self.remove_hostnamefrom_url(stored_url)
1023
- second_path = self.remove_hostnamefrom_url(request_url)
1024
- self.assert_static_error(first_path == second_path, 'url mismatch', first_path, second_path)
1025
- # body (aka storedOutput and newOutput) is not defined and information is in the url
1026
- # example: "https://open-api.bingx.com/openApi/spot/v1/trade/order?quoteOrderQty=5&side=BUY&symbol=LTC-USDT&timestamp=1698777135343&type=MARKET&signature=d55a7e4f7f9dbe56c4004c9f3ab340869d3cb004e2f0b5b861e5fbd1762fd9a0
1027
- if (stored_output is None) and (new_output is None):
1028
- if (stored_url is not None) and (request_url is not None):
1029
- stored_url_parts = stored_url.split('?')
1030
- new_url_parts = request_url.split('?')
1031
- stored_url_query = exchange.safe_value(stored_url_parts, 1)
1032
- new_url_query = exchange.safe_value(new_url_parts, 1)
1033
- if (stored_url_query is None) and (new_url_query is None):
1034
- # might be a get request without any query parameters
1035
- # example: https://api.gateio.ws/api/v4/delivery/usdt/positions
1036
- return
1037
- stored_url_params = self.urlencoded_to_dict(stored_url_query)
1038
- new_url_params = self.urlencoded_to_dict(new_url_query)
1039
- self.assert_new_and_stored_output(exchange, skip_keys, new_url_params, stored_url_params)
1040
- return
1041
- if type == 'json' and (stored_output is not None) and (new_output is not None):
1042
- if isinstance(stored_output, str):
1043
- stored_output = json_parse(stored_output)
1044
- if isinstance(new_output, str):
1045
- new_output = json_parse(new_output)
1046
- elif type == 'urlencoded' and (stored_output is not None) and (new_output is not None):
1047
- stored_output = self.urlencoded_to_dict(stored_output)
1048
- new_output = self.urlencoded_to_dict(new_output)
1049
- elif type == 'both':
1050
- if stored_output.startswith('{') or stored_output.startswith('['):
1051
- stored_output = json_parse(stored_output)
1052
- new_output = json_parse(new_output)
1053
- else:
1054
- stored_output = self.urlencoded_to_dict(stored_output)
1055
- new_output = self.urlencoded_to_dict(new_output)
1056
- self.assert_new_and_stored_output(exchange, skip_keys, new_output, stored_output)
1057
-
1058
- def assert_static_response_output(self, exchange, skip_keys, computed_result, stored_result):
1059
- self.assert_new_and_stored_output(exchange, skip_keys, computed_result, stored_result, False)
1060
-
1061
- def sanitize_data_input(self, input):
1062
- # remove nulls and replace with unefined instead
1063
- if input is None:
1064
- return None
1065
- new_input = []
1066
- for i in range(0, len(input)):
1067
- current = input[i]
1068
- if is_null_value(current):
1069
- new_input.append(None)
1070
- else:
1071
- new_input.append(current)
1072
- return new_input
1073
-
1074
- async def test_method_statically(self, exchange, method, data, type, skip_keys):
1075
- output = None
1076
- request_url = None
1077
- try:
1078
- await call_exchange_method_dynamically(exchange, method, self.sanitize_data_input(data['input']))
1079
- except Exception as e:
1080
- if not (isinstance(e, ProxyError)):
1081
- raise e
1082
- output = exchange.last_request_body
1083
- request_url = exchange.last_request_url
1084
- try:
1085
- call_output = exchange.safe_value(data, 'output')
1086
- self.assert_static_request_output(exchange, type, skip_keys, data['url'], request_url, call_output, output)
1087
- except Exception as e:
1088
- self.request_tests_failed = True
1089
- error_message = '[' + self.lang + '][STATIC_REQUEST_TEST_FAILURE]' + '[' + self.exchange_hint(exchange) + ']' + '[' + method + ']' + '[' + data['description'] + ']' + str(e)
1090
- dump('[TEST_FAILURE]' + error_message)
1091
-
1092
- async def test_response_statically(self, exchange, method, skip_keys, data):
1093
- expected_result = exchange.safe_value(data, 'parsedResponse')
1094
- mocked_exchange = set_fetch_response(exchange, data['httpResponse'])
1095
- try:
1096
- unified_result = await call_exchange_method_dynamically(exchange, method, self.sanitize_data_input(data['input']))
1097
- self.assert_static_response_output(mocked_exchange, skip_keys, unified_result, expected_result)
1098
- except Exception as e:
1099
- self.request_tests_failed = True
1100
- error_message = '[' + self.lang + '][STATIC_RESPONSE_TEST_FAILURE]' + '[' + self.exchange_hint(exchange) + ']' + '[' + method + ']' + '[' + data['description'] + ']' + str(e)
1101
- dump('[TEST_FAILURE]' + error_message)
1102
- set_fetch_response(exchange, None) # reset state
1103
-
1104
- def init_offline_exchange(self, exchange_name):
1105
- markets = self.load_markets_from_file(exchange_name)
1106
- currencies = self.load_currencies_from_file(exchange_name)
1107
- exchange = init_exchange(exchange_name, {
1108
- 'markets': markets,
1109
- 'currencies': currencies,
1110
- 'enableRateLimit': False,
1111
- 'rateLimit': 1,
1112
- 'httpProxy': 'http://fake:8080',
1113
- 'httpsProxy': 'http://fake:8080',
1114
- 'apiKey': 'key',
1115
- 'secret': 'secretsecret',
1116
- 'password': 'password',
1117
- 'walletAddress': 'wallet',
1118
- 'privateKey': '0xff3bdd43534543d421f05aec535965b5050ad6ac15345435345435453495e771',
1119
- 'uid': 'uid',
1120
- 'token': 'token',
1121
- 'accountId': 'accountId',
1122
- 'accounts': [{
1123
- 'id': 'myAccount',
1124
- 'code': 'USDT',
1125
- }, {
1126
- 'id': 'myAccount',
1127
- 'code': 'USDC',
1128
- }],
1129
- 'options': {
1130
- 'enableUnifiedAccount': True,
1131
- 'enableUnifiedMargin': False,
1132
- 'accessToken': 'token',
1133
- 'expires': 999999999999999,
1134
- 'leverageBrackets': {},
1135
- },
1136
- })
1137
- exchange.currencies = currencies # not working in python if assigned in the config dict
1138
- return exchange
1139
-
1140
- async def test_exchange_request_statically(self, exchange_name, exchange_data, test_name=None):
1141
- # instantiate the exchange and make sure that we sink the requests to avoid an actual request
1142
- exchange = self.init_offline_exchange(exchange_name)
1143
- global_options = exchange.safe_dict(exchange_data, 'options', {})
1144
- # read apiKey/secret from the test file
1145
- api_key = exchange.safe_string(exchange_data, 'apiKey')
1146
- if api_key:
1147
- exchange.api_key = str(api_key)
1148
- secret = exchange.safe_string(exchange_data, 'secret')
1149
- if secret:
1150
- exchange.secret = str(secret)
1151
- # exchange.options = exchange.deepExtend (exchange.options, globalOptions); # custom options to be used in the tests
1152
- exchange.extend_exchange_options(global_options)
1153
- methods = exchange.safe_value(exchange_data, 'methods', {})
1154
- methods_names = list(methods.keys())
1155
- for i in range(0, len(methods_names)):
1156
- method = methods_names[i]
1157
- results = methods[method]
1158
- for j in range(0, len(results)):
1159
- result = results[j]
1160
- old_exchange_options = exchange.options # snapshot options;
1161
- test_exchange_options = exchange.safe_value(result, 'options', {})
1162
- # exchange.options = exchange.deepExtend (oldExchangeOptions, testExchangeOptions); # custom options to be used in the tests
1163
- exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, test_exchange_options))
1164
- description = exchange.safe_value(result, 'description')
1165
- if (test_name is not None) and (test_name != description):
1166
- continue
1167
- is_disabled = exchange.safe_bool(result, 'disabled', False)
1168
- if is_disabled:
1169
- continue
1170
- type = exchange.safe_string(exchange_data, 'outputType')
1171
- skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
1172
- await self.test_method_statically(exchange, method, result, type, skip_keys)
1173
- # reset options
1174
- # exchange.options = exchange.deepExtend (oldExchangeOptions, {});
1175
- exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, {}))
1176
- await close(exchange)
1177
- return True # in c# methods that will be used with promiseAll need to return something
1178
-
1179
- async def test_exchange_response_statically(self, exchange_name, exchange_data, test_name=None):
1180
- exchange = self.init_offline_exchange(exchange_name)
1181
- # read apiKey/secret from the test file
1182
- api_key = exchange.safe_string(exchange_data, 'apiKey')
1183
- if api_key:
1184
- exchange.api_key = str(api_key)
1185
- secret = exchange.safe_string(exchange_data, 'secret')
1186
- if secret:
1187
- exchange.secret = str(secret)
1188
- methods = exchange.safe_value(exchange_data, 'methods', {})
1189
- options = exchange.safe_value(exchange_data, 'options', {})
1190
- # exchange.options = exchange.deepExtend (exchange.options, options); # custom options to be used in the tests
1191
- exchange.extend_exchange_options(options)
1192
- methods_names = list(methods.keys())
1193
- for i in range(0, len(methods_names)):
1194
- method = methods_names[i]
1195
- results = methods[method]
1196
- for j in range(0, len(results)):
1197
- result = results[j]
1198
- description = exchange.safe_value(result, 'description')
1199
- old_exchange_options = exchange.options # snapshot options;
1200
- test_exchange_options = exchange.safe_value(result, 'options', {})
1201
- # exchange.options = exchange.deepExtend (oldExchangeOptions, testExchangeOptions); # custom options to be used in the tests
1202
- exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, test_exchange_options))
1203
- is_disabled = exchange.safe_bool(result, 'disabled', False)
1204
- if is_disabled:
1205
- continue
1206
- is_disabled_c_sharp = exchange.safe_bool(result, 'disabledCS', False)
1207
- if is_disabled_c_sharp and (self.lang == 'C#'):
1208
- continue
1209
- is_disabled_php = exchange.safe_bool(result, 'disabledPHP', False)
1210
- if is_disabled_php and (self.lang == 'PHP'):
1211
- continue
1212
- if (test_name is not None) and (test_name != description):
1213
- continue
1214
- skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
1215
- await self.test_response_statically(exchange, method, skip_keys, result)
1216
- # reset options
1217
- # exchange.options = exchange.deepExtend (oldExchangeOptions, {});
1218
- exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, {}))
1219
- await close(exchange)
1220
- return True # in c# methods that will be used with promiseAll need to return something
1221
-
1222
- def get_number_of_tests_from_exchange(self, exchange, exchange_data, test_name=None):
1223
- if test_name is not None:
1224
- return 1
1225
- sum = 0
1226
- methods = exchange_data['methods']
1227
- methods_names = list(methods.keys())
1228
- for i in range(0, len(methods_names)):
1229
- method = methods_names[i]
1230
- results = methods[method]
1231
- results_length = len(results)
1232
- sum = exchange.sum(sum, results_length)
1233
- return sum
1234
-
1235
- async def run_static_request_tests(self, target_exchange=None, test_name=None):
1236
- await self.run_static_tests('request', target_exchange, test_name)
1237
-
1238
- async def run_static_tests(self, type, target_exchange=None, test_name=None):
1239
- folder = self.root_dir + './ts/src/test/static/' + type + '/'
1240
- static_data = self.load_static_data(folder, target_exchange)
1241
- if static_data is None:
1242
- return
1243
- exchanges = list(static_data.keys())
1244
- exchange = init_exchange('Exchange', {}) # tmp to do the calculations until we have the ast-transpiler transpiling this code
1245
- promises = []
1246
- sum = 0
1247
- if target_exchange:
1248
- dump('[INFO:MAIN] Exchange to test: ' + target_exchange)
1249
- if test_name:
1250
- dump('[INFO:MAIN] Testing only: ' + test_name)
1251
- for i in range(0, len(exchanges)):
1252
- exchange_name = exchanges[i]
1253
- exchange_data = static_data[exchange_name]
1254
- number_of_tests = self.get_number_of_tests_from_exchange(exchange, exchange_data, test_name)
1255
- sum = exchange.sum(sum, number_of_tests)
1256
- if type == 'request':
1257
- promises.append(self.test_exchange_request_statically(exchange_name, exchange_data, test_name))
1258
- else:
1259
- promises.append(self.test_exchange_response_statically(exchange_name, exchange_data, test_name))
1260
- await asyncio.gather(*promises)
1261
- if self.request_tests_failed or self.response_tests_failed:
1262
- exit_script(1)
1263
- else:
1264
- success_message = '[' + self.lang + '][TEST_SUCCESS] ' + str(sum) + ' static ' + type + ' tests passed.'
1265
- dump('[INFO]' + success_message)
1266
-
1267
- async def run_static_response_tests(self, exchange_name=None, test=None):
1268
- # -----------------------------------------------------------------------------
1269
- # --- Init of mockResponses tests functions------------------------------------
1270
- # -----------------------------------------------------------------------------
1271
- await self.run_static_tests('response', exchange_name, test)
1272
-
1273
- async def run_broker_id_tests(self):
1274
- # -----------------------------------------------------------------------------
1275
- # --- Init of brokerId tests functions-----------------------------------------
1276
- # -----------------------------------------------------------------------------
1277
- promises = [self.test_binance(), self.test_okx(), self.test_cryptocom(), self.test_bybit(), self.test_kucoin(), self.test_kucoinfutures(), self.test_bitget(), self.test_mexc(), self.test_htx(), self.test_woo(), self.test_bitmart(), self.test_coinex(), self.test_bingx(), self.test_phemex(), self.test_blofin(), self.test_hyperliquid(), self.test_coinbaseinternational(), self.test_coinbase_advanced(), self.test_woofi_pro(), self.test_oxfun(), self.test_xt()]
1278
- await asyncio.gather(*promises)
1279
- success_message = '[' + self.lang + '][TEST_SUCCESS] brokerId tests passed.'
1280
- dump('[INFO]' + success_message)
1281
- exit_script(0)
1282
-
1283
- async def test_binance(self):
1284
- exchange = self.init_offline_exchange('binance')
1285
- spot_id = 'x-R4BD3S82'
1286
- spot_order_request = None
1287
- try:
1288
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1289
- except Exception as e:
1290
- spot_order_request = self.urlencoded_to_dict(exchange.last_request_body)
1291
- client_order_id = spot_order_request['newClientOrderId']
1292
- spot_id_string = str(spot_id)
1293
- assert client_order_id.startswith(spot_id_string), 'binance - spot clientOrderId: ' + client_order_id + ' does not start with spotId' + spot_id_string
1294
- swap_id = 'x-xcKtGhcu'
1295
- swap_order_request = None
1296
- try:
1297
- await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
1298
- except Exception as e:
1299
- swap_order_request = self.urlencoded_to_dict(exchange.last_request_body)
1300
- swap_inverse_order_request = None
1301
- try:
1302
- await exchange.create_order('BTC/USD:BTC', 'limit', 'buy', 1, 20000)
1303
- except Exception as e:
1304
- swap_inverse_order_request = self.urlencoded_to_dict(exchange.last_request_body)
1305
- client_order_id_swap = swap_order_request['newClientOrderId']
1306
- swap_id_string = str(swap_id)
1307
- assert client_order_id_swap.startswith(swap_id_string), 'binance - swap clientOrderId: ' + client_order_id_swap + ' does not start with swapId' + swap_id_string
1308
- client_order_id_inverse = swap_inverse_order_request['newClientOrderId']
1309
- assert client_order_id_inverse.startswith(swap_id_string), 'binance - swap clientOrderIdInverse: ' + client_order_id_inverse + ' does not start with swapId' + swap_id_string
1310
- await close(exchange)
1311
- return True
1312
-
1313
- async def test_okx(self):
1314
- exchange = self.init_offline_exchange('okx')
1315
- id = 'e847386590ce4dBC'
1316
- spot_order_request = None
1317
- try:
1318
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1319
- except Exception as e:
1320
- spot_order_request = json_parse(exchange.last_request_body)
1321
- client_order_id = spot_order_request[0]['clOrdId'] # returns order inside array
1322
- id_string = str(id)
1323
- assert client_order_id.startswith(id_string), 'okx - spot clientOrderId: ' + client_order_id + ' does not start with id: ' + id_string
1324
- spot_tag = spot_order_request[0]['tag']
1325
- assert spot_tag == id, 'okx - id: ' + id + ' different from spot tag: ' + spot_tag
1326
- swap_order_request = None
1327
- try:
1328
- await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
1329
- except Exception as e:
1330
- swap_order_request = json_parse(exchange.last_request_body)
1331
- client_order_id_swap = swap_order_request[0]['clOrdId']
1332
- assert client_order_id_swap.startswith(id_string), 'okx - swap clientOrderId: ' + client_order_id_swap + ' does not start with id: ' + id_string
1333
- swap_tag = swap_order_request[0]['tag']
1334
- assert swap_tag == id, 'okx - id: ' + id + ' different from swap tag: ' + swap_tag
1335
- await close(exchange)
1336
- return True
1337
-
1338
- async def test_cryptocom(self):
1339
- exchange = self.init_offline_exchange('cryptocom')
1340
- id = 'CCXT'
1341
- await exchange.load_markets()
1342
- request = None
1343
- try:
1344
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1345
- except Exception as e:
1346
- request = json_parse(exchange.last_request_body)
1347
- broker_id = request['params']['broker_id']
1348
- assert broker_id == id, 'cryptocom - id: ' + id + ' different from broker_id: ' + broker_id
1349
- await close(exchange)
1350
- return True
1351
-
1352
- async def test_bybit(self):
1353
- exchange = self.init_offline_exchange('bybit')
1354
- req_headers = None
1355
- id = 'CCXT'
1356
- assert exchange.options['brokerId'] == id, 'id not in options'
1357
- try:
1358
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1359
- except Exception as e:
1360
- # we expect an error here, we're only interested in the headers
1361
- req_headers = exchange.last_request_headers
1362
- assert req_headers['Referer'] == id, 'bybit - id: ' + id + ' not in headers.'
1363
- await close(exchange)
1364
- return True
1365
-
1366
- async def test_kucoin(self):
1367
- exchange = self.init_offline_exchange('kucoin')
1368
- req_headers = None
1369
- spot_id = exchange.options['partner']['spot']['id']
1370
- spot_key = exchange.options['partner']['spot']['key']
1371
- assert spot_id == 'ccxt', 'kucoin - id: ' + spot_id + ' not in options'
1372
- assert spot_key == '9e58cc35-5b5e-4133-92ec-166e3f077cb8', 'kucoin - key: ' + spot_key + ' not in options.'
1373
- try:
1374
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1375
- except Exception as e:
1376
- # we expect an error here, we're only interested in the headers
1377
- req_headers = exchange.last_request_headers
1378
- id = 'ccxt'
1379
- assert req_headers['KC-API-PARTNER'] == id, 'kucoin - id: ' + id + ' not in headers.'
1380
- await close(exchange)
1381
- return True
1382
-
1383
- async def test_kucoinfutures(self):
1384
- exchange = self.init_offline_exchange('kucoinfutures')
1385
- req_headers = None
1386
- id = 'ccxtfutures'
1387
- future_id = exchange.options['partner']['future']['id']
1388
- future_key = exchange.options['partner']['future']['key']
1389
- assert future_id == id, 'kucoinfutures - id: ' + future_id + ' not in options.'
1390
- assert future_key == '1b327198-f30c-4f14-a0ac-918871282f15', 'kucoinfutures - key: ' + future_key + ' not in options.'
1391
- try:
1392
- await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
1393
- except Exception as e:
1394
- req_headers = exchange.last_request_headers
1395
- assert req_headers['KC-API-PARTNER'] == id, 'kucoinfutures - id: ' + id + ' not in headers.'
1396
- await close(exchange)
1397
- return True
1398
-
1399
- async def test_bitget(self):
1400
- exchange = self.init_offline_exchange('bitget')
1401
- req_headers = None
1402
- id = 'p4sve'
1403
- assert exchange.options['broker'] == id, 'bitget - id: ' + id + ' not in options'
1404
- try:
1405
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1406
- except Exception as e:
1407
- req_headers = exchange.last_request_headers
1408
- assert req_headers['X-CHANNEL-API-CODE'] == id, 'bitget - id: ' + id + ' not in headers.'
1409
- await close(exchange)
1410
- return True
1411
-
1412
- async def test_mexc(self):
1413
- exchange = self.init_offline_exchange('mexc')
1414
- req_headers = None
1415
- id = 'CCXT'
1416
- assert exchange.options['broker'] == id, 'mexc - id: ' + id + ' not in options'
1417
- await exchange.load_markets()
1418
- try:
1419
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1420
- except Exception as e:
1421
- req_headers = exchange.last_request_headers
1422
- assert req_headers['source'] == id, 'mexc - id: ' + id + ' not in headers.'
1423
- await close(exchange)
1424
- return True
1425
-
1426
- async def test_htx(self):
1427
- exchange = self.init_offline_exchange('htx')
1428
- # spot test
1429
- id = 'AA03022abc'
1430
- spot_order_request = None
1431
- try:
1432
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1433
- except Exception as e:
1434
- spot_order_request = json_parse(exchange.last_request_body)
1435
- client_order_id = spot_order_request['client-order-id']
1436
- id_string = str(id)
1437
- assert client_order_id.startswith(id_string), 'htx - spot clientOrderId ' + client_order_id + ' does not start with id: ' + id_string
1438
- # swap test
1439
- swap_order_request = None
1440
- try:
1441
- await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
1442
- except Exception as e:
1443
- swap_order_request = json_parse(exchange.last_request_body)
1444
- swap_inverse_order_request = None
1445
- try:
1446
- await exchange.create_order('BTC/USD:BTC', 'limit', 'buy', 1, 20000)
1447
- except Exception as e:
1448
- swap_inverse_order_request = json_parse(exchange.last_request_body)
1449
- client_order_id_swap = swap_order_request['channel_code']
1450
- assert client_order_id_swap.startswith(id_string), 'htx - swap channel_code ' + client_order_id_swap + ' does not start with id: ' + id_string
1451
- client_order_id_inverse = swap_inverse_order_request['channel_code']
1452
- assert client_order_id_inverse.startswith(id_string), 'htx - swap inverse channel_code ' + client_order_id_inverse + ' does not start with id: ' + id_string
1453
- await close(exchange)
1454
- return True
1455
-
1456
- async def test_woo(self):
1457
- exchange = self.init_offline_exchange('woo')
1458
- # spot test
1459
- id = 'bc830de7-50f3-460b-9ee0-f430f83f9dad'
1460
- spot_order_request = None
1461
- try:
1462
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1463
- except Exception as e:
1464
- spot_order_request = self.urlencoded_to_dict(exchange.last_request_body)
1465
- broker_id = spot_order_request['broker_id']
1466
- id_string = str(id)
1467
- assert broker_id.startswith(id_string), 'woo - broker_id: ' + broker_id + ' does not start with id: ' + id_string
1468
- # swap test
1469
- stop_order_request = None
1470
- try:
1471
- await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000, {
1472
- 'stopPrice': 30000,
1473
- })
1474
- except Exception as e:
1475
- stop_order_request = json_parse(exchange.last_request_body)
1476
- client_order_id_stop = stop_order_request['brokerId']
1477
- assert client_order_id_stop.startswith(id_string), 'woo - brokerId: ' + client_order_id_stop + ' does not start with id: ' + id_string
1478
- await close(exchange)
1479
- return True
1480
-
1481
- async def test_bitmart(self):
1482
- exchange = self.init_offline_exchange('bitmart')
1483
- req_headers = None
1484
- id = 'CCXTxBitmart000'
1485
- assert exchange.options['brokerId'] == id, 'bitmart - id: ' + id + ' not in options'
1486
- await exchange.load_markets()
1487
- try:
1488
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1489
- except Exception as e:
1490
- req_headers = exchange.last_request_headers
1491
- assert req_headers['X-BM-BROKER-ID'] == id, 'bitmart - id: ' + id + ' not in headers'
1492
- await close(exchange)
1493
- return True
1494
-
1495
- async def test_coinex(self):
1496
- exchange = self.init_offline_exchange('coinex')
1497
- id = 'x-167673045'
1498
- assert exchange.options['brokerId'] == id, 'coinex - id: ' + id + ' not in options'
1499
- spot_order_request = None
1500
- try:
1501
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1502
- except Exception as e:
1503
- spot_order_request = json_parse(exchange.last_request_body)
1504
- client_order_id = spot_order_request['client_id']
1505
- id_string = str(id)
1506
- assert client_order_id.startswith(id_string), 'coinex - clientOrderId: ' + client_order_id + ' does not start with id: ' + id_string
1507
- await close(exchange)
1508
- return True
1509
-
1510
- async def test_bingx(self):
1511
- exchange = self.init_offline_exchange('bingx')
1512
- req_headers = None
1513
- id = 'CCXT'
1514
- assert exchange.options['broker'] == id, 'bingx - id: ' + id + ' not in options'
1515
- try:
1516
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1517
- except Exception as e:
1518
- # we expect an error here, we're only interested in the headers
1519
- req_headers = exchange.last_request_headers
1520
- assert req_headers['X-SOURCE-KEY'] == id, 'bingx - id: ' + id + ' not in headers.'
1521
- await close(exchange)
1522
-
1523
- async def test_phemex(self):
1524
- exchange = self.init_offline_exchange('phemex')
1525
- id = 'CCXT123456'
1526
- request = None
1527
- try:
1528
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1529
- except Exception as e:
1530
- request = json_parse(exchange.last_request_body)
1531
- client_order_id = request['clOrdID']
1532
- id_string = str(id)
1533
- assert client_order_id.startswith(id_string), 'phemex - clOrdID: ' + client_order_id + ' does not start with id: ' + id_string
1534
- await close(exchange)
1535
-
1536
- async def test_blofin(self):
1537
- exchange = self.init_offline_exchange('blofin')
1538
- id = 'ec6dd3a7dd982d0b'
1539
- request = None
1540
- try:
1541
- await exchange.create_order('LTC/USDT:USDT', 'market', 'buy', 1)
1542
- except Exception as e:
1543
- request = json_parse(exchange.last_request_body)
1544
- broker_id = request['brokerId']
1545
- id_string = str(id)
1546
- assert broker_id.startswith(id_string), 'blofin - brokerId: ' + broker_id + ' does not start with id: ' + id_string
1547
- await close(exchange)
1548
-
1549
- async def test_hyperliquid(self):
1550
- exchange = self.init_offline_exchange('hyperliquid')
1551
- id = '1'
1552
- request = None
1553
- try:
1554
- await exchange.create_order('SOL/USDC:USDC', 'limit', 'buy', 1, 100)
1555
- except Exception as e:
1556
- request = json_parse(exchange.last_request_body)
1557
- broker_id = str((request['action']['brokerCode']))
1558
- assert broker_id == id, 'hyperliquid - brokerId: ' + broker_id + ' does not start with id: ' + id
1559
- await close(exchange)
1560
-
1561
- async def test_coinbaseinternational(self):
1562
- exchange = self.init_offline_exchange('coinbaseinternational')
1563
- exchange.options['portfolio'] = 'random'
1564
- id = 'nfqkvdjp'
1565
- assert exchange.options['brokerId'] == id, 'id not in options'
1566
- request = None
1567
- try:
1568
- await exchange.create_order('BTC/USDC:USDC', 'limit', 'buy', 1, 20000)
1569
- except Exception as e:
1570
- request = json_parse(exchange.last_request_body)
1571
- client_order_id = request['client_order_id']
1572
- assert client_order_id.startswith(str(id)), 'clientOrderId does not start with id'
1573
- await close(exchange)
1574
- return True
1575
-
1576
- async def test_coinbase_advanced(self):
1577
- exchange = self.init_offline_exchange('coinbase')
1578
- id = 'ccxt'
1579
- assert exchange.options['brokerId'] == id, 'id not in options'
1580
- request = None
1581
- try:
1582
- await exchange.create_order('BTC/USDC', 'limit', 'buy', 1, 20000)
1583
- except Exception as e:
1584
- request = json_parse(exchange.last_request_body)
1585
- client_order_id = request['client_order_id']
1586
- assert client_order_id.startswith(str(id)), 'clientOrderId does not start with id'
1587
- await close(exchange)
1588
- return True
1589
-
1590
- async def test_woofi_pro(self):
1591
- exchange = self.init_offline_exchange('woofipro')
1592
- exchange.secret = 'secretsecretsecretsecretsecretsecretsecrets'
1593
- id = 'CCXT'
1594
- await exchange.load_markets()
1595
- request = None
1596
- try:
1597
- await exchange.create_order('BTC/USDC:USDC', 'limit', 'buy', 1, 20000)
1598
- except Exception as e:
1599
- request = json_parse(exchange.last_request_body)
1600
- broker_id = request['order_tag']
1601
- assert broker_id == id, 'woofipro - id: ' + id + ' different from broker_id: ' + broker_id
1602
- await close(exchange)
1603
- return True
1604
-
1605
- async def test_oxfun(self):
1606
- exchange = self.init_offline_exchange('oxfun')
1607
- exchange.secret = 'secretsecretsecretsecretsecretsecretsecrets'
1608
- id = 1000
1609
- await exchange.load_markets()
1610
- request = None
1611
- try:
1612
- await exchange.create_order('BTC/USD:OX', 'limit', 'buy', 1, 20000)
1613
- except Exception as e:
1614
- request = json_parse(exchange.last_request_body)
1615
- orders = request['orders']
1616
- first = orders[0]
1617
- broker_id = first['source']
1618
- assert broker_id == id, 'oxfun - id: ' + str(id) + ' different from broker_id: ' + str(broker_id)
1619
- return True
1620
-
1621
- async def test_xt(self):
1622
- exchange = self.init_offline_exchange('xt')
1623
- id = 'CCXT'
1624
- spot_order_request = None
1625
- try:
1626
- await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1627
- except Exception as e:
1628
- spot_order_request = json_parse(exchange.last_request_body)
1629
- spot_media = spot_order_request['media']
1630
- assert spot_media == id, 'xt - id: ' + id + ' different from swap tag: ' + spot_media
1631
- swap_order_request = None
1632
- try:
1633
- await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
1634
- except Exception as e:
1635
- swap_order_request = json_parse(exchange.last_request_body)
1636
- swap_media = swap_order_request['clientMedia']
1637
- assert swap_media == id, 'xt - id: ' + id + ' different from swap tag: ' + swap_media
1638
- await close(exchange)
1639
- return True
1640
-
1641
- # ***** AUTO-TRANSPILER-END *****
1642
- # *******************************
1643
-
1644
-
1645
- if __name__ == '__main__':
1646
- argvSymbol = argv.symbol if argv.symbol and '/' in argv.symbol else None
1647
- # in python, we check it through "symbol" arg (as opposed to JS/PHP) because argvs were already built above
1648
- argvMethod = argv.symbol if argv.symbol and '()' in argv.symbol else None
1649
- asyncio.run(testMainClass().init(argv.exchange, argvSymbol, argvMethod))