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