ccxt-ir 4.3.46.0.1__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 (772) hide show
  1. ccxt/__init__.py +358 -0
  2. ccxt/abantether.py +316 -0
  3. ccxt/abstract/__init__.py +0 -0
  4. ccxt/abstract/abantether.py +5 -0
  5. ccxt/abstract/ace.py +15 -0
  6. ccxt/abstract/afratether.py +6 -0
  7. ccxt/abstract/alpaca.py +70 -0
  8. ccxt/abstract/arzinja.py +5 -0
  9. ccxt/abstract/arzplus.py +7 -0
  10. ccxt/abstract/ascendex.py +77 -0
  11. ccxt/abstract/bequant.py +115 -0
  12. ccxt/abstract/bigone.py +45 -0
  13. ccxt/abstract/binance.py +712 -0
  14. ccxt/abstract/binancecoinm.py +712 -0
  15. ccxt/abstract/binanceus.py +764 -0
  16. ccxt/abstract/binanceusdm.py +712 -0
  17. ccxt/abstract/bingx.py +113 -0
  18. ccxt/abstract/bit2c.py +27 -0
  19. ccxt/abstract/bitbank.py +27 -0
  20. ccxt/abstract/bitbay.py +53 -0
  21. ccxt/abstract/bitbns.py +40 -0
  22. ccxt/abstract/bitcoincom.py +115 -0
  23. ccxt/abstract/bitfinex.py +69 -0
  24. ccxt/abstract/bitfinex2.py +139 -0
  25. ccxt/abstract/bitflyer.py +38 -0
  26. ccxt/abstract/bitget.py +508 -0
  27. ccxt/abstract/bithumb.py +32 -0
  28. ccxt/abstract/bitimen.py +7 -0
  29. ccxt/abstract/bitir.py +7 -0
  30. ccxt/abstract/bitmart.py +99 -0
  31. ccxt/abstract/bitmex.py +97 -0
  32. ccxt/abstract/bitopro.py +29 -0
  33. ccxt/abstract/bitpanda.py +35 -0
  34. ccxt/abstract/bitpin.py +7 -0
  35. ccxt/abstract/bitrue.py +72 -0
  36. ccxt/abstract/bitso.py +43 -0
  37. ccxt/abstract/bitstamp.py +258 -0
  38. ccxt/abstract/bitteam.py +29 -0
  39. ccxt/abstract/bitvavo.py +27 -0
  40. ccxt/abstract/bl3p.py +19 -0
  41. ccxt/abstract/blockchaincom.py +28 -0
  42. ccxt/abstract/blofin.py +37 -0
  43. ccxt/abstract/btcalpha.py +18 -0
  44. ccxt/abstract/btcbox.py +13 -0
  45. ccxt/abstract/btcmarkets.py +39 -0
  46. ccxt/abstract/btcturk.py +20 -0
  47. ccxt/abstract/bybit.py +298 -0
  48. ccxt/abstract/cex.py +33 -0
  49. ccxt/abstract/coinbase.py +94 -0
  50. ccxt/abstract/coinbaseadvanced.py +94 -0
  51. ccxt/abstract/coinbaseexchange.py +67 -0
  52. ccxt/abstract/coinbaseinternational.py +39 -0
  53. ccxt/abstract/coincatch.py +94 -0
  54. ccxt/abstract/coincheck.py +33 -0
  55. ccxt/abstract/coinex.py +237 -0
  56. ccxt/abstract/coinlist.py +54 -0
  57. ccxt/abstract/coinmate.py +62 -0
  58. ccxt/abstract/coinmetro.py +34 -0
  59. ccxt/abstract/coinone.py +67 -0
  60. ccxt/abstract/coinsph.py +54 -0
  61. ccxt/abstract/coinspot.py +28 -0
  62. ccxt/abstract/cryptocom.py +107 -0
  63. ccxt/abstract/currencycom.py +68 -0
  64. ccxt/abstract/delta.py +50 -0
  65. ccxt/abstract/deribit.py +125 -0
  66. ccxt/abstract/digifinex.py +91 -0
  67. ccxt/abstract/eterex.py +5 -0
  68. ccxt/abstract/excoino.py +7 -0
  69. ccxt/abstract/exir.py +8 -0
  70. ccxt/abstract/exmo.py +55 -0
  71. ccxt/abstract/exnovin.py +6 -0
  72. ccxt/abstract/farhadexchange.py +5 -0
  73. ccxt/abstract/fmfwio.py +115 -0
  74. ccxt/abstract/gate.py +265 -0
  75. ccxt/abstract/gateio.py +265 -0
  76. ccxt/abstract/gemini.py +58 -0
  77. ccxt/abstract/hashkey.py +67 -0
  78. ccxt/abstract/hitbtc.py +115 -0
  79. ccxt/abstract/hitbtc3.py +115 -0
  80. ccxt/abstract/hitobit.py +8 -0
  81. ccxt/abstract/hollaex.py +33 -0
  82. ccxt/abstract/htx.py +548 -0
  83. ccxt/abstract/huobi.py +548 -0
  84. ccxt/abstract/huobijp.py +114 -0
  85. ccxt/abstract/hyperliquid.py +6 -0
  86. ccxt/abstract/idex.py +26 -0
  87. ccxt/abstract/independentreserve.py +37 -0
  88. ccxt/abstract/indodax.py +26 -0
  89. ccxt/abstract/jibitex.py +7 -0
  90. ccxt/abstract/kraken.py +57 -0
  91. ccxt/abstract/krakenfutures.py +38 -0
  92. ccxt/abstract/kucoin.py +214 -0
  93. ccxt/abstract/kucoinfutures.py +233 -0
  94. ccxt/abstract/kuna.py +182 -0
  95. ccxt/abstract/latoken.py +56 -0
  96. ccxt/abstract/lbank.py +61 -0
  97. ccxt/abstract/luno.py +37 -0
  98. ccxt/abstract/lykke.py +29 -0
  99. ccxt/abstract/mercado.py +25 -0
  100. ccxt/abstract/mexc.py +178 -0
  101. ccxt/abstract/ndax.py +97 -0
  102. ccxt/abstract/nobitex.py +7 -0
  103. ccxt/abstract/novadax.py +29 -0
  104. ccxt/abstract/oceanex.py +22 -0
  105. ccxt/abstract/okcoin.py +74 -0
  106. ccxt/abstract/okexchange.py +8 -0
  107. ccxt/abstract/okx.py +324 -0
  108. ccxt/abstract/ompfinex.py +7 -0
  109. ccxt/abstract/onetrading.py +35 -0
  110. ccxt/abstract/oxfun.py +34 -0
  111. ccxt/abstract/p2b.py +22 -0
  112. ccxt/abstract/paradex.py +40 -0
  113. ccxt/abstract/paymium.py +28 -0
  114. ccxt/abstract/phemex.py +115 -0
  115. ccxt/abstract/poloniex.py +69 -0
  116. ccxt/abstract/poloniexfutures.py +48 -0
  117. ccxt/abstract/probit.py +23 -0
  118. ccxt/abstract/ramzinex.py +7 -0
  119. ccxt/abstract/sarmayex.py +5 -0
  120. ccxt/abstract/sarrafex.py +7 -0
  121. ccxt/abstract/tabdeal.py +7 -0
  122. ccxt/abstract/tetherland.py +5 -0
  123. ccxt/abstract/timex.py +62 -0
  124. ccxt/abstract/tokocrypto.py +37 -0
  125. ccxt/abstract/tradeogre.py +16 -0
  126. ccxt/abstract/twox.py +5 -0
  127. ccxt/abstract/ubitex.py +7 -0
  128. ccxt/abstract/upbit.py +38 -0
  129. ccxt/abstract/vertex.py +19 -0
  130. ccxt/abstract/wallex.py +8 -0
  131. ccxt/abstract/wavesexchange.py +154 -0
  132. ccxt/abstract/wazirx.py +30 -0
  133. ccxt/abstract/whitebit.py +98 -0
  134. ccxt/abstract/woo.py +83 -0
  135. ccxt/abstract/woofipro.py +119 -0
  136. ccxt/abstract/xt.py +152 -0
  137. ccxt/abstract/yobit.py +16 -0
  138. ccxt/abstract/zaif.py +38 -0
  139. ccxt/abstract/zonda.py +53 -0
  140. ccxt/ace.py +1012 -0
  141. ccxt/afratether.py +293 -0
  142. ccxt/alpaca.py +1083 -0
  143. ccxt/arzinja.py +285 -0
  144. ccxt/arzplus.py +412 -0
  145. ccxt/ascendex.py +3330 -0
  146. ccxt/async_support/__init__.py +337 -0
  147. ccxt/async_support/abantether.py +316 -0
  148. ccxt/async_support/ace.py +1012 -0
  149. ccxt/async_support/afratether.py +293 -0
  150. ccxt/async_support/alpaca.py +1083 -0
  151. ccxt/async_support/arzinja.py +285 -0
  152. ccxt/async_support/arzplus.py +412 -0
  153. ccxt/async_support/ascendex.py +3330 -0
  154. ccxt/async_support/base/__init__.py +1 -0
  155. ccxt/async_support/base/exchange.py +1966 -0
  156. ccxt/async_support/base/throttler.py +50 -0
  157. ccxt/async_support/base/ws/__init__.py +38 -0
  158. ccxt/async_support/base/ws/aiohttp_client.py +125 -0
  159. ccxt/async_support/base/ws/cache.py +212 -0
  160. ccxt/async_support/base/ws/client.py +193 -0
  161. ccxt/async_support/base/ws/fast_client.py +96 -0
  162. ccxt/async_support/base/ws/functions.py +59 -0
  163. ccxt/async_support/base/ws/future.py +58 -0
  164. ccxt/async_support/base/ws/order_book.py +78 -0
  165. ccxt/async_support/base/ws/order_book_side.py +174 -0
  166. ccxt/async_support/bequant.py +33 -0
  167. ccxt/async_support/bigone.py +2113 -0
  168. ccxt/async_support/binance.py +12234 -0
  169. ccxt/async_support/binancecoinm.py +45 -0
  170. ccxt/async_support/binanceus.py +211 -0
  171. ccxt/async_support/binanceusdm.py +58 -0
  172. ccxt/async_support/bingx.py +4325 -0
  173. ccxt/async_support/bit2c.py +866 -0
  174. ccxt/async_support/bitbank.py +1001 -0
  175. ccxt/async_support/bitbay.py +17 -0
  176. ccxt/async_support/bitbns.py +1154 -0
  177. ccxt/async_support/bitcoincom.py +17 -0
  178. ccxt/async_support/bitfinex.py +1617 -0
  179. ccxt/async_support/bitfinex2.py +3552 -0
  180. ccxt/async_support/bitflyer.py +995 -0
  181. ccxt/async_support/bitget.py +8273 -0
  182. ccxt/async_support/bithumb.py +1061 -0
  183. ccxt/async_support/bitimen.py +401 -0
  184. ccxt/async_support/bitir.py +490 -0
  185. ccxt/async_support/bitmart.py +4415 -0
  186. ccxt/async_support/bitmex.py +2756 -0
  187. ccxt/async_support/bitopro.py +1630 -0
  188. ccxt/async_support/bitpanda.py +16 -0
  189. ccxt/async_support/bitpin.py +454 -0
  190. ccxt/async_support/bitrue.py +3027 -0
  191. ccxt/async_support/bitso.py +1670 -0
  192. ccxt/async_support/bitstamp.py +2203 -0
  193. ccxt/async_support/bitteam.py +2239 -0
  194. ccxt/async_support/bitvavo.py +1968 -0
  195. ccxt/async_support/bl3p.py +485 -0
  196. ccxt/async_support/blockchaincom.py +1104 -0
  197. ccxt/async_support/blofin.py +2066 -0
  198. ccxt/async_support/btcalpha.py +891 -0
  199. ccxt/async_support/btcbox.py +544 -0
  200. ccxt/async_support/btcmarkets.py +1221 -0
  201. ccxt/async_support/btcturk.py +911 -0
  202. ccxt/async_support/bybit.py +8159 -0
  203. ccxt/async_support/cex.py +1605 -0
  204. ccxt/async_support/coinbase.py +4475 -0
  205. ccxt/async_support/coinbaseadvanced.py +17 -0
  206. ccxt/async_support/coinbaseexchange.py +1734 -0
  207. ccxt/async_support/coinbaseinternational.py +1899 -0
  208. ccxt/async_support/coincatch.py +5069 -0
  209. ccxt/async_support/coincheck.py +815 -0
  210. ccxt/async_support/coinex.py +5526 -0
  211. ccxt/async_support/coinlist.py +2243 -0
  212. ccxt/async_support/coinmate.py +1067 -0
  213. ccxt/async_support/coinmetro.py +1797 -0
  214. ccxt/async_support/coinone.py +1127 -0
  215. ccxt/async_support/coinsph.py +1850 -0
  216. ccxt/async_support/coinspot.py +534 -0
  217. ccxt/async_support/cryptocom.py +2822 -0
  218. ccxt/async_support/currencycom.py +1950 -0
  219. ccxt/async_support/delta.py +3376 -0
  220. ccxt/async_support/deribit.py +3437 -0
  221. ccxt/async_support/digifinex.py +3960 -0
  222. ccxt/async_support/eterex.py +286 -0
  223. ccxt/async_support/excoino.py +399 -0
  224. ccxt/async_support/exir.py +375 -0
  225. ccxt/async_support/exmo.py +2462 -0
  226. ccxt/async_support/exnovin.py +360 -0
  227. ccxt/async_support/farhadexchange.py +266 -0
  228. ccxt/async_support/fmfwio.py +34 -0
  229. ccxt/async_support/gate.py +6976 -0
  230. ccxt/async_support/gateio.py +16 -0
  231. ccxt/async_support/gemini.py +1825 -0
  232. ccxt/async_support/hashkey.py +4150 -0
  233. ccxt/async_support/hitbtc.py +3423 -0
  234. ccxt/async_support/hitbtc3.py +16 -0
  235. ccxt/async_support/hitobit.py +391 -0
  236. ccxt/async_support/hollaex.py +1813 -0
  237. ccxt/async_support/htx.py +8506 -0
  238. ccxt/async_support/huobi.py +16 -0
  239. ccxt/async_support/huobijp.py +1801 -0
  240. ccxt/async_support/hyperliquid.py +2431 -0
  241. ccxt/async_support/idex.py +1766 -0
  242. ccxt/async_support/independentreserve.py +784 -0
  243. ccxt/async_support/indodax.py +1247 -0
  244. ccxt/async_support/jibitex.py +395 -0
  245. ccxt/async_support/kraken.py +2894 -0
  246. ccxt/async_support/krakenfutures.py +2601 -0
  247. ccxt/async_support/kucoin.py +4602 -0
  248. ccxt/async_support/kucoinfutures.py +2698 -0
  249. ccxt/async_support/kuna.py +1841 -0
  250. ccxt/async_support/latoken.py +1664 -0
  251. ccxt/async_support/lbank.py +2683 -0
  252. ccxt/async_support/luno.py +1067 -0
  253. ccxt/async_support/lykke.py +1270 -0
  254. ccxt/async_support/mercado.py +842 -0
  255. ccxt/async_support/mexc.py +5369 -0
  256. ccxt/async_support/ndax.py +2354 -0
  257. ccxt/async_support/nobitex.py +419 -0
  258. ccxt/async_support/novadax.py +1484 -0
  259. ccxt/async_support/oceanex.py +903 -0
  260. ccxt/async_support/okcoin.py +2936 -0
  261. ccxt/async_support/okexchange.py +349 -0
  262. ccxt/async_support/okx.py +7827 -0
  263. ccxt/async_support/ompfinex.py +472 -0
  264. ccxt/async_support/onetrading.py +1911 -0
  265. ccxt/async_support/oxfun.py +2773 -0
  266. ccxt/async_support/p2b.py +1194 -0
  267. ccxt/async_support/paradex.py +2015 -0
  268. ccxt/async_support/paymium.py +564 -0
  269. ccxt/async_support/phemex.py +4473 -0
  270. ccxt/async_support/poloniex.py +2232 -0
  271. ccxt/async_support/poloniexfutures.py +1717 -0
  272. ccxt/async_support/probit.py +1734 -0
  273. ccxt/async_support/ramzinex.py +476 -0
  274. ccxt/async_support/sarmayex.py +357 -0
  275. ccxt/async_support/sarrafex.py +478 -0
  276. ccxt/async_support/tabdeal.py +364 -0
  277. ccxt/async_support/tetherland.py +349 -0
  278. ccxt/async_support/timex.py +1593 -0
  279. ccxt/async_support/tokocrypto.py +2405 -0
  280. ccxt/async_support/tradeogre.py +608 -0
  281. ccxt/async_support/twox.py +326 -0
  282. ccxt/async_support/ubitex.py +409 -0
  283. ccxt/async_support/upbit.py +1833 -0
  284. ccxt/async_support/vertex.py +2922 -0
  285. ccxt/async_support/wallex.py +445 -0
  286. ccxt/async_support/wavesexchange.py +2473 -0
  287. ccxt/async_support/wazirx.py +1224 -0
  288. ccxt/async_support/whitebit.py +2469 -0
  289. ccxt/async_support/woo.py +3114 -0
  290. ccxt/async_support/woofipro.py +2533 -0
  291. ccxt/async_support/xt.py +4454 -0
  292. ccxt/async_support/yobit.py +1283 -0
  293. ccxt/async_support/zaif.py +725 -0
  294. ccxt/async_support/zonda.py +1828 -0
  295. ccxt/base/__init__.py +27 -0
  296. ccxt/base/decimal_to_precision.py +174 -0
  297. ccxt/base/errors.py +242 -0
  298. ccxt/base/exchange.py +5941 -0
  299. ccxt/base/precise.py +287 -0
  300. ccxt/base/types.py +502 -0
  301. ccxt/bequant.py +33 -0
  302. ccxt/bigone.py +2112 -0
  303. ccxt/binance.py +12233 -0
  304. ccxt/binancecoinm.py +45 -0
  305. ccxt/binanceus.py +211 -0
  306. ccxt/binanceusdm.py +58 -0
  307. ccxt/bingx.py +4324 -0
  308. ccxt/bit2c.py +866 -0
  309. ccxt/bitbank.py +1001 -0
  310. ccxt/bitbay.py +17 -0
  311. ccxt/bitbns.py +1154 -0
  312. ccxt/bitcoincom.py +17 -0
  313. ccxt/bitfinex.py +1617 -0
  314. ccxt/bitfinex2.py +3552 -0
  315. ccxt/bitflyer.py +995 -0
  316. ccxt/bitget.py +8272 -0
  317. ccxt/bithumb.py +1061 -0
  318. ccxt/bitimen.py +401 -0
  319. ccxt/bitir.py +490 -0
  320. ccxt/bitmart.py +4415 -0
  321. ccxt/bitmex.py +2756 -0
  322. ccxt/bitopro.py +1630 -0
  323. ccxt/bitpanda.py +16 -0
  324. ccxt/bitpin.py +454 -0
  325. ccxt/bitrue.py +3026 -0
  326. ccxt/bitso.py +1670 -0
  327. ccxt/bitstamp.py +2203 -0
  328. ccxt/bitteam.py +2239 -0
  329. ccxt/bitvavo.py +1968 -0
  330. ccxt/bl3p.py +485 -0
  331. ccxt/blockchaincom.py +1104 -0
  332. ccxt/blofin.py +2066 -0
  333. ccxt/btcalpha.py +891 -0
  334. ccxt/btcbox.py +544 -0
  335. ccxt/btcmarkets.py +1221 -0
  336. ccxt/btcturk.py +911 -0
  337. ccxt/bybit.py +8158 -0
  338. ccxt/cex.py +1605 -0
  339. ccxt/coinbase.py +4474 -0
  340. ccxt/coinbaseadvanced.py +17 -0
  341. ccxt/coinbaseexchange.py +1734 -0
  342. ccxt/coinbaseinternational.py +1899 -0
  343. ccxt/coincatch.py +5069 -0
  344. ccxt/coincheck.py +815 -0
  345. ccxt/coinex.py +5525 -0
  346. ccxt/coinlist.py +2243 -0
  347. ccxt/coinmate.py +1067 -0
  348. ccxt/coinmetro.py +1797 -0
  349. ccxt/coinone.py +1127 -0
  350. ccxt/coinsph.py +1850 -0
  351. ccxt/coinspot.py +534 -0
  352. ccxt/cryptocom.py +2822 -0
  353. ccxt/currencycom.py +1950 -0
  354. ccxt/delta.py +3376 -0
  355. ccxt/deribit.py +3437 -0
  356. ccxt/digifinex.py +3959 -0
  357. ccxt/eterex.py +286 -0
  358. ccxt/excoino.py +399 -0
  359. ccxt/exir.py +375 -0
  360. ccxt/exmo.py +2462 -0
  361. ccxt/exnovin.py +360 -0
  362. ccxt/farhadexchange.py +266 -0
  363. ccxt/fmfwio.py +34 -0
  364. ccxt/gate.py +6975 -0
  365. ccxt/gateio.py +16 -0
  366. ccxt/gemini.py +1824 -0
  367. ccxt/hashkey.py +4150 -0
  368. ccxt/hitbtc.py +3423 -0
  369. ccxt/hitbtc3.py +16 -0
  370. ccxt/hitobit.py +391 -0
  371. ccxt/hollaex.py +1813 -0
  372. ccxt/htx.py +8505 -0
  373. ccxt/huobi.py +16 -0
  374. ccxt/huobijp.py +1801 -0
  375. ccxt/hyperliquid.py +2430 -0
  376. ccxt/idex.py +1766 -0
  377. ccxt/independentreserve.py +784 -0
  378. ccxt/indodax.py +1247 -0
  379. ccxt/jibitex.py +395 -0
  380. ccxt/kraken.py +2894 -0
  381. ccxt/krakenfutures.py +2601 -0
  382. ccxt/kucoin.py +4601 -0
  383. ccxt/kucoinfutures.py +2698 -0
  384. ccxt/kuna.py +1841 -0
  385. ccxt/latoken.py +1664 -0
  386. ccxt/lbank.py +2682 -0
  387. ccxt/luno.py +1067 -0
  388. ccxt/lykke.py +1270 -0
  389. ccxt/mercado.py +842 -0
  390. ccxt/mexc.py +5369 -0
  391. ccxt/ndax.py +2354 -0
  392. ccxt/nobitex.py +419 -0
  393. ccxt/novadax.py +1484 -0
  394. ccxt/oceanex.py +903 -0
  395. ccxt/okcoin.py +2936 -0
  396. ccxt/okexchange.py +349 -0
  397. ccxt/okx.py +7826 -0
  398. ccxt/ompfinex.py +472 -0
  399. ccxt/onetrading.py +1911 -0
  400. ccxt/oxfun.py +2772 -0
  401. ccxt/p2b.py +1194 -0
  402. ccxt/paradex.py +2015 -0
  403. ccxt/paymium.py +564 -0
  404. ccxt/phemex.py +4473 -0
  405. ccxt/poloniex.py +2232 -0
  406. ccxt/poloniexfutures.py +1717 -0
  407. ccxt/pro/__init__.py +149 -0
  408. ccxt/pro/alpaca.py +685 -0
  409. ccxt/pro/ascendex.py +916 -0
  410. ccxt/pro/bequant.py +38 -0
  411. ccxt/pro/binance.py +3488 -0
  412. ccxt/pro/binancecoinm.py +28 -0
  413. ccxt/pro/binanceus.py +48 -0
  414. ccxt/pro/binanceusdm.py +31 -0
  415. ccxt/pro/bingx.py +1264 -0
  416. ccxt/pro/bitcoincom.py +34 -0
  417. ccxt/pro/bitfinex.py +621 -0
  418. ccxt/pro/bitfinex2.py +1083 -0
  419. ccxt/pro/bitget.py +1692 -0
  420. ccxt/pro/bithumb.py +368 -0
  421. ccxt/pro/bitmart.py +1449 -0
  422. ccxt/pro/bitmex.py +1656 -0
  423. ccxt/pro/bitopro.py +445 -0
  424. ccxt/pro/bitpanda.py +15 -0
  425. ccxt/pro/bitrue.py +447 -0
  426. ccxt/pro/bitstamp.py +522 -0
  427. ccxt/pro/bitvavo.py +1270 -0
  428. ccxt/pro/blockchaincom.py +738 -0
  429. ccxt/pro/blofin.py +692 -0
  430. ccxt/pro/bybit.py +2000 -0
  431. ccxt/pro/cex.py +1440 -0
  432. ccxt/pro/coinbase.py +678 -0
  433. ccxt/pro/coinbaseadvanced.py +16 -0
  434. ccxt/pro/coinbaseexchange.py +895 -0
  435. ccxt/pro/coinbaseinternational.py +620 -0
  436. ccxt/pro/coincatch.py +1464 -0
  437. ccxt/pro/coincheck.py +199 -0
  438. ccxt/pro/coinex.py +1061 -0
  439. ccxt/pro/coinone.py +395 -0
  440. ccxt/pro/cryptocom.py +947 -0
  441. ccxt/pro/currencycom.py +536 -0
  442. ccxt/pro/deribit.py +892 -0
  443. ccxt/pro/exmo.py +629 -0
  444. ccxt/pro/gate.py +1416 -0
  445. ccxt/pro/gateio.py +15 -0
  446. ccxt/pro/gemini.py +865 -0
  447. ccxt/pro/hashkey.py +802 -0
  448. ccxt/pro/hitbtc.py +1216 -0
  449. ccxt/pro/hollaex.py +563 -0
  450. ccxt/pro/htx.py +2215 -0
  451. ccxt/pro/huobi.py +15 -0
  452. ccxt/pro/huobijp.py +570 -0
  453. ccxt/pro/hyperliquid.py +525 -0
  454. ccxt/pro/idex.py +672 -0
  455. ccxt/pro/independentreserve.py +270 -0
  456. ccxt/pro/kraken.py +1356 -0
  457. ccxt/pro/krakenfutures.py +1492 -0
  458. ccxt/pro/kucoin.py +1133 -0
  459. ccxt/pro/kucoinfutures.py +1081 -0
  460. ccxt/pro/lbank.py +843 -0
  461. ccxt/pro/luno.py +303 -0
  462. ccxt/pro/mexc.py +1122 -0
  463. ccxt/pro/ndax.py +506 -0
  464. ccxt/pro/okcoin.py +698 -0
  465. ccxt/pro/okx.py +1851 -0
  466. ccxt/pro/onetrading.py +1275 -0
  467. ccxt/pro/oxfun.py +950 -0
  468. ccxt/pro/p2b.py +419 -0
  469. ccxt/pro/paradex.py +352 -0
  470. ccxt/pro/phemex.py +1441 -0
  471. ccxt/pro/poloniex.py +1166 -0
  472. ccxt/pro/poloniexfutures.py +990 -0
  473. ccxt/pro/probit.py +551 -0
  474. ccxt/pro/upbit.py +520 -0
  475. ccxt/pro/vertex.py +943 -0
  476. ccxt/pro/wazirx.py +749 -0
  477. ccxt/pro/whitebit.py +864 -0
  478. ccxt/pro/woo.py +1078 -0
  479. ccxt/pro/woofipro.py +1183 -0
  480. ccxt/pro/xt.py +1067 -0
  481. ccxt/probit.py +1734 -0
  482. ccxt/ramzinex.py +476 -0
  483. ccxt/sarmayex.py +357 -0
  484. ccxt/sarrafex.py +478 -0
  485. ccxt/static_dependencies/__init__.py +1 -0
  486. ccxt/static_dependencies/ecdsa/__init__.py +14 -0
  487. ccxt/static_dependencies/ecdsa/_version.py +520 -0
  488. ccxt/static_dependencies/ecdsa/curves.py +56 -0
  489. ccxt/static_dependencies/ecdsa/der.py +221 -0
  490. ccxt/static_dependencies/ecdsa/ecdsa.py +310 -0
  491. ccxt/static_dependencies/ecdsa/ellipticcurve.py +197 -0
  492. ccxt/static_dependencies/ecdsa/keys.py +332 -0
  493. ccxt/static_dependencies/ecdsa/numbertheory.py +531 -0
  494. ccxt/static_dependencies/ecdsa/rfc6979.py +100 -0
  495. ccxt/static_dependencies/ecdsa/util.py +266 -0
  496. ccxt/static_dependencies/ethereum/__init__.py +7 -0
  497. ccxt/static_dependencies/ethereum/abi/__init__.py +16 -0
  498. ccxt/static_dependencies/ethereum/abi/abi.py +19 -0
  499. ccxt/static_dependencies/ethereum/abi/base.py +152 -0
  500. ccxt/static_dependencies/ethereum/abi/codec.py +217 -0
  501. ccxt/static_dependencies/ethereum/abi/constants.py +3 -0
  502. ccxt/static_dependencies/ethereum/abi/decoding.py +565 -0
  503. ccxt/static_dependencies/ethereum/abi/encoding.py +720 -0
  504. ccxt/static_dependencies/ethereum/abi/exceptions.py +139 -0
  505. ccxt/static_dependencies/ethereum/abi/grammar.py +443 -0
  506. ccxt/static_dependencies/ethereum/abi/packed.py +13 -0
  507. ccxt/static_dependencies/ethereum/abi/py.typed +0 -0
  508. ccxt/static_dependencies/ethereum/abi/registry.py +643 -0
  509. ccxt/static_dependencies/ethereum/abi/tools/__init__.py +3 -0
  510. ccxt/static_dependencies/ethereum/abi/tools/_strategies.py +230 -0
  511. ccxt/static_dependencies/ethereum/abi/utils/__init__.py +0 -0
  512. ccxt/static_dependencies/ethereum/abi/utils/numeric.py +83 -0
  513. ccxt/static_dependencies/ethereum/abi/utils/padding.py +27 -0
  514. ccxt/static_dependencies/ethereum/abi/utils/string.py +19 -0
  515. ccxt/static_dependencies/ethereum/account/__init__.py +3 -0
  516. ccxt/static_dependencies/ethereum/account/encode_typed_data/__init__.py +4 -0
  517. ccxt/static_dependencies/ethereum/account/encode_typed_data/encoding_and_hashing.py +239 -0
  518. ccxt/static_dependencies/ethereum/account/encode_typed_data/helpers.py +40 -0
  519. ccxt/static_dependencies/ethereum/account/messages.py +263 -0
  520. ccxt/static_dependencies/ethereum/account/py.typed +0 -0
  521. ccxt/static_dependencies/ethereum/hexbytes/__init__.py +5 -0
  522. ccxt/static_dependencies/ethereum/hexbytes/_utils.py +54 -0
  523. ccxt/static_dependencies/ethereum/hexbytes/main.py +65 -0
  524. ccxt/static_dependencies/ethereum/hexbytes/py.typed +0 -0
  525. ccxt/static_dependencies/ethereum/typing/__init__.py +63 -0
  526. ccxt/static_dependencies/ethereum/typing/abi.py +6 -0
  527. ccxt/static_dependencies/ethereum/typing/bls.py +7 -0
  528. ccxt/static_dependencies/ethereum/typing/discovery.py +5 -0
  529. ccxt/static_dependencies/ethereum/typing/encoding.py +7 -0
  530. ccxt/static_dependencies/ethereum/typing/enums.py +17 -0
  531. ccxt/static_dependencies/ethereum/typing/ethpm.py +9 -0
  532. ccxt/static_dependencies/ethereum/typing/evm.py +20 -0
  533. ccxt/static_dependencies/ethereum/typing/networks.py +1122 -0
  534. ccxt/static_dependencies/ethereum/typing/py.typed +0 -0
  535. ccxt/static_dependencies/ethereum/utils/__init__.py +115 -0
  536. ccxt/static_dependencies/ethereum/utils/abi.py +72 -0
  537. ccxt/static_dependencies/ethereum/utils/address.py +171 -0
  538. ccxt/static_dependencies/ethereum/utils/applicators.py +151 -0
  539. ccxt/static_dependencies/ethereum/utils/conversions.py +190 -0
  540. ccxt/static_dependencies/ethereum/utils/currency.py +107 -0
  541. ccxt/static_dependencies/ethereum/utils/curried/__init__.py +269 -0
  542. ccxt/static_dependencies/ethereum/utils/debug.py +20 -0
  543. ccxt/static_dependencies/ethereum/utils/decorators.py +132 -0
  544. ccxt/static_dependencies/ethereum/utils/encoding.py +6 -0
  545. ccxt/static_dependencies/ethereum/utils/exceptions.py +4 -0
  546. ccxt/static_dependencies/ethereum/utils/functional.py +75 -0
  547. ccxt/static_dependencies/ethereum/utils/hexadecimal.py +74 -0
  548. ccxt/static_dependencies/ethereum/utils/humanize.py +188 -0
  549. ccxt/static_dependencies/ethereum/utils/logging.py +159 -0
  550. ccxt/static_dependencies/ethereum/utils/module_loading.py +31 -0
  551. ccxt/static_dependencies/ethereum/utils/numeric.py +43 -0
  552. ccxt/static_dependencies/ethereum/utils/py.typed +0 -0
  553. ccxt/static_dependencies/ethereum/utils/toolz.py +76 -0
  554. ccxt/static_dependencies/ethereum/utils/types.py +54 -0
  555. ccxt/static_dependencies/ethereum/utils/typing/__init__.py +18 -0
  556. ccxt/static_dependencies/ethereum/utils/typing/misc.py +14 -0
  557. ccxt/static_dependencies/ethereum/utils/units.py +31 -0
  558. ccxt/static_dependencies/keccak/__init__.py +3 -0
  559. ccxt/static_dependencies/keccak/keccak.py +197 -0
  560. ccxt/static_dependencies/lark/__init__.py +38 -0
  561. ccxt/static_dependencies/lark/__pyinstaller/__init__.py +6 -0
  562. ccxt/static_dependencies/lark/__pyinstaller/hook-lark.py +14 -0
  563. ccxt/static_dependencies/lark/ast_utils.py +59 -0
  564. ccxt/static_dependencies/lark/common.py +86 -0
  565. ccxt/static_dependencies/lark/exceptions.py +292 -0
  566. ccxt/static_dependencies/lark/grammar.py +130 -0
  567. ccxt/static_dependencies/lark/grammars/__init__.py +0 -0
  568. ccxt/static_dependencies/lark/indenter.py +143 -0
  569. ccxt/static_dependencies/lark/lark.py +658 -0
  570. ccxt/static_dependencies/lark/lexer.py +678 -0
  571. ccxt/static_dependencies/lark/load_grammar.py +1428 -0
  572. ccxt/static_dependencies/lark/parse_tree_builder.py +391 -0
  573. ccxt/static_dependencies/lark/parser_frontends.py +257 -0
  574. ccxt/static_dependencies/lark/parsers/__init__.py +0 -0
  575. ccxt/static_dependencies/lark/parsers/cyk.py +340 -0
  576. ccxt/static_dependencies/lark/parsers/earley.py +314 -0
  577. ccxt/static_dependencies/lark/parsers/earley_common.py +42 -0
  578. ccxt/static_dependencies/lark/parsers/earley_forest.py +801 -0
  579. ccxt/static_dependencies/lark/parsers/grammar_analysis.py +203 -0
  580. ccxt/static_dependencies/lark/parsers/lalr_analysis.py +332 -0
  581. ccxt/static_dependencies/lark/parsers/lalr_interactive_parser.py +158 -0
  582. ccxt/static_dependencies/lark/parsers/lalr_parser.py +122 -0
  583. ccxt/static_dependencies/lark/parsers/lalr_parser_state.py +110 -0
  584. ccxt/static_dependencies/lark/parsers/xearley.py +165 -0
  585. ccxt/static_dependencies/lark/reconstruct.py +107 -0
  586. ccxt/static_dependencies/lark/tools/__init__.py +70 -0
  587. ccxt/static_dependencies/lark/tools/nearley.py +202 -0
  588. ccxt/static_dependencies/lark/tools/serialize.py +32 -0
  589. ccxt/static_dependencies/lark/tools/standalone.py +196 -0
  590. ccxt/static_dependencies/lark/tree.py +267 -0
  591. ccxt/static_dependencies/lark/tree_matcher.py +186 -0
  592. ccxt/static_dependencies/lark/tree_templates.py +180 -0
  593. ccxt/static_dependencies/lark/utils.py +343 -0
  594. ccxt/static_dependencies/lark/visitors.py +596 -0
  595. ccxt/static_dependencies/marshmallow/__init__.py +81 -0
  596. ccxt/static_dependencies/marshmallow/base.py +65 -0
  597. ccxt/static_dependencies/marshmallow/class_registry.py +94 -0
  598. ccxt/static_dependencies/marshmallow/decorators.py +231 -0
  599. ccxt/static_dependencies/marshmallow/error_store.py +60 -0
  600. ccxt/static_dependencies/marshmallow/exceptions.py +71 -0
  601. ccxt/static_dependencies/marshmallow/fields.py +2114 -0
  602. ccxt/static_dependencies/marshmallow/orderedset.py +89 -0
  603. ccxt/static_dependencies/marshmallow/schema.py +1228 -0
  604. ccxt/static_dependencies/marshmallow/types.py +12 -0
  605. ccxt/static_dependencies/marshmallow/utils.py +378 -0
  606. ccxt/static_dependencies/marshmallow/validate.py +678 -0
  607. ccxt/static_dependencies/marshmallow/warnings.py +2 -0
  608. ccxt/static_dependencies/marshmallow_dataclass/__init__.py +1047 -0
  609. ccxt/static_dependencies/marshmallow_dataclass/collection_field.py +51 -0
  610. ccxt/static_dependencies/marshmallow_dataclass/lazy_class_attribute.py +45 -0
  611. ccxt/static_dependencies/marshmallow_dataclass/mypy.py +71 -0
  612. ccxt/static_dependencies/marshmallow_dataclass/typing.py +14 -0
  613. ccxt/static_dependencies/marshmallow_dataclass/union_field.py +82 -0
  614. ccxt/static_dependencies/marshmallow_oneofschema/__init__.py +1 -0
  615. ccxt/static_dependencies/marshmallow_oneofschema/one_of_schema.py +193 -0
  616. ccxt/static_dependencies/msgpack/__init__.py +55 -0
  617. ccxt/static_dependencies/msgpack/exceptions.py +48 -0
  618. ccxt/static_dependencies/msgpack/ext.py +168 -0
  619. ccxt/static_dependencies/msgpack/fallback.py +951 -0
  620. ccxt/static_dependencies/parsimonious/__init__.py +10 -0
  621. ccxt/static_dependencies/parsimonious/exceptions.py +105 -0
  622. ccxt/static_dependencies/parsimonious/expressions.py +479 -0
  623. ccxt/static_dependencies/parsimonious/grammar.py +487 -0
  624. ccxt/static_dependencies/parsimonious/nodes.py +325 -0
  625. ccxt/static_dependencies/parsimonious/utils.py +40 -0
  626. ccxt/static_dependencies/starknet/__init__.py +0 -0
  627. ccxt/static_dependencies/starknet/cairo/__init__.py +0 -0
  628. ccxt/static_dependencies/starknet/cairo/data_types.py +123 -0
  629. ccxt/static_dependencies/starknet/cairo/deprecated_parse/__init__.py +0 -0
  630. ccxt/static_dependencies/starknet/cairo/deprecated_parse/cairo_types.py +77 -0
  631. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser.py +46 -0
  632. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser_transformer.py +138 -0
  633. ccxt/static_dependencies/starknet/cairo/felt.py +64 -0
  634. ccxt/static_dependencies/starknet/cairo/type_parser.py +121 -0
  635. ccxt/static_dependencies/starknet/cairo/v1/__init__.py +0 -0
  636. ccxt/static_dependencies/starknet/cairo/v1/type_parser.py +59 -0
  637. ccxt/static_dependencies/starknet/cairo/v2/__init__.py +0 -0
  638. ccxt/static_dependencies/starknet/cairo/v2/type_parser.py +77 -0
  639. ccxt/static_dependencies/starknet/ccxt_utils.py +7 -0
  640. ccxt/static_dependencies/starknet/common.py +15 -0
  641. ccxt/static_dependencies/starknet/constants.py +39 -0
  642. ccxt/static_dependencies/starknet/hash/__init__.py +0 -0
  643. ccxt/static_dependencies/starknet/hash/address.py +79 -0
  644. ccxt/static_dependencies/starknet/hash/compiled_class_hash_objects.py +111 -0
  645. ccxt/static_dependencies/starknet/hash/selector.py +16 -0
  646. ccxt/static_dependencies/starknet/hash/storage.py +12 -0
  647. ccxt/static_dependencies/starknet/hash/utils.py +78 -0
  648. ccxt/static_dependencies/starknet/models/__init__.py +0 -0
  649. ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
  650. ccxt/static_dependencies/starknet/serialization/__init__.py +24 -0
  651. ccxt/static_dependencies/starknet/serialization/_calldata_reader.py +40 -0
  652. ccxt/static_dependencies/starknet/serialization/_context.py +142 -0
  653. ccxt/static_dependencies/starknet/serialization/data_serializers/__init__.py +10 -0
  654. ccxt/static_dependencies/starknet/serialization/data_serializers/_common.py +82 -0
  655. ccxt/static_dependencies/starknet/serialization/data_serializers/array_serializer.py +43 -0
  656. ccxt/static_dependencies/starknet/serialization/data_serializers/bool_serializer.py +37 -0
  657. ccxt/static_dependencies/starknet/serialization/data_serializers/byte_array_serializer.py +66 -0
  658. ccxt/static_dependencies/starknet/serialization/data_serializers/cairo_data_serializer.py +71 -0
  659. ccxt/static_dependencies/starknet/serialization/data_serializers/enum_serializer.py +71 -0
  660. ccxt/static_dependencies/starknet/serialization/data_serializers/felt_serializer.py +50 -0
  661. ccxt/static_dependencies/starknet/serialization/data_serializers/named_tuple_serializer.py +58 -0
  662. ccxt/static_dependencies/starknet/serialization/data_serializers/option_serializer.py +43 -0
  663. ccxt/static_dependencies/starknet/serialization/data_serializers/output_serializer.py +40 -0
  664. ccxt/static_dependencies/starknet/serialization/data_serializers/payload_serializer.py +72 -0
  665. ccxt/static_dependencies/starknet/serialization/data_serializers/struct_serializer.py +36 -0
  666. ccxt/static_dependencies/starknet/serialization/data_serializers/tuple_serializer.py +36 -0
  667. ccxt/static_dependencies/starknet/serialization/data_serializers/uint256_serializer.py +76 -0
  668. ccxt/static_dependencies/starknet/serialization/data_serializers/uint_serializer.py +100 -0
  669. ccxt/static_dependencies/starknet/serialization/data_serializers/unit_serializer.py +32 -0
  670. ccxt/static_dependencies/starknet/serialization/errors.py +10 -0
  671. ccxt/static_dependencies/starknet/serialization/factory.py +229 -0
  672. ccxt/static_dependencies/starknet/serialization/function_serialization_adapter.py +110 -0
  673. ccxt/static_dependencies/starknet/serialization/tuple_dataclass.py +59 -0
  674. ccxt/static_dependencies/starknet/utils/__init__.py +0 -0
  675. ccxt/static_dependencies/starknet/utils/constructor_args_translator.py +86 -0
  676. ccxt/static_dependencies/starknet/utils/iterable.py +13 -0
  677. ccxt/static_dependencies/starknet/utils/schema.py +13 -0
  678. ccxt/static_dependencies/starknet/utils/typed_data.py +182 -0
  679. ccxt/static_dependencies/starkware/__init__.py +0 -0
  680. ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
  681. ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
  682. ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
  683. ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
  684. ccxt/static_dependencies/starkware/crypto/utils.py +63 -0
  685. ccxt/static_dependencies/sympy/__init__.py +0 -0
  686. ccxt/static_dependencies/sympy/core/__init__.py +0 -0
  687. ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
  688. ccxt/static_dependencies/sympy/external/__init__.py +0 -0
  689. ccxt/static_dependencies/sympy/external/gmpy.py +345 -0
  690. ccxt/static_dependencies/sympy/external/importtools.py +187 -0
  691. ccxt/static_dependencies/sympy/external/ntheory.py +637 -0
  692. ccxt/static_dependencies/sympy/external/pythonmpq.py +341 -0
  693. ccxt/static_dependencies/toolz/__init__.py +26 -0
  694. ccxt/static_dependencies/toolz/_signatures.py +784 -0
  695. ccxt/static_dependencies/toolz/_version.py +520 -0
  696. ccxt/static_dependencies/toolz/compatibility.py +30 -0
  697. ccxt/static_dependencies/toolz/curried/__init__.py +101 -0
  698. ccxt/static_dependencies/toolz/curried/exceptions.py +22 -0
  699. ccxt/static_dependencies/toolz/curried/operator.py +22 -0
  700. ccxt/static_dependencies/toolz/dicttoolz.py +339 -0
  701. ccxt/static_dependencies/toolz/functoolz.py +1049 -0
  702. ccxt/static_dependencies/toolz/itertoolz.py +1057 -0
  703. ccxt/static_dependencies/toolz/recipes.py +46 -0
  704. ccxt/static_dependencies/toolz/utils.py +9 -0
  705. ccxt/static_dependencies/typing_inspect/__init__.py +0 -0
  706. ccxt/static_dependencies/typing_inspect/typing_inspect.py +851 -0
  707. ccxt/tabdeal.py +364 -0
  708. ccxt/test/__init__.py +3 -0
  709. ccxt/test/base/__init__.py +29 -0
  710. ccxt/test/base/test_account.py +26 -0
  711. ccxt/test/base/test_balance.py +56 -0
  712. ccxt/test/base/test_borrow_interest.py +35 -0
  713. ccxt/test/base/test_borrow_rate.py +32 -0
  714. ccxt/test/base/test_calculate_fee.py +51 -0
  715. ccxt/test/base/test_crypto.py +127 -0
  716. ccxt/test/base/test_currency.py +76 -0
  717. ccxt/test/base/test_datetime.py +109 -0
  718. ccxt/test/base/test_decimal_to_precision.py +392 -0
  719. ccxt/test/base/test_deep_extend.py +68 -0
  720. ccxt/test/base/test_deposit_withdrawal.py +50 -0
  721. ccxt/test/base/test_exchange_datetime_functions.py +76 -0
  722. ccxt/test/base/test_funding_rate_history.py +29 -0
  723. ccxt/test/base/test_last_price.py +31 -0
  724. ccxt/test/base/test_ledger_entry.py +45 -0
  725. ccxt/test/base/test_ledger_item.py +48 -0
  726. ccxt/test/base/test_leverage_tier.py +33 -0
  727. ccxt/test/base/test_liquidation.py +50 -0
  728. ccxt/test/base/test_margin_mode.py +24 -0
  729. ccxt/test/base/test_margin_modification.py +35 -0
  730. ccxt/test/base/test_market.py +193 -0
  731. ccxt/test/base/test_number.py +411 -0
  732. ccxt/test/base/test_ohlcv.py +33 -0
  733. ccxt/test/base/test_open_interest.py +32 -0
  734. ccxt/test/base/test_order.py +64 -0
  735. ccxt/test/base/test_order_book.py +69 -0
  736. ccxt/test/base/test_position.py +60 -0
  737. ccxt/test/base/test_shared_methods.py +353 -0
  738. ccxt/test/base/test_status.py +24 -0
  739. ccxt/test/base/test_throttle.py +126 -0
  740. ccxt/test/base/test_ticker.py +92 -0
  741. ccxt/test/base/test_trade.py +47 -0
  742. ccxt/test/base/test_trading_fee.py +26 -0
  743. ccxt/test/base/test_transaction.py +39 -0
  744. ccxt/test/test_async.py +1649 -0
  745. ccxt/test/test_sync.py +1648 -0
  746. ccxt/test/tests_async.py +1558 -0
  747. ccxt/test/tests_helpers.py +287 -0
  748. ccxt/test/tests_init.py +39 -0
  749. ccxt/test/tests_sync.py +1555 -0
  750. ccxt/tetherland.py +349 -0
  751. ccxt/timex.py +1593 -0
  752. ccxt/tokocrypto.py +2405 -0
  753. ccxt/tradeogre.py +608 -0
  754. ccxt/twox.py +326 -0
  755. ccxt/ubitex.py +409 -0
  756. ccxt/upbit.py +1833 -0
  757. ccxt/vertex.py +2922 -0
  758. ccxt/wallex.py +445 -0
  759. ccxt/wavesexchange.py +2472 -0
  760. ccxt/wazirx.py +1224 -0
  761. ccxt/whitebit.py +2469 -0
  762. ccxt/woo.py +3114 -0
  763. ccxt/woofipro.py +2533 -0
  764. ccxt/xt.py +4453 -0
  765. ccxt/yobit.py +1283 -0
  766. ccxt/zaif.py +725 -0
  767. ccxt/zonda.py +1828 -0
  768. ccxt_ir-4.3.46.0.1.dist-info/LICENSE.txt +21 -0
  769. ccxt_ir-4.3.46.0.1.dist-info/METADATA +655 -0
  770. ccxt_ir-4.3.46.0.1.dist-info/RECORD +772 -0
  771. ccxt_ir-4.3.46.0.1.dist-info/WHEEL +6 -0
  772. ccxt_ir-4.3.46.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1558 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import asyncio
