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