ccxt-ir 4.3.46.0.2__py2.py3-none-any.whl → 4.5.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccxt/__init__.py +39 -35
- ccxt/abantether.py +9 -9
- ccxt/abstract/alpaca.py +4 -0
- ccxt/abstract/apex.py +31 -0
- ccxt/abstract/bigone.py +1 -1
- ccxt/abstract/binance.py +106 -48
- ccxt/abstract/binancecoinm.py +106 -48
- ccxt/abstract/binanceus.py +141 -83
- ccxt/abstract/binanceusdm.py +106 -48
- ccxt/abstract/bingx.py +50 -1
- ccxt/abstract/bitbank.py +5 -0
- ccxt/abstract/bitfinex.py +136 -65
- ccxt/abstract/bitflyer.py +1 -0
- ccxt/abstract/bitget.py +67 -0
- ccxt/abstract/bitmart.py +19 -1
- ccxt/abstract/bitopro.py +1 -0
- ccxt/abstract/bitrue.py +68 -68
- ccxt/abstract/bitstamp.py +1 -0
- ccxt/abstract/blofin.py +30 -0
- ccxt/abstract/btcbox.py +2 -0
- ccxt/abstract/bybit.py +28 -13
- ccxt/abstract/cex.py +28 -29
- ccxt/abstract/coinbaseexchange.py +1 -0
- ccxt/abstract/coinbaseinternational.py +1 -1
- ccxt/abstract/cryptocom.py +16 -0
- ccxt/abstract/cryptomus.py +20 -0
- ccxt/abstract/defx.py +69 -0
- ccxt/abstract/deribit.py +1 -0
- ccxt/abstract/derive.py +117 -0
- ccxt/abstract/digifinex.py +1 -0
- ccxt/abstract/ellipx.py +25 -0
- ccxt/abstract/foxbit.py +26 -0
- ccxt/abstract/gate.py +19 -0
- ccxt/abstract/gateio.py +19 -0
- ccxt/abstract/gemini.py +1 -0
- ccxt/abstract/hibachi.py +26 -0
- ccxt/abstract/hyperliquid.py +1 -1
- ccxt/abstract/independentreserve.py +6 -0
- ccxt/abstract/kraken.py +1 -0
- ccxt/abstract/krakenfutures.py +4 -0
- ccxt/abstract/kucoin.py +10 -0
- ccxt/abstract/kucoinfutures.py +18 -0
- ccxt/abstract/lbank.py +2 -1
- ccxt/abstract/luno.py +1 -0
- ccxt/abstract/mexc.py +2 -0
- ccxt/abstract/modetrade.py +119 -0
- ccxt/abstract/myokx.py +349 -0
- ccxt/abstract/oceanex.py +5 -0
- ccxt/abstract/okx.py +25 -0
- ccxt/abstract/okxus.py +349 -0
- ccxt/abstract/onetrading.py +0 -12
- ccxt/abstract/paradex.py +23 -0
- ccxt/abstract/phemex.py +2 -0
- ccxt/abstract/poloniex.py +36 -0
- ccxt/abstract/tradeogre.py +3 -1
- ccxt/abstract/upbit.py +51 -34
- ccxt/abstract/whitebit.py +16 -0
- ccxt/abstract/woo.py +64 -6
- ccxt/abstract/xt.py +10 -5
- ccxt/afratether.py +7 -7
- ccxt/alpaca.py +828 -51
- ccxt/apex.py +1875 -0
- ccxt/arzinja.py +7 -7
- ccxt/arzplus.py +9 -9
- ccxt/ascendex.py +501 -306
- ccxt/async_support/__init__.py +39 -35
- ccxt/async_support/abantether.py +10 -10
- ccxt/async_support/afratether.py +9 -9
- ccxt/async_support/alpaca.py +828 -51
- ccxt/async_support/apex.py +1875 -0
- ccxt/async_support/arzinja.py +10 -10
- ccxt/async_support/arzplus.py +12 -12
- ccxt/async_support/ascendex.py +502 -306
- ccxt/async_support/base/exchange.py +303 -89
- ccxt/async_support/base/ws/cache.py +9 -3
- ccxt/async_support/base/ws/client.py +173 -38
- ccxt/async_support/base/ws/future.py +25 -37
- ccxt/async_support/bequant.py +5 -3
- ccxt/async_support/bigone.py +279 -144
- ccxt/async_support/binance.py +2347 -1158
- ccxt/async_support/binancecoinm.py +9 -3
- ccxt/async_support/binanceus.py +17 -3
- ccxt/async_support/binanceusdm.py +9 -4
- ccxt/async_support/bingx.py +2962 -920
- ccxt/async_support/bit2c.py +147 -27
- ccxt/async_support/bitbank.py +151 -23
- ccxt/async_support/bitbns.py +104 -30
- ccxt/async_support/bitfinex.py +3291 -1113
- ccxt/async_support/bitflyer.py +202 -27
- ccxt/async_support/bitget.py +3683 -1538
- ccxt/async_support/bithumb.py +195 -38
- ccxt/async_support/bitimen.py +12 -12
- ccxt/async_support/bitir.py +38 -38
- ccxt/async_support/bitmart.py +1288 -350
- ccxt/async_support/bitmex.py +260 -75
- ccxt/async_support/bitopro.py +262 -62
- ccxt/async_support/bitpin.py +17 -16
- ccxt/async_support/bitrue.py +459 -290
- ccxt/async_support/bitso.py +199 -54
- ccxt/async_support/bitstamp.py +230 -96
- ccxt/async_support/bitteam.py +167 -25
- ccxt/async_support/{huobijp.py → bittrade.py} +158 -30
- ccxt/async_support/bitvavo.py +213 -49
- ccxt/async_support/blockchaincom.py +160 -46
- ccxt/async_support/blofin.py +502 -120
- ccxt/async_support/btcalpha.py +169 -31
- ccxt/async_support/btcbox.py +292 -23
- ccxt/async_support/btcmarkets.py +211 -58
- ccxt/async_support/btcturk.py +161 -38
- ccxt/async_support/bybit.py +1775 -1030
- ccxt/async_support/cex.py +1440 -1303
- ccxt/async_support/coinbase.py +724 -212
- ccxt/async_support/coinbaseadvanced.py +2 -1
- ccxt/async_support/coinbaseexchange.py +388 -89
- ccxt/async_support/coinbaseinternational.py +412 -57
- ccxt/async_support/coincatch.py +177 -78
- ccxt/async_support/coincheck.py +135 -19
- ccxt/async_support/coinex.py +606 -232
- ccxt/async_support/coinmate.py +189 -63
- ccxt/async_support/coinmetro.py +195 -54
- ccxt/async_support/coinone.py +158 -51
- ccxt/async_support/coinsph.py +336 -61
- ccxt/async_support/coinspot.py +151 -52
- ccxt/async_support/cryptocom.py +661 -111
- ccxt/async_support/cryptomus.py +1137 -0
- ccxt/async_support/defx.py +2071 -0
- ccxt/async_support/delta.py +299 -99
- ccxt/async_support/deribit.py +348 -126
- ccxt/async_support/derive.py +2572 -0
- ccxt/async_support/digifinex.py +430 -214
- ccxt/async_support/ellipx.py +2029 -0
- ccxt/async_support/eterex.py +10 -10
- ccxt/async_support/excoino.py +31 -31
- ccxt/async_support/exir.py +14 -14
- ccxt/async_support/exmo.py +344 -131
- ccxt/async_support/exnovin.py +10 -10
- ccxt/async_support/farhadexchange.py +12 -12
- ccxt/async_support/fmfwio.py +2 -1
- ccxt/async_support/foxbit.py +1935 -0
- ccxt/async_support/gate.py +1351 -529
- ccxt/async_support/gateio.py +2 -1
- ccxt/async_support/gemini.py +144 -39
- ccxt/async_support/hashkey.py +152 -109
- ccxt/async_support/hibachi.py +2080 -0
- ccxt/async_support/hitbtc.py +395 -167
- ccxt/async_support/hitobit.py +12 -12
- ccxt/async_support/hollaex.py +307 -119
- ccxt/async_support/htx.py +851 -383
- ccxt/async_support/huobi.py +2 -1
- ccxt/async_support/hyperliquid.py +1848 -536
- ccxt/async_support/independentreserve.py +288 -15
- ccxt/async_support/indodax.py +190 -33
- ccxt/async_support/jibitex.py +12 -12
- ccxt/async_support/kraken.py +795 -351
- ccxt/async_support/krakenfutures.py +214 -62
- ccxt/async_support/kucoin.py +715 -396
- ccxt/async_support/kucoinfutures.py +652 -89
- ccxt/async_support/latoken.py +217 -113
- ccxt/async_support/lbank.py +425 -97
- ccxt/async_support/luno.py +382 -35
- ccxt/async_support/mercado.py +113 -6
- ccxt/async_support/mexc.py +874 -437
- ccxt/async_support/modetrade.py +2818 -0
- ccxt/async_support/myokx.py +54 -0
- ccxt/async_support/ndax.py +221 -64
- ccxt/async_support/nobitex.py +31 -37
- ccxt/async_support/novadax.py +190 -34
- ccxt/async_support/oceanex.py +217 -28
- ccxt/async_support/okcoin.py +253 -145
- ccxt/async_support/okexchange.py +11 -11
- ccxt/async_support/okx.py +1088 -351
- ccxt/async_support/okxus.py +54 -0
- ccxt/async_support/ompfinex.py +25 -24
- ccxt/async_support/onetrading.py +213 -392
- ccxt/async_support/oxfun.py +245 -166
- ccxt/async_support/p2b.py +151 -29
- ccxt/async_support/paradex.py +562 -49
- ccxt/async_support/paymium.py +82 -19
- ccxt/async_support/phemex.py +713 -172
- ccxt/async_support/poloniex.py +1602 -283
- ccxt/async_support/probit.py +224 -95
- ccxt/async_support/ramzinex.py +30 -27
- ccxt/async_support/sarmayex.py +9 -9
- ccxt/async_support/sarrafex.py +13 -13
- ccxt/async_support/tabdeal.py +14 -13
- ccxt/async_support/tetherland.py +9 -9
- ccxt/async_support/timex.py +210 -51
- ccxt/async_support/tokocrypto.py +167 -47
- ccxt/async_support/tradeogre.py +266 -31
- ccxt/async_support/twox.py +9 -9
- ccxt/async_support/ubitex.py +12 -12
- ccxt/async_support/upbit.py +568 -165
- ccxt/async_support/vertex.py +160 -32
- ccxt/async_support/wallex.py +12 -12
- ccxt/async_support/wavesexchange.py +165 -30
- ccxt/async_support/whitebit.py +975 -127
- ccxt/async_support/woo.py +1918 -1016
- ccxt/async_support/woofipro.py +433 -141
- ccxt/async_support/xt.py +649 -193
- ccxt/async_support/yobit.py +195 -70
- ccxt/async_support/zaif.py +91 -15
- ccxt/async_support/zonda.py +151 -36
- ccxt/base/decimal_to_precision.py +14 -10
- ccxt/base/errors.py +49 -18
- ccxt/base/exchange.py +1556 -450
- ccxt/base/precise.py +10 -0
- ccxt/base/types.py +114 -6
- ccxt/bequant.py +5 -3
- ccxt/bigone.py +279 -144
- ccxt/binance.py +2347 -1158
- ccxt/binancecoinm.py +9 -3
- ccxt/binanceus.py +17 -3
- ccxt/binanceusdm.py +9 -4
- ccxt/bingx.py +2962 -920
- ccxt/bit2c.py +147 -27
- ccxt/bitbank.py +151 -23
- ccxt/bitbns.py +104 -30
- ccxt/bitfinex.py +3290 -1113
- ccxt/bitflyer.py +202 -27
- ccxt/bitget.py +3683 -1538
- ccxt/bithumb.py +194 -38
- ccxt/bitimen.py +9 -9
- ccxt/bitir.py +35 -35
- ccxt/bitmart.py +1288 -350
- ccxt/bitmex.py +260 -75
- ccxt/bitopro.py +262 -62
- ccxt/bitpin.py +15 -14
- ccxt/bitrue.py +459 -290
- ccxt/bitso.py +199 -54
- ccxt/bitstamp.py +230 -96
- ccxt/bitteam.py +167 -25
- ccxt/{huobijp.py → bittrade.py} +158 -30
- ccxt/bitvavo.py +213 -49
- ccxt/blockchaincom.py +160 -46
- ccxt/blofin.py +502 -120
- ccxt/btcalpha.py +169 -31
- ccxt/btcbox.py +291 -23
- ccxt/btcmarkets.py +211 -58
- ccxt/btcturk.py +161 -38
- ccxt/bybit.py +1775 -1030
- ccxt/cex.py +1439 -1303
- ccxt/coinbase.py +724 -212
- ccxt/coinbaseadvanced.py +2 -1
- ccxt/coinbaseexchange.py +388 -89
- ccxt/coinbaseinternational.py +412 -57
- ccxt/coincatch.py +177 -78
- ccxt/coincheck.py +135 -19
- ccxt/coinex.py +606 -232
- ccxt/coinmate.py +189 -63
- ccxt/coinmetro.py +194 -54
- ccxt/coinone.py +158 -51
- ccxt/coinsph.py +336 -61
- ccxt/coinspot.py +151 -52
- ccxt/cryptocom.py +661 -111
- ccxt/cryptomus.py +1137 -0
- ccxt/defx.py +2070 -0
- ccxt/delta.py +299 -99
- ccxt/deribit.py +348 -126
- ccxt/derive.py +2571 -0
- ccxt/digifinex.py +430 -214
- ccxt/ellipx.py +2029 -0
- ccxt/eterex.py +7 -7
- ccxt/excoino.py +29 -29
- ccxt/exir.py +11 -11
- ccxt/exmo.py +343 -131
- ccxt/exnovin.py +8 -8
- ccxt/farhadexchange.py +10 -10
- ccxt/fmfwio.py +2 -1
- ccxt/foxbit.py +1935 -0
- ccxt/gate.py +1351 -529
- ccxt/gateio.py +2 -1
- ccxt/gemini.py +144 -39
- ccxt/hashkey.py +152 -109
- ccxt/hibachi.py +2079 -0
- ccxt/hitbtc.py +395 -167
- ccxt/hitobit.py +9 -9
- ccxt/hollaex.py +307 -119
- ccxt/htx.py +851 -383
- ccxt/huobi.py +2 -1
- ccxt/hyperliquid.py +1848 -536
- ccxt/independentreserve.py +287 -15
- ccxt/indodax.py +190 -33
- ccxt/jibitex.py +9 -9
- ccxt/kraken.py +794 -351
- ccxt/krakenfutures.py +214 -62
- ccxt/kucoin.py +715 -396
- ccxt/kucoinfutures.py +652 -89
- ccxt/latoken.py +217 -113
- ccxt/lbank.py +425 -97
- ccxt/luno.py +382 -35
- ccxt/mercado.py +113 -6
- ccxt/mexc.py +873 -437
- ccxt/modetrade.py +2818 -0
- ccxt/myokx.py +54 -0
- ccxt/ndax.py +221 -64
- ccxt/nobitex.py +29 -35
- ccxt/novadax.py +190 -34
- ccxt/oceanex.py +217 -28
- ccxt/okcoin.py +253 -145
- ccxt/okexchange.py +9 -9
- ccxt/okx.py +1088 -351
- ccxt/okxus.py +54 -0
- ccxt/ompfinex.py +22 -21
- ccxt/onetrading.py +213 -392
- ccxt/oxfun.py +245 -166
- ccxt/p2b.py +151 -29
- ccxt/paradex.py +562 -49
- ccxt/paymium.py +82 -19
- ccxt/phemex.py +712 -172
- ccxt/poloniex.py +1601 -283
- ccxt/pro/__init__.py +76 -17
- ccxt/pro/alpaca.py +21 -6
- ccxt/pro/apex.py +984 -0
- ccxt/pro/ascendex.py +58 -10
- ccxt/pro/bequant.py +6 -1
- ccxt/pro/binance.py +728 -156
- ccxt/pro/binancecoinm.py +6 -2
- ccxt/pro/binanceus.py +8 -4
- ccxt/pro/binanceusdm.py +7 -2
- ccxt/pro/bingx.py +333 -142
- ccxt/pro/bitfinex.py +727 -262
- ccxt/pro/bitget.py +570 -79
- ccxt/pro/bithumb.py +20 -6
- ccxt/pro/bitmart.py +216 -87
- ccxt/pro/bitmex.py +47 -9
- ccxt/pro/bitopro.py +26 -14
- ccxt/pro/bitrue.py +22 -22
- ccxt/pro/bitstamp.py +54 -21
- ccxt/pro/{huobijp.py → bittrade.py} +7 -6
- ccxt/pro/bitvavo.py +191 -67
- ccxt/pro/blockchaincom.py +21 -8
- ccxt/pro/blofin.py +9 -1
- ccxt/pro/bybit.py +632 -245
- ccxt/pro/cex.py +59 -24
- ccxt/pro/coinbase.py +102 -73
- ccxt/pro/coinbaseadvanced.py +2 -1
- ccxt/pro/coinbaseexchange.py +8 -8
- ccxt/pro/coinbaseinternational.py +181 -25
- ccxt/pro/coincatch.py +6 -7
- ccxt/pro/coincheck.py +11 -6
- ccxt/pro/coinex.py +967 -665
- ccxt/pro/coinone.py +16 -9
- ccxt/pro/cryptocom.py +448 -45
- ccxt/pro/defx.py +831 -0
- ccxt/pro/deribit.py +150 -14
- ccxt/pro/derive.py +704 -0
- ccxt/pro/exmo.py +239 -6
- ccxt/pro/gate.py +623 -65
- ccxt/pro/gateio.py +2 -1
- ccxt/pro/gemini.py +27 -11
- ccxt/pro/hashkey.py +2 -2
- ccxt/pro/hitbtc.py +196 -91
- ccxt/pro/hollaex.py +23 -7
- ccxt/pro/htx.py +51 -14
- ccxt/pro/huobi.py +2 -1
- ccxt/pro/hyperliquid.py +591 -27
- ccxt/pro/independentreserve.py +9 -6
- ccxt/pro/kraken.py +640 -320
- ccxt/pro/krakenfutures.py +62 -35
- ccxt/pro/kucoin.py +267 -46
- ccxt/pro/kucoinfutures.py +165 -21
- ccxt/pro/lbank.py +102 -21
- ccxt/pro/luno.py +12 -8
- ccxt/pro/mexc.py +877 -111
- ccxt/pro/modetrade.py +1271 -0
- ccxt/pro/myokx.py +38 -0
- ccxt/pro/ndax.py +15 -2
- ccxt/pro/okcoin.py +23 -4
- ccxt/pro/okx.py +573 -98
- ccxt/pro/okxus.py +38 -0
- ccxt/pro/onetrading.py +30 -13
- ccxt/pro/oxfun.py +131 -27
- ccxt/pro/p2b.py +88 -22
- ccxt/pro/paradex.py +3 -3
- ccxt/pro/phemex.py +75 -21
- ccxt/pro/poloniex.py +124 -41
- ccxt/pro/probit.py +87 -80
- ccxt/pro/tradeogre.py +272 -0
- ccxt/pro/upbit.py +152 -12
- ccxt/pro/vertex.py +8 -3
- ccxt/pro/whitebit.py +58 -5
- ccxt/pro/woo.py +228 -37
- ccxt/pro/woofipro.py +106 -18
- ccxt/pro/xt.py +111 -5
- ccxt/probit.py +224 -95
- ccxt/protobuf/__init__.py +0 -0
- ccxt/protobuf/mexc/PrivateAccountV3Api_pb2.py +37 -0
- ccxt/protobuf/mexc/PrivateDealsV3Api_pb2.py +37 -0
- ccxt/protobuf/mexc/PrivateOrdersV3Api_pb2.py +37 -0
- ccxt/protobuf/mexc/PublicAggreBookTickerV3Api_pb2.py +37 -0
- ccxt/protobuf/mexc/PublicAggreDealsV3Api_pb2.py +39 -0
- ccxt/protobuf/mexc/PublicAggreDepthsV3Api_pb2.py +39 -0
- ccxt/protobuf/mexc/PublicBookTickerBatchV3Api_pb2.py +38 -0
- ccxt/protobuf/mexc/PublicBookTickerV3Api_pb2.py +37 -0
- ccxt/protobuf/mexc/PublicDealsV3Api_pb2.py +39 -0
- ccxt/protobuf/mexc/PublicIncreaseDepthsBatchV3Api_pb2.py +38 -0
- ccxt/protobuf/mexc/PublicIncreaseDepthsV3Api_pb2.py +39 -0
- ccxt/protobuf/mexc/PublicLimitDepthsV3Api_pb2.py +39 -0
- ccxt/protobuf/mexc/PublicMiniTickerV3Api_pb2.py +37 -0
- ccxt/protobuf/mexc/PublicMiniTickersV3Api_pb2.py +38 -0
- ccxt/protobuf/mexc/PublicSpotKlineV3Api_pb2.py +37 -0
- ccxt/protobuf/mexc/PushDataV3ApiWrapper_pb2.py +52 -0
- ccxt/protobuf/mexc/__init__.py +0 -0
- ccxt/ramzinex.py +28 -25
- ccxt/sarmayex.py +7 -7
- ccxt/sarrafex.py +10 -10
- ccxt/static_dependencies/__init__.py +1 -1
- ccxt/static_dependencies/lark/py.typed +0 -0
- ccxt/static_dependencies/marshmallow/py.typed +0 -0
- ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
- ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
- ccxt/tabdeal.py +12 -11
- ccxt/test/tests_async.py +261 -57
- ccxt/test/tests_helpers.py +1 -3
- ccxt/test/tests_init.py +4 -3
- ccxt/test/tests_sync.py +261 -57
- ccxt/tetherland.py +7 -7
- ccxt/timex.py +210 -51
- ccxt/tokocrypto.py +167 -47
- ccxt/tradeogre.py +266 -31
- ccxt/twox.py +7 -7
- ccxt/ubitex.py +9 -9
- ccxt/upbit.py +568 -165
- ccxt/vertex.py +160 -32
- ccxt/wallex.py +9 -9
- ccxt/wavesexchange.py +165 -30
- ccxt/whitebit.py +975 -127
- ccxt/woo.py +1917 -1016
- ccxt/woofipro.py +432 -141
- ccxt/xt.py +649 -193
- ccxt/yobit.py +194 -70
- ccxt/zaif.py +91 -15
- ccxt/zonda.py +151 -36
- {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info}/METADATA +225 -73
- ccxt_ir-4.5.0.dist-info/RECORD +743 -0
- {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info}/WHEEL +1 -1
- ccxt/abstract/ace.py +0 -15
- ccxt/abstract/bitbay.py +0 -53
- ccxt/abstract/bitcoincom.py +0 -115
- ccxt/abstract/bitfinex2.py +0 -139
- ccxt/abstract/bitpanda.py +0 -35
- ccxt/abstract/bl3p.py +0 -19
- ccxt/abstract/coinlist.py +0 -54
- ccxt/abstract/currencycom.py +0 -68
- ccxt/abstract/hitbtc3.py +0 -115
- ccxt/abstract/idex.py +0 -26
- ccxt/abstract/kuna.py +0 -182
- ccxt/abstract/lykke.py +0 -29
- ccxt/abstract/poloniexfutures.py +0 -48
- ccxt/abstract/wazirx.py +0 -30
- ccxt/ace.py +0 -1012
- ccxt/async_support/ace.py +0 -1012
- ccxt/async_support/base/ws/aiohttp_client.py +0 -125
- ccxt/async_support/base/ws/fast_client.py +0 -96
- ccxt/async_support/bitbay.py +0 -17
- ccxt/async_support/bitcoincom.py +0 -17
- ccxt/async_support/bitfinex2.py +0 -3552
- ccxt/async_support/bitpanda.py +0 -16
- ccxt/async_support/bl3p.py +0 -485
- ccxt/async_support/coinlist.py +0 -2243
- ccxt/async_support/currencycom.py +0 -1950
- ccxt/async_support/hitbtc3.py +0 -16
- ccxt/async_support/idex.py +0 -1766
- ccxt/async_support/kuna.py +0 -1841
- ccxt/async_support/lykke.py +0 -1270
- ccxt/async_support/poloniexfutures.py +0 -1717
- ccxt/async_support/wazirx.py +0 -1224
- ccxt/bitbay.py +0 -17
- ccxt/bitcoincom.py +0 -17
- ccxt/bitfinex2.py +0 -3552
- ccxt/bitpanda.py +0 -16
- ccxt/bl3p.py +0 -485
- ccxt/coinlist.py +0 -2243
- ccxt/currencycom.py +0 -1950
- ccxt/hitbtc3.py +0 -16
- ccxt/idex.py +0 -1766
- ccxt/kuna.py +0 -1841
- ccxt/lykke.py +0 -1270
- ccxt/poloniexfutures.py +0 -1717
- ccxt/pro/bitcoincom.py +0 -34
- ccxt/pro/bitfinex2.py +0 -1083
- ccxt/pro/bitpanda.py +0 -15
- ccxt/pro/currencycom.py +0 -536
- ccxt/pro/idex.py +0 -672
- ccxt/pro/poloniexfutures.py +0 -990
- ccxt/pro/wazirx.py +0 -749
- ccxt/test/base/__init__.py +0 -29
- ccxt/test/base/test_account.py +0 -26
- ccxt/test/base/test_balance.py +0 -56
- ccxt/test/base/test_borrow_interest.py +0 -35
- ccxt/test/base/test_borrow_rate.py +0 -32
- ccxt/test/base/test_calculate_fee.py +0 -51
- ccxt/test/base/test_crypto.py +0 -127
- ccxt/test/base/test_currency.py +0 -76
- ccxt/test/base/test_datetime.py +0 -109
- ccxt/test/base/test_decimal_to_precision.py +0 -392
- ccxt/test/base/test_deep_extend.py +0 -68
- ccxt/test/base/test_deposit_withdrawal.py +0 -50
- ccxt/test/base/test_exchange_datetime_functions.py +0 -76
- ccxt/test/base/test_funding_rate_history.py +0 -29
- ccxt/test/base/test_last_price.py +0 -31
- ccxt/test/base/test_ledger_entry.py +0 -45
- ccxt/test/base/test_ledger_item.py +0 -48
- ccxt/test/base/test_leverage_tier.py +0 -33
- ccxt/test/base/test_liquidation.py +0 -50
- ccxt/test/base/test_margin_mode.py +0 -24
- ccxt/test/base/test_margin_modification.py +0 -35
- ccxt/test/base/test_market.py +0 -193
- ccxt/test/base/test_number.py +0 -411
- ccxt/test/base/test_ohlcv.py +0 -33
- ccxt/test/base/test_open_interest.py +0 -32
- ccxt/test/base/test_order.py +0 -64
- ccxt/test/base/test_order_book.py +0 -69
- ccxt/test/base/test_position.py +0 -60
- ccxt/test/base/test_shared_methods.py +0 -353
- ccxt/test/base/test_status.py +0 -24
- ccxt/test/base/test_throttle.py +0 -126
- ccxt/test/base/test_ticker.py +0 -92
- ccxt/test/base/test_trade.py +0 -47
- ccxt/test/base/test_trading_fee.py +0 -26
- ccxt/test/base/test_transaction.py +0 -39
- ccxt/test/test_async.py +0 -1649
- ccxt/test/test_sync.py +0 -1648
- ccxt/wazirx.py +0 -1224
- ccxt_ir-4.3.46.0.2.dist-info/RECORD +0 -772
- /ccxt/abstract/{huobijp.py → bittrade.py} +0 -0
- {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info/licenses}/LICENSE.txt +0 -0
- {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info}/top_level.txt +0 -0
ccxt/base/exchange.py
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# -----------------------------------------------------------------------------
|
|
6
6
|
|
|
7
|
-
__version__ = '4.
|
|
7
|
+
__version__ = '4.5.0'
|
|
8
8
|
|
|
9
9
|
# -----------------------------------------------------------------------------
|
|
10
10
|
|
|
@@ -21,9 +21,11 @@ from ccxt.base.errors import ArgumentsRequired
|
|
|
21
21
|
from ccxt.base.errors import BadSymbol
|
|
22
22
|
from ccxt.base.errors import NullResponse
|
|
23
23
|
from ccxt.base.errors import RateLimitExceeded
|
|
24
|
+
from ccxt.base.errors import OperationFailed
|
|
24
25
|
from ccxt.base.errors import BadRequest
|
|
25
26
|
from ccxt.base.errors import BadResponse
|
|
26
|
-
from ccxt.base.errors import
|
|
27
|
+
from ccxt.base.errors import InvalidProxySettings
|
|
28
|
+
from ccxt.base.errors import UnsubscribeError
|
|
27
29
|
|
|
28
30
|
# -----------------------------------------------------------------------------
|
|
29
31
|
|
|
@@ -31,7 +33,7 @@ from ccxt.base.decimal_to_precision import decimal_to_precision
|
|
|
31
33
|
from ccxt.base.decimal_to_precision import DECIMAL_PLACES, TICK_SIZE, NO_PADDING, TRUNCATE, ROUND, ROUND_UP, ROUND_DOWN, SIGNIFICANT_DIGITS
|
|
32
34
|
from ccxt.base.decimal_to_precision import number_to_string
|
|
33
35
|
from ccxt.base.precise import Precise
|
|
34
|
-
from ccxt.base.types import BalanceAccount, Currency, IndexType, OrderSide, OrderType, Trade, OrderRequest, Market, MarketType, Str, Num, Strings, CancellationRequest, Bool
|
|
36
|
+
from ccxt.base.types import ConstructorArgs, BalanceAccount, Currency, IndexType, OrderSide, OrderType, Trade, OrderRequest, Market, MarketType, Str, Num, Strings, CancellationRequest, Bool
|
|
35
37
|
|
|
36
38
|
# -----------------------------------------------------------------------------
|
|
37
39
|
|
|
@@ -59,6 +61,16 @@ from ccxt.static_dependencies.ethereum import abi
|
|
|
59
61
|
from ccxt.static_dependencies.ethereum import account
|
|
60
62
|
from ccxt.static_dependencies.msgpack import packb
|
|
61
63
|
|
|
64
|
+
# starknet
|
|
65
|
+
from ccxt.static_dependencies.starknet.ccxt_utils import get_private_key_from_eth_signature
|
|
66
|
+
from ccxt.static_dependencies.starknet.hash.address import compute_address
|
|
67
|
+
from ccxt.static_dependencies.starknet.hash.selector import get_selector_from_name
|
|
68
|
+
from ccxt.static_dependencies.starknet.hash.utils import message_signature, private_to_stark_key
|
|
69
|
+
from ccxt.static_dependencies.starknet.utils.typed_data import TypedData as TypedDataDataclass
|
|
70
|
+
try:
|
|
71
|
+
import apexpro.zklink_sdk as zklink_sdk
|
|
72
|
+
except ImportError:
|
|
73
|
+
zklink_sdk = None
|
|
62
74
|
|
|
63
75
|
# -----------------------------------------------------------------------------
|
|
64
76
|
|
|
@@ -81,6 +93,14 @@ import gzip
|
|
|
81
93
|
import hashlib
|
|
82
94
|
import hmac
|
|
83
95
|
import io
|
|
96
|
+
|
|
97
|
+
# load orjson if available, otherwise default to json
|
|
98
|
+
orjson = None
|
|
99
|
+
try:
|
|
100
|
+
import orjson as orjson
|
|
101
|
+
except ImportError:
|
|
102
|
+
pass
|
|
103
|
+
|
|
84
104
|
import json
|
|
85
105
|
import math
|
|
86
106
|
import random
|
|
@@ -104,11 +124,20 @@ from ccxt.base.types import Int
|
|
|
104
124
|
|
|
105
125
|
# -----------------------------------------------------------------------------
|
|
106
126
|
|
|
127
|
+
class SafeJSONEncoder(json.JSONEncoder):
|
|
128
|
+
def default(self, obj):
|
|
129
|
+
if isinstance(obj, Exception):
|
|
130
|
+
return {"name": obj.__class__.__name__}
|
|
131
|
+
try:
|
|
132
|
+
return super().default(obj)
|
|
133
|
+
except TypeError:
|
|
134
|
+
return f"TypeError: Object of type {type(obj).__name__} is not JSON serializable"
|
|
107
135
|
|
|
108
136
|
class Exchange(object):
|
|
109
137
|
"""Base exchange class"""
|
|
110
138
|
id = None
|
|
111
139
|
name = None
|
|
140
|
+
countries = None
|
|
112
141
|
version = None
|
|
113
142
|
certified = False # if certified by the CCXT dev team
|
|
114
143
|
pro = False # if it is integrated with CCXT Pro for WebSocket support
|
|
@@ -124,6 +153,8 @@ class Exchange(object):
|
|
|
124
153
|
aiohttp_trust_env = False
|
|
125
154
|
requests_trust_env = False
|
|
126
155
|
session = None # Session () by default
|
|
156
|
+
tcp_connector = None # aiohttp.TCPConnector
|
|
157
|
+
aiohttp_socks_connector = None
|
|
127
158
|
socks_proxy_sessions = None
|
|
128
159
|
verify = True # SSL verification
|
|
129
160
|
validateServerSsl = True
|
|
@@ -133,12 +164,19 @@ class Exchange(object):
|
|
|
133
164
|
markets = None
|
|
134
165
|
symbols = None
|
|
135
166
|
codes = None
|
|
136
|
-
timeframes =
|
|
167
|
+
timeframes = {}
|
|
168
|
+
tokenBucket = None
|
|
169
|
+
|
|
137
170
|
fees = {
|
|
138
171
|
'trading': {
|
|
139
|
-
'
|
|
172
|
+
'tierBased': None,
|
|
173
|
+
'percentage': None,
|
|
174
|
+
'taker': None,
|
|
175
|
+
'maker': None,
|
|
140
176
|
},
|
|
141
177
|
'funding': {
|
|
178
|
+
'tierBased': None,
|
|
179
|
+
'percentage': None,
|
|
142
180
|
'withdraw': {},
|
|
143
181
|
'deposit': {},
|
|
144
182
|
},
|
|
@@ -156,6 +194,7 @@ class Exchange(object):
|
|
|
156
194
|
urls = None
|
|
157
195
|
api = None
|
|
158
196
|
parseJsonResponse = True
|
|
197
|
+
throttler = None
|
|
159
198
|
|
|
160
199
|
# PROXY & USER-AGENTS (see "examples/proxy-usage" file for explanation)
|
|
161
200
|
proxy = None # for backwards compatibility
|
|
@@ -190,7 +229,9 @@ class Exchange(object):
|
|
|
190
229
|
'chrome100': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36',
|
|
191
230
|
}
|
|
192
231
|
headers = None
|
|
232
|
+
returnResponseHeaders = False
|
|
193
233
|
origin = '*' # CORS origin
|
|
234
|
+
MAX_VALUE = float('inf')
|
|
194
235
|
#
|
|
195
236
|
proxies = None
|
|
196
237
|
|
|
@@ -206,6 +247,7 @@ class Exchange(object):
|
|
|
206
247
|
twofa = None
|
|
207
248
|
markets_by_id = None
|
|
208
249
|
currencies_by_id = None
|
|
250
|
+
|
|
209
251
|
precision = None
|
|
210
252
|
exceptions = None
|
|
211
253
|
limits = {
|
|
@@ -226,6 +268,7 @@ class Exchange(object):
|
|
|
226
268
|
'max': None,
|
|
227
269
|
},
|
|
228
270
|
}
|
|
271
|
+
|
|
229
272
|
httpExceptions = {
|
|
230
273
|
'422': ExchangeError,
|
|
231
274
|
'418': DDoSProtection,
|
|
@@ -269,10 +312,14 @@ class Exchange(object):
|
|
|
269
312
|
base_currencies = None
|
|
270
313
|
quote_currencies = None
|
|
271
314
|
currencies = None
|
|
315
|
+
|
|
272
316
|
options = None # Python does not allow to define properties in run-time with setattr
|
|
317
|
+
isSandboxModeEnabled = False
|
|
273
318
|
accounts = None
|
|
274
319
|
positions = None
|
|
275
320
|
|
|
321
|
+
status = None
|
|
322
|
+
|
|
276
323
|
requiredCredentials = {
|
|
277
324
|
'apiKey': True,
|
|
278
325
|
'secret': True,
|
|
@@ -287,117 +334,8 @@ class Exchange(object):
|
|
|
287
334
|
}
|
|
288
335
|
|
|
289
336
|
# API method metainfo
|
|
290
|
-
has = {
|
|
291
|
-
|
|
292
|
-
'privateAPI': True,
|
|
293
|
-
'CORS': None,
|
|
294
|
-
'spot': None,
|
|
295
|
-
'margin': None,
|
|
296
|
-
'swap': None,
|
|
297
|
-
'future': None,
|
|
298
|
-
'option': None,
|
|
299
|
-
'addMargin': None,
|
|
300
|
-
'cancelAllOrders': None,
|
|
301
|
-
'cancelOrder': True,
|
|
302
|
-
'cancelOrders': None,
|
|
303
|
-
'createDepositAddress': None,
|
|
304
|
-
'createLimitOrder': True,
|
|
305
|
-
'createMarketOrder': True,
|
|
306
|
-
'createOrder': True,
|
|
307
|
-
'createPostOnlyOrder': None,
|
|
308
|
-
'createReduceOnlyOrder': None,
|
|
309
|
-
'createStopOrder': None,
|
|
310
|
-
'createStopLimitOrder': None,
|
|
311
|
-
'createStopMarketOrder': None,
|
|
312
|
-
'editOrder': 'emulated',
|
|
313
|
-
'fetchAccounts': None,
|
|
314
|
-
'fetchBalance': True,
|
|
315
|
-
'fetchBidsAsks': None,
|
|
316
|
-
'fetchBorrowInterest': None,
|
|
317
|
-
'fetchBorrowRate': None,
|
|
318
|
-
'fetchBorrowRateHistory': None,
|
|
319
|
-
'fetchBorrowRatesPerSymbol': None,
|
|
320
|
-
'fetchBorrowRates': None,
|
|
321
|
-
'fetchCanceledOrders': None,
|
|
322
|
-
'fetchClosedOrder': None,
|
|
323
|
-
'fetchClosedOrders': None,
|
|
324
|
-
'fetchClosedOrdersWs': None,
|
|
325
|
-
'fetchConvertCurrencies': None,
|
|
326
|
-
'fetchConvertQuote': None,
|
|
327
|
-
'fetchConvertTrade': None,
|
|
328
|
-
'fetchConvertTradeHistory': None,
|
|
329
|
-
'fetchCrossBorrowRate': None,
|
|
330
|
-
'fetchCrossBorrowRates': None,
|
|
331
|
-
'fetchCurrencies': 'emulated',
|
|
332
|
-
'fetchCurrenciesWs': 'emulated',
|
|
333
|
-
'fetchDeposit': None,
|
|
334
|
-
'fetchDepositAddress': None,
|
|
335
|
-
'fetchDepositAddresses': None,
|
|
336
|
-
'fetchDepositAddressesByNetwork': None,
|
|
337
|
-
'fetchDeposits': None,
|
|
338
|
-
'fetchFundingHistory': None,
|
|
339
|
-
'fetchFundingRate': None,
|
|
340
|
-
'fetchFundingRateHistory': None,
|
|
341
|
-
'fetchFundingRates': None,
|
|
342
|
-
'fetchIndexOHLCV': None,
|
|
343
|
-
'fetchLastPrices': None,
|
|
344
|
-
'fetchL2OrderBook': True,
|
|
345
|
-
'fetchLedger': None,
|
|
346
|
-
'fetchLedgerEntry': None,
|
|
347
|
-
'fetchLeverageTiers': None,
|
|
348
|
-
'fetchMarketLeverageTiers': None,
|
|
349
|
-
'fetchMarkets': True,
|
|
350
|
-
'fetchMarkOHLCV': None,
|
|
351
|
-
'fetchMyTrades': None,
|
|
352
|
-
'fetchOHLCV': None,
|
|
353
|
-
'fetchOpenOrder': None,
|
|
354
|
-
'fetchOpenOrders': None,
|
|
355
|
-
'fetchOrder': None,
|
|
356
|
-
'fetchOrderBook': True,
|
|
357
|
-
'fetchOrderBooks': None,
|
|
358
|
-
'fetchOrders': None,
|
|
359
|
-
'fetchOrderTrades': None,
|
|
360
|
-
'fetchPermissions': None,
|
|
361
|
-
'fetchPosition': None,
|
|
362
|
-
'fetchPositions': None,
|
|
363
|
-
'fetchPositionsRisk': None,
|
|
364
|
-
'fetchPremiumIndexOHLCV': None,
|
|
365
|
-
'fetchStatus': None,
|
|
366
|
-
'fetchTicker': True,
|
|
367
|
-
'fetchTickers': None,
|
|
368
|
-
'fetchTime': None,
|
|
369
|
-
'fetchTrades': True,
|
|
370
|
-
'fetchTradingFee': None,
|
|
371
|
-
'fetchTradingFees': None,
|
|
372
|
-
'fetchTradingLimits': None,
|
|
373
|
-
'fetchTransactions': None,
|
|
374
|
-
'fetchTransfers': None,
|
|
375
|
-
'fetchWithdrawal': None,
|
|
376
|
-
'fetchWithdrawals': None,
|
|
377
|
-
'reduceMargin': None,
|
|
378
|
-
'setLeverage': None,
|
|
379
|
-
'setMargin': None,
|
|
380
|
-
'setMarginMode': None,
|
|
381
|
-
'setPositionMode': None,
|
|
382
|
-
'signIn': None,
|
|
383
|
-
'transfer': None,
|
|
384
|
-
'withdraw': None,
|
|
385
|
-
'watchOrderBook': None,
|
|
386
|
-
'watchOrders': None,
|
|
387
|
-
'watchMyTrades': None,
|
|
388
|
-
'watchTickers': None,
|
|
389
|
-
'watchTicker': None,
|
|
390
|
-
'watchTrades': None,
|
|
391
|
-
'watchTradesForSymbols': None,
|
|
392
|
-
'watchOrderBookForSymbols': None,
|
|
393
|
-
'watchOHLCVForSymbols': None,
|
|
394
|
-
'watchBalance': None,
|
|
395
|
-
'watchLiquidations': None,
|
|
396
|
-
'watchLiquidationsForSymbols': None,
|
|
397
|
-
'watchMyLiquidations': None,
|
|
398
|
-
'watchMyLiquidationsForSymbols': None,
|
|
399
|
-
'watchOHLCV': None,
|
|
400
|
-
}
|
|
337
|
+
has = {}
|
|
338
|
+
features = {}
|
|
401
339
|
precisionMode = DECIMAL_PLACES
|
|
402
340
|
paddingMode = NO_PADDING
|
|
403
341
|
minFundingAddressLength = 1 # used in check_address
|
|
@@ -415,7 +353,7 @@ class Exchange(object):
|
|
|
415
353
|
rateLimitMaxTokens = 16
|
|
416
354
|
rateLimitUpdateTime = 0
|
|
417
355
|
enableLastHttpResponse = True
|
|
418
|
-
enableLastJsonResponse =
|
|
356
|
+
enableLastJsonResponse = False
|
|
419
357
|
enableLastResponseHeaders = True
|
|
420
358
|
last_http_response = None
|
|
421
359
|
last_json_response = None
|
|
@@ -437,7 +375,7 @@ class Exchange(object):
|
|
|
437
375
|
}
|
|
438
376
|
synchronous = True
|
|
439
377
|
|
|
440
|
-
def __init__(self, config={}):
|
|
378
|
+
def __init__(self, config: ConstructorArgs = {}):
|
|
441
379
|
self.aiohttp_trust_env = self.aiohttp_trust_env or self.trust_env
|
|
442
380
|
self.requests_trust_env = self.requests_trust_env or self.trust_env
|
|
443
381
|
|
|
@@ -453,6 +391,8 @@ class Exchange(object):
|
|
|
453
391
|
self.trades = dict() if self.trades is None else self.trades
|
|
454
392
|
self.transactions = dict() if self.transactions is None else self.transactions
|
|
455
393
|
self.ohlcvs = dict() if self.ohlcvs is None else self.ohlcvs
|
|
394
|
+
self.liquidations = dict() if self.liquidations is None else self.liquidations
|
|
395
|
+
self.myLiquidations = dict() if self.myLiquidations is None else self.myLiquidations
|
|
456
396
|
self.currencies = dict() if self.currencies is None else self.currencies
|
|
457
397
|
self.options = self.get_default_options() if self.options is None else self.options # Python does not allow to define properties in run-time with setattr
|
|
458
398
|
self.decimal_to_precision = decimal_to_precision
|
|
@@ -474,14 +414,10 @@ class Exchange(object):
|
|
|
474
414
|
else:
|
|
475
415
|
setattr(self, key, settings[key])
|
|
476
416
|
|
|
477
|
-
if self.markets:
|
|
478
|
-
self.set_markets(self.markets)
|
|
479
|
-
|
|
480
417
|
self.after_construct()
|
|
481
418
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
self.set_sandbox_mode(is_sandbox)
|
|
419
|
+
if self.safe_bool(config, 'sandbox') or self.safe_bool(config, 'testnet'):
|
|
420
|
+
self.set_sandbox_mode(True)
|
|
485
421
|
|
|
486
422
|
# convert all properties from underscore notation foo_bar to camelcase notation fooBar
|
|
487
423
|
cls = type(self)
|
|
@@ -501,13 +437,6 @@ class Exchange(object):
|
|
|
501
437
|
else:
|
|
502
438
|
setattr(self, camelcase, attr)
|
|
503
439
|
|
|
504
|
-
self.tokenBucket = self.extend({
|
|
505
|
-
'refillRate': 1.0 / self.rateLimit if self.rateLimit > 0 else float('inf'),
|
|
506
|
-
'delay': 0.001,
|
|
507
|
-
'capacity': 1.0,
|
|
508
|
-
'defaultCost': 1.0,
|
|
509
|
-
}, getattr(self, 'tokenBucket', {}))
|
|
510
|
-
|
|
511
440
|
if not self.session and self.synchronous:
|
|
512
441
|
self.session = Session()
|
|
513
442
|
self.session.trust_env = self.requests_trust_env
|
|
@@ -526,8 +455,9 @@ class Exchange(object):
|
|
|
526
455
|
def __str__(self):
|
|
527
456
|
return self.name
|
|
528
457
|
|
|
529
|
-
def
|
|
530
|
-
|
|
458
|
+
def init_throttler(self, cost=None):
|
|
459
|
+
# stub in sync
|
|
460
|
+
pass
|
|
531
461
|
|
|
532
462
|
def throttle(self, cost=None):
|
|
533
463
|
now = float(self.milliseconds())
|
|
@@ -569,9 +499,11 @@ class Exchange(object):
|
|
|
569
499
|
return response_body.strip()
|
|
570
500
|
|
|
571
501
|
def on_json_response(self, response_body):
|
|
572
|
-
if self.quoteJsonNumbers:
|
|
502
|
+
if self.quoteJsonNumbers and orjson is None:
|
|
573
503
|
return json.loads(response_body, parse_float=str, parse_int=str)
|
|
574
504
|
else:
|
|
505
|
+
if orjson:
|
|
506
|
+
return orjson.loads(response_body)
|
|
575
507
|
return json.loads(response_body)
|
|
576
508
|
|
|
577
509
|
def fetch(self, url, method='GET', headers=None, body=None):
|
|
@@ -583,7 +515,7 @@ class Exchange(object):
|
|
|
583
515
|
proxyUrl = self.check_proxy_url_settings(url, method, headers, body)
|
|
584
516
|
if proxyUrl is not None:
|
|
585
517
|
request_headers.update({'Origin': self.origin})
|
|
586
|
-
url = proxyUrl + url
|
|
518
|
+
url = proxyUrl + self.url_encoder_for_proxy_url(url)
|
|
587
519
|
# proxy agents
|
|
588
520
|
proxies = None # set default
|
|
589
521
|
httpProxy, httpsProxy, socksProxy = self.check_proxy_settings(url, method, headers, body)
|
|
@@ -648,6 +580,8 @@ class Exchange(object):
|
|
|
648
580
|
if self.verbose:
|
|
649
581
|
self.log("\nfetch Response:", self.id, method, url, http_status_code, "ResponseHeaders:", headers, "ResponseBody:", http_response)
|
|
650
582
|
self.logger.debug("%s %s, Response: %s %s %s", method, url, http_status_code, headers, http_response)
|
|
583
|
+
if json_response and not isinstance(json_response, list) and self.returnResponseHeaders:
|
|
584
|
+
json_response['responseHeaders'] = headers
|
|
651
585
|
response.raise_for_status()
|
|
652
586
|
|
|
653
587
|
except Timeout as e:
|
|
@@ -907,11 +841,13 @@ class Exchange(object):
|
|
|
907
841
|
|
|
908
842
|
@staticmethod
|
|
909
843
|
def get_object_value_from_key_list(dictionary_or_list, key_list):
|
|
844
|
+
isDataArray = isinstance(dictionary_or_list, list)
|
|
845
|
+
isDataDict = isinstance(dictionary_or_list, dict)
|
|
910
846
|
for key in key_list:
|
|
911
|
-
if
|
|
847
|
+
if isDataDict:
|
|
912
848
|
if key in dictionary_or_list and dictionary_or_list[key] is not None and dictionary_or_list[key] != '':
|
|
913
849
|
return dictionary_or_list[key]
|
|
914
|
-
elif
|
|
850
|
+
elif isDataArray and not isinstance(key, str):
|
|
915
851
|
if (key < len(dictionary_or_list)) and (dictionary_or_list[key] is not None) and (dictionary_or_list[key] != ''):
|
|
916
852
|
return dictionary_or_list[key]
|
|
917
853
|
return None
|
|
@@ -973,6 +909,10 @@ class Exchange(object):
|
|
|
973
909
|
def keysort(dictionary):
|
|
974
910
|
return collections.OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
|
|
975
911
|
|
|
912
|
+
@staticmethod
|
|
913
|
+
def sort(array):
|
|
914
|
+
return sorted(array)
|
|
915
|
+
|
|
976
916
|
@staticmethod
|
|
977
917
|
def extend(*args):
|
|
978
918
|
if args is not None:
|
|
@@ -1023,6 +963,11 @@ class Exchange(object):
|
|
|
1023
963
|
def groupBy(array, key):
|
|
1024
964
|
return Exchange.group_by(array, key)
|
|
1025
965
|
|
|
966
|
+
|
|
967
|
+
@staticmethod
|
|
968
|
+
def index_by_safe(array, key):
|
|
969
|
+
return Exchange.index_by(array, key) # wrapper for go
|
|
970
|
+
|
|
1026
971
|
@staticmethod
|
|
1027
972
|
def index_by(array, key):
|
|
1028
973
|
result = {}
|
|
@@ -1068,7 +1013,7 @@ class Exchange(object):
|
|
|
1068
1013
|
return string
|
|
1069
1014
|
|
|
1070
1015
|
@staticmethod
|
|
1071
|
-
def urlencode(params={}, doseq=False):
|
|
1016
|
+
def urlencode(params={}, doseq=False, sort=False):
|
|
1072
1017
|
newParams = params.copy()
|
|
1073
1018
|
for key, value in params.items():
|
|
1074
1019
|
if isinstance(value, bool):
|
|
@@ -1101,10 +1046,10 @@ class Exchange(object):
|
|
|
1101
1046
|
if isinstance(params, dict):
|
|
1102
1047
|
for key in params:
|
|
1103
1048
|
_encode_params(params[key], key)
|
|
1104
|
-
return _urlencode.urlencode(result)
|
|
1049
|
+
return _urlencode.urlencode(result, quote_via=_urlencode.quote)
|
|
1105
1050
|
|
|
1106
1051
|
@staticmethod
|
|
1107
|
-
def rawencode(params={}):
|
|
1052
|
+
def rawencode(params={}, sort=False):
|
|
1108
1053
|
return _urlencode.unquote(Exchange.urlencode(params))
|
|
1109
1054
|
|
|
1110
1055
|
@staticmethod
|
|
@@ -1388,6 +1333,57 @@ class Exchange(object):
|
|
|
1388
1333
|
encodedData = account.messages.encode_typed_data(domain, messageTypes, message)
|
|
1389
1334
|
return Exchange.binary_concat(b"\x19\x01", encodedData.header, encodedData.body)
|
|
1390
1335
|
|
|
1336
|
+
@staticmethod
|
|
1337
|
+
def retrieve_stark_account (signature, accountClassHash, accountProxyClassHash):
|
|
1338
|
+
privateKey = get_private_key_from_eth_signature(signature)
|
|
1339
|
+
publicKey = private_to_stark_key(privateKey)
|
|
1340
|
+
calldata = [
|
|
1341
|
+
int(accountClassHash, 16),
|
|
1342
|
+
get_selector_from_name("initialize"),
|
|
1343
|
+
2,
|
|
1344
|
+
publicKey,
|
|
1345
|
+
0,
|
|
1346
|
+
]
|
|
1347
|
+
|
|
1348
|
+
address = compute_address(
|
|
1349
|
+
class_hash=int(accountProxyClassHash, 16),
|
|
1350
|
+
constructor_calldata=calldata,
|
|
1351
|
+
salt=publicKey,
|
|
1352
|
+
)
|
|
1353
|
+
return {
|
|
1354
|
+
'privateKey': privateKey,
|
|
1355
|
+
'publicKey': publicKey,
|
|
1356
|
+
'address': hex(address)
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
@staticmethod
|
|
1360
|
+
def starknet_encode_structured_data (domain, messageTypes, messageData, address):
|
|
1361
|
+
types = list(messageTypes.keys())
|
|
1362
|
+
if len(types) > 1:
|
|
1363
|
+
raise NotSupported('starknetEncodeStructuredData only support single type')
|
|
1364
|
+
|
|
1365
|
+
request = {
|
|
1366
|
+
'domain': domain,
|
|
1367
|
+
'primaryType': types[0],
|
|
1368
|
+
'types': Exchange.extend({
|
|
1369
|
+
'StarkNetDomain': [
|
|
1370
|
+
{'name': "name", 'type': "felt"},
|
|
1371
|
+
{'name': "chainId", 'type': "felt"},
|
|
1372
|
+
{'name': "version", 'type': "felt"},
|
|
1373
|
+
],
|
|
1374
|
+
}, messageTypes),
|
|
1375
|
+
'message': messageData,
|
|
1376
|
+
}
|
|
1377
|
+
typedDataClass = TypedDataDataclass.from_dict(request)
|
|
1378
|
+
msgHash = typedDataClass.message_hash(int(address, 16))
|
|
1379
|
+
return msgHash
|
|
1380
|
+
|
|
1381
|
+
@staticmethod
|
|
1382
|
+
def starknet_sign (hash, pri):
|
|
1383
|
+
# // TODO: unify to ecdsa
|
|
1384
|
+
r, s = message_signature(hash, pri)
|
|
1385
|
+
return Exchange.json([hex(r), hex(s)])
|
|
1386
|
+
|
|
1391
1387
|
@staticmethod
|
|
1392
1388
|
def packb(o):
|
|
1393
1389
|
return packb(o)
|
|
@@ -1449,7 +1445,7 @@ class Exchange(object):
|
|
|
1449
1445
|
@staticmethod
|
|
1450
1446
|
def eddsa(request, secret, curve='ed25519'):
|
|
1451
1447
|
if isinstance(secret, str):
|
|
1452
|
-
Exchange.encode(secret)
|
|
1448
|
+
secret = Exchange.encode(secret)
|
|
1453
1449
|
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret) if len(secret) == 32 else load_pem_private_key(secret, None)
|
|
1454
1450
|
return Exchange.binary_to_base64(private_key.sign(request))
|
|
1455
1451
|
|
|
@@ -1463,7 +1459,9 @@ class Exchange(object):
|
|
|
1463
1459
|
|
|
1464
1460
|
@staticmethod
|
|
1465
1461
|
def json(data, params=None):
|
|
1466
|
-
|
|
1462
|
+
if orjson:
|
|
1463
|
+
return orjson.dumps(data).decode('utf-8')
|
|
1464
|
+
return json.dumps(data, separators=(',', ':'), cls=SafeJSONEncoder)
|
|
1467
1465
|
|
|
1468
1466
|
@staticmethod
|
|
1469
1467
|
def is_json_encoded_object(input):
|
|
@@ -1473,11 +1471,11 @@ class Exchange(object):
|
|
|
1473
1471
|
|
|
1474
1472
|
@staticmethod
|
|
1475
1473
|
def encode(string):
|
|
1476
|
-
return string.encode('
|
|
1474
|
+
return string.encode('utf-8')
|
|
1477
1475
|
|
|
1478
1476
|
@staticmethod
|
|
1479
1477
|
def decode(string):
|
|
1480
|
-
return string.decode('
|
|
1478
|
+
return string.decode('utf-8')
|
|
1481
1479
|
|
|
1482
1480
|
@staticmethod
|
|
1483
1481
|
def to_array(value):
|
|
@@ -1508,18 +1506,10 @@ class Exchange(object):
|
|
|
1508
1506
|
return error
|
|
1509
1507
|
return result
|
|
1510
1508
|
|
|
1511
|
-
def check_address(self, address):
|
|
1512
|
-
"""Checks an address is not the same character repeated or an empty sequence"""
|
|
1513
|
-
if address is None:
|
|
1514
|
-
raise InvalidAddress(self.id + ' address is None')
|
|
1515
|
-
if all(letter == address[0] for letter in address) or len(address) < self.minFundingAddressLength or ' ' in address:
|
|
1516
|
-
raise InvalidAddress(self.id + ' address is invalid or has less than ' + str(self.minFundingAddressLength) + ' characters: "' + str(address) + '"')
|
|
1517
|
-
return address
|
|
1518
|
-
|
|
1519
1509
|
def precision_from_string(self, str):
|
|
1520
1510
|
# support string formats like '1e-4'
|
|
1521
|
-
if 'e' in str:
|
|
1522
|
-
numStr = re.sub(r'\
|
|
1511
|
+
if 'e' in str or 'E' in str:
|
|
1512
|
+
numStr = re.sub(r'\d\.?\d*[eE]', '', str)
|
|
1523
1513
|
return int(numStr) * -1
|
|
1524
1514
|
# support integer formats (without dot) like '1', '10' etc [Note: bug in decimalToPrecision, so this should not be used atm]
|
|
1525
1515
|
# if not ('.' in str):
|
|
@@ -1528,7 +1518,32 @@ class Exchange(object):
|
|
|
1528
1518
|
parts = re.sub(r'0+$', '', str).split('.')
|
|
1529
1519
|
return len(parts[1]) if len(parts) > 1 else 0
|
|
1530
1520
|
|
|
1521
|
+
def map_to_safe_map(self, dictionary):
|
|
1522
|
+
return dictionary # wrapper for go
|
|
1523
|
+
|
|
1524
|
+
def safe_map_to_map(self, dictionary):
|
|
1525
|
+
return dictionary # wrapper for go
|
|
1526
|
+
|
|
1531
1527
|
def load_markets(self, reload=False, params={}):
|
|
1528
|
+
"""
|
|
1529
|
+
Loads and prepares the markets for trading.
|
|
1530
|
+
|
|
1531
|
+
Args:
|
|
1532
|
+
reload (bool): If True, the markets will be reloaded from the exchange.
|
|
1533
|
+
params (dict): Additional exchange-specific parameters for the request.
|
|
1534
|
+
|
|
1535
|
+
Returns:
|
|
1536
|
+
dict: A dictionary of markets.
|
|
1537
|
+
|
|
1538
|
+
Raises:
|
|
1539
|
+
Exception: If the markets cannot be loaded or prepared.
|
|
1540
|
+
|
|
1541
|
+
Notes:
|
|
1542
|
+
It ensures that the markets are only loaded once, even if called multiple times.
|
|
1543
|
+
If the markets are already loaded and `reload` is False or not provided, it returns the existing markets.
|
|
1544
|
+
If a reload is in progress, it waits for completion before returning.
|
|
1545
|
+
If an error occurs during loading or preparation, an exception is raised.
|
|
1546
|
+
"""
|
|
1532
1547
|
if not reload:
|
|
1533
1548
|
if self.markets:
|
|
1534
1549
|
if not self.markets_by_id:
|
|
@@ -1537,7 +1552,10 @@ class Exchange(object):
|
|
|
1537
1552
|
currencies = None
|
|
1538
1553
|
if self.has['fetchCurrencies'] is True:
|
|
1539
1554
|
currencies = self.fetch_currencies()
|
|
1555
|
+
self.options['cachedCurrencies'] = currencies
|
|
1540
1556
|
markets = self.fetch_markets(params)
|
|
1557
|
+
if 'cachedCurrencies' in self.options:
|
|
1558
|
+
del self.options['cachedCurrencies']
|
|
1541
1559
|
return self.set_markets(markets, currencies)
|
|
1542
1560
|
|
|
1543
1561
|
def fetch_markets(self, params={}):
|
|
@@ -1773,6 +1791,80 @@ class Exchange(object):
|
|
|
1773
1791
|
def create_safe_dictionary(self):
|
|
1774
1792
|
return {}
|
|
1775
1793
|
|
|
1794
|
+
def convert_to_safe_dictionary(self, dictionary):
|
|
1795
|
+
return dictionary
|
|
1796
|
+
|
|
1797
|
+
def rand_number(self, size):
|
|
1798
|
+
return int(''.join([str(random.randint(0, 9)) for _ in range(size)]))
|
|
1799
|
+
|
|
1800
|
+
def binary_length(self, binary):
|
|
1801
|
+
return len(binary)
|
|
1802
|
+
|
|
1803
|
+
def get_zk_contract_signature_obj(self, seeds: str, params={}):
|
|
1804
|
+
if zklink_sdk is None:
|
|
1805
|
+
raise Exception('zklink_sdk is not installed, please do pip3 install apexomni-arm or apexomni-x86-mac or apexomni-x86-windows-linux')
|
|
1806
|
+
|
|
1807
|
+
slotId = self.safe_string(params, 'slotId')
|
|
1808
|
+
nonceInt = int(self.remove0x_prefix(self.hash(self.encode(slotId), 'sha256', 'hex')), 16)
|
|
1809
|
+
|
|
1810
|
+
maxUint64 = 18446744073709551615
|
|
1811
|
+
maxUint32 = 4294967295
|
|
1812
|
+
|
|
1813
|
+
slotId = (nonceInt % maxUint64) / maxUint32
|
|
1814
|
+
nonce = nonceInt % maxUint32
|
|
1815
|
+
accountId = int(self.safe_string(params, 'accountId'), 10) % maxUint32
|
|
1816
|
+
|
|
1817
|
+
priceStr = (Decimal(self.safe_string(params, 'price')) * Decimal(10) ** Decimal('18')).quantize(Decimal(0), rounding='ROUND_DOWN')
|
|
1818
|
+
sizeStr = (Decimal(self.safe_string(params, 'size')) * Decimal(10) ** Decimal('18')).quantize(Decimal(0), rounding='ROUND_DOWN')
|
|
1819
|
+
|
|
1820
|
+
takerFeeRateStr = (Decimal(self.safe_string(params, 'takerFeeRate')) * Decimal(10000)).quantize(Decimal(0), rounding='ROUND_UP')
|
|
1821
|
+
makerFeeRateStr = (Decimal(self.safe_string(params, 'makerFeeRate')) * Decimal(10000)).quantize(Decimal(0), rounding='ROUND_UP')
|
|
1822
|
+
|
|
1823
|
+
builder = zklink_sdk.ContractBuilder(
|
|
1824
|
+
int(accountId), int(0), int(slotId), int(nonce), int(self.safe_number(params, 'pairId')),
|
|
1825
|
+
sizeStr.__str__(), priceStr.__str__(), self.safe_string(params, 'direction') == "BUY",
|
|
1826
|
+
int(takerFeeRateStr), int(makerFeeRateStr), False)
|
|
1827
|
+
|
|
1828
|
+
|
|
1829
|
+
tx = zklink_sdk.Contract(builder)
|
|
1830
|
+
seedsByte = bytes.fromhex(seeds.removeprefix('0x'))
|
|
1831
|
+
signerSeed = zklink_sdk.ZkLinkSigner().new_from_seed(seedsByte)
|
|
1832
|
+
auth_data = signerSeed.sign_musig(tx.get_bytes())
|
|
1833
|
+
signature = auth_data.signature
|
|
1834
|
+
return signature
|
|
1835
|
+
|
|
1836
|
+
def get_zk_transfer_signature_obj(self, seeds: str, params={}):
|
|
1837
|
+
if zklink_sdk is None:
|
|
1838
|
+
raise Exception('zklink_sdk is not installed, please do pip3 install apexomni-arm or apexomni-x86-mac or apexomni-x86-windows-linux')
|
|
1839
|
+
|
|
1840
|
+
nonce = self.safe_string(params, 'nonce', '0')
|
|
1841
|
+
if self.safe_bool(params, 'isContract'):
|
|
1842
|
+
formattedUint32 = '4294967295'
|
|
1843
|
+
formattedNonce = int(self.remove0x_prefix(self.hash(self.encode(nonce), 'sha256', 'hex')), 16)
|
|
1844
|
+
nonce = Precise.string_mod(str(formattedNonce), formattedUint32)
|
|
1845
|
+
|
|
1846
|
+
tx_builder = zklink_sdk.TransferBuilder(
|
|
1847
|
+
int(self.safe_number(params, 'zkAccountId', 0)),
|
|
1848
|
+
self.safe_string(params, 'receiverAddress'),
|
|
1849
|
+
int(self.safe_number(params, 'subAccountId', 0)),
|
|
1850
|
+
int(self.safe_number(params, 'receiverSubAccountId', 0)),
|
|
1851
|
+
int(self.safe_number(params, 'tokenId', 0)),
|
|
1852
|
+
self.safe_string(params, 'amount', '0'),
|
|
1853
|
+
self.safe_string(params, 'fee', '0'),
|
|
1854
|
+
self.parse_to_int(nonce),
|
|
1855
|
+
int(self.safe_number(params, 'timestampSeconds', 0))
|
|
1856
|
+
)
|
|
1857
|
+
|
|
1858
|
+
tx = zklink_sdk.Transfer(tx_builder)
|
|
1859
|
+
seedsByte = bytes.fromhex(seeds.removeprefix('0x'))
|
|
1860
|
+
signerSeed = zklink_sdk.ZkLinkSigner().new_from_seed(seedsByte)
|
|
1861
|
+
auth_data = signerSeed.sign_musig(tx.get_bytes())
|
|
1862
|
+
signature = auth_data.signature
|
|
1863
|
+
return signature
|
|
1864
|
+
|
|
1865
|
+
def is_binary_message(self, message):
|
|
1866
|
+
return isinstance(message, bytes) or isinstance(message, bytearray)
|
|
1867
|
+
|
|
1776
1868
|
# ########################################################################
|
|
1777
1869
|
# ########################################################################
|
|
1778
1870
|
# ########################################################################
|
|
@@ -1812,9 +1904,340 @@ class Exchange(object):
|
|
|
1812
1904
|
|
|
1813
1905
|
# METHODS BELOW THIS LINE ARE TRANSPILED FROM JAVASCRIPT TO PYTHON AND PHP
|
|
1814
1906
|
|
|
1907
|
+
def describe(self) -> Any:
|
|
1908
|
+
return {
|
|
1909
|
+
'id': None,
|
|
1910
|
+
'name': None,
|
|
1911
|
+
'countries': None,
|
|
1912
|
+
'enableRateLimit': True,
|
|
1913
|
+
'rateLimit': 2000, # milliseconds = seconds * 1000
|
|
1914
|
+
'timeout': self.timeout, # milliseconds = seconds * 1000
|
|
1915
|
+
'certified': False, # if certified by the CCXT dev team
|
|
1916
|
+
'pro': False, # if it is integrated with CCXT Pro for WebSocket support
|
|
1917
|
+
'alias': False, # whether self exchange is an alias to another exchange
|
|
1918
|
+
'dex': False,
|
|
1919
|
+
'has': {
|
|
1920
|
+
'publicAPI': True,
|
|
1921
|
+
'privateAPI': True,
|
|
1922
|
+
'CORS': None,
|
|
1923
|
+
'sandbox': None,
|
|
1924
|
+
'spot': None,
|
|
1925
|
+
'margin': None,
|
|
1926
|
+
'swap': None,
|
|
1927
|
+
'future': None,
|
|
1928
|
+
'option': None,
|
|
1929
|
+
'addMargin': None,
|
|
1930
|
+
'borrowCrossMargin': None,
|
|
1931
|
+
'borrowIsolatedMargin': None,
|
|
1932
|
+
'borrowMargin': None,
|
|
1933
|
+
'cancelAllOrders': None,
|
|
1934
|
+
'cancelAllOrdersWs': None,
|
|
1935
|
+
'cancelOrder': True,
|
|
1936
|
+
'cancelOrderWs': None,
|
|
1937
|
+
'cancelOrders': None,
|
|
1938
|
+
'cancelOrdersWs': None,
|
|
1939
|
+
'closeAllPositions': None,
|
|
1940
|
+
'closePosition': None,
|
|
1941
|
+
'createDepositAddress': None,
|
|
1942
|
+
'createLimitBuyOrder': None,
|
|
1943
|
+
'createLimitBuyOrderWs': None,
|
|
1944
|
+
'createLimitOrder': True,
|
|
1945
|
+
'createLimitOrderWs': None,
|
|
1946
|
+
'createLimitSellOrder': None,
|
|
1947
|
+
'createLimitSellOrderWs': None,
|
|
1948
|
+
'createMarketBuyOrder': None,
|
|
1949
|
+
'createMarketBuyOrderWs': None,
|
|
1950
|
+
'createMarketBuyOrderWithCost': None,
|
|
1951
|
+
'createMarketBuyOrderWithCostWs': None,
|
|
1952
|
+
'createMarketOrder': True,
|
|
1953
|
+
'createMarketOrderWs': True,
|
|
1954
|
+
'createMarketOrderWithCost': None,
|
|
1955
|
+
'createMarketOrderWithCostWs': None,
|
|
1956
|
+
'createMarketSellOrder': None,
|
|
1957
|
+
'createMarketSellOrderWs': None,
|
|
1958
|
+
'createMarketSellOrderWithCost': None,
|
|
1959
|
+
'createMarketSellOrderWithCostWs': None,
|
|
1960
|
+
'createOrder': True,
|
|
1961
|
+
'createOrderWs': None,
|
|
1962
|
+
'createOrders': None,
|
|
1963
|
+
'createOrderWithTakeProfitAndStopLoss': None,
|
|
1964
|
+
'createOrderWithTakeProfitAndStopLossWs': None,
|
|
1965
|
+
'createPostOnlyOrder': None,
|
|
1966
|
+
'createPostOnlyOrderWs': None,
|
|
1967
|
+
'createReduceOnlyOrder': None,
|
|
1968
|
+
'createReduceOnlyOrderWs': None,
|
|
1969
|
+
'createStopLimitOrder': None,
|
|
1970
|
+
'createStopLimitOrderWs': None,
|
|
1971
|
+
'createStopLossOrder': None,
|
|
1972
|
+
'createStopLossOrderWs': None,
|
|
1973
|
+
'createStopMarketOrder': None,
|
|
1974
|
+
'createStopMarketOrderWs': None,
|
|
1975
|
+
'createStopOrder': None,
|
|
1976
|
+
'createStopOrderWs': None,
|
|
1977
|
+
'createTakeProfitOrder': None,
|
|
1978
|
+
'createTakeProfitOrderWs': None,
|
|
1979
|
+
'createTrailingAmountOrder': None,
|
|
1980
|
+
'createTrailingAmountOrderWs': None,
|
|
1981
|
+
'createTrailingPercentOrder': None,
|
|
1982
|
+
'createTrailingPercentOrderWs': None,
|
|
1983
|
+
'createTriggerOrder': None,
|
|
1984
|
+
'createTriggerOrderWs': None,
|
|
1985
|
+
'deposit': None,
|
|
1986
|
+
'editOrder': 'emulated',
|
|
1987
|
+
'editOrders': None,
|
|
1988
|
+
'editOrderWs': None,
|
|
1989
|
+
'fetchAccounts': None,
|
|
1990
|
+
'fetchBalance': True,
|
|
1991
|
+
'fetchBalanceWs': None,
|
|
1992
|
+
'fetchBidsAsks': None,
|
|
1993
|
+
'fetchBorrowInterest': None,
|
|
1994
|
+
'fetchBorrowRate': None,
|
|
1995
|
+
'fetchBorrowRateHistories': None,
|
|
1996
|
+
'fetchBorrowRateHistory': None,
|
|
1997
|
+
'fetchBorrowRates': None,
|
|
1998
|
+
'fetchBorrowRatesPerSymbol': None,
|
|
1999
|
+
'fetchCanceledAndClosedOrders': None,
|
|
2000
|
+
'fetchCanceledOrders': None,
|
|
2001
|
+
'fetchClosedOrder': None,
|
|
2002
|
+
'fetchClosedOrders': None,
|
|
2003
|
+
'fetchClosedOrdersWs': None,
|
|
2004
|
+
'fetchConvertCurrencies': None,
|
|
2005
|
+
'fetchConvertQuote': None,
|
|
2006
|
+
'fetchConvertTrade': None,
|
|
2007
|
+
'fetchConvertTradeHistory': None,
|
|
2008
|
+
'fetchCrossBorrowRate': None,
|
|
2009
|
+
'fetchCrossBorrowRates': None,
|
|
2010
|
+
'fetchCurrencies': 'emulated',
|
|
2011
|
+
'fetchCurrenciesWs': 'emulated',
|
|
2012
|
+
'fetchDeposit': None,
|
|
2013
|
+
'fetchDepositAddress': None,
|
|
2014
|
+
'fetchDepositAddresses': None,
|
|
2015
|
+
'fetchDepositAddressesByNetwork': None,
|
|
2016
|
+
'fetchDeposits': None,
|
|
2017
|
+
'fetchDepositsWithdrawals': None,
|
|
2018
|
+
'fetchDepositsWs': None,
|
|
2019
|
+
'fetchDepositWithdrawFee': None,
|
|
2020
|
+
'fetchDepositWithdrawFees': None,
|
|
2021
|
+
'fetchFundingHistory': None,
|
|
2022
|
+
'fetchFundingRate': None,
|
|
2023
|
+
'fetchFundingRateHistory': None,
|
|
2024
|
+
'fetchFundingInterval': None,
|
|
2025
|
+
'fetchFundingIntervals': None,
|
|
2026
|
+
'fetchFundingRates': None,
|
|
2027
|
+
'fetchGreeks': None,
|
|
2028
|
+
'fetchIndexOHLCV': None,
|
|
2029
|
+
'fetchIsolatedBorrowRate': None,
|
|
2030
|
+
'fetchIsolatedBorrowRates': None,
|
|
2031
|
+
'fetchMarginAdjustmentHistory': None,
|
|
2032
|
+
'fetchIsolatedPositions': None,
|
|
2033
|
+
'fetchL2OrderBook': True,
|
|
2034
|
+
'fetchL3OrderBook': None,
|
|
2035
|
+
'fetchLastPrices': None,
|
|
2036
|
+
'fetchLedger': None,
|
|
2037
|
+
'fetchLedgerEntry': None,
|
|
2038
|
+
'fetchLeverage': None,
|
|
2039
|
+
'fetchLeverages': None,
|
|
2040
|
+
'fetchLeverageTiers': None,
|
|
2041
|
+
'fetchLiquidations': None,
|
|
2042
|
+
'fetchLongShortRatio': None,
|
|
2043
|
+
'fetchLongShortRatioHistory': None,
|
|
2044
|
+
'fetchMarginMode': None,
|
|
2045
|
+
'fetchMarginModes': None,
|
|
2046
|
+
'fetchMarketLeverageTiers': None,
|
|
2047
|
+
'fetchMarkets': True,
|
|
2048
|
+
'fetchMarketsWs': None,
|
|
2049
|
+
'fetchMarkOHLCV': None,
|
|
2050
|
+
'fetchMyLiquidations': None,
|
|
2051
|
+
'fetchMySettlementHistory': None,
|
|
2052
|
+
'fetchMyTrades': None,
|
|
2053
|
+
'fetchMyTradesWs': None,
|
|
2054
|
+
'fetchOHLCV': None,
|
|
2055
|
+
'fetchOHLCVWs': None,
|
|
2056
|
+
'fetchOpenInterest': None,
|
|
2057
|
+
'fetchOpenInterests': None,
|
|
2058
|
+
'fetchOpenInterestHistory': None,
|
|
2059
|
+
'fetchOpenOrder': None,
|
|
2060
|
+
'fetchOpenOrders': None,
|
|
2061
|
+
'fetchOpenOrdersWs': None,
|
|
2062
|
+
'fetchOption': None,
|
|
2063
|
+
'fetchOptionChain': None,
|
|
2064
|
+
'fetchOrder': None,
|
|
2065
|
+
'fetchOrderBook': True,
|
|
2066
|
+
'fetchOrderBooks': None,
|
|
2067
|
+
'fetchOrderBookWs': None,
|
|
2068
|
+
'fetchOrders': None,
|
|
2069
|
+
'fetchOrdersByStatus': None,
|
|
2070
|
+
'fetchOrdersWs': None,
|
|
2071
|
+
'fetchOrderTrades': None,
|
|
2072
|
+
'fetchOrderWs': None,
|
|
2073
|
+
'fetchPosition': None,
|
|
2074
|
+
'fetchPositionHistory': None,
|
|
2075
|
+
'fetchPositionsHistory': None,
|
|
2076
|
+
'fetchPositionWs': None,
|
|
2077
|
+
'fetchPositionMode': None,
|
|
2078
|
+
'fetchPositions': None,
|
|
2079
|
+
'fetchPositionsWs': None,
|
|
2080
|
+
'fetchPositionsForSymbol': None,
|
|
2081
|
+
'fetchPositionsForSymbolWs': None,
|
|
2082
|
+
'fetchPositionsRisk': None,
|
|
2083
|
+
'fetchPremiumIndexOHLCV': None,
|
|
2084
|
+
'fetchSettlementHistory': None,
|
|
2085
|
+
'fetchStatus': None,
|
|
2086
|
+
'fetchTicker': True,
|
|
2087
|
+
'fetchTickerWs': None,
|
|
2088
|
+
'fetchTickers': None,
|
|
2089
|
+
'fetchMarkPrices': None,
|
|
2090
|
+
'fetchTickersWs': None,
|
|
2091
|
+
'fetchTime': None,
|
|
2092
|
+
'fetchTrades': True,
|
|
2093
|
+
'fetchTradesWs': None,
|
|
2094
|
+
'fetchTradingFee': None,
|
|
2095
|
+
'fetchTradingFees': None,
|
|
2096
|
+
'fetchTradingFeesWs': None,
|
|
2097
|
+
'fetchTradingLimits': None,
|
|
2098
|
+
'fetchTransactionFee': None,
|
|
2099
|
+
'fetchTransactionFees': None,
|
|
2100
|
+
'fetchTransactions': None,
|
|
2101
|
+
'fetchTransfer': None,
|
|
2102
|
+
'fetchTransfers': None,
|
|
2103
|
+
'fetchUnderlyingAssets': None,
|
|
2104
|
+
'fetchVolatilityHistory': None,
|
|
2105
|
+
'fetchWithdrawAddresses': None,
|
|
2106
|
+
'fetchWithdrawal': None,
|
|
2107
|
+
'fetchWithdrawals': None,
|
|
2108
|
+
'fetchWithdrawalsWs': None,
|
|
2109
|
+
'fetchWithdrawalWhitelist': None,
|
|
2110
|
+
'reduceMargin': None,
|
|
2111
|
+
'repayCrossMargin': None,
|
|
2112
|
+
'repayIsolatedMargin': None,
|
|
2113
|
+
'setLeverage': None,
|
|
2114
|
+
'setMargin': None,
|
|
2115
|
+
'setMarginMode': None,
|
|
2116
|
+
'setPositionMode': None,
|
|
2117
|
+
'signIn': None,
|
|
2118
|
+
'transfer': None,
|
|
2119
|
+
'watchBalance': None,
|
|
2120
|
+
'watchMyTrades': None,
|
|
2121
|
+
'watchOHLCV': None,
|
|
2122
|
+
'watchOHLCVForSymbols': None,
|
|
2123
|
+
'watchOrderBook': None,
|
|
2124
|
+
'watchBidsAsks': None,
|
|
2125
|
+
'watchOrderBookForSymbols': None,
|
|
2126
|
+
'watchOrders': None,
|
|
2127
|
+
'watchOrdersForSymbols': None,
|
|
2128
|
+
'watchPosition': None,
|
|
2129
|
+
'watchPositions': None,
|
|
2130
|
+
'watchStatus': None,
|
|
2131
|
+
'watchTicker': None,
|
|
2132
|
+
'watchTickers': None,
|
|
2133
|
+
'watchTrades': None,
|
|
2134
|
+
'watchTradesForSymbols': None,
|
|
2135
|
+
'watchLiquidations': None,
|
|
2136
|
+
'watchLiquidationsForSymbols': None,
|
|
2137
|
+
'watchMyLiquidations': None,
|
|
2138
|
+
'unWatchOrders': None,
|
|
2139
|
+
'unWatchTrades': None,
|
|
2140
|
+
'unWatchTradesForSymbols': None,
|
|
2141
|
+
'unWatchOHLCVForSymbols': None,
|
|
2142
|
+
'unWatchOrderBookForSymbols': None,
|
|
2143
|
+
'unWatchPositions': None,
|
|
2144
|
+
'unWatchOrderBook': None,
|
|
2145
|
+
'unWatchTickers': None,
|
|
2146
|
+
'unWatchMyTrades': None,
|
|
2147
|
+
'unWatchTicker': None,
|
|
2148
|
+
'unWatchOHLCV': None,
|
|
2149
|
+
'watchMyLiquidationsForSymbols': None,
|
|
2150
|
+
'withdraw': None,
|
|
2151
|
+
'ws': None,
|
|
2152
|
+
},
|
|
2153
|
+
'urls': {
|
|
2154
|
+
'logo': None,
|
|
2155
|
+
'api': None,
|
|
2156
|
+
'www': None,
|
|
2157
|
+
'doc': None,
|
|
2158
|
+
'fees': None,
|
|
2159
|
+
},
|
|
2160
|
+
'api': None,
|
|
2161
|
+
'requiredCredentials': {
|
|
2162
|
+
'apiKey': True,
|
|
2163
|
+
'secret': True,
|
|
2164
|
+
'uid': False,
|
|
2165
|
+
'accountId': False,
|
|
2166
|
+
'login': False,
|
|
2167
|
+
'password': False,
|
|
2168
|
+
'twofa': False, # 2-factor authentication(one-time password key)
|
|
2169
|
+
'privateKey': False, # a "0x"-prefixed hexstring private key for a wallet
|
|
2170
|
+
'walletAddress': False, # the wallet address "0x"-prefixed hexstring
|
|
2171
|
+
'token': False, # reserved for HTTP auth in some cases
|
|
2172
|
+
},
|
|
2173
|
+
'markets': None, # to be filled manually or by fetchMarkets
|
|
2174
|
+
'currencies': {}, # to be filled manually or by fetchMarkets
|
|
2175
|
+
'timeframes': None, # redefine if the exchange has.fetchOHLCV
|
|
2176
|
+
'fees': {
|
|
2177
|
+
'trading': {
|
|
2178
|
+
'tierBased': None,
|
|
2179
|
+
'percentage': None,
|
|
2180
|
+
'taker': None,
|
|
2181
|
+
'maker': None,
|
|
2182
|
+
},
|
|
2183
|
+
'funding': {
|
|
2184
|
+
'tierBased': None,
|
|
2185
|
+
'percentage': None,
|
|
2186
|
+
'withdraw': {},
|
|
2187
|
+
'deposit': {},
|
|
2188
|
+
},
|
|
2189
|
+
},
|
|
2190
|
+
'status': {
|
|
2191
|
+
'status': 'ok',
|
|
2192
|
+
'updated': None,
|
|
2193
|
+
'eta': None,
|
|
2194
|
+
'url': None,
|
|
2195
|
+
},
|
|
2196
|
+
'exceptions': None,
|
|
2197
|
+
'httpExceptions': {
|
|
2198
|
+
'422': ExchangeError,
|
|
2199
|
+
'418': DDoSProtection,
|
|
2200
|
+
'429': RateLimitExceeded,
|
|
2201
|
+
'404': ExchangeNotAvailable,
|
|
2202
|
+
'409': ExchangeNotAvailable,
|
|
2203
|
+
'410': ExchangeNotAvailable,
|
|
2204
|
+
'451': ExchangeNotAvailable,
|
|
2205
|
+
'500': ExchangeNotAvailable,
|
|
2206
|
+
'501': ExchangeNotAvailable,
|
|
2207
|
+
'502': ExchangeNotAvailable,
|
|
2208
|
+
'520': ExchangeNotAvailable,
|
|
2209
|
+
'521': ExchangeNotAvailable,
|
|
2210
|
+
'522': ExchangeNotAvailable,
|
|
2211
|
+
'525': ExchangeNotAvailable,
|
|
2212
|
+
'526': ExchangeNotAvailable,
|
|
2213
|
+
'400': ExchangeNotAvailable,
|
|
2214
|
+
'403': ExchangeNotAvailable,
|
|
2215
|
+
'405': ExchangeNotAvailable,
|
|
2216
|
+
'503': ExchangeNotAvailable,
|
|
2217
|
+
'530': ExchangeNotAvailable,
|
|
2218
|
+
'408': RequestTimeout,
|
|
2219
|
+
'504': RequestTimeout,
|
|
2220
|
+
'401': AuthenticationError,
|
|
2221
|
+
'407': AuthenticationError,
|
|
2222
|
+
'511': AuthenticationError,
|
|
2223
|
+
},
|
|
2224
|
+
'commonCurrencies': {
|
|
2225
|
+
'XBT': 'BTC',
|
|
2226
|
+
'BCHSV': 'BSV',
|
|
2227
|
+
},
|
|
2228
|
+
'precisionMode': TICK_SIZE,
|
|
2229
|
+
'paddingMode': NO_PADDING,
|
|
2230
|
+
'limits': {
|
|
2231
|
+
'leverage': {'min': None, 'max': None},
|
|
2232
|
+
'amount': {'min': None, 'max': None},
|
|
2233
|
+
'price': {'min': None, 'max': None},
|
|
2234
|
+
'cost': {'min': None, 'max': None},
|
|
2235
|
+
},
|
|
2236
|
+
}
|
|
2237
|
+
|
|
1815
2238
|
def safe_bool_n(self, dictionaryOrList, keys: List[IndexType], defaultValue: bool = None):
|
|
1816
2239
|
"""
|
|
1817
|
-
|
|
2240
|
+
@ignore
|
|
1818
2241
|
safely extract boolean value from dictionary or list
|
|
1819
2242
|
:returns bool | None:
|
|
1820
2243
|
"""
|
|
@@ -1825,7 +2248,7 @@ class Exchange(object):
|
|
|
1825
2248
|
|
|
1826
2249
|
def safe_bool_2(self, dictionary, key1: IndexType, key2: IndexType, defaultValue: bool = None):
|
|
1827
2250
|
"""
|
|
1828
|
-
|
|
2251
|
+
@ignore
|
|
1829
2252
|
safely extract boolean value from dictionary or list
|
|
1830
2253
|
:returns bool | None:
|
|
1831
2254
|
"""
|
|
@@ -1833,7 +2256,7 @@ class Exchange(object):
|
|
|
1833
2256
|
|
|
1834
2257
|
def safe_bool(self, dictionary, key: IndexType, defaultValue: bool = None):
|
|
1835
2258
|
"""
|
|
1836
|
-
|
|
2259
|
+
@ignore
|
|
1837
2260
|
safely extract boolean value from dictionary or list
|
|
1838
2261
|
:returns bool | None:
|
|
1839
2262
|
"""
|
|
@@ -1841,20 +2264,21 @@ class Exchange(object):
|
|
|
1841
2264
|
|
|
1842
2265
|
def safe_dict_n(self, dictionaryOrList, keys: List[IndexType], defaultValue: dict = None):
|
|
1843
2266
|
"""
|
|
1844
|
-
|
|
2267
|
+
@ignore
|
|
1845
2268
|
safely extract a dictionary from dictionary or list
|
|
1846
2269
|
:returns dict | None:
|
|
1847
2270
|
"""
|
|
1848
2271
|
value = self.safe_value_n(dictionaryOrList, keys, defaultValue)
|
|
1849
2272
|
if value is None:
|
|
1850
2273
|
return defaultValue
|
|
1851
|
-
if isinstance(value, dict):
|
|
1852
|
-
|
|
2274
|
+
if (isinstance(value, dict)):
|
|
2275
|
+
if not isinstance(value, list):
|
|
2276
|
+
return value
|
|
1853
2277
|
return defaultValue
|
|
1854
2278
|
|
|
1855
2279
|
def safe_dict(self, dictionary, key: IndexType, defaultValue: dict = None):
|
|
1856
2280
|
"""
|
|
1857
|
-
|
|
2281
|
+
@ignore
|
|
1858
2282
|
safely extract a dictionary from dictionary or list
|
|
1859
2283
|
:returns dict | None:
|
|
1860
2284
|
"""
|
|
@@ -1862,7 +2286,7 @@ class Exchange(object):
|
|
|
1862
2286
|
|
|
1863
2287
|
def safe_dict_2(self, dictionary, key1: IndexType, key2: str, defaultValue: dict = None):
|
|
1864
2288
|
"""
|
|
1865
|
-
|
|
2289
|
+
@ignore
|
|
1866
2290
|
safely extract a dictionary from dictionary or list
|
|
1867
2291
|
:returns dict | None:
|
|
1868
2292
|
"""
|
|
@@ -1870,7 +2294,7 @@ class Exchange(object):
|
|
|
1870
2294
|
|
|
1871
2295
|
def safe_list_n(self, dictionaryOrList, keys: List[IndexType], defaultValue: List[Any] = None):
|
|
1872
2296
|
"""
|
|
1873
|
-
|
|
2297
|
+
@ignore
|
|
1874
2298
|
safely extract an Array from dictionary or list
|
|
1875
2299
|
:returns Array | None:
|
|
1876
2300
|
"""
|
|
@@ -1883,7 +2307,7 @@ class Exchange(object):
|
|
|
1883
2307
|
|
|
1884
2308
|
def safe_list_2(self, dictionaryOrList, key1: IndexType, key2: str, defaultValue: List[Any] = None):
|
|
1885
2309
|
"""
|
|
1886
|
-
|
|
2310
|
+
@ignore
|
|
1887
2311
|
safely extract an Array from dictionary or list
|
|
1888
2312
|
:returns Array | None:
|
|
1889
2313
|
"""
|
|
@@ -1891,7 +2315,7 @@ class Exchange(object):
|
|
|
1891
2315
|
|
|
1892
2316
|
def safe_list(self, dictionaryOrList, key: IndexType, defaultValue: List[Any] = None):
|
|
1893
2317
|
"""
|
|
1894
|
-
|
|
2318
|
+
@ignore
|
|
1895
2319
|
safely extract an Array from dictionary or list
|
|
1896
2320
|
:returns Array | None:
|
|
1897
2321
|
"""
|
|
@@ -1904,10 +2328,21 @@ class Exchange(object):
|
|
|
1904
2328
|
def handle_delta(self, bookside, delta):
|
|
1905
2329
|
raise NotSupported(self.id + ' handleDelta not supported yet')
|
|
1906
2330
|
|
|
2331
|
+
def handle_deltas_with_keys(self, bookSide: Any, deltas, priceKey: IndexType = 0, amountKey: IndexType = 1, countOrIdKey: IndexType = 2):
|
|
2332
|
+
for i in range(0, len(deltas)):
|
|
2333
|
+
bidAsk = self.parse_bid_ask(deltas[i], priceKey, amountKey, countOrIdKey)
|
|
2334
|
+
bookSide.storeArray(bidAsk)
|
|
2335
|
+
|
|
1907
2336
|
def get_cache_index(self, orderbook, deltas):
|
|
1908
2337
|
# return the first index of the cache that can be applied to the orderbook or -1 if not possible
|
|
1909
2338
|
return -1
|
|
1910
2339
|
|
|
2340
|
+
def arrays_concat(self, arraysOfArrays: List[Any]):
|
|
2341
|
+
result = []
|
|
2342
|
+
for i in range(0, len(arraysOfArrays)):
|
|
2343
|
+
result = self.array_concat(result, arraysOfArrays[i])
|
|
2344
|
+
return result
|
|
2345
|
+
|
|
1911
2346
|
def find_timeframe(self, timeframe, timeframes=None):
|
|
1912
2347
|
if timeframes is None:
|
|
1913
2348
|
timeframes = self.timeframes
|
|
@@ -1943,58 +2378,58 @@ class Exchange(object):
|
|
|
1943
2378
|
length = len(usedProxies)
|
|
1944
2379
|
if length > 1:
|
|
1945
2380
|
joinedProxyNames = ','.join(usedProxies)
|
|
1946
|
-
raise
|
|
2381
|
+
raise InvalidProxySettings(self.id + ' you have multiple conflicting proxy settings(' + joinedProxyNames + '), please use only one from : proxyUrl, proxy_url, proxyUrlCallback, proxy_url_callback')
|
|
1947
2382
|
return proxyUrl
|
|
1948
2383
|
|
|
2384
|
+
def url_encoder_for_proxy_url(self, targetUrl: str):
|
|
2385
|
+
# to be overriden
|
|
2386
|
+
includesQuery = targetUrl.find('?') >= 0
|
|
2387
|
+
finalUrl = self.encode_uri_component(targetUrl) if includesQuery else targetUrl
|
|
2388
|
+
return finalUrl
|
|
2389
|
+
|
|
1949
2390
|
def check_proxy_settings(self, url: Str = None, method: Str = None, headers=None, body=None):
|
|
1950
2391
|
usedProxies = []
|
|
1951
2392
|
httpProxy = None
|
|
1952
2393
|
httpsProxy = None
|
|
1953
2394
|
socksProxy = None
|
|
1954
2395
|
# httpProxy
|
|
1955
|
-
|
|
2396
|
+
isHttpProxyDefined = self.value_is_defined(self.httpProxy)
|
|
2397
|
+
isHttp_proxy_defined = self.value_is_defined(self.http_proxy)
|
|
2398
|
+
if isHttpProxyDefined or isHttp_proxy_defined:
|
|
1956
2399
|
usedProxies.append('httpProxy')
|
|
1957
|
-
httpProxy = self.httpProxy
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
if self.httpProxyCallback is not None:
|
|
2400
|
+
httpProxy = self.httpProxy if isHttpProxyDefined else self.http_proxy
|
|
2401
|
+
ishttpProxyCallbackDefined = self.value_is_defined(self.httpProxyCallback)
|
|
2402
|
+
ishttp_proxy_callback_defined = self.value_is_defined(self.http_proxy_callback)
|
|
2403
|
+
if ishttpProxyCallbackDefined or ishttp_proxy_callback_defined:
|
|
1962
2404
|
usedProxies.append('httpProxyCallback')
|
|
1963
|
-
httpProxy = self.httpProxyCallback(url, method, headers, body)
|
|
1964
|
-
if self.http_proxy_callback is not None:
|
|
1965
|
-
usedProxies.append('http_proxy_callback')
|
|
1966
|
-
httpProxy = self.http_proxy_callback(url, method, headers, body)
|
|
2405
|
+
httpProxy = self.httpProxyCallback(url, method, headers, body) if ishttpProxyCallbackDefined else self.http_proxy_callback(url, method, headers, body)
|
|
1967
2406
|
# httpsProxy
|
|
1968
|
-
|
|
2407
|
+
isHttpsProxyDefined = self.value_is_defined(self.httpsProxy)
|
|
2408
|
+
isHttps_proxy_defined = self.value_is_defined(self.https_proxy)
|
|
2409
|
+
if isHttpsProxyDefined or isHttps_proxy_defined:
|
|
1969
2410
|
usedProxies.append('httpsProxy')
|
|
1970
|
-
httpsProxy = self.httpsProxy
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
if self.httpsProxyCallback is not None:
|
|
2411
|
+
httpsProxy = self.httpsProxy if isHttpsProxyDefined else self.https_proxy
|
|
2412
|
+
ishttpsProxyCallbackDefined = self.value_is_defined(self.httpsProxyCallback)
|
|
2413
|
+
ishttps_proxy_callback_defined = self.value_is_defined(self.https_proxy_callback)
|
|
2414
|
+
if ishttpsProxyCallbackDefined or ishttps_proxy_callback_defined:
|
|
1975
2415
|
usedProxies.append('httpsProxyCallback')
|
|
1976
|
-
httpsProxy = self.httpsProxyCallback(url, method, headers, body)
|
|
1977
|
-
if self.https_proxy_callback is not None:
|
|
1978
|
-
usedProxies.append('https_proxy_callback')
|
|
1979
|
-
httpsProxy = self.https_proxy_callback(url, method, headers, body)
|
|
2416
|
+
httpsProxy = self.httpsProxyCallback(url, method, headers, body) if ishttpsProxyCallbackDefined else self.https_proxy_callback(url, method, headers, body)
|
|
1980
2417
|
# socksProxy
|
|
1981
|
-
|
|
2418
|
+
isSocksProxyDefined = self.value_is_defined(self.socksProxy)
|
|
2419
|
+
isSocks_proxy_defined = self.value_is_defined(self.socks_proxy)
|
|
2420
|
+
if isSocksProxyDefined or isSocks_proxy_defined:
|
|
1982
2421
|
usedProxies.append('socksProxy')
|
|
1983
|
-
socksProxy = self.socksProxy
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
if self.socksProxyCallback is not None:
|
|
2422
|
+
socksProxy = self.socksProxy if isSocksProxyDefined else self.socks_proxy
|
|
2423
|
+
issocksProxyCallbackDefined = self.value_is_defined(self.socksProxyCallback)
|
|
2424
|
+
issocks_proxy_callback_defined = self.value_is_defined(self.socks_proxy_callback)
|
|
2425
|
+
if issocksProxyCallbackDefined or issocks_proxy_callback_defined:
|
|
1988
2426
|
usedProxies.append('socksProxyCallback')
|
|
1989
|
-
socksProxy = self.socksProxyCallback(url, method, headers, body)
|
|
1990
|
-
if self.socks_proxy_callback is not None:
|
|
1991
|
-
usedProxies.append('socks_proxy_callback')
|
|
1992
|
-
socksProxy = self.socks_proxy_callback(url, method, headers, body)
|
|
2427
|
+
socksProxy = self.socksProxyCallback(url, method, headers, body) if issocksProxyCallbackDefined else self.socks_proxy_callback(url, method, headers, body)
|
|
1993
2428
|
# check
|
|
1994
2429
|
length = len(usedProxies)
|
|
1995
2430
|
if length > 1:
|
|
1996
2431
|
joinedProxyNames = ','.join(usedProxies)
|
|
1997
|
-
raise
|
|
2432
|
+
raise InvalidProxySettings(self.id + ' you have multiple conflicting proxy settings(' + joinedProxyNames + '), please use only one from: httpProxy, httpsProxy, httpProxyCallback, httpsProxyCallback, socksProxy, socksProxyCallback')
|
|
1998
2433
|
return [httpProxy, httpsProxy, socksProxy]
|
|
1999
2434
|
|
|
2000
2435
|
def check_ws_proxy_settings(self):
|
|
@@ -2003,36 +2438,43 @@ class Exchange(object):
|
|
|
2003
2438
|
wssProxy = None
|
|
2004
2439
|
wsSocksProxy = None
|
|
2005
2440
|
# ws proxy
|
|
2006
|
-
|
|
2441
|
+
isWsProxyDefined = self.value_is_defined(self.wsProxy)
|
|
2442
|
+
is_ws_proxy_defined = self.value_is_defined(self.ws_proxy)
|
|
2443
|
+
if isWsProxyDefined or is_ws_proxy_defined:
|
|
2007
2444
|
usedProxies.append('wsProxy')
|
|
2008
|
-
wsProxy = self.wsProxy
|
|
2009
|
-
if self.value_is_defined(self.ws_proxy):
|
|
2010
|
-
usedProxies.append('ws_proxy')
|
|
2011
|
-
wsProxy = self.ws_proxy
|
|
2445
|
+
wsProxy = self.wsProxy if (isWsProxyDefined) else self.ws_proxy
|
|
2012
2446
|
# wss proxy
|
|
2013
|
-
|
|
2447
|
+
isWssProxyDefined = self.value_is_defined(self.wssProxy)
|
|
2448
|
+
is_wss_proxy_defined = self.value_is_defined(self.wss_proxy)
|
|
2449
|
+
if isWssProxyDefined or is_wss_proxy_defined:
|
|
2014
2450
|
usedProxies.append('wssProxy')
|
|
2015
|
-
wssProxy = self.wssProxy
|
|
2016
|
-
if self.value_is_defined(self.wss_proxy):
|
|
2017
|
-
usedProxies.append('wss_proxy')
|
|
2018
|
-
wssProxy = self.wss_proxy
|
|
2451
|
+
wssProxy = self.wssProxy if (isWssProxyDefined) else self.wss_proxy
|
|
2019
2452
|
# ws socks proxy
|
|
2020
|
-
|
|
2453
|
+
isWsSocksProxyDefined = self.value_is_defined(self.wsSocksProxy)
|
|
2454
|
+
is_ws_socks_proxy_defined = self.value_is_defined(self.ws_socks_proxy)
|
|
2455
|
+
if isWsSocksProxyDefined or is_ws_socks_proxy_defined:
|
|
2021
2456
|
usedProxies.append('wsSocksProxy')
|
|
2022
|
-
wsSocksProxy = self.wsSocksProxy
|
|
2023
|
-
if self.value_is_defined(self.ws_socks_proxy):
|
|
2024
|
-
usedProxies.append('ws_socks_proxy')
|
|
2025
|
-
wsSocksProxy = self.ws_socks_proxy
|
|
2457
|
+
wsSocksProxy = self.wsSocksProxy if (isWsSocksProxyDefined) else self.ws_socks_proxy
|
|
2026
2458
|
# check
|
|
2027
2459
|
length = len(usedProxies)
|
|
2028
2460
|
if length > 1:
|
|
2029
2461
|
joinedProxyNames = ','.join(usedProxies)
|
|
2030
|
-
raise
|
|
2462
|
+
raise InvalidProxySettings(self.id + ' you have multiple conflicting proxy settings(' + joinedProxyNames + '), please use only one from: wsProxy, wssProxy, wsSocksProxy')
|
|
2031
2463
|
return [wsProxy, wssProxy, wsSocksProxy]
|
|
2032
2464
|
|
|
2033
2465
|
def check_conflicting_proxies(self, proxyAgentSet, proxyUrlSet):
|
|
2034
2466
|
if proxyAgentSet and proxyUrlSet:
|
|
2035
|
-
raise
|
|
2467
|
+
raise InvalidProxySettings(self.id + ' you have multiple conflicting proxy settings, please use only one from : proxyUrl, httpProxy, httpsProxy, socksProxy')
|
|
2468
|
+
|
|
2469
|
+
def check_address(self, address: Str = None):
|
|
2470
|
+
if address is None:
|
|
2471
|
+
raise InvalidAddress(self.id + ' address is None')
|
|
2472
|
+
# check the address is not the same letter like 'aaaaa' nor too short nor has a space
|
|
2473
|
+
uniqChars = (self.unique(self.string_to_chars_array(address)))
|
|
2474
|
+
length = len(uniqChars) # py transpiler trick
|
|
2475
|
+
if length == 1 or len(address) < self.minFundingAddressLength or address.find(' ') > -1:
|
|
2476
|
+
raise InvalidAddress(self.id + ' address is invalid or has less than ' + str(self.minFundingAddressLength) + ' characters: "' + str(address) + '"')
|
|
2477
|
+
return address
|
|
2036
2478
|
|
|
2037
2479
|
def find_message_hashes(self, client, element: str):
|
|
2038
2480
|
result = []
|
|
@@ -2056,9 +2498,17 @@ class Exchange(object):
|
|
|
2056
2498
|
if fromStart:
|
|
2057
2499
|
if limit > arrayLength:
|
|
2058
2500
|
limit = arrayLength
|
|
2059
|
-
array = self.array_slice(array, 0, limit) if ascending else self.array_slice(array, -limit)
|
|
2501
|
+
# array = self.array_slice(array, 0, limit) if ascending else self.array_slice(array, -limit)
|
|
2502
|
+
if ascending:
|
|
2503
|
+
array = self.array_slice(array, 0, limit)
|
|
2504
|
+
else:
|
|
2505
|
+
array = self.array_slice(array, -limit)
|
|
2060
2506
|
else:
|
|
2061
|
-
array = self.array_slice(array, -limit) if ascending else self.array_slice(array, 0, limit)
|
|
2507
|
+
# array = self.array_slice(array, -limit) if ascending else self.array_slice(array, 0, limit)
|
|
2508
|
+
if ascending:
|
|
2509
|
+
array = self.array_slice(array, -limit)
|
|
2510
|
+
else:
|
|
2511
|
+
array = self.array_slice(array, 0, limit)
|
|
2062
2512
|
return array
|
|
2063
2513
|
|
|
2064
2514
|
def filter_by_since_limit(self, array: List[object], since: Int = None, limit: Int = None, key: IndexType = 'timestamp', tail=False):
|
|
@@ -2092,7 +2542,7 @@ class Exchange(object):
|
|
|
2092
2542
|
entryFiledEqualValue = entry[field] == value
|
|
2093
2543
|
firstCondition = entryFiledEqualValue if valueIsDefined else True
|
|
2094
2544
|
entryKeyValue = self.safe_value(entry, key)
|
|
2095
|
-
entryKeyGESince = (entryKeyValue) and since and (entryKeyValue >= since)
|
|
2545
|
+
entryKeyGESince = (entryKeyValue) and (since is not None) and (entryKeyValue >= since)
|
|
2096
2546
|
secondCondition = entryKeyGESince if sinceIsDefined else True
|
|
2097
2547
|
if firstCondition and secondCondition:
|
|
2098
2548
|
result.append(entry)
|
|
@@ -2101,6 +2551,10 @@ class Exchange(object):
|
|
|
2101
2551
|
return self.filter_by_limit(result, limit, key, sinceIsDefined)
|
|
2102
2552
|
|
|
2103
2553
|
def set_sandbox_mode(self, enabled: bool):
|
|
2554
|
+
"""
|
|
2555
|
+
set the sandbox mode for the exchange
|
|
2556
|
+
:param boolean enabled: True to enable sandbox mode, False to disable it
|
|
2557
|
+
"""
|
|
2104
2558
|
if enabled:
|
|
2105
2559
|
if 'test' in self.urls:
|
|
2106
2560
|
if isinstance(self.urls['api'], str):
|
|
@@ -2111,6 +2565,8 @@ class Exchange(object):
|
|
|
2111
2565
|
self.urls['api'] = self.clone(self.urls['test'])
|
|
2112
2566
|
else:
|
|
2113
2567
|
raise NotSupported(self.id + ' does not have a sandbox URL')
|
|
2568
|
+
# set flag
|
|
2569
|
+
self.isSandboxModeEnabled = True
|
|
2114
2570
|
elif 'apiBackup' in self.urls:
|
|
2115
2571
|
if isinstance(self.urls['api'], str):
|
|
2116
2572
|
self.urls['api'] = self.urls['apiBackup']
|
|
@@ -2118,6 +2574,8 @@ class Exchange(object):
|
|
|
2118
2574
|
self.urls['api'] = self.clone(self.urls['apiBackup'])
|
|
2119
2575
|
newUrls = self.omit(self.urls, 'apiBackup')
|
|
2120
2576
|
self.urls = newUrls
|
|
2577
|
+
# set flag
|
|
2578
|
+
self.isSandboxModeEnabled = False
|
|
2121
2579
|
|
|
2122
2580
|
def sign(self, path, api: Any = 'public', method='GET', params={}, headers: Any = None, body: Any = None):
|
|
2123
2581
|
return {}
|
|
@@ -2150,9 +2608,18 @@ class Exchange(object):
|
|
|
2150
2608
|
def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}):
|
|
2151
2609
|
raise NotSupported(self.id + ' watchTrades() is not supported yet')
|
|
2152
2610
|
|
|
2611
|
+
def un_watch_orders(self, symbol: Str = None, params={}):
|
|
2612
|
+
raise NotSupported(self.id + ' unWatchOrders() is not supported yet')
|
|
2613
|
+
|
|
2614
|
+
def un_watch_trades(self, symbol: str, params={}):
|
|
2615
|
+
raise NotSupported(self.id + ' unWatchTrades() is not supported yet')
|
|
2616
|
+
|
|
2153
2617
|
def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}):
|
|
2154
2618
|
raise NotSupported(self.id + ' watchTradesForSymbols() is not supported yet')
|
|
2155
2619
|
|
|
2620
|
+
def un_watch_trades_for_symbols(self, symbols: List[str], params={}):
|
|
2621
|
+
raise NotSupported(self.id + ' unWatchTradesForSymbols() is not supported yet')
|
|
2622
|
+
|
|
2156
2623
|
def watch_my_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}):
|
|
2157
2624
|
raise NotSupported(self.id + ' watchMyTradesForSymbols() is not supported yet')
|
|
2158
2625
|
|
|
@@ -2162,15 +2629,27 @@ class Exchange(object):
|
|
|
2162
2629
|
def watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], since: Int = None, limit: Int = None, params={}):
|
|
2163
2630
|
raise NotSupported(self.id + ' watchOHLCVForSymbols() is not supported yet')
|
|
2164
2631
|
|
|
2632
|
+
def un_watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], params={}):
|
|
2633
|
+
raise NotSupported(self.id + ' unWatchOHLCVForSymbols() is not supported yet')
|
|
2634
|
+
|
|
2165
2635
|
def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}):
|
|
2166
2636
|
raise NotSupported(self.id + ' watchOrderBookForSymbols() is not supported yet')
|
|
2167
2637
|
|
|
2638
|
+
def un_watch_order_book_for_symbols(self, symbols: List[str], params={}):
|
|
2639
|
+
raise NotSupported(self.id + ' unWatchOrderBookForSymbols() is not supported yet')
|
|
2640
|
+
|
|
2641
|
+
def un_watch_positions(self, symbols: Strings = None, params={}):
|
|
2642
|
+
raise NotSupported(self.id + ' unWatchPositions() is not supported yet')
|
|
2643
|
+
|
|
2168
2644
|
def fetch_deposit_addresses(self, codes: Strings = None, params={}):
|
|
2169
2645
|
raise NotSupported(self.id + ' fetchDepositAddresses() is not supported yet')
|
|
2170
2646
|
|
|
2171
2647
|
def fetch_order_book(self, symbol: str, limit: Int = None, params={}):
|
|
2172
2648
|
raise NotSupported(self.id + ' fetchOrderBook() is not supported yet')
|
|
2173
2649
|
|
|
2650
|
+
def fetch_order_book_ws(self, symbol: str, limit: Int = None, params={}):
|
|
2651
|
+
raise NotSupported(self.id + ' fetchOrderBookWs() is not supported yet')
|
|
2652
|
+
|
|
2174
2653
|
def fetch_margin_mode(self, symbol: str, params={}):
|
|
2175
2654
|
if self.has['fetchMarginModes']:
|
|
2176
2655
|
marginModes = self.fetch_margin_modes([symbol], params)
|
|
@@ -2195,12 +2674,27 @@ class Exchange(object):
|
|
|
2195
2674
|
def watch_order_book(self, symbol: str, limit: Int = None, params={}):
|
|
2196
2675
|
raise NotSupported(self.id + ' watchOrderBook() is not supported yet')
|
|
2197
2676
|
|
|
2677
|
+
def un_watch_order_book(self, symbol: str, params={}):
|
|
2678
|
+
raise NotSupported(self.id + ' unWatchOrderBook() is not supported yet')
|
|
2679
|
+
|
|
2198
2680
|
def fetch_time(self, params={}):
|
|
2199
2681
|
raise NotSupported(self.id + ' fetchTime() is not supported yet')
|
|
2200
2682
|
|
|
2201
2683
|
def fetch_trading_limits(self, symbols: Strings = None, params={}):
|
|
2202
2684
|
raise NotSupported(self.id + ' fetchTradingLimits() is not supported yet')
|
|
2203
2685
|
|
|
2686
|
+
def parse_currency(self, rawCurrency: dict):
|
|
2687
|
+
raise NotSupported(self.id + ' parseCurrency() is not supported yet')
|
|
2688
|
+
|
|
2689
|
+
def parse_currencies(self, rawCurrencies):
|
|
2690
|
+
result = {}
|
|
2691
|
+
arr = self.to_array(rawCurrencies)
|
|
2692
|
+
for i in range(0, len(arr)):
|
|
2693
|
+
parsed = self.parse_currency(arr[i])
|
|
2694
|
+
code = parsed['code']
|
|
2695
|
+
result[code] = parsed
|
|
2696
|
+
return result
|
|
2697
|
+
|
|
2204
2698
|
def parse_market(self, market: dict):
|
|
2205
2699
|
raise NotSupported(self.id + ' parseMarket() is not supported yet')
|
|
2206
2700
|
|
|
@@ -2225,7 +2719,7 @@ class Exchange(object):
|
|
|
2225
2719
|
def parse_transfer(self, transfer: dict, currency: Currency = None):
|
|
2226
2720
|
raise NotSupported(self.id + ' parseTransfer() is not supported yet')
|
|
2227
2721
|
|
|
2228
|
-
def parse_account(self, account):
|
|
2722
|
+
def parse_account(self, account: dict):
|
|
2229
2723
|
raise NotSupported(self.id + ' parseAccount() is not supported yet')
|
|
2230
2724
|
|
|
2231
2725
|
def parse_ledger_entry(self, item: dict, currency: Currency = None):
|
|
@@ -2255,16 +2749,16 @@ class Exchange(object):
|
|
|
2255
2749
|
def parse_borrow_interest(self, info: dict, market: Market = None):
|
|
2256
2750
|
raise NotSupported(self.id + ' parseBorrowInterest() is not supported yet')
|
|
2257
2751
|
|
|
2258
|
-
def parse_isolated_borrow_rate(self, info, market: Market = None):
|
|
2752
|
+
def parse_isolated_borrow_rate(self, info: dict, market: Market = None):
|
|
2259
2753
|
raise NotSupported(self.id + ' parseIsolatedBorrowRate() is not supported yet')
|
|
2260
2754
|
|
|
2261
|
-
def parse_ws_trade(self, trade, market: Market = None):
|
|
2755
|
+
def parse_ws_trade(self, trade: dict, market: Market = None):
|
|
2262
2756
|
raise NotSupported(self.id + ' parseWsTrade() is not supported yet')
|
|
2263
2757
|
|
|
2264
|
-
def parse_ws_order(self, order, market: Market = None):
|
|
2758
|
+
def parse_ws_order(self, order: dict, market: Market = None):
|
|
2265
2759
|
raise NotSupported(self.id + ' parseWsOrder() is not supported yet')
|
|
2266
2760
|
|
|
2267
|
-
def parse_ws_order_trade(self, trade, market: Market = None):
|
|
2761
|
+
def parse_ws_order_trade(self, trade: dict, market: Market = None):
|
|
2268
2762
|
raise NotSupported(self.id + ' parseWsOrderTrade() is not supported yet')
|
|
2269
2763
|
|
|
2270
2764
|
def parse_ws_ohlcv(self, ohlcv, market: Market = None):
|
|
@@ -2273,6 +2767,9 @@ class Exchange(object):
|
|
|
2273
2767
|
def fetch_funding_rates(self, symbols: Strings = None, params={}):
|
|
2274
2768
|
raise NotSupported(self.id + ' fetchFundingRates() is not supported yet')
|
|
2275
2769
|
|
|
2770
|
+
def fetch_funding_intervals(self, symbols: Strings = None, params={}):
|
|
2771
|
+
raise NotSupported(self.id + ' fetchFundingIntervals() is not supported yet')
|
|
2772
|
+
|
|
2276
2773
|
def watch_funding_rate(self, symbol: str, params={}):
|
|
2277
2774
|
raise NotSupported(self.id + ' watchFundingRate() is not supported yet')
|
|
2278
2775
|
|
|
@@ -2285,13 +2782,13 @@ class Exchange(object):
|
|
|
2285
2782
|
def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}):
|
|
2286
2783
|
raise NotSupported(self.id + ' transfer() is not supported yet')
|
|
2287
2784
|
|
|
2288
|
-
def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
|
|
2785
|
+
def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}):
|
|
2289
2786
|
raise NotSupported(self.id + ' withdraw() is not supported yet')
|
|
2290
2787
|
|
|
2291
2788
|
def create_deposit_address(self, code: str, params={}):
|
|
2292
2789
|
raise NotSupported(self.id + ' createDepositAddress() is not supported yet')
|
|
2293
2790
|
|
|
2294
|
-
def set_leverage(self, leverage:
|
|
2791
|
+
def set_leverage(self, leverage: int, symbol: Str = None, params={}):
|
|
2295
2792
|
raise NotSupported(self.id + ' setLeverage() is not supported yet')
|
|
2296
2793
|
|
|
2297
2794
|
def fetch_leverage(self, symbol: str, params={}):
|
|
@@ -2316,6 +2813,12 @@ class Exchange(object):
|
|
|
2316
2813
|
def set_margin(self, symbol: str, amount: float, params={}):
|
|
2317
2814
|
raise NotSupported(self.id + ' setMargin() is not supported yet')
|
|
2318
2815
|
|
|
2816
|
+
def fetch_long_short_ratio(self, symbol: str, timeframe: Str = None, params={}):
|
|
2817
|
+
raise NotSupported(self.id + ' fetchLongShortRatio() is not supported yet')
|
|
2818
|
+
|
|
2819
|
+
def fetch_long_short_ratio_history(self, symbol: Str = None, timeframe: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
2820
|
+
raise NotSupported(self.id + ' fetchLongShortRatioHistory() is not supported yet')
|
|
2821
|
+
|
|
2319
2822
|
def fetch_margin_adjustment_history(self, symbol: Str = None, type: Str = None, since: Num = None, limit: Num = None, params={}):
|
|
2320
2823
|
"""
|
|
2321
2824
|
fetches the history of margin added or reduced from contract isolated positions
|
|
@@ -2334,12 +2837,15 @@ class Exchange(object):
|
|
|
2334
2837
|
def fetch_deposit_addresses_by_network(self, code: str, params={}):
|
|
2335
2838
|
raise NotSupported(self.id + ' fetchDepositAddressesByNetwork() is not supported yet')
|
|
2336
2839
|
|
|
2337
|
-
def fetch_open_interest_history(self, symbol: str, timeframe='1h', since: Int = None, limit: Int = None, params={}):
|
|
2840
|
+
def fetch_open_interest_history(self, symbol: str, timeframe: str = '1h', since: Int = None, limit: Int = None, params={}):
|
|
2338
2841
|
raise NotSupported(self.id + ' fetchOpenInterestHistory() is not supported yet')
|
|
2339
2842
|
|
|
2340
2843
|
def fetch_open_interest(self, symbol: str, params={}):
|
|
2341
2844
|
raise NotSupported(self.id + ' fetchOpenInterest() is not supported yet')
|
|
2342
2845
|
|
|
2846
|
+
def fetch_open_interests(self, symbols: Strings = None, params={}):
|
|
2847
|
+
raise NotSupported(self.id + ' fetchOpenInterests() is not supported yet')
|
|
2848
|
+
|
|
2343
2849
|
def sign_in(self, params={}):
|
|
2344
2850
|
raise NotSupported(self.id + ' signIn() is not supported yet')
|
|
2345
2851
|
|
|
@@ -2358,7 +2864,7 @@ class Exchange(object):
|
|
|
2358
2864
|
# keep self in mind:
|
|
2359
2865
|
# in JS: 1 == 1.0 is True; 1 == 1.0 is True
|
|
2360
2866
|
# in Python: 1 == 1.0 is True
|
|
2361
|
-
# in PHP 1 == 1.0 is True, but 1 == 1.0 is False
|
|
2867
|
+
# in PHP 1 == 1.0 is True, but 1 == 1.0 is False.
|
|
2362
2868
|
if stringVersion.find('.') >= 0:
|
|
2363
2869
|
return float(stringVersion)
|
|
2364
2870
|
return int(stringVersion)
|
|
@@ -2369,6 +2875,11 @@ class Exchange(object):
|
|
|
2369
2875
|
res = self.parse_to_numeric((value % 1))
|
|
2370
2876
|
return res == 0
|
|
2371
2877
|
|
|
2878
|
+
def safe_number_omit_zero(self, obj: object, key: IndexType, defaultValue: Num = None):
|
|
2879
|
+
value = self.safe_string(obj, key)
|
|
2880
|
+
final = self.parse_number(self.omit_zero(value))
|
|
2881
|
+
return defaultValue if (final is None) else final
|
|
2882
|
+
|
|
2372
2883
|
def safe_integer_omit_zero(self, obj: object, key: IndexType, defaultValue: Int = None):
|
|
2373
2884
|
timestamp = self.safe_integer(obj, key, defaultValue)
|
|
2374
2885
|
if timestamp is None or timestamp == 0:
|
|
@@ -2376,7 +2887,113 @@ class Exchange(object):
|
|
|
2376
2887
|
return timestamp
|
|
2377
2888
|
|
|
2378
2889
|
def after_construct(self):
|
|
2890
|
+
# networks
|
|
2379
2891
|
self.create_networks_by_id_object()
|
|
2892
|
+
self.features_generator()
|
|
2893
|
+
# init predefined markets if any
|
|
2894
|
+
if self.markets:
|
|
2895
|
+
self.set_markets(self.markets)
|
|
2896
|
+
# init the request rate limiter
|
|
2897
|
+
self.init_rest_rate_limiter()
|
|
2898
|
+
# sanbox mode
|
|
2899
|
+
isSandbox = self.safe_bool_2(self.options, 'sandbox', 'testnet', False)
|
|
2900
|
+
if isSandbox:
|
|
2901
|
+
self.set_sandbox_mode(isSandbox)
|
|
2902
|
+
|
|
2903
|
+
def init_rest_rate_limiter(self):
|
|
2904
|
+
if self.rateLimit is None or (self.id is not None and self.rateLimit == -1):
|
|
2905
|
+
raise ExchangeError(self.id + '.rateLimit property is not configured')
|
|
2906
|
+
refillRate = self.MAX_VALUE
|
|
2907
|
+
if self.rateLimit > 0:
|
|
2908
|
+
refillRate = 1 / self.rateLimit
|
|
2909
|
+
defaultBucket = {
|
|
2910
|
+
'delay': 0.001,
|
|
2911
|
+
'capacity': 1,
|
|
2912
|
+
'cost': 1,
|
|
2913
|
+
'maxCapacity': 1000,
|
|
2914
|
+
'refillRate': refillRate,
|
|
2915
|
+
}
|
|
2916
|
+
existingBucket = {} if (self.tokenBucket is None) else self.tokenBucket
|
|
2917
|
+
self.tokenBucket = self.extend(defaultBucket, existingBucket)
|
|
2918
|
+
self.init_throttler()
|
|
2919
|
+
|
|
2920
|
+
def features_generator(self):
|
|
2921
|
+
#
|
|
2922
|
+
# in the exchange-specific features can be something like self, where we support 'string' aliases too:
|
|
2923
|
+
#
|
|
2924
|
+
# {
|
|
2925
|
+
# 'my' : {
|
|
2926
|
+
# 'createOrder' : {...},
|
|
2927
|
+
# },
|
|
2928
|
+
# 'swap': {
|
|
2929
|
+
# 'linear': {
|
|
2930
|
+
# 'extends': my',
|
|
2931
|
+
# },
|
|
2932
|
+
# },
|
|
2933
|
+
# }
|
|
2934
|
+
#
|
|
2935
|
+
if self.features is None:
|
|
2936
|
+
return
|
|
2937
|
+
# reconstruct
|
|
2938
|
+
initialFeatures = self.features
|
|
2939
|
+
self.features = {}
|
|
2940
|
+
unifiedMarketTypes = ['spot', 'swap', 'future', 'option']
|
|
2941
|
+
subTypes = ['linear', 'inverse']
|
|
2942
|
+
# atm only support basic methods, eg: 'createOrder', 'fetchOrder', 'fetchOrders', 'fetchMyTrades'
|
|
2943
|
+
for i in range(0, len(unifiedMarketTypes)):
|
|
2944
|
+
marketType = unifiedMarketTypes[i]
|
|
2945
|
+
# if marketType is not filled for self exchange, don't add that in `features`
|
|
2946
|
+
if not (marketType in initialFeatures):
|
|
2947
|
+
self.features[marketType] = None
|
|
2948
|
+
else:
|
|
2949
|
+
if marketType == 'spot':
|
|
2950
|
+
self.features[marketType] = self.features_mapper(initialFeatures, marketType, None)
|
|
2951
|
+
else:
|
|
2952
|
+
self.features[marketType] = {}
|
|
2953
|
+
for j in range(0, len(subTypes)):
|
|
2954
|
+
subType = subTypes[j]
|
|
2955
|
+
self.features[marketType][subType] = self.features_mapper(initialFeatures, marketType, subType)
|
|
2956
|
+
|
|
2957
|
+
def features_mapper(self, initialFeatures: Any, marketType: Str, subType: Str = None):
|
|
2958
|
+
featuresObj = initialFeatures[marketType][subType] if (subType is not None) else initialFeatures[marketType]
|
|
2959
|
+
# if exchange does not have that market-type(eg. future>inverse)
|
|
2960
|
+
if featuresObj is None:
|
|
2961
|
+
return None
|
|
2962
|
+
extendsStr: Str = self.safe_string(featuresObj, 'extends')
|
|
2963
|
+
if extendsStr is not None:
|
|
2964
|
+
featuresObj = self.omit(featuresObj, 'extends')
|
|
2965
|
+
extendObj = self.features_mapper(initialFeatures, extendsStr)
|
|
2966
|
+
featuresObj = self.deep_extend(extendObj, featuresObj)
|
|
2967
|
+
#
|
|
2968
|
+
# ### corrections ###
|
|
2969
|
+
#
|
|
2970
|
+
# createOrder
|
|
2971
|
+
if 'createOrder' in featuresObj:
|
|
2972
|
+
value = self.safe_dict(featuresObj['createOrder'], 'attachedStopLossTakeProfit')
|
|
2973
|
+
featuresObj['createOrder']['stopLoss'] = value
|
|
2974
|
+
featuresObj['createOrder']['takeProfit'] = value
|
|
2975
|
+
if marketType == 'spot':
|
|
2976
|
+
# default 'hedged': False
|
|
2977
|
+
featuresObj['createOrder']['hedged'] = False
|
|
2978
|
+
# default 'leverage': False
|
|
2979
|
+
if not ('leverage' in featuresObj['createOrder']):
|
|
2980
|
+
featuresObj['createOrder']['leverage'] = False
|
|
2981
|
+
# default 'GTC' to True
|
|
2982
|
+
if self.safe_bool(featuresObj['createOrder']['timeInForce'], 'GTC') is None:
|
|
2983
|
+
featuresObj['createOrder']['timeInForce']['GTC'] = True
|
|
2984
|
+
# other methods
|
|
2985
|
+
keys = list(featuresObj.keys())
|
|
2986
|
+
for i in range(0, len(keys)):
|
|
2987
|
+
key = keys[i]
|
|
2988
|
+
featureBlock = featuresObj[key]
|
|
2989
|
+
if not self.in_array(key, ['sandbox']) and featureBlock is not None:
|
|
2990
|
+
# default "symbolRequired" to False to all methods(except `createOrder`)
|
|
2991
|
+
if not ('symbolRequired' in featureBlock):
|
|
2992
|
+
featureBlock['symbolRequired'] = self.in_array(key, ['createOrder', 'createOrders', 'fetchOHLCV'])
|
|
2993
|
+
return featuresObj
|
|
2994
|
+
|
|
2995
|
+
def orderbook_checksum_message(self, symbol: Str):
|
|
2996
|
+
return symbol + ' = False'
|
|
2380
2997
|
|
|
2381
2998
|
def create_networks_by_id_object(self):
|
|
2382
2999
|
# automatically generate network-id-to-code mappings
|
|
@@ -2389,6 +3006,7 @@ class Exchange(object):
|
|
|
2389
3006
|
'ETH': {'ERC20': 'ETH'},
|
|
2390
3007
|
'TRX': {'TRC20': 'TRX'},
|
|
2391
3008
|
'CRO': {'CRC20': 'CRONOS'},
|
|
3009
|
+
'BRC20': {'BRC20': 'BTC'},
|
|
2392
3010
|
},
|
|
2393
3011
|
}
|
|
2394
3012
|
|
|
@@ -2433,6 +3051,78 @@ class Exchange(object):
|
|
|
2433
3051
|
}
|
|
2434
3052
|
|
|
2435
3053
|
def safe_currency_structure(self, currency: object):
|
|
3054
|
+
# derive data from networks: deposit, withdraw, active, fee, limits, precision
|
|
3055
|
+
networks = self.safe_dict(currency, 'networks', {})
|
|
3056
|
+
keys = list(networks.keys())
|
|
3057
|
+
length = len(keys)
|
|
3058
|
+
if length != 0:
|
|
3059
|
+
for i in range(0, length):
|
|
3060
|
+
key = keys[i]
|
|
3061
|
+
network = networks[key]
|
|
3062
|
+
deposit = self.safe_bool(network, 'deposit')
|
|
3063
|
+
currencyDeposit = self.safe_bool(currency, 'deposit')
|
|
3064
|
+
if currencyDeposit is None or deposit:
|
|
3065
|
+
currency['deposit'] = deposit
|
|
3066
|
+
withdraw = self.safe_bool(network, 'withdraw')
|
|
3067
|
+
currencyWithdraw = self.safe_bool(currency, 'withdraw')
|
|
3068
|
+
if currencyWithdraw is None or withdraw:
|
|
3069
|
+
currency['withdraw'] = withdraw
|
|
3070
|
+
# set network 'active' to False if D or W is disabled
|
|
3071
|
+
active = self.safe_bool(network, 'active')
|
|
3072
|
+
if active is None:
|
|
3073
|
+
if deposit and withdraw:
|
|
3074
|
+
currency['networks'][key]['active'] = True
|
|
3075
|
+
elif deposit is not None and withdraw is not None:
|
|
3076
|
+
currency['networks'][key]['active'] = False
|
|
3077
|
+
active = self.safe_bool(currency['networks'][key], 'active') # dict might have been updated on above lines, so access directly instead of `network` variable
|
|
3078
|
+
currencyActive = self.safe_bool(currency, 'active')
|
|
3079
|
+
if currencyActive is None or active:
|
|
3080
|
+
currency['active'] = active
|
|
3081
|
+
# find lowest fee(which is more desired)
|
|
3082
|
+
fee = self.safe_string(network, 'fee')
|
|
3083
|
+
feeMain = self.safe_string(currency, 'fee')
|
|
3084
|
+
if feeMain is None or Precise.string_lt(fee, feeMain):
|
|
3085
|
+
currency['fee'] = self.parse_number(fee)
|
|
3086
|
+
# find lowest precision(which is more desired)
|
|
3087
|
+
precision = self.safe_string(network, 'precision')
|
|
3088
|
+
precisionMain = self.safe_string(currency, 'precision')
|
|
3089
|
+
if precisionMain is None or Precise.string_gt(precision, precisionMain):
|
|
3090
|
+
currency['precision'] = self.parse_number(precision)
|
|
3091
|
+
# limits
|
|
3092
|
+
limits = self.safe_dict(network, 'limits')
|
|
3093
|
+
limitsMain = self.safe_dict(currency, 'limits')
|
|
3094
|
+
if limitsMain is None:
|
|
3095
|
+
currency['limits'] = {}
|
|
3096
|
+
# deposits
|
|
3097
|
+
limitsDeposit = self.safe_dict(limits, 'deposit')
|
|
3098
|
+
limitsDepositMain = self.safe_dict(limitsMain, 'deposit')
|
|
3099
|
+
if limitsDepositMain is None:
|
|
3100
|
+
currency['limits']['deposit'] = {}
|
|
3101
|
+
limitsDepositMin = self.safe_string(limitsDeposit, 'min')
|
|
3102
|
+
limitsDepositMax = self.safe_string(limitsDeposit, 'max')
|
|
3103
|
+
limitsDepositMinMain = self.safe_string(limitsDepositMain, 'min')
|
|
3104
|
+
limitsDepositMaxMain = self.safe_string(limitsDepositMain, 'max')
|
|
3105
|
+
# find min
|
|
3106
|
+
if limitsDepositMinMain is None or Precise.string_lt(limitsDepositMin, limitsDepositMinMain):
|
|
3107
|
+
currency['limits']['deposit']['min'] = self.parse_number(limitsDepositMin)
|
|
3108
|
+
# find max
|
|
3109
|
+
if limitsDepositMaxMain is None or Precise.string_gt(limitsDepositMax, limitsDepositMaxMain):
|
|
3110
|
+
currency['limits']['deposit']['max'] = self.parse_number(limitsDepositMax)
|
|
3111
|
+
# withdrawals
|
|
3112
|
+
limitsWithdraw = self.safe_dict(limits, 'withdraw')
|
|
3113
|
+
limitsWithdrawMain = self.safe_dict(limitsMain, 'withdraw')
|
|
3114
|
+
if limitsWithdrawMain is None:
|
|
3115
|
+
currency['limits']['withdraw'] = {}
|
|
3116
|
+
limitsWithdrawMin = self.safe_string(limitsWithdraw, 'min')
|
|
3117
|
+
limitsWithdrawMax = self.safe_string(limitsWithdraw, 'max')
|
|
3118
|
+
limitsWithdrawMinMain = self.safe_string(limitsWithdrawMain, 'min')
|
|
3119
|
+
limitsWithdrawMaxMain = self.safe_string(limitsWithdrawMain, 'max')
|
|
3120
|
+
# find min
|
|
3121
|
+
if limitsWithdrawMinMain is None or Precise.string_lt(limitsWithdrawMin, limitsWithdrawMinMain):
|
|
3122
|
+
currency['limits']['withdraw']['min'] = self.parse_number(limitsWithdrawMin)
|
|
3123
|
+
# find max
|
|
3124
|
+
if limitsWithdrawMaxMain is None or Precise.string_gt(limitsWithdrawMax, limitsWithdrawMaxMain):
|
|
3125
|
+
currency['limits']['withdraw']['max'] = self.parse_number(limitsWithdrawMax)
|
|
2436
3126
|
return self.extend({
|
|
2437
3127
|
'info': None,
|
|
2438
3128
|
'id': None,
|
|
@@ -2459,7 +3149,7 @@ class Exchange(object):
|
|
|
2459
3149
|
},
|
|
2460
3150
|
}, currency)
|
|
2461
3151
|
|
|
2462
|
-
def safe_market_structure(self, market=None):
|
|
3152
|
+
def safe_market_structure(self, market: dict = None):
|
|
2463
3153
|
cleanStructure = {
|
|
2464
3154
|
'id': None,
|
|
2465
3155
|
'lowercaseId': None,
|
|
@@ -2514,6 +3204,10 @@ class Exchange(object):
|
|
|
2514
3204
|
'max': None,
|
|
2515
3205
|
},
|
|
2516
3206
|
},
|
|
3207
|
+
'marginModes': {
|
|
3208
|
+
'cross': None,
|
|
3209
|
+
'isolated': None,
|
|
3210
|
+
},
|
|
2517
3211
|
'created': None,
|
|
2518
3212
|
'info': None,
|
|
2519
3213
|
}
|
|
@@ -2536,14 +3230,16 @@ class Exchange(object):
|
|
|
2536
3230
|
|
|
2537
3231
|
def set_markets(self, markets, currencies=None):
|
|
2538
3232
|
values = []
|
|
2539
|
-
self.markets_by_id =
|
|
3233
|
+
self.markets_by_id = self.create_safe_dictionary()
|
|
2540
3234
|
# handle marketId conflicts
|
|
2541
3235
|
# we insert spot markets first
|
|
2542
3236
|
marketValues = self.sort_by(self.to_array(markets), 'spot', True, True)
|
|
2543
3237
|
for i in range(0, len(marketValues)):
|
|
2544
3238
|
value = marketValues[i]
|
|
2545
3239
|
if value['id'] in self.markets_by_id:
|
|
2546
|
-
(self.markets_by_id[value['id']])
|
|
3240
|
+
marketsByIdArray = (self.markets_by_id[value['id']])
|
|
3241
|
+
marketsByIdArray.append(value)
|
|
3242
|
+
self.markets_by_id[value['id']] = marketsByIdArray
|
|
2547
3243
|
else:
|
|
2548
3244
|
self.markets_by_id[value['id']] = [value]
|
|
2549
3245
|
market = self.deep_extend(self.safe_market_structure(), {
|
|
@@ -2557,14 +3253,14 @@ class Exchange(object):
|
|
|
2557
3253
|
else:
|
|
2558
3254
|
market['subType'] = None
|
|
2559
3255
|
values.append(market)
|
|
2560
|
-
self.markets = self.index_by(values, 'symbol')
|
|
3256
|
+
self.markets = self.map_to_safe_map(self.index_by(values, 'symbol'))
|
|
2561
3257
|
marketsSortedBySymbol = self.keysort(self.markets)
|
|
2562
3258
|
marketsSortedById = self.keysort(self.markets_by_id)
|
|
2563
3259
|
self.symbols = list(marketsSortedBySymbol.keys())
|
|
2564
3260
|
self.ids = list(marketsSortedById.keys())
|
|
2565
3261
|
if currencies is not None:
|
|
2566
3262
|
# currencies is always None when called in constructor but not when called from loadMarkets
|
|
2567
|
-
self.currencies = self.deep_extend(self.currencies, currencies)
|
|
3263
|
+
self.currencies = self.map_to_safe_map(self.deep_extend(self.currencies, currencies))
|
|
2568
3264
|
else:
|
|
2569
3265
|
baseCurrencies = []
|
|
2570
3266
|
quoteCurrencies = []
|
|
@@ -2590,8 +3286,8 @@ class Exchange(object):
|
|
|
2590
3286
|
quoteCurrencies.append(currency)
|
|
2591
3287
|
baseCurrencies = self.sort_by(baseCurrencies, 'code', False, '')
|
|
2592
3288
|
quoteCurrencies = self.sort_by(quoteCurrencies, 'code', False, '')
|
|
2593
|
-
self.baseCurrencies = self.index_by(baseCurrencies, 'code')
|
|
2594
|
-
self.quoteCurrencies = self.index_by(quoteCurrencies, 'code')
|
|
3289
|
+
self.baseCurrencies = self.map_to_safe_map(self.index_by(baseCurrencies, 'code'))
|
|
3290
|
+
self.quoteCurrencies = self.map_to_safe_map(self.index_by(quoteCurrencies, 'code'))
|
|
2595
3291
|
allCurrencies = self.array_concat(baseCurrencies, quoteCurrencies)
|
|
2596
3292
|
groupedCurrencies = self.group_by(allCurrencies, 'code')
|
|
2597
3293
|
codes = list(groupedCurrencies.keys())
|
|
@@ -2608,8 +3304,8 @@ class Exchange(object):
|
|
|
2608
3304
|
highestPrecisionCurrency = currentCurrency if (currentCurrency['precision'] > highestPrecisionCurrency['precision']) else highestPrecisionCurrency
|
|
2609
3305
|
resultingCurrencies.append(highestPrecisionCurrency)
|
|
2610
3306
|
sortedCurrencies = self.sort_by(resultingCurrencies, 'code')
|
|
2611
|
-
self.currencies = self.deep_extend(self.currencies, self.index_by(sortedCurrencies, 'code'))
|
|
2612
|
-
self.currencies_by_id = self.
|
|
3307
|
+
self.currencies = self.map_to_safe_map(self.deep_extend(self.currencies, self.index_by(sortedCurrencies, 'code')))
|
|
3308
|
+
self.currencies_by_id = self.index_by_safe(self.currencies, 'id')
|
|
2613
3309
|
currenciesSortedByCode = self.keysort(self.currencies)
|
|
2614
3310
|
self.codes = list(currenciesSortedByCode.keys())
|
|
2615
3311
|
return self.markets
|
|
@@ -2619,7 +3315,7 @@ class Exchange(object):
|
|
|
2619
3315
|
superWithRestDescribe = self.deep_extend(extendedRestDescribe, wsBaseDescribe)
|
|
2620
3316
|
return superWithRestDescribe
|
|
2621
3317
|
|
|
2622
|
-
def safe_balance(self, balance:
|
|
3318
|
+
def safe_balance(self, balance: dict):
|
|
2623
3319
|
balances = self.omit(balance, ['info', 'timestamp', 'datetime', 'free', 'used', 'total'])
|
|
2624
3320
|
codes = list(balances.keys())
|
|
2625
3321
|
balance['free'] = {}
|
|
@@ -2653,7 +3349,7 @@ class Exchange(object):
|
|
|
2653
3349
|
balance['debt'] = debtBalance
|
|
2654
3350
|
return balance
|
|
2655
3351
|
|
|
2656
|
-
def safe_order(self, order:
|
|
3352
|
+
def safe_order(self, order: dict, market: Market = None):
|
|
2657
3353
|
# parses numbers
|
|
2658
3354
|
# * it is important pass the trades rawTrades
|
|
2659
3355
|
amount = self.omit_zero(self.safe_string(order, 'amount'))
|
|
@@ -2680,9 +3376,10 @@ class Exchange(object):
|
|
|
2680
3376
|
isTriggerOrSLTpOrder = ((self.safe_string(order, 'triggerPrice') is not None or (self.safe_string(order, 'stopLossPrice') is not None)) or (self.safe_string(order, 'takeProfitPrice') is not None))
|
|
2681
3377
|
if parseFilled or parseCost or shouldParseFees:
|
|
2682
3378
|
rawTrades = self.safe_value(order, 'trades', trades)
|
|
2683
|
-
oldNumber = self.number
|
|
3379
|
+
# oldNumber = self.number
|
|
2684
3380
|
# we parse trades here!
|
|
2685
|
-
self
|
|
3381
|
+
# i don't think self is needed anymore
|
|
3382
|
+
# self.number = str
|
|
2686
3383
|
firstTrade = self.safe_value(rawTrades, 0)
|
|
2687
3384
|
# parse trades if they haven't already been parsed
|
|
2688
3385
|
tradesAreParsed = ((firstTrade is not None) and ('info' in firstTrade) and ('id' in firstTrade))
|
|
@@ -2690,7 +3387,7 @@ class Exchange(object):
|
|
|
2690
3387
|
trades = self.parse_trades(rawTrades, market)
|
|
2691
3388
|
else:
|
|
2692
3389
|
trades = rawTrades
|
|
2693
|
-
self.number = oldNumber
|
|
3390
|
+
# self.number = oldNumber; why parse trades if you read the value using `safeString` ?
|
|
2694
3391
|
tradesLength = 0
|
|
2695
3392
|
isArray = isinstance(trades, list)
|
|
2696
3393
|
if isArray:
|
|
@@ -2897,30 +3594,22 @@ class Exchange(object):
|
|
|
2897
3594
|
results = []
|
|
2898
3595
|
if isinstance(orders, list):
|
|
2899
3596
|
for i in range(0, len(orders)):
|
|
2900
|
-
|
|
3597
|
+
parsed = self.parse_order(orders[i], market) # don't inline self call
|
|
3598
|
+
order = self.extend(parsed, params)
|
|
2901
3599
|
results.append(order)
|
|
2902
3600
|
else:
|
|
2903
3601
|
ids = list(orders.keys())
|
|
2904
3602
|
for i in range(0, len(ids)):
|
|
2905
3603
|
id = ids[i]
|
|
2906
|
-
|
|
3604
|
+
idExtended = self.extend({'id': id}, orders[id])
|
|
3605
|
+
parsedOrder = self.parse_order(idExtended, market) # don't inline these calls
|
|
3606
|
+
order = self.extend(parsedOrder, params)
|
|
2907
3607
|
results.append(order)
|
|
2908
3608
|
results = self.sort_by(results, 'timestamp')
|
|
2909
3609
|
symbol = market['symbol'] if (market is not None) else None
|
|
2910
3610
|
return self.filter_by_symbol_since_limit(results, symbol, since, limit)
|
|
2911
3611
|
|
|
2912
|
-
def
|
|
2913
|
-
"""
|
|
2914
|
-
calculates the presumptive fee that would be charged for an order
|
|
2915
|
-
:param str symbol: unified market symbol
|
|
2916
|
-
:param str type: 'market' or 'limit'
|
|
2917
|
-
:param str side: 'buy' or 'sell'
|
|
2918
|
-
:param float amount: how much you want to trade, in units of the base currency on most exchanges, or number of contracts
|
|
2919
|
-
:param float price: the price for the order to be filled at, in units of the quote currency
|
|
2920
|
-
:param str takerOrMaker: 'taker' or 'maker'
|
|
2921
|
-
:param dict params:
|
|
2922
|
-
:returns dict: contains the rate, the percentage multiplied to the order amount to obtain the fee amount, and cost, the total value of the fee in units of the quote currency, for the order
|
|
2923
|
-
"""
|
|
3612
|
+
def calculate_fee_with_rate(self, symbol: str, type: str, side: str, amount: float, price: float, takerOrMaker='taker', feeRate: Num = None, params={}):
|
|
2924
3613
|
if type == 'market' and takerOrMaker == 'maker':
|
|
2925
3614
|
raise ArgumentsRequired(self.id + ' calculateFee() - you have provided incompatible arguments - "market" type order can not be "maker". Change either the "type" or the "takerOrMaker" argument to calculate the fee.')
|
|
2926
3615
|
market = self.markets[symbol]
|
|
@@ -2949,7 +3638,7 @@ class Exchange(object):
|
|
|
2949
3638
|
# even if `takerOrMaker` argument was set to 'maker', for 'market' orders we should forcefully override it to 'taker'
|
|
2950
3639
|
if type == 'market':
|
|
2951
3640
|
takerOrMaker = 'taker'
|
|
2952
|
-
rate = self.safe_string(market, takerOrMaker)
|
|
3641
|
+
rate = self.number_to_string(feeRate) if (feeRate is not None) else self.safe_string(market, takerOrMaker)
|
|
2953
3642
|
cost = Precise.string_mul(cost, rate)
|
|
2954
3643
|
return {
|
|
2955
3644
|
'type': takerOrMaker,
|
|
@@ -2958,7 +3647,21 @@ class Exchange(object):
|
|
|
2958
3647
|
'cost': self.parse_number(cost),
|
|
2959
3648
|
}
|
|
2960
3649
|
|
|
2961
|
-
def
|
|
3650
|
+
def calculate_fee(self, symbol: str, type: str, side: str, amount: float, price: float, takerOrMaker='taker', params={}):
|
|
3651
|
+
"""
|
|
3652
|
+
calculates the presumptive fee that would be charged for an order
|
|
3653
|
+
:param str symbol: unified market symbol
|
|
3654
|
+
:param str type: 'market' or 'limit'
|
|
3655
|
+
:param str side: 'buy' or 'sell'
|
|
3656
|
+
:param float amount: how much you want to trade, in units of the base currency on most exchanges, or number of contracts
|
|
3657
|
+
:param float price: the price for the order to be filled at, in units of the quote currency
|
|
3658
|
+
:param str takerOrMaker: 'taker' or 'maker'
|
|
3659
|
+
:param dict params:
|
|
3660
|
+
:returns dict: contains the rate, the percentage multiplied to the order amount to obtain the fee amount, and cost, the total value of the fee in units of the quote currency, for the order
|
|
3661
|
+
"""
|
|
3662
|
+
return self.calculate_fee_with_rate(symbol, type, side, amount, price, takerOrMaker, None, params)
|
|
3663
|
+
|
|
3664
|
+
def safe_liquidation(self, liquidation: dict, market: Market = None):
|
|
2962
3665
|
contracts = self.safe_string(liquidation, 'contracts')
|
|
2963
3666
|
contractSize = self.safe_string(market, 'contractSize')
|
|
2964
3667
|
price = self.safe_string(liquidation, 'price')
|
|
@@ -2975,7 +3678,7 @@ class Exchange(object):
|
|
|
2975
3678
|
liquidation['quoteValue'] = self.parse_number(quoteValue)
|
|
2976
3679
|
return liquidation
|
|
2977
3680
|
|
|
2978
|
-
def safe_trade(self, trade:
|
|
3681
|
+
def safe_trade(self, trade: dict, market: Market = None):
|
|
2979
3682
|
amount = self.safe_string(trade, 'amount')
|
|
2980
3683
|
price = self.safe_string(trade, 'price')
|
|
2981
3684
|
cost = self.safe_string(trade, 'cost')
|
|
@@ -2989,40 +3692,68 @@ class Exchange(object):
|
|
|
2989
3692
|
multiplyPrice = Precise.string_div('1', price)
|
|
2990
3693
|
multiplyPrice = Precise.string_mul(multiplyPrice, contractSize)
|
|
2991
3694
|
cost = Precise.string_mul(multiplyPrice, amount)
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
fees = []
|
|
2996
|
-
fee = self.safe_value(trade, 'fee')
|
|
2997
|
-
if shouldParseFees:
|
|
2998
|
-
reducedFees = self.reduce_fees_by_currency(fees) if self.reduceFees else fees
|
|
2999
|
-
reducedLength = len(reducedFees)
|
|
3000
|
-
for i in range(0, reducedLength):
|
|
3001
|
-
reducedFees[i]['cost'] = self.safe_number(reducedFees[i], 'cost')
|
|
3002
|
-
if 'rate' in reducedFees[i]:
|
|
3003
|
-
reducedFees[i]['rate'] = self.safe_number(reducedFees[i], 'rate')
|
|
3004
|
-
if not parseFee and (reducedLength == 0):
|
|
3005
|
-
# copy fee to avoid modification by reference
|
|
3006
|
-
feeCopy = self.deep_extend(fee)
|
|
3007
|
-
feeCopy['cost'] = self.safe_number(feeCopy, 'cost')
|
|
3008
|
-
if 'rate' in feeCopy:
|
|
3009
|
-
feeCopy['rate'] = self.safe_number(feeCopy, 'rate')
|
|
3010
|
-
reducedFees.append(feeCopy)
|
|
3011
|
-
if parseFees:
|
|
3012
|
-
trade['fees'] = reducedFees
|
|
3013
|
-
if parseFee and (reducedLength == 1):
|
|
3014
|
-
trade['fee'] = reducedFees[0]
|
|
3015
|
-
tradeFee = self.safe_value(trade, 'fee')
|
|
3016
|
-
if tradeFee is not None:
|
|
3017
|
-
tradeFee['cost'] = self.safe_number(tradeFee, 'cost')
|
|
3018
|
-
if 'rate' in tradeFee:
|
|
3019
|
-
tradeFee['rate'] = self.safe_number(tradeFee, 'rate')
|
|
3020
|
-
trade['fee'] = tradeFee
|
|
3695
|
+
resultFee, resultFees = self.parsed_fee_and_fees(trade)
|
|
3696
|
+
trade['fee'] = resultFee
|
|
3697
|
+
trade['fees'] = resultFees
|
|
3021
3698
|
trade['amount'] = self.parse_number(amount)
|
|
3022
3699
|
trade['price'] = self.parse_number(price)
|
|
3023
3700
|
trade['cost'] = self.parse_number(cost)
|
|
3024
3701
|
return trade
|
|
3025
3702
|
|
|
3703
|
+
def create_ccxt_trade_id(self, timestamp=None, side=None, amount=None, price=None, takerOrMaker=None):
|
|
3704
|
+
# self approach is being used by multiple exchanges(mexc, woo, coinsbit, dydx, ...)
|
|
3705
|
+
id = None
|
|
3706
|
+
if timestamp is not None:
|
|
3707
|
+
id = self.number_to_string(timestamp)
|
|
3708
|
+
if side is not None:
|
|
3709
|
+
id += '-' + side
|
|
3710
|
+
if amount is not None:
|
|
3711
|
+
id += '-' + self.number_to_string(amount)
|
|
3712
|
+
if price is not None:
|
|
3713
|
+
id += '-' + self.number_to_string(price)
|
|
3714
|
+
if takerOrMaker is not None:
|
|
3715
|
+
id += '-' + takerOrMaker
|
|
3716
|
+
return id
|
|
3717
|
+
|
|
3718
|
+
def parsed_fee_and_fees(self, container: Any):
|
|
3719
|
+
fee = self.safe_dict(container, 'fee')
|
|
3720
|
+
fees = self.safe_list(container, 'fees')
|
|
3721
|
+
feeDefined = fee is not None
|
|
3722
|
+
feesDefined = fees is not None
|
|
3723
|
+
# parsing only if at least one of them is defined
|
|
3724
|
+
shouldParseFees = (feeDefined or feesDefined)
|
|
3725
|
+
if shouldParseFees:
|
|
3726
|
+
if feeDefined:
|
|
3727
|
+
fee = self.parse_fee_numeric(fee)
|
|
3728
|
+
if not feesDefined:
|
|
3729
|
+
# just set it directly, no further processing needed
|
|
3730
|
+
fees = [fee]
|
|
3731
|
+
# 'fees' were set, so reparse them
|
|
3732
|
+
reducedFees = self.reduce_fees_by_currency(fees) if self.reduceFees else fees
|
|
3733
|
+
reducedLength = len(reducedFees)
|
|
3734
|
+
for i in range(0, reducedLength):
|
|
3735
|
+
reducedFees[i] = self.parse_fee_numeric(reducedFees[i])
|
|
3736
|
+
fees = reducedFees
|
|
3737
|
+
if reducedLength == 1:
|
|
3738
|
+
fee = reducedFees[0]
|
|
3739
|
+
elif reducedLength == 0:
|
|
3740
|
+
fee = None
|
|
3741
|
+
# in case `fee & fees` are None, set `fees` array
|
|
3742
|
+
if fee is None:
|
|
3743
|
+
fee = {
|
|
3744
|
+
'cost': None,
|
|
3745
|
+
'currency': None,
|
|
3746
|
+
}
|
|
3747
|
+
if fees is None:
|
|
3748
|
+
fees = []
|
|
3749
|
+
return [fee, fees]
|
|
3750
|
+
|
|
3751
|
+
def parse_fee_numeric(self, fee: Any):
|
|
3752
|
+
fee['cost'] = self.safe_number(fee, 'cost') # ensure numeric
|
|
3753
|
+
if 'rate' in fee:
|
|
3754
|
+
fee['rate'] = self.safe_number(fee, 'rate')
|
|
3755
|
+
return fee
|
|
3756
|
+
|
|
3026
3757
|
def find_nearest_ceiling(self, arr: List[float], providedValue: float):
|
|
3027
3758
|
# i.e. findNearestCeiling([10, 30, 50], 23) returns 30
|
|
3028
3759
|
length = len(arr)
|
|
@@ -3091,12 +3822,13 @@ class Exchange(object):
|
|
|
3091
3822
|
reduced = {}
|
|
3092
3823
|
for i in range(0, len(fees)):
|
|
3093
3824
|
fee = fees[i]
|
|
3094
|
-
|
|
3825
|
+
code = self.safe_string(fee, 'currency')
|
|
3826
|
+
feeCurrencyCode = code if (code is not None) else str(i)
|
|
3095
3827
|
if feeCurrencyCode is not None:
|
|
3096
3828
|
rate = self.safe_string(fee, 'rate')
|
|
3097
|
-
cost = self.
|
|
3098
|
-
if
|
|
3099
|
-
# omit
|
|
3829
|
+
cost = self.safe_string(fee, 'cost')
|
|
3830
|
+
if cost is None:
|
|
3831
|
+
# omit None cost, does not make sense, however, don't omit '0' costs, still make sense
|
|
3100
3832
|
continue
|
|
3101
3833
|
if not (feeCurrencyCode in reduced):
|
|
3102
3834
|
reduced[feeCurrencyCode] = {}
|
|
@@ -3105,7 +3837,7 @@ class Exchange(object):
|
|
|
3105
3837
|
reduced[feeCurrencyCode][rateKey]['cost'] = Precise.string_add(reduced[feeCurrencyCode][rateKey]['cost'], cost)
|
|
3106
3838
|
else:
|
|
3107
3839
|
reduced[feeCurrencyCode][rateKey] = {
|
|
3108
|
-
'currency':
|
|
3840
|
+
'currency': code,
|
|
3109
3841
|
'cost': cost,
|
|
3110
3842
|
}
|
|
3111
3843
|
if rate is not None:
|
|
@@ -3117,35 +3849,63 @@ class Exchange(object):
|
|
|
3117
3849
|
result = self.array_concat(result, reducedFeeValues)
|
|
3118
3850
|
return result
|
|
3119
3851
|
|
|
3120
|
-
def safe_ticker(self, ticker:
|
|
3852
|
+
def safe_ticker(self, ticker: dict, market: Market = None):
|
|
3121
3853
|
open = self.omit_zero(self.safe_string(ticker, 'open'))
|
|
3122
|
-
close = self.omit_zero(self.
|
|
3123
|
-
last = self.omit_zero(self.safe_string(ticker, 'last'))
|
|
3854
|
+
close = self.omit_zero(self.safe_string_2(ticker, 'close', 'last'))
|
|
3124
3855
|
change = self.omit_zero(self.safe_string(ticker, 'change'))
|
|
3125
3856
|
percentage = self.omit_zero(self.safe_string(ticker, 'percentage'))
|
|
3126
3857
|
average = self.omit_zero(self.safe_string(ticker, 'average'))
|
|
3127
|
-
vwap = self.
|
|
3858
|
+
vwap = self.safe_string(ticker, 'vwap')
|
|
3128
3859
|
baseVolume = self.safe_string(ticker, 'baseVolume')
|
|
3129
3860
|
quoteVolume = self.safe_string(ticker, 'quoteVolume')
|
|
3130
3861
|
if vwap is None:
|
|
3131
3862
|
vwap = Precise.string_div(self.omit_zero(quoteVolume), baseVolume)
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
if average is None:
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3863
|
+
# calculate open
|
|
3864
|
+
if change is not None:
|
|
3865
|
+
if close is None and average is not None:
|
|
3866
|
+
close = Precise.string_add(average, Precise.string_div(change, '2'))
|
|
3867
|
+
if open is None and close is not None:
|
|
3868
|
+
open = Precise.string_sub(close, change)
|
|
3869
|
+
elif percentage is not None:
|
|
3870
|
+
if close is None and average is not None:
|
|
3871
|
+
openAddClose = Precise.string_mul(average, '2')
|
|
3872
|
+
# openAddClose = open * (1 + (100 + percentage)/100)
|
|
3873
|
+
denominator = Precise.string_add('2', Precise.string_div(percentage, '100'))
|
|
3874
|
+
calcOpen = open if (open is not None) else Precise.string_div(openAddClose, denominator)
|
|
3875
|
+
close = Precise.string_mul(calcOpen, Precise.string_add('1', Precise.string_div(percentage, '100')))
|
|
3876
|
+
if open is None and close is not None:
|
|
3877
|
+
open = Precise.string_div(close, Precise.string_add('1', Precise.string_div(percentage, '100')))
|
|
3878
|
+
# change
|
|
3879
|
+
if change is None:
|
|
3880
|
+
if close is not None and open is not None:
|
|
3881
|
+
change = Precise.string_sub(close, open)
|
|
3882
|
+
elif close is not None and percentage is not None:
|
|
3883
|
+
change = Precise.string_mul(Precise.string_div(percentage, '100'), Precise.string_div(close, '100'))
|
|
3884
|
+
elif open is not None and percentage is not None:
|
|
3885
|
+
change = Precise.string_mul(open, Precise.string_div(percentage, '100'))
|
|
3886
|
+
# calculate things according to "open"(similar can be done with "close")
|
|
3887
|
+
if open is not None:
|
|
3888
|
+
# percentage(using change)
|
|
3889
|
+
if percentage is None and change is not None:
|
|
3890
|
+
percentage = Precise.string_mul(Precise.string_div(change, open), '100')
|
|
3891
|
+
# close(using change)
|
|
3892
|
+
if close is None and change is not None:
|
|
3893
|
+
close = Precise.string_add(open, change)
|
|
3894
|
+
# close(using average)
|
|
3895
|
+
if close is None and average is not None:
|
|
3896
|
+
close = Precise.string_mul(average, '2')
|
|
3897
|
+
# average
|
|
3898
|
+
if average is None and close is not None:
|
|
3899
|
+
precision = 18
|
|
3900
|
+
if market is not None and self.is_tick_precision():
|
|
3901
|
+
marketPrecision = self.safe_dict(market, 'precision')
|
|
3902
|
+
precisionPrice = self.safe_string(marketPrecision, 'price')
|
|
3903
|
+
if precisionPrice is not None:
|
|
3904
|
+
precision = self.precision_from_string(precisionPrice)
|
|
3905
|
+
average = Precise.string_div(Precise.string_add(open, close), '2', precision)
|
|
3147
3906
|
# timestamp and symbol operations don't belong in safeTicker
|
|
3148
3907
|
# they should be done in the derived classes
|
|
3908
|
+
closeParsed = self.parse_number(self.omit_zero(close))
|
|
3149
3909
|
return self.extend(ticker, {
|
|
3150
3910
|
'bid': self.parse_number(self.omit_zero(self.safe_string(ticker, 'bid'))),
|
|
3151
3911
|
'bidVolume': self.safe_number(ticker, 'bidVolume'),
|
|
@@ -3154,8 +3914,8 @@ class Exchange(object):
|
|
|
3154
3914
|
'high': self.parse_number(self.omit_zero(self.safe_string(ticker, 'high'))),
|
|
3155
3915
|
'low': self.parse_number(self.omit_zero(self.safe_string(ticker, 'low'))),
|
|
3156
3916
|
'open': self.parse_number(self.omit_zero(open)),
|
|
3157
|
-
'close':
|
|
3158
|
-
'last':
|
|
3917
|
+
'close': closeParsed,
|
|
3918
|
+
'last': closeParsed,
|
|
3159
3919
|
'change': self.parse_number(change),
|
|
3160
3920
|
'percentage': self.parse_number(percentage),
|
|
3161
3921
|
'average': self.parse_number(average),
|
|
@@ -3163,15 +3923,17 @@ class Exchange(object):
|
|
|
3163
3923
|
'baseVolume': self.parse_number(baseVolume),
|
|
3164
3924
|
'quoteVolume': self.parse_number(quoteVolume),
|
|
3165
3925
|
'previousClose': self.safe_number(ticker, 'previousClose'),
|
|
3926
|
+
'indexPrice': self.safe_number(ticker, 'indexPrice'),
|
|
3927
|
+
'markPrice': self.safe_number(ticker, 'markPrice'),
|
|
3166
3928
|
})
|
|
3167
3929
|
|
|
3168
|
-
def fetch_borrow_rate(self, code: str, amount, params={}):
|
|
3930
|
+
def fetch_borrow_rate(self, code: str, amount: float, params={}):
|
|
3169
3931
|
raise NotSupported(self.id + ' fetchBorrowRate is deprecated, please use fetchCrossBorrowRate or fetchIsolatedBorrowRate instead')
|
|
3170
3932
|
|
|
3171
|
-
def repay_cross_margin(self, code: str, amount, params={}):
|
|
3933
|
+
def repay_cross_margin(self, code: str, amount: float, params={}):
|
|
3172
3934
|
raise NotSupported(self.id + ' repayCrossMargin is not support yet')
|
|
3173
3935
|
|
|
3174
|
-
def repay_isolated_margin(self, symbol: str, code: str, amount, params={}):
|
|
3936
|
+
def repay_isolated_margin(self, symbol: str, code: str, amount: float, params={}):
|
|
3175
3937
|
raise NotSupported(self.id + ' repayIsolatedMargin is not support yet')
|
|
3176
3938
|
|
|
3177
3939
|
def borrow_cross_margin(self, code: str, amount: float, params={}):
|
|
@@ -3180,10 +3942,10 @@ class Exchange(object):
|
|
|
3180
3942
|
def borrow_isolated_margin(self, symbol: str, code: str, amount: float, params={}):
|
|
3181
3943
|
raise NotSupported(self.id + ' borrowIsolatedMargin is not support yet')
|
|
3182
3944
|
|
|
3183
|
-
def borrow_margin(self, code: str, amount, symbol: Str = None, params={}):
|
|
3945
|
+
def borrow_margin(self, code: str, amount: float, symbol: Str = None, params={}):
|
|
3184
3946
|
raise NotSupported(self.id + ' borrowMargin is deprecated, please use borrowCrossMargin or borrowIsolatedMargin instead')
|
|
3185
3947
|
|
|
3186
|
-
def repay_margin(self, code: str, amount, symbol: Str = None, params={}):
|
|
3948
|
+
def repay_margin(self, code: str, amount: float, symbol: Str = None, params={}):
|
|
3187
3949
|
raise NotSupported(self.id + ' repayMargin is deprecated, please use repayCrossMargin or repayIsolatedMargin instead')
|
|
3188
3950
|
|
|
3189
3951
|
def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
|
@@ -3230,12 +3992,18 @@ class Exchange(object):
|
|
|
3230
3992
|
result[volume] = []
|
|
3231
3993
|
for i in range(0, len(ohlcvs)):
|
|
3232
3994
|
ts = ohlcvs[i][0] if ms else self.parse_to_int(ohlcvs[i][0] / 1000)
|
|
3233
|
-
result[timestamp]
|
|
3234
|
-
|
|
3235
|
-
result[
|
|
3236
|
-
|
|
3237
|
-
result[
|
|
3238
|
-
|
|
3995
|
+
resultTimestamp = result[timestamp]
|
|
3996
|
+
resultTimestamp.append(ts)
|
|
3997
|
+
resultOpen = result[open]
|
|
3998
|
+
resultOpen.append(ohlcvs[i][1])
|
|
3999
|
+
resultHigh = result[high]
|
|
4000
|
+
resultHigh.append(ohlcvs[i][2])
|
|
4001
|
+
resultLow = result[low]
|
|
4002
|
+
resultLow.append(ohlcvs[i][3])
|
|
4003
|
+
resultClose = result[close]
|
|
4004
|
+
resultClose.append(ohlcvs[i][4])
|
|
4005
|
+
resultVolume = result[volume]
|
|
4006
|
+
resultVolume.append(ohlcvs[i][5])
|
|
3239
4007
|
return result
|
|
3240
4008
|
|
|
3241
4009
|
def fetch_web_endpoint(self, method, endpointMethod, returnAsJson, startRegex=None, endRegex=None):
|
|
@@ -3249,14 +4017,18 @@ class Exchange(object):
|
|
|
3249
4017
|
maxRetries = self.safe_value(options, 'webApiRetries', 10)
|
|
3250
4018
|
response = None
|
|
3251
4019
|
retry = 0
|
|
4020
|
+
shouldBreak = False
|
|
3252
4021
|
while(retry < maxRetries):
|
|
3253
4022
|
try:
|
|
3254
4023
|
response = getattr(self, endpointMethod)({})
|
|
4024
|
+
shouldBreak = True
|
|
3255
4025
|
break
|
|
3256
4026
|
except Exception as e:
|
|
3257
4027
|
retry = retry + 1
|
|
3258
4028
|
if retry == maxRetries:
|
|
3259
4029
|
raise e
|
|
4030
|
+
if shouldBreak:
|
|
4031
|
+
break # self is needed because of GO
|
|
3260
4032
|
content = response
|
|
3261
4033
|
if startRegex is not None:
|
|
3262
4034
|
splitted_by_start = content.split(startRegex)
|
|
@@ -3287,6 +4059,14 @@ class Exchange(object):
|
|
|
3287
4059
|
result.append(self.market_id(symbols[i]))
|
|
3288
4060
|
return result
|
|
3289
4061
|
|
|
4062
|
+
def currency_ids(self, codes: Strings = None):
|
|
4063
|
+
if codes is None:
|
|
4064
|
+
return codes
|
|
4065
|
+
result = []
|
|
4066
|
+
for i in range(0, len(codes)):
|
|
4067
|
+
result.append(self.currency_id(codes[i]))
|
|
4068
|
+
return result
|
|
4069
|
+
|
|
3290
4070
|
def markets_for_symbols(self, symbols: Strings = None):
|
|
3291
4071
|
if symbols is None:
|
|
3292
4072
|
return symbols
|
|
@@ -3371,7 +4151,7 @@ class Exchange(object):
|
|
|
3371
4151
|
|
|
3372
4152
|
def network_code_to_id(self, networkCode: str, currencyCode: Str = None):
|
|
3373
4153
|
"""
|
|
3374
|
-
|
|
4154
|
+
@ignore
|
|
3375
4155
|
tries to convert the provided networkCode(which is expected to be an unified network code) to a network id. In order to achieve self, derived class needs to have 'options->networks' defined.
|
|
3376
4156
|
:param str networkCode: unified network code
|
|
3377
4157
|
:param str currencyCode: unified currency code, but self argument is not required by default, unless there is an exchange(like huobi) that needs an override of the method to be able to pass currencyCode argument additionally
|
|
@@ -3386,7 +4166,7 @@ class Exchange(object):
|
|
|
3386
4166
|
if currencyCode is None:
|
|
3387
4167
|
currencies = list(self.currencies.values())
|
|
3388
4168
|
for i in range(0, len(currencies)):
|
|
3389
|
-
currency = [i]
|
|
4169
|
+
currency = currencies[i]
|
|
3390
4170
|
networks = self.safe_dict(currency, 'networks')
|
|
3391
4171
|
network = self.safe_dict(networks, networkCode)
|
|
3392
4172
|
networkId = self.safe_string(network, 'id')
|
|
@@ -3419,7 +4199,7 @@ class Exchange(object):
|
|
|
3419
4199
|
|
|
3420
4200
|
def network_id_to_code(self, networkId: Str = None, currencyCode: Str = None):
|
|
3421
4201
|
"""
|
|
3422
|
-
|
|
4202
|
+
@ignore
|
|
3423
4203
|
tries to convert the provided exchange-specific networkId to an unified network Code. In order to achieve self, derived class needs to have "options['networksById']" defined.
|
|
3424
4204
|
:param str networkId: exchange specific network id/title, like: TRON, Trc-20, usdt-erc20, etc
|
|
3425
4205
|
:param str|None currencyCode: unified currency code, but self argument is not required by default, unless there is an exchange(like huobi) that needs an override of the method to be able to pass currencyCode argument additionally
|
|
@@ -3452,7 +4232,7 @@ class Exchange(object):
|
|
|
3452
4232
|
defaultNetworkCode = defaultNetworks[currencyCode]
|
|
3453
4233
|
else:
|
|
3454
4234
|
# otherwise, try to use the global-scope 'defaultNetwork' value(even if that network is not supported by currency, it doesn't make any problem, self will be just used "at first" if currency supports self network at all)
|
|
3455
|
-
defaultNetwork = self.
|
|
4235
|
+
defaultNetwork = self.safe_string(self.options, 'defaultNetwork')
|
|
3456
4236
|
if defaultNetwork is not None:
|
|
3457
4237
|
defaultNetworkCode = defaultNetwork
|
|
3458
4238
|
return defaultNetworkCode
|
|
@@ -3473,11 +4253,11 @@ class Exchange(object):
|
|
|
3473
4253
|
raise NotSupported(self.id + ' - ' + networkCode + ' network did not return any result for ' + currencyCode)
|
|
3474
4254
|
else:
|
|
3475
4255
|
# if networkCode was provided by user, we should check it after response, referenced exchange doesn't support network-code during request
|
|
3476
|
-
|
|
3477
|
-
if
|
|
3478
|
-
chosenNetworkId =
|
|
4256
|
+
networkIdOrCode = networkCode if isIndexedByUnifiedNetworkCode else self.network_code_to_id(networkCode, currencyCode)
|
|
4257
|
+
if networkIdOrCode in indexedNetworkEntries:
|
|
4258
|
+
chosenNetworkId = networkIdOrCode
|
|
3479
4259
|
else:
|
|
3480
|
-
raise NotSupported(self.id + ' - ' +
|
|
4260
|
+
raise NotSupported(self.id + ' - ' + networkIdOrCode + ' network was not found for ' + currencyCode + ', use one of ' + ', '.join(availableNetworkIds))
|
|
3481
4261
|
else:
|
|
3482
4262
|
if responseNetworksLength == 0:
|
|
3483
4263
|
raise NotSupported(self.id + ' - no networks were returned for ' + currencyCode)
|
|
@@ -3485,7 +4265,9 @@ class Exchange(object):
|
|
|
3485
4265
|
# if networkCode was not provided by user, then we try to use the default network(if it was defined in "defaultNetworks"), otherwise, we just return the first network entry
|
|
3486
4266
|
defaultNetworkCode = self.default_network_code(currencyCode)
|
|
3487
4267
|
defaultNetworkId = defaultNetworkCode if isIndexedByUnifiedNetworkCode else self.network_code_to_id(defaultNetworkCode, currencyCode)
|
|
3488
|
-
|
|
4268
|
+
if defaultNetworkId in indexedNetworkEntries:
|
|
4269
|
+
return defaultNetworkId
|
|
4270
|
+
raise NotSupported(self.id + ' - can not determine the default network, please pass param["network"] one from : ' + ', '.join(availableNetworkIds))
|
|
3489
4271
|
return chosenNetworkId
|
|
3490
4272
|
|
|
3491
4273
|
def safe_number_2(self, dictionary: object, key1: IndexType, key2: IndexType, d=None):
|
|
@@ -3504,15 +4286,15 @@ class Exchange(object):
|
|
|
3504
4286
|
'nonce': None,
|
|
3505
4287
|
}
|
|
3506
4288
|
|
|
3507
|
-
def parse_ohlcvs(self, ohlcvs: List[object], market: Any = None, timeframe: str = '1m', since: Int = None, limit: Int = None):
|
|
4289
|
+
def parse_ohlcvs(self, ohlcvs: List[object], market: Any = None, timeframe: str = '1m', since: Int = None, limit: Int = None, tail: Bool = False):
|
|
3508
4290
|
results = []
|
|
3509
4291
|
for i in range(0, len(ohlcvs)):
|
|
3510
4292
|
results.append(self.parse_ohlcv(ohlcvs[i], market))
|
|
3511
4293
|
sorted = self.sort_by(results, 0)
|
|
3512
|
-
return self.filter_by_since_limit(sorted, since, limit, 0)
|
|
4294
|
+
return self.filter_by_since_limit(sorted, since, limit, 0, tail)
|
|
3513
4295
|
|
|
3514
4296
|
def parse_leverage_tiers(self, response: Any, symbols: List[str] = None, marketIdKey=None):
|
|
3515
|
-
# marketIdKey should only be None when response is a dictionary
|
|
4297
|
+
# marketIdKey should only be None when response is a dictionary.
|
|
3516
4298
|
symbols = self.market_symbols(symbols)
|
|
3517
4299
|
tiers = {}
|
|
3518
4300
|
symbolsLength = 0
|
|
@@ -3550,7 +4332,7 @@ class Exchange(object):
|
|
|
3550
4332
|
self.options['limitsLoaded'] = self.milliseconds()
|
|
3551
4333
|
return self.markets
|
|
3552
4334
|
|
|
3553
|
-
def safe_position(self, position):
|
|
4335
|
+
def safe_position(self, position: dict):
|
|
3554
4336
|
# simplified version of: /pull/12765/
|
|
3555
4337
|
unrealizedPnlString = self.safe_string(position, 'unrealisedPnl')
|
|
3556
4338
|
initialMarginString = self.safe_string(position, 'initialMargin')
|
|
@@ -3640,6 +4422,14 @@ class Exchange(object):
|
|
|
3640
4422
|
def set_headers(self, headers):
|
|
3641
4423
|
return headers
|
|
3642
4424
|
|
|
4425
|
+
def currency_id(self, code: str):
|
|
4426
|
+
currency = self.safe_dict(self.currencies, code)
|
|
4427
|
+
if currency is None:
|
|
4428
|
+
currency = self.safe_currency(code)
|
|
4429
|
+
if currency is not None:
|
|
4430
|
+
return currency['id']
|
|
4431
|
+
return code
|
|
4432
|
+
|
|
3643
4433
|
def market_id(self, symbol: str):
|
|
3644
4434
|
market = self.market(symbol)
|
|
3645
4435
|
if market is not None:
|
|
@@ -3686,6 +4476,23 @@ class Exchange(object):
|
|
|
3686
4476
|
params = self.omit(params, [paramName1, paramName2])
|
|
3687
4477
|
return [value, params]
|
|
3688
4478
|
|
|
4479
|
+
def handle_request_network(self, params: dict, request: dict, exchangeSpecificKey: str, currencyCode: Str = None, isRequired: bool = False):
|
|
4480
|
+
"""
|
|
4481
|
+
:param dict params: - extra parameters
|
|
4482
|
+
:param dict request: - existing dictionary of request
|
|
4483
|
+
:param str exchangeSpecificKey: - the key for chain id to be set in request
|
|
4484
|
+
:param dict currencyCode: - (optional) existing dictionary of request
|
|
4485
|
+
:param boolean isRequired: - (optional) whether that param is required to be present
|
|
4486
|
+
:returns dict[]: - returns [request, params] where request is the modified request object and params is the modified params object
|
|
4487
|
+
"""
|
|
4488
|
+
networkCode = None
|
|
4489
|
+
networkCode, params = self.handle_network_code_and_params(params)
|
|
4490
|
+
if networkCode is not None:
|
|
4491
|
+
request[exchangeSpecificKey] = self.network_code_to_id(networkCode, currencyCode)
|
|
4492
|
+
elif isRequired:
|
|
4493
|
+
raise ArgumentsRequired(self.id + ' - "network" param is required for self request')
|
|
4494
|
+
return [request, params]
|
|
4495
|
+
|
|
3689
4496
|
def resolve_path(self, path, params):
|
|
3690
4497
|
return [
|
|
3691
4498
|
self.implode_params(path, params),
|
|
@@ -3693,7 +4500,9 @@ class Exchange(object):
|
|
|
3693
4500
|
]
|
|
3694
4501
|
|
|
3695
4502
|
def get_list_from_object_values(self, objects, key: IndexType):
|
|
3696
|
-
newArray =
|
|
4503
|
+
newArray = objects
|
|
4504
|
+
if not isinstance(objects, list):
|
|
4505
|
+
newArray = self.to_array(objects)
|
|
3697
4506
|
results = []
|
|
3698
4507
|
for i in range(0, len(newArray)):
|
|
3699
4508
|
results.append(newArray[i][key])
|
|
@@ -3718,23 +4527,48 @@ class Exchange(object):
|
|
|
3718
4527
|
objects = self.to_array(objects)
|
|
3719
4528
|
# return all of them if no values were passed
|
|
3720
4529
|
if values is None or not values:
|
|
3721
|
-
return self.index_by(objects, key) if indexed else objects
|
|
4530
|
+
# return self.index_by(objects, key) if indexed else objects
|
|
4531
|
+
if indexed:
|
|
4532
|
+
return self.index_by(objects, key)
|
|
4533
|
+
else:
|
|
4534
|
+
return objects
|
|
3722
4535
|
results = []
|
|
3723
4536
|
for i in range(0, len(objects)):
|
|
3724
4537
|
if self.in_array(objects[i][key], values):
|
|
3725
4538
|
results.append(objects[i])
|
|
3726
|
-
return self.index_by(results, key) if indexed else results
|
|
4539
|
+
# return self.index_by(results, key) if indexed else results
|
|
4540
|
+
if indexed:
|
|
4541
|
+
return self.index_by(results, key)
|
|
4542
|
+
return results
|
|
3727
4543
|
|
|
3728
4544
|
def fetch2(self, path, api: Any = 'public', method='GET', params={}, headers: Any = None, body: Any = None, config={}):
|
|
3729
4545
|
if self.enableRateLimit:
|
|
3730
4546
|
cost = self.calculate_rate_limiter_cost(api, method, path, params, config)
|
|
3731
4547
|
self.throttle(cost)
|
|
4548
|
+
retries = None
|
|
4549
|
+
retries, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailure', 0)
|
|
4550
|
+
retryDelay = None
|
|
4551
|
+
retryDelay, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailureDelay', 0)
|
|
3732
4552
|
self.lastRestRequestTimestamp = self.milliseconds()
|
|
3733
4553
|
request = self.sign(path, api, method, params, headers, body)
|
|
3734
4554
|
self.last_request_headers = request['headers']
|
|
3735
4555
|
self.last_request_body = request['body']
|
|
3736
4556
|
self.last_request_url = request['url']
|
|
3737
|
-
|
|
4557
|
+
for i in range(0, retries + 1):
|
|
4558
|
+
try:
|
|
4559
|
+
return self.fetch(request['url'], request['method'], request['headers'], request['body'])
|
|
4560
|
+
except Exception as e:
|
|
4561
|
+
if isinstance(e, OperationFailed):
|
|
4562
|
+
if i < retries:
|
|
4563
|
+
if self.verbose:
|
|
4564
|
+
self.log('Request failed with the error: ' + str(e) + ', retrying ' + (i + str(1)) + ' of ' + str(retries) + '...')
|
|
4565
|
+
if (retryDelay is not None) and (retryDelay != 0):
|
|
4566
|
+
self.sleep(retryDelay)
|
|
4567
|
+
else:
|
|
4568
|
+
raise e
|
|
4569
|
+
else:
|
|
4570
|
+
raise e
|
|
4571
|
+
return None # self line is never reached, but exists for c# value return requirement
|
|
3738
4572
|
|
|
3739
4573
|
def request(self, path, api: Any = 'public', method='GET', params={}, headers: Any = None, body: Any = None, config={}):
|
|
3740
4574
|
return self.fetch2(path, api, method, params, headers, body, config)
|
|
@@ -3815,9 +4649,6 @@ class Exchange(object):
|
|
|
3815
4649
|
self.cancel_order_ws(id, symbol)
|
|
3816
4650
|
return self.create_order_ws(symbol, type, side, amount, price, params)
|
|
3817
4651
|
|
|
3818
|
-
def fetch_permissions(self, params={}):
|
|
3819
|
-
raise NotSupported(self.id + ' fetchPermissions() is not supported yet')
|
|
3820
|
-
|
|
3821
4652
|
def fetch_position(self, symbol: str, params={}):
|
|
3822
4653
|
raise NotSupported(self.id + ' fetchPosition() is not supported yet')
|
|
3823
4654
|
|
|
@@ -3895,7 +4726,7 @@ class Exchange(object):
|
|
|
3895
4726
|
'precision': None,
|
|
3896
4727
|
})
|
|
3897
4728
|
|
|
3898
|
-
def safe_market(self, marketId: Str, market: Market = None, delimiter: Str = None, marketType: Str = None):
|
|
4729
|
+
def safe_market(self, marketId: Str = None, market: Market = None, delimiter: Str = None, marketType: Str = None):
|
|
3899
4730
|
result = self.safe_market_structure({
|
|
3900
4731
|
'symbol': marketId,
|
|
3901
4732
|
'marketId': marketId,
|
|
@@ -3934,7 +4765,7 @@ class Exchange(object):
|
|
|
3934
4765
|
|
|
3935
4766
|
def check_required_credentials(self, error=True):
|
|
3936
4767
|
"""
|
|
3937
|
-
|
|
4768
|
+
@ignore
|
|
3938
4769
|
:param boolean error: raise an error that a credential is required if True
|
|
3939
4770
|
:returns boolean: True if all required credentials have been set, otherwise False or an error is thrown is param error=true
|
|
3940
4771
|
"""
|
|
@@ -4049,21 +4880,24 @@ class Exchange(object):
|
|
|
4049
4880
|
|
|
4050
4881
|
def handle_option_and_params_2(self, params: object, methodName1: str, optionName1: str, optionName2: str, defaultValue=None):
|
|
4051
4882
|
value = None
|
|
4052
|
-
value, params = self.handle_option_and_params(params, methodName1, optionName1
|
|
4883
|
+
value, params = self.handle_option_and_params(params, methodName1, optionName1)
|
|
4884
|
+
if value is not None:
|
|
4885
|
+
# omit optionName2 too from params
|
|
4886
|
+
params = self.omit(params, optionName2)
|
|
4887
|
+
return [value, params]
|
|
4053
4888
|
# if still None, try optionName2
|
|
4054
4889
|
value2 = None
|
|
4055
|
-
value2, params = self.handle_option_and_params(params, methodName1, optionName2,
|
|
4890
|
+
value2, params = self.handle_option_and_params(params, methodName1, optionName2, defaultValue)
|
|
4056
4891
|
return [value2, params]
|
|
4057
4892
|
|
|
4058
4893
|
def handle_option(self, methodName: str, optionName: str, defaultValue=None):
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
return result
|
|
4894
|
+
res = self.handle_option_and_params({}, methodName, optionName, defaultValue)
|
|
4895
|
+
return self.safe_value(res, 0)
|
|
4062
4896
|
|
|
4063
4897
|
def handle_market_type_and_params(self, methodName: str, market: Market = None, params={}, defaultValue=None):
|
|
4064
4898
|
"""
|
|
4065
|
-
|
|
4066
|
-
|
|
4899
|
+
@ignore
|
|
4900
|
+
@param methodName the method calling handleMarketTypeAndParams
|
|
4067
4901
|
:param Market market:
|
|
4068
4902
|
:param dict params:
|
|
4069
4903
|
:param str [params.type]: type assigned by user
|
|
@@ -4071,20 +4905,27 @@ class Exchange(object):
|
|
|
4071
4905
|
:param str [defaultValue]: assigned programatically in the method calling handleMarketTypeAndParams
|
|
4072
4906
|
:returns [str, dict]: the market type and params with type and defaultType omitted
|
|
4073
4907
|
"""
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4908
|
+
# type from param
|
|
4909
|
+
type = self.safe_string_2(params, 'defaultType', 'type')
|
|
4910
|
+
if type is not None:
|
|
4911
|
+
params = self.omit(params, ['defaultType', 'type'])
|
|
4912
|
+
return [type, params]
|
|
4913
|
+
# type from market
|
|
4914
|
+
if market is not None:
|
|
4915
|
+
return [market['type'], params]
|
|
4916
|
+
# type from default-argument
|
|
4917
|
+
if defaultValue is not None:
|
|
4918
|
+
return [defaultValue, params]
|
|
4077
4919
|
methodOptions = self.safe_dict(self.options, methodName)
|
|
4078
|
-
|
|
4079
|
-
if methodOptions is not None: # user defined methodType takes precedence over defaultValue
|
|
4920
|
+
if methodOptions is not None:
|
|
4080
4921
|
if isinstance(methodOptions, str):
|
|
4081
|
-
|
|
4922
|
+
return [methodOptions, params]
|
|
4082
4923
|
else:
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
return [
|
|
4924
|
+
typeFromMethod = self.safe_string_2(methodOptions, 'defaultType', 'type')
|
|
4925
|
+
if typeFromMethod is not None:
|
|
4926
|
+
return [typeFromMethod, params]
|
|
4927
|
+
defaultType = self.safe_string_2(self.options, 'defaultType', 'type', 'spot')
|
|
4928
|
+
return [defaultType, params]
|
|
4088
4929
|
|
|
4089
4930
|
def handle_sub_type_and_params(self, methodName: str, market=None, params={}, defaultValue=None):
|
|
4090
4931
|
subType = None
|
|
@@ -4109,7 +4950,7 @@ class Exchange(object):
|
|
|
4109
4950
|
|
|
4110
4951
|
def handle_margin_mode_and_params(self, methodName: str, params={}, defaultValue=None):
|
|
4111
4952
|
"""
|
|
4112
|
-
|
|
4953
|
+
@ignore
|
|
4113
4954
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
4114
4955
|
:returns Array: the marginMode in lowercase by params["marginMode"], params["defaultMarginMode"] self.options["marginMode"] or self.options["defaultMarginMode"]
|
|
4115
4956
|
"""
|
|
@@ -4158,19 +4999,33 @@ class Exchange(object):
|
|
|
4158
4999
|
else:
|
|
4159
5000
|
raise NotSupported(self.id + ' fetchTicker() is not supported yet')
|
|
4160
5001
|
|
|
5002
|
+
def fetch_mark_price(self, symbol: str, params={}):
|
|
5003
|
+
if self.has['fetchMarkPrices']:
|
|
5004
|
+
self.load_markets()
|
|
5005
|
+
market = self.market(symbol)
|
|
5006
|
+
symbol = market['symbol']
|
|
5007
|
+
tickers = self.fetch_mark_prices([symbol], params)
|
|
5008
|
+
ticker = self.safe_dict(tickers, symbol)
|
|
5009
|
+
if ticker is None:
|
|
5010
|
+
raise NullResponse(self.id + ' fetchMarkPrices() could not find a ticker for ' + symbol)
|
|
5011
|
+
else:
|
|
5012
|
+
return ticker
|
|
5013
|
+
else:
|
|
5014
|
+
raise NotSupported(self.id + ' fetchMarkPrices() is not supported yet')
|
|
5015
|
+
|
|
4161
5016
|
def fetch_ticker_ws(self, symbol: str, params={}):
|
|
4162
5017
|
if self.has['fetchTickersWs']:
|
|
4163
5018
|
self.load_markets()
|
|
4164
5019
|
market = self.market(symbol)
|
|
4165
5020
|
symbol = market['symbol']
|
|
4166
|
-
tickers = self.
|
|
5021
|
+
tickers = self.fetch_tickers_ws([symbol], params)
|
|
4167
5022
|
ticker = self.safe_dict(tickers, symbol)
|
|
4168
5023
|
if ticker is None:
|
|
4169
|
-
raise NullResponse(self.id + '
|
|
5024
|
+
raise NullResponse(self.id + ' fetchTickerWs() could not find a ticker for ' + symbol)
|
|
4170
5025
|
else:
|
|
4171
5026
|
return ticker
|
|
4172
5027
|
else:
|
|
4173
|
-
raise NotSupported(self.id + '
|
|
5028
|
+
raise NotSupported(self.id + ' fetchTickerWs() is not supported yet')
|
|
4174
5029
|
|
|
4175
5030
|
def watch_ticker(self, symbol: str, params={}):
|
|
4176
5031
|
raise NotSupported(self.id + ' watchTicker() is not supported yet')
|
|
@@ -4178,6 +5033,9 @@ class Exchange(object):
|
|
|
4178
5033
|
def fetch_tickers(self, symbols: Strings = None, params={}):
|
|
4179
5034
|
raise NotSupported(self.id + ' fetchTickers() is not supported yet')
|
|
4180
5035
|
|
|
5036
|
+
def fetch_mark_prices(self, symbols: Strings = None, params={}):
|
|
5037
|
+
raise NotSupported(self.id + ' fetchMarkPrices() is not supported yet')
|
|
5038
|
+
|
|
4181
5039
|
def fetch_tickers_ws(self, symbols: Strings = None, params={}):
|
|
4182
5040
|
raise NotSupported(self.id + ' fetchTickers() is not supported yet')
|
|
4183
5041
|
|
|
@@ -4190,6 +5048,9 @@ class Exchange(object):
|
|
|
4190
5048
|
def watch_tickers(self, symbols: Strings = None, params={}):
|
|
4191
5049
|
raise NotSupported(self.id + ' watchTickers() is not supported yet')
|
|
4192
5050
|
|
|
5051
|
+
def un_watch_tickers(self, symbols: Strings = None, params={}):
|
|
5052
|
+
raise NotSupported(self.id + ' unWatchTickers() is not supported yet')
|
|
5053
|
+
|
|
4193
5054
|
def fetch_order(self, id: str, symbol: Str = None, params={}):
|
|
4194
5055
|
raise NotSupported(self.id + ' fetchOrder() is not supported yet')
|
|
4195
5056
|
|
|
@@ -4208,7 +5069,19 @@ class Exchange(object):
|
|
|
4208
5069
|
def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
4209
5070
|
raise NotSupported(self.id + ' createOrder() is not supported yet')
|
|
4210
5071
|
|
|
4211
|
-
def
|
|
5072
|
+
def create_convert_trade(self, id: str, fromCode: str, toCode: str, amount: Num = None, params={}):
|
|
5073
|
+
raise NotSupported(self.id + ' createConvertTrade() is not supported yet')
|
|
5074
|
+
|
|
5075
|
+
def fetch_convert_trade(self, id: str, code: Str = None, params={}):
|
|
5076
|
+
raise NotSupported(self.id + ' fetchConvertTrade() is not supported yet')
|
|
5077
|
+
|
|
5078
|
+
def fetch_convert_trade_history(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
5079
|
+
raise NotSupported(self.id + ' fetchConvertTradeHistory() is not supported yet')
|
|
5080
|
+
|
|
5081
|
+
def fetch_position_mode(self, symbol: Str = None, params={}):
|
|
5082
|
+
raise NotSupported(self.id + ' fetchPositionMode() is not supported yet')
|
|
5083
|
+
|
|
5084
|
+
def create_trailing_amount_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingAmount: Num = None, trailingTriggerPrice: Num = None, params={}):
|
|
4212
5085
|
"""
|
|
4213
5086
|
create a trailing order by providing the symbol, type, side, amount, price and trailingAmount
|
|
4214
5087
|
:param str symbol: unified symbol of the market to create an order in
|
|
@@ -4230,7 +5103,7 @@ class Exchange(object):
|
|
|
4230
5103
|
return self.create_order(symbol, type, side, amount, price, params)
|
|
4231
5104
|
raise NotSupported(self.id + ' createTrailingAmountOrder() is not supported yet')
|
|
4232
5105
|
|
|
4233
|
-
def create_trailing_amount_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingAmount=None, trailingTriggerPrice=None, params={}):
|
|
5106
|
+
def create_trailing_amount_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingAmount: Num = None, trailingTriggerPrice: Num = None, params={}):
|
|
4234
5107
|
"""
|
|
4235
5108
|
create a trailing order by providing the symbol, type, side, amount, price and trailingAmount
|
|
4236
5109
|
:param str symbol: unified symbol of the market to create an order in
|
|
@@ -4252,7 +5125,7 @@ class Exchange(object):
|
|
|
4252
5125
|
return self.create_order_ws(symbol, type, side, amount, price, params)
|
|
4253
5126
|
raise NotSupported(self.id + ' createTrailingAmountOrderWs() is not supported yet')
|
|
4254
5127
|
|
|
4255
|
-
def create_trailing_percent_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent=None, trailingTriggerPrice=None, params={}):
|
|
5128
|
+
def create_trailing_percent_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent: Num = None, trailingTriggerPrice: Num = None, params={}):
|
|
4256
5129
|
"""
|
|
4257
5130
|
create a trailing order by providing the symbol, type, side, amount, price and trailingPercent
|
|
4258
5131
|
:param str symbol: unified symbol of the market to create an order in
|
|
@@ -4274,7 +5147,7 @@ class Exchange(object):
|
|
|
4274
5147
|
return self.create_order(symbol, type, side, amount, price, params)
|
|
4275
5148
|
raise NotSupported(self.id + ' createTrailingPercentOrder() is not supported yet')
|
|
4276
5149
|
|
|
4277
|
-
def create_trailing_percent_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent=None, trailingTriggerPrice=None, params={}):
|
|
5150
|
+
def create_trailing_percent_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent: Num = None, trailingTriggerPrice: Num = None, params={}):
|
|
4278
5151
|
"""
|
|
4279
5152
|
create a trailing order by providing the symbol, type, side, amount, price and trailingPercent
|
|
4280
5153
|
:param str symbol: unified symbol of the market to create an order in
|
|
@@ -4553,6 +5426,9 @@ class Exchange(object):
|
|
|
4553
5426
|
def create_orders(self, orders: List[OrderRequest], params={}):
|
|
4554
5427
|
raise NotSupported(self.id + ' createOrders() is not supported yet')
|
|
4555
5428
|
|
|
5429
|
+
def edit_orders(self, orders: List[OrderRequest], params={}):
|
|
5430
|
+
raise NotSupported(self.id + ' editOrders() is not supported yet')
|
|
5431
|
+
|
|
4556
5432
|
def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
4557
5433
|
raise NotSupported(self.id + ' createOrderWs() is not supported yet')
|
|
4558
5434
|
|
|
@@ -4639,6 +5515,9 @@ class Exchange(object):
|
|
|
4639
5515
|
def fetch_greeks(self, symbol: str, params={}):
|
|
4640
5516
|
raise NotSupported(self.id + ' fetchGreeks() is not supported yet')
|
|
4641
5517
|
|
|
5518
|
+
def fetch_all_greeks(self, symbols: Strings = None, params={}):
|
|
5519
|
+
raise NotSupported(self.id + ' fetchAllGreeks() is not supported yet')
|
|
5520
|
+
|
|
4642
5521
|
def fetch_option_chain(self, code: str, params={}):
|
|
4643
5522
|
raise NotSupported(self.id + ' fetchOptionChain() is not supported yet')
|
|
4644
5523
|
|
|
@@ -4659,10 +5538,10 @@ class Exchange(object):
|
|
|
4659
5538
|
"""
|
|
4660
5539
|
raise NotSupported(self.id + ' fetchDepositsWithdrawals() is not supported yet')
|
|
4661
5540
|
|
|
4662
|
-
def fetch_deposits(self,
|
|
5541
|
+
def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
4663
5542
|
raise NotSupported(self.id + ' fetchDeposits() is not supported yet')
|
|
4664
5543
|
|
|
4665
|
-
def fetch_withdrawals(self,
|
|
5544
|
+
def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
4666
5545
|
raise NotSupported(self.id + ' fetchWithdrawals() is not supported yet')
|
|
4667
5546
|
|
|
4668
5547
|
def fetch_deposits_ws(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
@@ -4752,6 +5631,24 @@ class Exchange(object):
|
|
|
4752
5631
|
def create_expired_option_market(self, symbol: str):
|
|
4753
5632
|
raise NotSupported(self.id + ' createExpiredOptionMarket() is not supported yet')
|
|
4754
5633
|
|
|
5634
|
+
def is_leveraged_currency(self, currencyCode, checkBaseCoin: Bool = False, existingCurrencies: dict = None):
|
|
5635
|
+
leverageSuffixes = [
|
|
5636
|
+
'2L', '2S', '3L', '3S', '4L', '4S', '5L', '5S', # Leveraged Tokens(LT)
|
|
5637
|
+
'UP', 'DOWN', # exchange-specific(e.g. BLVT)
|
|
5638
|
+
'BULL', 'BEAR', # similar
|
|
5639
|
+
]
|
|
5640
|
+
for i in range(0, len(leverageSuffixes)):
|
|
5641
|
+
leverageSuffix = leverageSuffixes[i]
|
|
5642
|
+
if currencyCode.endswith(leverageSuffix):
|
|
5643
|
+
if not checkBaseCoin:
|
|
5644
|
+
return True
|
|
5645
|
+
else:
|
|
5646
|
+
# check if base currency is inside dict
|
|
5647
|
+
baseCurrencyCode = currencyCode.replace(leverageSuffix, '')
|
|
5648
|
+
if baseCurrencyCode in existingCurrencies:
|
|
5649
|
+
return True
|
|
5650
|
+
return False
|
|
5651
|
+
|
|
4755
5652
|
def handle_withdraw_tag_and_params(self, tag, params):
|
|
4756
5653
|
if (tag is not None) and (isinstance(tag, dict)):
|
|
4757
5654
|
params = self.extend(tag, params)
|
|
@@ -4799,10 +5696,14 @@ class Exchange(object):
|
|
|
4799
5696
|
return self.create_order_ws(symbol, 'market', 'sell', amount, None, params)
|
|
4800
5697
|
|
|
4801
5698
|
def cost_to_precision(self, symbol: str, cost):
|
|
5699
|
+
if cost is None:
|
|
5700
|
+
return None
|
|
4802
5701
|
market = self.market(symbol)
|
|
4803
5702
|
return self.decimal_to_precision(cost, TRUNCATE, market['precision']['price'], self.precisionMode, self.paddingMode)
|
|
4804
5703
|
|
|
4805
5704
|
def price_to_precision(self, symbol: str, price):
|
|
5705
|
+
if price is None:
|
|
5706
|
+
return None
|
|
4806
5707
|
market = self.market(symbol)
|
|
4807
5708
|
result = self.decimal_to_precision(price, ROUND, market['precision']['price'], self.precisionMode, self.paddingMode)
|
|
4808
5709
|
if result == '0':
|
|
@@ -4810,6 +5711,8 @@ class Exchange(object):
|
|
|
4810
5711
|
return result
|
|
4811
5712
|
|
|
4812
5713
|
def amount_to_precision(self, symbol: str, amount):
|
|
5714
|
+
if amount is None:
|
|
5715
|
+
return None
|
|
4813
5716
|
market = self.market(symbol)
|
|
4814
5717
|
result = self.decimal_to_precision(amount, TRUNCATE, market['precision']['amount'], self.precisionMode, self.paddingMode)
|
|
4815
5718
|
if result == '0':
|
|
@@ -4817,6 +5720,8 @@ class Exchange(object):
|
|
|
4817
5720
|
return result
|
|
4818
5721
|
|
|
4819
5722
|
def fee_to_precision(self, symbol: str, fee):
|
|
5723
|
+
if fee is None:
|
|
5724
|
+
return None
|
|
4820
5725
|
market = self.market(symbol)
|
|
4821
5726
|
return self.decimal_to_precision(fee, ROUND, market['precision']['price'], self.precisionMode, self.paddingMode)
|
|
4822
5727
|
|
|
@@ -4830,7 +5735,8 @@ class Exchange(object):
|
|
|
4830
5735
|
if precision is None:
|
|
4831
5736
|
return self.force_string(fee)
|
|
4832
5737
|
else:
|
|
4833
|
-
|
|
5738
|
+
roundingMode = self.safe_integer(self.options, 'currencyToPrecisionRoundingMode', ROUND)
|
|
5739
|
+
return self.decimal_to_precision(fee, roundingMode, precision, self.precisionMode, self.paddingMode)
|
|
4834
5740
|
|
|
4835
5741
|
def force_string(self, value):
|
|
4836
5742
|
if not isinstance(value, str):
|
|
@@ -4856,7 +5762,7 @@ class Exchange(object):
|
|
|
4856
5762
|
|
|
4857
5763
|
def parse_precision(self, precision: str):
|
|
4858
5764
|
"""
|
|
4859
|
-
|
|
5765
|
+
@ignore
|
|
4860
5766
|
:param str precision: The number of digits to the right of the decimal
|
|
4861
5767
|
:returns str: a string number equal to 1e-precision
|
|
4862
5768
|
"""
|
|
@@ -4865,14 +5771,20 @@ class Exchange(object):
|
|
|
4865
5771
|
precisionNumber = int(precision)
|
|
4866
5772
|
if precisionNumber == 0:
|
|
4867
5773
|
return '1'
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
5774
|
+
if precisionNumber > 0:
|
|
5775
|
+
parsedPrecision = '0.'
|
|
5776
|
+
for i in range(0, precisionNumber - 1):
|
|
5777
|
+
parsedPrecision = parsedPrecision + '0'
|
|
5778
|
+
return parsedPrecision + '1'
|
|
5779
|
+
else:
|
|
5780
|
+
parsedPrecision = '1'
|
|
5781
|
+
for i in range(0, precisionNumber * -1 - 1):
|
|
5782
|
+
parsedPrecision = parsedPrecision + '0'
|
|
5783
|
+
return parsedPrecision + '0'
|
|
4872
5784
|
|
|
4873
5785
|
def integer_precision_to_amount(self, precision: Str):
|
|
4874
5786
|
"""
|
|
4875
|
-
|
|
5787
|
+
@ignore
|
|
4876
5788
|
handles positive & negative numbers too. parsePrecision() does not handle negative numbers, but self method handles
|
|
4877
5789
|
:param str precision: The number of digits to the right of the decimal
|
|
4878
5790
|
:returns str: a string number equal to 1e-precision
|
|
@@ -4910,66 +5822,66 @@ class Exchange(object):
|
|
|
4910
5822
|
|
|
4911
5823
|
def create_post_only_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
4912
5824
|
if not self.has['createPostOnlyOrder']:
|
|
4913
|
-
raise NotSupported(self.id + 'createPostOnlyOrder() is not supported yet')
|
|
5825
|
+
raise NotSupported(self.id + ' createPostOnlyOrder() is not supported yet')
|
|
4914
5826
|
query = self.extend(params, {'postOnly': True})
|
|
4915
5827
|
return self.create_order(symbol, type, side, amount, price, query)
|
|
4916
5828
|
|
|
4917
5829
|
def create_post_only_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
4918
5830
|
if not self.has['createPostOnlyOrderWs']:
|
|
4919
|
-
raise NotSupported(self.id + 'createPostOnlyOrderWs() is not supported yet')
|
|
5831
|
+
raise NotSupported(self.id + ' createPostOnlyOrderWs() is not supported yet')
|
|
4920
5832
|
query = self.extend(params, {'postOnly': True})
|
|
4921
5833
|
return self.create_order_ws(symbol, type, side, amount, price, query)
|
|
4922
5834
|
|
|
4923
5835
|
def create_reduce_only_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
4924
5836
|
if not self.has['createReduceOnlyOrder']:
|
|
4925
|
-
raise NotSupported(self.id + 'createReduceOnlyOrder() is not supported yet')
|
|
5837
|
+
raise NotSupported(self.id + ' createReduceOnlyOrder() is not supported yet')
|
|
4926
5838
|
query = self.extend(params, {'reduceOnly': True})
|
|
4927
5839
|
return self.create_order(symbol, type, side, amount, price, query)
|
|
4928
5840
|
|
|
4929
5841
|
def create_reduce_only_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
4930
5842
|
if not self.has['createReduceOnlyOrderWs']:
|
|
4931
|
-
raise NotSupported(self.id + 'createReduceOnlyOrderWs() is not supported yet')
|
|
5843
|
+
raise NotSupported(self.id + ' createReduceOnlyOrderWs() is not supported yet')
|
|
4932
5844
|
query = self.extend(params, {'reduceOnly': True})
|
|
4933
5845
|
return self.create_order_ws(symbol, type, side, amount, price, query)
|
|
4934
5846
|
|
|
4935
|
-
def create_stop_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None,
|
|
5847
|
+
def create_stop_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, triggerPrice: Num = None, params={}):
|
|
4936
5848
|
if not self.has['createStopOrder']:
|
|
4937
5849
|
raise NotSupported(self.id + ' createStopOrder() is not supported yet')
|
|
4938
|
-
if
|
|
5850
|
+
if triggerPrice is None:
|
|
4939
5851
|
raise ArgumentsRequired(self.id + ' create_stop_order() requires a stopPrice argument')
|
|
4940
|
-
query = self.extend(params, {'stopPrice':
|
|
5852
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
|
4941
5853
|
return self.create_order(symbol, type, side, amount, price, query)
|
|
4942
5854
|
|
|
4943
|
-
def create_stop_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None,
|
|
5855
|
+
def create_stop_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, triggerPrice: Num = None, params={}):
|
|
4944
5856
|
if not self.has['createStopOrderWs']:
|
|
4945
5857
|
raise NotSupported(self.id + ' createStopOrderWs() is not supported yet')
|
|
4946
|
-
if
|
|
5858
|
+
if triggerPrice is None:
|
|
4947
5859
|
raise ArgumentsRequired(self.id + ' createStopOrderWs() requires a stopPrice argument')
|
|
4948
|
-
query = self.extend(params, {'stopPrice':
|
|
5860
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
|
4949
5861
|
return self.create_order_ws(symbol, type, side, amount, price, query)
|
|
4950
5862
|
|
|
4951
|
-
def create_stop_limit_order(self, symbol: str, side: OrderSide, amount: float, price: float,
|
|
5863
|
+
def create_stop_limit_order(self, symbol: str, side: OrderSide, amount: float, price: float, triggerPrice: float, params={}):
|
|
4952
5864
|
if not self.has['createStopLimitOrder']:
|
|
4953
5865
|
raise NotSupported(self.id + ' createStopLimitOrder() is not supported yet')
|
|
4954
|
-
query = self.extend(params, {'stopPrice':
|
|
5866
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
|
4955
5867
|
return self.create_order(symbol, 'limit', side, amount, price, query)
|
|
4956
5868
|
|
|
4957
|
-
def create_stop_limit_order_ws(self, symbol: str, side: OrderSide, amount: float, price: float,
|
|
5869
|
+
def create_stop_limit_order_ws(self, symbol: str, side: OrderSide, amount: float, price: float, triggerPrice: float, params={}):
|
|
4958
5870
|
if not self.has['createStopLimitOrderWs']:
|
|
4959
5871
|
raise NotSupported(self.id + ' createStopLimitOrderWs() is not supported yet')
|
|
4960
|
-
query = self.extend(params, {'stopPrice':
|
|
5872
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
|
4961
5873
|
return self.create_order_ws(symbol, 'limit', side, amount, price, query)
|
|
4962
5874
|
|
|
4963
|
-
def create_stop_market_order(self, symbol: str, side: OrderSide, amount: float,
|
|
5875
|
+
def create_stop_market_order(self, symbol: str, side: OrderSide, amount: float, triggerPrice: float, params={}):
|
|
4964
5876
|
if not self.has['createStopMarketOrder']:
|
|
4965
5877
|
raise NotSupported(self.id + ' createStopMarketOrder() is not supported yet')
|
|
4966
|
-
query = self.extend(params, {'stopPrice':
|
|
5878
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
|
4967
5879
|
return self.create_order(symbol, 'market', side, amount, None, query)
|
|
4968
5880
|
|
|
4969
|
-
def create_stop_market_order_ws(self, symbol: str, side: OrderSide, amount: float,
|
|
5881
|
+
def create_stop_market_order_ws(self, symbol: str, side: OrderSide, amount: float, triggerPrice: float, params={}):
|
|
4970
5882
|
if not self.has['createStopMarketOrderWs']:
|
|
4971
5883
|
raise NotSupported(self.id + ' createStopMarketOrderWs() is not supported yet')
|
|
4972
|
-
query = self.extend(params, {'stopPrice':
|
|
5884
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
|
4973
5885
|
return self.create_order_ws(symbol, 'market', side, amount, None, query)
|
|
4974
5886
|
|
|
4975
5887
|
def safe_currency_code(self, currencyId: Str, currency: Currency = None):
|
|
@@ -5047,14 +5959,16 @@ class Exchange(object):
|
|
|
5047
5959
|
results = []
|
|
5048
5960
|
if isinstance(tickers, list):
|
|
5049
5961
|
for i in range(0, len(tickers)):
|
|
5050
|
-
|
|
5962
|
+
parsedTicker = self.parse_ticker(tickers[i])
|
|
5963
|
+
ticker = self.extend(parsedTicker, params)
|
|
5051
5964
|
results.append(ticker)
|
|
5052
5965
|
else:
|
|
5053
5966
|
marketIds = list(tickers.keys())
|
|
5054
5967
|
for i in range(0, len(marketIds)):
|
|
5055
5968
|
marketId = marketIds[i]
|
|
5056
5969
|
market = self.safe_market(marketId)
|
|
5057
|
-
|
|
5970
|
+
parsed = self.parse_ticker(tickers[marketId], market)
|
|
5971
|
+
ticker = self.extend(parsed, params)
|
|
5058
5972
|
results.append(ticker)
|
|
5059
5973
|
symbols = self.market_symbols(symbols)
|
|
5060
5974
|
return self.filter_by_array(results, 'symbol', symbols)
|
|
@@ -5067,7 +5981,7 @@ class Exchange(object):
|
|
|
5067
5981
|
if codes is not None:
|
|
5068
5982
|
result = self.filter_by_array(result, 'currency', codes, False)
|
|
5069
5983
|
if indexed:
|
|
5070
|
-
|
|
5984
|
+
result = self.filter_by_array(result, 'currency', None, indexed)
|
|
5071
5985
|
return result
|
|
5072
5986
|
|
|
5073
5987
|
def parse_borrow_interests(self, response, market: Market = None):
|
|
@@ -5077,6 +5991,18 @@ class Exchange(object):
|
|
|
5077
5991
|
interests.append(self.parse_borrow_interest(row, market))
|
|
5078
5992
|
return interests
|
|
5079
5993
|
|
|
5994
|
+
def parse_borrow_rate(self, info, currency: Currency = None):
|
|
5995
|
+
raise NotSupported(self.id + ' parseBorrowRate() is not supported yet')
|
|
5996
|
+
|
|
5997
|
+
def parse_borrow_rate_history(self, response, code: Str, since: Int, limit: Int):
|
|
5998
|
+
result = []
|
|
5999
|
+
for i in range(0, len(response)):
|
|
6000
|
+
item = response[i]
|
|
6001
|
+
borrowRate = self.parse_borrow_rate(item)
|
|
6002
|
+
result.append(borrowRate)
|
|
6003
|
+
sorted = self.sort_by(result, 'timestamp')
|
|
6004
|
+
return self.filter_by_currency_since_limit(sorted, code, since, limit)
|
|
6005
|
+
|
|
5080
6006
|
def parse_isolated_borrow_rates(self, info: Any):
|
|
5081
6007
|
result = {}
|
|
5082
6008
|
for i in range(0, len(info)):
|
|
@@ -5102,12 +6028,46 @@ class Exchange(object):
|
|
|
5102
6028
|
def parse_funding_rate(self, contract: str, market: Market = None):
|
|
5103
6029
|
raise NotSupported(self.id + ' parseFundingRate() is not supported yet')
|
|
5104
6030
|
|
|
5105
|
-
def parse_funding_rates(self, response,
|
|
5106
|
-
|
|
6031
|
+
def parse_funding_rates(self, response, symbols: Strings = None):
|
|
6032
|
+
fundingRates = {}
|
|
5107
6033
|
for i in range(0, len(response)):
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
6034
|
+
entry = response[i]
|
|
6035
|
+
parsed = self.parse_funding_rate(entry)
|
|
6036
|
+
fundingRates[parsed['symbol']] = parsed
|
|
6037
|
+
return self.filter_by_array(fundingRates, 'symbol', symbols)
|
|
6038
|
+
|
|
6039
|
+
def parse_long_short_ratio(self, info: dict, market: Market = None):
|
|
6040
|
+
raise NotSupported(self.id + ' parseLongShortRatio() is not supported yet')
|
|
6041
|
+
|
|
6042
|
+
def parse_long_short_ratio_history(self, response, market=None, since: Int = None, limit: Int = None):
|
|
6043
|
+
rates = []
|
|
6044
|
+
for i in range(0, len(response)):
|
|
6045
|
+
entry = response[i]
|
|
6046
|
+
rates.append(self.parse_long_short_ratio(entry, market))
|
|
6047
|
+
sorted = self.sort_by(rates, 'timestamp')
|
|
6048
|
+
symbol = None if (market is None) else market['symbol']
|
|
6049
|
+
return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
|
|
6050
|
+
|
|
6051
|
+
def handle_trigger_direction_and_params(self, params, exchangeSpecificKey: Str = None, allowEmpty: Bool = False):
|
|
6052
|
+
"""
|
|
6053
|
+
@ignore
|
|
6054
|
+
:returns [str, dict]: the trigger-direction value and omited params
|
|
6055
|
+
"""
|
|
6056
|
+
triggerDirection = self.safe_string(params, 'triggerDirection')
|
|
6057
|
+
exchangeSpecificDefined = (exchangeSpecificKey is not None) and (exchangeSpecificKey in params)
|
|
6058
|
+
if triggerDirection is not None:
|
|
6059
|
+
params = self.omit(params, 'triggerDirection')
|
|
6060
|
+
# raise exception if:
|
|
6061
|
+
# A) if provided value is not unified(support old "up/down" strings too)
|
|
6062
|
+
# B) if exchange specific "trigger direction key"(eg. "stopPriceSide") was not provided
|
|
6063
|
+
if not self.in_array(triggerDirection, ['ascending', 'descending', 'up', 'down', 'above', 'below']) and not exchangeSpecificDefined and not allowEmpty:
|
|
6064
|
+
raise ArgumentsRequired(self.id + ' createOrder() : trigger orders require params["triggerDirection"] to be either "ascending" or "descending"')
|
|
6065
|
+
# if old format was provided, overwrite to new
|
|
6066
|
+
if triggerDirection == 'up' or triggerDirection == 'above':
|
|
6067
|
+
triggerDirection = 'ascending'
|
|
6068
|
+
elif triggerDirection == 'down' or triggerDirection == 'below':
|
|
6069
|
+
triggerDirection = 'descending'
|
|
6070
|
+
return [triggerDirection, params]
|
|
5111
6071
|
|
|
5112
6072
|
def handle_trigger_and_params(self, params):
|
|
5113
6073
|
isTrigger = self.safe_bool_2(params, 'trigger', 'stop')
|
|
@@ -5121,7 +6081,7 @@ class Exchange(object):
|
|
|
5121
6081
|
|
|
5122
6082
|
def is_post_only(self, isMarketOrder: bool, exchangeSpecificParam, params={}):
|
|
5123
6083
|
"""
|
|
5124
|
-
|
|
6084
|
+
@ignore
|
|
5125
6085
|
:param str type: Order type
|
|
5126
6086
|
:param boolean exchangeSpecificParam: exchange specific postOnly
|
|
5127
6087
|
:param dict [params]: exchange specific params
|
|
@@ -5146,7 +6106,7 @@ class Exchange(object):
|
|
|
5146
6106
|
|
|
5147
6107
|
def handle_post_only(self, isMarketOrder: bool, exchangeSpecificPostOnlyOption: bool, params: Any = {}):
|
|
5148
6108
|
"""
|
|
5149
|
-
|
|
6109
|
+
@ignore
|
|
5150
6110
|
:param str type: Order type
|
|
5151
6111
|
:param boolean exchangeSpecificBoolean: exchange specific postOnly
|
|
5152
6112
|
:param dict [params]: exchange specific params
|
|
@@ -5191,7 +6151,15 @@ class Exchange(object):
|
|
|
5191
6151
|
def parse_open_interest(self, interest, market: Market = None):
|
|
5192
6152
|
raise NotSupported(self.id + ' parseOpenInterest() is not supported yet')
|
|
5193
6153
|
|
|
5194
|
-
def parse_open_interests(self, response,
|
|
6154
|
+
def parse_open_interests(self, response, symbols: Strings = None):
|
|
6155
|
+
result = {}
|
|
6156
|
+
for i in range(0, len(response)):
|
|
6157
|
+
entry = response[i]
|
|
6158
|
+
parsed = self.parse_open_interest(entry)
|
|
6159
|
+
result[parsed['symbol']] = parsed
|
|
6160
|
+
return self.filter_by_array(result, 'symbol', symbols)
|
|
6161
|
+
|
|
6162
|
+
def parse_open_interests_history(self, response, market=None, since: Int = None, limit: Int = None):
|
|
5195
6163
|
interests = []
|
|
5196
6164
|
for i in range(0, len(response)):
|
|
5197
6165
|
entry = response[i]
|
|
@@ -5217,7 +6185,23 @@ class Exchange(object):
|
|
|
5217
6185
|
else:
|
|
5218
6186
|
raise NotSupported(self.id + ' fetchFundingRate() is not supported yet')
|
|
5219
6187
|
|
|
5220
|
-
def
|
|
6188
|
+
def fetch_funding_interval(self, symbol: str, params={}):
|
|
6189
|
+
if self.has['fetchFundingIntervals']:
|
|
6190
|
+
self.load_markets()
|
|
6191
|
+
market = self.market(symbol)
|
|
6192
|
+
symbol = market['symbol']
|
|
6193
|
+
if not market['contract']:
|
|
6194
|
+
raise BadSymbol(self.id + ' fetchFundingInterval() supports contract markets only')
|
|
6195
|
+
rates = self.fetch_funding_intervals([symbol], params)
|
|
6196
|
+
rate = self.safe_value(rates, symbol)
|
|
6197
|
+
if rate is None:
|
|
6198
|
+
raise NullResponse(self.id + ' fetchFundingInterval() returned no data for ' + symbol)
|
|
6199
|
+
else:
|
|
6200
|
+
return rate
|
|
6201
|
+
else:
|
|
6202
|
+
raise NotSupported(self.id + ' fetchFundingInterval() is not supported yet')
|
|
6203
|
+
|
|
6204
|
+
def fetch_mark_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
|
5221
6205
|
"""
|
|
5222
6206
|
fetches historical mark price candlestick data containing the open, high, low, and close price of a market
|
|
5223
6207
|
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
|
@@ -5243,7 +6227,7 @@ class Exchange(object):
|
|
|
5243
6227
|
:param int [since]: timestamp in ms of the earliest candle to fetch
|
|
5244
6228
|
:param int [limit]: the maximum amount of candles to fetch
|
|
5245
6229
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
5246
|
-
|
|
6230
|
+
@returns {} A list of candles ordered, open, high, low, close, None
|
|
5247
6231
|
"""
|
|
5248
6232
|
if self.has['fetchIndexOHLCV']:
|
|
5249
6233
|
request: dict = {
|
|
@@ -5273,8 +6257,8 @@ class Exchange(object):
|
|
|
5273
6257
|
|
|
5274
6258
|
def handle_time_in_force(self, params={}):
|
|
5275
6259
|
"""
|
|
5276
|
-
|
|
5277
|
-
|
|
6260
|
+
@ignore
|
|
6261
|
+
Must add timeInForce to self.options to use self method
|
|
5278
6262
|
:returns str: returns the exchange specific value for timeInForce
|
|
5279
6263
|
"""
|
|
5280
6264
|
timeInForce = self.safe_string_upper(params, 'timeInForce') # supported values GTC, IOC, PO
|
|
@@ -5287,8 +6271,8 @@ class Exchange(object):
|
|
|
5287
6271
|
|
|
5288
6272
|
def convert_type_to_account(self, account):
|
|
5289
6273
|
"""
|
|
5290
|
-
|
|
5291
|
-
|
|
6274
|
+
@ignore
|
|
6275
|
+
Must add accountsByType to self.options to use self method
|
|
5292
6276
|
:param str account: key for account name in self.options['accountsByType']
|
|
5293
6277
|
:returns: the exchange specific account name or the isolated margin id for transfers
|
|
5294
6278
|
"""
|
|
@@ -5304,7 +6288,7 @@ class Exchange(object):
|
|
|
5304
6288
|
|
|
5305
6289
|
def check_required_argument(self, methodName: str, argument, argumentName, options=[]):
|
|
5306
6290
|
"""
|
|
5307
|
-
|
|
6291
|
+
@ignore
|
|
5308
6292
|
:param str methodName: the name of the method that the argument is being checked for
|
|
5309
6293
|
:param str argument: the argument's actual value provided
|
|
5310
6294
|
:param str argumentName: the name of the argument being checked(for logging purposes)
|
|
@@ -5321,7 +6305,7 @@ class Exchange(object):
|
|
|
5321
6305
|
|
|
5322
6306
|
def check_required_margin_argument(self, methodName: str, symbol: Str, marginMode: str):
|
|
5323
6307
|
"""
|
|
5324
|
-
|
|
6308
|
+
@ignore
|
|
5325
6309
|
:param str symbol: unified symbol of the market
|
|
5326
6310
|
:param str methodName: name of the method that requires a symbol
|
|
5327
6311
|
:param str marginMode: is either 'isolated' or 'cross'
|
|
@@ -5333,7 +6317,7 @@ class Exchange(object):
|
|
|
5333
6317
|
|
|
5334
6318
|
def parse_deposit_withdraw_fees(self, response, codes: Strings = None, currencyIdKey=None):
|
|
5335
6319
|
"""
|
|
5336
|
-
|
|
6320
|
+
@ignore
|
|
5337
6321
|
:param object[]|dict response: unparsed response from the exchange
|
|
5338
6322
|
:param str[]|None codes: the unified currency codes to fetch transactions fees for, returns all currencies when None
|
|
5339
6323
|
:param str currencyIdKey: *should only be None when response is a dictionary* the object key that corresponds to the currency id
|
|
@@ -5373,7 +6357,7 @@ class Exchange(object):
|
|
|
5373
6357
|
|
|
5374
6358
|
def assign_default_deposit_withdraw_fees(self, fee, currency=None):
|
|
5375
6359
|
"""
|
|
5376
|
-
|
|
6360
|
+
@ignore
|
|
5377
6361
|
Takes a depositWithdrawFee structure and assigns the default values for withdraw and deposit
|
|
5378
6362
|
:param dict fee: A deposit withdraw fee structure
|
|
5379
6363
|
:param dict currency: A currency structure, the response from self.currency()
|
|
@@ -5398,7 +6382,7 @@ class Exchange(object):
|
|
|
5398
6382
|
|
|
5399
6383
|
def parse_incomes(self, incomes, market=None, since: Int = None, limit: Int = None):
|
|
5400
6384
|
"""
|
|
5401
|
-
|
|
6385
|
+
@ignore
|
|
5402
6386
|
parses funding fee info from exchange response
|
|
5403
6387
|
:param dict[] incomes: each item describes once instance of currency being received or paid
|
|
5404
6388
|
:param dict market: ccxt market
|
|
@@ -5412,7 +6396,8 @@ class Exchange(object):
|
|
|
5412
6396
|
parsed = self.parse_income(entry, market)
|
|
5413
6397
|
result.append(parsed)
|
|
5414
6398
|
sorted = self.sort_by(result, 'timestamp')
|
|
5415
|
-
|
|
6399
|
+
symbol = self.safe_string(market, 'symbol')
|
|
6400
|
+
return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
|
|
5416
6401
|
|
|
5417
6402
|
def get_market_from_symbols(self, symbols: Strings = None):
|
|
5418
6403
|
if symbols is None:
|
|
@@ -5429,7 +6414,7 @@ class Exchange(object):
|
|
|
5429
6414
|
|
|
5430
6415
|
def fetch_transactions(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
5431
6416
|
"""
|
|
5432
|
-
|
|
6417
|
+
@deprecated
|
|
5433
6418
|
*DEPRECATED* use fetchDepositsWithdrawals instead
|
|
5434
6419
|
:param str code: unified currency code for the currency of the deposit/withdrawals, default is None
|
|
5435
6420
|
:param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
|
|
@@ -5444,14 +6429,14 @@ class Exchange(object):
|
|
|
5444
6429
|
|
|
5445
6430
|
def filter_by_array_positions(self, objects, key: IndexType, values=None, indexed=True):
|
|
5446
6431
|
"""
|
|
5447
|
-
|
|
6432
|
+
@ignore
|
|
5448
6433
|
Typed wrapper for filterByArray that returns a list of positions
|
|
5449
6434
|
"""
|
|
5450
6435
|
return self.filter_by_array(objects, key, values, indexed)
|
|
5451
6436
|
|
|
5452
6437
|
def filter_by_array_tickers(self, objects, key: IndexType, values=None, indexed=True):
|
|
5453
6438
|
"""
|
|
5454
|
-
|
|
6439
|
+
@ignore
|
|
5455
6440
|
Typed wrapper for filterByArray that returns a dictionary of tickers
|
|
5456
6441
|
"""
|
|
5457
6442
|
return self.filter_by_array(objects, key, values, indexed)
|
|
@@ -5471,7 +6456,7 @@ class Exchange(object):
|
|
|
5471
6456
|
maxEntriesPerRequest = 1000 # default to 1000
|
|
5472
6457
|
return [maxEntriesPerRequest, params]
|
|
5473
6458
|
|
|
5474
|
-
def fetch_paginated_call_dynamic(self, method: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}, maxEntriesPerRequest: Int = None):
|
|
6459
|
+
def fetch_paginated_call_dynamic(self, method: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}, maxEntriesPerRequest: Int = None, removeRepeated=True):
|
|
5475
6460
|
maxCalls = None
|
|
5476
6461
|
maxCalls, params = self.handle_option_and_params(params, method, 'paginationCalls', 10)
|
|
5477
6462
|
maxRetries = None
|
|
@@ -5479,10 +6464,12 @@ class Exchange(object):
|
|
|
5479
6464
|
paginationDirection = None
|
|
5480
6465
|
paginationDirection, params = self.handle_option_and_params(params, method, 'paginationDirection', 'backward')
|
|
5481
6466
|
paginationTimestamp = None
|
|
6467
|
+
removeRepeatedOption = removeRepeated
|
|
6468
|
+
removeRepeatedOption, params = self.handle_option_and_params(params, method, 'removeRepeated', removeRepeated)
|
|
5482
6469
|
calls = 0
|
|
5483
6470
|
result = []
|
|
5484
6471
|
errors = 0
|
|
5485
|
-
until = self.
|
|
6472
|
+
until = self.safe_integer_n(params, ['until', 'untill', 'till']) # do not omit it from params here
|
|
5486
6473
|
maxEntriesPerRequest, params = self.handle_max_entries_per_request_and_params(method, maxEntriesPerRequest, params)
|
|
5487
6474
|
if (paginationDirection == 'forward'):
|
|
5488
6475
|
if since is None:
|
|
@@ -5525,14 +6512,16 @@ class Exchange(object):
|
|
|
5525
6512
|
errors = 0
|
|
5526
6513
|
result = self.array_concat(result, response)
|
|
5527
6514
|
last = self.safe_value(response, responseLength - 1)
|
|
5528
|
-
paginationTimestamp = self.safe_integer(last, 'timestamp')
|
|
6515
|
+
paginationTimestamp = self.safe_integer(last, 'timestamp') + 1
|
|
5529
6516
|
if (until is not None) and (paginationTimestamp >= until):
|
|
5530
6517
|
break
|
|
5531
6518
|
except Exception as e:
|
|
5532
6519
|
errors += 1
|
|
5533
6520
|
if errors > maxRetries:
|
|
5534
6521
|
raise e
|
|
5535
|
-
uniqueResults =
|
|
6522
|
+
uniqueResults = result
|
|
6523
|
+
if removeRepeatedOption:
|
|
6524
|
+
uniqueResults = self.remove_repeated_elements_from_array(result)
|
|
5536
6525
|
key = 0 if (method == 'fetchOHLCV') else 'timestamp'
|
|
5537
6526
|
return self.filter_by_since_limit(uniqueResults, since, limit, key)
|
|
5538
6527
|
|
|
@@ -5597,6 +6586,8 @@ class Exchange(object):
|
|
|
5597
6586
|
i = 0
|
|
5598
6587
|
errors = 0
|
|
5599
6588
|
result = []
|
|
6589
|
+
timeframe = self.safe_string(params, 'timeframe')
|
|
6590
|
+
params = self.omit(params, 'timeframe') # reading the timeframe from the method arguments to avoid changing the signature
|
|
5600
6591
|
while(i < maxCalls):
|
|
5601
6592
|
try:
|
|
5602
6593
|
if cursorValue is not None:
|
|
@@ -5606,8 +6597,10 @@ class Exchange(object):
|
|
|
5606
6597
|
response = None
|
|
5607
6598
|
if method == 'fetchAccounts':
|
|
5608
6599
|
response = getattr(self, method)(params)
|
|
5609
|
-
elif method == 'getLeverageTiersPaginated':
|
|
6600
|
+
elif method == 'getLeverageTiersPaginated' or method == 'fetchPositions':
|
|
5610
6601
|
response = getattr(self, method)(symbol, params)
|
|
6602
|
+
elif method == 'fetchOpenInterestHistory':
|
|
6603
|
+
response = getattr(self, method)(symbol, timeframe, since, maxEntriesPerRequest, params)
|
|
5611
6604
|
else:
|
|
5612
6605
|
response = getattr(self, method)(symbol, since, maxEntriesPerRequest, params)
|
|
5613
6606
|
errors = 0
|
|
@@ -5620,8 +6613,17 @@ class Exchange(object):
|
|
|
5620
6613
|
if responseLength == 0:
|
|
5621
6614
|
break
|
|
5622
6615
|
result = self.array_concat(result, response)
|
|
5623
|
-
last = self.
|
|
5624
|
-
cursorValue = self.safe_value(last['info'], cursorReceived)
|
|
6616
|
+
last = self.safe_dict(response, responseLength - 1)
|
|
6617
|
+
# cursorValue = self.safe_value(last['info'], cursorReceived)
|
|
6618
|
+
cursorValue = None # search for the cursor
|
|
6619
|
+
for j in range(0, responseLength):
|
|
6620
|
+
index = responseLength - j - 1
|
|
6621
|
+
entry = self.safe_dict(response, index)
|
|
6622
|
+
info = self.safe_dict(entry, 'info')
|
|
6623
|
+
cursor = self.safe_value(info, cursorReceived)
|
|
6624
|
+
if cursor is not None:
|
|
6625
|
+
cursorValue = cursor
|
|
6626
|
+
break
|
|
5625
6627
|
if cursorValue is None:
|
|
5626
6628
|
break
|
|
5627
6629
|
lastTimestamp = self.safe_integer(last, 'timestamp')
|
|
@@ -5676,24 +6678,36 @@ class Exchange(object):
|
|
|
5676
6678
|
return self.sort_by(result, 'id', True)
|
|
5677
6679
|
return result
|
|
5678
6680
|
|
|
5679
|
-
def remove_repeated_elements_from_array(self, input):
|
|
6681
|
+
def remove_repeated_elements_from_array(self, input, fallbackToTimestamp: bool = True):
|
|
6682
|
+
uniqueDic = {}
|
|
6683
|
+
uniqueResult = []
|
|
6684
|
+
for i in range(0, len(input)):
|
|
6685
|
+
entry = input[i]
|
|
6686
|
+
uniqValue = self.safe_string_n(entry, ['id', 'timestamp', 0]) if fallbackToTimestamp else self.safe_string(entry, 'id')
|
|
6687
|
+
if uniqValue is not None and not (uniqValue in uniqueDic):
|
|
6688
|
+
uniqueDic[uniqValue] = 1
|
|
6689
|
+
uniqueResult.append(entry)
|
|
6690
|
+
valuesLength = len(uniqueResult)
|
|
6691
|
+
if valuesLength > 0:
|
|
6692
|
+
return uniqueResult
|
|
6693
|
+
return input
|
|
6694
|
+
|
|
6695
|
+
def remove_repeated_trades_from_array(self, input):
|
|
5680
6696
|
uniqueResult = {}
|
|
5681
6697
|
for i in range(0, len(input)):
|
|
5682
6698
|
entry = input[i]
|
|
5683
6699
|
id = self.safe_string(entry, 'id')
|
|
5684
|
-
if id is
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
6700
|
+
if id is None:
|
|
6701
|
+
price = self.safe_string(entry, 'price')
|
|
6702
|
+
amount = self.safe_string(entry, 'amount')
|
|
6703
|
+
timestamp = self.safe_string(entry, 'timestamp')
|
|
6704
|
+
side = self.safe_string(entry, 'side')
|
|
6705
|
+
# unique trade identifier
|
|
6706
|
+
id = 't_' + str(timestamp) + '_' + side + '_' + price + '_' + amount
|
|
6707
|
+
if id is not None and not (id in uniqueResult):
|
|
6708
|
+
uniqueResult[id] = entry
|
|
5692
6709
|
values = list(uniqueResult.values())
|
|
5693
|
-
|
|
5694
|
-
if valuesLength > 0:
|
|
5695
|
-
return values
|
|
5696
|
-
return input
|
|
6710
|
+
return values
|
|
5697
6711
|
|
|
5698
6712
|
def handle_until_option(self, key: str, request, params, multiplier=1):
|
|
5699
6713
|
until = self.safe_integer_2(params, 'until', 'till')
|
|
@@ -5702,9 +6716,12 @@ class Exchange(object):
|
|
|
5702
6716
|
params = self.omit(params, ['until', 'till'])
|
|
5703
6717
|
return [request, params]
|
|
5704
6718
|
|
|
5705
|
-
def safe_open_interest(self, interest, market: Market = None):
|
|
6719
|
+
def safe_open_interest(self, interest: dict, market: Market = None):
|
|
6720
|
+
symbol = self.safe_string(interest, 'symbol')
|
|
6721
|
+
if symbol is None:
|
|
6722
|
+
symbol = self.safe_string(market, 'symbol')
|
|
5706
6723
|
return self.extend(interest, {
|
|
5707
|
-
'symbol':
|
|
6724
|
+
'symbol': symbol,
|
|
5708
6725
|
'baseVolume': self.safe_number(interest, 'baseVolume'), # deprecated
|
|
5709
6726
|
'quoteVolume': self.safe_number(interest, 'quoteVolume'), # deprecated
|
|
5710
6727
|
'openInterestAmount': self.safe_number(interest, 'openInterestAmount'),
|
|
@@ -5719,7 +6736,7 @@ class Exchange(object):
|
|
|
5719
6736
|
|
|
5720
6737
|
def parse_liquidations(self, liquidations: List[dict], market: Market = None, since: Int = None, limit: Int = None):
|
|
5721
6738
|
"""
|
|
5722
|
-
|
|
6739
|
+
@ignore
|
|
5723
6740
|
parses liquidation info from the exchange response
|
|
5724
6741
|
:param dict[] liquidations: each item describes an instance of a liquidation event
|
|
5725
6742
|
:param dict market: ccxt market
|
|
@@ -5739,6 +6756,27 @@ class Exchange(object):
|
|
|
5739
6756
|
def parse_greeks(self, greeks: dict, market: Market = None):
|
|
5740
6757
|
raise NotSupported(self.id + ' parseGreeks() is not supported yet')
|
|
5741
6758
|
|
|
6759
|
+
def parse_all_greeks(self, greeks, symbols: Strings = None, params={}):
|
|
6760
|
+
#
|
|
6761
|
+
# the value of greeks is either a dict or a list
|
|
6762
|
+
#
|
|
6763
|
+
results = []
|
|
6764
|
+
if isinstance(greeks, list):
|
|
6765
|
+
for i in range(0, len(greeks)):
|
|
6766
|
+
parsedTicker = self.parse_greeks(greeks[i])
|
|
6767
|
+
greek = self.extend(parsedTicker, params)
|
|
6768
|
+
results.append(greek)
|
|
6769
|
+
else:
|
|
6770
|
+
marketIds = list(greeks.keys())
|
|
6771
|
+
for i in range(0, len(marketIds)):
|
|
6772
|
+
marketId = marketIds[i]
|
|
6773
|
+
market = self.safe_market(marketId)
|
|
6774
|
+
parsed = self.parse_greeks(greeks[marketId], market)
|
|
6775
|
+
greek = self.extend(parsed, params)
|
|
6776
|
+
results.append(greek)
|
|
6777
|
+
symbols = self.market_symbols(symbols)
|
|
6778
|
+
return self.filter_by_array(results, 'symbol', symbols)
|
|
6779
|
+
|
|
5742
6780
|
def parse_option(self, chain: dict, currency: Currency = None, market: Market = None):
|
|
5743
6781
|
raise NotSupported(self.id + ' parseOption() is not supported yet')
|
|
5744
6782
|
|
|
@@ -5855,7 +6893,7 @@ class Exchange(object):
|
|
|
5855
6893
|
return reconstructedDate
|
|
5856
6894
|
|
|
5857
6895
|
def convert_market_id_expire_date(self, date: str):
|
|
5858
|
-
# parse 03JAN24 to 240103
|
|
6896
|
+
# parse 03JAN24 to 240103.
|
|
5859
6897
|
monthMappping = {
|
|
5860
6898
|
'JAN': '01',
|
|
5861
6899
|
'FEB': '02',
|
|
@@ -5891,7 +6929,7 @@ class Exchange(object):
|
|
|
5891
6929
|
"""
|
|
5892
6930
|
if self.has['fetchPositionsHistory']:
|
|
5893
6931
|
positions = self.fetch_positions_history([symbol], since, limit, params)
|
|
5894
|
-
return
|
|
6932
|
+
return positions
|
|
5895
6933
|
else:
|
|
5896
6934
|
raise NotSupported(self.id + ' fetchPositionHistory() is not supported yet')
|
|
5897
6935
|
|
|
@@ -5909,7 +6947,7 @@ class Exchange(object):
|
|
|
5909
6947
|
def parse_margin_modification(self, data: dict, market: Market = None):
|
|
5910
6948
|
raise NotSupported(self.id + ' parseMarginModification() is not supported yet')
|
|
5911
6949
|
|
|
5912
|
-
def parse_margin_modifications(self, response: List[object], symbols:
|
|
6950
|
+
def parse_margin_modifications(self, response: List[object], symbols: Strings = None, symbolKey: Str = None, marketType: MarketType = None):
|
|
5913
6951
|
marginModifications = []
|
|
5914
6952
|
for i in range(0, len(response)):
|
|
5915
6953
|
info = response[i]
|
|
@@ -5939,3 +6977,71 @@ class Exchange(object):
|
|
|
5939
6977
|
:returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
|
|
5940
6978
|
"""
|
|
5941
6979
|
raise NotSupported(self.id + ' fetchTransfers() is not supported yet')
|
|
6980
|
+
|
|
6981
|
+
def clean_unsubscription(self, client, subHash: str, unsubHash: str, subHashIsPrefix=False):
|
|
6982
|
+
if unsubHash in client.subscriptions:
|
|
6983
|
+
del client.subscriptions[unsubHash]
|
|
6984
|
+
if not subHashIsPrefix:
|
|
6985
|
+
if subHash in client.subscriptions:
|
|
6986
|
+
del client.subscriptions[subHash]
|
|
6987
|
+
if subHash in client.futures:
|
|
6988
|
+
error = UnsubscribeError(self.id + ' ' + subHash)
|
|
6989
|
+
client.reject(error, subHash)
|
|
6990
|
+
else:
|
|
6991
|
+
clientSubscriptions = list(client.subscriptions.keys())
|
|
6992
|
+
for i in range(0, len(clientSubscriptions)):
|
|
6993
|
+
sub = clientSubscriptions[i]
|
|
6994
|
+
if sub.startswith(subHash):
|
|
6995
|
+
del client.subscriptions[sub]
|
|
6996
|
+
clientFutures = list(client.futures.keys())
|
|
6997
|
+
for i in range(0, len(clientFutures)):
|
|
6998
|
+
future = clientFutures[i]
|
|
6999
|
+
if future.startswith(subHash):
|
|
7000
|
+
error = UnsubscribeError(self.id + ' ' + future)
|
|
7001
|
+
client.reject(error, future)
|
|
7002
|
+
client.resolve(True, unsubHash)
|
|
7003
|
+
|
|
7004
|
+
def clean_cache(self, subscription: dict):
|
|
7005
|
+
topic = self.safe_string(subscription, 'topic')
|
|
7006
|
+
symbols = self.safe_list(subscription, 'symbols', [])
|
|
7007
|
+
symbolsLength = len(symbols)
|
|
7008
|
+
if topic == 'ohlcv':
|
|
7009
|
+
symbolsAndTimeFrames = self.safe_list(subscription, 'symbolsAndTimeframes', [])
|
|
7010
|
+
for i in range(0, len(symbolsAndTimeFrames)):
|
|
7011
|
+
symbolAndTimeFrame = symbolsAndTimeFrames[i]
|
|
7012
|
+
symbol = self.safe_string(symbolAndTimeFrame, 0)
|
|
7013
|
+
timeframe = self.safe_string(symbolAndTimeFrame, 1)
|
|
7014
|
+
if (self.ohlcvs is not None) and (symbol in self.ohlcvs):
|
|
7015
|
+
if timeframe in self.ohlcvs[symbol]:
|
|
7016
|
+
del self.ohlcvs[symbol][timeframe]
|
|
7017
|
+
elif symbolsLength > 0:
|
|
7018
|
+
for i in range(0, len(symbols)):
|
|
7019
|
+
symbol = symbols[i]
|
|
7020
|
+
if topic == 'trades':
|
|
7021
|
+
if symbol in self.trades:
|
|
7022
|
+
del self.trades[symbol]
|
|
7023
|
+
elif topic == 'orderbook':
|
|
7024
|
+
if symbol in self.orderbooks:
|
|
7025
|
+
del self.orderbooks[symbol]
|
|
7026
|
+
elif topic == 'ticker':
|
|
7027
|
+
if symbol in self.tickers:
|
|
7028
|
+
del self.tickers[symbol]
|
|
7029
|
+
else:
|
|
7030
|
+
if topic == 'myTrades' and (self.myTrades is not None):
|
|
7031
|
+
self.myTrades = None
|
|
7032
|
+
elif topic == 'orders' and (self.orders is not None):
|
|
7033
|
+
self.orders = None
|
|
7034
|
+
elif topic == 'positions' and (self.positions is not None):
|
|
7035
|
+
self.positions = None
|
|
7036
|
+
clients = list(self.clients.values())
|
|
7037
|
+
for i in range(0, len(clients)):
|
|
7038
|
+
client = clients[i]
|
|
7039
|
+
futures = self.safe_dict(client, 'futures')
|
|
7040
|
+
if (futures is not None) and ('fetchPositionsSnapshot' in futures):
|
|
7041
|
+
del futures['fetchPositionsSnapshot']
|
|
7042
|
+
elif topic == 'ticker' and (self.tickers is not None):
|
|
7043
|
+
tickerSymbols = list(self.tickers.keys())
|
|
7044
|
+
for i in range(0, len(tickerSymbols)):
|
|
7045
|
+
tickerSymbol = tickerSymbols[i]
|
|
7046
|
+
if tickerSymbol in self.tickers:
|
|
7047
|
+
del self.tickers[tickerSymbol]
|