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
|
@@ -0,0 +1,2462 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
|
4
|
+
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
|
5
|
+
|
|
6
|
+
from ccxt.async_support.base.exchange import Exchange
|
|
7
|
+
from ccxt.abstract.exmo import ImplicitAPI
|
|
8
|
+
import hashlib
|
|
9
|
+
from ccxt.base.types import Balances, Currencies, Currency, Int, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction
|
|
10
|
+
from typing import List
|
|
11
|
+
from ccxt.base.errors import ExchangeError
|
|
12
|
+
from ccxt.base.errors import AuthenticationError
|
|
13
|
+
from ccxt.base.errors import PermissionDenied
|
|
14
|
+
from ccxt.base.errors import ArgumentsRequired
|
|
15
|
+
from ccxt.base.errors import BadRequest
|
|
16
|
+
from ccxt.base.errors import InsufficientFunds
|
|
17
|
+
from ccxt.base.errors import InvalidOrder
|
|
18
|
+
from ccxt.base.errors import OrderNotFound
|
|
19
|
+
from ccxt.base.errors import RateLimitExceeded
|
|
20
|
+
from ccxt.base.errors import OnMaintenance
|
|
21
|
+
from ccxt.base.errors import InvalidNonce
|
|
22
|
+
from ccxt.base.decimal_to_precision import TICK_SIZE
|
|
23
|
+
from ccxt.base.precise import Precise
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class exmo(Exchange, ImplicitAPI):
|
|
27
|
+
|
|
28
|
+
def describe(self):
|
|
29
|
+
return self.deep_extend(super(exmo, self).describe(), {
|
|
30
|
+
'id': 'exmo',
|
|
31
|
+
'name': 'EXMO',
|
|
32
|
+
'countries': ['LT'], # Lithuania
|
|
33
|
+
'rateLimit': 350, # once every 350 ms ≈ 180 requests per minute ≈ 3 requests per second
|
|
34
|
+
'version': 'v1.1',
|
|
35
|
+
'has': {
|
|
36
|
+
'CORS': None,
|
|
37
|
+
'spot': True,
|
|
38
|
+
'margin': True,
|
|
39
|
+
'swap': False,
|
|
40
|
+
'future': False,
|
|
41
|
+
'option': False,
|
|
42
|
+
'addMargin': True,
|
|
43
|
+
'cancelOrder': True,
|
|
44
|
+
'cancelOrders': False,
|
|
45
|
+
'createDepositAddress': False,
|
|
46
|
+
'createOrder': True,
|
|
47
|
+
'createStopLimitOrder': True,
|
|
48
|
+
'createStopMarketOrder': True,
|
|
49
|
+
'createStopOrder': True,
|
|
50
|
+
'editOrder': True, # margin only
|
|
51
|
+
'fetchAccounts': False,
|
|
52
|
+
'fetchBalance': True,
|
|
53
|
+
'fetchCanceledOrders': True,
|
|
54
|
+
'fetchCurrencies': True,
|
|
55
|
+
'fetchDeposit': True,
|
|
56
|
+
'fetchDepositAddress': True,
|
|
57
|
+
'fetchDeposits': True,
|
|
58
|
+
'fetchDepositsWithdrawals': True,
|
|
59
|
+
'fetchDepositWithdrawFee': 'emulated',
|
|
60
|
+
'fetchDepositWithdrawFees': True,
|
|
61
|
+
'fetchFundingHistory': False,
|
|
62
|
+
'fetchFundingRate': False,
|
|
63
|
+
'fetchFundingRateHistory': False,
|
|
64
|
+
'fetchFundingRates': False,
|
|
65
|
+
'fetchIndexOHLCV': False,
|
|
66
|
+
'fetchMarginMode': False,
|
|
67
|
+
'fetchMarkets': True,
|
|
68
|
+
'fetchMarkOHLCV': False,
|
|
69
|
+
'fetchMyTrades': True,
|
|
70
|
+
'fetchOHLCV': True,
|
|
71
|
+
'fetchOpenInterestHistory': False,
|
|
72
|
+
'fetchOpenOrders': True,
|
|
73
|
+
'fetchOrder': 'emulated',
|
|
74
|
+
'fetchOrderBook': True,
|
|
75
|
+
'fetchOrderBooks': True,
|
|
76
|
+
'fetchOrderTrades': True,
|
|
77
|
+
'fetchPosition': False,
|
|
78
|
+
'fetchPositionHistory': False,
|
|
79
|
+
'fetchPositionMode': False,
|
|
80
|
+
'fetchPositions': False,
|
|
81
|
+
'fetchPositionsHistory': False,
|
|
82
|
+
'fetchPositionsRisk': False,
|
|
83
|
+
'fetchPremiumIndexOHLCV': False,
|
|
84
|
+
'fetchTicker': True,
|
|
85
|
+
'fetchTickers': True,
|
|
86
|
+
'fetchTrades': True,
|
|
87
|
+
'fetchTradingFee': False,
|
|
88
|
+
'fetchTradingFees': True,
|
|
89
|
+
'fetchTransactionFees': True,
|
|
90
|
+
'fetchTransactions': 'emulated',
|
|
91
|
+
'fetchTransfer': False,
|
|
92
|
+
'fetchTransfers': False,
|
|
93
|
+
'fetchWithdrawal': True,
|
|
94
|
+
'fetchWithdrawals': True,
|
|
95
|
+
'reduceMargin': True,
|
|
96
|
+
'setMargin': False,
|
|
97
|
+
'transfer': False,
|
|
98
|
+
'withdraw': True,
|
|
99
|
+
},
|
|
100
|
+
'timeframes': {
|
|
101
|
+
'1m': '1',
|
|
102
|
+
'5m': '5',
|
|
103
|
+
'15m': '15',
|
|
104
|
+
'30m': '30',
|
|
105
|
+
'45m': '45',
|
|
106
|
+
'1h': '60',
|
|
107
|
+
'2h': '120',
|
|
108
|
+
'3h': '180',
|
|
109
|
+
'4h': '240',
|
|
110
|
+
'1d': 'D',
|
|
111
|
+
'1w': 'W',
|
|
112
|
+
'1M': 'M',
|
|
113
|
+
},
|
|
114
|
+
'urls': {
|
|
115
|
+
'logo': 'https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg',
|
|
116
|
+
'api': {
|
|
117
|
+
'public': 'https://api.exmo.com',
|
|
118
|
+
'private': 'https://api.exmo.com',
|
|
119
|
+
'web': 'https://exmo.me',
|
|
120
|
+
},
|
|
121
|
+
'www': 'https://exmo.me',
|
|
122
|
+
'referral': 'https://exmo.me/?ref=131685',
|
|
123
|
+
'doc': [
|
|
124
|
+
'https://exmo.me/en/api_doc?ref=131685',
|
|
125
|
+
],
|
|
126
|
+
'fees': 'https://exmo.com/en/docs/fees',
|
|
127
|
+
},
|
|
128
|
+
'api': {
|
|
129
|
+
'web': {
|
|
130
|
+
'get': [
|
|
131
|
+
'ctrl/feesAndLimits',
|
|
132
|
+
'en/docs/fees',
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
'public': {
|
|
136
|
+
'get': [
|
|
137
|
+
'currency',
|
|
138
|
+
'currency/list/extended',
|
|
139
|
+
'order_book',
|
|
140
|
+
'pair_settings',
|
|
141
|
+
'ticker',
|
|
142
|
+
'trades',
|
|
143
|
+
'candles_history',
|
|
144
|
+
'required_amount',
|
|
145
|
+
'payments/providers/crypto/list',
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
'private': {
|
|
149
|
+
'post': [
|
|
150
|
+
'user_info',
|
|
151
|
+
'order_create',
|
|
152
|
+
'order_cancel',
|
|
153
|
+
'stop_market_order_create',
|
|
154
|
+
'stop_market_order_cancel',
|
|
155
|
+
'user_open_orders',
|
|
156
|
+
'user_trades',
|
|
157
|
+
'user_cancelled_orders',
|
|
158
|
+
'order_trades',
|
|
159
|
+
'deposit_address',
|
|
160
|
+
'withdraw_crypt',
|
|
161
|
+
'withdraw_get_txid',
|
|
162
|
+
'excode_create',
|
|
163
|
+
'excode_load',
|
|
164
|
+
'code_check',
|
|
165
|
+
'wallet_history',
|
|
166
|
+
'wallet_operations',
|
|
167
|
+
'margin/user/order/create',
|
|
168
|
+
'margin/user/order/update',
|
|
169
|
+
'margin/user/order/cancel',
|
|
170
|
+
'margin/user/position/close',
|
|
171
|
+
'margin/user/position/margin_add',
|
|
172
|
+
'margin/user/position/margin_remove',
|
|
173
|
+
'margin/currency/list',
|
|
174
|
+
'margin/pair/list',
|
|
175
|
+
'margin/settings',
|
|
176
|
+
'margin/funding/list',
|
|
177
|
+
'margin/user/info',
|
|
178
|
+
'margin/user/order/list',
|
|
179
|
+
'margin/user/order/history',
|
|
180
|
+
'margin/user/order/trades',
|
|
181
|
+
'margin/user/order/max_quantity',
|
|
182
|
+
'margin/user/position/list',
|
|
183
|
+
'margin/user/position/margin_remove_info',
|
|
184
|
+
'margin/user/position/margin_add_info',
|
|
185
|
+
'margin/user/wallet/list',
|
|
186
|
+
'margin/user/wallet/history',
|
|
187
|
+
'margin/user/trade/list',
|
|
188
|
+
'margin/trades',
|
|
189
|
+
'margin/liquidation/feed',
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
'fees': {
|
|
194
|
+
'trading': {
|
|
195
|
+
'feeSide': 'get',
|
|
196
|
+
'tierBased': True,
|
|
197
|
+
'percentage': True,
|
|
198
|
+
'maker': self.parse_number('0.004'),
|
|
199
|
+
'taker': self.parse_number('0.004'),
|
|
200
|
+
},
|
|
201
|
+
'transaction': {
|
|
202
|
+
'tierBased': False,
|
|
203
|
+
'percentage': False, # fixed transaction fees for crypto, see fetchDepositWithdrawFees below
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
'options': {
|
|
207
|
+
'networks': {
|
|
208
|
+
'ETH': 'ERC20',
|
|
209
|
+
'TRX': 'TRC20',
|
|
210
|
+
},
|
|
211
|
+
'fetchTradingFees': {
|
|
212
|
+
'method': 'fetchPrivateTradingFees', # or 'fetchPublicTradingFees'
|
|
213
|
+
},
|
|
214
|
+
'margin': {
|
|
215
|
+
'fillResponseFromRequest': True,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
'commonCurrencies': {
|
|
219
|
+
'GMT': 'GMT Token',
|
|
220
|
+
},
|
|
221
|
+
'precisionMode': TICK_SIZE,
|
|
222
|
+
'exceptions': {
|
|
223
|
+
'exact': {
|
|
224
|
+
'140434': BadRequest,
|
|
225
|
+
'40005': AuthenticationError, # Authorization error, incorrect signature
|
|
226
|
+
'40009': InvalidNonce, #
|
|
227
|
+
'40015': ExchangeError, # API function do not exist
|
|
228
|
+
'40016': OnMaintenance, # {"result":false,"error":"Error 40016: Maintenance work in progress"}
|
|
229
|
+
'40017': AuthenticationError, # Wrong API Key
|
|
230
|
+
'40032': PermissionDenied, # {"result":false,"error":"Error 40032: Access is denied for self API key"}
|
|
231
|
+
'40033': PermissionDenied, # {"result":false,"error":"Error 40033: Access is denied, self resources are temporarily blocked to user"}
|
|
232
|
+
'40034': RateLimitExceeded, # {"result":false,"error":"Error 40034: Access is denied, rate limit is exceeded"}
|
|
233
|
+
'50052': InsufficientFunds,
|
|
234
|
+
'50054': InsufficientFunds,
|
|
235
|
+
'50304': OrderNotFound, # "Order was not found '123456789'"(fetching order trades for an order that does not have trades yet)
|
|
236
|
+
'50173': OrderNotFound, # "Order with id X was not found."(cancelling non-existent, closed and cancelled order)
|
|
237
|
+
'50277': InvalidOrder,
|
|
238
|
+
'50319': InvalidOrder, # Price by order is less than permissible minimum for self pair
|
|
239
|
+
'50321': InvalidOrder, # Price by order is more than permissible maximum for self pair
|
|
240
|
+
'50381': InvalidOrder, # {"result":false,"error":"Error 50381: More than 2 decimal places are not permitted for pair BTC_USD"}
|
|
241
|
+
},
|
|
242
|
+
'broad': {
|
|
243
|
+
'range period is too long': BadRequest,
|
|
244
|
+
'invalid syntax': BadRequest,
|
|
245
|
+
'API rate limit exceeded': RateLimitExceeded, # {"result":false,"error":"API rate limit exceeded for x.x.x.x. Retry after 60 sec.","history":[],"begin":1579392000,"end":1579478400}
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
async def modify_margin_helper(self, symbol: str, amount, type, params={}):
|
|
251
|
+
await self.load_markets()
|
|
252
|
+
market = self.market(symbol)
|
|
253
|
+
request: dict = {
|
|
254
|
+
'position_id': market['id'],
|
|
255
|
+
'quantity': amount,
|
|
256
|
+
}
|
|
257
|
+
response = None
|
|
258
|
+
if type == 'add':
|
|
259
|
+
response = await self.privatePostMarginUserPositionMarginAdd(self.extend(request, params))
|
|
260
|
+
elif type == 'reduce':
|
|
261
|
+
response = await self.privatePostMarginUserPositionMarginRemove(self.extend(request, params))
|
|
262
|
+
#
|
|
263
|
+
# {}
|
|
264
|
+
#
|
|
265
|
+
margin = self.parse_margin_modification(response, market)
|
|
266
|
+
options = self.safe_value(self.options, 'margin', {})
|
|
267
|
+
fillResponseFromRequest = self.safe_bool(options, 'fillResponseFromRequest', True)
|
|
268
|
+
if fillResponseFromRequest:
|
|
269
|
+
margin['type'] = type
|
|
270
|
+
margin['amount'] = amount
|
|
271
|
+
return margin
|
|
272
|
+
|
|
273
|
+
def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
|
|
274
|
+
#
|
|
275
|
+
# {}
|
|
276
|
+
#
|
|
277
|
+
return {
|
|
278
|
+
'info': data,
|
|
279
|
+
'symbol': self.safe_symbol(None, market),
|
|
280
|
+
'type': None,
|
|
281
|
+
'marginMode': 'isolated',
|
|
282
|
+
'amount': None,
|
|
283
|
+
'total': None,
|
|
284
|
+
'code': self.safe_value(market, 'quote'),
|
|
285
|
+
'status': 'ok',
|
|
286
|
+
'timestamp': None,
|
|
287
|
+
'datetime': None,
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
|
|
291
|
+
"""
|
|
292
|
+
remove margin from a position
|
|
293
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#eebf9f25-0289-4946-9482-89872c738449
|
|
294
|
+
:param str symbol: unified market symbol
|
|
295
|
+
:param float amount: the amount of margin to remove
|
|
296
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
297
|
+
:returns dict: a `margin structure <https://docs.ccxt.com/#/?id=reduce-margin-structure>`
|
|
298
|
+
"""
|
|
299
|
+
return await self.modify_margin_helper(symbol, amount, 'reduce', params)
|
|
300
|
+
|
|
301
|
+
async def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
|
|
302
|
+
"""
|
|
303
|
+
add margin
|
|
304
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#143ef808-79ca-4e49-9e79-a60ea4d8c0e3
|
|
305
|
+
:param str symbol: unified market symbol
|
|
306
|
+
:param float amount: amount of margin to add
|
|
307
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
308
|
+
:returns dict: a `margin structure <https://docs.ccxt.com/#/?id=add-margin-structure>`
|
|
309
|
+
"""
|
|
310
|
+
return await self.modify_margin_helper(symbol, amount, 'add', params)
|
|
311
|
+
|
|
312
|
+
async def fetch_trading_fees(self, params={}) -> TradingFees:
|
|
313
|
+
"""
|
|
314
|
+
fetch the trading fees for multiple markets
|
|
315
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#90927062-256c-4b03-900f-2b99131f9a54
|
|
316
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#7de7e75c-5833-45a8-b937-c2276d235aaa
|
|
317
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
318
|
+
:returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
|
|
319
|
+
"""
|
|
320
|
+
options = self.safe_value(self.options, 'fetchTradingFees', {})
|
|
321
|
+
defaultMethod = self.safe_string(options, 'method', 'fetchPrivateTradingFees')
|
|
322
|
+
method = self.safe_string(params, 'method', defaultMethod)
|
|
323
|
+
params = self.omit(params, 'method')
|
|
324
|
+
if method == 'fetchPrivateTradingFees':
|
|
325
|
+
return await self.fetch_private_trading_fees(params)
|
|
326
|
+
else:
|
|
327
|
+
return await self.fetch_public_trading_fees(params)
|
|
328
|
+
|
|
329
|
+
async def fetch_private_trading_fees(self, params={}):
|
|
330
|
+
await self.load_markets()
|
|
331
|
+
response = await self.privatePostMarginPairList(params)
|
|
332
|
+
#
|
|
333
|
+
# {
|
|
334
|
+
# "pairs": [{
|
|
335
|
+
# "name": "EXM_USD",
|
|
336
|
+
# "buy_price": "0.02728391",
|
|
337
|
+
# "sell_price": "0.0276",
|
|
338
|
+
# "last_trade_price": "0.0276",
|
|
339
|
+
# "ticker_updated": "1646956050056696046",
|
|
340
|
+
# "is_fair_price": True,
|
|
341
|
+
# "max_price_precision": "8",
|
|
342
|
+
# "min_order_quantity": "1",
|
|
343
|
+
# "max_order_quantity": "50000",
|
|
344
|
+
# "min_order_price": "0.00000001",
|
|
345
|
+
# "max_order_price": "1000",
|
|
346
|
+
# "max_position_quantity": "50000",
|
|
347
|
+
# "trade_taker_fee": "0.05",
|
|
348
|
+
# "trade_maker_fee": "0",
|
|
349
|
+
# "liquidation_fee": "0.5",
|
|
350
|
+
# "max_leverage": "3",
|
|
351
|
+
# "default_leverage": "3",
|
|
352
|
+
# "liquidation_level": "5",
|
|
353
|
+
# "margin_call_level": "7.5",
|
|
354
|
+
# "position": "1",
|
|
355
|
+
# "updated": "1638976144797807397"
|
|
356
|
+
# }
|
|
357
|
+
# ...
|
|
358
|
+
# ]
|
|
359
|
+
# }
|
|
360
|
+
#
|
|
361
|
+
pairs = self.safe_value(response, 'pairs', [])
|
|
362
|
+
result: dict = {}
|
|
363
|
+
for i in range(0, len(pairs)):
|
|
364
|
+
pair = pairs[i]
|
|
365
|
+
marketId = self.safe_string(pair, 'name')
|
|
366
|
+
symbol = self.safe_symbol(marketId, None, '_')
|
|
367
|
+
makerString = self.safe_string(pair, 'trade_maker_fee')
|
|
368
|
+
takerString = self.safe_string(pair, 'trade_taker_fee')
|
|
369
|
+
maker = self.parse_number(Precise.string_div(makerString, '100'))
|
|
370
|
+
taker = self.parse_number(Precise.string_div(takerString, '100'))
|
|
371
|
+
result[symbol] = {
|
|
372
|
+
'info': pair,
|
|
373
|
+
'symbol': symbol,
|
|
374
|
+
'maker': maker,
|
|
375
|
+
'taker': taker,
|
|
376
|
+
'percentage': True,
|
|
377
|
+
'tierBased': True,
|
|
378
|
+
}
|
|
379
|
+
return result
|
|
380
|
+
|
|
381
|
+
async def fetch_public_trading_fees(self, params={}):
|
|
382
|
+
await self.load_markets()
|
|
383
|
+
response = await self.publicGetPairSettings(params)
|
|
384
|
+
#
|
|
385
|
+
# {
|
|
386
|
+
# "BTC_USD": {
|
|
387
|
+
# "min_quantity": "0.00002",
|
|
388
|
+
# "max_quantity": "1000",
|
|
389
|
+
# "min_price": "1",
|
|
390
|
+
# "max_price": "150000",
|
|
391
|
+
# "max_amount": "500000",
|
|
392
|
+
# "min_amount": "1",
|
|
393
|
+
# "price_precision": "2",
|
|
394
|
+
# "commission_taker_percent": "0.3",
|
|
395
|
+
# "commission_maker_percent": "0.3"
|
|
396
|
+
# },
|
|
397
|
+
# }
|
|
398
|
+
#
|
|
399
|
+
result: dict = {}
|
|
400
|
+
for i in range(0, len(self.symbols)):
|
|
401
|
+
symbol = self.symbols[i]
|
|
402
|
+
market = self.market(symbol)
|
|
403
|
+
fee = self.safe_value(response, market['id'], {})
|
|
404
|
+
makerString = self.safe_string(fee, 'commission_maker_percent')
|
|
405
|
+
takerString = self.safe_string(fee, 'commission_taker_percent')
|
|
406
|
+
maker = self.parse_number(Precise.string_div(makerString, '100'))
|
|
407
|
+
taker = self.parse_number(Precise.string_div(takerString, '100'))
|
|
408
|
+
result[symbol] = {
|
|
409
|
+
'info': fee,
|
|
410
|
+
'symbol': symbol,
|
|
411
|
+
'maker': maker,
|
|
412
|
+
'taker': taker,
|
|
413
|
+
'percentage': True,
|
|
414
|
+
'tierBased': True,
|
|
415
|
+
}
|
|
416
|
+
return result
|
|
417
|
+
|
|
418
|
+
def parse_fixed_float_value(self, input):
|
|
419
|
+
if (input is None) or (input == '-'):
|
|
420
|
+
return None
|
|
421
|
+
if input == '':
|
|
422
|
+
return 0
|
|
423
|
+
isPercentage = (input.find('%') >= 0)
|
|
424
|
+
parts = input.split(' ')
|
|
425
|
+
value = parts[0].replace('%', '')
|
|
426
|
+
result = float(value)
|
|
427
|
+
if (result > 0) and isPercentage:
|
|
428
|
+
raise ExchangeError(self.id + ' parseFixedFloatValue() detected an unsupported non-zero percentage-based fee ' + input)
|
|
429
|
+
return result
|
|
430
|
+
|
|
431
|
+
async def fetch_transaction_fees(self, codes: Strings = None, params={}):
|
|
432
|
+
"""
|
|
433
|
+
* @deprecated
|
|
434
|
+
please use fetchDepositWithdrawFees instead
|
|
435
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#4190035d-24b1-453d-833b-37e0a52f88e2
|
|
436
|
+
:param str[]|None codes: list of unified currency codes
|
|
437
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
438
|
+
:returns dict: a list of `transaction fees structures <https://docs.ccxt.com/#/?id=fees-structure>`
|
|
439
|
+
"""
|
|
440
|
+
await self.load_markets()
|
|
441
|
+
cryptoList = await self.publicGetPaymentsProvidersCryptoList(params)
|
|
442
|
+
#
|
|
443
|
+
# {
|
|
444
|
+
# "BTC":[
|
|
445
|
+
# {"type":"deposit", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.001 BTC. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1},
|
|
446
|
+
# {"type":"withdraw", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"350", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"0.0005 BTC", "currency_confirmations":6}
|
|
447
|
+
# ],
|
|
448
|
+
# "ETH":[
|
|
449
|
+
# {"type":"withdraw", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"500", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"0.004 ETH", "currency_confirmations":4},
|
|
450
|
+
# {"type":"deposit", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.01 ETH. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1}
|
|
451
|
+
# ],
|
|
452
|
+
# "USDT":[
|
|
453
|
+
# {"type":"deposit", "name":"USDT(OMNI)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":false,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2},
|
|
454
|
+
# {"type":"withdraw", "name":"USDT(OMNI)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":false,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"5 USDT", "currency_confirmations":6},
|
|
455
|
+
# {"type":"deposit", "name":"USDT(ERC20)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2},
|
|
456
|
+
# {
|
|
457
|
+
# "type":"withdraw",
|
|
458
|
+
# "name":"USDT(ERC20)",
|
|
459
|
+
# "currency_name":"USDT",
|
|
460
|
+
# "min":"55",
|
|
461
|
+
# "max":"200000",
|
|
462
|
+
# "enabled":true,
|
|
463
|
+
# "comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales. Recommendation: Due to the high load of ERC20 network, using TRC20 address for withdrawal is recommended.",
|
|
464
|
+
# "commission_desc":"10 USDT",
|
|
465
|
+
# "currency_confirmations":6
|
|
466
|
+
# },
|
|
467
|
+
# {"type":"deposit", "name":"USDT(TRC20)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":true,"comment":"Minimum deposit amount is 10 USDT. Only TRON main network supported", "commission_desc":"0%", "currency_confirmations":2},
|
|
468
|
+
# {"type":"withdraw", "name":"USDT(TRC20)", "currency_name":"USDT", "min":"10", "max":"150000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales. Only TRON main network supported.", "commission_desc":"1 USDT", "currency_confirmations":6}
|
|
469
|
+
# ],
|
|
470
|
+
# "XLM":[
|
|
471
|
+
# {"type":"deposit", "name":"XLM", "currency_name":"XLM", "min":"1", "max":"1000000", "enabled":true,"comment":"Attention! A deposit without memo(invoice) will not be credited. Minimum deposit amount is 1 XLM. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1},
|
|
472
|
+
# {"type":"withdraw", "name":"XLM", "currency_name":"XLM", "min":"21", "max":"1000000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales.", "commission_desc":"0.01 XLM", "currency_confirmations":1}
|
|
473
|
+
# ],
|
|
474
|
+
# }
|
|
475
|
+
#
|
|
476
|
+
result: dict = {}
|
|
477
|
+
cryptoListKeys = list(cryptoList.keys())
|
|
478
|
+
for i in range(0, len(cryptoListKeys)):
|
|
479
|
+
code = cryptoListKeys[i]
|
|
480
|
+
if codes is not None and not self.in_array(code, codes):
|
|
481
|
+
continue
|
|
482
|
+
result[code] = {
|
|
483
|
+
'deposit': None,
|
|
484
|
+
'withdraw': None,
|
|
485
|
+
}
|
|
486
|
+
currency = self.currency(code)
|
|
487
|
+
currencyId = self.safe_string(currency, 'id')
|
|
488
|
+
providers = self.safe_value(cryptoList, currencyId, [])
|
|
489
|
+
for j in range(0, len(providers)):
|
|
490
|
+
provider = providers[j]
|
|
491
|
+
typeInner = self.safe_string(provider, 'type')
|
|
492
|
+
commissionDesc = self.safe_string(provider, 'commission_desc')
|
|
493
|
+
fee = self.parse_fixed_float_value(commissionDesc)
|
|
494
|
+
result[code][typeInner] = fee
|
|
495
|
+
result[code]['info'] = providers
|
|
496
|
+
# cache them for later use
|
|
497
|
+
self.options['transactionFees'] = result
|
|
498
|
+
return result
|
|
499
|
+
|
|
500
|
+
async def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
|
|
501
|
+
"""
|
|
502
|
+
fetch deposit and withdraw fees
|
|
503
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#4190035d-24b1-453d-833b-37e0a52f88e2
|
|
504
|
+
:param str[]|None codes: list of unified currency codes
|
|
505
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
506
|
+
:returns dict: a list of `transaction fees structures <https://docs.ccxt.com/#/?id=fees-structure>`
|
|
507
|
+
"""
|
|
508
|
+
await self.load_markets()
|
|
509
|
+
response = await self.publicGetPaymentsProvidersCryptoList(params)
|
|
510
|
+
#
|
|
511
|
+
# {
|
|
512
|
+
# "USDT": [
|
|
513
|
+
# {
|
|
514
|
+
# "type": "deposit", # or "withdraw"
|
|
515
|
+
# "name": "USDT(ERC20)",
|
|
516
|
+
# "currency_name": "USDT",
|
|
517
|
+
# "min": "10",
|
|
518
|
+
# "max": "0",
|
|
519
|
+
# "enabled": True,
|
|
520
|
+
# "comment": "Minimum deposit amount is 10 USDT",
|
|
521
|
+
# "commission_desc": "0%",
|
|
522
|
+
# "currency_confirmations": 2
|
|
523
|
+
# },
|
|
524
|
+
# ...
|
|
525
|
+
# ],
|
|
526
|
+
# ...
|
|
527
|
+
# }
|
|
528
|
+
#
|
|
529
|
+
result = self.parse_deposit_withdraw_fees(response, codes)
|
|
530
|
+
# cache them for later use
|
|
531
|
+
self.options['transactionFees'] = result
|
|
532
|
+
return result
|
|
533
|
+
|
|
534
|
+
def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
|
|
535
|
+
#
|
|
536
|
+
# [
|
|
537
|
+
# {
|
|
538
|
+
# "type": "deposit", # or "withdraw"
|
|
539
|
+
# "name": "BTC",
|
|
540
|
+
# "currency_name": "BTC",
|
|
541
|
+
# "min": "0.001",
|
|
542
|
+
# "max": "0",
|
|
543
|
+
# "enabled": True,
|
|
544
|
+
# "comment": "Minimum deposit amount is 0.001 BTC. We do not support BSC and BEP20 network, please consider self when sending funds",
|
|
545
|
+
# "commission_desc": "0%",
|
|
546
|
+
# "currency_confirmations": 1
|
|
547
|
+
# },
|
|
548
|
+
# ...
|
|
549
|
+
# ]
|
|
550
|
+
#
|
|
551
|
+
result = self.deposit_withdraw_fee(fee)
|
|
552
|
+
for i in range(0, len(fee)):
|
|
553
|
+
provider = fee[i]
|
|
554
|
+
type = self.safe_string(provider, 'type')
|
|
555
|
+
networkId = self.safe_string(provider, 'name')
|
|
556
|
+
networkCode = self.network_id_to_code(networkId, self.safe_string(currency, 'code'))
|
|
557
|
+
commissionDesc = self.safe_string(provider, 'commission_desc')
|
|
558
|
+
splitCommissionDesc = []
|
|
559
|
+
percentage = None
|
|
560
|
+
if commissionDesc is not None:
|
|
561
|
+
splitCommissionDesc = commissionDesc.split('%')
|
|
562
|
+
splitCommissionDescLength = len(splitCommissionDesc)
|
|
563
|
+
percentage = splitCommissionDescLength >= 2
|
|
564
|
+
network = self.safe_value(result['networks'], networkCode)
|
|
565
|
+
if network is None:
|
|
566
|
+
result['networks'][networkCode] = {
|
|
567
|
+
'withdraw': {
|
|
568
|
+
'fee': None,
|
|
569
|
+
'percentage': None,
|
|
570
|
+
},
|
|
571
|
+
'deposit': {
|
|
572
|
+
'fee': None,
|
|
573
|
+
'percentage': None,
|
|
574
|
+
},
|
|
575
|
+
}
|
|
576
|
+
result['networks'][networkCode][type] = {
|
|
577
|
+
'fee': self.parse_fixed_float_value(self.safe_string(splitCommissionDesc, 0)),
|
|
578
|
+
'percentage': percentage,
|
|
579
|
+
}
|
|
580
|
+
return self.assign_default_deposit_withdraw_fees(result)
|
|
581
|
+
|
|
582
|
+
async def fetch_currencies(self, params={}) -> Currencies:
|
|
583
|
+
"""
|
|
584
|
+
fetches all available currencies on an exchange
|
|
585
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#7cdf0ca8-9ff6-4cf3-aa33-bcec83155c49
|
|
586
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#4190035d-24b1-453d-833b-37e0a52f88e2
|
|
587
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
588
|
+
:returns dict: an associative dictionary of currencies
|
|
589
|
+
"""
|
|
590
|
+
#
|
|
591
|
+
currencyList = await self.publicGetCurrencyListExtended(params)
|
|
592
|
+
#
|
|
593
|
+
# [
|
|
594
|
+
# {"name":"VLX","description":"Velas"},
|
|
595
|
+
# {"name":"RUB","description":"Russian Ruble"},
|
|
596
|
+
# {"name":"BTC","description":"Bitcoin"},
|
|
597
|
+
# {"name":"USD","description":"US Dollar"}
|
|
598
|
+
# ]
|
|
599
|
+
#
|
|
600
|
+
cryptoList = await self.publicGetPaymentsProvidersCryptoList(params)
|
|
601
|
+
#
|
|
602
|
+
# {
|
|
603
|
+
# "BTC":[
|
|
604
|
+
# {"type":"deposit", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.001 BTC. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1},
|
|
605
|
+
# {"type":"withdraw", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"350", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"0.0005 BTC", "currency_confirmations":6}
|
|
606
|
+
# ],
|
|
607
|
+
# "ETH":[
|
|
608
|
+
# {"type":"withdraw", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"500", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"0.004 ETH", "currency_confirmations":4},
|
|
609
|
+
# {"type":"deposit", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.01 ETH. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1}
|
|
610
|
+
# ],
|
|
611
|
+
# "USDT":[
|
|
612
|
+
# {"type":"deposit", "name":"USDT(OMNI)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":false,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2},
|
|
613
|
+
# {"type":"withdraw", "name":"USDT(OMNI)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":false,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"5 USDT", "currency_confirmations":6},
|
|
614
|
+
# {"type":"deposit", "name":"USDT(ERC20)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2},
|
|
615
|
+
# {"type":"withdraw", "name":"USDT(ERC20)", "currency_name":"USDT", "min":"55", "max":"200000", "enabled":true, "comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales. Recommendation: Due to the high load of ERC20 network, using TRC20 address for withdrawal is recommended.", "commission_desc":"10 USDT", "currency_confirmations":6},
|
|
616
|
+
# {"type":"deposit", "name":"USDT(TRC20)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":true,"comment":"Minimum deposit amount is 10 USDT. Only TRON main network supported", "commission_desc":"0%", "currency_confirmations":2},
|
|
617
|
+
# {"type":"withdraw", "name":"USDT(TRC20)", "currency_name":"USDT", "min":"10", "max":"150000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales. Only TRON main network supported.", "commission_desc":"1 USDT", "currency_confirmations":6}
|
|
618
|
+
# ],
|
|
619
|
+
# "XLM":[
|
|
620
|
+
# {"type":"deposit", "name":"XLM", "currency_name":"XLM", "min":"1", "max":"1000000", "enabled":true,"comment":"Attention! A deposit without memo(invoice) will not be credited. Minimum deposit amount is 1 XLM. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1},
|
|
621
|
+
# {"type":"withdraw", "name":"XLM", "currency_name":"XLM", "min":"21", "max":"1000000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales.", "commission_desc":"0.01 XLM", "currency_confirmations":1}
|
|
622
|
+
# ],
|
|
623
|
+
# }
|
|
624
|
+
#
|
|
625
|
+
result: dict = {}
|
|
626
|
+
for i in range(0, len(currencyList)):
|
|
627
|
+
currency = currencyList[i]
|
|
628
|
+
currencyId = self.safe_string(currency, 'name')
|
|
629
|
+
name = self.safe_string(currency, 'description')
|
|
630
|
+
providers = self.safe_value(cryptoList, currencyId)
|
|
631
|
+
active = False
|
|
632
|
+
type = 'crypto'
|
|
633
|
+
limits: dict = {
|
|
634
|
+
'deposit': {
|
|
635
|
+
'min': None,
|
|
636
|
+
'max': None,
|
|
637
|
+
},
|
|
638
|
+
'withdraw': {
|
|
639
|
+
'min': None,
|
|
640
|
+
'max': None,
|
|
641
|
+
},
|
|
642
|
+
}
|
|
643
|
+
fee = None
|
|
644
|
+
depositEnabled = None
|
|
645
|
+
withdrawEnabled = None
|
|
646
|
+
if providers is None:
|
|
647
|
+
active = True
|
|
648
|
+
type = 'fiat'
|
|
649
|
+
else:
|
|
650
|
+
for j in range(0, len(providers)):
|
|
651
|
+
provider = providers[j]
|
|
652
|
+
typeInner = self.safe_string(provider, 'type')
|
|
653
|
+
minValue = self.safe_string(provider, 'min')
|
|
654
|
+
maxValue = self.safe_string(provider, 'max')
|
|
655
|
+
if Precise.string_eq(maxValue, '0.0'):
|
|
656
|
+
maxValue = None
|
|
657
|
+
activeProvider = self.safe_value(provider, 'enabled')
|
|
658
|
+
if typeInner == 'deposit':
|
|
659
|
+
if activeProvider and not depositEnabled:
|
|
660
|
+
depositEnabled = True
|
|
661
|
+
elif not activeProvider:
|
|
662
|
+
depositEnabled = False
|
|
663
|
+
elif typeInner == 'withdraw':
|
|
664
|
+
if activeProvider and not withdrawEnabled:
|
|
665
|
+
withdrawEnabled = True
|
|
666
|
+
elif not activeProvider:
|
|
667
|
+
withdrawEnabled = False
|
|
668
|
+
if activeProvider:
|
|
669
|
+
active = True
|
|
670
|
+
limitMin = self.number_to_string(limits[typeInner]['min'])
|
|
671
|
+
if (limits[typeInner]['min'] is None) or (Precise.string_lt(minValue, limitMin)):
|
|
672
|
+
limits[typeInner]['min'] = minValue
|
|
673
|
+
limits[typeInner]['max'] = maxValue
|
|
674
|
+
if typeInner == 'withdraw':
|
|
675
|
+
commissionDesc = self.safe_string(provider, 'commission_desc')
|
|
676
|
+
fee = self.parse_fixed_float_value(commissionDesc)
|
|
677
|
+
code = self.safe_currency_code(currencyId)
|
|
678
|
+
result[code] = {
|
|
679
|
+
'id': currencyId,
|
|
680
|
+
'code': code,
|
|
681
|
+
'name': name,
|
|
682
|
+
'type': type,
|
|
683
|
+
'active': active,
|
|
684
|
+
'deposit': depositEnabled,
|
|
685
|
+
'withdraw': withdrawEnabled,
|
|
686
|
+
'fee': fee,
|
|
687
|
+
'precision': self.parse_number('1e-8'),
|
|
688
|
+
'limits': limits,
|
|
689
|
+
'info': providers,
|
|
690
|
+
'networks': {},
|
|
691
|
+
}
|
|
692
|
+
return result
|
|
693
|
+
|
|
694
|
+
async def fetch_markets(self, params={}) -> List[Market]:
|
|
695
|
+
"""
|
|
696
|
+
retrieves data on all markets for exmo
|
|
697
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#7de7e75c-5833-45a8-b937-c2276d235aaa
|
|
698
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
699
|
+
:returns dict[]: an array of objects representing market data
|
|
700
|
+
"""
|
|
701
|
+
response = await self.publicGetPairSettings(params)
|
|
702
|
+
#
|
|
703
|
+
# {
|
|
704
|
+
# "BTC_USD":{
|
|
705
|
+
# "min_quantity":"0.0001",
|
|
706
|
+
# "max_quantity":"1000",
|
|
707
|
+
# "min_price":"1",
|
|
708
|
+
# "max_price":"30000",
|
|
709
|
+
# "max_amount":"500000",
|
|
710
|
+
# "min_amount":"1",
|
|
711
|
+
# "price_precision":8,
|
|
712
|
+
# "commission_taker_percent":"0.4",
|
|
713
|
+
# "commission_maker_percent":"0.4"
|
|
714
|
+
# },
|
|
715
|
+
# }
|
|
716
|
+
#
|
|
717
|
+
marginPairsDict: dict = {}
|
|
718
|
+
if self.check_required_credentials(False):
|
|
719
|
+
marginPairs = await self.privatePostMarginPairList(params)
|
|
720
|
+
#
|
|
721
|
+
# {
|
|
722
|
+
# "pairs": [
|
|
723
|
+
# {
|
|
724
|
+
# "buy_price": "55978.85",
|
|
725
|
+
# "default_leverage": "3",
|
|
726
|
+
# "is_fair_price": True,
|
|
727
|
+
# "last_trade_price": "55999.23",
|
|
728
|
+
# "liquidation_fee": "2",
|
|
729
|
+
# "liquidation_level": "10",
|
|
730
|
+
# "margin_call_level": "15",
|
|
731
|
+
# "max_leverage": "3",
|
|
732
|
+
# "max_order_price": "150000",
|
|
733
|
+
# "max_order_quantity": "1",
|
|
734
|
+
# "max_position_quantity": "1",
|
|
735
|
+
# "max_price_precision": 2,
|
|
736
|
+
# "min_order_price": "1",
|
|
737
|
+
# "min_order_quantity": "0.00002",
|
|
738
|
+
# "name": "BTC_USD",
|
|
739
|
+
# "position": 1,
|
|
740
|
+
# "sell_price": "55985.51",
|
|
741
|
+
# "ticker_updated": "1619019818936107989",
|
|
742
|
+
# "trade_maker_fee": "0",
|
|
743
|
+
# "trade_taker_fee": "0.05",
|
|
744
|
+
# "updated": "1619008608955599013"
|
|
745
|
+
# }
|
|
746
|
+
# ]
|
|
747
|
+
# }
|
|
748
|
+
#
|
|
749
|
+
pairs = self.safe_value(marginPairs, 'pairs')
|
|
750
|
+
marginPairsDict = self.index_by(pairs, 'name')
|
|
751
|
+
keys = list(response.keys())
|
|
752
|
+
result = []
|
|
753
|
+
for i in range(0, len(keys)):
|
|
754
|
+
id = keys[i]
|
|
755
|
+
market = response[id]
|
|
756
|
+
marginMarket = self.safe_value(marginPairsDict, id)
|
|
757
|
+
symbol = id.replace('_', '/')
|
|
758
|
+
baseId, quoteId = symbol.split('/')
|
|
759
|
+
base = self.safe_currency_code(baseId)
|
|
760
|
+
quote = self.safe_currency_code(quoteId)
|
|
761
|
+
takerString = self.safe_string(market, 'commission_taker_percent')
|
|
762
|
+
makerString = self.safe_string(market, 'commission_maker_percent')
|
|
763
|
+
maxQuantity = self.safe_string(market, 'max_quantity')
|
|
764
|
+
marginMaxQuantity = self.safe_string(marginMarket, 'max_order_quantity')
|
|
765
|
+
result.append({
|
|
766
|
+
'id': id,
|
|
767
|
+
'symbol': symbol,
|
|
768
|
+
'base': base,
|
|
769
|
+
'quote': quote,
|
|
770
|
+
'settle': None,
|
|
771
|
+
'baseId': baseId,
|
|
772
|
+
'quoteId': quoteId,
|
|
773
|
+
'settleId': None,
|
|
774
|
+
'type': 'spot',
|
|
775
|
+
'spot': True,
|
|
776
|
+
'margin': marginMarket is not None,
|
|
777
|
+
'swap': False,
|
|
778
|
+
'future': False,
|
|
779
|
+
'option': False,
|
|
780
|
+
'active': None,
|
|
781
|
+
'contract': False,
|
|
782
|
+
'linear': None,
|
|
783
|
+
'inverse': None,
|
|
784
|
+
'taker': self.parse_number(Precise.string_div(takerString, '100')),
|
|
785
|
+
'maker': self.parse_number(Precise.string_div(makerString, '100')),
|
|
786
|
+
'contractSize': None,
|
|
787
|
+
'expiry': None,
|
|
788
|
+
'expiryDatetime': None,
|
|
789
|
+
'strike': None,
|
|
790
|
+
'optionType': None,
|
|
791
|
+
'precision': {
|
|
792
|
+
'amount': self.parse_number('1e-8'),
|
|
793
|
+
'price': self.parse_number(self.parse_precision(self.safe_string(market, 'price_precision'))),
|
|
794
|
+
},
|
|
795
|
+
'limits': {
|
|
796
|
+
'leverage': {
|
|
797
|
+
'min': None,
|
|
798
|
+
'max': self.safe_number(market, 'leverage'),
|
|
799
|
+
},
|
|
800
|
+
'amount': {
|
|
801
|
+
'min': self.safe_number(market, 'min_quantity'),
|
|
802
|
+
'max': self.parse_number(Precise.string_max(maxQuantity, marginMaxQuantity)),
|
|
803
|
+
},
|
|
804
|
+
'price': {
|
|
805
|
+
'min': self.safe_number(market, 'min_price'),
|
|
806
|
+
'max': self.safe_number(market, 'max_price'),
|
|
807
|
+
},
|
|
808
|
+
'cost': {
|
|
809
|
+
'min': self.safe_number(market, 'min_amount'),
|
|
810
|
+
'max': self.safe_number(market, 'max_amount'),
|
|
811
|
+
},
|
|
812
|
+
},
|
|
813
|
+
'created': None,
|
|
814
|
+
'info': market,
|
|
815
|
+
})
|
|
816
|
+
return result
|
|
817
|
+
|
|
818
|
+
async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
|
819
|
+
"""
|
|
820
|
+
fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
821
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#65eeb949-74e5-4631-9184-c38387fe53e8
|
|
822
|
+
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
|
823
|
+
:param str timeframe: the length of time each candle represents
|
|
824
|
+
:param int [since]: timestamp in ms of the earliest candle to fetch
|
|
825
|
+
:param int [limit]: the maximum amount of candles to fetch
|
|
826
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
827
|
+
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
|
828
|
+
"""
|
|
829
|
+
await self.load_markets()
|
|
830
|
+
market = self.market(symbol)
|
|
831
|
+
request: dict = {
|
|
832
|
+
'symbol': market['id'],
|
|
833
|
+
'resolution': self.safe_string(self.timeframes, timeframe, timeframe),
|
|
834
|
+
}
|
|
835
|
+
maxLimit = 3000
|
|
836
|
+
duration = self.parse_timeframe(timeframe)
|
|
837
|
+
now = self.milliseconds()
|
|
838
|
+
if since is None:
|
|
839
|
+
if limit is None:
|
|
840
|
+
limit = 1000 # cap default at generous amount
|
|
841
|
+
else:
|
|
842
|
+
limit = min(limit, maxLimit)
|
|
843
|
+
request['from'] = self.parse_to_int(now / 1000) - limit * duration - 1
|
|
844
|
+
request['to'] = self.parse_to_int(now / 1000)
|
|
845
|
+
else:
|
|
846
|
+
request['from'] = self.parse_to_int(since / 1000) - 1
|
|
847
|
+
if limit is None:
|
|
848
|
+
limit = maxLimit
|
|
849
|
+
else:
|
|
850
|
+
limit = min(limit, maxLimit)
|
|
851
|
+
to = self.sum(since, limit * duration * 1000)
|
|
852
|
+
request['to'] = self.parse_to_int(to / 1000)
|
|
853
|
+
response = await self.publicGetCandlesHistory(self.extend(request, params))
|
|
854
|
+
#
|
|
855
|
+
# {
|
|
856
|
+
# "candles":[
|
|
857
|
+
# {"t":1584057600000,"o":0.02235144,"c":0.02400233,"h":0.025171,"l":0.02221,"v":5988.34031761},
|
|
858
|
+
# {"t":1584144000000,"o":0.0240373,"c":0.02367413,"h":0.024399,"l":0.0235,"v":2027.82522329},
|
|
859
|
+
# {"t":1584230400000,"o":0.02363458,"c":0.02319242,"h":0.0237948,"l":0.02223196,"v":1707.96944997},
|
|
860
|
+
# ]
|
|
861
|
+
# }
|
|
862
|
+
#
|
|
863
|
+
candles = self.safe_list(response, 'candles', [])
|
|
864
|
+
return self.parse_ohlcvs(candles, market, timeframe, since, limit)
|
|
865
|
+
|
|
866
|
+
def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
|
|
867
|
+
#
|
|
868
|
+
# {
|
|
869
|
+
# "t":1584057600000,
|
|
870
|
+
# "o":0.02235144,
|
|
871
|
+
# "c":0.02400233,
|
|
872
|
+
# "h":0.025171,
|
|
873
|
+
# "l":0.02221,
|
|
874
|
+
# "v":5988.34031761
|
|
875
|
+
# }
|
|
876
|
+
#
|
|
877
|
+
return [
|
|
878
|
+
self.safe_integer(ohlcv, 't'),
|
|
879
|
+
self.safe_number(ohlcv, 'o'),
|
|
880
|
+
self.safe_number(ohlcv, 'h'),
|
|
881
|
+
self.safe_number(ohlcv, 'l'),
|
|
882
|
+
self.safe_number(ohlcv, 'c'),
|
|
883
|
+
self.safe_number(ohlcv, 'v'),
|
|
884
|
+
]
|
|
885
|
+
|
|
886
|
+
def parse_balance(self, response) -> Balances:
|
|
887
|
+
result: dict = {'info': response}
|
|
888
|
+
wallets = self.safe_value(response, 'wallets')
|
|
889
|
+
if wallets is not None:
|
|
890
|
+
currencyIds = list(wallets.keys())
|
|
891
|
+
for i in range(0, len(currencyIds)):
|
|
892
|
+
currencyId = currencyIds[i]
|
|
893
|
+
item = wallets[currencyId]
|
|
894
|
+
currency = self.safe_currency_code(currencyId)
|
|
895
|
+
account = self.account()
|
|
896
|
+
account['used'] = self.safe_string(item, 'used')
|
|
897
|
+
account['free'] = self.safe_string(item, 'free')
|
|
898
|
+
account['total'] = self.safe_string(item, 'balance')
|
|
899
|
+
result[currency] = account
|
|
900
|
+
else:
|
|
901
|
+
free = self.safe_value(response, 'balances', {})
|
|
902
|
+
used = self.safe_value(response, 'reserved', {})
|
|
903
|
+
currencyIds = list(free.keys())
|
|
904
|
+
for i in range(0, len(currencyIds)):
|
|
905
|
+
currencyId = currencyIds[i]
|
|
906
|
+
code = self.safe_currency_code(currencyId)
|
|
907
|
+
account = self.account()
|
|
908
|
+
if currencyId in free:
|
|
909
|
+
account['free'] = self.safe_string(free, currencyId)
|
|
910
|
+
if currencyId in used:
|
|
911
|
+
account['used'] = self.safe_string(used, currencyId)
|
|
912
|
+
result[code] = account
|
|
913
|
+
return self.safe_balance(result)
|
|
914
|
+
|
|
915
|
+
async def fetch_balance(self, params={}) -> Balances:
|
|
916
|
+
"""
|
|
917
|
+
query for balance and get the amount of funds available for trading or funds locked in orders
|
|
918
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#59c5160f-27a1-4d9a-8cfb-7979c7ffaac6
|
|
919
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#c8388df7-1f9f-4d41-81c4-5a387d171dc6
|
|
920
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
921
|
+
:param str [params.marginMode]: *isolated* fetches the isolated margin balance
|
|
922
|
+
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
|
|
923
|
+
"""
|
|
924
|
+
await self.load_markets()
|
|
925
|
+
marginMode = None
|
|
926
|
+
marginMode, params = self.handle_margin_mode_and_params('fetchBalance', params)
|
|
927
|
+
if marginMode == 'cross':
|
|
928
|
+
raise BadRequest(self.id + ' does not support cross margin')
|
|
929
|
+
response = None
|
|
930
|
+
if marginMode == 'isolated':
|
|
931
|
+
response = await self.privatePostMarginUserWalletList(params)
|
|
932
|
+
#
|
|
933
|
+
# {
|
|
934
|
+
# "wallets": {
|
|
935
|
+
# "USD": {
|
|
936
|
+
# "balance": "1000",
|
|
937
|
+
# "free": "600",
|
|
938
|
+
# "used": "400"
|
|
939
|
+
# }
|
|
940
|
+
# }
|
|
941
|
+
# }
|
|
942
|
+
#
|
|
943
|
+
else:
|
|
944
|
+
response = await self.privatePostUserInfo(params)
|
|
945
|
+
#
|
|
946
|
+
# {
|
|
947
|
+
# "uid":131685,
|
|
948
|
+
# "server_date":1628999600,
|
|
949
|
+
# "balances":{
|
|
950
|
+
# "EXM":"0",
|
|
951
|
+
# "USD":"0",
|
|
952
|
+
# "EUR":"0",
|
|
953
|
+
# "GBP":"0",
|
|
954
|
+
# },
|
|
955
|
+
# }
|
|
956
|
+
#
|
|
957
|
+
return self.parse_balance(response)
|
|
958
|
+
|
|
959
|
+
async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
|
960
|
+
"""
|
|
961
|
+
fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
|
962
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#c60c51a8-e683-4f45-a000-820723d37871
|
|
963
|
+
:param str symbol: unified symbol of the market to fetch the order book for
|
|
964
|
+
:param int [limit]: the maximum amount of order book entries to return
|
|
965
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
966
|
+
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
|
967
|
+
"""
|
|
968
|
+
await self.load_markets()
|
|
969
|
+
market = self.market(symbol)
|
|
970
|
+
request: dict = {
|
|
971
|
+
'pair': market['id'],
|
|
972
|
+
}
|
|
973
|
+
if limit is not None:
|
|
974
|
+
request['limit'] = limit
|
|
975
|
+
response = await self.publicGetOrderBook(self.extend(request, params))
|
|
976
|
+
result = self.safe_dict(response, market['id'])
|
|
977
|
+
return self.parse_order_book(result, market['symbol'], None, 'bid', 'ask')
|
|
978
|
+
|
|
979
|
+
async def fetch_order_books(self, symbols: Strings = None, limit: Int = None, params={}):
|
|
980
|
+
"""
|
|
981
|
+
fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data for multiple markets
|
|
982
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#c60c51a8-e683-4f45-a000-820723d37871
|
|
983
|
+
:param str[]|None symbols: list of unified market symbols, all symbols fetched if None, default is None
|
|
984
|
+
:param int [limit]: max number of entries per orderbook to return, default is None
|
|
985
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
986
|
+
:returns dict: a dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbol
|
|
987
|
+
"""
|
|
988
|
+
await self.load_markets()
|
|
989
|
+
ids = None
|
|
990
|
+
if symbols is None:
|
|
991
|
+
ids = ','.join(self.ids)
|
|
992
|
+
# max URL length is 2083 symbols, including http schema, hostname, tld, etc...
|
|
993
|
+
if len(ids) > 2048:
|
|
994
|
+
numIds = len(self.ids)
|
|
995
|
+
raise ExchangeError(self.id + ' fetchOrderBooks() has ' + str(numIds) + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks')
|
|
996
|
+
else:
|
|
997
|
+
ids = self.market_ids(symbols)
|
|
998
|
+
ids = ','.join(ids)
|
|
999
|
+
request: dict = {
|
|
1000
|
+
'pair': ids,
|
|
1001
|
+
}
|
|
1002
|
+
if limit is not None:
|
|
1003
|
+
request['limit'] = limit
|
|
1004
|
+
response = await self.publicGetOrderBook(self.extend(request, params))
|
|
1005
|
+
result: dict = {}
|
|
1006
|
+
marketIds = list(response.keys())
|
|
1007
|
+
for i in range(0, len(marketIds)):
|
|
1008
|
+
marketId = marketIds[i]
|
|
1009
|
+
symbol = self.safe_symbol(marketId)
|
|
1010
|
+
result[symbol] = self.parse_order_book(response[marketId], symbol, None, 'bid', 'ask')
|
|
1011
|
+
return result
|
|
1012
|
+
|
|
1013
|
+
def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
|
1014
|
+
#
|
|
1015
|
+
# {
|
|
1016
|
+
# "buy_price":"0.00002996",
|
|
1017
|
+
# "sell_price":"0.00003002",
|
|
1018
|
+
# "last_trade":"0.00002992",
|
|
1019
|
+
# "high":"0.00003028",
|
|
1020
|
+
# "low":"0.00002935",
|
|
1021
|
+
# "avg":"0.00002963",
|
|
1022
|
+
# "vol":"1196546.3163222",
|
|
1023
|
+
# "vol_curr":"35.80066578",
|
|
1024
|
+
# "updated":1642291733
|
|
1025
|
+
# }
|
|
1026
|
+
#
|
|
1027
|
+
timestamp = self.safe_timestamp(ticker, 'updated')
|
|
1028
|
+
market = self.safe_market(None, market)
|
|
1029
|
+
last = self.safe_string(ticker, 'last_trade')
|
|
1030
|
+
return self.safe_ticker({
|
|
1031
|
+
'symbol': market['symbol'],
|
|
1032
|
+
'timestamp': timestamp,
|
|
1033
|
+
'datetime': self.iso8601(timestamp),
|
|
1034
|
+
'high': self.safe_string(ticker, 'high'),
|
|
1035
|
+
'low': self.safe_string(ticker, 'low'),
|
|
1036
|
+
'bid': self.safe_string(ticker, 'buy_price'),
|
|
1037
|
+
'bidVolume': None,
|
|
1038
|
+
'ask': self.safe_string(ticker, 'sell_price'),
|
|
1039
|
+
'askVolume': None,
|
|
1040
|
+
'vwap': None,
|
|
1041
|
+
'open': None,
|
|
1042
|
+
'close': last,
|
|
1043
|
+
'last': last,
|
|
1044
|
+
'previousClose': None,
|
|
1045
|
+
'change': None,
|
|
1046
|
+
'percentage': None,
|
|
1047
|
+
'average': self.safe_string(ticker, 'avg'),
|
|
1048
|
+
'baseVolume': self.safe_string(ticker, 'vol'),
|
|
1049
|
+
'quoteVolume': self.safe_string(ticker, 'vol_curr'),
|
|
1050
|
+
'info': ticker,
|
|
1051
|
+
}, market)
|
|
1052
|
+
|
|
1053
|
+
async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
|
1054
|
+
"""
|
|
1055
|
+
fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
|
1056
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#4c8e6459-3503-4361-b012-c34bb9f7e385
|
|
1057
|
+
:param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
|
|
1058
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1059
|
+
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
|
1060
|
+
"""
|
|
1061
|
+
await self.load_markets()
|
|
1062
|
+
symbols = self.market_symbols(symbols)
|
|
1063
|
+
response = await self.publicGetTicker(params)
|
|
1064
|
+
#
|
|
1065
|
+
# {
|
|
1066
|
+
# "ADA_BTC":{
|
|
1067
|
+
# "buy_price":"0.00002996",
|
|
1068
|
+
# "sell_price":"0.00003002",
|
|
1069
|
+
# "last_trade":"0.00002992",
|
|
1070
|
+
# "high":"0.00003028",
|
|
1071
|
+
# "low":"0.00002935",
|
|
1072
|
+
# "avg":"0.00002963",
|
|
1073
|
+
# "vol":"1196546.3163222",
|
|
1074
|
+
# "vol_curr":"35.80066578",
|
|
1075
|
+
# "updated":1642291733
|
|
1076
|
+
# }
|
|
1077
|
+
# }
|
|
1078
|
+
#
|
|
1079
|
+
result: dict = {}
|
|
1080
|
+
marketIds = list(response.keys())
|
|
1081
|
+
for i in range(0, len(marketIds)):
|
|
1082
|
+
marketId = marketIds[i]
|
|
1083
|
+
market = self.safe_market(marketId, None, '_')
|
|
1084
|
+
symbol = market['symbol']
|
|
1085
|
+
ticker = self.safe_value(response, marketId)
|
|
1086
|
+
result[symbol] = self.parse_ticker(ticker, market)
|
|
1087
|
+
return self.filter_by_array_tickers(result, 'symbol', symbols)
|
|
1088
|
+
|
|
1089
|
+
async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
|
|
1090
|
+
"""
|
|
1091
|
+
fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
|
1092
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#4c8e6459-3503-4361-b012-c34bb9f7e385
|
|
1093
|
+
:param str symbol: unified symbol of the market to fetch the ticker for
|
|
1094
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1095
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
|
1096
|
+
"""
|
|
1097
|
+
await self.load_markets()
|
|
1098
|
+
response = await self.publicGetTicker(params)
|
|
1099
|
+
market = self.market(symbol)
|
|
1100
|
+
return self.parse_ticker(response[market['id']], market)
|
|
1101
|
+
|
|
1102
|
+
def parse_trade(self, trade: dict, market: Market = None) -> Trade:
|
|
1103
|
+
#
|
|
1104
|
+
# fetchTrades(public)
|
|
1105
|
+
#
|
|
1106
|
+
# {
|
|
1107
|
+
# "trade_id":165087520,
|
|
1108
|
+
# "date":1587470005,
|
|
1109
|
+
# "type":"buy",
|
|
1110
|
+
# "quantity":"1.004",
|
|
1111
|
+
# "price":"0.02491461",
|
|
1112
|
+
# "amount":"0.02501426"
|
|
1113
|
+
# },
|
|
1114
|
+
#
|
|
1115
|
+
# fetchMyTrades, fetchOrderTrades
|
|
1116
|
+
#
|
|
1117
|
+
# {
|
|
1118
|
+
# "trade_id": 3,
|
|
1119
|
+
# "date": 1435488248,
|
|
1120
|
+
# "type": "buy",
|
|
1121
|
+
# "pair": "BTC_USD",
|
|
1122
|
+
# "order_id": 12345,
|
|
1123
|
+
# "quantity": 1,
|
|
1124
|
+
# "price": 100,
|
|
1125
|
+
# "amount": 100,
|
|
1126
|
+
# "exec_type": "taker",
|
|
1127
|
+
# "commission_amount": "0.02",
|
|
1128
|
+
# "commission_currency": "BTC",
|
|
1129
|
+
# "commission_percent": "0.2"
|
|
1130
|
+
# }
|
|
1131
|
+
#
|
|
1132
|
+
# fetchMyTrades(margin)
|
|
1133
|
+
#
|
|
1134
|
+
# {
|
|
1135
|
+
# "trade_id": "692861757015952517",
|
|
1136
|
+
# "trade_dt": "1693951853197811824",
|
|
1137
|
+
# "trade_type": "buy",
|
|
1138
|
+
# "pair": "ADA_USDT",
|
|
1139
|
+
# "quantity": "1.96607879",
|
|
1140
|
+
# "price": "0.2568",
|
|
1141
|
+
# "amount": "0.50488903"
|
|
1142
|
+
# }
|
|
1143
|
+
#
|
|
1144
|
+
timestamp = self.safe_timestamp(trade, 'date')
|
|
1145
|
+
id = self.safe_string(trade, 'trade_id')
|
|
1146
|
+
orderId = self.safe_string(trade, 'order_id')
|
|
1147
|
+
priceString = self.safe_string(trade, 'price')
|
|
1148
|
+
amountString = self.safe_string(trade, 'quantity')
|
|
1149
|
+
costString = self.safe_string(trade, 'amount')
|
|
1150
|
+
side = self.safe_string_2(trade, 'type', 'trade_type')
|
|
1151
|
+
type = None
|
|
1152
|
+
marketId = self.safe_string(trade, 'pair')
|
|
1153
|
+
market = self.safe_market(marketId, market, '_')
|
|
1154
|
+
symbol = market['symbol']
|
|
1155
|
+
isMaker = self.safe_value(trade, 'is_maker')
|
|
1156
|
+
takerOrMakerDefault = None
|
|
1157
|
+
if isMaker is not None:
|
|
1158
|
+
takerOrMakerDefault = 'maker' if isMaker else 'taker'
|
|
1159
|
+
takerOrMaker = self.safe_string(trade, 'exec_type', takerOrMakerDefault)
|
|
1160
|
+
fee = None
|
|
1161
|
+
feeCostString = self.safe_string(trade, 'commission_amount')
|
|
1162
|
+
if feeCostString is not None:
|
|
1163
|
+
feeCurrencyId = self.safe_string(trade, 'commission_currency')
|
|
1164
|
+
feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
|
|
1165
|
+
feeRateString = self.safe_string(trade, 'commission_percent')
|
|
1166
|
+
if feeRateString is not None:
|
|
1167
|
+
feeRateString = Precise.string_div(feeRateString, '1000', 18)
|
|
1168
|
+
fee = {
|
|
1169
|
+
'cost': feeCostString,
|
|
1170
|
+
'currency': feeCurrencyCode,
|
|
1171
|
+
'rate': feeRateString,
|
|
1172
|
+
}
|
|
1173
|
+
return self.safe_trade({
|
|
1174
|
+
'id': id,
|
|
1175
|
+
'info': trade,
|
|
1176
|
+
'timestamp': timestamp,
|
|
1177
|
+
'datetime': self.iso8601(timestamp),
|
|
1178
|
+
'symbol': symbol,
|
|
1179
|
+
'order': orderId,
|
|
1180
|
+
'type': type,
|
|
1181
|
+
'side': side,
|
|
1182
|
+
'takerOrMaker': takerOrMaker,
|
|
1183
|
+
'price': priceString,
|
|
1184
|
+
'amount': amountString,
|
|
1185
|
+
'cost': costString,
|
|
1186
|
+
'fee': fee,
|
|
1187
|
+
}, market)
|
|
1188
|
+
|
|
1189
|
+
async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
|
1190
|
+
"""
|
|
1191
|
+
get the list of most recent trades for a particular symbol
|
|
1192
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#5a5a9c0d-cf17-47f6-9d62-6d4404ebd5ac
|
|
1193
|
+
:param str symbol: unified symbol of the market to fetch trades for
|
|
1194
|
+
:param int [since]: timestamp in ms of the earliest trade to fetch
|
|
1195
|
+
:param int [limit]: the maximum amount of trades to fetch
|
|
1196
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1197
|
+
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
|
1198
|
+
"""
|
|
1199
|
+
await self.load_markets()
|
|
1200
|
+
market = self.market(symbol)
|
|
1201
|
+
request: dict = {
|
|
1202
|
+
'pair': market['id'],
|
|
1203
|
+
}
|
|
1204
|
+
response = await self.publicGetTrades(self.extend(request, params))
|
|
1205
|
+
#
|
|
1206
|
+
# {
|
|
1207
|
+
# "ETH_BTC":[
|
|
1208
|
+
# {
|
|
1209
|
+
# "trade_id":165087520,
|
|
1210
|
+
# "date":1587470005,
|
|
1211
|
+
# "type":"buy",
|
|
1212
|
+
# "quantity":"1.004",
|
|
1213
|
+
# "price":"0.02491461",
|
|
1214
|
+
# "amount":"0.02501426"
|
|
1215
|
+
# },
|
|
1216
|
+
# {
|
|
1217
|
+
# "trade_id":165087369,
|
|
1218
|
+
# "date":1587469938,
|
|
1219
|
+
# "type":"buy",
|
|
1220
|
+
# "quantity":"0.94",
|
|
1221
|
+
# "price":"0.02492348",
|
|
1222
|
+
# "amount":"0.02342807"
|
|
1223
|
+
# }
|
|
1224
|
+
# ]
|
|
1225
|
+
# }
|
|
1226
|
+
#
|
|
1227
|
+
data = self.safe_list(response, market['id'], [])
|
|
1228
|
+
return self.parse_trades(data, market, since, limit)
|
|
1229
|
+
|
|
1230
|
+
async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
1231
|
+
"""
|
|
1232
|
+
fetch all trades made by the user
|
|
1233
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#b8d8d9af-4f46-46a1-939b-ad261d79f452 # spot
|
|
1234
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#f4b1aaf8-399f-403b-ab5e-4926d967a106 # margin
|
|
1235
|
+
:param str symbol: a symbol is required but it can be a single string, or a non-empty array
|
|
1236
|
+
:param int [since]: the earliest time in ms to fetch trades for
|
|
1237
|
+
:param int [limit]: *required for margin orders* the maximum number of trades structures to retrieve
|
|
1238
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1239
|
+
*
|
|
1240
|
+
* EXCHANGE SPECIFIC PARAMETERS
|
|
1241
|
+
:param int [params.offset]: last deal offset, default = 0
|
|
1242
|
+
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
|
|
1243
|
+
"""
|
|
1244
|
+
if symbol is None:
|
|
1245
|
+
raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
|
|
1246
|
+
marginMode = None
|
|
1247
|
+
marginMode, params = self.handle_margin_mode_and_params('fetchMyTrades', params)
|
|
1248
|
+
if marginMode == 'cross':
|
|
1249
|
+
raise BadRequest(self.id + 'only isolated margin is supported')
|
|
1250
|
+
await self.load_markets()
|
|
1251
|
+
market = self.market(symbol)
|
|
1252
|
+
pair = market['id']
|
|
1253
|
+
isSpot = marginMode != 'isolated'
|
|
1254
|
+
if limit is None:
|
|
1255
|
+
limit = 100
|
|
1256
|
+
request: dict = {}
|
|
1257
|
+
if isSpot:
|
|
1258
|
+
request['pair'] = pair
|
|
1259
|
+
else:
|
|
1260
|
+
request['pair_name'] = pair
|
|
1261
|
+
if limit is not None:
|
|
1262
|
+
request['limit'] = limit
|
|
1263
|
+
offset = self.safe_integer(params, 'offset', 0)
|
|
1264
|
+
request['offset'] = offset
|
|
1265
|
+
response = None
|
|
1266
|
+
if isSpot:
|
|
1267
|
+
response = await self.privatePostUserTrades(self.extend(request, params))
|
|
1268
|
+
#
|
|
1269
|
+
# {
|
|
1270
|
+
# "BTC_USD": [
|
|
1271
|
+
# {
|
|
1272
|
+
# "trade_id": 20056872,
|
|
1273
|
+
# "client_id": 100500,
|
|
1274
|
+
# "date": 1435488248,
|
|
1275
|
+
# "type": "buy",
|
|
1276
|
+
# "pair": "BTC_USD",
|
|
1277
|
+
# "quantity": "1",
|
|
1278
|
+
# "price": "100",
|
|
1279
|
+
# "amount": "100",
|
|
1280
|
+
# "order_id": 7,
|
|
1281
|
+
# "parent_order_id": 117684023830293,
|
|
1282
|
+
# "exec_type": "taker",
|
|
1283
|
+
# "commission_amount": "0.02",
|
|
1284
|
+
# "commission_currency": "BTC",
|
|
1285
|
+
# "commission_percent": "0.2"
|
|
1286
|
+
# }
|
|
1287
|
+
# ],
|
|
1288
|
+
# ...
|
|
1289
|
+
# }
|
|
1290
|
+
#
|
|
1291
|
+
else:
|
|
1292
|
+
responseFromExchange = await self.privatePostMarginTrades(self.extend(request, params))
|
|
1293
|
+
#
|
|
1294
|
+
# {
|
|
1295
|
+
# "trades": {
|
|
1296
|
+
# "ADA_USDT": [
|
|
1297
|
+
# {
|
|
1298
|
+
# "trade_id": "692861757015952517",
|
|
1299
|
+
# "trade_dt": "1693951853197811824",
|
|
1300
|
+
# "trade_type": "buy",
|
|
1301
|
+
# "pair": "ADA_USDT",
|
|
1302
|
+
# "quantity": "1.96607879",
|
|
1303
|
+
# "price": "0.2568",
|
|
1304
|
+
# "amount": "0.50488903"
|
|
1305
|
+
# },
|
|
1306
|
+
# ]
|
|
1307
|
+
# ...
|
|
1308
|
+
# }
|
|
1309
|
+
# }
|
|
1310
|
+
#
|
|
1311
|
+
response = self.safe_value(responseFromExchange, 'trades')
|
|
1312
|
+
result = []
|
|
1313
|
+
marketIdsInner = list(response.keys())
|
|
1314
|
+
for i in range(0, len(marketIdsInner)):
|
|
1315
|
+
marketId = marketIdsInner[i]
|
|
1316
|
+
resultMarket = self.safe_market(marketId, None, '_')
|
|
1317
|
+
items = response[marketId]
|
|
1318
|
+
trades = self.parse_trades(items, resultMarket, since, limit)
|
|
1319
|
+
result = self.array_concat(result, trades)
|
|
1320
|
+
return self.filter_by_since_limit(result, since, limit)
|
|
1321
|
+
|
|
1322
|
+
async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
1323
|
+
"""
|
|
1324
|
+
create a trade order
|
|
1325
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#80daa469-ec59-4d0a-b229-6a311d8dd1cd
|
|
1326
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#de6f4321-eeac-468c-87f7-c4ad7062e265 # stop market
|
|
1327
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#3561b86c-9ff1-436e-8e68-ac926b7eb523 # margin
|
|
1328
|
+
:param str symbol: unified symbol of the market to create an order in
|
|
1329
|
+
:param str type: 'market' or 'limit'
|
|
1330
|
+
:param str side: 'buy' or 'sell'
|
|
1331
|
+
:param float amount: how much of currency you want to trade in units of base currency
|
|
1332
|
+
:param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
|
|
1333
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1334
|
+
:param float [params.stopPrice]: the price at which a trigger order is triggered at
|
|
1335
|
+
:param str [params.timeInForce]: *spot only* 'fok', 'ioc' or 'post_only'
|
|
1336
|
+
:param boolean [params.postOnly]: *spot only* True for post only orders
|
|
1337
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
1338
|
+
"""
|
|
1339
|
+
await self.load_markets()
|
|
1340
|
+
market = self.market(symbol)
|
|
1341
|
+
isMarket = (type == 'market') and (price is None)
|
|
1342
|
+
marginMode = None
|
|
1343
|
+
marginMode, params = self.handle_margin_mode_and_params('createOrder', params)
|
|
1344
|
+
if marginMode == 'cross':
|
|
1345
|
+
raise BadRequest(self.id + ' only supports isolated margin')
|
|
1346
|
+
isSpot = (marginMode != 'isolated')
|
|
1347
|
+
triggerPrice = self.safe_number_n(params, ['triggerPrice', 'stopPrice', 'stop_price'])
|
|
1348
|
+
request: dict = {
|
|
1349
|
+
'pair': market['id'],
|
|
1350
|
+
# 'leverage': 2,
|
|
1351
|
+
'quantity': self.amount_to_precision(market['symbol'], amount),
|
|
1352
|
+
# spot - buy, sell, market_buy, market_sell, market_buy_total, market_sell_total
|
|
1353
|
+
# margin - limit_buy, limit_sell, market_buy, market_sell, stop_buy, stop_sell, stop_limit_buy, stop_limit_sell, trailing_stop_buy, trailing_stop_sell
|
|
1354
|
+
# 'stop_price': self.price_to_precision(symbol, stopPrice),
|
|
1355
|
+
# 'distance': 0, # distance for trailing stop orders
|
|
1356
|
+
# 'expire': 0, # expiration timestamp in UTC timezone for the order, unless expire is 0
|
|
1357
|
+
# 'client_id': 123, # optional, must be a positive integer
|
|
1358
|
+
# 'comment': '', # up to 50 latin symbols, whitespaces, underscores
|
|
1359
|
+
}
|
|
1360
|
+
clientOrderId = self.safe_value_2(params, 'client_id', 'clientOrderId')
|
|
1361
|
+
if clientOrderId is not None:
|
|
1362
|
+
clientOrderId = self.safe_integer_2(params, 'client_id', 'clientOrderId')
|
|
1363
|
+
if clientOrderId is None:
|
|
1364
|
+
raise BadRequest(self.id + ' createOrder() client order id must be an integer / numeric literal')
|
|
1365
|
+
else:
|
|
1366
|
+
request['client_id'] = clientOrderId
|
|
1367
|
+
leverage = self.safe_number(params, 'leverage')
|
|
1368
|
+
if not isSpot and (leverage is None):
|
|
1369
|
+
raise ArgumentsRequired(self.id + ' createOrder requires an extra param params["leverage"] for margin orders')
|
|
1370
|
+
params = self.omit(params, ['stopPrice', 'stop_price', 'triggerPrice', 'timeInForce', 'client_id', 'clientOrderId'])
|
|
1371
|
+
if price is not None:
|
|
1372
|
+
request['price'] = self.price_to_precision(market['symbol'], price)
|
|
1373
|
+
response = None
|
|
1374
|
+
if isSpot:
|
|
1375
|
+
if triggerPrice is not None:
|
|
1376
|
+
if type == 'limit':
|
|
1377
|
+
raise BadRequest(self.id + ' createOrder() cannot create stop limit orders for spot, only stop market')
|
|
1378
|
+
else:
|
|
1379
|
+
request['type'] = side
|
|
1380
|
+
request['trigger_price'] = self.price_to_precision(symbol, triggerPrice)
|
|
1381
|
+
response = await self.privatePostStopMarketOrderCreate(self.extend(request, params))
|
|
1382
|
+
else:
|
|
1383
|
+
execType = self.safe_string(params, 'exec_type')
|
|
1384
|
+
isPostOnly = None
|
|
1385
|
+
isPostOnly, params = self.handle_post_only(type == 'market', execType == 'post_only', params)
|
|
1386
|
+
timeInForce = self.safe_string(params, 'timeInForce')
|
|
1387
|
+
request['price'] = 0 if isMarket else self.price_to_precision(market['symbol'], price)
|
|
1388
|
+
if type == 'limit':
|
|
1389
|
+
request['type'] = side
|
|
1390
|
+
elif type == 'market':
|
|
1391
|
+
request['type'] = 'market_' + side
|
|
1392
|
+
if isPostOnly:
|
|
1393
|
+
request['exec_type'] = 'post_only'
|
|
1394
|
+
elif timeInForce is not None:
|
|
1395
|
+
request['exec_type'] = timeInForce
|
|
1396
|
+
response = await self.privatePostOrderCreate(self.extend(request, params))
|
|
1397
|
+
else:
|
|
1398
|
+
if triggerPrice is not None:
|
|
1399
|
+
request['stop_price'] = self.price_to_precision(symbol, triggerPrice)
|
|
1400
|
+
if type == 'limit':
|
|
1401
|
+
request['type'] = 'stop_limit_' + side
|
|
1402
|
+
elif type == 'market':
|
|
1403
|
+
request['type'] = 'stop_' + side
|
|
1404
|
+
else:
|
|
1405
|
+
request['type'] = type
|
|
1406
|
+
else:
|
|
1407
|
+
if type == 'limit' or type == 'market':
|
|
1408
|
+
request['type'] = type + '_' + side
|
|
1409
|
+
else:
|
|
1410
|
+
request['type'] = type
|
|
1411
|
+
response = await self.privatePostMarginUserOrderCreate(self.extend(request, params))
|
|
1412
|
+
return self.parse_order(response, market)
|
|
1413
|
+
|
|
1414
|
+
async def cancel_order(self, id: str, symbol: Str = None, params={}):
|
|
1415
|
+
"""
|
|
1416
|
+
cancels an open order
|
|
1417
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#1f710d4b-75bc-4b65-ad68-006f863a3f26
|
|
1418
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#a4d0aae8-28f7-41ac-94fd-c4030130453d # stop market
|
|
1419
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#705dfec5-2b35-4667-862b-faf54eca6209 # margin
|
|
1420
|
+
:param str id: order id
|
|
1421
|
+
:param str symbol: not used by exmo cancelOrder()
|
|
1422
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1423
|
+
:param boolean [params.trigger]: True to cancel a trigger order
|
|
1424
|
+
:param str [params.marginMode]: set to 'cross' or 'isolated' to cancel a margin order
|
|
1425
|
+
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
1426
|
+
"""
|
|
1427
|
+
await self.load_markets()
|
|
1428
|
+
request: dict = {}
|
|
1429
|
+
stop = self.safe_value_2(params, 'trigger', 'stop')
|
|
1430
|
+
params = self.omit(params, ['trigger', 'stop'])
|
|
1431
|
+
marginMode = None
|
|
1432
|
+
marginMode, params = self.handle_margin_mode_and_params('cancelOrder', params)
|
|
1433
|
+
if marginMode == 'cross':
|
|
1434
|
+
raise BadRequest(self.id + ' only supports isolated margin')
|
|
1435
|
+
response = None
|
|
1436
|
+
if (marginMode == 'isolated'):
|
|
1437
|
+
request['order_id'] = id
|
|
1438
|
+
response = await self.privatePostMarginUserOrderCancel(self.extend(request, params))
|
|
1439
|
+
#
|
|
1440
|
+
# {}
|
|
1441
|
+
#
|
|
1442
|
+
else:
|
|
1443
|
+
if stop:
|
|
1444
|
+
request['parent_order_id'] = id
|
|
1445
|
+
response = await self.privatePostStopMarketOrderCancel(self.extend(request, params))
|
|
1446
|
+
#
|
|
1447
|
+
# {}
|
|
1448
|
+
#
|
|
1449
|
+
else:
|
|
1450
|
+
request['order_id'] = id
|
|
1451
|
+
response = await self.privatePostOrderCancel(self.extend(request, params))
|
|
1452
|
+
#
|
|
1453
|
+
# {
|
|
1454
|
+
# "error": '',
|
|
1455
|
+
# "result": True
|
|
1456
|
+
# }
|
|
1457
|
+
#
|
|
1458
|
+
return self.parse_order(response)
|
|
1459
|
+
|
|
1460
|
+
async def fetch_order(self, id: str, symbol: Str = None, params={}):
|
|
1461
|
+
"""
|
|
1462
|
+
*spot only* fetches information on an order made by the user
|
|
1463
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#cf27781e-28e5-4b39-a52d-3110f5d22459 # spot
|
|
1464
|
+
:param str symbol: not used by exmo fetchOrder
|
|
1465
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1466
|
+
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
1467
|
+
"""
|
|
1468
|
+
await self.load_markets()
|
|
1469
|
+
request: dict = {
|
|
1470
|
+
'order_id': str(id),
|
|
1471
|
+
}
|
|
1472
|
+
response = await self.privatePostOrderTrades(self.extend(request, params))
|
|
1473
|
+
#
|
|
1474
|
+
# {
|
|
1475
|
+
# "type": "buy",
|
|
1476
|
+
# "in_currency": "BTC",
|
|
1477
|
+
# "in_amount": "1",
|
|
1478
|
+
# "out_currency": "USD",
|
|
1479
|
+
# "out_amount": "100",
|
|
1480
|
+
# "trades": [
|
|
1481
|
+
# {
|
|
1482
|
+
# "trade_id": 3,
|
|
1483
|
+
# "date": 1435488248,
|
|
1484
|
+
# "type": "buy",
|
|
1485
|
+
# "pair": "BTC_USD",
|
|
1486
|
+
# "order_id": 12345,
|
|
1487
|
+
# "quantity": 1,
|
|
1488
|
+
# "price": 100,
|
|
1489
|
+
# "amount": 100
|
|
1490
|
+
# }
|
|
1491
|
+
# ]
|
|
1492
|
+
# }
|
|
1493
|
+
#
|
|
1494
|
+
order = self.parse_order(response)
|
|
1495
|
+
order['id'] = str(id)
|
|
1496
|
+
return order
|
|
1497
|
+
|
|
1498
|
+
async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
1499
|
+
"""
|
|
1500
|
+
fetch all the trades made from a single order
|
|
1501
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#cf27781e-28e5-4b39-a52d-3110f5d22459 # spot
|
|
1502
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#00810661-9119-46c5-aec5-55abe9cb42c7 # margin
|
|
1503
|
+
:param str id: order id
|
|
1504
|
+
:param str symbol: unified market symbol
|
|
1505
|
+
:param int [since]: the earliest time in ms to fetch trades for
|
|
1506
|
+
:param int [limit]: the maximum number of trades to retrieve
|
|
1507
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1508
|
+
:param str [params.marginMode]: set to "isolated" to fetch trades for a margin order
|
|
1509
|
+
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
|
|
1510
|
+
"""
|
|
1511
|
+
marginMode = None
|
|
1512
|
+
marginMode, params = self.handle_margin_mode_and_params('fetchOrderTrades', params)
|
|
1513
|
+
if marginMode == 'cross':
|
|
1514
|
+
raise BadRequest(self.id + ' only supports isolated margin')
|
|
1515
|
+
market = None
|
|
1516
|
+
if symbol is not None:
|
|
1517
|
+
market = self.market(symbol)
|
|
1518
|
+
request: dict = {
|
|
1519
|
+
'order_id': str(id),
|
|
1520
|
+
}
|
|
1521
|
+
response = None
|
|
1522
|
+
if marginMode == 'isolated':
|
|
1523
|
+
response = await self.privatePostMarginUserOrderTrades(self.extend(request, params))
|
|
1524
|
+
#
|
|
1525
|
+
# {
|
|
1526
|
+
# "trades": [
|
|
1527
|
+
# {
|
|
1528
|
+
# "is_maker": False,
|
|
1529
|
+
# "order_id": "123",
|
|
1530
|
+
# "pair": "BTC_USD",
|
|
1531
|
+
# "price": "54122.25",
|
|
1532
|
+
# "quantity": "0.00069994",
|
|
1533
|
+
# "trade_dt": "1619069561718824428",
|
|
1534
|
+
# "trade_id": "692842802860135010",
|
|
1535
|
+
# "type": "sell"
|
|
1536
|
+
# }
|
|
1537
|
+
# ]
|
|
1538
|
+
# }
|
|
1539
|
+
#
|
|
1540
|
+
else:
|
|
1541
|
+
response = await self.privatePostOrderTrades(self.extend(request, params))
|
|
1542
|
+
#
|
|
1543
|
+
# {
|
|
1544
|
+
# "type": "buy",
|
|
1545
|
+
# "in_currency": "BTC",
|
|
1546
|
+
# "in_amount": "1",
|
|
1547
|
+
# "out_currency": "USD",
|
|
1548
|
+
# "out_amount": "100",
|
|
1549
|
+
# "trades": [
|
|
1550
|
+
# {
|
|
1551
|
+
# "trade_id": 3,
|
|
1552
|
+
# "date": 1435488248,
|
|
1553
|
+
# "type": "buy",
|
|
1554
|
+
# "pair": "BTC_USD",
|
|
1555
|
+
# "order_id": 12345,
|
|
1556
|
+
# "quantity": 1,
|
|
1557
|
+
# "price": 100,
|
|
1558
|
+
# "amount": 100,
|
|
1559
|
+
# "exec_type": "taker",
|
|
1560
|
+
# "commission_amount": "0.02",
|
|
1561
|
+
# "commission_currency": "BTC",
|
|
1562
|
+
# "commission_percent": "0.2"
|
|
1563
|
+
# }
|
|
1564
|
+
# ]
|
|
1565
|
+
# }
|
|
1566
|
+
#
|
|
1567
|
+
trades = self.safe_list(response, 'trades')
|
|
1568
|
+
return self.parse_trades(trades, market, since, limit)
|
|
1569
|
+
|
|
1570
|
+
async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
|
1571
|
+
"""
|
|
1572
|
+
fetch all unfilled currently open orders
|
|
1573
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#0e135370-daa4-4689-8acd-b6876dee9ba1 # spot open orders
|
|
1574
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#a7cfd4f0-476e-4675-b33f-22a46902f245 # margin
|
|
1575
|
+
:param str symbol: unified market symbol
|
|
1576
|
+
:param int [since]: the earliest time in ms to fetch open orders for
|
|
1577
|
+
:param int [limit]: the maximum number of open orders structures to retrieve
|
|
1578
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1579
|
+
:param str [params.marginMode]: set to "isolated" for margin orders
|
|
1580
|
+
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
|
1581
|
+
"""
|
|
1582
|
+
await self.load_markets()
|
|
1583
|
+
market = None
|
|
1584
|
+
if symbol is not None:
|
|
1585
|
+
market = self.market(symbol)
|
|
1586
|
+
symbol = market['symbol']
|
|
1587
|
+
marginMode = None
|
|
1588
|
+
marginMode, params = self.handle_margin_mode_and_params('fetchOpenOrders', params)
|
|
1589
|
+
isMargin = ((marginMode == 'cross') or (marginMode == 'isolated'))
|
|
1590
|
+
response = None
|
|
1591
|
+
orders = []
|
|
1592
|
+
if isMargin:
|
|
1593
|
+
response = await self.privatePostMarginUserOrderList(params)
|
|
1594
|
+
#
|
|
1595
|
+
# {
|
|
1596
|
+
# "orders": [
|
|
1597
|
+
# {
|
|
1598
|
+
# "client_id": "0",
|
|
1599
|
+
# "comment": "",
|
|
1600
|
+
# "created": "1619068707985325495",
|
|
1601
|
+
# "distance": "0",
|
|
1602
|
+
# "expire": 0,
|
|
1603
|
+
# "funding_currency": "BTC",
|
|
1604
|
+
# "funding_quantity": "0.01",
|
|
1605
|
+
# "funding_rate": "0.02",
|
|
1606
|
+
# "leverage": "2",
|
|
1607
|
+
# "order_id": "123",
|
|
1608
|
+
# "pair": "BTC_USD",
|
|
1609
|
+
# "previous_type": "limit_sell",
|
|
1610
|
+
# "price": "58000",
|
|
1611
|
+
# "quantity": "0.01",
|
|
1612
|
+
# "src": 0,
|
|
1613
|
+
# "stop_price": "0",
|
|
1614
|
+
# "trigger_price": "58000",
|
|
1615
|
+
# "type": "limit_sell",
|
|
1616
|
+
# "updated": 1619068707989411800
|
|
1617
|
+
# }
|
|
1618
|
+
# ]
|
|
1619
|
+
# }
|
|
1620
|
+
#
|
|
1621
|
+
params = self.extend(params, {
|
|
1622
|
+
'status': 'open',
|
|
1623
|
+
})
|
|
1624
|
+
responseOrders = self.safe_value(response, 'orders')
|
|
1625
|
+
orders = self.parse_orders(responseOrders, market, since, limit, params)
|
|
1626
|
+
else:
|
|
1627
|
+
response = await self.privatePostUserOpenOrders(params)
|
|
1628
|
+
#
|
|
1629
|
+
# {
|
|
1630
|
+
# "USDT_USD": [
|
|
1631
|
+
# {
|
|
1632
|
+
# "parent_order_id": "507061384740151010",
|
|
1633
|
+
# "client_id": "100500",
|
|
1634
|
+
# "created": "1589547391",
|
|
1635
|
+
# "type": "stop_market_buy",
|
|
1636
|
+
# "pair": "USDT_USD",
|
|
1637
|
+
# "quantity": "1",
|
|
1638
|
+
# "trigger_price": "5",
|
|
1639
|
+
# "amount": "5"
|
|
1640
|
+
# }
|
|
1641
|
+
# ],
|
|
1642
|
+
# ...
|
|
1643
|
+
# }
|
|
1644
|
+
#
|
|
1645
|
+
marketIds = list(response.keys())
|
|
1646
|
+
for i in range(0, len(marketIds)):
|
|
1647
|
+
marketId = marketIds[i]
|
|
1648
|
+
marketInner = self.safe_market(marketId)
|
|
1649
|
+
params = self.extend(params, {
|
|
1650
|
+
'status': 'open',
|
|
1651
|
+
})
|
|
1652
|
+
parsedOrders = self.parse_orders(response[marketId], marketInner, since, limit, params)
|
|
1653
|
+
orders = self.array_concat(orders, parsedOrders)
|
|
1654
|
+
return orders
|
|
1655
|
+
|
|
1656
|
+
def parse_status(self, status):
|
|
1657
|
+
if status is None:
|
|
1658
|
+
return None
|
|
1659
|
+
statuses: dict = {
|
|
1660
|
+
'cancel_started': 'canceled',
|
|
1661
|
+
}
|
|
1662
|
+
if status.find('cancel') >= 0:
|
|
1663
|
+
status = 'canceled'
|
|
1664
|
+
return self.safe_string(statuses, status, status)
|
|
1665
|
+
|
|
1666
|
+
def parse_side(self, orderType):
|
|
1667
|
+
side: dict = {
|
|
1668
|
+
'limit_buy': 'buy',
|
|
1669
|
+
'limit_sell': 'sell',
|
|
1670
|
+
'market_buy': 'buy',
|
|
1671
|
+
'market_sell': 'sell',
|
|
1672
|
+
'stop_buy': 'buy',
|
|
1673
|
+
'stop_sell': 'sell',
|
|
1674
|
+
'stop_limit_buy': 'buy',
|
|
1675
|
+
'stop_limit_sell': 'sell',
|
|
1676
|
+
'trailing_stop_buy': 'buy',
|
|
1677
|
+
'trailing_stop_sell': 'sell',
|
|
1678
|
+
'stop_market_sell': 'sell',
|
|
1679
|
+
'stop_market_buy': 'buy',
|
|
1680
|
+
'buy': 'buy',
|
|
1681
|
+
'sell': 'sell',
|
|
1682
|
+
}
|
|
1683
|
+
return self.safe_string(side, orderType, orderType)
|
|
1684
|
+
|
|
1685
|
+
def parse_order(self, order: dict, market: Market = None) -> Order:
|
|
1686
|
+
#
|
|
1687
|
+
# fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders
|
|
1688
|
+
#
|
|
1689
|
+
# {
|
|
1690
|
+
# "order_id": "14",
|
|
1691
|
+
# "created": "1435517311",
|
|
1692
|
+
# "type": "buy",
|
|
1693
|
+
# "pair": "BTC_USD",
|
|
1694
|
+
# "price": "100",
|
|
1695
|
+
# "quantity": "1",
|
|
1696
|
+
# "amount": "100"
|
|
1697
|
+
# }
|
|
1698
|
+
#
|
|
1699
|
+
# fetchOrder
|
|
1700
|
+
#
|
|
1701
|
+
# {
|
|
1702
|
+
# "type": "buy",
|
|
1703
|
+
# "in_currency": "BTC",
|
|
1704
|
+
# "in_amount": "1",
|
|
1705
|
+
# "out_currency": "USD",
|
|
1706
|
+
# "out_amount": "100",
|
|
1707
|
+
# "trades": [
|
|
1708
|
+
# {
|
|
1709
|
+
# "trade_id": 3,
|
|
1710
|
+
# "date": 1435488248,
|
|
1711
|
+
# "type": "buy",
|
|
1712
|
+
# "pair": "BTC_USD",
|
|
1713
|
+
# "order_id": 12345,
|
|
1714
|
+
# "quantity": 1,
|
|
1715
|
+
# "price": 100,
|
|
1716
|
+
# "amount": 100
|
|
1717
|
+
# }
|
|
1718
|
+
# ]
|
|
1719
|
+
# }
|
|
1720
|
+
#
|
|
1721
|
+
# Margin fetchOpenOrders
|
|
1722
|
+
#
|
|
1723
|
+
# {
|
|
1724
|
+
# "client_id": "0",
|
|
1725
|
+
# "comment": "",
|
|
1726
|
+
# "created": "1619068707985325495",
|
|
1727
|
+
# "distance": "0",
|
|
1728
|
+
# "expire": 0,
|
|
1729
|
+
# "funding_currency": "BTC",
|
|
1730
|
+
# "funding_quantity": "0.01",
|
|
1731
|
+
# "funding_rate": "0.02",
|
|
1732
|
+
# "leverage": "2",
|
|
1733
|
+
# "order_id": "123",
|
|
1734
|
+
# "pair": "BTC_USD",
|
|
1735
|
+
# "previous_type": "limit_sell",
|
|
1736
|
+
# "price": "58000",
|
|
1737
|
+
# "quantity": "0.01",
|
|
1738
|
+
# "src": 0,
|
|
1739
|
+
# "stop_price": "0",
|
|
1740
|
+
# "trigger_price": "58000",
|
|
1741
|
+
# "type": "limit_sell",
|
|
1742
|
+
# "updated": 1619068707989411800
|
|
1743
|
+
# }
|
|
1744
|
+
#
|
|
1745
|
+
# Margin fetchClosedOrders
|
|
1746
|
+
#
|
|
1747
|
+
# {
|
|
1748
|
+
# "distance": "0",
|
|
1749
|
+
# "event_id": "692842802860022508",
|
|
1750
|
+
# "event_time": "1619069531190173720",
|
|
1751
|
+
# "event_type": "OrderCancelStarted",
|
|
1752
|
+
# "order_id": "123",
|
|
1753
|
+
# "order_status": "cancel_started",
|
|
1754
|
+
# "order_type": "limit_sell",
|
|
1755
|
+
# "pair": "BTC_USD",
|
|
1756
|
+
# "price": "54115",
|
|
1757
|
+
# "quantity": "0.001",
|
|
1758
|
+
# "stop_price": "0",
|
|
1759
|
+
# "trade_id": "0",
|
|
1760
|
+
# "trade_price": "0",
|
|
1761
|
+
# "trade_quantity": "0",
|
|
1762
|
+
# "trade_type": ""
|
|
1763
|
+
# },
|
|
1764
|
+
#
|
|
1765
|
+
id = self.safe_string_2(order, 'order_id', 'parent_order_id')
|
|
1766
|
+
eventTime = self.safe_integer_product_2(order, 'event_time', 'created', 0.000001)
|
|
1767
|
+
timestamp = self.safe_timestamp(order, 'created', eventTime)
|
|
1768
|
+
orderType = self.safe_string_2(order, 'type', 'order_type')
|
|
1769
|
+
side = self.parse_side(orderType)
|
|
1770
|
+
marketId = None
|
|
1771
|
+
if 'pair' in order:
|
|
1772
|
+
marketId = order['pair']
|
|
1773
|
+
elif ('in_currency' in order) and ('out_currency' in order):
|
|
1774
|
+
if side == 'buy':
|
|
1775
|
+
marketId = order['in_currency'] + '_' + order['out_currency']
|
|
1776
|
+
else:
|
|
1777
|
+
marketId = order['out_currency'] + '_' + order['in_currency']
|
|
1778
|
+
market = self.safe_market(marketId, market)
|
|
1779
|
+
symbol = market['symbol']
|
|
1780
|
+
amount = self.safe_string(order, 'quantity')
|
|
1781
|
+
if amount is None:
|
|
1782
|
+
amountField = 'in_amount' if (side == 'buy') else 'out_amount'
|
|
1783
|
+
amount = self.safe_string(order, amountField)
|
|
1784
|
+
price = self.safe_string(order, 'price')
|
|
1785
|
+
cost = self.safe_string(order, 'amount')
|
|
1786
|
+
transactions = self.safe_value(order, 'trades', [])
|
|
1787
|
+
clientOrderId = self.safe_integer(order, 'client_id')
|
|
1788
|
+
triggerPrice = self.safe_string(order, 'stop_price')
|
|
1789
|
+
if triggerPrice == '0':
|
|
1790
|
+
triggerPrice = None
|
|
1791
|
+
type = None
|
|
1792
|
+
if (orderType != 'buy') and (orderType != 'sell'):
|
|
1793
|
+
type = orderType
|
|
1794
|
+
return self.safe_order({
|
|
1795
|
+
'id': id,
|
|
1796
|
+
'clientOrderId': clientOrderId,
|
|
1797
|
+
'datetime': self.iso8601(timestamp),
|
|
1798
|
+
'timestamp': timestamp,
|
|
1799
|
+
'lastTradeTimestamp': self.safe_integer_product(order, 'updated', 0.000001),
|
|
1800
|
+
'status': self.parse_status(self.safe_string(order, 'order_status')),
|
|
1801
|
+
'symbol': symbol,
|
|
1802
|
+
'type': type,
|
|
1803
|
+
'timeInForce': None,
|
|
1804
|
+
'postOnly': None,
|
|
1805
|
+
'side': side,
|
|
1806
|
+
'price': price,
|
|
1807
|
+
'stopPrice': triggerPrice,
|
|
1808
|
+
'triggerPrice': triggerPrice,
|
|
1809
|
+
'cost': cost,
|
|
1810
|
+
'amount': amount,
|
|
1811
|
+
'filled': None,
|
|
1812
|
+
'remaining': None,
|
|
1813
|
+
'average': None,
|
|
1814
|
+
'trades': transactions,
|
|
1815
|
+
'fee': None,
|
|
1816
|
+
'info': order,
|
|
1817
|
+
}, market)
|
|
1818
|
+
|
|
1819
|
+
async def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
1820
|
+
"""
|
|
1821
|
+
fetches information on multiple canceled orders made by the user
|
|
1822
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#1d2524dd-ae6d-403a-a067-77b50d13fbe5 # margin
|
|
1823
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#a51be1d0-af5f-44e4-99d7-f7b04c6067d0 # spot canceled orders
|
|
1824
|
+
:param str symbol: unified market symbol of the market orders were made in
|
|
1825
|
+
:param int [since]: timestamp in ms of the earliest order, default is None
|
|
1826
|
+
:param int [limit]: max number of orders to return, default is None
|
|
1827
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1828
|
+
:param str [params.marginMode]: set to "isolated" for margin orders
|
|
1829
|
+
:returns dict: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
|
1830
|
+
"""
|
|
1831
|
+
await self.load_markets()
|
|
1832
|
+
marginMode = None
|
|
1833
|
+
marginMode, params = self.handle_margin_mode_and_params('fetchOrders', params)
|
|
1834
|
+
if marginMode == 'cross':
|
|
1835
|
+
raise BadRequest(self.id + ' only supports isolated margin')
|
|
1836
|
+
if limit is None:
|
|
1837
|
+
limit = 100
|
|
1838
|
+
isSpot = (marginMode != 'isolated')
|
|
1839
|
+
if symbol is not None:
|
|
1840
|
+
marketInner = self.market(symbol)
|
|
1841
|
+
symbol = marketInner['symbol']
|
|
1842
|
+
request: dict = {
|
|
1843
|
+
'limit': limit,
|
|
1844
|
+
}
|
|
1845
|
+
request['offset'] = limit if (since is not None) else 0
|
|
1846
|
+
request['limit'] = limit
|
|
1847
|
+
market = None
|
|
1848
|
+
if symbol is not None:
|
|
1849
|
+
market = self.market(symbol)
|
|
1850
|
+
response = None
|
|
1851
|
+
if isSpot:
|
|
1852
|
+
response = await self.privatePostUserCancelledOrders(self.extend(request, params))
|
|
1853
|
+
#
|
|
1854
|
+
# [
|
|
1855
|
+
# {
|
|
1856
|
+
# "order_id": "27056153840",
|
|
1857
|
+
# "client_id": "0",
|
|
1858
|
+
# "created": "1653428646",
|
|
1859
|
+
# "type": "buy",
|
|
1860
|
+
# "pair": "BTC_USDT",
|
|
1861
|
+
# "quantity": "0.1",
|
|
1862
|
+
# "price": "10",
|
|
1863
|
+
# "amount": "1"
|
|
1864
|
+
# }
|
|
1865
|
+
# ]
|
|
1866
|
+
#
|
|
1867
|
+
params = self.extend(params, {
|
|
1868
|
+
'status': 'canceled',
|
|
1869
|
+
})
|
|
1870
|
+
return self.parse_orders(response, market, since, limit, params)
|
|
1871
|
+
else:
|
|
1872
|
+
responseSwap = await self.privatePostMarginUserOrderHistory(self.extend(request, params))
|
|
1873
|
+
#
|
|
1874
|
+
# {
|
|
1875
|
+
# "items": [
|
|
1876
|
+
# {
|
|
1877
|
+
# "event_id": "692862104574106858",
|
|
1878
|
+
# "event_time": "1694116400173489405",
|
|
1879
|
+
# "event_type": "OrderCancelStarted",
|
|
1880
|
+
# "order_id": "692862104561289319",
|
|
1881
|
+
# "order_type": "stop_limit_sell",
|
|
1882
|
+
# "order_status": "cancel_started",
|
|
1883
|
+
# "trade_id": "0",
|
|
1884
|
+
# "trade_type":"",
|
|
1885
|
+
# "trade_quantity": "0",
|
|
1886
|
+
# "trade_price": "0",
|
|
1887
|
+
# "pair": "ADA_USDT",
|
|
1888
|
+
# "quantity": "12",
|
|
1889
|
+
# "price": "0.23",
|
|
1890
|
+
# "stop_price": "0.22",
|
|
1891
|
+
# "distance": "0"
|
|
1892
|
+
# }
|
|
1893
|
+
# ...
|
|
1894
|
+
# ]
|
|
1895
|
+
# }
|
|
1896
|
+
#
|
|
1897
|
+
items = self.safe_value(responseSwap, 'items')
|
|
1898
|
+
orders = self.parse_orders(items, market, since, limit, params)
|
|
1899
|
+
result = []
|
|
1900
|
+
for i in range(0, len(orders)):
|
|
1901
|
+
order = orders[i]
|
|
1902
|
+
if order['status'] == 'canceled':
|
|
1903
|
+
result.append(order)
|
|
1904
|
+
return result
|
|
1905
|
+
|
|
1906
|
+
async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
|
|
1907
|
+
"""
|
|
1908
|
+
*margin only* edit a trade order
|
|
1909
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#f27ee040-c75f-4b59-b608-d05bd45b7899 # margin
|
|
1910
|
+
:param str id: order id
|
|
1911
|
+
:param str symbol: unified CCXT market symbol
|
|
1912
|
+
:param str type: not used by exmo editOrder
|
|
1913
|
+
:param str side: not used by exmo editOrder
|
|
1914
|
+
:param float [amount]: how much of the currency you want to trade in units of the base currency
|
|
1915
|
+
:param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
|
|
1916
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1917
|
+
:param float [params.triggerPrice]: stop price for stop-market and stop-limit orders
|
|
1918
|
+
:param str params['marginMode']: must be set to isolated
|
|
1919
|
+
*
|
|
1920
|
+
* EXCHANGE SPECIFIC PARAMETERS
|
|
1921
|
+
:param int [params.distance]: distance for trailing stop orders
|
|
1922
|
+
:param int [params.expire]: expiration timestamp in UTC timezone for the order. order will not be expired if expire is 0
|
|
1923
|
+
:param str [params.comment]: optional comment for order. up to 50 latin symbols, whitespaces, underscores
|
|
1924
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
1925
|
+
"""
|
|
1926
|
+
await self.load_markets()
|
|
1927
|
+
market = self.market(symbol)
|
|
1928
|
+
marginMode = None
|
|
1929
|
+
marginMode, params = self.handle_margin_mode_and_params('editOrder', params)
|
|
1930
|
+
if marginMode != 'isolated':
|
|
1931
|
+
raise BadRequest(self.id + ' editOrder() can only be used for isolated margin orders')
|
|
1932
|
+
triggerPrice = self.safe_number_n(params, ['triggerPrice', 'stopPrice', 'stop_price'])
|
|
1933
|
+
params = self.omit(params, ['triggerPrice', 'stopPrice'])
|
|
1934
|
+
request: dict = {
|
|
1935
|
+
'order_id': id, # id of the open order
|
|
1936
|
+
}
|
|
1937
|
+
if amount is not None:
|
|
1938
|
+
request['quantity'] = amount
|
|
1939
|
+
if price is not None:
|
|
1940
|
+
request['price'] = self.price_to_precision(market['symbol'], price)
|
|
1941
|
+
if triggerPrice is not None:
|
|
1942
|
+
request['stop_price'] = self.price_to_precision(market['symbol'], triggerPrice)
|
|
1943
|
+
response = await self.privatePostMarginUserOrderUpdate(self.extend(request, params))
|
|
1944
|
+
return self.parse_order(response)
|
|
1945
|
+
|
|
1946
|
+
async def fetch_deposit_address(self, code: str, params={}):
|
|
1947
|
+
"""
|
|
1948
|
+
fetch the deposit address for a currency associated with self account
|
|
1949
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#c8f9ced9-7ab6-4383-a6a4-bc54469ba60e
|
|
1950
|
+
:param str code: unified currency code
|
|
1951
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1952
|
+
:returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
|
|
1953
|
+
"""
|
|
1954
|
+
await self.load_markets()
|
|
1955
|
+
response = await self.privatePostDepositAddress(params)
|
|
1956
|
+
#
|
|
1957
|
+
# {
|
|
1958
|
+
# "TRX":"TBnwrf4ZdoYXE3C8L2KMs7YPSL3fg6q6V9",
|
|
1959
|
+
# "USDTTRC20":"TBnwrf4ZdoYXE3C8L2KMs7YPSL3fg6q6V9"
|
|
1960
|
+
# }
|
|
1961
|
+
#
|
|
1962
|
+
depositAddress = self.safe_string(response, code)
|
|
1963
|
+
address = None
|
|
1964
|
+
tag = None
|
|
1965
|
+
if depositAddress:
|
|
1966
|
+
addressAndTag = depositAddress.split(',')
|
|
1967
|
+
address = addressAndTag[0]
|
|
1968
|
+
numParts = len(addressAndTag)
|
|
1969
|
+
if numParts > 1:
|
|
1970
|
+
tag = addressAndTag[1]
|
|
1971
|
+
self.check_address(address)
|
|
1972
|
+
return {
|
|
1973
|
+
'currency': code,
|
|
1974
|
+
'address': address,
|
|
1975
|
+
'tag': tag,
|
|
1976
|
+
'network': None,
|
|
1977
|
+
'info': response,
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
def get_market_from_trades(self, trades):
|
|
1981
|
+
tradesBySymbol = self.index_by(trades, 'pair')
|
|
1982
|
+
symbols = list(tradesBySymbol.keys())
|
|
1983
|
+
numSymbols = len(symbols)
|
|
1984
|
+
if numSymbols == 1:
|
|
1985
|
+
return self.markets[symbols[0]]
|
|
1986
|
+
return None
|
|
1987
|
+
|
|
1988
|
+
async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
|
|
1989
|
+
"""
|
|
1990
|
+
make a withdrawal
|
|
1991
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#3ab9c34d-ad58-4f87-9c57-2e2ea88a8325
|
|
1992
|
+
:param str code: unified currency code
|
|
1993
|
+
:param float amount: the amount to withdraw
|
|
1994
|
+
:param str address: the address to withdraw to
|
|
1995
|
+
:param str tag:
|
|
1996
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
1997
|
+
:returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
1998
|
+
"""
|
|
1999
|
+
tag, params = self.handle_withdraw_tag_and_params(tag, params)
|
|
2000
|
+
await self.load_markets()
|
|
2001
|
+
currency = self.currency(code)
|
|
2002
|
+
request: dict = {
|
|
2003
|
+
'amount': amount,
|
|
2004
|
+
'currency': currency['id'],
|
|
2005
|
+
'address': address,
|
|
2006
|
+
}
|
|
2007
|
+
if tag is not None:
|
|
2008
|
+
request['invoice'] = tag
|
|
2009
|
+
networks = self.safe_value(self.options, 'networks', {})
|
|
2010
|
+
network = self.safe_string_upper(params, 'network') # self line allows the user to specify either ERC20 or ETH
|
|
2011
|
+
network = self.safe_string(networks, network, network) # handle ERC20>ETH alias
|
|
2012
|
+
if network is not None:
|
|
2013
|
+
request['transport'] = network
|
|
2014
|
+
params = self.omit(params, 'network')
|
|
2015
|
+
response = await self.privatePostWithdrawCrypt(self.extend(request, params))
|
|
2016
|
+
return self.parse_transaction(response, currency)
|
|
2017
|
+
|
|
2018
|
+
def parse_transaction_status(self, status: Str):
|
|
2019
|
+
statuses: dict = {
|
|
2020
|
+
'transferred': 'ok',
|
|
2021
|
+
'paid': 'ok',
|
|
2022
|
+
'pending': 'pending',
|
|
2023
|
+
'processing': 'pending',
|
|
2024
|
+
'verifying': 'pending',
|
|
2025
|
+
}
|
|
2026
|
+
return self.safe_string(statuses, status, status)
|
|
2027
|
+
|
|
2028
|
+
def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
|
|
2029
|
+
#
|
|
2030
|
+
# fetchDepositsWithdrawals
|
|
2031
|
+
#
|
|
2032
|
+
# {
|
|
2033
|
+
# "dt": 1461841192,
|
|
2034
|
+
# "type": "deposit",
|
|
2035
|
+
# "curr": "RUB",
|
|
2036
|
+
# "status": "processing",
|
|
2037
|
+
# "provider": "Qiwi(LA) [12345]",
|
|
2038
|
+
# "amount": "1",
|
|
2039
|
+
# "account": "",
|
|
2040
|
+
# "txid": "ec46f784ad976fd7f7539089d1a129fe46...",
|
|
2041
|
+
# }
|
|
2042
|
+
#
|
|
2043
|
+
# fetchWithdrawals
|
|
2044
|
+
#
|
|
2045
|
+
# {
|
|
2046
|
+
# "operation_id": 47412538520634344,
|
|
2047
|
+
# "created": 1573760013,
|
|
2048
|
+
# "updated": 1573760013,
|
|
2049
|
+
# "type": "withdraw",
|
|
2050
|
+
# "currency": "DOGE",
|
|
2051
|
+
# "status": "Paid",
|
|
2052
|
+
# "amount": "300",
|
|
2053
|
+
# "provider": "DOGE",
|
|
2054
|
+
# "commission": "0",
|
|
2055
|
+
# "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
|
|
2056
|
+
# "order_id": 69670170,
|
|
2057
|
+
# "provider_type": "crypto",
|
|
2058
|
+
# "crypto_address": "DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
|
|
2059
|
+
# "card_number": "",
|
|
2060
|
+
# "wallet_address": "",
|
|
2061
|
+
# "email": "",
|
|
2062
|
+
# "phone": "",
|
|
2063
|
+
# "extra": {
|
|
2064
|
+
# "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
|
|
2065
|
+
# "confirmations": null,
|
|
2066
|
+
# "excode": "",
|
|
2067
|
+
# "invoice": ""
|
|
2068
|
+
# },
|
|
2069
|
+
# "error": ""
|
|
2070
|
+
# }
|
|
2071
|
+
#
|
|
2072
|
+
# withdraw
|
|
2073
|
+
#
|
|
2074
|
+
# {
|
|
2075
|
+
# "result": True,
|
|
2076
|
+
# "error": "",
|
|
2077
|
+
# "task_id": 11775077
|
|
2078
|
+
# }
|
|
2079
|
+
#
|
|
2080
|
+
timestamp = self.safe_timestamp_2(transaction, 'dt', 'created')
|
|
2081
|
+
amountString = self.safe_string(transaction, 'amount')
|
|
2082
|
+
if amountString is not None:
|
|
2083
|
+
amountString = Precise.string_abs(amountString)
|
|
2084
|
+
txid = self.safe_string(transaction, 'txid')
|
|
2085
|
+
if txid is None:
|
|
2086
|
+
extra = self.safe_value(transaction, 'extra', {})
|
|
2087
|
+
extraTxid = self.safe_string(extra, 'txid')
|
|
2088
|
+
if extraTxid != '':
|
|
2089
|
+
txid = extraTxid
|
|
2090
|
+
type = self.safe_string(transaction, 'type')
|
|
2091
|
+
currencyId = self.safe_string_2(transaction, 'curr', 'currency')
|
|
2092
|
+
code = self.safe_currency_code(currencyId, currency)
|
|
2093
|
+
address = None
|
|
2094
|
+
comment = None
|
|
2095
|
+
account = self.safe_string(transaction, 'account')
|
|
2096
|
+
if type == 'deposit':
|
|
2097
|
+
comment = account
|
|
2098
|
+
elif type == 'withdrawal':
|
|
2099
|
+
address = account
|
|
2100
|
+
if address is not None:
|
|
2101
|
+
parts = address.split(':')
|
|
2102
|
+
numParts = len(parts)
|
|
2103
|
+
if numParts == 2:
|
|
2104
|
+
address = self.safe_string(parts, 1)
|
|
2105
|
+
address = address.replace(' ', '')
|
|
2106
|
+
fee = {
|
|
2107
|
+
'currency': None,
|
|
2108
|
+
'cost': None,
|
|
2109
|
+
'rate': None,
|
|
2110
|
+
}
|
|
2111
|
+
# fixed funding fees only(for now)
|
|
2112
|
+
if not self.fees['transaction']['percentage']:
|
|
2113
|
+
key = 'withdraw' if (type == 'withdrawal') else 'deposit'
|
|
2114
|
+
feeCost = self.safe_string(transaction, 'commission')
|
|
2115
|
+
if feeCost is None:
|
|
2116
|
+
transactionFees = self.safe_value(self.options, 'transactionFees', {})
|
|
2117
|
+
codeFees = self.safe_value(transactionFees, code, {})
|
|
2118
|
+
feeCost = self.safe_string(codeFees, key)
|
|
2119
|
+
# users don't pay for cashbacks, no fees for that
|
|
2120
|
+
provider = self.safe_string(transaction, 'provider')
|
|
2121
|
+
if provider == 'cashback':
|
|
2122
|
+
feeCost = '0'
|
|
2123
|
+
if feeCost is not None:
|
|
2124
|
+
# withdrawal amount includes the fee
|
|
2125
|
+
if type == 'withdrawal':
|
|
2126
|
+
amountString = Precise.string_sub(amountString, feeCost)
|
|
2127
|
+
fee['cost'] = self.parse_number(feeCost)
|
|
2128
|
+
fee['currency'] = code
|
|
2129
|
+
return {
|
|
2130
|
+
'info': transaction,
|
|
2131
|
+
'id': self.safe_string_2(transaction, 'order_id', 'task_id'),
|
|
2132
|
+
'txid': txid,
|
|
2133
|
+
'type': type,
|
|
2134
|
+
'currency': code,
|
|
2135
|
+
'network': self.safe_string(transaction, 'provider'),
|
|
2136
|
+
'amount': self.parse_number(amountString),
|
|
2137
|
+
'status': self.parse_transaction_status(self.safe_string_lower(transaction, 'status')),
|
|
2138
|
+
'timestamp': timestamp,
|
|
2139
|
+
'datetime': self.iso8601(timestamp),
|
|
2140
|
+
'address': address,
|
|
2141
|
+
'addressFrom': None,
|
|
2142
|
+
'addressTo': address,
|
|
2143
|
+
'tag': None,
|
|
2144
|
+
'tagFrom': None,
|
|
2145
|
+
'tagTo': None,
|
|
2146
|
+
'updated': self.safe_timestamp(transaction, 'updated'),
|
|
2147
|
+
'comment': comment,
|
|
2148
|
+
'internal': None,
|
|
2149
|
+
'fee': fee,
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
|
2153
|
+
"""
|
|
2154
|
+
fetch history of deposits and withdrawals
|
|
2155
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#31e69a33-4849-4e6a-b4b4-6d574238f6a7
|
|
2156
|
+
:param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
|
|
2157
|
+
:param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
|
|
2158
|
+
:param int [limit]: max number of deposit/withdrawals to return, default is None
|
|
2159
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
2160
|
+
:returns dict: a list of `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
2161
|
+
"""
|
|
2162
|
+
await self.load_markets()
|
|
2163
|
+
request: dict = {}
|
|
2164
|
+
if since is not None:
|
|
2165
|
+
request['date'] = self.parse_to_int(since / 1000)
|
|
2166
|
+
currency = None
|
|
2167
|
+
if code is not None:
|
|
2168
|
+
currency = self.currency(code)
|
|
2169
|
+
response = await self.privatePostWalletHistory(self.extend(request, params))
|
|
2170
|
+
#
|
|
2171
|
+
# {
|
|
2172
|
+
# "result": True,
|
|
2173
|
+
# "error": "",
|
|
2174
|
+
# "begin": "1493942400",
|
|
2175
|
+
# "end": "1494028800",
|
|
2176
|
+
# "history": [
|
|
2177
|
+
# {
|
|
2178
|
+
# "dt": 1461841192,
|
|
2179
|
+
# "type": "deposit",
|
|
2180
|
+
# "curr": "RUB",
|
|
2181
|
+
# "status": "processing",
|
|
2182
|
+
# "provider": "Qiwi(LA) [12345]",
|
|
2183
|
+
# "amount": "1",
|
|
2184
|
+
# "account": "",
|
|
2185
|
+
# "txid": "ec46f784ad976fd7f7539089d1a129fe46...",
|
|
2186
|
+
# },
|
|
2187
|
+
# {
|
|
2188
|
+
# "dt": 1463414785,
|
|
2189
|
+
# "type": "withdrawal",
|
|
2190
|
+
# "curr": "USD",
|
|
2191
|
+
# "status": "paid",
|
|
2192
|
+
# "provider": "EXCODE",
|
|
2193
|
+
# "amount": "-1",
|
|
2194
|
+
# "account": "EX-CODE_19371_USDda...",
|
|
2195
|
+
# "txid": "",
|
|
2196
|
+
# },
|
|
2197
|
+
# ],
|
|
2198
|
+
# }
|
|
2199
|
+
#
|
|
2200
|
+
return self.parse_transactions(response['history'], currency, since, limit)
|
|
2201
|
+
|
|
2202
|
+
async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
|
2203
|
+
"""
|
|
2204
|
+
fetch all withdrawals made from an account
|
|
2205
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#97f1becd-7aad-4e0e-babe-7bbe09e33706
|
|
2206
|
+
:param str code: unified currency code
|
|
2207
|
+
:param int [since]: the earliest time in ms to fetch withdrawals for
|
|
2208
|
+
:param int [limit]: the maximum number of withdrawals structures to retrieve
|
|
2209
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
2210
|
+
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
2211
|
+
"""
|
|
2212
|
+
await self.load_markets()
|
|
2213
|
+
currency = None
|
|
2214
|
+
request: dict = {
|
|
2215
|
+
'type': 'withdraw',
|
|
2216
|
+
}
|
|
2217
|
+
if limit is not None:
|
|
2218
|
+
request['limit'] = limit # default: 100, maximum: 100
|
|
2219
|
+
if code is not None:
|
|
2220
|
+
currency = self.currency(code)
|
|
2221
|
+
request['currency'] = currency['id']
|
|
2222
|
+
response = await self.privatePostWalletOperations(self.extend(request, params))
|
|
2223
|
+
#
|
|
2224
|
+
# {
|
|
2225
|
+
# "items": [
|
|
2226
|
+
# {
|
|
2227
|
+
# "operation_id": 47412538520634344,
|
|
2228
|
+
# "created": 1573760013,
|
|
2229
|
+
# "updated": 1573760013,
|
|
2230
|
+
# "type": "withdraw",
|
|
2231
|
+
# "currency": "DOGE",
|
|
2232
|
+
# "status": "Paid",
|
|
2233
|
+
# "amount": "300",
|
|
2234
|
+
# "provider": "DOGE",
|
|
2235
|
+
# "commission": "0",
|
|
2236
|
+
# "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
|
|
2237
|
+
# "order_id": 69670170,
|
|
2238
|
+
# "extra": {
|
|
2239
|
+
# "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
|
|
2240
|
+
# "excode": "",
|
|
2241
|
+
# "invoice": ""
|
|
2242
|
+
# },
|
|
2243
|
+
# "error": ""
|
|
2244
|
+
# },
|
|
2245
|
+
# ],
|
|
2246
|
+
# "count": 23
|
|
2247
|
+
# }
|
|
2248
|
+
#
|
|
2249
|
+
items = self.safe_list(response, 'items', [])
|
|
2250
|
+
return self.parse_transactions(items, currency, since, limit)
|
|
2251
|
+
|
|
2252
|
+
async def fetch_withdrawal(self, id: str, code: Str = None, params={}):
|
|
2253
|
+
"""
|
|
2254
|
+
fetch data on a currency withdrawal via the withdrawal id
|
|
2255
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#97f1becd-7aad-4e0e-babe-7bbe09e33706
|
|
2256
|
+
:param str id: withdrawal id
|
|
2257
|
+
:param str code: unified currency code of the currency withdrawn, default is None
|
|
2258
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
2259
|
+
:returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
2260
|
+
"""
|
|
2261
|
+
await self.load_markets()
|
|
2262
|
+
currency = None
|
|
2263
|
+
request: dict = {
|
|
2264
|
+
'order_id': id,
|
|
2265
|
+
'type': 'withdraw',
|
|
2266
|
+
}
|
|
2267
|
+
if code is not None:
|
|
2268
|
+
currency = self.currency(code)
|
|
2269
|
+
request['currency'] = currency['id']
|
|
2270
|
+
response = await self.privatePostWalletOperations(self.extend(request, params))
|
|
2271
|
+
#
|
|
2272
|
+
# {
|
|
2273
|
+
# "items": [
|
|
2274
|
+
# {
|
|
2275
|
+
# "operation_id": 47412538520634344,
|
|
2276
|
+
# "created": 1573760013,
|
|
2277
|
+
# "updated": 1573760013,
|
|
2278
|
+
# "type": "deposit",
|
|
2279
|
+
# "currency": "DOGE",
|
|
2280
|
+
# "status": "Paid",
|
|
2281
|
+
# "amount": "300",
|
|
2282
|
+
# "provider": "DOGE",
|
|
2283
|
+
# "commission": "0",
|
|
2284
|
+
# "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
|
|
2285
|
+
# "order_id": 69670170,
|
|
2286
|
+
# "extra": {
|
|
2287
|
+
# "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
|
|
2288
|
+
# "excode": "",
|
|
2289
|
+
# "invoice": ""
|
|
2290
|
+
# },
|
|
2291
|
+
# "error": ""
|
|
2292
|
+
# },
|
|
2293
|
+
# ],
|
|
2294
|
+
# "count": 23
|
|
2295
|
+
# }
|
|
2296
|
+
#
|
|
2297
|
+
items = self.safe_value(response, 'items', [])
|
|
2298
|
+
first = self.safe_dict(items, 0, {})
|
|
2299
|
+
return self.parse_transaction(first, currency)
|
|
2300
|
+
|
|
2301
|
+
async def fetch_deposit(self, id=None, code: Str = None, params={}):
|
|
2302
|
+
"""
|
|
2303
|
+
fetch information on a deposit
|
|
2304
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#97f1becd-7aad-4e0e-babe-7bbe09e33706
|
|
2305
|
+
:param str id: deposit id
|
|
2306
|
+
:param str code: unified currency code, default is None
|
|
2307
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
2308
|
+
:returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
2309
|
+
"""
|
|
2310
|
+
await self.load_markets()
|
|
2311
|
+
currency = None
|
|
2312
|
+
request: dict = {
|
|
2313
|
+
'order_id': id,
|
|
2314
|
+
'type': 'deposit',
|
|
2315
|
+
}
|
|
2316
|
+
if code is not None:
|
|
2317
|
+
currency = self.currency(code)
|
|
2318
|
+
request['currency'] = currency['id']
|
|
2319
|
+
response = await self.privatePostWalletOperations(self.extend(request, params))
|
|
2320
|
+
#
|
|
2321
|
+
# {
|
|
2322
|
+
# "items": [
|
|
2323
|
+
# {
|
|
2324
|
+
# "operation_id": 47412538520634344,
|
|
2325
|
+
# "created": 1573760013,
|
|
2326
|
+
# "updated": 1573760013,
|
|
2327
|
+
# "type": "deposit",
|
|
2328
|
+
# "currency": "DOGE",
|
|
2329
|
+
# "status": "Paid",
|
|
2330
|
+
# "amount": "300",
|
|
2331
|
+
# "provider": "DOGE",
|
|
2332
|
+
# "commission": "0",
|
|
2333
|
+
# "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
|
|
2334
|
+
# "order_id": 69670170,
|
|
2335
|
+
# "extra": {
|
|
2336
|
+
# "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
|
|
2337
|
+
# "excode": "",
|
|
2338
|
+
# "invoice": ""
|
|
2339
|
+
# },
|
|
2340
|
+
# "error": ""
|
|
2341
|
+
# },
|
|
2342
|
+
# ],
|
|
2343
|
+
# "count": 23
|
|
2344
|
+
# }
|
|
2345
|
+
#
|
|
2346
|
+
items = self.safe_value(response, 'items', [])
|
|
2347
|
+
first = self.safe_dict(items, 0, {})
|
|
2348
|
+
return self.parse_transaction(first, currency)
|
|
2349
|
+
|
|
2350
|
+
async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
|
2351
|
+
"""
|
|
2352
|
+
fetch all deposits made to an account
|
|
2353
|
+
:see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#97f1becd-7aad-4e0e-babe-7bbe09e33706
|
|
2354
|
+
:param str code: unified currency code
|
|
2355
|
+
:param int [since]: the earliest time in ms to fetch deposits for
|
|
2356
|
+
:param int [limit]: the maximum number of deposits structures to retrieve
|
|
2357
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
2358
|
+
:returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
2359
|
+
"""
|
|
2360
|
+
await self.load_markets()
|
|
2361
|
+
currency = None
|
|
2362
|
+
request: dict = {
|
|
2363
|
+
'type': 'deposit',
|
|
2364
|
+
}
|
|
2365
|
+
if limit is not None:
|
|
2366
|
+
request['limit'] = limit # default: 100, maximum: 100
|
|
2367
|
+
if code is not None:
|
|
2368
|
+
currency = self.currency(code)
|
|
2369
|
+
request['currency'] = currency['id']
|
|
2370
|
+
response = await self.privatePostWalletOperations(self.extend(request, params))
|
|
2371
|
+
#
|
|
2372
|
+
# {
|
|
2373
|
+
# "items": [
|
|
2374
|
+
# {
|
|
2375
|
+
# "operation_id": 47412538520634344,
|
|
2376
|
+
# "created": 1573760013,
|
|
2377
|
+
# "updated": 1573760013,
|
|
2378
|
+
# "type": "deposit",
|
|
2379
|
+
# "currency": "DOGE",
|
|
2380
|
+
# "status": "Paid",
|
|
2381
|
+
# "amount": "300",
|
|
2382
|
+
# "provider": "DOGE",
|
|
2383
|
+
# "commission": "0",
|
|
2384
|
+
# "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
|
|
2385
|
+
# "order_id": 69670170,
|
|
2386
|
+
# "extra": {
|
|
2387
|
+
# "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
|
|
2388
|
+
# "excode": "",
|
|
2389
|
+
# "invoice": ""
|
|
2390
|
+
# },
|
|
2391
|
+
# "error": ""
|
|
2392
|
+
# },
|
|
2393
|
+
# ],
|
|
2394
|
+
# "count": 23
|
|
2395
|
+
# }
|
|
2396
|
+
#
|
|
2397
|
+
items = self.safe_list(response, 'items', [])
|
|
2398
|
+
return self.parse_transactions(items, currency, since, limit)
|
|
2399
|
+
|
|
2400
|
+
def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
|
|
2401
|
+
url = self.urls['api'][api] + '/'
|
|
2402
|
+
if api != 'web':
|
|
2403
|
+
url += self.version + '/'
|
|
2404
|
+
url += path
|
|
2405
|
+
if (api == 'public') or (api == 'web'):
|
|
2406
|
+
if params:
|
|
2407
|
+
url += '?' + self.urlencode(params)
|
|
2408
|
+
elif api == 'private':
|
|
2409
|
+
self.check_required_credentials()
|
|
2410
|
+
nonce = self.nonce()
|
|
2411
|
+
body = self.urlencode(self.extend({'nonce': nonce}, params))
|
|
2412
|
+
headers = {
|
|
2413
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
2414
|
+
'Key': self.apiKey,
|
|
2415
|
+
'Sign': self.hmac(self.encode(body), self.encode(self.secret), hashlib.sha512),
|
|
2416
|
+
}
|
|
2417
|
+
return {'url': url, 'method': method, 'body': body, 'headers': headers}
|
|
2418
|
+
|
|
2419
|
+
def nonce(self):
|
|
2420
|
+
return self.milliseconds()
|
|
2421
|
+
|
|
2422
|
+
def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
|
|
2423
|
+
if response is None:
|
|
2424
|
+
return None # fallback to default error handler
|
|
2425
|
+
if ('error' in response) and not ('result' in response):
|
|
2426
|
+
# error: {
|
|
2427
|
+
# "code": "140434",
|
|
2428
|
+
# "msg": "Your margin balance is not sufficient to place the order for '5 TON'. Please top up your margin wallet by "2.5 USDT"."
|
|
2429
|
+
# }
|
|
2430
|
+
#
|
|
2431
|
+
errorCode = self.safe_value(response, 'error', {})
|
|
2432
|
+
messageError = self.safe_string(errorCode, 'msg')
|
|
2433
|
+
code = self.safe_string(errorCode, 'code')
|
|
2434
|
+
feedback = self.id + ' ' + body
|
|
2435
|
+
self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
|
|
2436
|
+
self.throw_broadly_matched_exception(self.exceptions['broad'], messageError, feedback)
|
|
2437
|
+
raise ExchangeError(feedback)
|
|
2438
|
+
if ('result' in response) or ('errmsg' in response):
|
|
2439
|
+
#
|
|
2440
|
+
# {"result":false,"error":"Error 50052: Insufficient funds"}
|
|
2441
|
+
# {"s":"error","errmsg":"strconv.ParseInt: parsing \"\": invalid syntax"}
|
|
2442
|
+
#
|
|
2443
|
+
success = self.safe_bool(response, 'result', False)
|
|
2444
|
+
if isinstance(success, str):
|
|
2445
|
+
if (success == 'true') or (success == '1'):
|
|
2446
|
+
success = True
|
|
2447
|
+
else:
|
|
2448
|
+
success = False
|
|
2449
|
+
if not success:
|
|
2450
|
+
code = None
|
|
2451
|
+
message = self.safe_string_2(response, 'error', 'errmsg')
|
|
2452
|
+
errorParts = message.split(':')
|
|
2453
|
+
numParts = len(errorParts)
|
|
2454
|
+
if numParts > 1:
|
|
2455
|
+
errorSubParts = errorParts[0].split(' ')
|
|
2456
|
+
numSubParts = len(errorSubParts)
|
|
2457
|
+
code = errorSubParts[1] if (numSubParts > 1) else errorSubParts[0]
|
|
2458
|
+
feedback = self.id + ' ' + body
|
|
2459
|
+
self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
|
|
2460
|
+
self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
|
|
2461
|
+
raise ExchangeError(feedback)
|
|
2462
|
+
return None
|