4
+
5
+
6
+ from tests_helpers import AuthenticationError, NotSupported, InvalidProxySettings, ExchangeNotAvailable, OperationFailed, OnMaintenance, get_cli_arg_value, get_root_dir, is_sync, dump, json_parse, json_stringify, convert_ascii, io_file_exists, io_file_read, io_dir_read, call_method, call_method_sync, call_exchange_method_dynamically, call_exchange_method_dynamically_sync, get_root_exception, exception_message, exit_script, get_exchange_prop, set_exchange_prop, init_exchange, get_test_files_sync, get_test_files, set_fetch_response, is_null_value, close, get_env_vars, get_lang, get_ext # noqa: F401
7
+
8
+ class testMainClass:
9
+ id_tests = False
10
+ request_tests_failed = False
11
+ response_tests_failed = False
12
+ request_tests = False
13
+ ws_tests = False
14
+ response_tests = False
15
+ static_tests = False
16
+ info = False
17
+ verbose = False
18
+ debug = False
19
+ private_test = False
20
+ private_test_only = False
21
+ load_keys = False
22
+ sandbox = False
23
+ only_specific_tests = []
24
+ skipped_settings_for_exchange = {}
25
+ skipped_methods = {}
26
+ checked_public_tests = {}
27
+ test_files = {}
28
+ public_tests = {}
29
+ ext = ''
30
+ lang = ''
31
+ proxy_test_file_name = 'proxies'
32
+
33
+ def parse_cli_args_and_props(self):
34
+ self.response_tests = get_cli_arg_value('--responseTests') or get_cli_arg_value('--response')
35
+ self.id_tests = get_cli_arg_value('--idTests')
36
+ self.request_tests = get_cli_arg_value('--requestTests') or get_cli_arg_value('--request')
37
+ self.info = get_cli_arg_value('--info')
38
+ self.verbose = get_cli_arg_value('--verbose')
39
+ self.debug = get_cli_arg_value('--debug')
40
+ self.private_test = get_cli_arg_value('--private')
41
+ self.private_test_only = get_cli_arg_value('--privateOnly')
42
+ self.sandbox = get_cli_arg_value('--sandbox')
43
+ self.load_keys = get_cli_arg_value('--loadKeys')
44
+ self.ws_tests = get_cli_arg_value('--ws')
45
+ self.lang = get_lang()
46
+ self.ext = get_ext()
47
+
48
+ async def init(self, exchange_id, symbol_argv, method_argv):
49
+ self.parse_cli_args_and_props()
50
+ if self.request_tests and self.response_tests:
51
+ await self.run_static_request_tests(exchange_id, symbol_argv)
52
+ await self.run_static_response_tests(exchange_id, symbol_argv)
53
+ return
54
+ if self.response_tests:
55
+ await self.run_static_response_tests(exchange_id, symbol_argv)
56
+ return
57
+ if self.request_tests:
58
+ await self.run_static_request_tests(exchange_id, symbol_argv) # symbol here is the testname
59
+ return
60
+ if self.id_tests:
61
+ await self.run_broker_id_tests()
62
+ return
63
+ new_line = '\n'
64
+ dump(new_line + '' + new_line + '' + '[INFO] TESTING ', self.ext, {
65
+ 'exchange': exchange_id,
66
+ 'symbol': symbol_argv,
67
+ 'method': method_argv,
68
+ 'isWs': self.ws_tests,
69
+ 'useProxy': get_cli_arg_value('--useProxy'),
70
+ }, new_line)
71
+ exchange_args = {
72
+ 'verbose': self.verbose,
73
+ 'debug': self.debug,
74
+ 'enableRateLimit': True,
75
+ 'timeout': 30000,
76
+ }
77
+ exchange = init_exchange(exchange_id, exchange_args, self.ws_tests)
78
+ if exchange.alias:
79
+ exit_script(0)
80
+ await self.import_files(exchange)
81
+ assert len(list(self.test_files.keys())) > 0, 'Test files were not loaded' # ensure test files are found & filled
82
+ self.expand_settings(exchange)
83
+ self.check_if_specific_test_is_chosen(method_argv)
84
+ await self.start_test(exchange, symbol_argv)
85
+ exit_script(0) # needed to be explicitly finished for WS tests
86
+
87
+ def check_if_specific_test_is_chosen(self, method_argv):
88
+ if method_argv is not None:
89
+ test_file_names = list(self.test_files.keys())
90
+ possible_method_names = method_argv.split(',') # i.e. `test.ts binance fetchBalance,fetchDeposits`
91
+ if len(possible_method_names) >= 1:
92
+ for i in range(0, len(test_file_names)):
93
+ test_file_name = test_file_names[i]
94
+ for j in range(0, len(possible_method_names)):
95
+ method_name = possible_method_names[j]
96
+ method_name = method_name.replace('()', '')
97
+ if test_file_name == method_name:
98
+ self.only_specific_tests.append(test_file_name)
99
+
100
+ async def import_files(self, exchange):
101
+ properties = list(exchange.has.keys())
102
+ properties.append('loadMarkets')
103
+ if is_sync():
104
+ self.test_files = get_test_files_sync(properties, self.ws_tests)
105
+ else:
106
+ self.test_files = await get_test_files(properties, self.ws_tests)
107
+
108
+ def load_credentials_from_env(self, exchange):
109
+ exchange_id = exchange.id
110
+ 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
111
+ objkeys = list(req_creds.keys())
112
+ for i in range(0, len(objkeys)):
113
+ credential = objkeys[i]
114
+ is_required = req_creds[credential]
115
+ if is_required and get_exchange_prop(exchange, credential) is None:
116
+ full_key = exchange_id + '_' + credential
117
+ credential_env_name = full_key.upper() # example: KRAKEN_APIKEY
118
+ env_vars = get_env_vars()
119
+ credential_value = env_vars[credential_env_name] if (credential_env_name in env_vars) else None
120
+ if credential_value:
121
+ set_exchange_prop(exchange, credential, credential_value)
122
+
123
+ def expand_settings(self, exchange):
124
+ exchange_id = exchange.id
125
+ keys_global = get_root_dir() + 'keys.json'
126
+ keys_local = get_root_dir() + 'keys.local.json'
127
+ keys_global_exists = io_file_exists(keys_global)
128
+ keys_local_exists = io_file_exists(keys_local)
129
+ global_settings = io_file_read(keys_global) if keys_global_exists else {}
130
+ local_settings = io_file_read(keys_local) if keys_local_exists else {}
131
+ all_settings = exchange.deep_extend(global_settings, local_settings)
132
+ exchange_settings = exchange.safe_value(all_settings, exchange_id, {})
133
+ if exchange_settings:
134
+ setting_keys = list(exchange_settings.keys())
135
+ for i in range(0, len(setting_keys)):
136
+ key = setting_keys[i]
137
+ if exchange_settings[key]:
138
+ final_value = None
139
+ if isinstance(exchange_settings[key], dict):
140
+ existing = get_exchange_prop(exchange, key, {})
141
+ final_value = exchange.deep_extend(existing, exchange_settings[key])
142
+ else:
143
+ final_value = exchange_settings[key]
144
+ set_exchange_prop(exchange, key, final_value)
145
+ # credentials
146
+ if self.load_keys:
147
+ self.load_credentials_from_env(exchange)
148
+ # skipped tests
149
+ skipped_file = get_root_dir() + 'skip-tests.json'
150
+ skipped_settings = io_file_read(skipped_file)
151
+ self.skipped_settings_for_exchange = exchange.safe_value(skipped_settings, exchange_id, {})
152
+ skipped_settings_for_exchange = self.skipped_settings_for_exchange
153
+ # others
154
+ timeout = exchange.safe_value(skipped_settings_for_exchange, 'timeout')
155
+ if timeout is not None:
156
+ exchange.timeout = exchange.parse_to_int(timeout)
157
+ if get_cli_arg_value('--useProxy'):
158
+ exchange.http_proxy = exchange.safe_string(skipped_settings_for_exchange, 'httpProxy')
159
+ exchange.https_proxy = exchange.safe_string(skipped_settings_for_exchange, 'httpsProxy')
160
+ exchange.ws_proxy = exchange.safe_string(skipped_settings_for_exchange, 'wsProxy')
161
+ exchange.wss_proxy = exchange.safe_string(skipped_settings_for_exchange, 'wssProxy')
162
+ self.skipped_methods = exchange.safe_value(skipped_settings_for_exchange, 'skipMethods', {})
163
+ self.checked_public_tests = {}
164
+
165
+ def add_padding(self, message, size):
166
+ # has to be transpilable
167
+ res = ''
168
+ message_length = len(message) # avoid php transpilation issue
169
+ missing_space = size - message_length - 0 # - 0 is added just to trick transpile to treat the .length as a string for php
170
+ if missing_space > 0:
171
+ for i in range(0, missing_space):
172
+ res += ' '
173
+ return message + res
174
+
175
+ async def test_method(self, method_name, exchange, args, is_public):
176
+ # todo: temporary skip for c#
177
+ if 'OrderBook' in method_name and self.ext == 'cs':
178
+ exchange.options['checksum'] = False
179
+ # todo: temporary skip for php
180
+ if 'OrderBook' in method_name and self.ext == 'php':
181
+ return
182
+ skipped_properties_for_method = self.get_skips(exchange, method_name)
183
+ is_load_markets = (method_name == 'loadMarkets')
184
+ is_fetch_currencies = (method_name == 'fetchCurrencies')
185
+ is_proxy_test = (method_name == self.proxy_test_file_name)
186
+ # 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)
187
+ if not is_public and (method_name in self.checked_public_tests) and not is_fetch_currencies:
188
+ return
189
+ skip_message = None
190
+ supported_by_exchange = (method_name in exchange.has) and exchange.has[method_name]
191
+ if not is_load_markets and (len(self.only_specific_tests) > 0 and not exchange.in_array(method_name, self.only_specific_tests)):
192
+ skip_message = '[INFO] IGNORED_TEST'
193
+ elif not is_load_markets and not supported_by_exchange and not is_proxy_test:
194
+ skip_message = '[INFO] UNSUPPORTED_TEST' # keep it aligned with the longest message
195
+ elif isinstance(skipped_properties_for_method, str):
196
+ skip_message = '[INFO] SKIPPED_TEST'
197
+ elif not (method_name in self.test_files):
198
+ skip_message = '[INFO] UNIMPLEMENTED_TEST'
199
+ # 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)
200
+ if is_load_markets:
201
+ await exchange.load_markets(True)
202
+ name = exchange.id
203
+ if skip_message:
204
+ if self.info:
205
+ dump(self.add_padding(skip_message, 25), name, method_name)
206
+ return
207
+ if self.info:
208
+ 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"
209
+ dump(self.add_padding('[INFO] TESTING', 25), name, method_name, args_stringified)
210
+ if is_sync():
211
+ call_method_sync(self.test_files, method_name, exchange, skipped_properties_for_method, args)
212
+ else:
213
+ await call_method(self.test_files, method_name, exchange, skipped_properties_for_method, args)
214
+ if self.info:
215
+ dump(self.add_padding('[INFO] TESTING DONE', 25), name, method_name)
216
+ # add to the list of successed tests
217
+ if is_public:
218
+ self.checked_public_tests[method_name] = True
219
+ return
220
+
221
+ def get_skips(self, exchange, method_name):
222
+ final_skips = {}
223
+ # check the exact method (i.e. `fetchTrades`) and language-specific (i.e. `fetchTrades.php`)
224
+ method_names = [method_name, method_name + '.' + self.ext]
225
+ for i in range(0, len(method_names)):
226
+ m_name = method_names[i]
227
+ if m_name in self.skipped_methods:
228
+ # if whole method is skipped, by assigning a string to it, i.e. "fetchOrders":"blabla"
229
+ if isinstance(self.skipped_methods[m_name], str):
230
+ return self.skipped_methods[m_name]
231
+ else:
232
+ final_skips = exchange.deep_extend(final_skips, self.skipped_methods[m_name])
233
+ # get "object-specific" skips
234
+ object_skips = {
235
+ 'orderBook': ['fetchOrderBook', 'fetchOrderBooks', 'fetchL2OrderBook', 'watchOrderBook', 'watchOrderBookForSymbols'],
236
+ 'ticker': ['fetchTicker', 'fetchTickers', 'watchTicker', 'watchTickers'],
237
+ 'trade': ['fetchTrades', 'watchTrades', 'watchTradesForSymbols'],
238
+ 'ohlcv': ['fetchOHLCV', 'watchOHLCV', 'watchOHLCVForSymbols'],
239
+ 'ledger': ['fetchLedger', 'fetchLedgerEntry'],
240
+ 'depositWithdraw': ['fetchDepositsWithdrawals', 'fetchDeposits', 'fetchWithdrawals'],
241
+ 'depositWithdrawFee': ['fetchDepositWithdrawFee', 'fetchDepositWithdrawFees'],
242
+ }
243
+ object_names = list(object_skips.keys())
244
+ for i in range(0, len(object_names)):
245
+ object_name = object_names[i]
246
+ object_methods = object_skips[object_name]
247
+ if exchange.in_array(method_name, object_methods):
248
+ # if whole object is skipped, by assigning a string to it, i.e. "orderBook":"blabla"
249
+ if (object_name in self.skipped_methods) and (isinstance(self.skipped_methods[object_name], str)):
250
+ return self.skipped_methods[object_name]
251
+ extra_skips = exchange.safe_dict(self.skipped_methods, object_name, {})
252
+ final_skips = exchange.deep_extend(final_skips, extra_skips)
253
+ # extend related skips
254
+ # - if 'timestamp' is skipped, we should do so for 'datetime' too
255
+ # - if 'bid' is skipped, skip 'ask' too
256
+ if ('timestamp' in final_skips) and not ('datetime' in final_skips):
257
+ final_skips['datetime'] = final_skips['timestamp']
258
+ if ('bid' in final_skips) and not ('ask' in final_skips):
259
+ final_skips['ask'] = final_skips['bid']
260
+ if ('baseVolume' in final_skips) and not ('quoteVolume' in final_skips):
261
+ final_skips['quoteVolume'] = final_skips['baseVolume']
262
+ return final_skips
263
+
264
+ async def test_safe(self, method_name, exchange, args=[], is_public=False):
265
+ # `testSafe` method does not throw an exception, instead mutes it. The reason we
266
+ # mute the thrown exceptions here is because we don't want to stop the whole
267
+ # tests queue if any single test-method fails. Instead, they are echoed with
268
+ # formatted message "[TEST_FAILURE] ..." and that output is then regex-matched by
269
+ # run-tests.js, so the exceptions are still printed out to console from there.
270
+ max_retries = 3
271
+ 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"
272
+ for i in range(0, max_retries):
273
+ try:
274
+ await self.test_method(method_name, exchange, args, is_public)
275
+ return True
276
+ except Exception as ex:
277
+ e = get_root_exception(ex)
278
+ is_load_markets = (method_name == 'loadMarkets')
279
+ is_auth_error = (isinstance(e, AuthenticationError))
280
+ is_not_supported = (isinstance(e, NotSupported))
281
+ is_operation_failed = (isinstance(e, OperationFailed)) # includes "DDoSProtection", "RateLimitExceeded", "RequestTimeout", "ExchangeNotAvailable", "OperationFailed", "InvalidNonce", ...
282
+ if is_operation_failed:
283
+ # if last retry was gone with same `tempFailure` error, then let's eventually return false
284
+ if i == max_retries - 1:
285
+ is_on_maintenance = (isinstance(e, OnMaintenance))
286
+ is_exchange_not_available = (isinstance(e, ExchangeNotAvailable))
287
+ should_fail = None
288
+ return_success = None
289
+ if is_load_markets:
290
+ # if "loadMarkets" does not succeed, we must return "false" to caller method, to stop tests continual
291
+ return_success = False
292
+ # we might not break exchange tests, if exchange is on maintenance at this moment
293
+ if is_on_maintenance:
294
+ should_fail = False
295
+ else:
296
+ should_fail = True
297
+ else:
298
+ # for any other method tests:
299
+ if is_exchange_not_available and not is_on_maintenance:
300
+ # break exchange tests if "ExchangeNotAvailable" exception is thrown, but it's not maintenance
301
+ should_fail = True
302
+ return_success = False
303
+ else:
304
+ # in all other cases of OperationFailed, show Warning, but don't mark test as failed
305
+ should_fail = False
306
+ return_success = True
307
+ # output the message
308
+ fail_type = '[TEST_FAILURE]' if should_fail else '[TEST_WARNING]'
309
+ dump(fail_type, 'Method could not be tested due to a repeated Network/Availability issues', ' | ', exchange.id, method_name, args_stringified, exception_message(e))
310
+ return return_success
311
+ else:
312
+ # wait and retry again
313
+ # (increase wait time on every retry)
314
+ await exchange.sleep((i + 1) * 1000)
315
+ continue
316
+ else:
317
+ # if it's loadMarkets, then fail test, because it's mandatory for tests
318
+ if is_load_markets:
319
+ dump('[TEST_FAILURE]', 'Exchange can not load markets', exception_message(e), exchange.id, method_name, args_stringified)
320
+ return False
321
+ # if the specific arguments to the test method throws "NotSupported" exception
322
+ # then let's don't fail the test
323
+ if is_not_supported:
324
+ if self.info:
325
+ dump('[INFO] NOT_SUPPORTED', exception_message(e), exchange.id, method_name, args_stringified)
326
+ return True
327
+ # If public test faces authentication error, we don't break (see comments under `testSafe` method)
328
+ if is_public and is_auth_error:
329
+ if self.info:
330
+ dump('[INFO]', 'Authentication problem for public method', exception_message(e), exchange.id, method_name, args_stringified)
331
+ return True
332
+ else:
333
+ dump('[TEST_FAILURE]', exception_message(e), exchange.id, method_name, args_stringified)
334
+ return False
335
+ return True
336
+
337
+ async def run_public_tests(self, exchange, symbol):
338
+ tests = {
339
+ 'fetchCurrencies': [],
340
+ 'fetchTicker': [symbol],
341
+ 'fetchTickers': [symbol],
342
+ 'fetchLastPrices': [symbol],
343
+ 'fetchOHLCV': [symbol],
344
+ 'fetchTrades': [symbol],
345
+ 'fetchOrderBook': [symbol],
346
+ 'fetchL2OrderBook': [symbol],
347
+ 'fetchOrderBooks': [],
348
+ 'fetchBidsAsks': [],
349
+ 'fetchStatus': [],
350
+ 'fetchTime': [],
351
+ }
352
+ if self.ws_tests:
353
+ tests = {
354
+ 'watchOHLCV': [symbol],
355
+ 'watchOHLCVForSymbols': [symbol],
356
+ 'watchTicker': [symbol],
357
+ 'watchTickers': [symbol],
358
+ 'watchBidsAsks': [symbol],
359
+ 'watchOrderBook': [symbol],
360
+ 'watchOrderBookForSymbols': [[symbol]],
361
+ 'watchTrades': [symbol],
362
+ 'watchTradesForSymbols': [[symbol]],
363
+ }
364
+ market = exchange.market(symbol)
365
+ is_spot = market['spot']
366
+ if not self.ws_tests:
367
+ if is_spot:
368
+ tests['fetchCurrencies'] = []
369
+ else:
370
+ tests['fetchFundingRates'] = [symbol]
371
+ tests['fetchFundingRate'] = [symbol]
372
+ tests['fetchFundingRateHistory'] = [symbol]
373
+ tests['fetchIndexOHLCV'] = [symbol]
374
+ tests['fetchMarkOHLCV'] = [symbol]
375
+ tests['fetchPremiumIndexOHLCV'] = [symbol]
376
+ self.public_tests = tests
377
+ await self.run_tests(exchange, tests, True)
378
+
379
+ async def run_tests(self, exchange, tests, is_public_test):
380
+ test_names = list(tests.keys())
381
+ promises = []
382
+ for i in range(0, len(test_names)):
383
+ test_name = test_names[i]
384
+ test_args = tests[test_name]
385
+ promises.append(self.test_safe(test_name, exchange, test_args, is_public_test))
386
+ # todo - not yet ready in other langs too
387
+ # promises.push (testThrottle ());
388
+ results = await asyncio.gather(*promises)
389
+ # now count which test-methods retuned `false` from "testSafe" and dump that info below
390
+ failed_methods = []
391
+ for i in range(0, len(test_names)):
392
+ test_name = test_names[i]
393
+ test_returned_value = results[i]
394
+ if not test_returned_value:
395
+ failed_methods.append(test_name)
396
+ test_prefix_string = 'PUBLIC_TESTS' if is_public_test else 'PRIVATE_TESTS'
397
+ if len(failed_methods):
398
+ errors_string = ', '.join(failed_methods)
399
+ dump('[TEST_FAILURE]', exchange.id, test_prefix_string, 'Failed methods : ' + errors_string)
400
+ if self.info:
401
+ dump(self.add_padding('[INFO] END ' + test_prefix_string + ' ' + exchange.id, 25))
402
+
403
+ async def load_exchange(self, exchange):
404
+ result = await self.test_safe('loadMarkets', exchange, [], True)
405
+ if not result:
406
+ return False
407
+ exchange_symbols_length = len(exchange.symbols)
408
+ dump('[INFO:MAIN] Exchange loaded', exchange_symbols_length, 'symbols')
409
+ return True
410
+
411
+ def get_test_symbol(self, exchange, is_spot, symbols):
412
+ symbol = None
413
+ preferred_spot_symbol = exchange.safe_string(self.skipped_settings_for_exchange, 'preferredSpotSymbol')
414
+ preferred_swap_symbol = exchange.safe_string(self.skipped_settings_for_exchange, 'preferredSwapSymbol')
415
+ if is_spot and preferred_spot_symbol:
416
+ return preferred_spot_symbol
417
+ elif not is_spot and preferred_swap_symbol:
418
+ return preferred_swap_symbol
419
+ for i in range(0, len(symbols)):
420
+ s = symbols[i]
421
+ market = exchange.safe_value(exchange.markets, s)
422
+ if market is not None:
423
+ active = exchange.safe_value(market, 'active')
424
+ if active or (active is None):
425
+ symbol = s
426
+ break
427
+ return symbol
428
+
429
+ def get_exchange_code(self, exchange, codes=None):
430
+ if codes is None:
431
+ codes = ['BTC', 'ETH', 'XRP', 'LTC', 'BCH', 'EOS', 'BNB', 'BSV', 'USDT']
432
+ code = codes[0]
433
+ for i in range(0, len(codes)):
434
+ if codes[i] in exchange.currencies:
435
+ return codes[i]
436
+ return code
437
+
438
+ def get_markets_from_exchange(self, exchange, spot=True):
439
+ res = {}
440
+ markets = exchange.markets
441
+ keys = list(markets.keys())
442
+ for i in range(0, len(keys)):
443
+ key = keys[i]
444
+ market = markets[key]
445
+ if spot and market['spot']:
446
+ res[market['symbol']] = market
447
+ elif not spot and not market['spot']:
448
+ res[market['symbol']] = market
449
+ return res
450
+
451
+ def get_valid_symbol(self, exchange, spot=True):
452
+ current_type_markets = self.get_markets_from_exchange(exchange, spot)
453
+ codes = ['BTC', 'ETH', 'XRP', 'LTC', 'BNB', 'DASH', 'DOGE', 'ETC', 'TRX', 'USDT', 'USDC', 'USD', 'EUR', 'TUSD', 'CNY', 'JPY', 'BRL']
454
+ 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']
455
+ 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']
456
+ target_symbols = spot_symbols if spot else swap_symbols
457
+ symbol = self.get_test_symbol(exchange, spot, target_symbols)
458
+ # if symbols wasn't found from above hardcoded list, then try to locate any symbol which has our target hardcoded 'base' code
459
+ if symbol is None:
460
+ for i in range(0, len(codes)):
461
+ current_code = codes[i]
462
+ markets_array_for_current_code = exchange.filter_by(current_type_markets, 'base', current_code)
463
+ indexed_mkts = exchange.index_by(markets_array_for_current_code, 'symbol')
464
+ symbols_array_for_current_code = list(indexed_mkts.keys())
465
+ symbols_length = len(symbols_array_for_current_code)
466
+ if symbols_length:
467
+ symbol = self.get_test_symbol(exchange, spot, symbols_array_for_current_code)
468
+ break
469
+ # if there wasn't found any symbol with our hardcoded 'base' code, then just try to find symbols that are 'active'
470
+ if symbol is None:
471
+ active_markets = exchange.filter_by(current_type_markets, 'active', True)
472
+ active_symbols = []
473
+ for i in range(0, len(active_markets)):
474
+ active_symbols.append(active_markets[i]['symbol'])
475
+ symbol = self.get_test_symbol(exchange, spot, active_symbols)
476
+ if symbol is None:
477
+ values = list(current_type_markets.values())
478
+ values_length = len(values)
479
+ if values_length > 0:
480
+ first = values[0]
481
+ if first is not None:
482
+ symbol = first['symbol']
483
+ return symbol
484
+
485
+ async def test_exchange(self, exchange, provided_symbol=None):
486
+ spot_symbol = None
487
+ swap_symbol = None
488
+ if provided_symbol is not None:
489
+ market = exchange.market(provided_symbol)
490
+ if market['spot']:
491
+ spot_symbol = provided_symbol
492
+ else:
493
+ swap_symbol = provided_symbol
494
+ else:
495
+ if exchange.has['spot']:
496
+ spot_symbol = self.get_valid_symbol(exchange, True)
497
+ if exchange.has['swap']:
498
+ swap_symbol = self.get_valid_symbol(exchange, False)
499
+ if spot_symbol is not None:
500
+ dump('[INFO:MAIN] Selected SPOT SYMBOL:', spot_symbol)
501
+ if swap_symbol is not None:
502
+ dump('[INFO:MAIN] Selected SWAP SYMBOL:', swap_symbol)
503
+ if not self.private_test_only:
504
+ # note, spot & swap tests should run sequentially, because of conflicting `exchange.options['defaultType']` setting
505
+ if exchange.has['spot'] and spot_symbol is not None:
506
+ if self.info:
507
+ dump('[INFO] ### SPOT TESTS ###')
508
+ exchange.options['defaultType'] = 'spot'
509
+ await self.run_public_tests(exchange, spot_symbol)
510
+ if exchange.has['swap'] and swap_symbol is not None:
511
+ if self.info:
512
+ dump('[INFO] ### SWAP TESTS ###')
513
+ exchange.options['defaultType'] = 'swap'
514
+ await self.run_public_tests(exchange, swap_symbol)
515
+ if self.private_test or self.private_test_only:
516
+ if exchange.has['spot'] and spot_symbol is not None:
517
+ exchange.options['defaultType'] = 'spot'
518
+ await self.run_private_tests(exchange, spot_symbol)
519
+ if exchange.has['swap'] and swap_symbol is not None:
520
+ exchange.options['defaultType'] = 'swap'
521
+ await self.run_private_tests(exchange, swap_symbol)
522
+
523
+ async def run_private_tests(self, exchange, symbol):
524
+ if not exchange.check_required_credentials(False):
525
+ dump('[INFO] Skipping private tests', 'Keys not found')
526
+ return
527
+ code = self.get_exchange_code(exchange)
528
+ # if (exchange.deepExtendedTest) {
529
+ # await test ('InvalidNonce', exchange, symbol);
530
+ # await test ('OrderNotFound', exchange, symbol);
531
+ # await test ('InvalidOrder', exchange, symbol);
532
+ # await test ('InsufficientFunds', exchange, symbol, balance); # danger zone - won't execute with non-empty balance
533
+ # }
534
+ tests = {
535
+ 'signIn': [],
536
+ 'fetchBalance': [],
537
+ 'fetchAccounts': [],
538
+ 'fetchTransactionFees': [],
539
+ 'fetchTradingFees': [],
540
+ 'fetchStatus': [],
541
+ 'fetchOrders': [symbol],
542
+ 'fetchOpenOrders': [symbol],
543
+ 'fetchClosedOrders': [symbol],
544
+ 'fetchMyTrades': [symbol],
545
+ 'fetchLeverageTiers': [[symbol]],
546
+ 'fetchLedger': [code],
547
+ 'fetchTransactions': [code],
548
+ 'fetchDeposits': [code],
549
+ 'fetchWithdrawals': [code],
550
+ 'fetchBorrowInterest': [code, symbol],
551
+ 'cancelAllOrders': [symbol],
552
+ 'fetchCanceledOrders': [symbol],
553
+ 'fetchMarginModes': [symbol],
554
+ 'fetchPosition': [symbol],
555
+ 'fetchDeposit': [code],
556
+ 'createDepositAddress': [code],
557
+ 'fetchDepositAddress': [code],
558
+ 'fetchDepositAddresses': [code],
559
+ 'fetchDepositAddressesByNetwork': [code],
560
+ 'fetchBorrowRateHistory': [code],
561
+ 'fetchLedgerEntry': [code],
562
+ }
563
+ if self.ws_tests:
564
+ tests = {
565
+ 'watchBalance': [code],
566
+ 'watchMyTrades': [symbol],
567
+ 'watchOrders': [symbol],
568
+ 'watchPosition': [symbol],
569
+ 'watchPositions': [symbol],
570
+ }
571
+ market = exchange.market(symbol)
572
+ is_spot = market['spot']
573
+ if not self.ws_tests:
574
+ if is_spot:
575
+ tests['fetchCurrencies'] = []
576
+ else:
577
+ # derivatives only
578
+ tests['fetchPositions'] = [symbol] # this test fetches all positions for 1 symbol
579
+ tests['fetchPosition'] = [symbol]
580
+ tests['fetchPositionRisk'] = [symbol]
581
+ tests['setPositionMode'] = [symbol]
582
+ tests['setMarginMode'] = [symbol]
583
+ tests['fetchOpenInterestHistory'] = [symbol]
584
+ tests['fetchFundingRateHistory'] = [symbol]
585
+ tests['fetchFundingHistory'] = [symbol]
586
+ # const combinedTests = exchange.deepExtend (this.publicTests, privateTests);
587
+ await self.run_tests(exchange, tests, False)
588
+
589
+ async def test_proxies(self, exchange):
590
+ # these tests should be synchronously executed, because of conflicting nature of proxy settings
591
+ proxy_test_name = self.proxy_test_file_name
592
+ # todo: temporary skip for sync py
593
+ if self.ext == 'py' and is_sync():
594
+ return
595
+ # try proxy several times
596
+ max_retries = 3
597
+ exception = None
598
+ for j in range(0, max_retries):
599
+ try:
600
+ await self.test_method(proxy_test_name, exchange, [], True)
601
+ return # if successfull, then end the test
602
+ except Exception as e:
603
+ exception = e
604
+ await exchange.sleep(j * 1000)
605
+ # if exception was set, then throw it
606
+ if exception is not None:
607
+ error_message = '[TEST_FAILURE] Failed ' + proxy_test_name + ' : ' + exception_message(exception)
608
+ # temporary comment the below, because c# transpilation failure
609
+ # throw new Exchange Error (errorMessage.toString ());
610
+ dump('[TEST_WARNING]' + str(error_message))
611
+
612
+ async def start_test(self, exchange, symbol):
613
+ # we do not need to test aliases
614
+ if exchange.alias:
615
+ return
616
+ if self.sandbox or get_exchange_prop(exchange, 'sandbox'):
617
+ exchange.set_sandbox_mode(True)
618
+ try:
619
+ result = await self.load_exchange(exchange)
620
+ if not result:
621
+ if not is_sync():
622
+ await close(exchange)
623
+ return
624
+ # if (exchange.id === 'binance') {
625
+ # # 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
626
+ # # await this.testProxies (exchange);
627
+ # }
628
+ await self.test_exchange(exchange, symbol)
629
+ if not is_sync():
630
+ await close(exchange)
631
+ except Exception as e:
632
+ if not is_sync():
633
+ await close(exchange)
634
+ raise e
635
+
636
+ def assert_static_error(self, cond, message, calculated_output, stored_output, key=None):
637
+ # -----------------------------------------------------------------------------
638
+ # --- Init of static tests functions------------------------------------------
639
+ # -----------------------------------------------------------------------------
640
+ calculated_string = json_stringify(calculated_output)
641
+ stored_string = json_stringify(stored_output)
642
+ error_message = message
643
+ if key is not None:
644
+ error_message = '[' + key + ']'
645
+ error_message += ' computed: ' + stored_string + ' stored: ' + calculated_string
646
+ assert cond, error_message
647
+
648
+ def load_markets_from_file(self, id):
649
+ # load markets from file
650
+ # to make this test as fast as possible
651
+ # and basically independent from the exchange
652
+ # so we can run it offline
653
+ filename = get_root_dir() + './ts/src/test/static/markets/' + id + '.json'
654
+ content = io_file_read(filename)
655
+ return content
656
+
657
+ def load_currencies_from_file(self, id):
658
+ filename = get_root_dir() + './ts/src/test/static/currencies/' + id + '.json'
659
+ content = io_file_read(filename)
660
+ return content
661
+
662
+ def load_static_data(self, folder, target_exchange=None):
663
+ result = {}
664
+ if target_exchange:
665
+ # read a single exchange
666
+ path = folder + target_exchange + '.json'
667
+ if not io_file_exists(path):
668
+ dump('[WARN] tests not found: ' + path)
669
+ return None
670
+ result[target_exchange] = io_file_read(path)
671
+ return result
672
+ files = io_dir_read(folder)
673
+ for i in range(0, len(files)):
674
+ file = files[i]
675
+ exchange_name = file.replace('.json', '')
676
+ content = io_file_read(folder + file)
677
+ result[exchange_name] = content
678
+ return result
679
+
680
+ def remove_hostnamefrom_url(self, url):
681
+ if url is None:
682
+ return None
683
+ url_parts = url.split('/')
684
+ res = ''
685
+ for i in range(0, len(url_parts)):
686
+ if i > 2:
687
+ current = url_parts[i]
688
+ if current.find('?') > -1:
689
+ # handle urls like this: /v1/account/accounts?AccessK
690
+ current_parts = current.split('?')
691
+ res += '/'
692
+ res += current_parts[0]
693
+ break
694
+ res += '/'
695
+ res += current
696
+ return res
697
+
698
+ def urlencoded_to_dict(self, url):
699
+ result = {}
700
+ parts = url.split('&')
701
+ for i in range(0, len(parts)):
702
+ part = parts[i]
703
+ key_value = part.split('=')
704
+ keys_length = len(key_value)
705
+ if keys_length != 2:
706
+ continue
707
+ key = key_value[0]
708
+ value = key_value[1]
709
+ if (value is not None) and ((value.startswith('[')) or (value.startswith('{'))):
710
+ # some exchanges might return something like this: timestamp=1699382693405&batchOrders=[{\"symbol\":\"LTCUSDT\",\"side\":\"BUY\",\"newClientOrderI
711
+ value = json_parse(value)
712
+ result[key] = value
713
+ return result
714
+
715
+ def assert_new_and_stored_output(self, exchange, skip_keys, new_output, stored_output, strict_type_check=True, asserting_key=None):
716
+ if is_null_value(new_output) and is_null_value(stored_output):
717
+ return True
718
+ if not new_output and not stored_output:
719
+ return True
720
+ if (isinstance(stored_output, dict)) and (isinstance(new_output, dict)):
721
+ stored_output_keys = list(stored_output.keys())
722
+ new_output_keys = list(new_output.keys())
723
+ stored_keys_length = len(stored_output_keys)
724
+ new_keys_length = len(new_output_keys)
725
+ self.assert_static_error(stored_keys_length == new_keys_length, 'output length mismatch', stored_output, new_output)
726
+ # iterate over the keys
727
+ for i in range(0, len(stored_output_keys)):
728
+ key = stored_output_keys[i]
729
+ if exchange.in_array(key, skip_keys):
730
+ continue
731
+ if not (exchange.in_array(key, new_output_keys)):
732
+ self.assert_static_error(False, 'output key missing: ' + key, stored_output, new_output)
733
+ stored_value = stored_output[key]
734
+ new_value = new_output[key]
735
+ self.assert_new_and_stored_output(exchange, skip_keys, new_value, stored_value, strict_type_check, key)
736
+ elif isinstance(stored_output, list) and (isinstance(new_output, list)):
737
+ stored_array_length = len(stored_output)
738
+ new_array_length = len(new_output)
739
+ self.assert_static_error(stored_array_length == new_array_length, 'output length mismatch', stored_output, new_output)
740
+ for i in range(0, len(stored_output)):
741
+ stored_item = stored_output[i]
742
+ new_item = new_output[i]
743
+ self.assert_new_and_stored_output(exchange, skip_keys, new_item, stored_item, strict_type_check)
744
+ else:
745
+ # built-in types like strings, numbers, booleans
746
+ 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
747
+ sanitized_stored_output = None if (not stored_output) else stored_output
748
+ new_output_string = str(sanitized_new_output) if sanitized_new_output else 'undefined'
749
+ stored_output_string = str(sanitized_stored_output) if sanitized_stored_output else 'undefined'
750
+ message_error = 'output value mismatch:' + new_output_string + ' != ' + stored_output_string
751
+ if strict_type_check and (self.lang != 'C#'):
752
+ # upon building the request we want strict type check to make sure all the types are correct
753
+ # 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
754
+ self.assert_static_error(sanitized_new_output == sanitized_stored_output, message_error, stored_output, new_output, asserting_key)
755
+ else:
756
+ is_computed_bool = (isinstance(sanitized_new_output, bool))
757
+ is_stored_bool = (isinstance(sanitized_stored_output, bool))
758
+ is_computed_string = (isinstance(sanitized_new_output, str))
759
+ is_stored_string = (isinstance(sanitized_stored_output, str))
760
+ is_computed_undefined = (sanitized_new_output is None)
761
+ is_stored_undefined = (sanitized_stored_output is None)
762
+ should_be_same = (is_computed_bool == is_stored_bool) and (is_computed_string == is_stored_string) and (is_computed_undefined == is_stored_undefined)
763
+ self.assert_static_error(should_be_same, 'output type mismatch', stored_output, new_output, asserting_key)
764
+ is_boolean = is_computed_bool or is_stored_bool
765
+ is_string = is_computed_string or is_stored_string
766
+ is_undefined = is_computed_undefined or is_stored_undefined # undefined is a perfetly valid value
767
+ if is_boolean or is_string or is_undefined:
768
+ if self.lang == 'C#':
769
+ # tmp c# number comparsion
770
+ is_number = False
771
+ try:
772
+ exchange.parse_to_numeric(sanitized_new_output)
773
+ is_number = True
774
+ except Exception as e:
775
+ # if we can't parse it to number, then it's not a number
776
+ is_number = False
777
+ if is_number:
778
+ 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)
779
+ return True
780
+ else:
781
+ self.assert_static_error(convert_ascii(new_output_string) == convert_ascii(stored_output_string), message_error, stored_output, new_output, asserting_key)
782
+ return True
783
+ else:
784
+ self.assert_static_error(convert_ascii(new_output_string) == convert_ascii(stored_output_string), message_error, stored_output, new_output, asserting_key)
785
+ return True
786
+ else:
787
+ if self.lang == 'C#':
788
+ stringified_new_output = exchange.number_to_string(sanitized_new_output)
789
+ stringified_stored_output = exchange.number_to_string(sanitized_stored_output)
790
+ self.assert_static_error(str(stringified_new_output) == str(stringified_stored_output), message_error, stored_output, new_output, asserting_key)
791
+ else:
792
+ numeric_new_output = exchange.parse_to_numeric(new_output_string)
793
+ numeric_stored_output = exchange.parse_to_numeric(stored_output_string)
794
+ self.assert_static_error(numeric_new_output == numeric_stored_output, message_error, stored_output, new_output, asserting_key)
795
+ return True # c# requ
796
+
797
+ def assert_static_request_output(self, exchange, type, skip_keys, stored_url, request_url, stored_output, new_output):
798
+ if stored_url != request_url:
799
+ # remove the host part from the url
800
+ first_path = self.remove_hostnamefrom_url(stored_url)
801
+ second_path = self.remove_hostnamefrom_url(request_url)
802
+ self.assert_static_error(first_path == second_path, 'url mismatch', first_path, second_path)
803
+ # body (aka storedOutput and newOutput) is not defined and information is in the url
804
+ # example: "https://open-api.bingx.com/openApi/spot/v1/trade/order?quoteOrderQty=5&side=BUY&symbol=LTC-USDT&timestamp=1698777135343&type=MARKET&signature=d55a7e4f7f9dbe56c4004c9f3ab340869d3cb004e2f0b5b861e5fbd1762fd9a0
805
+ if (stored_output is None) and (new_output is None):
806
+ if (stored_url is not None) and (request_url is not None):
807
+ stored_url_parts = stored_url.split('?')
808
+ new_url_parts = request_url.split('?')
809
+ stored_url_query = exchange.safe_value(stored_url_parts, 1)
810
+ new_url_query = exchange.safe_value(new_url_parts, 1)
811
+ if (stored_url_query is None) and (new_url_query is None):
812
+ # might be a get request without any query parameters
813
+ # example: https://api.gateio.ws/api/v4/delivery/usdt/positions
814
+ return
815
+ stored_url_params = self.urlencoded_to_dict(stored_url_query)
816
+ new_url_params = self.urlencoded_to_dict(new_url_query)
817
+ self.assert_new_and_stored_output(exchange, skip_keys, new_url_params, stored_url_params)
818
+ return
819
+ if type == 'json' and (stored_output is not None) and (new_output is not None):
820
+ if isinstance(stored_output, str):
821
+ stored_output = json_parse(stored_output)
822
+ if isinstance(new_output, str):
823
+ new_output = json_parse(new_output)
824
+ elif type == 'urlencoded' and (stored_output is not None) and (new_output is not None):
825
+ stored_output = self.urlencoded_to_dict(stored_output)
826
+ new_output = self.urlencoded_to_dict(new_output)
827
+ elif type == 'both':
828
+ if stored_output.startswith('{') or stored_output.startswith('['):
829
+ stored_output = json_parse(stored_output)
830
+ new_output = json_parse(new_output)
831
+ else:
832
+ stored_output = self.urlencoded_to_dict(stored_output)
833
+ new_output = self.urlencoded_to_dict(new_output)
834
+ self.assert_new_and_stored_output(exchange, skip_keys, new_output, stored_output)
835
+
836
+ def assert_static_response_output(self, exchange, skip_keys, computed_result, stored_result):
837
+ self.assert_new_and_stored_output(exchange, skip_keys, computed_result, stored_result, False)
838
+
839
+ def sanitize_data_input(self, input):
840
+ # remove nulls and replace with unefined instead
841
+ if input is None:
842
+ return None
843
+ new_input = []
844
+ for i in range(0, len(input)):
845
+ current = input[i]
846
+ if is_null_value(current):
847
+ new_input.append(None)
848
+ else:
849
+ new_input.append(current)
850
+ return new_input
851
+
852
+ async def test_request_statically(self, exchange, method, data, type, skip_keys):
853
+ output = None
854
+ request_url = None
855
+ try:
856
+ if not is_sync():
857
+ await call_exchange_method_dynamically(exchange, method, self.sanitize_data_input(data['input']))
858
+ else:
859
+ call_exchange_method_dynamically_sync(exchange, method, self.sanitize_data_input(data['input']))
860
+ except Exception as e:
861
+ if not (isinstance(e, InvalidProxySettings)):
862
+ raise e
863
+ output = exchange.last_request_body
864
+ request_url = exchange.last_request_url
865
+ try:
866
+ call_output = exchange.safe_value(data, 'output')
867
+ self.assert_static_request_output(exchange, type, skip_keys, data['url'], request_url, call_output, output)
868
+ except Exception as e:
869
+ self.request_tests_failed = True
870
+ error_message = '[' + self.lang + '][STATIC_REQUEST]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' + str(e)
871
+ dump('[TEST_FAILURE]' + error_message)
872
+
873
+ async def test_response_statically(self, exchange, method, skip_keys, data):
874
+ expected_result = exchange.safe_value(data, 'parsedResponse')
875
+ mocked_exchange = set_fetch_response(exchange, data['httpResponse'])
876
+ try:
877
+ if not is_sync():
878
+ unified_result = await call_exchange_method_dynamically(exchange, method, self.sanitize_data_input(data['input']))
879
+ self.assert_static_response_output(mocked_exchange, skip_keys, unified_result, expected_result)
880
+ else:
881
+ unified_result_sync = call_exchange_method_dynamically_sync(exchange, method, self.sanitize_data_input(data['input']))
882
+ self.assert_static_response_output(mocked_exchange, skip_keys, unified_result_sync, expected_result)
883
+ except Exception as e:
884
+ self.response_tests_failed = True
885
+ error_message = '[' + self.lang + '][STATIC_RESPONSE]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' + str(e)
886
+ dump('[TEST_FAILURE]' + error_message)
887
+ set_fetch_response(exchange, None) # reset state
888
+
889
+ def init_offline_exchange(self, exchange_name):
890
+ markets = self.load_markets_from_file(exchange_name)
891
+ currencies = self.load_currencies_from_file(exchange_name)
892
+ exchange = init_exchange(exchange_name, {
893
+ 'markets': markets,
894
+ 'currencies': currencies,
895
+ 'enableRateLimit': False,
896
+ 'rateLimit': 1,
897
+ 'httpProxy': 'http://fake:8080',
898
+ 'httpsProxy': 'http://fake:8080',
899
+ 'apiKey': 'key',
900
+ 'secret': 'secretsecret',
901
+ 'password': 'password',
902
+ 'walletAddress': 'wallet',
903
+ 'privateKey': '0xff3bdd43534543d421f05aec535965b5050ad6ac15345435345435453495e771',
904
+ 'uid': 'uid',
905
+ 'token': 'token',
906
+ 'accountId': 'accountId',
907
+ 'accounts': [{
908
+ 'id': 'myAccount',
909
+ 'code': 'USDT',
910
+ }, {
911
+ 'id': 'myAccount',
912
+ 'code': 'USDC',
913
+ }],
914
+ 'options': {
915
+ 'enableUnifiedAccount': True,
916
+ 'enableUnifiedMargin': False,
917
+ 'accessToken': 'token',
918
+ 'expires': 999999999999999,
919
+ 'leverageBrackets': {},
920
+ },
921
+ })
922
+ exchange.currencies = currencies # not working in python if assigned in the config dict
923
+ return exchange
924
+
925
+ async def test_exchange_request_statically(self, exchange_name, exchange_data, test_name=None):
926
+ # instantiate the exchange and make sure that we sink the requests to avoid an actual request
927
+ exchange = self.init_offline_exchange(exchange_name)
928
+ global_options = exchange.safe_dict(exchange_data, 'options', {})
929
+ # read apiKey/secret from the test file
930
+ api_key = exchange.safe_string(exchange_data, 'apiKey')
931
+ if api_key:
932
+ exchange.apiKey = str(api_key)
933
+ secret = exchange.safe_string(exchange_data, 'secret')
934
+ if secret:
935
+ exchange.secret = str(secret)
936
+ private_key = exchange.safe_string(exchange_data, 'privateKey')
937
+ if private_key:
938
+ exchange.privateKey = str(private_key)
939
+ wallet_address = exchange.safe_string(exchange_data, 'walletAddress')
940
+ if wallet_address:
941
+ exchange.walletAddress = str(wallet_address)
942
+ # exchange.options = exchange.deepExtend (exchange.options, globalOptions); # custom options to be used in the tests
943
+ exchange.extend_exchange_options(global_options)
944
+ methods = exchange.safe_value(exchange_data, 'methods', {})
945
+ methods_names = list(methods.keys())
946
+ for i in range(0, len(methods_names)):
947
+ method = methods_names[i]
948
+ results = methods[method]
949
+ for j in range(0, len(results)):
950
+ result = results[j]
951
+ old_exchange_options = exchange.options # snapshot options;
952
+ test_exchange_options = exchange.safe_value(result, 'options', {})
953
+ # exchange.options = exchange.deepExtend (oldExchangeOptions, testExchangeOptions); # custom options to be used in the tests
954
+ exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, test_exchange_options))
955
+ description = exchange.safe_value(result, 'description')
956
+ if (test_name is not None) and (test_name != description):
957
+ continue
958
+ is_disabled = exchange.safe_bool(result, 'disabled', False)
959
+ if is_disabled:
960
+ continue
961
+ disabled_string = exchange.safe_string(result, 'disabled', '')
962
+ if disabled_string != '':
963
+ continue
964
+ is_disabled_c_sharp = exchange.safe_bool(result, 'disabledCS', False)
965
+ if is_disabled_c_sharp and (self.lang == 'C#'):
966
+ continue
967
+ type = exchange.safe_string(exchange_data, 'outputType')
968
+ skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
969
+ await self.test_request_statically(exchange, method, result, type, skip_keys)
970
+ # reset options
971
+ # exchange.options = exchange.deepExtend (oldExchangeOptions, {});
972
+ exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, {}))
973
+ if not is_sync():
974
+ await close(exchange)
975
+ return True # in c# methods that will be used with promiseAll need to return something
976
+
977
+ async def test_exchange_response_statically(self, exchange_name, exchange_data, test_name=None):
978
+ exchange = self.init_offline_exchange(exchange_name)
979
+ # read apiKey/secret from the test file
980
+ api_key = exchange.safe_string(exchange_data, 'apiKey')
981
+ if api_key:
982
+ exchange.apiKey = str(api_key)
983
+ secret = exchange.safe_string(exchange_data, 'secret')
984
+ if secret:
985
+ exchange.secret = str(secret)
986
+ private_key = exchange.safe_string(exchange_data, 'privateKey')
987
+ if private_key:
988
+ exchange.privateKey = str(private_key)
989
+ wallet_address = exchange.safe_string(exchange_data, 'walletAddress')
990
+ if wallet_address:
991
+ exchange.walletAddress = str(wallet_address)
992
+ methods = exchange.safe_value(exchange_data, 'methods', {})
993
+ options = exchange.safe_value(exchange_data, 'options', {})
994
+ # exchange.options = exchange.deepExtend (exchange.options, options); # custom options to be used in the tests
995
+ exchange.extend_exchange_options(options)
996
+ methods_names = list(methods.keys())
997
+ for i in range(0, len(methods_names)):
998
+ method = methods_names[i]
999
+ results = methods[method]
1000
+ for j in range(0, len(results)):
1001
+ result = results[j]
1002
+ description = exchange.safe_value(result, 'description')
1003
+ old_exchange_options = exchange.options # snapshot options;
1004
+ test_exchange_options = exchange.safe_value(result, 'options', {})
1005
+ # exchange.options = exchange.deepExtend (oldExchangeOptions, testExchangeOptions); # custom options to be used in the tests
1006
+ exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, test_exchange_options))
1007
+ is_disabled = exchange.safe_bool(result, 'disabled', False)
1008
+ if is_disabled:
1009
+ continue
1010
+ is_disabled_c_sharp = exchange.safe_bool(result, 'disabledCS', False)
1011
+ if is_disabled_c_sharp and (self.lang == 'C#'):
1012
+ continue
1013
+ is_disabled_php = exchange.safe_bool(result, 'disabledPHP', False)
1014
+ if is_disabled_php and (self.lang == 'PHP'):
1015
+ continue
1016
+ if (test_name is not None) and (test_name != description):
1017
+ continue
1018
+ skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
1019
+ await self.test_response_statically(exchange, method, skip_keys, result)
1020
+ # reset options
1021
+ # exchange.options = exchange.deepExtend (oldExchangeOptions, {});
1022
+ exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, {}))
1023
+ if not is_sync():
1024
+ await close(exchange)
1025
+ return True # in c# methods that will be used with promiseAll need to return something
1026
+
1027
+ def get_number_of_tests_from_exchange(self, exchange, exchange_data, test_name=None):
1028
+ if test_name is not None:
1029
+ return 1
1030
+ sum = 0
1031
+ methods = exchange_data['methods']
1032
+ methods_names = list(methods.keys())
1033
+ for i in range(0, len(methods_names)):
1034
+ method = methods_names[i]
1035
+ results = methods[method]
1036
+ results_length = len(results)
1037
+ sum = exchange.sum(sum, results_length)
1038
+ return sum
1039
+
1040
+ async def run_static_request_tests(self, target_exchange=None, test_name=None):
1041
+ await self.run_static_tests('request', target_exchange, test_name)
1042
+
1043
+ async def run_static_tests(self, type, target_exchange=None, test_name=None):
1044
+ folder = get_root_dir() + './ts/src/test/static/' + type + '/'
1045
+ static_data = self.load_static_data(folder, target_exchange)
1046
+ if static_data is None:
1047
+ return
1048
+ exchanges = list(static_data.keys())
1049
+ exchange = init_exchange('Exchange', {}) # tmp to do the calculations until we have the ast-transpiler transpiling this code
1050
+ promises = []
1051
+ sum = 0
1052
+ if target_exchange:
1053
+ dump('[INFO:MAIN] Exchange to test: ' + target_exchange)
1054
+ if test_name:
1055
+ dump('[INFO:MAIN] Testing only: ' + test_name)
1056
+ for i in range(0, len(exchanges)):
1057
+ exchange_name = exchanges[i]
1058
+ exchange_data = static_data[exchange_name]
1059
+ number_of_tests = self.get_number_of_tests_from_exchange(exchange, exchange_data, test_name)
1060
+ sum = exchange.sum(sum, number_of_tests)
1061
+ if type == 'request':
1062
+ promises.append(self.test_exchange_request_statically(exchange_name, exchange_data, test_name))
1063
+ else:
1064
+ promises.append(self.test_exchange_response_statically(exchange_name, exchange_data, test_name))
1065
+ await asyncio.gather(*promises)
1066
+ if self.request_tests_failed or self.response_tests_failed:
1067
+ exit_script(1)
1068
+ else:
1069
+ prefix = '[SYNC]' if (is_sync()) else ''
1070
+ success_message = '[' + self.lang + ']' + prefix + '[TEST_SUCCESS] ' + str(sum) + ' static ' + type + ' tests passed.'
1071
+ dump('[INFO]' + success_message)
1072
+
1073
+ async def run_static_response_tests(self, exchange_name=None, test=None):
1074
+ # -----------------------------------------------------------------------------
1075
+ # --- Init of mockResponses tests functions------------------------------------
1076
+ # -----------------------------------------------------------------------------
1077
+ await self.run_static_tests('response', exchange_name, test)
1078
+
1079
+ async def run_broker_id_tests(self):
1080
+ # -----------------------------------------------------------------------------
1081
+ # --- Init of brokerId tests functions-----------------------------------------
1082
+ # -----------------------------------------------------------------------------
1083
+ 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(), self.test_vertex(), self.test_paradex(), self.test_hashkey(), self.test_coincatch()]
1084
+ await asyncio.gather(*promises)
1085
+ success_message = '[' + self.lang + '][TEST_SUCCESS] brokerId tests passed.'
1086
+ dump('[INFO]' + success_message)
1087
+ exit_script(0)
1088
+
1089
+ async def test_binance(self):
1090
+ exchange = self.init_offline_exchange('binance')
1091
+ spot_id = 'x-R4BD3S82'
1092
+ spot_order_request = None
1093
+ try:
1094
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1095
+ except Exception as e:
1096
+ spot_order_request = self.urlencoded_to_dict(exchange.last_request_body)
1097
+ client_order_id = spot_order_request['newClientOrderId']
1098
+ spot_id_string = str(spot_id)
1099
+ assert client_order_id.startswith(spot_id_string), 'binance - spot clientOrderId: ' + client_order_id + ' does not start with spotId' + spot_id_string
1100
+ swap_id = 'x-xcKtGhcu'
1101
+ swap_order_request = None
1102
+ try:
1103
+ await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
1104
+ except Exception as e:
1105
+ swap_order_request = self.urlencoded_to_dict(exchange.last_request_body)
1106
+ swap_inverse_order_request = None
1107
+ try:
1108
+ await exchange.create_order('BTC/USD:BTC', 'limit', 'buy', 1, 20000)
1109
+ except Exception as e:
1110
+ swap_inverse_order_request = self.urlencoded_to_dict(exchange.last_request_body)
1111
+ client_order_id_swap = swap_order_request['newClientOrderId']
1112
+ swap_id_string = str(swap_id)
1113
+ assert client_order_id_swap.startswith(swap_id_string), 'binance - swap clientOrderId: ' + client_order_id_swap + ' does not start with swapId' + swap_id_string
1114
+ client_order_id_inverse = swap_inverse_order_request['newClientOrderId']
1115
+ assert client_order_id_inverse.startswith(swap_id_string), 'binance - swap clientOrderIdInverse: ' + client_order_id_inverse + ' does not start with swapId' + swap_id_string
1116
+ if not is_sync():
1117
+ await close(exchange)
1118
+ return True
1119
+
1120
+ async def test_okx(self):
1121
+ exchange = self.init_offline_exchange('okx')
1122
+ id = 'e847386590ce4dBC'
1123
+ spot_order_request = None
1124
+ try:
1125
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1126
+ except Exception as e:
1127
+ spot_order_request = json_parse(exchange.last_request_body)
1128
+ client_order_id = spot_order_request[0]['clOrdId'] # returns order inside array
1129
+ id_string = str(id)
1130
+ assert client_order_id.startswith(id_string), 'okx - spot clientOrderId: ' + client_order_id + ' does not start with id: ' + id_string
1131
+ spot_tag = spot_order_request[0]['tag']
1132
+ assert spot_tag == id, 'okx - id: ' + id + ' different from spot tag: ' + spot_tag
1133
+ swap_order_request = None
1134
+ try:
1135
+ await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
1136
+ except Exception as e:
1137
+ swap_order_request = json_parse(exchange.last_request_body)
1138
+ client_order_id_swap = swap_order_request[0]['clOrdId']
1139
+ assert client_order_id_swap.startswith(id_string), 'okx - swap clientOrderId: ' + client_order_id_swap + ' does not start with id: ' + id_string
1140
+ swap_tag = swap_order_request[0]['tag']
1141
+ assert swap_tag == id, 'okx - id: ' + id + ' different from swap tag: ' + swap_tag
1142
+ if not is_sync():
1143
+ await close(exchange)
1144
+ return True
1145
+
1146
+ async def test_cryptocom(self):
1147
+ exchange = self.init_offline_exchange('cryptocom')
1148
+ id = 'CCXT'
1149
+ await exchange.load_markets()
1150
+ request = None
1151
+ try:
1152
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1153
+ except Exception as e:
1154
+ request = json_parse(exchange.last_request_body)
1155
+ broker_id = request['params']['broker_id']
1156
+ assert broker_id == id, 'cryptocom - id: ' + id + ' different from broker_id: ' + broker_id
1157
+ if not is_sync():
1158
+ await close(exchange)
1159
+ return True
1160
+
1161
+ async def test_bybit(self):
1162
+ exchange = self.init_offline_exchange('bybit')
1163
+ req_headers = None
1164
+ id = 'CCXT'
1165
+ assert exchange.options['brokerId'] == id, 'id not in options'
1166
+ try:
1167
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1168
+ except Exception as e:
1169
+ # we expect an error here, we're only interested in the headers
1170
+ req_headers = exchange.last_request_headers
1171
+ assert req_headers['Referer'] == id, 'bybit - id: ' + id + ' not in headers.'
1172
+ if not is_sync():
1173
+ await close(exchange)
1174
+ return True
1175
+
1176
+ async def test_kucoin(self):
1177
+ exchange = self.init_offline_exchange('kucoin')
1178
+ req_headers = None
1179
+ spot_id = exchange.options['partner']['spot']['id']
1180
+ spot_key = exchange.options['partner']['spot']['key']
1181
+ assert spot_id == 'ccxt', 'kucoin - id: ' + spot_id + ' not in options'
1182
+ assert spot_key == '9e58cc35-5b5e-4133-92ec-166e3f077cb8', 'kucoin - key: ' + spot_key + ' not in options.'
1183
+ try:
1184
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1185
+ except Exception as e:
1186
+ # we expect an error here, we're only interested in the headers
1187
+ req_headers = exchange.last_request_headers
1188
+ id = 'ccxt'
1189
+ assert req_headers['KC-API-PARTNER'] == id, 'kucoin - id: ' + id + ' not in headers.'
1190
+ if not is_sync():
1191
+ await close(exchange)
1192
+ return True
1193
+
1194
+ async def test_kucoinfutures(self):
1195
+ exchange = self.init_offline_exchange('kucoinfutures')
1196
+ req_headers = None
1197
+ id = 'ccxtfutures'
1198
+ future_id = exchange.options['partner']['future']['id']
1199
+ future_key = exchange.options['partner']['future']['key']
1200
+ assert future_id == id, 'kucoinfutures - id: ' + future_id + ' not in options.'
1201
+ assert future_key == '1b327198-f30c-4f14-a0ac-918871282f15', 'kucoinfutures - key: ' + future_key + ' not in options.'
1202
+ try:
1203
+ await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
1204
+ except Exception as e:
1205
+ req_headers = exchange.last_request_headers
1206
+ assert req_headers['KC-API-PARTNER'] == id, 'kucoinfutures - id: ' + id + ' not in headers.'
1207
+ if not is_sync():
1208
+ await close(exchange)
1209
+ return True
1210
+
1211
+ async def test_bitget(self):
1212
+ exchange = self.init_offline_exchange('bitget')
1213
+ req_headers = None
1214
+ id = 'p4sve'
1215
+ assert exchange.options['broker'] == id, 'bitget - id: ' + id + ' not in options'
1216
+ try:
1217
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1218
+ except Exception as e:
1219
+ req_headers = exchange.last_request_headers
1220
+ assert req_headers['X-CHANNEL-API-CODE'] == id, 'bitget - id: ' + id + ' not in headers.'
1221
+ if not is_sync():
1222
+ await close(exchange)
1223
+ return True
1224
+
1225
+ async def test_mexc(self):
1226
+ exchange = self.init_offline_exchange('mexc')
1227
+ req_headers = None
1228
+ id = 'CCXT'
1229
+ assert exchange.options['broker'] == id, 'mexc - id: ' + id + ' not in options'
1230
+ await exchange.load_markets()
1231
+ try:
1232
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1233
+ except Exception as e:
1234
+ req_headers = exchange.last_request_headers
1235
+ assert req_headers['source'] == id, 'mexc - id: ' + id + ' not in headers.'
1236
+ if not is_sync():
1237
+ await close(exchange)
1238
+ return True
1239
+
1240
+ async def test_htx(self):
1241
+ exchange = self.init_offline_exchange('htx')
1242
+ # spot test
1243
+ id = 'AA03022abc'
1244
+ spot_order_request = None
1245
+ try:
1246
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1247
+ except Exception as e:
1248
+ spot_order_request = json_parse(exchange.last_request_body)
1249
+ client_order_id = spot_order_request['client-order-id']
1250
+ id_string = str(id)
1251
+ assert client_order_id.startswith(id_string), 'htx - spot clientOrderId ' + client_order_id + ' does not start with id: ' + id_string
1252
+ # swap test
1253
+ swap_order_request = None
1254
+ try:
1255
+ await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
1256
+ except Exception as e:
1257
+ swap_order_request = json_parse(exchange.last_request_body)
1258
+ swap_inverse_order_request = None
1259
+ try:
1260
+ await exchange.create_order('BTC/USD:BTC', 'limit', 'buy', 1, 20000)
1261
+ except Exception as e:
1262
+ swap_inverse_order_request = json_parse(exchange.last_request_body)
1263
+ client_order_id_swap = swap_order_request['channel_code']
1264
+ assert client_order_id_swap.startswith(id_string), 'htx - swap channel_code ' + client_order_id_swap + ' does not start with id: ' + id_string
1265
+ client_order_id_inverse = swap_inverse_order_request['channel_code']
1266
+ assert client_order_id_inverse.startswith(id_string), 'htx - swap inverse channel_code ' + client_order_id_inverse + ' does not start with id: ' + id_string
1267
+ if not is_sync():
1268
+ await close(exchange)
1269
+ return True
1270
+
1271
+ async def test_woo(self):
1272
+ exchange = self.init_offline_exchange('woo')
1273
+ # spot test
1274
+ id = 'bc830de7-50f3-460b-9ee0-f430f83f9dad'
1275
+ spot_order_request = None
1276
+ try:
1277
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1278
+ except Exception as e:
1279
+ spot_order_request = self.urlencoded_to_dict(exchange.last_request_body)
1280
+ broker_id = spot_order_request['broker_id']
1281
+ id_string = str(id)
1282
+ assert broker_id.startswith(id_string), 'woo - broker_id: ' + broker_id + ' does not start with id: ' + id_string
1283
+ # swap test
1284
+ stop_order_request = None
1285
+ try:
1286
+ await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000, {
1287
+ 'stopPrice': 30000,
1288
+ })
1289
+ except Exception as e:
1290
+ stop_order_request = json_parse(exchange.last_request_body)
1291
+ client_order_id_stop = stop_order_request['brokerId']
1292
+ assert client_order_id_stop.startswith(id_string), 'woo - brokerId: ' + client_order_id_stop + ' does not start with id: ' + id_string
1293
+ if not is_sync():
1294
+ await close(exchange)
1295
+ return True
1296
+
1297
+ async def test_bitmart(self):
1298
+ exchange = self.init_offline_exchange('bitmart')
1299
+ req_headers = None
1300
+ id = 'CCXTxBitmart000'
1301
+ assert exchange.options['brokerId'] == id, 'bitmart - id: ' + id + ' not in options'
1302
+ await exchange.load_markets()
1303
+ try:
1304
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1305
+ except Exception as e:
1306
+ req_headers = exchange.last_request_headers
1307
+ assert req_headers['X-BM-BROKER-ID'] == id, 'bitmart - id: ' + id + ' not in headers'
1308
+ if not is_sync():
1309
+ await close(exchange)
1310
+ return True
1311
+
1312
+ async def test_coinex(self):
1313
+ exchange = self.init_offline_exchange('coinex')
1314
+ id = 'x-167673045'
1315
+ assert exchange.options['brokerId'] == id, 'coinex - id: ' + id + ' not in options'
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['client_id']
1322
+ id_string = str(id)
1323
+ assert client_order_id.startswith(id_string), 'coinex - clientOrderId: ' + client_order_id + ' does not start with id: ' + id_string
1324
+ if not is_sync():
1325
+ await close(exchange)
1326
+ return True
1327
+
1328
+ async def test_bingx(self):
1329
+ exchange = self.init_offline_exchange('bingx')
1330
+ req_headers = None
1331
+ id = 'CCXT'
1332
+ assert exchange.options['broker'] == id, 'bingx - id: ' + id + ' not in options'
1333
+ try:
1334
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1335
+ except Exception as e:
1336
+ # we expect an error here, we're only interested in the headers
1337
+ req_headers = exchange.last_request_headers
1338
+ assert req_headers['X-SOURCE-KEY'] == id, 'bingx - id: ' + id + ' not in headers.'
1339
+ if not is_sync():
1340
+ await close(exchange)
1341
+
1342
+ async def test_phemex(self):
1343
+ exchange = self.init_offline_exchange('phemex')
1344
+ id = 'CCXT123456'
1345
+ request = None
1346
+ try:
1347
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1348
+ except Exception as e:
1349
+ request = json_parse(exchange.last_request_body)
1350
+ client_order_id = request['clOrdID']
1351
+ id_string = str(id)
1352
+ assert client_order_id.startswith(id_string), 'phemex - clOrdID: ' + client_order_id + ' does not start with id: ' + id_string
1353
+ if not is_sync():
1354
+ await close(exchange)
1355
+
1356
+ async def test_blofin(self):
1357
+ exchange = self.init_offline_exchange('blofin')
1358
+ id = 'ec6dd3a7dd982d0b'
1359
+ request = None
1360
+ try:
1361
+ await exchange.create_order('LTC/USDT:USDT', 'market', 'buy', 1)
1362
+ except Exception as e:
1363
+ request = json_parse(exchange.last_request_body)
1364
+ broker_id = request['brokerId']
1365
+ id_string = str(id)
1366
+ assert broker_id.startswith(id_string), 'blofin - brokerId: ' + broker_id + ' does not start with id: ' + id_string
1367
+ if not is_sync():
1368
+ await close(exchange)
1369
+
1370
+ async def test_hyperliquid(self):
1371
+ exchange = self.init_offline_exchange('hyperliquid')
1372
+ id = '1'
1373
+ request = None
1374
+ try:
1375
+ await exchange.create_order('SOL/USDC:USDC', 'limit', 'buy', 1, 100)
1376
+ except Exception as e:
1377
+ request = json_parse(exchange.last_request_body)
1378
+ broker_id = str((request['action']['brokerCode']))
1379
+ assert broker_id == id, 'hyperliquid - brokerId: ' + broker_id + ' does not start with id: ' + id
1380
+ if not is_sync():
1381
+ await close(exchange)
1382
+
1383
+ async def test_coinbaseinternational(self):
1384
+ exchange = self.init_offline_exchange('coinbaseinternational')
1385
+ exchange.options['portfolio'] = 'random'
1386
+ id = 'nfqkvdjp'
1387
+ assert exchange.options['brokerId'] == id, 'id not in options'
1388
+ request = None
1389
+ try:
1390
+ await exchange.create_order('BTC/USDC:USDC', 'limit', 'buy', 1, 20000)
1391
+ except Exception as e:
1392
+ request = json_parse(exchange.last_request_body)
1393
+ client_order_id = request['client_order_id']
1394
+ assert client_order_id.startswith(str(id)), 'clientOrderId does not start with id'
1395
+ if not is_sync():
1396
+ await close(exchange)
1397
+ return True
1398
+
1399
+ async def test_coinbase_advanced(self):
1400
+ exchange = self.init_offline_exchange('coinbase')
1401
+ id = 'ccxt'
1402
+ assert exchange.options['brokerId'] == id, 'id not in options'
1403
+ request = None
1404
+ try:
1405
+ await exchange.create_order('BTC/USDC', 'limit', 'buy', 1, 20000)
1406
+ except Exception as e:
1407
+ request = json_parse(exchange.last_request_body)
1408
+ client_order_id = request['client_order_id']
1409
+ assert client_order_id.startswith(str(id)), 'clientOrderId does not start with id'
1410
+ if not is_sync():
1411
+ await close(exchange)
1412
+ return True
1413
+
1414
+ async def test_woofi_pro(self):
1415
+ exchange = self.init_offline_exchange('woofipro')
1416
+ exchange.secret = 'secretsecretsecretsecretsecretsecretsecrets'
1417
+ id = 'CCXT'
1418
+ await exchange.load_markets()
1419
+ request = None
1420
+ try:
1421
+ await exchange.create_order('BTC/USDC:USDC', 'limit', 'buy', 1, 20000)
1422
+ except Exception as e:
1423
+ request = json_parse(exchange.last_request_body)
1424
+ broker_id = request['order_tag']
1425
+ assert broker_id == id, 'woofipro - id: ' + id + ' different from broker_id: ' + broker_id
1426
+ if not is_sync():
1427
+ await close(exchange)
1428
+ return True
1429
+
1430
+ async def test_oxfun(self):
1431
+ exchange = self.init_offline_exchange('oxfun')
1432
+ exchange.secret = 'secretsecretsecretsecretsecretsecretsecrets'
1433
+ id = 1000
1434
+ await exchange.load_markets()
1435
+ request = None
1436
+ try:
1437
+ await exchange.create_order('BTC/USD:OX', 'limit', 'buy', 1, 20000)
1438
+ except Exception as e:
1439
+ request = json_parse(exchange.last_request_body)
1440
+ orders = request['orders']
1441
+ first = orders[0]
1442
+ broker_id = first['source']
1443
+ assert broker_id == id, 'oxfun - id: ' + str(id) + ' different from broker_id: ' + str(broker_id)
1444
+ return True
1445
+
1446
+ async def test_xt(self):
1447
+ exchange = self.init_offline_exchange('xt')
1448
+ id = 'CCXT'
1449
+ spot_order_request = None
1450
+ try:
1451
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1452
+ except Exception as e:
1453
+ spot_order_request = json_parse(exchange.last_request_body)
1454
+ spot_media = spot_order_request['media']
1455
+ assert spot_media == id, 'xt - id: ' + id + ' different from swap tag: ' + spot_media
1456
+ swap_order_request = None
1457
+ try:
1458
+ await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
1459
+ except Exception as e:
1460
+ swap_order_request = json_parse(exchange.last_request_body)
1461
+ swap_media = swap_order_request['clientMedia']
1462
+ assert swap_media == id, 'xt - id: ' + id + ' different from swap tag: ' + swap_media
1463
+ if not is_sync():
1464
+ await close(exchange)
1465
+ return True
1466
+
1467
+ async def test_vertex(self):
1468
+ exchange = self.init_offline_exchange('vertex')
1469
+ exchange.walletAddress = '0xc751489d24a33172541ea451bc253d7a9e98c781'
1470
+ exchange.privateKey = 'c33b1eb4b53108bf52e10f636d8c1236c04c33a712357ba3543ab45f48a5cb0b'
1471
+ exchange.options['v1contracts'] = {
1472
+ 'chain_id': '42161',
1473
+ 'endpoint_addr': '0xbbee07b3e8121227afcfe1e2b82772246226128e',
1474
+ 'book_addrs': ['0x0000000000000000000000000000000000000000', '0x70e5911371472e406f1291c621d1c8f207764d73', '0xf03f457a30e598d5020164a339727ef40f2b8fbc', '0x1c6281a78aa0ed88949c319cba5f0f0de2ce8353', '0xfe653438a1a4a7f56e727509c341d60a7b54fa91', '0xb6304e9a6ca241376a5fc9294daa8fca65ddcdcd', '0x01ec802ae0ab1b2cc4f028b9fe6eb954aef06ed1', '0x0000000000000000000000000000000000000000', '0x9c52d5c4df5a68955ad088a781b4ab364a861e9e', '0x0000000000000000000000000000000000000000', '0x2a3bcda1bb3ef649f3571c96c597c3d2b25edc79', '0x0000000000000000000000000000000000000000', '0x0492ff9807f82856781488015ef7aa5526c0edd6', '0x0000000000000000000000000000000000000000', '0xea884c82418ebc21cd080b8f40ecc4d06a6a6883', '0x0000000000000000000000000000000000000000', '0x5ecf68f983253a818ca8c17a56a4f2fb48d6ec6b', '0x0000000000000000000000000000000000000000', '0xba3f57a977f099905531f7c2f294aad7b56ed254', '0x0000000000000000000000000000000000000000', '0x0ac8c26d207d0c6aabb3644fea18f530c4d6fc8e', '0x0000000000000000000000000000000000000000', '0x8bd80ad7630b3864bed66cf28f548143ea43dc3b', '0x0000000000000000000000000000000000000000', '0x045391227fc4b2cdd27b95f066864225afc9314e', '0x0000000000000000000000000000000000000000', '0x7d512bef2e6cfd7e7f5f6b2f8027e3728eb7b6c3', '0x0000000000000000000000000000000000000000', '0x678a6c5003b56b5e9a81559e9a0df880407c796f', '0x0000000000000000000000000000000000000000', '0x14b5a17208fa98843cc602b3f74e31c95ded3567', '0xe442a89a07b3888ab10579fbb2824aeceff3a282', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0xac28ac205275d7c2d6877bea8657cebe04fd9ae9', '0x0000000000000000000000000000000000000000', '0xed811409bfea901e75cb19ba347c08a154e860c9', '0x0000000000000000000000000000000000000000', '0x0f7afcb1612b305626cff84f84e4169ba2d0f12c', '0x0000000000000000000000000000000000000000', '0xe4b8d903db2ce2d3891ef04cfc3ac56330c1b0c3', '0x5f44362bad629846b7455ad9d36bbc3759a3ef62', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0xa64e04ed4b223a71e524dc7ebb7f28e422ccfdde', '0x0000000000000000000000000000000000000000', '0x2ee573caab73c1d8cf0ca6bd3589b67de79628a4', '0x0000000000000000000000000000000000000000', '0x01bb96883a8a478d4410387d4aaf11067edc2c74', '0x0000000000000000000000000000000000000000', '0xe7ed0c559d905436a867cddf07e06921d572363c', '0x0000000000000000000000000000000000000000', '0xa94f9e3433c92a5cd1925494811a67b1943557d9', '0x0000000000000000000000000000000000000000', '0xa63de7f89ba1270b85f3dcc193ff1a1390a7c7c7', '0x0000000000000000000000000000000000000000', '0xc8b0b37dffe3a711a076dc86dd617cc203f36121', '0x0000000000000000000000000000000000000000', '0x646df48947ff785fe609969ff634e7be9d1c34cd', '0x0000000000000000000000000000000000000000', '0x42582b404b0bec4a266631a0e178840b107a0c69', '0x0000000000000000000000000000000000000000', '0x36a94bc3edb1b629d1413091e22dc65fa050f17f', '0x0000000000000000000000000000000000000000', '0xb398d00b5a336f0ad33cfb352fd7646171cec442', '0x0000000000000000000000000000000000000000', '0xb4bc3b00de98e1c0498699379f6607b1f00bd5a1', '0x0000000000000000000000000000000000000000', '0xfe8b7baf68952bac2c04f386223d2013c1b4c601', '0x0000000000000000000000000000000000000000', '0x9c8764ec71f175c97c6c2fd558eb6546fcdbea32', '0x0000000000000000000000000000000000000000', '0x94d31188982c8eccf243e555b22dc57de1dba4e1', '0x0000000000000000000000000000000000000000', '0x407c5e2fadd7555be927c028bc358daa907c797a', '0x0000000000000000000000000000000000000000', '0x7e97da2dbbbdd7fb313cf9dc0581ac7cec999c70', '0x0000000000000000000000000000000000000000', '0x7f8d2662f64dd468c423805f98a6579ad59b28fa', '0x0000000000000000000000000000000000000000', '0x3398adf63fed17cbadd6080a1fb771e6a2a55958', '0x0000000000000000000000000000000000000000', '0xba8910a1d7ab62129729047d453091a1e6356170', '0x0000000000000000000000000000000000000000', '0xdc054bce222fe725da0f17abcef38253bd8bb745', '0x0000000000000000000000000000000000000000', '0xca21693467d0a5ea9e10a5a7c5044b9b3837e694', '0x0000000000000000000000000000000000000000', '0xe0b02de2139256dbae55cf350094b882fbe629ea', '0x0000000000000000000000000000000000000000', '0x02c38368a6f53858aab5a3a8d91d73eb59edf9b9', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0xfe8c4778843c3cb047ffe7c0c0154a724c05cab9', '0x0000000000000000000000000000000000000000', '0xe2e88862d9b7379e21c82fc4aec8d71bddbcdb4b', '0x0000000000000000000000000000000000000000', '0xbbaff9e73b30f9cea5c01481f12de75050947fd6', '0x0000000000000000000000000000000000000000', '0xa20f6f381fe0fec5a1035d37ebf8890726377ab9', '0x0000000000000000000000000000000000000000', '0xbad68032d012bf35d3a2a177b242e86684027ed0', '0x0000000000000000000000000000000000000000', '0x0e61ca37f0c67e8a8794e45e264970a2a23a513c', '0x0000000000000000000000000000000000000000', '0xa77b7048e378c5270b15918449ededf87c3a3db3', '0x0000000000000000000000000000000000000000', '0x15afca1e6f02b556fa6551021b3493a1e4a7f44f'],
1475
+ }
1476
+ id = 5930043274845996
1477
+ await exchange.load_markets()
1478
+ request = None
1479
+ try:
1480
+ await exchange.create_order('BTC/USDC:USDC', 'limit', 'buy', 1, 20000)
1481
+ except Exception as e:
1482
+ request = json_parse(exchange.last_request_body)
1483
+ order = request['place_order']
1484
+ broker_id = order['id']
1485
+ assert broker_id == id, 'vertex - id: ' + str(id) + ' different from broker_id: ' + str(broker_id)
1486
+ if not is_sync():
1487
+ await close(exchange)
1488
+ return True
1489
+
1490
+ async def test_paradex(self):
1491
+ exchange = self.init_offline_exchange('paradex')
1492
+ exchange.walletAddress = '0xc751489d24a33172541ea451bc253d7a9e98c781'
1493
+ exchange.privateKey = 'c33b1eb4b53108bf52e10f636d8c1236c04c33a712357ba3543ab45f48a5cb0b'
1494
+ exchange.options['authToken'] = 'token'
1495
+ exchange.options['systemConfig'] = {
1496
+ 'starknet_gateway_url': 'https://potc-testnet-sepolia.starknet.io',
1497
+ 'starknet_fullnode_rpc_url': 'https://pathfinder.api.testnet.paradex.trade/rpc/v0_7',
1498
+ 'starknet_chain_id': 'PRIVATE_SN_POTC_SEPOLIA',
1499
+ 'block_explorer_url': 'https://voyager.testnet.paradex.trade/',
1500
+ 'paraclear_address': '0x286003f7c7bfc3f94e8f0af48b48302e7aee2fb13c23b141479ba00832ef2c6',
1501
+ 'paraclear_decimals': 8,
1502
+ 'paraclear_account_proxy_hash': '0x3530cc4759d78042f1b543bf797f5f3d647cde0388c33734cf91b7f7b9314a9',
1503
+ 'paraclear_account_hash': '0x41cb0280ebadaa75f996d8d92c6f265f6d040bb3ba442e5f86a554f1765244e',
1504
+ 'oracle_address': '0x2c6a867917ef858d6b193a0ff9e62b46d0dc760366920d631715d58baeaca1f',
1505
+ 'bridged_tokens': [{
1506
+ 'name': 'TEST USDC',
1507
+ 'symbol': 'USDC',
1508
+ 'decimals': 6,
1509
+ 'l1_token_address': '0x29A873159D5e14AcBd63913D4A7E2df04570c666',
1510
+ 'l1_bridge_address': '0x8586e05adc0C35aa11609023d4Ae6075Cb813b4C',
1511
+ 'l2_token_address': '0x6f373b346561036d98ea10fb3e60d2f459c872b1933b50b21fe6ef4fda3b75e',
1512
+ 'l2_bridge_address': '0x46e9237f5408b5f899e72125dd69bd55485a287aaf24663d3ebe00d237fc7ef',
1513
+ }],
1514
+ 'l1_core_contract_address': '0x582CC5d9b509391232cd544cDF9da036e55833Af',
1515
+ 'l1_operator_address': '0x11bACdFbBcd3Febe5e8CEAa75E0Ef6444d9B45FB',
1516
+ 'l1_chain_id': '11155111',
1517
+ 'liquidation_fee': '0.2',
1518
+ }
1519
+ req_headers = None
1520
+ id = 'CCXT'
1521
+ assert exchange.options['broker'] == id, 'paradex - id: ' + id + ' not in options'
1522
+ await exchange.load_markets()
1523
+ try:
1524
+ await exchange.create_order('BTC/USD:USDC', 'limit', 'buy', 1, 20000)
1525
+ except Exception as e:
1526
+ req_headers = exchange.last_request_headers
1527
+ assert req_headers['PARADEX-PARTNER'] == id, 'paradex - id: ' + id + ' not in headers'
1528
+ if not is_sync():
1529
+ await close(exchange)
1530
+ return True
1531
+
1532
+ async def test_hashkey(self):
1533
+ exchange = self.init_offline_exchange('hashkey')
1534
+ req_headers = None
1535
+ id = '10000700011'
1536
+ try:
1537
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1538
+ except Exception as e:
1539
+ # we expect an error here, we're only interested in the headers
1540
+ req_headers = exchange.last_request_headers
1541
+ assert req_headers['INPUT-SOURCE'] == id, 'hashkey - id: ' + id + ' not in headers.'
1542
+ if not is_sync():
1543
+ await close(exchange)
1544
+ return True
1545
+
1546
+ async def test_coincatch(self):
1547
+ exchange = self.init_offline_exchange('coincatch')
1548
+ req_headers = None
1549
+ id = '47cfy'
1550
+ try:
1551
+ await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
1552
+ except Exception as e:
1553
+ # we expect an error here, we're only interested in the headers
1554
+ req_headers = exchange.last_request_headers
1555
+ assert req_headers['X-CHANNEL-API-CODE'] == id, 'coincatch - id: ' + id + ' not in headers.'
1556
+ if not is_sync():
1557
+ await close(exchange)
1558
+ return True