ccxt 4.2.77__py2.py3-none-any.whl → 4.4.48__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 +36 -14
- ccxt/abstract/alpaca.py +4 -0
- ccxt/abstract/bigone.py +1 -1
- ccxt/abstract/binance.py +112 -48
- ccxt/abstract/binancecoinm.py +112 -48
- ccxt/abstract/binanceus.py +147 -83
- ccxt/abstract/binanceusdm.py +112 -48
- ccxt/abstract/bingx.py +133 -78
- ccxt/abstract/bitbank.py +5 -0
- ccxt/abstract/bitfinex.py +136 -65
- ccxt/abstract/bitfinex1.py +69 -0
- ccxt/abstract/bitflyer.py +1 -0
- ccxt/abstract/bitget.py +8 -1
- ccxt/abstract/bitmart.py +13 -1
- ccxt/abstract/bitopro.py +1 -0
- ccxt/abstract/bitpanda.py +0 -12
- ccxt/abstract/bitrue.py +3 -3
- ccxt/abstract/bitstamp.py +26 -3
- ccxt/abstract/blofin.py +24 -0
- ccxt/abstract/btcbox.py +1 -0
- ccxt/abstract/bybit.py +29 -14
- ccxt/abstract/cex.py +28 -29
- ccxt/abstract/coinbase.py +6 -0
- ccxt/abstract/coinbaseadvanced.py +94 -0
- ccxt/abstract/{coinbasepro.py → coinbaseexchange.py} +1 -0
- ccxt/abstract/coinbaseinternational.py +1 -1
- ccxt/abstract/coincatch.py +94 -0
- ccxt/abstract/coinex.py +233 -123
- ccxt/abstract/coinmetro.py +1 -0
- ccxt/abstract/cryptocom.py +14 -0
- ccxt/abstract/defx.py +69 -0
- ccxt/abstract/deribit.py +1 -0
- ccxt/abstract/digifinex.py +1 -0
- ccxt/abstract/ellipx.py +25 -0
- ccxt/abstract/gate.py +20 -0
- ccxt/abstract/gateio.py +20 -0
- ccxt/abstract/gemini.py +1 -0
- ccxt/abstract/hashkey.py +67 -0
- ccxt/abstract/hyperliquid.py +1 -1
- ccxt/abstract/independentreserve.py +6 -0
- ccxt/abstract/kraken.py +4 -3
- ccxt/abstract/krakenfutures.py +4 -0
- ccxt/abstract/kucoin.py +24 -0
- ccxt/abstract/kucoinfutures.py +34 -0
- ccxt/abstract/luno.py +2 -0
- ccxt/abstract/mexc.py +4 -0
- ccxt/abstract/myokx.py +340 -0
- ccxt/abstract/oceanex.py +5 -0
- ccxt/abstract/okx.py +30 -0
- ccxt/abstract/onetrading.py +0 -12
- ccxt/abstract/oxfun.py +34 -0
- ccxt/abstract/paradex.py +40 -0
- ccxt/abstract/phemex.py +1 -0
- ccxt/abstract/upbit.py +4 -0
- ccxt/abstract/vertex.py +19 -0
- ccxt/abstract/whitebit.py +31 -1
- ccxt/abstract/woo.py +6 -2
- ccxt/abstract/woofipro.py +119 -0
- ccxt/abstract/xt.py +153 -0
- ccxt/abstract/zonda.py +6 -0
- ccxt/ace.py +164 -60
- ccxt/alpaca.py +727 -63
- ccxt/ascendex.py +395 -249
- ccxt/async_support/__init__.py +36 -14
- ccxt/async_support/ace.py +164 -60
- ccxt/async_support/alpaca.py +727 -63
- ccxt/async_support/ascendex.py +396 -249
- ccxt/async_support/base/exchange.py +531 -155
- ccxt/async_support/base/ws/aiohttp_client.py +28 -5
- ccxt/async_support/base/ws/cache.py +3 -2
- ccxt/async_support/base/ws/client.py +26 -5
- ccxt/async_support/base/ws/fast_client.py +4 -3
- ccxt/async_support/base/ws/functions.py +1 -1
- ccxt/async_support/base/ws/future.py +40 -31
- ccxt/async_support/base/ws/order_book_side.py +3 -0
- ccxt/async_support/bequant.py +1 -1
- ccxt/async_support/bigone.py +329 -202
- ccxt/async_support/binance.py +3030 -1087
- ccxt/async_support/binancecoinm.py +2 -1
- ccxt/async_support/binanceus.py +12 -1
- ccxt/async_support/binanceusdm.py +3 -1
- ccxt/async_support/bingx.py +3104 -880
- ccxt/async_support/bit2c.py +119 -38
- ccxt/async_support/bitbank.py +215 -76
- ccxt/async_support/bitbns.py +124 -53
- ccxt/async_support/bitfinex.py +3236 -1078
- ccxt/async_support/bitfinex1.py +1711 -0
- ccxt/async_support/bitflyer.py +238 -49
- ccxt/async_support/bitget.py +1513 -563
- ccxt/async_support/bithumb.py +199 -65
- ccxt/async_support/bitmart.py +1320 -435
- ccxt/async_support/bitmex.py +308 -111
- ccxt/async_support/bitopro.py +256 -96
- ccxt/async_support/bitrue.py +365 -233
- ccxt/async_support/bitso.py +201 -89
- ccxt/async_support/bitstamp.py +438 -269
- ccxt/async_support/bitteam.py +179 -73
- ccxt/async_support/bitvavo.py +180 -70
- ccxt/async_support/bl3p.py +92 -25
- ccxt/async_support/blockchaincom.py +193 -79
- ccxt/async_support/blofin.py +392 -148
- ccxt/async_support/btcalpha.py +161 -55
- ccxt/async_support/btcbox.py +250 -34
- ccxt/async_support/btcmarkets.py +232 -85
- ccxt/async_support/btcturk.py +159 -60
- ccxt/async_support/bybit.py +2231 -1193
- ccxt/async_support/cex.py +1409 -1329
- ccxt/async_support/coinbase.py +1454 -287
- ccxt/async_support/coinbaseadvanced.py +17 -0
- ccxt/async_support/{coinbasepro.py → coinbaseexchange.py} +233 -99
- ccxt/async_support/coinbaseinternational.py +428 -88
- ccxt/async_support/coincatch.py +5152 -0
- ccxt/async_support/coincheck.py +121 -38
- ccxt/async_support/coinex.py +4020 -3339
- ccxt/async_support/coinlist.py +273 -116
- ccxt/async_support/coinmate.py +204 -97
- ccxt/async_support/coinmetro.py +203 -110
- ccxt/async_support/coinone.py +142 -68
- ccxt/async_support/coinsph.py +206 -89
- ccxt/async_support/coinspot.py +137 -62
- ccxt/async_support/cryptocom.py +515 -185
- ccxt/async_support/currencycom.py +203 -85
- ccxt/async_support/defx.py +2066 -0
- ccxt/async_support/delta.py +404 -109
- ccxt/async_support/deribit.py +557 -323
- ccxt/async_support/digifinex.py +340 -223
- ccxt/async_support/ellipx.py +1826 -0
- ccxt/async_support/exmo.py +259 -128
- ccxt/async_support/gate.py +1472 -463
- ccxt/async_support/gemini.py +206 -84
- ccxt/async_support/hashkey.py +4164 -0
- ccxt/async_support/hitbtc.py +334 -178
- ccxt/async_support/hollaex.py +134 -83
- ccxt/async_support/htx.py +1095 -563
- ccxt/async_support/huobijp.py +105 -56
- ccxt/async_support/hyperliquid.py +1633 -268
- ccxt/async_support/idex.py +148 -95
- ccxt/async_support/independentreserve.py +236 -31
- ccxt/async_support/indodax.py +165 -62
- ccxt/async_support/kraken.py +871 -354
- ccxt/async_support/krakenfutures.py +324 -100
- ccxt/async_support/kucoin.py +917 -357
- ccxt/async_support/kucoinfutures.py +1004 -149
- ccxt/async_support/kuna.py +138 -106
- ccxt/async_support/latoken.py +135 -79
- ccxt/async_support/lbank.py +290 -113
- ccxt/async_support/luno.py +112 -62
- ccxt/async_support/lykke.py +104 -55
- ccxt/async_support/mercado.py +36 -29
- ccxt/async_support/mexc.py +995 -429
- ccxt/async_support/myokx.py +43 -0
- ccxt/async_support/ndax.py +163 -82
- ccxt/async_support/novadax.py +121 -75
- ccxt/async_support/oceanex.py +175 -59
- ccxt/async_support/okcoin.py +222 -163
- ccxt/async_support/okx.py +1776 -454
- ccxt/async_support/onetrading.py +132 -414
- ccxt/async_support/oxfun.py +2832 -0
- ccxt/async_support/p2b.py +79 -51
- ccxt/async_support/paradex.py +2017 -0
- ccxt/async_support/paymium.py +56 -32
- ccxt/async_support/phemex.py +572 -196
- ccxt/async_support/poloniex.py +218 -95
- ccxt/async_support/poloniexfutures.py +260 -92
- ccxt/async_support/probit.py +143 -110
- ccxt/async_support/timex.py +123 -70
- ccxt/async_support/tokocrypto.py +129 -93
- ccxt/async_support/tradeogre.py +39 -25
- ccxt/async_support/upbit.py +322 -113
- ccxt/async_support/vertex.py +2983 -0
- ccxt/async_support/wavesexchange.py +227 -173
- ccxt/async_support/wazirx.py +145 -65
- ccxt/async_support/whitebit.py +533 -138
- ccxt/async_support/woo.py +1137 -296
- ccxt/async_support/woofipro.py +2716 -0
- ccxt/async_support/xt.py +4628 -0
- ccxt/async_support/yobit.py +160 -92
- ccxt/async_support/zaif.py +80 -33
- ccxt/async_support/zonda.py +140 -69
- ccxt/base/errors.py +51 -20
- ccxt/base/exchange.py +1722 -480
- ccxt/base/precise.py +10 -0
- ccxt/base/types.py +223 -4
- ccxt/bequant.py +1 -1
- ccxt/bigone.py +329 -202
- ccxt/binance.py +3030 -1087
- ccxt/binancecoinm.py +2 -1
- ccxt/binanceus.py +12 -1
- ccxt/binanceusdm.py +3 -1
- ccxt/bingx.py +3104 -880
- ccxt/bit2c.py +119 -38
- ccxt/bitbank.py +215 -76
- ccxt/bitbns.py +124 -53
- ccxt/bitfinex.py +3235 -1078
- ccxt/bitfinex1.py +1710 -0
- ccxt/bitflyer.py +238 -49
- ccxt/bitget.py +1513 -563
- ccxt/bithumb.py +198 -65
- ccxt/bitmart.py +1320 -435
- ccxt/bitmex.py +308 -111
- ccxt/bitopro.py +256 -96
- ccxt/bitrue.py +365 -233
- ccxt/bitso.py +201 -89
- ccxt/bitstamp.py +438 -269
- ccxt/bitteam.py +179 -73
- ccxt/bitvavo.py +180 -70
- ccxt/bl3p.py +92 -25
- ccxt/blockchaincom.py +193 -79
- ccxt/blofin.py +392 -148
- ccxt/btcalpha.py +161 -55
- ccxt/btcbox.py +250 -34
- ccxt/btcmarkets.py +232 -85
- ccxt/btcturk.py +159 -60
- ccxt/bybit.py +2231 -1193
- ccxt/cex.py +1408 -1329
- ccxt/coinbase.py +1454 -287
- ccxt/coinbaseadvanced.py +17 -0
- ccxt/{coinbasepro.py → coinbaseexchange.py} +233 -99
- ccxt/coinbaseinternational.py +428 -88
- ccxt/coincatch.py +5152 -0
- ccxt/coincheck.py +121 -38
- ccxt/coinex.py +4020 -3339
- ccxt/coinlist.py +273 -116
- ccxt/coinmate.py +204 -97
- ccxt/coinmetro.py +203 -110
- ccxt/coinone.py +142 -68
- ccxt/coinsph.py +206 -89
- ccxt/coinspot.py +137 -62
- ccxt/cryptocom.py +515 -185
- ccxt/currencycom.py +203 -85
- ccxt/defx.py +2065 -0
- ccxt/delta.py +404 -109
- ccxt/deribit.py +557 -323
- ccxt/digifinex.py +340 -223
- ccxt/ellipx.py +1826 -0
- ccxt/exmo.py +259 -128
- ccxt/gate.py +1472 -463
- ccxt/gemini.py +206 -84
- ccxt/hashkey.py +4164 -0
- ccxt/hitbtc.py +334 -178
- ccxt/hollaex.py +134 -83
- ccxt/htx.py +1095 -563
- ccxt/huobijp.py +105 -56
- ccxt/hyperliquid.py +1632 -268
- ccxt/idex.py +148 -95
- ccxt/independentreserve.py +235 -31
- ccxt/indodax.py +165 -62
- ccxt/kraken.py +871 -354
- ccxt/krakenfutures.py +324 -100
- ccxt/kucoin.py +917 -357
- ccxt/kucoinfutures.py +1004 -149
- ccxt/kuna.py +138 -106
- ccxt/latoken.py +135 -79
- ccxt/lbank.py +290 -113
- ccxt/luno.py +112 -62
- ccxt/lykke.py +104 -55
- ccxt/mercado.py +36 -29
- ccxt/mexc.py +994 -429
- ccxt/myokx.py +43 -0
- ccxt/ndax.py +163 -82
- ccxt/novadax.py +121 -75
- ccxt/oceanex.py +175 -59
- ccxt/okcoin.py +222 -163
- ccxt/okx.py +1776 -454
- ccxt/onetrading.py +132 -414
- ccxt/oxfun.py +2831 -0
- ccxt/p2b.py +79 -51
- ccxt/paradex.py +2017 -0
- ccxt/paymium.py +56 -32
- ccxt/phemex.py +572 -196
- ccxt/poloniex.py +218 -95
- ccxt/poloniexfutures.py +260 -92
- ccxt/pro/__init__.py +29 -5
- ccxt/pro/alpaca.py +32 -17
- ccxt/pro/ascendex.py +62 -14
- ccxt/pro/bequant.py +4 -0
- ccxt/pro/binance.py +1596 -329
- ccxt/pro/binancecoinm.py +1 -0
- ccxt/pro/binanceus.py +2 -9
- ccxt/pro/binanceusdm.py +2 -0
- ccxt/pro/bingx.py +527 -134
- ccxt/pro/bitcoincom.py +4 -1
- ccxt/pro/bitfinex.py +731 -266
- ccxt/pro/bitfinex1.py +635 -0
- ccxt/pro/bitget.py +726 -357
- ccxt/pro/bithumb.py +380 -0
- ccxt/pro/bitmart.py +138 -39
- ccxt/pro/bitmex.py +199 -40
- ccxt/pro/bitopro.py +25 -13
- ccxt/pro/bitrue.py +31 -32
- ccxt/pro/bitstamp.py +7 -6
- ccxt/pro/bitvavo.py +203 -81
- ccxt/pro/blockchaincom.py +30 -17
- ccxt/pro/blofin.py +692 -0
- ccxt/pro/bybit.py +791 -82
- ccxt/pro/cex.py +99 -51
- ccxt/pro/coinbase.py +220 -30
- ccxt/{async_support/hitbtc3.py → pro/coinbaseadvanced.py} +5 -5
- ccxt/pro/{coinbasepro.py → coinbaseexchange.py} +19 -19
- ccxt/pro/coinbaseinternational.py +193 -30
- ccxt/pro/coincatch.py +1464 -0
- ccxt/pro/coincheck.py +11 -6
- ccxt/pro/coinex.py +965 -665
- ccxt/pro/coinone.py +17 -10
- ccxt/pro/cryptocom.py +446 -66
- ccxt/pro/currencycom.py +11 -10
- ccxt/pro/defx.py +832 -0
- ccxt/pro/deribit.py +167 -31
- ccxt/pro/exmo.py +252 -20
- ccxt/pro/gate.py +729 -64
- ccxt/pro/gemini.py +44 -26
- ccxt/pro/hashkey.py +802 -0
- ccxt/pro/hitbtc.py +208 -103
- ccxt/pro/hollaex.py +25 -9
- ccxt/pro/htx.py +83 -39
- ccxt/pro/huobijp.py +17 -16
- ccxt/pro/hyperliquid.py +502 -31
- ccxt/pro/idex.py +28 -13
- ccxt/pro/independentreserve.py +21 -16
- ccxt/pro/kraken.py +298 -51
- ccxt/pro/krakenfutures.py +166 -75
- ccxt/pro/kucoin.py +395 -77
- ccxt/pro/kucoinfutures.py +400 -99
- ccxt/pro/lbank.py +52 -31
- ccxt/pro/luno.py +12 -10
- ccxt/pro/mexc.py +400 -50
- ccxt/pro/myokx.py +28 -0
- ccxt/pro/ndax.py +25 -12
- ccxt/pro/okcoin.py +28 -9
- ccxt/pro/okx.py +935 -124
- ccxt/pro/onetrading.py +41 -24
- ccxt/pro/oxfun.py +1054 -0
- ccxt/pro/p2b.py +100 -24
- ccxt/pro/paradex.py +352 -0
- ccxt/pro/phemex.py +92 -33
- ccxt/pro/poloniex.py +128 -49
- ccxt/pro/poloniexfutures.py +53 -32
- ccxt/pro/probit.py +92 -85
- ccxt/pro/upbit.py +401 -8
- ccxt/pro/vertex.py +943 -0
- ccxt/pro/wazirx.py +46 -28
- ccxt/pro/whitebit.py +65 -12
- ccxt/pro/woo.py +437 -65
- ccxt/pro/woofipro.py +1271 -0
- ccxt/pro/xt.py +1067 -0
- ccxt/probit.py +143 -110
- ccxt/static_dependencies/__init__.py +1 -1
- ccxt/static_dependencies/lark/__init__.py +38 -0
- ccxt/static_dependencies/lark/__pyinstaller/__init__.py +6 -0
- ccxt/static_dependencies/lark/__pyinstaller/hook-lark.py +14 -0
- ccxt/static_dependencies/lark/ast_utils.py +59 -0
- ccxt/static_dependencies/lark/common.py +86 -0
- ccxt/static_dependencies/lark/exceptions.py +292 -0
- ccxt/static_dependencies/lark/grammar.py +130 -0
- ccxt/static_dependencies/lark/grammars/__init__.py +0 -0
- ccxt/static_dependencies/lark/indenter.py +143 -0
- ccxt/static_dependencies/lark/lark.py +658 -0
- ccxt/static_dependencies/lark/lexer.py +678 -0
- ccxt/static_dependencies/lark/load_grammar.py +1428 -0
- ccxt/static_dependencies/lark/parse_tree_builder.py +391 -0
- ccxt/static_dependencies/lark/parser_frontends.py +257 -0
- ccxt/static_dependencies/lark/parsers/__init__.py +0 -0
- ccxt/static_dependencies/lark/parsers/cyk.py +340 -0
- ccxt/static_dependencies/lark/parsers/earley.py +314 -0
- ccxt/static_dependencies/lark/parsers/earley_common.py +42 -0
- ccxt/static_dependencies/lark/parsers/earley_forest.py +801 -0
- ccxt/static_dependencies/lark/parsers/grammar_analysis.py +203 -0
- ccxt/static_dependencies/lark/parsers/lalr_analysis.py +332 -0
- ccxt/static_dependencies/lark/parsers/lalr_interactive_parser.py +158 -0
- ccxt/static_dependencies/lark/parsers/lalr_parser.py +122 -0
- ccxt/static_dependencies/lark/parsers/lalr_parser_state.py +110 -0
- ccxt/static_dependencies/lark/parsers/xearley.py +165 -0
- ccxt/static_dependencies/lark/py.typed +0 -0
- ccxt/static_dependencies/lark/reconstruct.py +107 -0
- ccxt/static_dependencies/lark/tools/__init__.py +70 -0
- ccxt/static_dependencies/lark/tools/nearley.py +202 -0
- ccxt/static_dependencies/lark/tools/serialize.py +32 -0
- ccxt/static_dependencies/lark/tools/standalone.py +196 -0
- ccxt/static_dependencies/lark/tree.py +267 -0
- ccxt/static_dependencies/lark/tree_matcher.py +186 -0
- ccxt/static_dependencies/lark/tree_templates.py +180 -0
- ccxt/static_dependencies/lark/utils.py +343 -0
- ccxt/static_dependencies/lark/visitors.py +596 -0
- ccxt/static_dependencies/marshmallow/__init__.py +81 -0
- ccxt/static_dependencies/marshmallow/base.py +65 -0
- ccxt/static_dependencies/marshmallow/class_registry.py +94 -0
- ccxt/static_dependencies/marshmallow/decorators.py +231 -0
- ccxt/static_dependencies/marshmallow/error_store.py +60 -0
- ccxt/static_dependencies/marshmallow/exceptions.py +71 -0
- ccxt/static_dependencies/marshmallow/fields.py +2114 -0
- ccxt/static_dependencies/marshmallow/orderedset.py +89 -0
- ccxt/static_dependencies/marshmallow/py.typed +0 -0
- ccxt/static_dependencies/marshmallow/schema.py +1228 -0
- ccxt/static_dependencies/marshmallow/types.py +12 -0
- ccxt/static_dependencies/marshmallow/utils.py +378 -0
- ccxt/static_dependencies/marshmallow/validate.py +678 -0
- ccxt/static_dependencies/marshmallow/warnings.py +2 -0
- ccxt/static_dependencies/marshmallow_dataclass/__init__.py +1047 -0
- ccxt/static_dependencies/marshmallow_dataclass/collection_field.py +51 -0
- ccxt/static_dependencies/marshmallow_dataclass/lazy_class_attribute.py +45 -0
- ccxt/static_dependencies/marshmallow_dataclass/mypy.py +71 -0
- ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
- ccxt/static_dependencies/marshmallow_dataclass/typing.py +14 -0
- ccxt/static_dependencies/marshmallow_dataclass/union_field.py +82 -0
- ccxt/static_dependencies/marshmallow_oneofschema/__init__.py +1 -0
- ccxt/static_dependencies/marshmallow_oneofschema/one_of_schema.py +193 -0
- ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
- ccxt/static_dependencies/starknet/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/data_types.py +123 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/cairo_types.py +77 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser.py +46 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser_transformer.py +138 -0
- ccxt/static_dependencies/starknet/cairo/felt.py +64 -0
- ccxt/static_dependencies/starknet/cairo/type_parser.py +121 -0
- ccxt/static_dependencies/starknet/cairo/v1/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/v1/type_parser.py +59 -0
- ccxt/static_dependencies/starknet/cairo/v2/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/v2/type_parser.py +77 -0
- ccxt/static_dependencies/starknet/ccxt_utils.py +7 -0
- ccxt/static_dependencies/starknet/common.py +15 -0
- ccxt/static_dependencies/starknet/constants.py +39 -0
- ccxt/static_dependencies/starknet/hash/__init__.py +0 -0
- ccxt/static_dependencies/starknet/hash/address.py +79 -0
- ccxt/static_dependencies/starknet/hash/compiled_class_hash_objects.py +111 -0
- ccxt/static_dependencies/starknet/hash/selector.py +16 -0
- ccxt/static_dependencies/starknet/hash/storage.py +12 -0
- ccxt/static_dependencies/starknet/hash/utils.py +78 -0
- ccxt/static_dependencies/starknet/models/__init__.py +0 -0
- ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
- ccxt/static_dependencies/starknet/serialization/__init__.py +24 -0
- ccxt/static_dependencies/starknet/serialization/_calldata_reader.py +40 -0
- ccxt/static_dependencies/starknet/serialization/_context.py +142 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/__init__.py +10 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/_common.py +82 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/array_serializer.py +43 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/bool_serializer.py +37 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/byte_array_serializer.py +66 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/cairo_data_serializer.py +71 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/enum_serializer.py +71 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/felt_serializer.py +50 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/named_tuple_serializer.py +58 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/option_serializer.py +43 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/output_serializer.py +40 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/payload_serializer.py +72 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/struct_serializer.py +36 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/tuple_serializer.py +36 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/uint256_serializer.py +76 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/uint_serializer.py +100 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/unit_serializer.py +32 -0
- ccxt/static_dependencies/starknet/serialization/errors.py +10 -0
- ccxt/static_dependencies/starknet/serialization/factory.py +229 -0
- ccxt/static_dependencies/starknet/serialization/function_serialization_adapter.py +110 -0
- ccxt/static_dependencies/starknet/serialization/tuple_dataclass.py +59 -0
- ccxt/static_dependencies/starknet/utils/__init__.py +0 -0
- ccxt/static_dependencies/starknet/utils/constructor_args_translator.py +86 -0
- ccxt/static_dependencies/starknet/utils/iterable.py +13 -0
- ccxt/static_dependencies/starknet/utils/schema.py +13 -0
- ccxt/static_dependencies/starknet/utils/typed_data.py +182 -0
- ccxt/static_dependencies/starkware/__init__.py +0 -0
- ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
- ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
- ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
- ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
- ccxt/static_dependencies/starkware/crypto/utils.py +63 -0
- ccxt/static_dependencies/sympy/__init__.py +0 -0
- ccxt/static_dependencies/sympy/core/__init__.py +0 -0
- ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
- ccxt/static_dependencies/sympy/external/__init__.py +0 -0
- ccxt/static_dependencies/sympy/external/gmpy.py +345 -0
- ccxt/static_dependencies/sympy/external/importtools.py +187 -0
- ccxt/static_dependencies/sympy/external/ntheory.py +637 -0
- ccxt/static_dependencies/sympy/external/pythonmpq.py +341 -0
- ccxt/static_dependencies/typing_inspect/__init__.py +0 -0
- ccxt/static_dependencies/typing_inspect/typing_inspect.py +851 -0
- ccxt/test/{test_async.py → tests_async.py} +456 -391
- ccxt/test/tests_helpers.py +285 -0
- ccxt/test/tests_init.py +39 -0
- ccxt/test/{test_sync.py → tests_sync.py} +456 -393
- ccxt/timex.py +123 -70
- ccxt/tokocrypto.py +129 -93
- ccxt/tradeogre.py +39 -25
- ccxt/upbit.py +322 -113
- ccxt/vertex.py +2983 -0
- ccxt/wavesexchange.py +227 -173
- ccxt/wazirx.py +145 -65
- ccxt/whitebit.py +533 -138
- ccxt/woo.py +1137 -296
- ccxt/woofipro.py +2716 -0
- ccxt/xt.py +4627 -0
- ccxt/yobit.py +159 -92
- ccxt/zaif.py +80 -33
- ccxt/zonda.py +140 -69
- ccxt-4.4.48.dist-info/LICENSE.txt +21 -0
- ccxt-4.4.48.dist-info/METADATA +646 -0
- ccxt-4.4.48.dist-info/RECORD +669 -0
- {ccxt-4.2.77.dist-info → ccxt-4.4.48.dist-info}/WHEEL +1 -1
- ccxt/abstract/bitbay.py +0 -47
- ccxt/abstract/bitfinex2.py +0 -139
- ccxt/abstract/hitbtc3.py +0 -115
- ccxt/async_support/bitbay.py +0 -17
- ccxt/async_support/bitfinex2.py +0 -3496
- ccxt/async_support/flowbtc.py +0 -34
- ccxt/bitbay.py +0 -17
- ccxt/bitfinex2.py +0 -3496
- ccxt/flowbtc.py +0 -34
- ccxt/hitbtc3.py +0 -16
- ccxt/pro/bitfinex2.py +0 -1081
- ccxt/test/base/__init__.py +0 -28
- 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 -103
- 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 -32
- 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_margin_mode.py +0 -24
- ccxt/test/base/test_margin_modification.py +0 -35
- ccxt/test/base/test_market.py +0 -190
- ccxt/test/base/test_number.py +0 -411
- ccxt/test/base/test_ohlcv.py +0 -32
- 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 -63
- ccxt/test/base/test_position.py +0 -60
- ccxt/test/base/test_shared_methods.py +0 -345
- ccxt/test/base/test_status.py +0 -24
- ccxt/test/base/test_throttle.py +0 -126
- ccxt/test/base/test_ticker.py +0 -86
- 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-4.2.77.dist-info/METADATA +0 -626
- ccxt-4.2.77.dist-info/RECORD +0 -534
- {ccxt-4.2.77.dist-info → ccxt-4.4.48.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.4.48'
|
8
8
|
|
9
9
|
# -----------------------------------------------------------------------------
|
10
10
|
|
@@ -23,7 +23,8 @@ from ccxt.base.errors import NullResponse
|
|
23
23
|
from ccxt.base.errors import RateLimitExceeded
|
24
24
|
from ccxt.base.errors import BadRequest
|
25
25
|
from ccxt.base.errors import BadResponse
|
26
|
-
from ccxt.base.errors import
|
26
|
+
from ccxt.base.errors import InvalidProxySettings
|
27
|
+
from ccxt.base.errors import UnsubscribeError
|
27
28
|
|
28
29
|
# -----------------------------------------------------------------------------
|
29
30
|
|
@@ -31,14 +32,15 @@ from ccxt.base.decimal_to_precision import decimal_to_precision
|
|
31
32
|
from ccxt.base.decimal_to_precision import DECIMAL_PLACES, TICK_SIZE, NO_PADDING, TRUNCATE, ROUND, ROUND_UP, ROUND_DOWN, SIGNIFICANT_DIGITS
|
32
33
|
from ccxt.base.decimal_to_precision import number_to_string
|
33
34
|
from ccxt.base.precise import Precise
|
34
|
-
from ccxt.base.types import BalanceAccount, Currency, IndexType, OrderSide, OrderType, Trade, OrderRequest, Market, MarketType, Str, Num
|
35
|
+
from ccxt.base.types import BalanceAccount, Currency, IndexType, OrderSide, OrderType, Trade, OrderRequest, Market, MarketType, Str, Num, Strings, CancellationRequest, Bool
|
35
36
|
|
36
37
|
# -----------------------------------------------------------------------------
|
37
38
|
|
38
39
|
# rsa jwt signing
|
39
40
|
from cryptography.hazmat import backends
|
40
41
|
from cryptography.hazmat.primitives import hashes
|
41
|
-
from cryptography.hazmat.primitives.asymmetric import padding
|
42
|
+
from cryptography.hazmat.primitives.asymmetric import padding, ed25519
|
43
|
+
# from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
|
42
44
|
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
43
45
|
|
44
46
|
# -----------------------------------------------------------------------------
|
@@ -58,6 +60,12 @@ from ccxt.static_dependencies.ethereum import abi
|
|
58
60
|
from ccxt.static_dependencies.ethereum import account
|
59
61
|
from ccxt.static_dependencies.msgpack import packb
|
60
62
|
|
63
|
+
# starknet
|
64
|
+
from ccxt.static_dependencies.starknet.ccxt_utils import get_private_key_from_eth_signature
|
65
|
+
from ccxt.static_dependencies.starknet.hash.address import compute_address
|
66
|
+
from ccxt.static_dependencies.starknet.hash.selector import get_selector_from_name
|
67
|
+
from ccxt.static_dependencies.starknet.hash.utils import message_signature, private_to_stark_key
|
68
|
+
from ccxt.static_dependencies.starknet.utils.typed_data import TypedData as TypedDataDataclass
|
61
69
|
|
62
70
|
# -----------------------------------------------------------------------------
|
63
71
|
|
@@ -80,6 +88,14 @@ import gzip
|
|
80
88
|
import hashlib
|
81
89
|
import hmac
|
82
90
|
import io
|
91
|
+
|
92
|
+
# load orjson if available, otherwise default to json
|
93
|
+
orjson = None
|
94
|
+
try:
|
95
|
+
import orjson as orjson
|
96
|
+
except ImportError:
|
97
|
+
pass
|
98
|
+
|
83
99
|
import json
|
84
100
|
import math
|
85
101
|
import random
|
@@ -103,11 +119,20 @@ from ccxt.base.types import Int
|
|
103
119
|
|
104
120
|
# -----------------------------------------------------------------------------
|
105
121
|
|
122
|
+
class SafeJSONEncoder(json.JSONEncoder):
|
123
|
+
def default(self, obj):
|
124
|
+
if isinstance(obj, Exception):
|
125
|
+
return {"name": obj.__class__.__name__}
|
126
|
+
try:
|
127
|
+
return super().default(obj)
|
128
|
+
except TypeError:
|
129
|
+
return f"TypeError: Object of type {type(obj).__name__} is not JSON serializable"
|
106
130
|
|
107
131
|
class Exchange(object):
|
108
132
|
"""Base exchange class"""
|
109
133
|
id = None
|
110
134
|
name = None
|
135
|
+
countries = None
|
111
136
|
version = None
|
112
137
|
certified = False # if certified by the CCXT dev team
|
113
138
|
pro = False # if it is integrated with CCXT Pro for WebSocket support
|
@@ -123,6 +148,8 @@ class Exchange(object):
|
|
123
148
|
aiohttp_trust_env = False
|
124
149
|
requests_trust_env = False
|
125
150
|
session = None # Session () by default
|
151
|
+
tcp_connector = None # aiohttp.TCPConnector
|
152
|
+
aiohttp_socks_connector = None
|
126
153
|
socks_proxy_sessions = None
|
127
154
|
verify = True # SSL verification
|
128
155
|
validateServerSsl = True
|
@@ -132,12 +159,18 @@ class Exchange(object):
|
|
132
159
|
markets = None
|
133
160
|
symbols = None
|
134
161
|
codes = None
|
135
|
-
timeframes =
|
162
|
+
timeframes = {}
|
163
|
+
|
136
164
|
fees = {
|
137
165
|
'trading': {
|
138
|
-
'
|
166
|
+
'tierBased': None,
|
167
|
+
'percentage': None,
|
168
|
+
'taker': None,
|
169
|
+
'maker': None,
|
139
170
|
},
|
140
171
|
'funding': {
|
172
|
+
'tierBased': None,
|
173
|
+
'percentage': None,
|
141
174
|
'withdraw': {},
|
142
175
|
'deposit': {},
|
143
176
|
},
|
@@ -198,12 +231,14 @@ class Exchange(object):
|
|
198
231
|
secret = ''
|
199
232
|
password = ''
|
200
233
|
uid = ''
|
234
|
+
accountId = None
|
201
235
|
privateKey = '' # a "0x"-prefixed hexstring private key for a wallet
|
202
236
|
walletAddress = '' # the wallet address "0x"-prefixed hexstring
|
203
237
|
token = '' # reserved for HTTP auth in some cases
|
204
238
|
twofa = None
|
205
239
|
markets_by_id = None
|
206
240
|
currencies_by_id = None
|
241
|
+
|
207
242
|
precision = None
|
208
243
|
exceptions = None
|
209
244
|
limits = {
|
@@ -224,6 +259,7 @@ class Exchange(object):
|
|
224
259
|
'max': None,
|
225
260
|
},
|
226
261
|
}
|
262
|
+
|
227
263
|
httpExceptions = {
|
228
264
|
'422': ExchangeError,
|
229
265
|
'418': DDoSProtection,
|
@@ -252,26 +288,33 @@ class Exchange(object):
|
|
252
288
|
'511': AuthenticationError,
|
253
289
|
}
|
254
290
|
balance = None
|
291
|
+
liquidations = None
|
255
292
|
orderbooks = None
|
256
293
|
orders = None
|
257
294
|
triggerOrders = None
|
295
|
+
myLiquidations = None
|
258
296
|
myTrades = None
|
259
297
|
trades = None
|
260
298
|
transactions = None
|
261
299
|
ohlcvs = None
|
262
300
|
tickers = None
|
301
|
+
fundingRates = None
|
263
302
|
bidsasks = None
|
264
303
|
base_currencies = None
|
265
304
|
quote_currencies = None
|
266
305
|
currencies = None
|
267
306
|
options = None # Python does not allow to define properties in run-time with setattr
|
307
|
+
isSandboxModeEnabled = False
|
268
308
|
accounts = None
|
269
309
|
positions = None
|
270
310
|
|
311
|
+
status = None
|
312
|
+
|
271
313
|
requiredCredentials = {
|
272
314
|
'apiKey': True,
|
273
315
|
'secret': True,
|
274
316
|
'uid': False,
|
317
|
+
'accountId': False,
|
275
318
|
'login': False,
|
276
319
|
'password': False,
|
277
320
|
'twofa': False, # 2-factor authentication (one-time password key)
|
@@ -281,107 +324,8 @@ class Exchange(object):
|
|
281
324
|
}
|
282
325
|
|
283
326
|
# API method metainfo
|
284
|
-
has = {
|
285
|
-
|
286
|
-
'privateAPI': True,
|
287
|
-
'CORS': None,
|
288
|
-
'spot': None,
|
289
|
-
'margin': None,
|
290
|
-
'swap': None,
|
291
|
-
'future': None,
|
292
|
-
'option': None,
|
293
|
-
'addMargin': None,
|
294
|
-
'cancelAllOrders': None,
|
295
|
-
'cancelOrder': True,
|
296
|
-
'cancelOrders': None,
|
297
|
-
'createDepositAddress': None,
|
298
|
-
'createLimitOrder': True,
|
299
|
-
'createMarketOrder': True,
|
300
|
-
'createOrder': True,
|
301
|
-
'createPostOnlyOrder': None,
|
302
|
-
'createReduceOnlyOrder': None,
|
303
|
-
'createStopOrder': None,
|
304
|
-
'createStopLimitOrder': None,
|
305
|
-
'createStopMarketOrder': None,
|
306
|
-
'editOrder': 'emulated',
|
307
|
-
'fetchAccounts': None,
|
308
|
-
'fetchBalance': True,
|
309
|
-
'fetchBidsAsks': None,
|
310
|
-
'fetchBorrowInterest': None,
|
311
|
-
'fetchBorrowRate': None,
|
312
|
-
'fetchBorrowRateHistory': None,
|
313
|
-
'fetchBorrowRatesPerSymbol': None,
|
314
|
-
'fetchBorrowRates': None,
|
315
|
-
'fetchCanceledOrders': None,
|
316
|
-
'fetchClosedOrder': None,
|
317
|
-
'fetchClosedOrders': None,
|
318
|
-
'fetchCurrencies': 'emulated',
|
319
|
-
'fetchDeposit': None,
|
320
|
-
'fetchDepositAddress': None,
|
321
|
-
'fetchDepositAddresses': None,
|
322
|
-
'fetchDepositAddressesByNetwork': None,
|
323
|
-
'fetchDeposits': None,
|
324
|
-
'fetchFundingFee': None,
|
325
|
-
'fetchFundingFees': None,
|
326
|
-
'fetchFundingHistory': None,
|
327
|
-
'fetchFundingRate': None,
|
328
|
-
'fetchFundingRateHistory': None,
|
329
|
-
'fetchFundingRates': None,
|
330
|
-
'fetchIndexOHLCV': None,
|
331
|
-
'fetchLastPrices': None,
|
332
|
-
'fetchL2OrderBook': True,
|
333
|
-
'fetchLedger': None,
|
334
|
-
'fetchLedgerEntry': None,
|
335
|
-
'fetchLeverageTiers': None,
|
336
|
-
'fetchMarketLeverageTiers': None,
|
337
|
-
'fetchMarkets': True,
|
338
|
-
'fetchMarkOHLCV': None,
|
339
|
-
'fetchMyTrades': None,
|
340
|
-
'fetchOHLCV': None,
|
341
|
-
'fetchOpenOrder': None,
|
342
|
-
'fetchOpenOrders': None,
|
343
|
-
'fetchOrder': None,
|
344
|
-
'fetchOrderBook': True,
|
345
|
-
'fetchOrderBooks': None,
|
346
|
-
'fetchOrders': None,
|
347
|
-
'fetchOrderTrades': None,
|
348
|
-
'fetchPermissions': None,
|
349
|
-
'fetchPosition': None,
|
350
|
-
'fetchPositions': None,
|
351
|
-
'fetchPositionsRisk': None,
|
352
|
-
'fetchPremiumIndexOHLCV': None,
|
353
|
-
'fetchStatus': None,
|
354
|
-
'fetchTicker': True,
|
355
|
-
'fetchTickers': None,
|
356
|
-
'fetchTime': None,
|
357
|
-
'fetchTrades': True,
|
358
|
-
'fetchTradingFee': None,
|
359
|
-
'fetchTradingFees': None,
|
360
|
-
'fetchTradingLimits': None,
|
361
|
-
'fetchTransactions': None,
|
362
|
-
'fetchTransfers': None,
|
363
|
-
'fetchWithdrawal': None,
|
364
|
-
'fetchWithdrawals': None,
|
365
|
-
'reduceMargin': None,
|
366
|
-
'setLeverage': None,
|
367
|
-
'setMargin': None,
|
368
|
-
'setMarginMode': None,
|
369
|
-
'setPositionMode': None,
|
370
|
-
'signIn': None,
|
371
|
-
'transfer': None,
|
372
|
-
'withdraw': None,
|
373
|
-
'watchOrderBook': None,
|
374
|
-
'watchOrders': None,
|
375
|
-
'watchMyTrades': None,
|
376
|
-
'watchTickers': None,
|
377
|
-
'watchTicker': None,
|
378
|
-
'watchTrades': None,
|
379
|
-
'watchTradesForSymbols': None,
|
380
|
-
'watchOrderBookForSymbols': None,
|
381
|
-
'watchOHLCVForSymbols': None,
|
382
|
-
'watchBalance': None,
|
383
|
-
'watchOHLCV': None,
|
384
|
-
}
|
327
|
+
has = {}
|
328
|
+
features = {}
|
385
329
|
precisionMode = DECIMAL_PLACES
|
386
330
|
paddingMode = NO_PADDING
|
387
331
|
minFundingAddressLength = 1 # used in check_address
|
@@ -399,7 +343,7 @@ class Exchange(object):
|
|
399
343
|
rateLimitMaxTokens = 16
|
400
344
|
rateLimitUpdateTime = 0
|
401
345
|
enableLastHttpResponse = True
|
402
|
-
enableLastJsonResponse =
|
346
|
+
enableLastJsonResponse = False
|
403
347
|
enableLastResponseHeaders = True
|
404
348
|
last_http_response = None
|
405
349
|
last_json_response = None
|
@@ -431,11 +375,14 @@ class Exchange(object):
|
|
431
375
|
self.headers = dict() if self.headers is None else self.headers
|
432
376
|
self.balance = dict() if self.balance is None else self.balance
|
433
377
|
self.orderbooks = dict() if self.orderbooks is None else self.orderbooks
|
378
|
+
self.fundingRates = dict() if self.fundingRates is None else self.fundingRates
|
434
379
|
self.tickers = dict() if self.tickers is None else self.tickers
|
435
380
|
self.bidsasks = dict() if self.bidsasks is None else self.bidsasks
|
436
381
|
self.trades = dict() if self.trades is None else self.trades
|
437
382
|
self.transactions = dict() if self.transactions is None else self.transactions
|
438
383
|
self.ohlcvs = dict() if self.ohlcvs is None else self.ohlcvs
|
384
|
+
self.liquidations = dict() if self.liquidations is None else self.liquidations
|
385
|
+
self.myLiquidations = dict() if self.myLiquidations is None else self.myLiquidations
|
439
386
|
self.currencies = dict() if self.currencies is None else self.currencies
|
440
387
|
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
|
441
388
|
self.decimal_to_precision = decimal_to_precision
|
@@ -462,6 +409,10 @@ class Exchange(object):
|
|
462
409
|
|
463
410
|
self.after_construct()
|
464
411
|
|
412
|
+
is_sandbox = self.safe_bool_2(self.options, 'sandbox', 'testnet', False)
|
413
|
+
if is_sandbox:
|
414
|
+
self.set_sandbox_mode(is_sandbox)
|
415
|
+
|
465
416
|
# convert all properties from underscore notation foo_bar to camelcase notation fooBar
|
466
417
|
cls = type(self)
|
467
418
|
for name in dir(self):
|
@@ -505,9 +456,6 @@ class Exchange(object):
|
|
505
456
|
def __str__(self):
|
506
457
|
return self.name
|
507
458
|
|
508
|
-
def describe(self):
|
509
|
-
return {}
|
510
|
-
|
511
459
|
def throttle(self, cost=None):
|
512
460
|
now = float(self.milliseconds())
|
513
461
|
elapsed = now - self.lastRestRequestTimestamp
|
@@ -548,9 +496,11 @@ class Exchange(object):
|
|
548
496
|
return response_body.strip()
|
549
497
|
|
550
498
|
def on_json_response(self, response_body):
|
551
|
-
if self.quoteJsonNumbers:
|
499
|
+
if self.quoteJsonNumbers and orjson is None:
|
552
500
|
return json.loads(response_body, parse_float=str, parse_int=str)
|
553
501
|
else:
|
502
|
+
if orjson:
|
503
|
+
return orjson.loads(response_body)
|
554
504
|
return json.loads(response_body)
|
555
505
|
|
556
506
|
def fetch(self, url, method='GET', headers=None, body=None):
|
@@ -886,11 +836,13 @@ class Exchange(object):
|
|
886
836
|
|
887
837
|
@staticmethod
|
888
838
|
def get_object_value_from_key_list(dictionary_or_list, key_list):
|
839
|
+
isDataArray = isinstance(dictionary_or_list, list)
|
840
|
+
isDataDict = isinstance(dictionary_or_list, dict)
|
889
841
|
for key in key_list:
|
890
|
-
if
|
842
|
+
if isDataDict:
|
891
843
|
if key in dictionary_or_list and dictionary_or_list[key] is not None and dictionary_or_list[key] != '':
|
892
844
|
return dictionary_or_list[key]
|
893
|
-
elif
|
845
|
+
elif isDataArray and not isinstance(key, str):
|
894
846
|
if (key < len(dictionary_or_list)) and (dictionary_or_list[key] is not None) and (dictionary_or_list[key] != ''):
|
895
847
|
return dictionary_or_list[key]
|
896
848
|
return None
|
@@ -1080,7 +1032,7 @@ class Exchange(object):
|
|
1080
1032
|
if isinstance(params, dict):
|
1081
1033
|
for key in params:
|
1082
1034
|
_encode_params(params[key], key)
|
1083
|
-
return _urlencode.urlencode(result)
|
1035
|
+
return _urlencode.urlencode(result, quote_via=_urlencode.quote)
|
1084
1036
|
|
1085
1037
|
@staticmethod
|
1086
1038
|
def rawencode(params={}):
|
@@ -1171,7 +1123,7 @@ class Exchange(object):
|
|
1171
1123
|
return None
|
1172
1124
|
|
1173
1125
|
try:
|
1174
|
-
utc = datetime.datetime.
|
1126
|
+
utc = datetime.datetime.fromtimestamp(timestamp // 1000, datetime.timezone.utc)
|
1175
1127
|
return utc.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-6] + "{:03d}".format(int(timestamp) % 1000) + 'Z'
|
1176
1128
|
except (TypeError, OverflowError, OSError):
|
1177
1129
|
return None
|
@@ -1187,13 +1139,13 @@ class Exchange(object):
|
|
1187
1139
|
|
1188
1140
|
@staticmethod
|
1189
1141
|
def dmy(timestamp, infix='-'):
|
1190
|
-
utc_datetime = datetime.datetime.
|
1142
|
+
utc_datetime = datetime.datetime.fromtimestamp(int(round(timestamp / 1000)), datetime.timezone.utc)
|
1191
1143
|
return utc_datetime.strftime('%m' + infix + '%d' + infix + '%Y')
|
1192
1144
|
|
1193
1145
|
@staticmethod
|
1194
1146
|
def ymd(timestamp, infix='-', fullYear=True):
|
1195
1147
|
year_format = '%Y' if fullYear else '%y'
|
1196
|
-
utc_datetime = datetime.datetime.
|
1148
|
+
utc_datetime = datetime.datetime.fromtimestamp(int(round(timestamp / 1000)), datetime.timezone.utc)
|
1197
1149
|
return utc_datetime.strftime(year_format + infix + '%m' + infix + '%d')
|
1198
1150
|
|
1199
1151
|
@staticmethod
|
@@ -1206,7 +1158,7 @@ class Exchange(object):
|
|
1206
1158
|
|
1207
1159
|
@staticmethod
|
1208
1160
|
def ymdhms(timestamp, infix=' '):
|
1209
|
-
utc_datetime = datetime.datetime.
|
1161
|
+
utc_datetime = datetime.datetime.fromtimestamp(int(round(timestamp / 1000)), datetime.timezone.utc)
|
1210
1162
|
return utc_datetime.strftime('%Y-%m-%d' + infix + '%H:%M:%S')
|
1211
1163
|
|
1212
1164
|
@staticmethod
|
@@ -1217,7 +1169,7 @@ class Exchange(object):
|
|
1217
1169
|
return None
|
1218
1170
|
if 'GMT' in timestamp:
|
1219
1171
|
try:
|
1220
|
-
string = ''.join([str(value) for value in parsedate(timestamp)[:6]]) + '.000Z'
|
1172
|
+
string = ''.join([str(value).zfill(2) for value in parsedate(timestamp)[:6]]) + '.000Z'
|
1221
1173
|
dt = datetime.datetime.strptime(string, "%Y%m%d%H%M%S.%fZ")
|
1222
1174
|
return calendar.timegm(dt.utctimetuple()) * 1000
|
1223
1175
|
except (TypeError, OverflowError, OSError):
|
@@ -1296,7 +1248,7 @@ class Exchange(object):
|
|
1296
1248
|
return result
|
1297
1249
|
|
1298
1250
|
@staticmethod
|
1299
|
-
def
|
1251
|
+
def urlencode_base64(s):
|
1300
1252
|
return Exchange.decode(base64.urlsafe_b64encode(s)).replace('=', '')
|
1301
1253
|
|
1302
1254
|
@staticmethod
|
@@ -1316,25 +1268,36 @@ class Exchange(object):
|
|
1316
1268
|
return Exchange.decode(base64.b64decode(s))
|
1317
1269
|
|
1318
1270
|
@staticmethod
|
1319
|
-
def jwt(request, secret, algorithm='sha256', is_rsa=False):
|
1271
|
+
def jwt(request, secret, algorithm='sha256', is_rsa=False, opts={}):
|
1320
1272
|
algos = {
|
1321
1273
|
'sha256': hashlib.sha256,
|
1322
1274
|
'sha384': hashlib.sha384,
|
1323
1275
|
'sha512': hashlib.sha512,
|
1324
1276
|
}
|
1325
1277
|
alg = ('RS' if is_rsa else 'HS') + algorithm[3:]
|
1326
|
-
|
1278
|
+
if 'alg' in opts and opts['alg'] is not None:
|
1279
|
+
alg = opts['alg']
|
1280
|
+
header_opts = {
|
1327
1281
|
'alg': alg,
|
1328
1282
|
'typ': 'JWT',
|
1329
|
-
}
|
1330
|
-
|
1331
|
-
|
1283
|
+
}
|
1284
|
+
if 'kid' in opts and opts['kid'] is not None:
|
1285
|
+
header_opts['kid'] = opts['kid']
|
1286
|
+
if 'nonce' in opts and opts['nonce'] is not None:
|
1287
|
+
header_opts['nonce'] = opts['nonce']
|
1288
|
+
header = Exchange.encode(Exchange.json(header_opts))
|
1289
|
+
encoded_header = Exchange.urlencode_base64(header)
|
1290
|
+
encoded_data = Exchange.urlencode_base64(Exchange.encode(Exchange.json(request)))
|
1332
1291
|
token = encoded_header + '.' + encoded_data
|
1333
|
-
|
1292
|
+
algoType = alg[0:2]
|
1293
|
+
if is_rsa or algoType == 'RS':
|
1334
1294
|
signature = Exchange.base64_to_binary(Exchange.rsa(token, Exchange.decode(secret), algorithm))
|
1295
|
+
elif algoType == 'ES':
|
1296
|
+
rawSignature = Exchange.ecdsa(token, secret, 'p256', algorithm)
|
1297
|
+
signature = Exchange.base16_to_binary(rawSignature['r'].rjust(64, "0") + rawSignature['s'].rjust(64, "0"))
|
1335
1298
|
else:
|
1336
1299
|
signature = Exchange.hmac(Exchange.encode(token), secret, algos[algorithm], 'binary')
|
1337
|
-
return token + '.' + Exchange.
|
1300
|
+
return token + '.' + Exchange.urlencode_base64(signature)
|
1338
1301
|
|
1339
1302
|
@staticmethod
|
1340
1303
|
def rsa(request, secret, alg='sha256'):
|
@@ -1356,6 +1319,57 @@ class Exchange(object):
|
|
1356
1319
|
encodedData = account.messages.encode_typed_data(domain, messageTypes, message)
|
1357
1320
|
return Exchange.binary_concat(b"\x19\x01", encodedData.header, encodedData.body)
|
1358
1321
|
|
1322
|
+
@staticmethod
|
1323
|
+
def retrieve_stark_account (signature, accountClassHash, accountProxyClassHash):
|
1324
|
+
privateKey = get_private_key_from_eth_signature(signature)
|
1325
|
+
publicKey = private_to_stark_key(privateKey)
|
1326
|
+
calldata = [
|
1327
|
+
int(accountClassHash, 16),
|
1328
|
+
get_selector_from_name("initialize"),
|
1329
|
+
2,
|
1330
|
+
publicKey,
|
1331
|
+
0,
|
1332
|
+
]
|
1333
|
+
|
1334
|
+
address = compute_address(
|
1335
|
+
class_hash=int(accountProxyClassHash, 16),
|
1336
|
+
constructor_calldata=calldata,
|
1337
|
+
salt=publicKey,
|
1338
|
+
)
|
1339
|
+
return {
|
1340
|
+
'privateKey': privateKey,
|
1341
|
+
'publicKey': publicKey,
|
1342
|
+
'address': hex(address)
|
1343
|
+
}
|
1344
|
+
|
1345
|
+
@staticmethod
|
1346
|
+
def starknet_encode_structured_data (domain, messageTypes, messageData, address):
|
1347
|
+
types = list(messageTypes.keys())
|
1348
|
+
if len(types) > 1:
|
1349
|
+
raise NotSupported('starknetEncodeStructuredData only support single type')
|
1350
|
+
|
1351
|
+
request = {
|
1352
|
+
'domain': domain,
|
1353
|
+
'primaryType': types[0],
|
1354
|
+
'types': Exchange.extend({
|
1355
|
+
'StarkNetDomain': [
|
1356
|
+
{'name': "name", 'type': "felt"},
|
1357
|
+
{'name': "chainId", 'type': "felt"},
|
1358
|
+
{'name': "version", 'type': "felt"},
|
1359
|
+
],
|
1360
|
+
}, messageTypes),
|
1361
|
+
'message': messageData,
|
1362
|
+
}
|
1363
|
+
typedDataClass = TypedDataDataclass.from_dict(request)
|
1364
|
+
msgHash = typedDataClass.message_hash(int(address, 16))
|
1365
|
+
return msgHash
|
1366
|
+
|
1367
|
+
@staticmethod
|
1368
|
+
def starknet_sign (hash, pri):
|
1369
|
+
# // TODO: unify to ecdsa
|
1370
|
+
r, s = message_signature(hash, pri)
|
1371
|
+
return Exchange.json([hex(r), hex(s)])
|
1372
|
+
|
1359
1373
|
@staticmethod
|
1360
1374
|
def packb(o):
|
1361
1375
|
return packb(o)
|
@@ -1364,6 +1378,10 @@ class Exchange(object):
|
|
1364
1378
|
def int_to_base16(num):
|
1365
1379
|
return "%0.2X" % num
|
1366
1380
|
|
1381
|
+
@staticmethod
|
1382
|
+
def random_bytes(length):
|
1383
|
+
return format(random.getrandbits(length * 8), 'x')
|
1384
|
+
|
1367
1385
|
@staticmethod
|
1368
1386
|
def ecdsa(request, secret, algorithm='p256', hash=None, fixed_length=False):
|
1369
1387
|
# your welcome - frosty00
|
@@ -1384,7 +1402,12 @@ class Exchange(object):
|
|
1384
1402
|
digest = Exchange.hash(encoded_request, hash, 'binary')
|
1385
1403
|
else:
|
1386
1404
|
digest = base64.b16decode(encoded_request, casefold=True)
|
1387
|
-
|
1405
|
+
if isinstance(secret, str):
|
1406
|
+
secret = Exchange.encode(secret)
|
1407
|
+
if secret.find(b'-----BEGIN EC PRIVATE KEY-----') > -1:
|
1408
|
+
key = ecdsa.SigningKey.from_pem(secret, hash_function)
|
1409
|
+
else:
|
1410
|
+
key = ecdsa.SigningKey.from_string(base64.b16decode(secret,
|
1388
1411
|
casefold=True), curve=curve_info[0])
|
1389
1412
|
r_binary, s_binary, v = key.sign_digest_deterministic(digest, hashfunc=hash_function,
|
1390
1413
|
sigencode=ecdsa.util.sigencode_strings_canonize)
|
@@ -1407,7 +1430,9 @@ class Exchange(object):
|
|
1407
1430
|
|
1408
1431
|
@staticmethod
|
1409
1432
|
def eddsa(request, secret, curve='ed25519'):
|
1410
|
-
|
1433
|
+
if isinstance(secret, str):
|
1434
|
+
secret = Exchange.encode(secret)
|
1435
|
+
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret) if len(secret) == 32 else load_pem_private_key(secret, None)
|
1411
1436
|
return Exchange.binary_to_base64(private_key.sign(request))
|
1412
1437
|
|
1413
1438
|
@staticmethod
|
@@ -1420,7 +1445,9 @@ class Exchange(object):
|
|
1420
1445
|
|
1421
1446
|
@staticmethod
|
1422
1447
|
def json(data, params=None):
|
1423
|
-
|
1448
|
+
if orjson:
|
1449
|
+
return orjson.dumps(data).decode('utf-8')
|
1450
|
+
return json.dumps(data, separators=(',', ':'), cls=SafeJSONEncoder)
|
1424
1451
|
|
1425
1452
|
@staticmethod
|
1426
1453
|
def is_json_encoded_object(input):
|
@@ -1465,18 +1492,10 @@ class Exchange(object):
|
|
1465
1492
|
return error
|
1466
1493
|
return result
|
1467
1494
|
|
1468
|
-
def check_address(self, address):
|
1469
|
-
"""Checks an address is not the same character repeated or an empty sequence"""
|
1470
|
-
if address is None:
|
1471
|
-
raise InvalidAddress(self.id + ' address is None')
|
1472
|
-
if all(letter == address[0] for letter in address) or len(address) < self.minFundingAddressLength or ' ' in address:
|
1473
|
-
raise InvalidAddress(self.id + ' address is invalid or has less than ' + str(self.minFundingAddressLength) + ' characters: "' + str(address) + '"')
|
1474
|
-
return address
|
1475
|
-
|
1476
1495
|
def precision_from_string(self, str):
|
1477
1496
|
# support string formats like '1e-4'
|
1478
|
-
if 'e' in str:
|
1479
|
-
numStr = re.sub(r'\
|
1497
|
+
if 'e' in str or 'E' in str:
|
1498
|
+
numStr = re.sub(r'\d\.?\d*[eE]', '', str)
|
1480
1499
|
return int(numStr) * -1
|
1481
1500
|
# support integer formats (without dot) like '1', '10' etc [Note: bug in decimalToPrecision, so this should not be used atm]
|
1482
1501
|
# if not ('.' in str):
|
@@ -1497,13 +1516,6 @@ class Exchange(object):
|
|
1497
1516
|
markets = self.fetch_markets(params)
|
1498
1517
|
return self.set_markets(markets, currencies)
|
1499
1518
|
|
1500
|
-
def load_fees(self, reload=False):
|
1501
|
-
if not reload:
|
1502
|
-
if self.loaded_fees != Exchange.loaded_fees:
|
1503
|
-
return self.loaded_fees
|
1504
|
-
self.loaded_fees = self.deep_extend(self.loaded_fees, self.fetch_fees())
|
1505
|
-
return self.loaded_fees
|
1506
|
-
|
1507
1519
|
def fetch_markets(self, params={}):
|
1508
1520
|
# markets are returned as a list
|
1509
1521
|
# currencies are returned as a dict
|
@@ -1659,11 +1671,14 @@ class Exchange(object):
|
|
1659
1671
|
return default
|
1660
1672
|
|
1661
1673
|
def omit_zero(self, string_number):
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1674
|
+
try:
|
1675
|
+
if string_number is None or string_number == '':
|
1676
|
+
return None
|
1677
|
+
if float(string_number) == 0:
|
1678
|
+
return None
|
1679
|
+
return string_number
|
1680
|
+
except Exception:
|
1681
|
+
return string_number
|
1667
1682
|
|
1668
1683
|
def check_order_arguments(self, market, type, side, amount, price, params):
|
1669
1684
|
if price is None:
|
@@ -1703,10 +1718,10 @@ class Exchange(object):
|
|
1703
1718
|
def string_to_chars_array(self, value):
|
1704
1719
|
return list(value)
|
1705
1720
|
|
1706
|
-
def
|
1721
|
+
def value_is_defined(self, value):
|
1707
1722
|
return value is not None
|
1708
1723
|
|
1709
|
-
def
|
1724
|
+
def array_slice(self, array, first, second=None):
|
1710
1725
|
return array[first:second] if second else array[first:]
|
1711
1726
|
|
1712
1727
|
def get_property(self, obj, property, defaultValue=None):
|
@@ -1728,6 +1743,15 @@ class Exchange(object):
|
|
1728
1743
|
modifiedContent = modifiedContent.replace('}"', '}')
|
1729
1744
|
return modifiedContent
|
1730
1745
|
|
1746
|
+
def extend_exchange_options(self, newOptions):
|
1747
|
+
self.options = self.extend(self.options, newOptions)
|
1748
|
+
|
1749
|
+
def create_safe_dictionary(self):
|
1750
|
+
return {}
|
1751
|
+
|
1752
|
+
def rand_number(self, size):
|
1753
|
+
return int(''.join([str(random.randint(0, 9)) for _ in range(size)]))
|
1754
|
+
|
1731
1755
|
# ########################################################################
|
1732
1756
|
# ########################################################################
|
1733
1757
|
# ########################################################################
|
@@ -1767,9 +1791,328 @@ class Exchange(object):
|
|
1767
1791
|
|
1768
1792
|
# METHODS BELOW THIS LINE ARE TRANSPILED FROM JAVASCRIPT TO PYTHON AND PHP
|
1769
1793
|
|
1794
|
+
def describe(self):
|
1795
|
+
return {
|
1796
|
+
'id': None,
|
1797
|
+
'name': None,
|
1798
|
+
'countries': None,
|
1799
|
+
'enableRateLimit': True,
|
1800
|
+
'rateLimit': 2000, # milliseconds = seconds * 1000
|
1801
|
+
'timeout': self.timeout, # milliseconds = seconds * 1000
|
1802
|
+
'certified': False, # if certified by the CCXT dev team
|
1803
|
+
'pro': False, # if it is integrated with CCXT Pro for WebSocket support
|
1804
|
+
'alias': False, # whether self exchange is an alias to another exchange
|
1805
|
+
'dex': False,
|
1806
|
+
'has': {
|
1807
|
+
'publicAPI': True,
|
1808
|
+
'privateAPI': True,
|
1809
|
+
'CORS': None,
|
1810
|
+
'sandbox': None,
|
1811
|
+
'spot': None,
|
1812
|
+
'margin': None,
|
1813
|
+
'swap': None,
|
1814
|
+
'future': None,
|
1815
|
+
'option': None,
|
1816
|
+
'addMargin': None,
|
1817
|
+
'borrowCrossMargin': None,
|
1818
|
+
'borrowIsolatedMargin': None,
|
1819
|
+
'borrowMargin': None,
|
1820
|
+
'cancelAllOrders': None,
|
1821
|
+
'cancelAllOrdersWs': None,
|
1822
|
+
'cancelOrder': True,
|
1823
|
+
'cancelOrderWs': None,
|
1824
|
+
'cancelOrders': None,
|
1825
|
+
'cancelOrdersWs': None,
|
1826
|
+
'closeAllPositions': None,
|
1827
|
+
'closePosition': None,
|
1828
|
+
'createDepositAddress': None,
|
1829
|
+
'createLimitBuyOrder': None,
|
1830
|
+
'createLimitBuyOrderWs': None,
|
1831
|
+
'createLimitOrder': True,
|
1832
|
+
'createLimitOrderWs': None,
|
1833
|
+
'createLimitSellOrder': None,
|
1834
|
+
'createLimitSellOrderWs': None,
|
1835
|
+
'createMarketBuyOrder': None,
|
1836
|
+
'createMarketBuyOrderWs': None,
|
1837
|
+
'createMarketBuyOrderWithCost': None,
|
1838
|
+
'createMarketBuyOrderWithCostWs': None,
|
1839
|
+
'createMarketOrder': True,
|
1840
|
+
'createMarketOrderWs': True,
|
1841
|
+
'createMarketOrderWithCost': None,
|
1842
|
+
'createMarketOrderWithCostWs': None,
|
1843
|
+
'createMarketSellOrder': None,
|
1844
|
+
'createMarketSellOrderWs': None,
|
1845
|
+
'createMarketSellOrderWithCost': None,
|
1846
|
+
'createMarketSellOrderWithCostWs': None,
|
1847
|
+
'createOrder': True,
|
1848
|
+
'createOrderWs': None,
|
1849
|
+
'createOrders': None,
|
1850
|
+
'createOrderWithTakeProfitAndStopLoss': None,
|
1851
|
+
'createOrderWithTakeProfitAndStopLossWs': None,
|
1852
|
+
'createPostOnlyOrder': None,
|
1853
|
+
'createPostOnlyOrderWs': None,
|
1854
|
+
'createReduceOnlyOrder': None,
|
1855
|
+
'createReduceOnlyOrderWs': None,
|
1856
|
+
'createStopLimitOrder': None,
|
1857
|
+
'createStopLimitOrderWs': None,
|
1858
|
+
'createStopLossOrder': None,
|
1859
|
+
'createStopLossOrderWs': None,
|
1860
|
+
'createStopMarketOrder': None,
|
1861
|
+
'createStopMarketOrderWs': None,
|
1862
|
+
'createStopOrder': None,
|
1863
|
+
'createStopOrderWs': None,
|
1864
|
+
'createTakeProfitOrder': None,
|
1865
|
+
'createTakeProfitOrderWs': None,
|
1866
|
+
'createTrailingAmountOrder': None,
|
1867
|
+
'createTrailingAmountOrderWs': None,
|
1868
|
+
'createTrailingPercentOrder': None,
|
1869
|
+
'createTrailingPercentOrderWs': None,
|
1870
|
+
'createTriggerOrder': None,
|
1871
|
+
'createTriggerOrderWs': None,
|
1872
|
+
'deposit': None,
|
1873
|
+
'editOrder': 'emulated',
|
1874
|
+
'editOrderWs': None,
|
1875
|
+
'fetchAccounts': None,
|
1876
|
+
'fetchBalance': True,
|
1877
|
+
'fetchBalanceWs': None,
|
1878
|
+
'fetchBidsAsks': None,
|
1879
|
+
'fetchBorrowInterest': None,
|
1880
|
+
'fetchBorrowRate': None,
|
1881
|
+
'fetchBorrowRateHistories': None,
|
1882
|
+
'fetchBorrowRateHistory': None,
|
1883
|
+
'fetchBorrowRates': None,
|
1884
|
+
'fetchBorrowRatesPerSymbol': None,
|
1885
|
+
'fetchCanceledAndClosedOrders': None,
|
1886
|
+
'fetchCanceledOrders': None,
|
1887
|
+
'fetchClosedOrder': None,
|
1888
|
+
'fetchClosedOrders': None,
|
1889
|
+
'fetchClosedOrdersWs': None,
|
1890
|
+
'fetchConvertCurrencies': None,
|
1891
|
+
'fetchConvertQuote': None,
|
1892
|
+
'fetchConvertTrade': None,
|
1893
|
+
'fetchConvertTradeHistory': None,
|
1894
|
+
'fetchCrossBorrowRate': None,
|
1895
|
+
'fetchCrossBorrowRates': None,
|
1896
|
+
'fetchCurrencies': 'emulated',
|
1897
|
+
'fetchCurrenciesWs': 'emulated',
|
1898
|
+
'fetchDeposit': None,
|
1899
|
+
'fetchDepositAddress': None,
|
1900
|
+
'fetchDepositAddresses': None,
|
1901
|
+
'fetchDepositAddressesByNetwork': None,
|
1902
|
+
'fetchDeposits': None,
|
1903
|
+
'fetchDepositsWithdrawals': None,
|
1904
|
+
'fetchDepositsWs': None,
|
1905
|
+
'fetchDepositWithdrawFee': None,
|
1906
|
+
'fetchDepositWithdrawFees': None,
|
1907
|
+
'fetchFundingHistory': None,
|
1908
|
+
'fetchFundingRate': None,
|
1909
|
+
'fetchFundingRateHistory': None,
|
1910
|
+
'fetchFundingInterval': None,
|
1911
|
+
'fetchFundingIntervals': None,
|
1912
|
+
'fetchFundingRates': None,
|
1913
|
+
'fetchGreeks': None,
|
1914
|
+
'fetchIndexOHLCV': None,
|
1915
|
+
'fetchIsolatedBorrowRate': None,
|
1916
|
+
'fetchIsolatedBorrowRates': None,
|
1917
|
+
'fetchMarginAdjustmentHistory': None,
|
1918
|
+
'fetchIsolatedPositions': None,
|
1919
|
+
'fetchL2OrderBook': True,
|
1920
|
+
'fetchL3OrderBook': None,
|
1921
|
+
'fetchLastPrices': None,
|
1922
|
+
'fetchLedger': None,
|
1923
|
+
'fetchLedgerEntry': None,
|
1924
|
+
'fetchLeverage': None,
|
1925
|
+
'fetchLeverages': None,
|
1926
|
+
'fetchLeverageTiers': None,
|
1927
|
+
'fetchLiquidations': None,
|
1928
|
+
'fetchLongShortRatio': None,
|
1929
|
+
'fetchLongShortRatioHistory': None,
|
1930
|
+
'fetchMarginMode': None,
|
1931
|
+
'fetchMarginModes': None,
|
1932
|
+
'fetchMarketLeverageTiers': None,
|
1933
|
+
'fetchMarkets': True,
|
1934
|
+
'fetchMarketsWs': None,
|
1935
|
+
'fetchMarkOHLCV': None,
|
1936
|
+
'fetchMyLiquidations': None,
|
1937
|
+
'fetchMySettlementHistory': None,
|
1938
|
+
'fetchMyTrades': None,
|
1939
|
+
'fetchMyTradesWs': None,
|
1940
|
+
'fetchOHLCV': None,
|
1941
|
+
'fetchOHLCVWs': None,
|
1942
|
+
'fetchOpenInterest': None,
|
1943
|
+
'fetchOpenInterests': None,
|
1944
|
+
'fetchOpenInterestHistory': None,
|
1945
|
+
'fetchOpenOrder': None,
|
1946
|
+
'fetchOpenOrders': None,
|
1947
|
+
'fetchOpenOrdersWs': None,
|
1948
|
+
'fetchOption': None,
|
1949
|
+
'fetchOptionChain': None,
|
1950
|
+
'fetchOrder': None,
|
1951
|
+
'fetchOrderBook': True,
|
1952
|
+
'fetchOrderBooks': None,
|
1953
|
+
'fetchOrderBookWs': None,
|
1954
|
+
'fetchOrders': None,
|
1955
|
+
'fetchOrdersByStatus': None,
|
1956
|
+
'fetchOrdersWs': None,
|
1957
|
+
'fetchOrderTrades': None,
|
1958
|
+
'fetchOrderWs': None,
|
1959
|
+
'fetchPosition': None,
|
1960
|
+
'fetchPositionHistory': None,
|
1961
|
+
'fetchPositionsHistory': None,
|
1962
|
+
'fetchPositionWs': None,
|
1963
|
+
'fetchPositionMode': None,
|
1964
|
+
'fetchPositions': None,
|
1965
|
+
'fetchPositionsWs': None,
|
1966
|
+
'fetchPositionsForSymbol': None,
|
1967
|
+
'fetchPositionsForSymbolWs': None,
|
1968
|
+
'fetchPositionsRisk': None,
|
1969
|
+
'fetchPremiumIndexOHLCV': None,
|
1970
|
+
'fetchSettlementHistory': None,
|
1971
|
+
'fetchStatus': None,
|
1972
|
+
'fetchTicker': True,
|
1973
|
+
'fetchTickerWs': None,
|
1974
|
+
'fetchTickers': None,
|
1975
|
+
'fetchMarkPrices': None,
|
1976
|
+
'fetchTickersWs': None,
|
1977
|
+
'fetchTime': None,
|
1978
|
+
'fetchTrades': True,
|
1979
|
+
'fetchTradesWs': None,
|
1980
|
+
'fetchTradingFee': None,
|
1981
|
+
'fetchTradingFees': None,
|
1982
|
+
'fetchTradingFeesWs': None,
|
1983
|
+
'fetchTradingLimits': None,
|
1984
|
+
'fetchTransactionFee': None,
|
1985
|
+
'fetchTransactionFees': None,
|
1986
|
+
'fetchTransactions': None,
|
1987
|
+
'fetchTransfer': None,
|
1988
|
+
'fetchTransfers': None,
|
1989
|
+
'fetchUnderlyingAssets': None,
|
1990
|
+
'fetchVolatilityHistory': None,
|
1991
|
+
'fetchWithdrawAddresses': None,
|
1992
|
+
'fetchWithdrawal': None,
|
1993
|
+
'fetchWithdrawals': None,
|
1994
|
+
'fetchWithdrawalsWs': None,
|
1995
|
+
'fetchWithdrawalWhitelist': None,
|
1996
|
+
'reduceMargin': None,
|
1997
|
+
'repayCrossMargin': None,
|
1998
|
+
'repayIsolatedMargin': None,
|
1999
|
+
'setLeverage': None,
|
2000
|
+
'setMargin': None,
|
2001
|
+
'setMarginMode': None,
|
2002
|
+
'setPositionMode': None,
|
2003
|
+
'signIn': None,
|
2004
|
+
'transfer': None,
|
2005
|
+
'watchBalance': None,
|
2006
|
+
'watchMyTrades': None,
|
2007
|
+
'watchOHLCV': None,
|
2008
|
+
'watchOHLCVForSymbols': None,
|
2009
|
+
'watchOrderBook': None,
|
2010
|
+
'watchOrderBookForSymbols': None,
|
2011
|
+
'watchOrders': None,
|
2012
|
+
'watchOrdersForSymbols': None,
|
2013
|
+
'watchPosition': None,
|
2014
|
+
'watchPositions': None,
|
2015
|
+
'watchStatus': None,
|
2016
|
+
'watchTicker': None,
|
2017
|
+
'watchTickers': None,
|
2018
|
+
'watchTrades': None,
|
2019
|
+
'watchTradesForSymbols': None,
|
2020
|
+
'watchLiquidations': None,
|
2021
|
+
'watchLiquidationsForSymbols': None,
|
2022
|
+
'watchMyLiquidations': None,
|
2023
|
+
'watchMyLiquidationsForSymbols': None,
|
2024
|
+
'withdraw': None,
|
2025
|
+
'ws': None,
|
2026
|
+
},
|
2027
|
+
'urls': {
|
2028
|
+
'logo': None,
|
2029
|
+
'api': None,
|
2030
|
+
'www': None,
|
2031
|
+
'doc': None,
|
2032
|
+
'fees': None,
|
2033
|
+
},
|
2034
|
+
'api': None,
|
2035
|
+
'requiredCredentials': {
|
2036
|
+
'apiKey': True,
|
2037
|
+
'secret': True,
|
2038
|
+
'uid': False,
|
2039
|
+
'accountId': False,
|
2040
|
+
'login': False,
|
2041
|
+
'password': False,
|
2042
|
+
'twofa': False, # 2-factor authentication(one-time password key)
|
2043
|
+
'privateKey': False, # a "0x"-prefixed hexstring private key for a wallet
|
2044
|
+
'walletAddress': False, # the wallet address "0x"-prefixed hexstring
|
2045
|
+
'token': False, # reserved for HTTP auth in some cases
|
2046
|
+
},
|
2047
|
+
'markets': None, # to be filled manually or by fetchMarkets
|
2048
|
+
'currencies': {}, # to be filled manually or by fetchMarkets
|
2049
|
+
'timeframes': None, # redefine if the exchange has.fetchOHLCV
|
2050
|
+
'fees': {
|
2051
|
+
'trading': {
|
2052
|
+
'tierBased': None,
|
2053
|
+
'percentage': None,
|
2054
|
+
'taker': None,
|
2055
|
+
'maker': None,
|
2056
|
+
},
|
2057
|
+
'funding': {
|
2058
|
+
'tierBased': None,
|
2059
|
+
'percentage': None,
|
2060
|
+
'withdraw': {},
|
2061
|
+
'deposit': {},
|
2062
|
+
},
|
2063
|
+
},
|
2064
|
+
'status': {
|
2065
|
+
'status': 'ok',
|
2066
|
+
'updated': None,
|
2067
|
+
'eta': None,
|
2068
|
+
'url': None,
|
2069
|
+
},
|
2070
|
+
'exceptions': None,
|
2071
|
+
'httpExceptions': {
|
2072
|
+
'422': ExchangeError,
|
2073
|
+
'418': DDoSProtection,
|
2074
|
+
'429': RateLimitExceeded,
|
2075
|
+
'404': ExchangeNotAvailable,
|
2076
|
+
'409': ExchangeNotAvailable,
|
2077
|
+
'410': ExchangeNotAvailable,
|
2078
|
+
'451': ExchangeNotAvailable,
|
2079
|
+
'500': ExchangeNotAvailable,
|
2080
|
+
'501': ExchangeNotAvailable,
|
2081
|
+
'502': ExchangeNotAvailable,
|
2082
|
+
'520': ExchangeNotAvailable,
|
2083
|
+
'521': ExchangeNotAvailable,
|
2084
|
+
'522': ExchangeNotAvailable,
|
2085
|
+
'525': ExchangeNotAvailable,
|
2086
|
+
'526': ExchangeNotAvailable,
|
2087
|
+
'400': ExchangeNotAvailable,
|
2088
|
+
'403': ExchangeNotAvailable,
|
2089
|
+
'405': ExchangeNotAvailable,
|
2090
|
+
'503': ExchangeNotAvailable,
|
2091
|
+
'530': ExchangeNotAvailable,
|
2092
|
+
'408': RequestTimeout,
|
2093
|
+
'504': RequestTimeout,
|
2094
|
+
'401': AuthenticationError,
|
2095
|
+
'407': AuthenticationError,
|
2096
|
+
'511': AuthenticationError,
|
2097
|
+
},
|
2098
|
+
'commonCurrencies': {
|
2099
|
+
'XBT': 'BTC',
|
2100
|
+
'BCC': 'BCH',
|
2101
|
+
'BCHSV': 'BSV',
|
2102
|
+
},
|
2103
|
+
'precisionMode': TICK_SIZE,
|
2104
|
+
'paddingMode': NO_PADDING,
|
2105
|
+
'limits': {
|
2106
|
+
'leverage': {'min': None, 'max': None},
|
2107
|
+
'amount': {'min': None, 'max': None},
|
2108
|
+
'price': {'min': None, 'max': None},
|
2109
|
+
'cost': {'min': None, 'max': None},
|
2110
|
+
},
|
2111
|
+
}
|
2112
|
+
|
1770
2113
|
def safe_bool_n(self, dictionaryOrList, keys: List[IndexType], defaultValue: bool = None):
|
1771
2114
|
"""
|
1772
|
-
|
2115
|
+
@ignore
|
1773
2116
|
safely extract boolean value from dictionary or list
|
1774
2117
|
:returns bool | None:
|
1775
2118
|
"""
|
@@ -1780,7 +2123,7 @@ class Exchange(object):
|
|
1780
2123
|
|
1781
2124
|
def safe_bool_2(self, dictionary, key1: IndexType, key2: IndexType, defaultValue: bool = None):
|
1782
2125
|
"""
|
1783
|
-
|
2126
|
+
@ignore
|
1784
2127
|
safely extract boolean value from dictionary or list
|
1785
2128
|
:returns bool | None:
|
1786
2129
|
"""
|
@@ -1788,7 +2131,7 @@ class Exchange(object):
|
|
1788
2131
|
|
1789
2132
|
def safe_bool(self, dictionary, key: IndexType, defaultValue: bool = None):
|
1790
2133
|
"""
|
1791
|
-
|
2134
|
+
@ignore
|
1792
2135
|
safely extract boolean value from dictionary or list
|
1793
2136
|
:returns bool | None:
|
1794
2137
|
"""
|
@@ -1796,20 +2139,21 @@ class Exchange(object):
|
|
1796
2139
|
|
1797
2140
|
def safe_dict_n(self, dictionaryOrList, keys: List[IndexType], defaultValue: dict = None):
|
1798
2141
|
"""
|
1799
|
-
|
2142
|
+
@ignore
|
1800
2143
|
safely extract a dictionary from dictionary or list
|
1801
2144
|
:returns dict | None:
|
1802
2145
|
"""
|
1803
2146
|
value = self.safe_value_n(dictionaryOrList, keys, defaultValue)
|
1804
2147
|
if value is None:
|
1805
2148
|
return defaultValue
|
1806
|
-
if isinstance(value, dict):
|
1807
|
-
|
2149
|
+
if (isinstance(value, dict)):
|
2150
|
+
if not isinstance(value, list):
|
2151
|
+
return value
|
1808
2152
|
return defaultValue
|
1809
2153
|
|
1810
2154
|
def safe_dict(self, dictionary, key: IndexType, defaultValue: dict = None):
|
1811
2155
|
"""
|
1812
|
-
|
2156
|
+
@ignore
|
1813
2157
|
safely extract a dictionary from dictionary or list
|
1814
2158
|
:returns dict | None:
|
1815
2159
|
"""
|
@@ -1817,7 +2161,7 @@ class Exchange(object):
|
|
1817
2161
|
|
1818
2162
|
def safe_dict_2(self, dictionary, key1: IndexType, key2: str, defaultValue: dict = None):
|
1819
2163
|
"""
|
1820
|
-
|
2164
|
+
@ignore
|
1821
2165
|
safely extract a dictionary from dictionary or list
|
1822
2166
|
:returns dict | None:
|
1823
2167
|
"""
|
@@ -1825,7 +2169,7 @@ class Exchange(object):
|
|
1825
2169
|
|
1826
2170
|
def safe_list_n(self, dictionaryOrList, keys: List[IndexType], defaultValue: List[Any] = None):
|
1827
2171
|
"""
|
1828
|
-
|
2172
|
+
@ignore
|
1829
2173
|
safely extract an Array from dictionary or list
|
1830
2174
|
:returns Array | None:
|
1831
2175
|
"""
|
@@ -1838,7 +2182,7 @@ class Exchange(object):
|
|
1838
2182
|
|
1839
2183
|
def safe_list_2(self, dictionaryOrList, key1: IndexType, key2: str, defaultValue: List[Any] = None):
|
1840
2184
|
"""
|
1841
|
-
|
2185
|
+
@ignore
|
1842
2186
|
safely extract an Array from dictionary or list
|
1843
2187
|
:returns Array | None:
|
1844
2188
|
"""
|
@@ -1846,7 +2190,7 @@ class Exchange(object):
|
|
1846
2190
|
|
1847
2191
|
def safe_list(self, dictionaryOrList, key: IndexType, defaultValue: List[Any] = None):
|
1848
2192
|
"""
|
1849
|
-
|
2193
|
+
@ignore
|
1850
2194
|
safely extract an Array from dictionary or list
|
1851
2195
|
:returns Array | None:
|
1852
2196
|
"""
|
@@ -1859,6 +2203,11 @@ class Exchange(object):
|
|
1859
2203
|
def handle_delta(self, bookside, delta):
|
1860
2204
|
raise NotSupported(self.id + ' handleDelta not supported yet')
|
1861
2205
|
|
2206
|
+
def handle_deltas_with_keys(self, bookSide: Any, deltas, priceKey: IndexType = 0, amountKey: IndexType = 1, countOrIdKey: IndexType = 2):
|
2207
|
+
for i in range(0, len(deltas)):
|
2208
|
+
bidAsk = self.parse_bid_ask(deltas[i], priceKey, amountKey, countOrIdKey)
|
2209
|
+
bookSide.storeArray(bidAsk)
|
2210
|
+
|
1862
2211
|
def get_cache_index(self, orderbook, deltas):
|
1863
2212
|
# return the first index of the cache that can be applied to the orderbook or -1 if not possible
|
1864
2213
|
return -1
|
@@ -1898,7 +2247,7 @@ class Exchange(object):
|
|
1898
2247
|
length = len(usedProxies)
|
1899
2248
|
if length > 1:
|
1900
2249
|
joinedProxyNames = ','.join(usedProxies)
|
1901
|
-
raise
|
2250
|
+
raise InvalidProxySettings(self.id + ' you have multiple conflicting proxy settings(' + joinedProxyNames + '), please use only one from : proxyUrl, proxy_url, proxyUrlCallback, proxy_url_callback')
|
1902
2251
|
return proxyUrl
|
1903
2252
|
|
1904
2253
|
def check_proxy_settings(self, url: Str = None, method: Str = None, headers=None, body=None):
|
@@ -1907,49 +2256,43 @@ class Exchange(object):
|
|
1907
2256
|
httpsProxy = None
|
1908
2257
|
socksProxy = None
|
1909
2258
|
# httpProxy
|
1910
|
-
|
2259
|
+
isHttpProxyDefined = self.value_is_defined(self.httpProxy)
|
2260
|
+
isHttp_proxy_defined = self.value_is_defined(self.http_proxy)
|
2261
|
+
if isHttpProxyDefined or isHttp_proxy_defined:
|
1911
2262
|
usedProxies.append('httpProxy')
|
1912
|
-
httpProxy = self.httpProxy
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1916
|
-
if self.httpProxyCallback is not None:
|
2263
|
+
httpProxy = self.httpProxy if isHttpProxyDefined else self.http_proxy
|
2264
|
+
ishttpProxyCallbackDefined = self.value_is_defined(self.httpProxyCallback)
|
2265
|
+
ishttp_proxy_callback_defined = self.value_is_defined(self.http_proxy_callback)
|
2266
|
+
if ishttpProxyCallbackDefined or ishttp_proxy_callback_defined:
|
1917
2267
|
usedProxies.append('httpProxyCallback')
|
1918
|
-
httpProxy = self.httpProxyCallback(url, method, headers, body)
|
1919
|
-
if self.http_proxy_callback is not None:
|
1920
|
-
usedProxies.append('http_proxy_callback')
|
1921
|
-
httpProxy = self.http_proxy_callback(url, method, headers, body)
|
2268
|
+
httpProxy = self.httpProxyCallback(url, method, headers, body) if ishttpProxyCallbackDefined else self.http_proxy_callback(url, method, headers, body)
|
1922
2269
|
# httpsProxy
|
1923
|
-
|
2270
|
+
isHttpsProxyDefined = self.value_is_defined(self.httpsProxy)
|
2271
|
+
isHttps_proxy_defined = self.value_is_defined(self.https_proxy)
|
2272
|
+
if isHttpsProxyDefined or isHttps_proxy_defined:
|
1924
2273
|
usedProxies.append('httpsProxy')
|
1925
|
-
httpsProxy = self.httpsProxy
|
1926
|
-
|
1927
|
-
|
1928
|
-
|
1929
|
-
if self.httpsProxyCallback is not None:
|
2274
|
+
httpsProxy = self.httpsProxy if isHttpsProxyDefined else self.https_proxy
|
2275
|
+
ishttpsProxyCallbackDefined = self.value_is_defined(self.httpsProxyCallback)
|
2276
|
+
ishttps_proxy_callback_defined = self.value_is_defined(self.https_proxy_callback)
|
2277
|
+
if ishttpsProxyCallbackDefined or ishttps_proxy_callback_defined:
|
1930
2278
|
usedProxies.append('httpsProxyCallback')
|
1931
|
-
httpsProxy = self.httpsProxyCallback(url, method, headers, body)
|
1932
|
-
if self.https_proxy_callback is not None:
|
1933
|
-
usedProxies.append('https_proxy_callback')
|
1934
|
-
httpsProxy = self.https_proxy_callback(url, method, headers, body)
|
2279
|
+
httpsProxy = self.httpsProxyCallback(url, method, headers, body) if ishttpsProxyCallbackDefined else self.https_proxy_callback(url, method, headers, body)
|
1935
2280
|
# socksProxy
|
1936
|
-
|
2281
|
+
isSocksProxyDefined = self.value_is_defined(self.socksProxy)
|
2282
|
+
isSocks_proxy_defined = self.value_is_defined(self.socks_proxy)
|
2283
|
+
if isSocksProxyDefined or isSocks_proxy_defined:
|
1937
2284
|
usedProxies.append('socksProxy')
|
1938
|
-
socksProxy = self.socksProxy
|
1939
|
-
|
1940
|
-
|
1941
|
-
|
1942
|
-
if self.socksProxyCallback is not None:
|
2285
|
+
socksProxy = self.socksProxy if isSocksProxyDefined else self.socks_proxy
|
2286
|
+
issocksProxyCallbackDefined = self.value_is_defined(self.socksProxyCallback)
|
2287
|
+
issocks_proxy_callback_defined = self.value_is_defined(self.socks_proxy_callback)
|
2288
|
+
if issocksProxyCallbackDefined or issocks_proxy_callback_defined:
|
1943
2289
|
usedProxies.append('socksProxyCallback')
|
1944
|
-
socksProxy = self.socksProxyCallback(url, method, headers, body)
|
1945
|
-
if self.socks_proxy_callback is not None:
|
1946
|
-
usedProxies.append('socks_proxy_callback')
|
1947
|
-
socksProxy = self.socks_proxy_callback(url, method, headers, body)
|
2290
|
+
socksProxy = self.socksProxyCallback(url, method, headers, body) if issocksProxyCallbackDefined else self.socks_proxy_callback(url, method, headers, body)
|
1948
2291
|
# check
|
1949
2292
|
length = len(usedProxies)
|
1950
2293
|
if length > 1:
|
1951
2294
|
joinedProxyNames = ','.join(usedProxies)
|
1952
|
-
raise
|
2295
|
+
raise InvalidProxySettings(self.id + ' you have multiple conflicting proxy settings(' + joinedProxyNames + '), please use only one from: httpProxy, httpsProxy, httpProxyCallback, httpsProxyCallback, socksProxy, socksProxyCallback')
|
1953
2296
|
return [httpProxy, httpsProxy, socksProxy]
|
1954
2297
|
|
1955
2298
|
def check_ws_proxy_settings(self):
|
@@ -1958,36 +2301,43 @@ class Exchange(object):
|
|
1958
2301
|
wssProxy = None
|
1959
2302
|
wsSocksProxy = None
|
1960
2303
|
# ws proxy
|
1961
|
-
|
2304
|
+
isWsProxyDefined = self.value_is_defined(self.wsProxy)
|
2305
|
+
is_ws_proxy_defined = self.value_is_defined(self.ws_proxy)
|
2306
|
+
if isWsProxyDefined or is_ws_proxy_defined:
|
1962
2307
|
usedProxies.append('wsProxy')
|
1963
|
-
wsProxy = self.wsProxy
|
1964
|
-
if self.valueIsDefined(self.ws_proxy):
|
1965
|
-
usedProxies.append('ws_proxy')
|
1966
|
-
wsProxy = self.ws_proxy
|
2308
|
+
wsProxy = self.wsProxy if (isWsProxyDefined) else self.ws_proxy
|
1967
2309
|
# wss proxy
|
1968
|
-
|
2310
|
+
isWssProxyDefined = self.value_is_defined(self.wssProxy)
|
2311
|
+
is_wss_proxy_defined = self.value_is_defined(self.wss_proxy)
|
2312
|
+
if isWssProxyDefined or is_wss_proxy_defined:
|
1969
2313
|
usedProxies.append('wssProxy')
|
1970
|
-
wssProxy = self.wssProxy
|
1971
|
-
if self.valueIsDefined(self.wss_proxy):
|
1972
|
-
usedProxies.append('wss_proxy')
|
1973
|
-
wssProxy = self.wss_proxy
|
2314
|
+
wssProxy = self.wssProxy if (isWssProxyDefined) else self.wss_proxy
|
1974
2315
|
# ws socks proxy
|
1975
|
-
|
2316
|
+
isWsSocksProxyDefined = self.value_is_defined(self.wsSocksProxy)
|
2317
|
+
is_ws_socks_proxy_defined = self.value_is_defined(self.ws_socks_proxy)
|
2318
|
+
if isWsSocksProxyDefined or is_ws_socks_proxy_defined:
|
1976
2319
|
usedProxies.append('wsSocksProxy')
|
1977
|
-
wsSocksProxy = self.wsSocksProxy
|
1978
|
-
if self.valueIsDefined(self.ws_socks_proxy):
|
1979
|
-
usedProxies.append('ws_socks_proxy')
|
1980
|
-
wsSocksProxy = self.ws_socks_proxy
|
2320
|
+
wsSocksProxy = self.wsSocksProxy if (isWsSocksProxyDefined) else self.ws_socks_proxy
|
1981
2321
|
# check
|
1982
2322
|
length = len(usedProxies)
|
1983
2323
|
if length > 1:
|
1984
2324
|
joinedProxyNames = ','.join(usedProxies)
|
1985
|
-
raise
|
2325
|
+
raise InvalidProxySettings(self.id + ' you have multiple conflicting proxy settings(' + joinedProxyNames + '), please use only one from: wsProxy, wssProxy, wsSocksProxy')
|
1986
2326
|
return [wsProxy, wssProxy, wsSocksProxy]
|
1987
2327
|
|
1988
2328
|
def check_conflicting_proxies(self, proxyAgentSet, proxyUrlSet):
|
1989
2329
|
if proxyAgentSet and proxyUrlSet:
|
1990
|
-
raise
|
2330
|
+
raise InvalidProxySettings(self.id + ' you have multiple conflicting proxy settings, please use only one from : proxyUrl, httpProxy, httpsProxy, socksProxy')
|
2331
|
+
|
2332
|
+
def check_address(self, address: Str = None):
|
2333
|
+
if address is None:
|
2334
|
+
raise InvalidAddress(self.id + ' address is None')
|
2335
|
+
# check the address is not the same letter like 'aaaaa' nor too short nor has a space
|
2336
|
+
uniqChars = (self.unique(self.string_to_chars_array(address)))
|
2337
|
+
length = len(uniqChars) # py transpiler trick
|
2338
|
+
if length == 1 or len(address) < self.minFundingAddressLength or address.find(' ') > -1:
|
2339
|
+
raise InvalidAddress(self.id + ' address is invalid or has less than ' + str(self.minFundingAddressLength) + ' characters: "' + str(address) + '"')
|
2340
|
+
return address
|
1991
2341
|
|
1992
2342
|
def find_message_hashes(self, client, element: str):
|
1993
2343
|
result = []
|
@@ -1999,7 +2349,7 @@ class Exchange(object):
|
|
1999
2349
|
return result
|
2000
2350
|
|
2001
2351
|
def filter_by_limit(self, array: List[object], limit: Int = None, key: IndexType = 'timestamp', fromStart: bool = False):
|
2002
|
-
if self.
|
2352
|
+
if self.value_is_defined(limit):
|
2003
2353
|
arrayLength = len(array)
|
2004
2354
|
if arrayLength > 0:
|
2005
2355
|
ascending = True
|
@@ -2011,13 +2361,13 @@ class Exchange(object):
|
|
2011
2361
|
if fromStart:
|
2012
2362
|
if limit > arrayLength:
|
2013
2363
|
limit = arrayLength
|
2014
|
-
array = self.
|
2364
|
+
array = self.array_slice(array, 0, limit) if ascending else self.array_slice(array, -limit)
|
2015
2365
|
else:
|
2016
|
-
array = self.
|
2366
|
+
array = self.array_slice(array, -limit) if ascending else self.array_slice(array, 0, limit)
|
2017
2367
|
return array
|
2018
2368
|
|
2019
2369
|
def filter_by_since_limit(self, array: List[object], since: Int = None, limit: Int = None, key: IndexType = 'timestamp', tail=False):
|
2020
|
-
sinceIsDefined = self.
|
2370
|
+
sinceIsDefined = self.value_is_defined(since)
|
2021
2371
|
parsedArray = self.to_array(array)
|
2022
2372
|
result = parsedArray
|
2023
2373
|
if sinceIsDefined:
|
@@ -2028,15 +2378,15 @@ class Exchange(object):
|
|
2028
2378
|
if value and (value >= since):
|
2029
2379
|
result.append(entry)
|
2030
2380
|
if tail and limit is not None:
|
2031
|
-
return self.
|
2381
|
+
return self.array_slice(result, -limit)
|
2032
2382
|
# if the user provided a 'since' argument
|
2033
2383
|
# we want to limit the result starting from the 'since'
|
2034
2384
|
shouldFilterFromStart = not tail and sinceIsDefined
|
2035
2385
|
return self.filter_by_limit(result, limit, key, shouldFilterFromStart)
|
2036
2386
|
|
2037
2387
|
def filter_by_value_since_limit(self, array: List[object], field: IndexType, value=None, since: Int = None, limit: Int = None, key='timestamp', tail=False):
|
2038
|
-
valueIsDefined = self.
|
2039
|
-
sinceIsDefined = self.
|
2388
|
+
valueIsDefined = self.value_is_defined(value)
|
2389
|
+
sinceIsDefined = self.value_is_defined(since)
|
2040
2390
|
parsedArray = self.to_array(array)
|
2041
2391
|
result = parsedArray
|
2042
2392
|
# single-pass filter for both symbol and since
|
@@ -2047,15 +2397,19 @@ class Exchange(object):
|
|
2047
2397
|
entryFiledEqualValue = entry[field] == value
|
2048
2398
|
firstCondition = entryFiledEqualValue if valueIsDefined else True
|
2049
2399
|
entryKeyValue = self.safe_value(entry, key)
|
2050
|
-
entryKeyGESince = (entryKeyValue) and since and (entryKeyValue >= since)
|
2400
|
+
entryKeyGESince = (entryKeyValue) and (since is not None) and (entryKeyValue >= since)
|
2051
2401
|
secondCondition = entryKeyGESince if sinceIsDefined else True
|
2052
2402
|
if firstCondition and secondCondition:
|
2053
2403
|
result.append(entry)
|
2054
2404
|
if tail and limit is not None:
|
2055
|
-
return self.
|
2405
|
+
return self.array_slice(result, -limit)
|
2056
2406
|
return self.filter_by_limit(result, limit, key, sinceIsDefined)
|
2057
2407
|
|
2058
2408
|
def set_sandbox_mode(self, enabled: bool):
|
2409
|
+
"""
|
2410
|
+
set the sandbox mode for the exchange
|
2411
|
+
:param boolean enabled: True to enable sandbox mode, False to disable it
|
2412
|
+
"""
|
2059
2413
|
if enabled:
|
2060
2414
|
if 'test' in self.urls:
|
2061
2415
|
if isinstance(self.urls['api'], str):
|
@@ -2066,6 +2420,8 @@ class Exchange(object):
|
|
2066
2420
|
self.urls['api'] = self.clone(self.urls['test'])
|
2067
2421
|
else:
|
2068
2422
|
raise NotSupported(self.id + ' does not have a sandbox URL')
|
2423
|
+
# set flag
|
2424
|
+
self.isSandboxModeEnabled = True
|
2069
2425
|
elif 'apiBackup' in self.urls:
|
2070
2426
|
if isinstance(self.urls['api'], str):
|
2071
2427
|
self.urls['api'] = self.urls['apiBackup']
|
@@ -2073,6 +2429,8 @@ class Exchange(object):
|
|
2073
2429
|
self.urls['api'] = self.clone(self.urls['apiBackup'])
|
2074
2430
|
newUrls = self.omit(self.urls, 'apiBackup')
|
2075
2431
|
self.urls = newUrls
|
2432
|
+
# set flag
|
2433
|
+
self.isSandboxModeEnabled = False
|
2076
2434
|
|
2077
2435
|
def sign(self, path, api: Any = 'public', method='GET', params={}, headers: Any = None, body: Any = None):
|
2078
2436
|
return {}
|
@@ -2086,12 +2444,34 @@ class Exchange(object):
|
|
2086
2444
|
def fetch_trades_ws(self, symbol: str, since: Int = None, limit: Int = None, params={}):
|
2087
2445
|
raise NotSupported(self.id + ' fetchTradesWs() is not supported yet')
|
2088
2446
|
|
2447
|
+
def watch_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}):
|
2448
|
+
if self.has['watchLiquidationsForSymbols']:
|
2449
|
+
return self.watch_liquidations_for_symbols([symbol], since, limit, params)
|
2450
|
+
raise NotSupported(self.id + ' watchLiquidations() is not supported yet')
|
2451
|
+
|
2452
|
+
def watch_liquidations_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}):
|
2453
|
+
raise NotSupported(self.id + ' watchLiquidationsForSymbols() is not supported yet')
|
2454
|
+
|
2455
|
+
def watch_my_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}):
|
2456
|
+
if self.has['watchMyLiquidationsForSymbols']:
|
2457
|
+
return self.watch_my_liquidations_for_symbols([symbol], since, limit, params)
|
2458
|
+
raise NotSupported(self.id + ' watchMyLiquidations() is not supported yet')
|
2459
|
+
|
2460
|
+
def watch_my_liquidations_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}):
|
2461
|
+
raise NotSupported(self.id + ' watchMyLiquidationsForSymbols() is not supported yet')
|
2462
|
+
|
2089
2463
|
def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}):
|
2090
2464
|
raise NotSupported(self.id + ' watchTrades() is not supported yet')
|
2091
2465
|
|
2466
|
+
def un_watch_trades(self, symbol: str, params={}):
|
2467
|
+
raise NotSupported(self.id + ' unWatchTrades() is not supported yet')
|
2468
|
+
|
2092
2469
|
def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}):
|
2093
2470
|
raise NotSupported(self.id + ' watchTradesForSymbols() is not supported yet')
|
2094
2471
|
|
2472
|
+
def un_watch_trades_for_symbols(self, symbols: List[str], params={}):
|
2473
|
+
raise NotSupported(self.id + ' unWatchTradesForSymbols() is not supported yet')
|
2474
|
+
|
2095
2475
|
def watch_my_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}):
|
2096
2476
|
raise NotSupported(self.id + ' watchMyTradesForSymbols() is not supported yet')
|
2097
2477
|
|
@@ -2101,27 +2481,36 @@ class Exchange(object):
|
|
2101
2481
|
def watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], since: Int = None, limit: Int = None, params={}):
|
2102
2482
|
raise NotSupported(self.id + ' watchOHLCVForSymbols() is not supported yet')
|
2103
2483
|
|
2484
|
+
def un_watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], params={}):
|
2485
|
+
raise NotSupported(self.id + ' unWatchOHLCVForSymbols() is not supported yet')
|
2486
|
+
|
2104
2487
|
def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}):
|
2105
2488
|
raise NotSupported(self.id + ' watchOrderBookForSymbols() is not supported yet')
|
2106
2489
|
|
2107
|
-
def
|
2490
|
+
def un_watch_order_book_for_symbols(self, symbols: List[str], params={}):
|
2491
|
+
raise NotSupported(self.id + ' unWatchOrderBookForSymbols() is not supported yet')
|
2492
|
+
|
2493
|
+
def fetch_deposit_addresses(self, codes: Strings = None, params={}):
|
2108
2494
|
raise NotSupported(self.id + ' fetchDepositAddresses() is not supported yet')
|
2109
2495
|
|
2110
2496
|
def fetch_order_book(self, symbol: str, limit: Int = None, params={}):
|
2111
2497
|
raise NotSupported(self.id + ' fetchOrderBook() is not supported yet')
|
2112
2498
|
|
2499
|
+
def fetch_order_book_ws(self, symbol: str, limit: Int = None, params={}):
|
2500
|
+
raise NotSupported(self.id + ' fetchOrderBookWs() is not supported yet')
|
2501
|
+
|
2113
2502
|
def fetch_margin_mode(self, symbol: str, params={}):
|
2114
2503
|
if self.has['fetchMarginModes']:
|
2115
|
-
marginModes = self.
|
2504
|
+
marginModes = self.fetch_margin_modes([symbol], params)
|
2116
2505
|
return self.safe_dict(marginModes, symbol)
|
2117
2506
|
else:
|
2118
2507
|
raise NotSupported(self.id + ' fetchMarginMode() is not supported yet')
|
2119
2508
|
|
2120
|
-
def fetch_margin_modes(self, symbols:
|
2509
|
+
def fetch_margin_modes(self, symbols: Strings = None, params={}):
|
2121
2510
|
raise NotSupported(self.id + ' fetchMarginModes() is not supported yet')
|
2122
2511
|
|
2123
2512
|
def fetch_rest_order_book_safe(self, symbol, limit=None, params={}):
|
2124
|
-
fetchSnapshotMaxRetries = self.
|
2513
|
+
fetchSnapshotMaxRetries = self.handle_option('watchOrderBook', 'maxRetries', 3)
|
2125
2514
|
for i in range(0, fetchSnapshotMaxRetries):
|
2126
2515
|
try:
|
2127
2516
|
orderBook = self.fetch_order_book(symbol, limit, params)
|
@@ -2134,13 +2523,28 @@ class Exchange(object):
|
|
2134
2523
|
def watch_order_book(self, symbol: str, limit: Int = None, params={}):
|
2135
2524
|
raise NotSupported(self.id + ' watchOrderBook() is not supported yet')
|
2136
2525
|
|
2526
|
+
def un_watch_order_book(self, symbol: str, params={}):
|
2527
|
+
raise NotSupported(self.id + ' unWatchOrderBook() is not supported yet')
|
2528
|
+
|
2137
2529
|
def fetch_time(self, params={}):
|
2138
2530
|
raise NotSupported(self.id + ' fetchTime() is not supported yet')
|
2139
2531
|
|
2140
|
-
def fetch_trading_limits(self, symbols:
|
2532
|
+
def fetch_trading_limits(self, symbols: Strings = None, params={}):
|
2141
2533
|
raise NotSupported(self.id + ' fetchTradingLimits() is not supported yet')
|
2142
2534
|
|
2143
|
-
def
|
2535
|
+
def parse_currency(self, rawCurrency: dict):
|
2536
|
+
raise NotSupported(self.id + ' parseCurrency() is not supported yet')
|
2537
|
+
|
2538
|
+
def parse_currencies(self, rawCurrencies):
|
2539
|
+
result = {}
|
2540
|
+
arr = self.to_array(rawCurrencies)
|
2541
|
+
for i in range(0, len(arr)):
|
2542
|
+
parsed = self.parse_currency(arr[i])
|
2543
|
+
code = parsed['code']
|
2544
|
+
result[code] = parsed
|
2545
|
+
return result
|
2546
|
+
|
2547
|
+
def parse_market(self, market: dict):
|
2144
2548
|
raise NotSupported(self.id + ' parseMarket() is not supported yet')
|
2145
2549
|
|
2146
2550
|
def parse_markets(self, markets):
|
@@ -2149,28 +2553,28 @@ class Exchange(object):
|
|
2149
2553
|
result.append(self.parse_market(markets[i]))
|
2150
2554
|
return result
|
2151
2555
|
|
2152
|
-
def parse_ticker(self, ticker:
|
2556
|
+
def parse_ticker(self, ticker: dict, market: Market = None):
|
2153
2557
|
raise NotSupported(self.id + ' parseTicker() is not supported yet')
|
2154
2558
|
|
2155
2559
|
def parse_deposit_address(self, depositAddress, currency: Currency = None):
|
2156
2560
|
raise NotSupported(self.id + ' parseDepositAddress() is not supported yet')
|
2157
2561
|
|
2158
|
-
def parse_trade(self, trade:
|
2562
|
+
def parse_trade(self, trade: dict, market: Market = None):
|
2159
2563
|
raise NotSupported(self.id + ' parseTrade() is not supported yet')
|
2160
2564
|
|
2161
|
-
def parse_transaction(self, transaction, currency: Currency = None):
|
2565
|
+
def parse_transaction(self, transaction: dict, currency: Currency = None):
|
2162
2566
|
raise NotSupported(self.id + ' parseTransaction() is not supported yet')
|
2163
2567
|
|
2164
|
-
def parse_transfer(self, transfer, currency: Currency = None):
|
2568
|
+
def parse_transfer(self, transfer: dict, currency: Currency = None):
|
2165
2569
|
raise NotSupported(self.id + ' parseTransfer() is not supported yet')
|
2166
2570
|
|
2167
|
-
def parse_account(self, account):
|
2571
|
+
def parse_account(self, account: dict):
|
2168
2572
|
raise NotSupported(self.id + ' parseAccount() is not supported yet')
|
2169
2573
|
|
2170
|
-
def parse_ledger_entry(self, item, currency: Currency = None):
|
2574
|
+
def parse_ledger_entry(self, item: dict, currency: Currency = None):
|
2171
2575
|
raise NotSupported(self.id + ' parseLedgerEntry() is not supported yet')
|
2172
2576
|
|
2173
|
-
def parse_order(self, order, market: Market = None):
|
2577
|
+
def parse_order(self, order: dict, market: Market = None):
|
2174
2578
|
raise NotSupported(self.id + ' parseOrder() is not supported yet')
|
2175
2579
|
|
2176
2580
|
def fetch_cross_borrow_rates(self, params={}):
|
@@ -2182,33 +2586,39 @@ class Exchange(object):
|
|
2182
2586
|
def parse_market_leverage_tiers(self, info, market: Market = None):
|
2183
2587
|
raise NotSupported(self.id + ' parseMarketLeverageTiers() is not supported yet')
|
2184
2588
|
|
2185
|
-
def fetch_leverage_tiers(self, symbols:
|
2589
|
+
def fetch_leverage_tiers(self, symbols: Strings = None, params={}):
|
2186
2590
|
raise NotSupported(self.id + ' fetchLeverageTiers() is not supported yet')
|
2187
2591
|
|
2188
|
-
def parse_position(self, position, market: Market = None):
|
2592
|
+
def parse_position(self, position: dict, market: Market = None):
|
2189
2593
|
raise NotSupported(self.id + ' parsePosition() is not supported yet')
|
2190
2594
|
|
2191
2595
|
def parse_funding_rate_history(self, info, market: Market = None):
|
2192
2596
|
raise NotSupported(self.id + ' parseFundingRateHistory() is not supported yet')
|
2193
2597
|
|
2194
|
-
def parse_borrow_interest(self, info, market: Market = None):
|
2598
|
+
def parse_borrow_interest(self, info: dict, market: Market = None):
|
2195
2599
|
raise NotSupported(self.id + ' parseBorrowInterest() is not supported yet')
|
2196
2600
|
|
2197
|
-
def
|
2601
|
+
def parse_isolated_borrow_rate(self, info: dict, market: Market = None):
|
2602
|
+
raise NotSupported(self.id + ' parseIsolatedBorrowRate() is not supported yet')
|
2603
|
+
|
2604
|
+
def parse_ws_trade(self, trade: dict, market: Market = None):
|
2198
2605
|
raise NotSupported(self.id + ' parseWsTrade() is not supported yet')
|
2199
2606
|
|
2200
|
-
def parse_ws_order(self, order, market: Market = None):
|
2607
|
+
def parse_ws_order(self, order: dict, market: Market = None):
|
2201
2608
|
raise NotSupported(self.id + ' parseWsOrder() is not supported yet')
|
2202
2609
|
|
2203
|
-
def parse_ws_order_trade(self, trade, market: Market = None):
|
2610
|
+
def parse_ws_order_trade(self, trade: dict, market: Market = None):
|
2204
2611
|
raise NotSupported(self.id + ' parseWsOrderTrade() is not supported yet')
|
2205
2612
|
|
2206
2613
|
def parse_ws_ohlcv(self, ohlcv, market: Market = None):
|
2207
2614
|
return self.parse_ohlcv(ohlcv, market)
|
2208
2615
|
|
2209
|
-
def fetch_funding_rates(self, symbols:
|
2616
|
+
def fetch_funding_rates(self, symbols: Strings = None, params={}):
|
2210
2617
|
raise NotSupported(self.id + ' fetchFundingRates() is not supported yet')
|
2211
2618
|
|
2619
|
+
def fetch_funding_intervals(self, symbols: Strings = None, params={}):
|
2620
|
+
raise NotSupported(self.id + ' fetchFundingIntervals() is not supported yet')
|
2621
|
+
|
2212
2622
|
def watch_funding_rate(self, symbol: str, params={}):
|
2213
2623
|
raise NotSupported(self.id + ' watchFundingRate() is not supported yet')
|
2214
2624
|
|
@@ -2216,7 +2626,7 @@ class Exchange(object):
|
|
2216
2626
|
raise NotSupported(self.id + ' watchFundingRates() is not supported yet')
|
2217
2627
|
|
2218
2628
|
def watch_funding_rates_for_symbols(self, symbols: List[str], params={}):
|
2219
|
-
return self.
|
2629
|
+
return self.watch_funding_rates(symbols, params)
|
2220
2630
|
|
2221
2631
|
def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}):
|
2222
2632
|
raise NotSupported(self.id + ' transfer() is not supported yet')
|
@@ -2232,12 +2642,12 @@ class Exchange(object):
|
|
2232
2642
|
|
2233
2643
|
def fetch_leverage(self, symbol: str, params={}):
|
2234
2644
|
if self.has['fetchLeverages']:
|
2235
|
-
leverages = self.
|
2645
|
+
leverages = self.fetch_leverages([symbol], params)
|
2236
2646
|
return self.safe_dict(leverages, symbol)
|
2237
2647
|
else:
|
2238
2648
|
raise NotSupported(self.id + ' fetchLeverage() is not supported yet')
|
2239
2649
|
|
2240
|
-
def fetch_leverages(self, symbols:
|
2650
|
+
def fetch_leverages(self, symbols: Strings = None, params={}):
|
2241
2651
|
raise NotSupported(self.id + ' fetchLeverages() is not supported yet')
|
2242
2652
|
|
2243
2653
|
def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
|
@@ -2252,6 +2662,24 @@ class Exchange(object):
|
|
2252
2662
|
def set_margin(self, symbol: str, amount: float, params={}):
|
2253
2663
|
raise NotSupported(self.id + ' setMargin() is not supported yet')
|
2254
2664
|
|
2665
|
+
def fetch_long_short_ratio(self, symbol: str, timeframe: Str = None, params={}):
|
2666
|
+
raise NotSupported(self.id + ' fetchLongShortRatio() is not supported yet')
|
2667
|
+
|
2668
|
+
def fetch_long_short_ratio_history(self, symbol: Str = None, timeframe: Str = None, since: Int = None, limit: Int = None, params={}):
|
2669
|
+
raise NotSupported(self.id + ' fetchLongShortRatioHistory() is not supported yet')
|
2670
|
+
|
2671
|
+
def fetch_margin_adjustment_history(self, symbol: Str = None, type: Str = None, since: Num = None, limit: Num = None, params={}):
|
2672
|
+
"""
|
2673
|
+
fetches the history of margin added or reduced from contract isolated positions
|
2674
|
+
:param str [symbol]: unified market symbol
|
2675
|
+
:param str [type]: "add" or "reduce"
|
2676
|
+
:param int [since]: timestamp in ms of the earliest change to fetch
|
2677
|
+
:param int [limit]: the maximum amount of changes to fetch
|
2678
|
+
:param dict params: extra parameters specific to the exchange api endpoint
|
2679
|
+
:returns dict[]: a list of `margin structures <https://docs.ccxt.com/#/?id=margin-loan-structure>`
|
2680
|
+
"""
|
2681
|
+
raise NotSupported(self.id + ' fetchMarginAdjustmentHistory() is not supported yet')
|
2682
|
+
|
2255
2683
|
def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
|
2256
2684
|
raise NotSupported(self.id + ' setMarginMode() is not supported yet')
|
2257
2685
|
|
@@ -2264,6 +2692,9 @@ class Exchange(object):
|
|
2264
2692
|
def fetch_open_interest(self, symbol: str, params={}):
|
2265
2693
|
raise NotSupported(self.id + ' fetchOpenInterest() is not supported yet')
|
2266
2694
|
|
2695
|
+
def fetch_open_interests(self, symbols: Strings = None, params={}):
|
2696
|
+
raise NotSupported(self.id + ' fetchOpenInterests() is not supported yet')
|
2697
|
+
|
2267
2698
|
def sign_in(self, params={}):
|
2268
2699
|
raise NotSupported(self.id + ' signIn() is not supported yet')
|
2269
2700
|
|
@@ -2273,7 +2704,7 @@ class Exchange(object):
|
|
2273
2704
|
def parse_to_int(self, number):
|
2274
2705
|
# Solve Common intmisuse ex: int((since / str(1000)))
|
2275
2706
|
# using a number which is not valid in ts
|
2276
|
-
stringifiedNumber =
|
2707
|
+
stringifiedNumber = self.number_to_string(number)
|
2277
2708
|
convertedNumber = float(stringifiedNumber)
|
2278
2709
|
return int(convertedNumber)
|
2279
2710
|
|
@@ -2293,8 +2724,101 @@ class Exchange(object):
|
|
2293
2724
|
res = self.parse_to_numeric((value % 1))
|
2294
2725
|
return res == 0
|
2295
2726
|
|
2727
|
+
def safe_integer_omit_zero(self, obj: object, key: IndexType, defaultValue: Int = None):
|
2728
|
+
timestamp = self.safe_integer(obj, key, defaultValue)
|
2729
|
+
if timestamp is None or timestamp == 0:
|
2730
|
+
return None
|
2731
|
+
return timestamp
|
2732
|
+
|
2296
2733
|
def after_construct(self):
|
2297
2734
|
self.create_networks_by_id_object()
|
2735
|
+
self.features_generator()
|
2736
|
+
|
2737
|
+
def features_generator(self):
|
2738
|
+
#
|
2739
|
+
# the exchange-specific features can be something like self, where we support 'string' aliases too:
|
2740
|
+
#
|
2741
|
+
# {
|
2742
|
+
# 'myItem' : {
|
2743
|
+
# 'createOrder' : {...},
|
2744
|
+
# 'fetchOrders' : {...},
|
2745
|
+
# },
|
2746
|
+
# 'swap': {
|
2747
|
+
# 'linear': 'myItem',
|
2748
|
+
# 'inverse': 'myItem',
|
2749
|
+
# },
|
2750
|
+
# 'future': {
|
2751
|
+
# 'linear': 'myItem',
|
2752
|
+
# 'inverse': 'myItem',
|
2753
|
+
# }
|
2754
|
+
# }
|
2755
|
+
#
|
2756
|
+
#
|
2757
|
+
#
|
2758
|
+
# self method would regenerate the blank features tree, eg:
|
2759
|
+
#
|
2760
|
+
# {
|
2761
|
+
# "spot": {
|
2762
|
+
# "createOrder": None,
|
2763
|
+
# "fetchBalance": None,
|
2764
|
+
# ...
|
2765
|
+
# },
|
2766
|
+
# "swap": {
|
2767
|
+
# ...
|
2768
|
+
# }
|
2769
|
+
# }
|
2770
|
+
#
|
2771
|
+
if self.features is None:
|
2772
|
+
return
|
2773
|
+
# reconstruct
|
2774
|
+
initialFeatures = self.features
|
2775
|
+
self.features = {}
|
2776
|
+
unifiedMarketTypes = ['spot', 'swap', 'future', 'option']
|
2777
|
+
subTypes = ['linear', 'inverse']
|
2778
|
+
# atm only support basic methods, eg: 'createOrder', 'fetchOrder', 'fetchOrders', 'fetchMyTrades'
|
2779
|
+
for i in range(0, len(unifiedMarketTypes)):
|
2780
|
+
marketType = unifiedMarketTypes[i]
|
2781
|
+
# if marketType is not filled for self exchange, don't add that in `features`
|
2782
|
+
if not (marketType in initialFeatures):
|
2783
|
+
self.features[marketType] = None
|
2784
|
+
else:
|
2785
|
+
if marketType == 'spot':
|
2786
|
+
self.features[marketType] = self.features_mapper(initialFeatures, marketType, None)
|
2787
|
+
else:
|
2788
|
+
self.features[marketType] = {}
|
2789
|
+
for j in range(0, len(subTypes)):
|
2790
|
+
subType = subTypes[j]
|
2791
|
+
self.features[marketType][subType] = self.features_mapper(initialFeatures, marketType, subType)
|
2792
|
+
|
2793
|
+
def features_mapper(self, initialFeatures: Any, marketType: Str, subType: Str = None):
|
2794
|
+
featuresObj = initialFeatures[marketType][subType] if (subType is not None) else initialFeatures[marketType]
|
2795
|
+
# if exchange does not have that market-type(eg. future>inverse)
|
2796
|
+
if featuresObj is None:
|
2797
|
+
return None
|
2798
|
+
extendsStr: Str = self.safe_string(featuresObj, 'extends')
|
2799
|
+
if extendsStr is not None:
|
2800
|
+
featuresObj = self.omit(featuresObj, 'extends')
|
2801
|
+
extendObj = self.features_mapper(initialFeatures, extendsStr)
|
2802
|
+
featuresObj = self.deep_extend(extendObj, featuresObj)
|
2803
|
+
#
|
2804
|
+
# corrections
|
2805
|
+
#
|
2806
|
+
if 'createOrder' in featuresObj:
|
2807
|
+
value = self.safe_dict(featuresObj['createOrder'], 'attachedStopLossTakeProfit')
|
2808
|
+
if value is not None:
|
2809
|
+
featuresObj['createOrder']['stopLoss'] = value
|
2810
|
+
featuresObj['createOrder']['takeProfit'] = value
|
2811
|
+
# for spot, default 'hedged' to False
|
2812
|
+
if marketType == 'spot':
|
2813
|
+
featuresObj['createOrder']['hedged'] = False
|
2814
|
+
# default 'GTC' to True
|
2815
|
+
gtcValue = self.safe_bool(featuresObj['createOrder']['timeInForce'], 'gtc')
|
2816
|
+
if gtcValue is None:
|
2817
|
+
featuresObj['createOrder']['timeInForce']['GTC'] = True
|
2818
|
+
return featuresObj
|
2819
|
+
|
2820
|
+
def orderbook_checksum_message(self, symbol: Str):
|
2821
|
+
return symbol + ' = False'
|
2298
2822
|
|
2299
2823
|
def create_networks_by_id_object(self):
|
2300
2824
|
# automatically generate network-id-to-code mappings
|
@@ -2307,6 +2831,7 @@ class Exchange(object):
|
|
2307
2831
|
'ETH': {'ERC20': 'ETH'},
|
2308
2832
|
'TRX': {'TRC20': 'TRX'},
|
2309
2833
|
'CRO': {'CRC20': 'CRONOS'},
|
2834
|
+
'BRC20': {'BRC20': 'BTC'},
|
2310
2835
|
},
|
2311
2836
|
}
|
2312
2837
|
|
@@ -2377,7 +2902,7 @@ class Exchange(object):
|
|
2377
2902
|
},
|
2378
2903
|
}, currency)
|
2379
2904
|
|
2380
|
-
def safe_market_structure(self, market=None):
|
2905
|
+
def safe_market_structure(self, market: dict = None):
|
2381
2906
|
cleanStructure = {
|
2382
2907
|
'id': None,
|
2383
2908
|
'lowercaseId': None,
|
@@ -2432,6 +2957,10 @@ class Exchange(object):
|
|
2432
2957
|
'max': None,
|
2433
2958
|
},
|
2434
2959
|
},
|
2960
|
+
'marginModes': {
|
2961
|
+
'cross': None,
|
2962
|
+
'isolated': None,
|
2963
|
+
},
|
2435
2964
|
'created': None,
|
2436
2965
|
'info': None,
|
2437
2966
|
}
|
@@ -2537,7 +3066,7 @@ class Exchange(object):
|
|
2537
3066
|
superWithRestDescribe = self.deep_extend(extendedRestDescribe, wsBaseDescribe)
|
2538
3067
|
return superWithRestDescribe
|
2539
3068
|
|
2540
|
-
def safe_balance(self, balance:
|
3069
|
+
def safe_balance(self, balance: dict):
|
2541
3070
|
balances = self.omit(balance, ['info', 'timestamp', 'datetime', 'free', 'used', 'total'])
|
2542
3071
|
codes = list(balances.keys())
|
2543
3072
|
balance['free'] = {}
|
@@ -2571,7 +3100,7 @@ class Exchange(object):
|
|
2571
3100
|
balance['debt'] = debtBalance
|
2572
3101
|
return balance
|
2573
3102
|
|
2574
|
-
def safe_order(self, order:
|
3103
|
+
def safe_order(self, order: dict, market: Market = None):
|
2575
3104
|
# parses numbers
|
2576
3105
|
# * it is important pass the trades rawTrades
|
2577
3106
|
amount = self.omit_zero(self.safe_string(order, 'amount'))
|
@@ -2595,11 +3124,13 @@ class Exchange(object):
|
|
2595
3124
|
shouldParseFees = parseFee or parseFees
|
2596
3125
|
fees = self.safe_list(order, 'fees', [])
|
2597
3126
|
trades = []
|
3127
|
+
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))
|
2598
3128
|
if parseFilled or parseCost or shouldParseFees:
|
2599
3129
|
rawTrades = self.safe_value(order, 'trades', trades)
|
2600
|
-
oldNumber = self.number
|
3130
|
+
# oldNumber = self.number
|
2601
3131
|
# we parse trades here!
|
2602
|
-
self
|
3132
|
+
# i don't think self is needed anymore
|
3133
|
+
# self.number = str
|
2603
3134
|
firstTrade = self.safe_value(rawTrades, 0)
|
2604
3135
|
# parse trades if they haven't already been parsed
|
2605
3136
|
tradesAreParsed = ((firstTrade is not None) and ('info' in firstTrade) and ('id' in firstTrade))
|
@@ -2607,7 +3138,7 @@ class Exchange(object):
|
|
2607
3138
|
trades = self.parse_trades(rawTrades, market)
|
2608
3139
|
else:
|
2609
3140
|
trades = rawTrades
|
2610
|
-
self.number = oldNumber
|
3141
|
+
# self.number = oldNumber; why parse trades if you read the value using `safeString` ?
|
2611
3142
|
tradesLength = 0
|
2612
3143
|
isArray = isinstance(trades, list)
|
2613
3144
|
if isArray:
|
@@ -2745,7 +3276,7 @@ class Exchange(object):
|
|
2745
3276
|
postOnly = self.safe_value(order, 'postOnly')
|
2746
3277
|
# timeInForceHandling
|
2747
3278
|
if timeInForce is None:
|
2748
|
-
if self.safe_string(order, 'type') == 'market':
|
3279
|
+
if not isTriggerOrSLTpOrder and (self.safe_string(order, 'type') == 'market'):
|
2749
3280
|
timeInForce = 'IOC'
|
2750
3281
|
# allow postOnly override
|
2751
3282
|
if postOnly:
|
@@ -2875,7 +3406,7 @@ class Exchange(object):
|
|
2875
3406
|
'cost': self.parse_number(cost),
|
2876
3407
|
}
|
2877
3408
|
|
2878
|
-
def safe_liquidation(self, liquidation:
|
3409
|
+
def safe_liquidation(self, liquidation: dict, market: Market = None):
|
2879
3410
|
contracts = self.safe_string(liquidation, 'contracts')
|
2880
3411
|
contractSize = self.safe_string(market, 'contractSize')
|
2881
3412
|
price = self.safe_string(liquidation, 'price')
|
@@ -2892,7 +3423,7 @@ class Exchange(object):
|
|
2892
3423
|
liquidation['quoteValue'] = self.parse_number(quoteValue)
|
2893
3424
|
return liquidation
|
2894
3425
|
|
2895
|
-
def safe_trade(self, trade:
|
3426
|
+
def safe_trade(self, trade: dict, market: Market = None):
|
2896
3427
|
amount = self.safe_string(trade, 'amount')
|
2897
3428
|
price = self.safe_string(trade, 'price')
|
2898
3429
|
cost = self.safe_string(trade, 'cost')
|
@@ -2906,40 +3437,62 @@ class Exchange(object):
|
|
2906
3437
|
multiplyPrice = Precise.string_div('1', price)
|
2907
3438
|
multiplyPrice = Precise.string_mul(multiplyPrice, contractSize)
|
2908
3439
|
cost = Precise.string_mul(multiplyPrice, amount)
|
2909
|
-
|
2910
|
-
|
2911
|
-
|
2912
|
-
fees = []
|
2913
|
-
fee = self.safe_value(trade, 'fee')
|
2914
|
-
if shouldParseFees:
|
2915
|
-
reducedFees = self.reduce_fees_by_currency(fees) if self.reduceFees else fees
|
2916
|
-
reducedLength = len(reducedFees)
|
2917
|
-
for i in range(0, reducedLength):
|
2918
|
-
reducedFees[i]['cost'] = self.safe_number(reducedFees[i], 'cost')
|
2919
|
-
if 'rate' in reducedFees[i]:
|
2920
|
-
reducedFees[i]['rate'] = self.safe_number(reducedFees[i], 'rate')
|
2921
|
-
if not parseFee and (reducedLength == 0):
|
2922
|
-
# copy fee to avoid modification by reference
|
2923
|
-
feeCopy = self.deep_extend(fee)
|
2924
|
-
feeCopy['cost'] = self.safe_number(feeCopy, 'cost')
|
2925
|
-
if 'rate' in feeCopy:
|
2926
|
-
feeCopy['rate'] = self.safe_number(feeCopy, 'rate')
|
2927
|
-
reducedFees.append(feeCopy)
|
2928
|
-
if parseFees:
|
2929
|
-
trade['fees'] = reducedFees
|
2930
|
-
if parseFee and (reducedLength == 1):
|
2931
|
-
trade['fee'] = reducedFees[0]
|
2932
|
-
tradeFee = self.safe_value(trade, 'fee')
|
2933
|
-
if tradeFee is not None:
|
2934
|
-
tradeFee['cost'] = self.safe_number(tradeFee, 'cost')
|
2935
|
-
if 'rate' in tradeFee:
|
2936
|
-
tradeFee['rate'] = self.safe_number(tradeFee, 'rate')
|
2937
|
-
trade['fee'] = tradeFee
|
3440
|
+
resultFee, resultFees = self.parsed_fee_and_fees(trade)
|
3441
|
+
trade['fee'] = resultFee
|
3442
|
+
trade['fees'] = resultFees
|
2938
3443
|
trade['amount'] = self.parse_number(amount)
|
2939
3444
|
trade['price'] = self.parse_number(price)
|
2940
3445
|
trade['cost'] = self.parse_number(cost)
|
2941
3446
|
return trade
|
2942
3447
|
|
3448
|
+
def parsed_fee_and_fees(self, container: Any):
|
3449
|
+
fee = self.safe_dict(container, 'fee')
|
3450
|
+
fees = self.safe_list(container, 'fees')
|
3451
|
+
feeDefined = fee is not None
|
3452
|
+
feesDefined = fees is not None
|
3453
|
+
# parsing only if at least one of them is defined
|
3454
|
+
shouldParseFees = (feeDefined or feesDefined)
|
3455
|
+
if shouldParseFees:
|
3456
|
+
if feeDefined:
|
3457
|
+
fee = self.parse_fee_numeric(fee)
|
3458
|
+
if not feesDefined:
|
3459
|
+
# just set it directly, no further processing needed
|
3460
|
+
fees = [fee]
|
3461
|
+
# 'fees' were set, so reparse them
|
3462
|
+
reducedFees = self.reduce_fees_by_currency(fees) if self.reduceFees else fees
|
3463
|
+
reducedLength = len(reducedFees)
|
3464
|
+
for i in range(0, reducedLength):
|
3465
|
+
reducedFees[i] = self.parse_fee_numeric(reducedFees[i])
|
3466
|
+
fees = reducedFees
|
3467
|
+
if reducedLength == 1:
|
3468
|
+
fee = reducedFees[0]
|
3469
|
+
elif reducedLength == 0:
|
3470
|
+
fee = None
|
3471
|
+
# in case `fee & fees` are None, set `fees` array
|
3472
|
+
if fee is None:
|
3473
|
+
fee = {
|
3474
|
+
'cost': None,
|
3475
|
+
'currency': None,
|
3476
|
+
}
|
3477
|
+
if fees is None:
|
3478
|
+
fees = []
|
3479
|
+
return [fee, fees]
|
3480
|
+
|
3481
|
+
def parse_fee_numeric(self, fee: Any):
|
3482
|
+
fee['cost'] = self.safe_number(fee, 'cost') # ensure numeric
|
3483
|
+
if 'rate' in fee:
|
3484
|
+
fee['rate'] = self.safe_number(fee, 'rate')
|
3485
|
+
return fee
|
3486
|
+
|
3487
|
+
def find_nearest_ceiling(self, arr: List[float], providedValue: float):
|
3488
|
+
# i.e. findNearestCeiling([10, 30, 50], 23) returns 30
|
3489
|
+
length = len(arr)
|
3490
|
+
for i in range(0, length):
|
3491
|
+
current = arr[i]
|
3492
|
+
if providedValue <= current:
|
3493
|
+
return current
|
3494
|
+
return arr[length - 1]
|
3495
|
+
|
2943
3496
|
def invert_flat_string_dictionary(self, dict):
|
2944
3497
|
reversed = {}
|
2945
3498
|
keys = list(dict.keys())
|
@@ -2999,12 +3552,13 @@ class Exchange(object):
|
|
2999
3552
|
reduced = {}
|
3000
3553
|
for i in range(0, len(fees)):
|
3001
3554
|
fee = fees[i]
|
3002
|
-
|
3555
|
+
code = self.safe_string(fee, 'currency')
|
3556
|
+
feeCurrencyCode = code is not code if None else str(i)
|
3003
3557
|
if feeCurrencyCode is not None:
|
3004
3558
|
rate = self.safe_string(fee, 'rate')
|
3005
|
-
cost = self.
|
3006
|
-
if
|
3007
|
-
# omit
|
3559
|
+
cost = self.safe_string(fee, 'cost')
|
3560
|
+
if cost is None:
|
3561
|
+
# omit None cost, does not make sense, however, don't omit '0' costs, still make sense
|
3008
3562
|
continue
|
3009
3563
|
if not (feeCurrencyCode in reduced):
|
3010
3564
|
reduced[feeCurrencyCode] = {}
|
@@ -3013,7 +3567,7 @@ class Exchange(object):
|
|
3013
3567
|
reduced[feeCurrencyCode][rateKey]['cost'] = Precise.string_add(reduced[feeCurrencyCode][rateKey]['cost'], cost)
|
3014
3568
|
else:
|
3015
3569
|
reduced[feeCurrencyCode][rateKey] = {
|
3016
|
-
'currency':
|
3570
|
+
'currency': code,
|
3017
3571
|
'cost': cost,
|
3018
3572
|
}
|
3019
3573
|
if rate is not None:
|
@@ -3025,7 +3579,7 @@ class Exchange(object):
|
|
3025
3579
|
result = self.array_concat(result, reducedFeeValues)
|
3026
3580
|
return result
|
3027
3581
|
|
3028
|
-
def safe_ticker(self, ticker:
|
3582
|
+
def safe_ticker(self, ticker: dict, market: Market = None):
|
3029
3583
|
open = self.omit_zero(self.safe_string(ticker, 'open'))
|
3030
3584
|
close = self.omit_zero(self.safe_string(ticker, 'close'))
|
3031
3585
|
last = self.omit_zero(self.safe_string(ticker, 'last'))
|
@@ -3045,7 +3599,13 @@ class Exchange(object):
|
|
3045
3599
|
if change is None:
|
3046
3600
|
change = Precise.string_sub(last, open)
|
3047
3601
|
if average is None:
|
3048
|
-
|
3602
|
+
precision = 18
|
3603
|
+
if market is not None and self.is_tick_precision():
|
3604
|
+
marketPrecision = self.safe_dict(market, 'precision')
|
3605
|
+
precisionPrice = self.safe_string(marketPrecision, 'price')
|
3606
|
+
if precisionPrice is not None:
|
3607
|
+
precision = self.precision_from_string(precisionPrice)
|
3608
|
+
average = Precise.string_div(Precise.string_add(last, open), '2', precision)
|
3049
3609
|
if (percentage is None) and (change is not None) and (open is not None) and Precise.string_gt(open, '0'):
|
3050
3610
|
percentage = Precise.string_mul(Precise.string_div(change, open), '100')
|
3051
3611
|
if (change is None) and (percentage is not None) and (open is not None):
|
@@ -3055,15 +3615,15 @@ class Exchange(object):
|
|
3055
3615
|
# timestamp and symbol operations don't belong in safeTicker
|
3056
3616
|
# they should be done in the derived classes
|
3057
3617
|
return self.extend(ticker, {
|
3058
|
-
'bid': self.parse_number(self.omit_zero(self.
|
3618
|
+
'bid': self.parse_number(self.omit_zero(self.safe_string(ticker, 'bid'))),
|
3059
3619
|
'bidVolume': self.safe_number(ticker, 'bidVolume'),
|
3060
|
-
'ask': self.parse_number(self.omit_zero(self.
|
3620
|
+
'ask': self.parse_number(self.omit_zero(self.safe_string(ticker, 'ask'))),
|
3061
3621
|
'askVolume': self.safe_number(ticker, 'askVolume'),
|
3062
3622
|
'high': self.parse_number(self.omit_zero(self.safe_string(ticker, 'high'))),
|
3063
|
-
'low': self.parse_number(self.omit_zero(self.
|
3064
|
-
'open': self.parse_number(self.omit_zero(
|
3065
|
-
'close': self.parse_number(self.omit_zero(
|
3066
|
-
'last': self.parse_number(self.omit_zero(
|
3623
|
+
'low': self.parse_number(self.omit_zero(self.safe_string(ticker, 'low'))),
|
3624
|
+
'open': self.parse_number(self.omit_zero(open)),
|
3625
|
+
'close': self.parse_number(self.omit_zero(close)),
|
3626
|
+
'last': self.parse_number(self.omit_zero(last)),
|
3067
3627
|
'change': self.parse_number(change),
|
3068
3628
|
'percentage': self.parse_number(percentage),
|
3069
3629
|
'average': self.parse_number(average),
|
@@ -3071,15 +3631,17 @@ class Exchange(object):
|
|
3071
3631
|
'baseVolume': self.parse_number(baseVolume),
|
3072
3632
|
'quoteVolume': self.parse_number(quoteVolume),
|
3073
3633
|
'previousClose': self.safe_number(ticker, 'previousClose'),
|
3634
|
+
'indexPrice': self.safe_number(ticker, 'indexPrice'),
|
3635
|
+
'markPrice': self.safe_number(ticker, 'markPrice'),
|
3074
3636
|
})
|
3075
3637
|
|
3076
|
-
def fetch_borrow_rate(self, code: str, amount, params={}):
|
3638
|
+
def fetch_borrow_rate(self, code: str, amount: float, params={}):
|
3077
3639
|
raise NotSupported(self.id + ' fetchBorrowRate is deprecated, please use fetchCrossBorrowRate or fetchIsolatedBorrowRate instead')
|
3078
3640
|
|
3079
|
-
def repay_cross_margin(self, code: str, amount, params={}):
|
3641
|
+
def repay_cross_margin(self, code: str, amount: float, params={}):
|
3080
3642
|
raise NotSupported(self.id + ' repayCrossMargin is not support yet')
|
3081
3643
|
|
3082
|
-
def repay_isolated_margin(self, symbol: str, code: str, amount, params={}):
|
3644
|
+
def repay_isolated_margin(self, symbol: str, code: str, amount: float, params={}):
|
3083
3645
|
raise NotSupported(self.id + ' repayIsolatedMargin is not support yet')
|
3084
3646
|
|
3085
3647
|
def borrow_cross_margin(self, code: str, amount: float, params={}):
|
@@ -3088,10 +3650,10 @@ class Exchange(object):
|
|
3088
3650
|
def borrow_isolated_margin(self, symbol: str, code: str, amount: float, params={}):
|
3089
3651
|
raise NotSupported(self.id + ' borrowIsolatedMargin is not support yet')
|
3090
3652
|
|
3091
|
-
def borrow_margin(self, code: str, amount, symbol: Str = None, params={}):
|
3653
|
+
def borrow_margin(self, code: str, amount: float, symbol: Str = None, params={}):
|
3092
3654
|
raise NotSupported(self.id + ' borrowMargin is deprecated, please use borrowCrossMargin or borrowIsolatedMargin instead')
|
3093
3655
|
|
3094
|
-
def repay_margin(self, code: str, amount, symbol: Str = None, params={}):
|
3656
|
+
def repay_margin(self, code: str, amount: float, symbol: Str = None, params={}):
|
3095
3657
|
raise NotSupported(self.id + ' repayMargin is deprecated, please use repayCrossMargin or repayIsolatedMargin instead')
|
3096
3658
|
|
3097
3659
|
def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
@@ -3137,7 +3699,7 @@ class Exchange(object):
|
|
3137
3699
|
result[close] = []
|
3138
3700
|
result[volume] = []
|
3139
3701
|
for i in range(0, len(ohlcvs)):
|
3140
|
-
ts = ohlcvs[i][0] if ms else self.
|
3702
|
+
ts = ohlcvs[i][0] if ms else self.parse_to_int(ohlcvs[i][0] / 1000)
|
3141
3703
|
result[timestamp].append(ts)
|
3142
3704
|
result[open].append(ohlcvs[i][1])
|
3143
3705
|
result[high].append(ohlcvs[i][2])
|
@@ -3187,7 +3749,7 @@ class Exchange(object):
|
|
3187
3749
|
else:
|
3188
3750
|
raise BadResponse(errorMessage)
|
3189
3751
|
|
3190
|
-
def market_ids(self, symbols):
|
3752
|
+
def market_ids(self, symbols: Strings = None):
|
3191
3753
|
if symbols is None:
|
3192
3754
|
return symbols
|
3193
3755
|
result = []
|
@@ -3195,7 +3757,23 @@ class Exchange(object):
|
|
3195
3757
|
result.append(self.market_id(symbols[i]))
|
3196
3758
|
return result
|
3197
3759
|
|
3198
|
-
def
|
3760
|
+
def currency_ids(self, codes: Strings = None):
|
3761
|
+
if codes is None:
|
3762
|
+
return codes
|
3763
|
+
result = []
|
3764
|
+
for i in range(0, len(codes)):
|
3765
|
+
result.append(self.currency_id(codes[i]))
|
3766
|
+
return result
|
3767
|
+
|
3768
|
+
def markets_for_symbols(self, symbols: Strings = None):
|
3769
|
+
if symbols is None:
|
3770
|
+
return symbols
|
3771
|
+
result = []
|
3772
|
+
for i in range(0, len(symbols)):
|
3773
|
+
result.append(self.market(symbols[i]))
|
3774
|
+
return result
|
3775
|
+
|
3776
|
+
def market_symbols(self, symbols: Strings = None, type: Str = None, allowEmpty=True, sameTypeOnly=False, sameSubTypeOnly=False):
|
3199
3777
|
if symbols is None:
|
3200
3778
|
if not allowEmpty:
|
3201
3779
|
raise ArgumentsRequired(self.id + ' empty list of symbols is not supported')
|
@@ -3225,7 +3803,7 @@ class Exchange(object):
|
|
3225
3803
|
result.append(symbol)
|
3226
3804
|
return result
|
3227
3805
|
|
3228
|
-
def market_codes(self, codes):
|
3806
|
+
def market_codes(self, codes: Strings = None):
|
3229
3807
|
if codes is None:
|
3230
3808
|
return codes
|
3231
3809
|
result = []
|
@@ -3271,7 +3849,7 @@ class Exchange(object):
|
|
3271
3849
|
|
3272
3850
|
def network_code_to_id(self, networkCode: str, currencyCode: Str = None):
|
3273
3851
|
"""
|
3274
|
-
|
3852
|
+
@ignore
|
3275
3853
|
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.
|
3276
3854
|
:param str networkCode: unified network code
|
3277
3855
|
: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
|
@@ -3286,7 +3864,7 @@ class Exchange(object):
|
|
3286
3864
|
if currencyCode is None:
|
3287
3865
|
currencies = list(self.currencies.values())
|
3288
3866
|
for i in range(0, len(currencies)):
|
3289
|
-
currency = [i]
|
3867
|
+
currency = currencies[i]
|
3290
3868
|
networks = self.safe_dict(currency, 'networks')
|
3291
3869
|
network = self.safe_dict(networks, networkCode)
|
3292
3870
|
networkId = self.safe_string(network, 'id')
|
@@ -3317,9 +3895,9 @@ class Exchange(object):
|
|
3317
3895
|
networkId = networkCode
|
3318
3896
|
return networkId
|
3319
3897
|
|
3320
|
-
def network_id_to_code(self, networkId:
|
3898
|
+
def network_id_to_code(self, networkId: Str = None, currencyCode: Str = None):
|
3321
3899
|
"""
|
3322
|
-
|
3900
|
+
@ignore
|
3323
3901
|
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.
|
3324
3902
|
:param str networkId: exchange specific network id/title, like: TRON, Trc-20, usdt-erc20, etc
|
3325
3903
|
: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
|
@@ -3352,7 +3930,7 @@ class Exchange(object):
|
|
3352
3930
|
defaultNetworkCode = defaultNetworks[currencyCode]
|
3353
3931
|
else:
|
3354
3932
|
# 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)
|
3355
|
-
defaultNetwork = self.
|
3933
|
+
defaultNetwork = self.safe_string(self.options, 'defaultNetwork')
|
3356
3934
|
if defaultNetwork is not None:
|
3357
3935
|
defaultNetworkCode = defaultNetwork
|
3358
3936
|
return defaultNetworkCode
|
@@ -3404,28 +3982,43 @@ class Exchange(object):
|
|
3404
3982
|
'nonce': None,
|
3405
3983
|
}
|
3406
3984
|
|
3407
|
-
def parse_ohlcvs(self, ohlcvs: List[object], market: Any = None, timeframe: str = '1m', since: Int = None, limit: Int = None):
|
3985
|
+
def parse_ohlcvs(self, ohlcvs: List[object], market: Any = None, timeframe: str = '1m', since: Int = None, limit: Int = None, tail: Bool = False):
|
3408
3986
|
results = []
|
3409
3987
|
for i in range(0, len(ohlcvs)):
|
3410
3988
|
results.append(self.parse_ohlcv(ohlcvs[i], market))
|
3411
3989
|
sorted = self.sort_by(results, 0)
|
3412
|
-
return self.filter_by_since_limit(sorted, since, limit, 0)
|
3990
|
+
return self.filter_by_since_limit(sorted, since, limit, 0, tail)
|
3413
3991
|
|
3414
|
-
def parse_leverage_tiers(self, response:
|
3992
|
+
def parse_leverage_tiers(self, response: Any, symbols: List[str] = None, marketIdKey=None):
|
3415
3993
|
# marketIdKey should only be None when response is a dictionary
|
3416
3994
|
symbols = self.market_symbols(symbols)
|
3417
3995
|
tiers = {}
|
3418
|
-
|
3419
|
-
|
3420
|
-
|
3421
|
-
|
3422
|
-
|
3423
|
-
|
3424
|
-
|
3425
|
-
|
3996
|
+
symbolsLength = 0
|
3997
|
+
if symbols is not None:
|
3998
|
+
symbolsLength = len(symbols)
|
3999
|
+
noSymbols = (symbols is None) or (symbolsLength == 0)
|
4000
|
+
if isinstance(response, list):
|
4001
|
+
for i in range(0, len(response)):
|
4002
|
+
item = response[i]
|
4003
|
+
id = self.safe_string(item, marketIdKey)
|
4004
|
+
market = self.safe_market(id, None, None, 'swap')
|
4005
|
+
symbol = market['symbol']
|
4006
|
+
contract = self.safe_bool(market, 'contract', False)
|
4007
|
+
if contract and (noSymbols or self.in_array(symbol, symbols)):
|
4008
|
+
tiers[symbol] = self.parse_market_leverage_tiers(item, market)
|
4009
|
+
else:
|
4010
|
+
keys = list(response.keys())
|
4011
|
+
for i in range(0, len(keys)):
|
4012
|
+
marketId = keys[i]
|
4013
|
+
item = response[marketId]
|
4014
|
+
market = self.safe_market(marketId, None, None, 'swap')
|
4015
|
+
symbol = market['symbol']
|
4016
|
+
contract = self.safe_bool(market, 'contract', False)
|
4017
|
+
if contract and (noSymbols or self.in_array(symbol, symbols)):
|
4018
|
+
tiers[symbol] = self.parse_market_leverage_tiers(item, market)
|
3426
4019
|
return tiers
|
3427
4020
|
|
3428
|
-
def load_trading_limits(self, symbols:
|
4021
|
+
def load_trading_limits(self, symbols: Strings = None, reload=False, params={}):
|
3429
4022
|
if self.has['fetchTradingLimits']:
|
3430
4023
|
if reload or not ('limitsLoaded' in self.options):
|
3431
4024
|
response = self.fetch_trading_limits(symbols)
|
@@ -3435,7 +4028,7 @@ class Exchange(object):
|
|
3435
4028
|
self.options['limitsLoaded'] = self.milliseconds()
|
3436
4029
|
return self.markets
|
3437
4030
|
|
3438
|
-
def safe_position(self, position):
|
4031
|
+
def safe_position(self, position: dict):
|
3439
4032
|
# simplified version of: /pull/12765/
|
3440
4033
|
unrealizedPnlString = self.safe_string(position, 'unrealisedPnl')
|
3441
4034
|
initialMarginString = self.safe_string(position, 'initialMargin')
|
@@ -3525,6 +4118,14 @@ class Exchange(object):
|
|
3525
4118
|
def set_headers(self, headers):
|
3526
4119
|
return headers
|
3527
4120
|
|
4121
|
+
def currency_id(self, code: str):
|
4122
|
+
currency = self.safe_dict(self.currencies, code)
|
4123
|
+
if currency is None:
|
4124
|
+
currency = self.safe_currency(code)
|
4125
|
+
if currency is not None:
|
4126
|
+
return currency['id']
|
4127
|
+
return code
|
4128
|
+
|
3528
4129
|
def market_id(self, symbol: str):
|
3529
4130
|
market = self.market(symbol)
|
3530
4131
|
if market is not None:
|
@@ -3541,12 +4142,36 @@ class Exchange(object):
|
|
3541
4142
|
params = self.omit(params, paramName)
|
3542
4143
|
return [value, params]
|
3543
4144
|
|
4145
|
+
def handle_param_string_2(self, params: object, paramName1: str, paramName2: str, defaultValue: Str = None):
|
4146
|
+
value = self.safe_string_2(params, paramName1, paramName2, defaultValue)
|
4147
|
+
if value is not None:
|
4148
|
+
params = self.omit(params, [paramName1, paramName2])
|
4149
|
+
return [value, params]
|
4150
|
+
|
3544
4151
|
def handle_param_integer(self, params: object, paramName: str, defaultValue: Int = None):
|
3545
4152
|
value = self.safe_integer(params, paramName, defaultValue)
|
3546
4153
|
if value is not None:
|
3547
4154
|
params = self.omit(params, paramName)
|
3548
4155
|
return [value, params]
|
3549
4156
|
|
4157
|
+
def handle_param_integer_2(self, params: object, paramName1: str, paramName2: str, defaultValue: Int = None):
|
4158
|
+
value = self.safe_integer_2(params, paramName1, paramName2, defaultValue)
|
4159
|
+
if value is not None:
|
4160
|
+
params = self.omit(params, [paramName1, paramName2])
|
4161
|
+
return [value, params]
|
4162
|
+
|
4163
|
+
def handle_param_bool(self, params: object, paramName: str, defaultValue: Bool = None):
|
4164
|
+
value = self.safe_bool(params, paramName, defaultValue)
|
4165
|
+
if value is not None:
|
4166
|
+
params = self.omit(params, paramName)
|
4167
|
+
return [value, params]
|
4168
|
+
|
4169
|
+
def handle_param_bool_2(self, params: object, paramName1: str, paramName2: str, defaultValue: Bool = None):
|
4170
|
+
value = self.safe_bool_2(params, paramName1, paramName2, defaultValue)
|
4171
|
+
if value is not None:
|
4172
|
+
params = self.omit(params, [paramName1, paramName2])
|
4173
|
+
return [value, params]
|
4174
|
+
|
3550
4175
|
def resolve_path(self, path, params):
|
3551
4176
|
return [
|
3552
4177
|
self.implode_params(path, params),
|
@@ -3554,7 +4179,9 @@ class Exchange(object):
|
|
3554
4179
|
]
|
3555
4180
|
|
3556
4181
|
def get_list_from_object_values(self, objects, key: IndexType):
|
3557
|
-
newArray =
|
4182
|
+
newArray = objects
|
4183
|
+
if not isinstance(objects, list):
|
4184
|
+
newArray = self.to_array(objects)
|
3558
4185
|
results = []
|
3559
4186
|
for i in range(0, len(newArray)):
|
3560
4187
|
results.append(newArray[i][key])
|
@@ -3595,7 +4222,23 @@ class Exchange(object):
|
|
3595
4222
|
self.last_request_headers = request['headers']
|
3596
4223
|
self.last_request_body = request['body']
|
3597
4224
|
self.last_request_url = request['url']
|
3598
|
-
|
4225
|
+
retries = None
|
4226
|
+
retries, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailure', 0)
|
4227
|
+
retryDelay = None
|
4228
|
+
retryDelay, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailureDelay', 0)
|
4229
|
+
for i in range(0, retries + 1):
|
4230
|
+
try:
|
4231
|
+
return self.fetch(request['url'], request['method'], request['headers'], request['body'])
|
4232
|
+
except Exception as e:
|
4233
|
+
if isinstance(e, NetworkError):
|
4234
|
+
if i < retries:
|
4235
|
+
if self.verbose:
|
4236
|
+
self.log('Request failed with the error: ' + str(e) + ', retrying ' + (i + str(1)) + ' of ' + str(retries) + '...')
|
4237
|
+
if (retryDelay is not None) and (retryDelay != 0):
|
4238
|
+
self.sleep(retryDelay)
|
4239
|
+
continue
|
4240
|
+
raise e
|
4241
|
+
return None # self line is never reached, but exists for c# value return requirement
|
3599
4242
|
|
3600
4243
|
def request(self, path, api: Any = 'public', method='GET', params={}, headers: Any = None, body: Any = None, config={}):
|
3601
4244
|
return self.fetch2(path, api, method, params, headers, body, config)
|
@@ -3669,27 +4312,27 @@ class Exchange(object):
|
|
3669
4312
|
return self.edit_order(id, symbol, 'limit', side, amount, price, params)
|
3670
4313
|
|
3671
4314
|
def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
|
3672
|
-
self.
|
4315
|
+
self.cancel_order(id, symbol)
|
3673
4316
|
return self.create_order(symbol, type, side, amount, price, params)
|
3674
4317
|
|
3675
|
-
def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount:
|
3676
|
-
self.
|
3677
|
-
return self.
|
3678
|
-
|
3679
|
-
def fetch_permissions(self, params={}):
|
3680
|
-
raise NotSupported(self.id + ' fetchPermissions() is not supported yet')
|
4318
|
+
def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
|
4319
|
+
self.cancel_order_ws(id, symbol)
|
4320
|
+
return self.create_order_ws(symbol, type, side, amount, price, params)
|
3681
4321
|
|
3682
4322
|
def fetch_position(self, symbol: str, params={}):
|
3683
4323
|
raise NotSupported(self.id + ' fetchPosition() is not supported yet')
|
3684
4324
|
|
4325
|
+
def fetch_position_ws(self, symbol: str, params={}):
|
4326
|
+
raise NotSupported(self.id + ' fetchPositionWs() is not supported yet')
|
4327
|
+
|
3685
4328
|
def watch_position(self, symbol: Str = None, params={}):
|
3686
4329
|
raise NotSupported(self.id + ' watchPosition() is not supported yet')
|
3687
4330
|
|
3688
|
-
def watch_positions(self, symbols:
|
4331
|
+
def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}):
|
3689
4332
|
raise NotSupported(self.id + ' watchPositions() is not supported yet')
|
3690
4333
|
|
3691
|
-
def watch_position_for_symbols(self, symbols:
|
3692
|
-
return self.
|
4334
|
+
def watch_position_for_symbols(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}):
|
4335
|
+
return self.watch_positions(symbols, since, limit, params)
|
3693
4336
|
|
3694
4337
|
def fetch_positions_for_symbol(self, symbol: str, params={}):
|
3695
4338
|
"""
|
@@ -3700,13 +4343,25 @@ class Exchange(object):
|
|
3700
4343
|
"""
|
3701
4344
|
raise NotSupported(self.id + ' fetchPositionsForSymbol() is not supported yet')
|
3702
4345
|
|
3703
|
-
def
|
4346
|
+
def fetch_positions_for_symbol_ws(self, symbol: str, params={}):
|
4347
|
+
"""
|
4348
|
+
fetches all open positions for specific symbol, unlike fetchPositions(which is designed to work with multiple symbols) so self method might be preffered for one-market position, because of less rate-limit consumption and speed
|
4349
|
+
:param str symbol: unified market symbol
|
4350
|
+
:param dict params: extra parameters specific to the endpoint
|
4351
|
+
:returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>` with maximum 3 items - possible one position for "one-way" mode, and possible two positions(long & short) for "two-way"(a.k.a. hedge) mode
|
4352
|
+
"""
|
4353
|
+
raise NotSupported(self.id + ' fetchPositionsForSymbol() is not supported yet')
|
4354
|
+
|
4355
|
+
def fetch_positions(self, symbols: Strings = None, params={}):
|
3704
4356
|
raise NotSupported(self.id + ' fetchPositions() is not supported yet')
|
3705
4357
|
|
3706
|
-
def
|
4358
|
+
def fetch_positions_ws(self, symbols: Strings = None, params={}):
|
4359
|
+
raise NotSupported(self.id + ' fetchPositions() is not supported yet')
|
4360
|
+
|
4361
|
+
def fetch_positions_risk(self, symbols: Strings = None, params={}):
|
3707
4362
|
raise NotSupported(self.id + ' fetchPositionsRisk() is not supported yet')
|
3708
4363
|
|
3709
|
-
def fetch_bids_asks(self, symbols:
|
4364
|
+
def fetch_bids_asks(self, symbols: Strings = None, params={}):
|
3710
4365
|
raise NotSupported(self.id + ' fetchBidsAsks() is not supported yet')
|
3711
4366
|
|
3712
4367
|
def fetch_borrow_interest(self, code: Str = None, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
@@ -3735,11 +4390,11 @@ class Exchange(object):
|
|
3735
4390
|
code = currencyId
|
3736
4391
|
if currencyId is not None:
|
3737
4392
|
code = self.common_currency_code(currencyId.upper())
|
3738
|
-
return {
|
4393
|
+
return self.safe_currency_structure({
|
3739
4394
|
'id': currencyId,
|
3740
4395
|
'code': code,
|
3741
4396
|
'precision': None,
|
3742
|
-
}
|
4397
|
+
})
|
3743
4398
|
|
3744
4399
|
def safe_market(self, marketId: Str, market: Market = None, delimiter: Str = None, marketType: Str = None):
|
3745
4400
|
result = self.safe_market_structure({
|
@@ -3780,7 +4435,7 @@ class Exchange(object):
|
|
3780
4435
|
|
3781
4436
|
def check_required_credentials(self, error=True):
|
3782
4437
|
"""
|
3783
|
-
|
4438
|
+
@ignore
|
3784
4439
|
:param boolean error: raise an error that a credential is required if True
|
3785
4440
|
:returns boolean: True if all required credentials have been set, otherwise False or an error is thrown is param error=true
|
3786
4441
|
"""
|
@@ -3828,33 +4483,21 @@ class Exchange(object):
|
|
3828
4483
|
def fetch_status(self, params={}):
|
3829
4484
|
raise NotSupported(self.id + ' fetchStatus() is not supported yet')
|
3830
4485
|
|
3831
|
-
def fetch_funding_fee(self, code: str, params={}):
|
3832
|
-
warnOnFetchFundingFee = self.safe_bool(self.options, 'warnOnFetchFundingFee', True)
|
3833
|
-
if warnOnFetchFundingFee:
|
3834
|
-
raise NotSupported(self.id + ' fetchFundingFee() method is deprecated, it will be removed in July 2022, please, use fetchTransactionFee() or set exchange.options["warnOnFetchFundingFee"] = False to suppress self warning')
|
3835
|
-
return self.fetch_transaction_fee(code, params)
|
3836
|
-
|
3837
|
-
def fetch_funding_fees(self, codes: List[str] = None, params={}):
|
3838
|
-
warnOnFetchFundingFees = self.safe_bool(self.options, 'warnOnFetchFundingFees', True)
|
3839
|
-
if warnOnFetchFundingFees:
|
3840
|
-
raise NotSupported(self.id + ' fetchFundingFees() method is deprecated, it will be removed in July 2022. Please, use fetchTransactionFees() or set exchange.options["warnOnFetchFundingFees"] = False to suppress self warning')
|
3841
|
-
return self.fetch_transaction_fees(codes, params)
|
3842
|
-
|
3843
4486
|
def fetch_transaction_fee(self, code: str, params={}):
|
3844
4487
|
if not self.has['fetchTransactionFees']:
|
3845
4488
|
raise NotSupported(self.id + ' fetchTransactionFee() is not supported yet')
|
3846
4489
|
return self.fetch_transaction_fees([code], params)
|
3847
4490
|
|
3848
|
-
def fetch_transaction_fees(self, codes:
|
4491
|
+
def fetch_transaction_fees(self, codes: Strings = None, params={}):
|
3849
4492
|
raise NotSupported(self.id + ' fetchTransactionFees() is not supported yet')
|
3850
4493
|
|
3851
|
-
def fetch_deposit_withdraw_fees(self, codes:
|
4494
|
+
def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
|
3852
4495
|
raise NotSupported(self.id + ' fetchDepositWithdrawFees() is not supported yet')
|
3853
4496
|
|
3854
4497
|
def fetch_deposit_withdraw_fee(self, code: str, params={}):
|
3855
4498
|
if not self.has['fetchDepositWithdrawFees']:
|
3856
4499
|
raise NotSupported(self.id + ' fetchDepositWithdrawFee() is not supported yet')
|
3857
|
-
fees = self.
|
4500
|
+
fees = self.fetch_deposit_withdraw_fees([code], params)
|
3858
4501
|
return self.safe_value(fees, code)
|
3859
4502
|
|
3860
4503
|
def get_supported_mapping(self, key, mapping={}):
|
@@ -3867,7 +4510,7 @@ class Exchange(object):
|
|
3867
4510
|
self.load_markets()
|
3868
4511
|
if not self.has['fetchBorrowRates']:
|
3869
4512
|
raise NotSupported(self.id + ' fetchCrossBorrowRate() is not supported yet')
|
3870
|
-
borrowRates = self.
|
4513
|
+
borrowRates = self.fetch_cross_borrow_rates(params)
|
3871
4514
|
rate = self.safe_value(borrowRates, code)
|
3872
4515
|
if rate is None:
|
3873
4516
|
raise ExchangeError(self.id + ' fetchCrossBorrowRate() could not find the borrow rate for currency code ' + code)
|
@@ -3905,36 +4548,40 @@ class Exchange(object):
|
|
3905
4548
|
value = value if (value is not None) else defaultValue
|
3906
4549
|
return [value, params]
|
3907
4550
|
|
3908
|
-
def handle_option_and_params_2(self, params: object,
|
3909
|
-
|
3910
|
-
|
3911
|
-
# check if params contain the key
|
3912
|
-
value = self.safe_value_2(params, optionName, defaultOptionName)
|
4551
|
+
def handle_option_and_params_2(self, params: object, methodName1: str, optionName1: str, optionName2: str, defaultValue=None):
|
4552
|
+
value = None
|
4553
|
+
value, params = self.handle_option_and_params(params, methodName1, optionName1)
|
3913
4554
|
if value is not None:
|
3914
|
-
|
3915
|
-
|
3916
|
-
|
3917
|
-
|
3918
|
-
|
3919
|
-
|
3920
|
-
|
3921
|
-
if value is None:
|
3922
|
-
# if it's still None, check if global exchange-wide option exists
|
3923
|
-
value = self.safe_value_2(self.options, optionName, defaultOptionName)
|
3924
|
-
# if it's still None, use the default value
|
3925
|
-
value = value if (value is not None) else defaultValue
|
3926
|
-
return [value, params]
|
4555
|
+
# omit optionName2 too from params
|
4556
|
+
params = self.omit(params, optionName2)
|
4557
|
+
return [value, params]
|
4558
|
+
# if still None, try optionName2
|
4559
|
+
value2 = None
|
4560
|
+
value2, params = self.handle_option_and_params(params, methodName1, optionName2, defaultValue)
|
4561
|
+
return [value2, params]
|
3927
4562
|
|
3928
4563
|
def handle_option(self, methodName: str, optionName: str, defaultValue=None):
|
3929
4564
|
# eslint-disable-next-line no-unused-vars
|
3930
4565
|
result, empty = self.handle_option_and_params({}, methodName, optionName, defaultValue)
|
3931
4566
|
return result
|
3932
4567
|
|
3933
|
-
def handle_market_type_and_params(self, methodName: str, market: Market = None, params={}):
|
4568
|
+
def handle_market_type_and_params(self, methodName: str, market: Market = None, params={}, defaultValue=None):
|
4569
|
+
"""
|
4570
|
+
@ignore
|
4571
|
+
@param methodName the method calling handleMarketTypeAndParams
|
4572
|
+
:param Market market:
|
4573
|
+
:param dict params:
|
4574
|
+
:param str [params.type]: type assigned by user
|
4575
|
+
:param str [params.defaultType]: same.type
|
4576
|
+
:param str [defaultValue]: assigned programatically in the method calling handleMarketTypeAndParams
|
4577
|
+
:returns [str, dict]: the market type and params with type and defaultType omitted
|
4578
|
+
"""
|
3934
4579
|
defaultType = self.safe_string_2(self.options, 'defaultType', 'type', 'spot')
|
4580
|
+
if defaultValue is None: # defaultValue takes precendence over exchange wide defaultType
|
4581
|
+
defaultValue = defaultType
|
3935
4582
|
methodOptions = self.safe_dict(self.options, methodName)
|
3936
|
-
methodType =
|
3937
|
-
if methodOptions is not None:
|
4583
|
+
methodType = defaultValue
|
4584
|
+
if methodOptions is not None: # user defined methodType takes precedence over defaultValue
|
3938
4585
|
if isinstance(methodOptions, str):
|
3939
4586
|
methodType = methodOptions
|
3940
4587
|
else:
|
@@ -3967,7 +4614,7 @@ class Exchange(object):
|
|
3967
4614
|
|
3968
4615
|
def handle_margin_mode_and_params(self, methodName: str, params={}, defaultValue=None):
|
3969
4616
|
"""
|
3970
|
-
|
4617
|
+
@ignore
|
3971
4618
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
3972
4619
|
:returns Array: the marginMode in lowercase by params["marginMode"], params["defaultMarginMode"] self.options["marginMode"] or self.options["defaultMarginMode"]
|
3973
4620
|
"""
|
@@ -3994,7 +4641,7 @@ class Exchange(object):
|
|
3994
4641
|
return key
|
3995
4642
|
return None
|
3996
4643
|
|
3997
|
-
def handle_errors(self, statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody):
|
4644
|
+
def handle_errors(self, statusCode: int, statusText: str, url: str, method: str, responseHeaders: dict, responseBody: str, response, requestHeaders, requestBody):
|
3998
4645
|
# it is a stub method that must be overrided in the derived exchange classes
|
3999
4646
|
# raise NotSupported(self.id + ' handleErrors() not implemented yet')
|
4000
4647
|
return None
|
@@ -4016,18 +4663,58 @@ class Exchange(object):
|
|
4016
4663
|
else:
|
4017
4664
|
raise NotSupported(self.id + ' fetchTicker() is not supported yet')
|
4018
4665
|
|
4666
|
+
def fetch_mark_price(self, symbol: str, params={}):
|
4667
|
+
if self.has['fetchMarkPrices']:
|
4668
|
+
self.load_markets()
|
4669
|
+
market = self.market(symbol)
|
4670
|
+
symbol = market['symbol']
|
4671
|
+
tickers = self.fetch_mark_prices([symbol], params)
|
4672
|
+
ticker = self.safe_dict(tickers, symbol)
|
4673
|
+
if ticker is None:
|
4674
|
+
raise NullResponse(self.id + ' fetchMarkPrices() could not find a ticker for ' + symbol)
|
4675
|
+
else:
|
4676
|
+
return ticker
|
4677
|
+
else:
|
4678
|
+
raise NotSupported(self.id + ' fetchMarkPrices() is not supported yet')
|
4679
|
+
|
4680
|
+
def fetch_ticker_ws(self, symbol: str, params={}):
|
4681
|
+
if self.has['fetchTickersWs']:
|
4682
|
+
self.load_markets()
|
4683
|
+
market = self.market(symbol)
|
4684
|
+
symbol = market['symbol']
|
4685
|
+
tickers = self.fetch_tickers_ws([symbol], params)
|
4686
|
+
ticker = self.safe_dict(tickers, symbol)
|
4687
|
+
if ticker is None:
|
4688
|
+
raise NullResponse(self.id + ' fetchTickerWs() could not find a ticker for ' + symbol)
|
4689
|
+
else:
|
4690
|
+
return ticker
|
4691
|
+
else:
|
4692
|
+
raise NotSupported(self.id + ' fetchTickerWs() is not supported yet')
|
4693
|
+
|
4019
4694
|
def watch_ticker(self, symbol: str, params={}):
|
4020
4695
|
raise NotSupported(self.id + ' watchTicker() is not supported yet')
|
4021
4696
|
|
4022
|
-
def fetch_tickers(self, symbols:
|
4697
|
+
def fetch_tickers(self, symbols: Strings = None, params={}):
|
4698
|
+
raise NotSupported(self.id + ' fetchTickers() is not supported yet')
|
4699
|
+
|
4700
|
+
def fetch_mark_prices(self, symbols: Strings = None, params={}):
|
4701
|
+
raise NotSupported(self.id + ' fetchMarkPrices() is not supported yet')
|
4702
|
+
|
4703
|
+
def fetch_tickers_ws(self, symbols: Strings = None, params={}):
|
4023
4704
|
raise NotSupported(self.id + ' fetchTickers() is not supported yet')
|
4024
4705
|
|
4025
|
-
def fetch_order_books(self, symbols:
|
4706
|
+
def fetch_order_books(self, symbols: Strings = None, limit: Int = None, params={}):
|
4026
4707
|
raise NotSupported(self.id + ' fetchOrderBooks() is not supported yet')
|
4027
4708
|
|
4028
|
-
def
|
4709
|
+
def watch_bids_asks(self, symbols: Strings = None, params={}):
|
4710
|
+
raise NotSupported(self.id + ' watchBidsAsks() is not supported yet')
|
4711
|
+
|
4712
|
+
def watch_tickers(self, symbols: Strings = None, params={}):
|
4029
4713
|
raise NotSupported(self.id + ' watchTickers() is not supported yet')
|
4030
4714
|
|
4715
|
+
def un_watch_tickers(self, symbols: Strings = None, params={}):
|
4716
|
+
raise NotSupported(self.id + ' unWatchTickers() is not supported yet')
|
4717
|
+
|
4031
4718
|
def fetch_order(self, id: str, symbol: Str = None, params={}):
|
4032
4719
|
raise NotSupported(self.id + ' fetchOrder() is not supported yet')
|
4033
4720
|
|
@@ -4068,6 +4755,28 @@ class Exchange(object):
|
|
4068
4755
|
return self.create_order(symbol, type, side, amount, price, params)
|
4069
4756
|
raise NotSupported(self.id + ' createTrailingAmountOrder() is not supported yet')
|
4070
4757
|
|
4758
|
+
def create_trailing_amount_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingAmount=None, trailingTriggerPrice=None, params={}):
|
4759
|
+
"""
|
4760
|
+
create a trailing order by providing the symbol, type, side, amount, price and trailingAmount
|
4761
|
+
:param str symbol: unified symbol of the market to create an order in
|
4762
|
+
:param str type: 'market' or 'limit'
|
4763
|
+
:param str side: 'buy' or 'sell'
|
4764
|
+
:param float amount: how much you want to trade in units of the base currency, or number of contracts
|
4765
|
+
:param float [price]: the price for the order to be filled at, in units of the quote currency, ignored in market orders
|
4766
|
+
:param float trailingAmount: the quote amount to trail away from the current market price
|
4767
|
+
:param float [trailingTriggerPrice]: the price to activate a trailing order, default uses the price argument
|
4768
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4769
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
4770
|
+
"""
|
4771
|
+
if trailingAmount is None:
|
4772
|
+
raise ArgumentsRequired(self.id + ' createTrailingAmountOrderWs() requires a trailingAmount argument')
|
4773
|
+
params['trailingAmount'] = trailingAmount
|
4774
|
+
if trailingTriggerPrice is not None:
|
4775
|
+
params['trailingTriggerPrice'] = trailingTriggerPrice
|
4776
|
+
if self.has['createTrailingAmountOrderWs']:
|
4777
|
+
return self.create_order_ws(symbol, type, side, amount, price, params)
|
4778
|
+
raise NotSupported(self.id + ' createTrailingAmountOrderWs() is not supported yet')
|
4779
|
+
|
4071
4780
|
def create_trailing_percent_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent=None, trailingTriggerPrice=None, params={}):
|
4072
4781
|
"""
|
4073
4782
|
create a trailing order by providing the symbol, type, side, amount, price and trailingPercent
|
@@ -4090,6 +4799,28 @@ class Exchange(object):
|
|
4090
4799
|
return self.create_order(symbol, type, side, amount, price, params)
|
4091
4800
|
raise NotSupported(self.id + ' createTrailingPercentOrder() is not supported yet')
|
4092
4801
|
|
4802
|
+
def create_trailing_percent_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent=None, trailingTriggerPrice=None, params={}):
|
4803
|
+
"""
|
4804
|
+
create a trailing order by providing the symbol, type, side, amount, price and trailingPercent
|
4805
|
+
:param str symbol: unified symbol of the market to create an order in
|
4806
|
+
:param str type: 'market' or 'limit'
|
4807
|
+
:param str side: 'buy' or 'sell'
|
4808
|
+
:param float amount: how much you want to trade in units of the base currency, or number of contracts
|
4809
|
+
:param float [price]: the price for the order to be filled at, in units of the quote currency, ignored in market orders
|
4810
|
+
:param float trailingPercent: the percent to trail away from the current market price
|
4811
|
+
:param float [trailingTriggerPrice]: the price to activate a trailing order, default uses the price argument
|
4812
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4813
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
4814
|
+
"""
|
4815
|
+
if trailingPercent is None:
|
4816
|
+
raise ArgumentsRequired(self.id + ' createTrailingPercentOrderWs() requires a trailingPercent argument')
|
4817
|
+
params['trailingPercent'] = trailingPercent
|
4818
|
+
if trailingTriggerPrice is not None:
|
4819
|
+
params['trailingTriggerPrice'] = trailingTriggerPrice
|
4820
|
+
if self.has['createTrailingPercentOrderWs']:
|
4821
|
+
return self.create_order_ws(symbol, type, side, amount, price, params)
|
4822
|
+
raise NotSupported(self.id + ' createTrailingPercentOrderWs() is not supported yet')
|
4823
|
+
|
4093
4824
|
def create_market_order_with_cost(self, symbol: str, side: OrderSide, cost: float, params={}):
|
4094
4825
|
"""
|
4095
4826
|
create a market order by providing the symbol, side and cost
|
@@ -4127,6 +4858,19 @@ class Exchange(object):
|
|
4127
4858
|
return self.create_order(symbol, 'market', 'sell', cost, 1, params)
|
4128
4859
|
raise NotSupported(self.id + ' createMarketSellOrderWithCost() is not supported yet')
|
4129
4860
|
|
4861
|
+
def create_market_order_with_cost_ws(self, symbol: str, side: OrderSide, cost: float, params={}):
|
4862
|
+
"""
|
4863
|
+
create a market order by providing the symbol, side and cost
|
4864
|
+
:param str symbol: unified symbol of the market to create an order in
|
4865
|
+
:param str side: 'buy' or 'sell'
|
4866
|
+
:param float cost: how much you want to trade in units of the quote currency
|
4867
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4868
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
4869
|
+
"""
|
4870
|
+
if self.has['createMarketOrderWithCostWs'] or (self.has['createMarketBuyOrderWithCostWs'] and self.has['createMarketSellOrderWithCostWs']):
|
4871
|
+
return self.create_order_ws(symbol, 'market', side, cost, 1, params)
|
4872
|
+
raise NotSupported(self.id + ' createMarketOrderWithCostWs() is not supported yet')
|
4873
|
+
|
4130
4874
|
def create_trigger_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, triggerPrice: Num = None, params={}):
|
4131
4875
|
"""
|
4132
4876
|
create a trigger stop order(type 1)
|
@@ -4146,6 +4890,25 @@ class Exchange(object):
|
|
4146
4890
|
return self.create_order(symbol, type, side, amount, price, params)
|
4147
4891
|
raise NotSupported(self.id + ' createTriggerOrder() is not supported yet')
|
4148
4892
|
|
4893
|
+
def create_trigger_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, triggerPrice: Num = None, params={}):
|
4894
|
+
"""
|
4895
|
+
create a trigger stop order(type 1)
|
4896
|
+
:param str symbol: unified symbol of the market to create an order in
|
4897
|
+
:param str type: 'market' or 'limit'
|
4898
|
+
:param str side: 'buy' or 'sell'
|
4899
|
+
:param float amount: how much you want to trade in units of the base currency or the number of contracts
|
4900
|
+
:param float [price]: the price to fulfill the order, in units of the quote currency, ignored in market orders
|
4901
|
+
:param float triggerPrice: the price to trigger the stop order, in units of the quote currency
|
4902
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4903
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
4904
|
+
"""
|
4905
|
+
if triggerPrice is None:
|
4906
|
+
raise ArgumentsRequired(self.id + ' createTriggerOrderWs() requires a triggerPrice argument')
|
4907
|
+
params['triggerPrice'] = triggerPrice
|
4908
|
+
if self.has['createTriggerOrderWs']:
|
4909
|
+
return self.create_order_ws(symbol, type, side, amount, price, params)
|
4910
|
+
raise NotSupported(self.id + ' createTriggerOrderWs() is not supported yet')
|
4911
|
+
|
4149
4912
|
def create_stop_loss_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, stopLossPrice: Num = None, params={}):
|
4150
4913
|
"""
|
4151
4914
|
create a trigger stop loss order(type 2)
|
@@ -4165,6 +4928,25 @@ class Exchange(object):
|
|
4165
4928
|
return self.create_order(symbol, type, side, amount, price, params)
|
4166
4929
|
raise NotSupported(self.id + ' createStopLossOrder() is not supported yet')
|
4167
4930
|
|
4931
|
+
def create_stop_loss_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, stopLossPrice: Num = None, params={}):
|
4932
|
+
"""
|
4933
|
+
create a trigger stop loss order(type 2)
|
4934
|
+
:param str symbol: unified symbol of the market to create an order in
|
4935
|
+
:param str type: 'market' or 'limit'
|
4936
|
+
:param str side: 'buy' or 'sell'
|
4937
|
+
:param float amount: how much you want to trade in units of the base currency or the number of contracts
|
4938
|
+
:param float [price]: the price to fulfill the order, in units of the quote currency, ignored in market orders
|
4939
|
+
:param float stopLossPrice: the price to trigger the stop loss order, in units of the quote currency
|
4940
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4941
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
4942
|
+
"""
|
4943
|
+
if stopLossPrice is None:
|
4944
|
+
raise ArgumentsRequired(self.id + ' createStopLossOrderWs() requires a stopLossPrice argument')
|
4945
|
+
params['stopLossPrice'] = stopLossPrice
|
4946
|
+
if self.has['createStopLossOrderWs']:
|
4947
|
+
return self.create_order_ws(symbol, type, side, amount, price, params)
|
4948
|
+
raise NotSupported(self.id + ' createStopLossOrderWs() is not supported yet')
|
4949
|
+
|
4168
4950
|
def create_take_profit_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, takeProfitPrice: Num = None, params={}):
|
4169
4951
|
"""
|
4170
4952
|
create a trigger take profit order(type 2)
|
@@ -4184,6 +4966,25 @@ class Exchange(object):
|
|
4184
4966
|
return self.create_order(symbol, type, side, amount, price, params)
|
4185
4967
|
raise NotSupported(self.id + ' createTakeProfitOrder() is not supported yet')
|
4186
4968
|
|
4969
|
+
def create_take_profit_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, takeProfitPrice: Num = None, params={}):
|
4970
|
+
"""
|
4971
|
+
create a trigger take profit order(type 2)
|
4972
|
+
:param str symbol: unified symbol of the market to create an order in
|
4973
|
+
:param str type: 'market' or 'limit'
|
4974
|
+
:param str side: 'buy' or 'sell'
|
4975
|
+
:param float amount: how much you want to trade in units of the base currency or the number of contracts
|
4976
|
+
:param float [price]: the price to fulfill the order, in units of the quote currency, ignored in market orders
|
4977
|
+
:param float takeProfitPrice: the price to trigger the take profit order, in units of the quote currency
|
4978
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4979
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
4980
|
+
"""
|
4981
|
+
if takeProfitPrice is None:
|
4982
|
+
raise ArgumentsRequired(self.id + ' createTakeProfitOrderWs() requires a takeProfitPrice argument')
|
4983
|
+
params['takeProfitPrice'] = takeProfitPrice
|
4984
|
+
if self.has['createTakeProfitOrderWs']:
|
4985
|
+
return self.create_order_ws(symbol, type, side, amount, price, params)
|
4986
|
+
raise NotSupported(self.id + ' createTakeProfitOrderWs() is not supported yet')
|
4987
|
+
|
4187
4988
|
def create_order_with_take_profit_and_stop_loss(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, takeProfit: Num = None, stopLoss: Num = None, params={}):
|
4188
4989
|
"""
|
4189
4990
|
create an order with a stop loss or take profit attached(type 3)
|
@@ -4205,6 +5006,12 @@ class Exchange(object):
|
|
4205
5006
|
:param float [params.stopLossAmount]: *not available on all exchanges* the amount for a stop loss
|
4206
5007
|
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
4207
5008
|
"""
|
5009
|
+
params = self.set_take_profit_and_stop_loss_params(symbol, type, side, amount, price, takeProfit, stopLoss, params)
|
5010
|
+
if self.has['createOrderWithTakeProfitAndStopLoss']:
|
5011
|
+
return self.create_order(symbol, type, side, amount, price, params)
|
5012
|
+
raise NotSupported(self.id + ' createOrderWithTakeProfitAndStopLoss() is not supported yet')
|
5013
|
+
|
5014
|
+
def set_take_profit_and_stop_loss_params(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, takeProfit: Num = None, stopLoss: Num = None, params={}):
|
4208
5015
|
if (takeProfit is None) and (stopLoss is None):
|
4209
5016
|
raise ArgumentsRequired(self.id + ' createOrderWithTakeProfitAndStopLoss() requires either a takeProfit or stopLoss argument')
|
4210
5017
|
if takeProfit is not None:
|
@@ -4240,9 +5047,33 @@ class Exchange(object):
|
|
4240
5047
|
if stopLossAmount is not None:
|
4241
5048
|
params['stopLoss']['amount'] = self.parse_to_numeric(stopLossAmount)
|
4242
5049
|
params = self.omit(params, ['takeProfitType', 'takeProfitPriceType', 'takeProfitLimitPrice', 'takeProfitAmount', 'stopLossType', 'stopLossPriceType', 'stopLossLimitPrice', 'stopLossAmount'])
|
4243
|
-
|
4244
|
-
|
4245
|
-
|
5050
|
+
return params
|
5051
|
+
|
5052
|
+
def create_order_with_take_profit_and_stop_loss_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, takeProfit: Num = None, stopLoss: Num = None, params={}):
|
5053
|
+
"""
|
5054
|
+
create an order with a stop loss or take profit attached(type 3)
|
5055
|
+
:param str symbol: unified symbol of the market to create an order in
|
5056
|
+
:param str type: 'market' or 'limit'
|
5057
|
+
:param str side: 'buy' or 'sell'
|
5058
|
+
:param float amount: how much you want to trade in units of the base currency or the number of contracts
|
5059
|
+
:param float [price]: the price to fulfill the order, in units of the quote currency, ignored in market orders
|
5060
|
+
:param float [takeProfit]: the take profit price, in units of the quote currency
|
5061
|
+
:param float [stopLoss]: the stop loss price, in units of the quote currency
|
5062
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
5063
|
+
:param str [params.takeProfitType]: *not available on all exchanges* 'limit' or 'market'
|
5064
|
+
:param str [params.stopLossType]: *not available on all exchanges* 'limit' or 'market'
|
5065
|
+
:param str [params.takeProfitPriceType]: *not available on all exchanges* 'last', 'mark' or 'index'
|
5066
|
+
:param str [params.stopLossPriceType]: *not available on all exchanges* 'last', 'mark' or 'index'
|
5067
|
+
:param float [params.takeProfitLimitPrice]: *not available on all exchanges* limit price for a limit take profit order
|
5068
|
+
:param float [params.stopLossLimitPrice]: *not available on all exchanges* stop loss for a limit stop loss order
|
5069
|
+
:param float [params.takeProfitAmount]: *not available on all exchanges* the amount for a take profit
|
5070
|
+
:param float [params.stopLossAmount]: *not available on all exchanges* the amount for a stop loss
|
5071
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
5072
|
+
"""
|
5073
|
+
params = self.set_take_profit_and_stop_loss_params(symbol, type, side, amount, price, takeProfit, stopLoss, params)
|
5074
|
+
if self.has['createOrderWithTakeProfitAndStopLossWs']:
|
5075
|
+
return self.create_order_ws(symbol, type, side, amount, price, params)
|
5076
|
+
raise NotSupported(self.id + ' createOrderWithTakeProfitAndStopLossWs() is not supported yet')
|
4246
5077
|
|
4247
5078
|
def create_orders(self, orders: List[OrderRequest], params={}):
|
4248
5079
|
raise NotSupported(self.id + ' createOrders() is not supported yet')
|
@@ -4262,11 +5093,17 @@ class Exchange(object):
|
|
4262
5093
|
def cancel_all_orders(self, symbol: Str = None, params={}):
|
4263
5094
|
raise NotSupported(self.id + ' cancelAllOrders() is not supported yet')
|
4264
5095
|
|
5096
|
+
def cancel_all_orders_after(self, timeout: Int, params={}):
|
5097
|
+
raise NotSupported(self.id + ' cancelAllOrdersAfter() is not supported yet')
|
5098
|
+
|
5099
|
+
def cancel_orders_for_symbols(self, orders: List[CancellationRequest], params={}):
|
5100
|
+
raise NotSupported(self.id + ' cancelOrdersForSymbols() is not supported yet')
|
5101
|
+
|
4265
5102
|
def cancel_all_orders_ws(self, symbol: Str = None, params={}):
|
4266
5103
|
raise NotSupported(self.id + ' cancelAllOrdersWs() is not supported yet')
|
4267
5104
|
|
4268
5105
|
def cancel_unified_order(self, order, params={}):
|
4269
|
-
return self.
|
5106
|
+
return self.cancel_order(self.safe_string(order, 'id'), self.safe_string(order, 'symbol'), params)
|
4270
5107
|
|
4271
5108
|
def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
4272
5109
|
if self.has['fetchOpenOrders'] and self.has['fetchClosedOrders']:
|
@@ -4290,7 +5127,7 @@ class Exchange(object):
|
|
4290
5127
|
|
4291
5128
|
def fetch_open_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
4292
5129
|
if self.has['fetchOrdersWs']:
|
4293
|
-
orders = self.
|
5130
|
+
orders = self.fetch_orders_ws(symbol, since, limit, params)
|
4294
5131
|
return self.filter_by(orders, 'status', 'open')
|
4295
5132
|
raise NotSupported(self.id + ' fetchOpenOrdersWs() is not supported yet')
|
4296
5133
|
|
@@ -4305,7 +5142,7 @@ class Exchange(object):
|
|
4305
5142
|
|
4306
5143
|
def fetch_closed_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
4307
5144
|
if self.has['fetchOrdersWs']:
|
4308
|
-
orders = self.
|
5145
|
+
orders = self.fetch_orders_ws(symbol, since, limit, params)
|
4309
5146
|
return self.filter_by(orders, 'status', 'closed')
|
4310
5147
|
raise NotSupported(self.id + ' fetchClosedOrdersWs() is not supported yet')
|
4311
5148
|
|
@@ -4327,6 +5164,15 @@ class Exchange(object):
|
|
4327
5164
|
def fetch_greeks(self, symbol: str, params={}):
|
4328
5165
|
raise NotSupported(self.id + ' fetchGreeks() is not supported yet')
|
4329
5166
|
|
5167
|
+
def fetch_option_chain(self, code: str, params={}):
|
5168
|
+
raise NotSupported(self.id + ' fetchOptionChain() is not supported yet')
|
5169
|
+
|
5170
|
+
def fetch_option(self, symbol: str, params={}):
|
5171
|
+
raise NotSupported(self.id + ' fetchOption() is not supported yet')
|
5172
|
+
|
5173
|
+
def fetch_convert_quote(self, fromCode: str, toCode: str, amount: Num = None, params={}):
|
5174
|
+
raise NotSupported(self.id + ' fetchConvertQuote() is not supported yet')
|
5175
|
+
|
4330
5176
|
def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
4331
5177
|
"""
|
4332
5178
|
fetch history of deposits and withdrawals
|
@@ -4370,7 +5216,7 @@ class Exchange(object):
|
|
4370
5216
|
|
4371
5217
|
def fetch_deposit_address(self, code: str, params={}):
|
4372
5218
|
if self.has['fetchDepositAddresses']:
|
4373
|
-
depositAddresses = self.
|
5219
|
+
depositAddresses = self.fetch_deposit_addresses([code], params)
|
4374
5220
|
depositAddress = self.safe_value(depositAddresses, code)
|
4375
5221
|
if depositAddress is None:
|
4376
5222
|
raise InvalidAddress(self.id + ' fetchDepositAddress() could not find a deposit address for ' + code + ', make sure you have created a corresponding deposit address in your wallet on the exchange website')
|
@@ -4379,7 +5225,7 @@ class Exchange(object):
|
|
4379
5225
|
elif self.has['fetchDepositAddressesByNetwork']:
|
4380
5226
|
network = self.safe_string(params, 'network')
|
4381
5227
|
params = self.omit(params, 'network')
|
4382
|
-
addressStructures = self.
|
5228
|
+
addressStructures = self.fetch_deposit_addresses_by_network(code, params)
|
4383
5229
|
if network is not None:
|
4384
5230
|
return self.safe_dict(addressStructures, network)
|
4385
5231
|
else:
|
@@ -4396,10 +5242,10 @@ class Exchange(object):
|
|
4396
5242
|
'total': None,
|
4397
5243
|
}
|
4398
5244
|
|
4399
|
-
def common_currency_code(self,
|
5245
|
+
def common_currency_code(self, code: str):
|
4400
5246
|
if not self.substituteCommonCurrencyCodes:
|
4401
|
-
return
|
4402
|
-
return self.safe_string(self.commonCurrencies,
|
5247
|
+
return code
|
5248
|
+
return self.safe_string(self.commonCurrencies, code, code)
|
4403
5249
|
|
4404
5250
|
def currency(self, code: str):
|
4405
5251
|
if self.currencies is None:
|
@@ -4425,7 +5271,7 @@ class Exchange(object):
|
|
4425
5271
|
return market
|
4426
5272
|
return markets[0]
|
4427
5273
|
elif (symbol.endswith('-C')) or (symbol.endswith('-P')) or (symbol.startswith('C-')) or (symbol.startswith('P-')):
|
4428
|
-
return self.
|
5274
|
+
return self.create_expired_option_market(symbol)
|
4429
5275
|
raise BadSymbol(self.id + ' does not have market symbol ' + symbol)
|
4430
5276
|
|
4431
5277
|
def create_expired_option_market(self, symbol: str):
|
@@ -4444,21 +5290,39 @@ class Exchange(object):
|
|
4444
5290
|
def create_limit_order(self, symbol: str, side: OrderSide, amount: float, price: float, params={}):
|
4445
5291
|
return self.create_order(symbol, 'limit', side, amount, price, params)
|
4446
5292
|
|
5293
|
+
def create_limit_order_ws(self, symbol: str, side: OrderSide, amount: float, price: float, params={}):
|
5294
|
+
return self.create_order_ws(symbol, 'limit', side, amount, price, params)
|
5295
|
+
|
4447
5296
|
def create_market_order(self, symbol: str, side: OrderSide, amount: float, price: Num = None, params={}):
|
4448
5297
|
return self.create_order(symbol, 'market', side, amount, price, params)
|
4449
5298
|
|
5299
|
+
def create_market_order_ws(self, symbol: str, side: OrderSide, amount: float, price: Num = None, params={}):
|
5300
|
+
return self.create_order_ws(symbol, 'market', side, amount, price, params)
|
5301
|
+
|
4450
5302
|
def create_limit_buy_order(self, symbol: str, amount: float, price: float, params={}):
|
4451
5303
|
return self.create_order(symbol, 'limit', 'buy', amount, price, params)
|
4452
5304
|
|
5305
|
+
def create_limit_buy_order_ws(self, symbol: str, amount: float, price: float, params={}):
|
5306
|
+
return self.create_order_ws(symbol, 'limit', 'buy', amount, price, params)
|
5307
|
+
|
4453
5308
|
def create_limit_sell_order(self, symbol: str, amount: float, price: float, params={}):
|
4454
5309
|
return self.create_order(symbol, 'limit', 'sell', amount, price, params)
|
4455
5310
|
|
5311
|
+
def create_limit_sell_order_ws(self, symbol: str, amount: float, price: float, params={}):
|
5312
|
+
return self.create_order_ws(symbol, 'limit', 'sell', amount, price, params)
|
5313
|
+
|
4456
5314
|
def create_market_buy_order(self, symbol: str, amount: float, params={}):
|
4457
5315
|
return self.create_order(symbol, 'market', 'buy', amount, None, params)
|
4458
5316
|
|
5317
|
+
def create_market_buy_order_ws(self, symbol: str, amount: float, params={}):
|
5318
|
+
return self.create_order_ws(symbol, 'market', 'buy', amount, None, params)
|
5319
|
+
|
4459
5320
|
def create_market_sell_order(self, symbol: str, amount: float, params={}):
|
4460
5321
|
return self.create_order(symbol, 'market', 'sell', amount, None, params)
|
4461
5322
|
|
5323
|
+
def create_market_sell_order_ws(self, symbol: str, amount: float, params={}):
|
5324
|
+
return self.create_order_ws(symbol, 'market', 'sell', amount, None, params)
|
5325
|
+
|
4462
5326
|
def cost_to_precision(self, symbol: str, cost):
|
4463
5327
|
market = self.market(symbol)
|
4464
5328
|
return self.decimal_to_precision(cost, TRUNCATE, market['precision']['price'], self.precisionMode, self.paddingMode)
|
@@ -4489,9 +5353,10 @@ class Exchange(object):
|
|
4489
5353
|
networkItem = self.safe_dict(networks, networkCode, {})
|
4490
5354
|
precision = self.safe_value(networkItem, 'precision', precision)
|
4491
5355
|
if precision is None:
|
4492
|
-
return self.
|
5356
|
+
return self.force_string(fee)
|
4493
5357
|
else:
|
4494
|
-
|
5358
|
+
roundingMode = self.safe_integer(self.options, 'currencyToPrecisionRoundingMode', ROUND)
|
5359
|
+
return self.decimal_to_precision(fee, roundingMode, precision, self.precisionMode, self.paddingMode)
|
4495
5360
|
|
4496
5361
|
def force_string(self, value):
|
4497
5362
|
if not isinstance(value, str):
|
@@ -4507,7 +5372,7 @@ class Exchange(object):
|
|
4507
5372
|
def is_significant_precision(self):
|
4508
5373
|
return self.precisionMode == SIGNIFICANT_DIGITS
|
4509
5374
|
|
4510
|
-
def safe_number(self, obj
|
5375
|
+
def safe_number(self, obj, key: IndexType, defaultNumber: Num = None):
|
4511
5376
|
value = self.safe_string(obj, key)
|
4512
5377
|
return self.parse_number(value, defaultNumber)
|
4513
5378
|
|
@@ -4517,7 +5382,7 @@ class Exchange(object):
|
|
4517
5382
|
|
4518
5383
|
def parse_precision(self, precision: str):
|
4519
5384
|
"""
|
4520
|
-
|
5385
|
+
@ignore
|
4521
5386
|
:param str precision: The number of digits to the right of the decimal
|
4522
5387
|
:returns str: a string number equal to 1e-precision
|
4523
5388
|
"""
|
@@ -4531,6 +5396,25 @@ class Exchange(object):
|
|
4531
5396
|
parsedPrecision = parsedPrecision + '0'
|
4532
5397
|
return parsedPrecision + '1'
|
4533
5398
|
|
5399
|
+
def integer_precision_to_amount(self, precision: Str):
|
5400
|
+
"""
|
5401
|
+
@ignore
|
5402
|
+
handles positive & negative numbers too. parsePrecision() does not handle negative numbers, but self method handles
|
5403
|
+
:param str precision: The number of digits to the right of the decimal
|
5404
|
+
:returns str: a string number equal to 1e-precision
|
5405
|
+
"""
|
5406
|
+
if precision is None:
|
5407
|
+
return None
|
5408
|
+
if Precise.string_ge(precision, '0'):
|
5409
|
+
return self.parse_precision(precision)
|
5410
|
+
else:
|
5411
|
+
positivePrecisionString = Precise.string_abs(precision)
|
5412
|
+
positivePrecision = int(positivePrecisionString)
|
5413
|
+
parsedPrecision = '1'
|
5414
|
+
for i in range(0, positivePrecision - 1):
|
5415
|
+
parsedPrecision = parsedPrecision + '0'
|
5416
|
+
return parsedPrecision + '0'
|
5417
|
+
|
4534
5418
|
def load_time_difference(self, params={}):
|
4535
5419
|
serverTime = self.fetch_time(params)
|
4536
5420
|
after = self.milliseconds()
|
@@ -4556,32 +5440,64 @@ class Exchange(object):
|
|
4556
5440
|
query = self.extend(params, {'postOnly': True})
|
4557
5441
|
return self.create_order(symbol, type, side, amount, price, query)
|
4558
5442
|
|
5443
|
+
def create_post_only_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
5444
|
+
if not self.has['createPostOnlyOrderWs']:
|
5445
|
+
raise NotSupported(self.id + 'createPostOnlyOrderWs() is not supported yet')
|
5446
|
+
query = self.extend(params, {'postOnly': True})
|
5447
|
+
return self.create_order_ws(symbol, type, side, amount, price, query)
|
5448
|
+
|
4559
5449
|
def create_reduce_only_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
4560
5450
|
if not self.has['createReduceOnlyOrder']:
|
4561
5451
|
raise NotSupported(self.id + 'createReduceOnlyOrder() is not supported yet')
|
4562
5452
|
query = self.extend(params, {'reduceOnly': True})
|
4563
5453
|
return self.create_order(symbol, type, side, amount, price, query)
|
4564
5454
|
|
4565
|
-
def
|
5455
|
+
def create_reduce_only_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
5456
|
+
if not self.has['createReduceOnlyOrderWs']:
|
5457
|
+
raise NotSupported(self.id + 'createReduceOnlyOrderWs() is not supported yet')
|
5458
|
+
query = self.extend(params, {'reduceOnly': True})
|
5459
|
+
return self.create_order_ws(symbol, type, side, amount, price, query)
|
5460
|
+
|
5461
|
+
def create_stop_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, triggerPrice: Num = None, params={}):
|
4566
5462
|
if not self.has['createStopOrder']:
|
4567
5463
|
raise NotSupported(self.id + ' createStopOrder() is not supported yet')
|
4568
|
-
if
|
5464
|
+
if triggerPrice is None:
|
4569
5465
|
raise ArgumentsRequired(self.id + ' create_stop_order() requires a stopPrice argument')
|
4570
|
-
query = self.extend(params, {'stopPrice':
|
5466
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
4571
5467
|
return self.create_order(symbol, type, side, amount, price, query)
|
4572
5468
|
|
4573
|
-
def
|
5469
|
+
def create_stop_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, triggerPrice: Num = None, params={}):
|
5470
|
+
if not self.has['createStopOrderWs']:
|
5471
|
+
raise NotSupported(self.id + ' createStopOrderWs() is not supported yet')
|
5472
|
+
if triggerPrice is None:
|
5473
|
+
raise ArgumentsRequired(self.id + ' createStopOrderWs() requires a stopPrice argument')
|
5474
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
5475
|
+
return self.create_order_ws(symbol, type, side, amount, price, query)
|
5476
|
+
|
5477
|
+
def create_stop_limit_order(self, symbol: str, side: OrderSide, amount: float, price: float, triggerPrice: float, params={}):
|
4574
5478
|
if not self.has['createStopLimitOrder']:
|
4575
5479
|
raise NotSupported(self.id + ' createStopLimitOrder() is not supported yet')
|
4576
|
-
query = self.extend(params, {'stopPrice':
|
5480
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
4577
5481
|
return self.create_order(symbol, 'limit', side, amount, price, query)
|
4578
5482
|
|
4579
|
-
def
|
5483
|
+
def create_stop_limit_order_ws(self, symbol: str, side: OrderSide, amount: float, price: float, triggerPrice: float, params={}):
|
5484
|
+
if not self.has['createStopLimitOrderWs']:
|
5485
|
+
raise NotSupported(self.id + ' createStopLimitOrderWs() is not supported yet')
|
5486
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
5487
|
+
return self.create_order_ws(symbol, 'limit', side, amount, price, query)
|
5488
|
+
|
5489
|
+
def create_stop_market_order(self, symbol: str, side: OrderSide, amount: float, triggerPrice: float, params={}):
|
4580
5490
|
if not self.has['createStopMarketOrder']:
|
4581
5491
|
raise NotSupported(self.id + ' createStopMarketOrder() is not supported yet')
|
4582
|
-
query = self.extend(params, {'stopPrice':
|
5492
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
4583
5493
|
return self.create_order(symbol, 'market', side, amount, None, query)
|
4584
5494
|
|
5495
|
+
def create_stop_market_order_ws(self, symbol: str, side: OrderSide, amount: float, triggerPrice: float, params={}):
|
5496
|
+
if not self.has['createStopMarketOrderWs']:
|
5497
|
+
raise NotSupported(self.id + ' createStopMarketOrderWs() is not supported yet')
|
5498
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
5499
|
+
return self.create_order_ws(symbol, 'market', side, amount, None, query)
|
5500
|
+
|
4585
5501
|
def safe_currency_code(self, currencyId: Str, currency: Currency = None):
|
4586
5502
|
currency = self.safe_currency(currencyId, currency)
|
4587
5503
|
return currency['code']
|
@@ -4619,22 +5535,23 @@ class Exchange(object):
|
|
4619
5535
|
results = []
|
4620
5536
|
if isinstance(pricesData, list):
|
4621
5537
|
for i in range(0, len(pricesData)):
|
4622
|
-
priceData = self.extend(self.
|
5538
|
+
priceData = self.extend(self.parse_last_price(pricesData[i]), params)
|
4623
5539
|
results.append(priceData)
|
4624
5540
|
else:
|
4625
5541
|
marketIds = list(pricesData.keys())
|
4626
5542
|
for i in range(0, len(marketIds)):
|
4627
5543
|
marketId = marketIds[i]
|
4628
5544
|
market = self.safe_market(marketId)
|
4629
|
-
priceData = self.extend(self.
|
5545
|
+
priceData = self.extend(self.parse_last_price(pricesData[marketId], market), params)
|
4630
5546
|
results.append(priceData)
|
4631
5547
|
symbols = self.market_symbols(symbols)
|
4632
5548
|
return self.filter_by_array(results, 'symbol', symbols)
|
4633
5549
|
|
4634
|
-
def parse_tickers(self, tickers, symbols:
|
5550
|
+
def parse_tickers(self, tickers, symbols: Strings = None, params={}):
|
4635
5551
|
#
|
4636
5552
|
# the value of tickers is either a dict or a list
|
4637
5553
|
#
|
5554
|
+
#
|
4638
5555
|
# dict
|
4639
5556
|
#
|
4640
5557
|
# {
|
@@ -4668,7 +5585,7 @@ class Exchange(object):
|
|
4668
5585
|
symbols = self.market_symbols(symbols)
|
4669
5586
|
return self.filter_by_array(results, 'symbol', symbols)
|
4670
5587
|
|
4671
|
-
def parse_deposit_addresses(self, addresses, codes:
|
5588
|
+
def parse_deposit_addresses(self, addresses, codes: Strings = None, indexed=True, params={}):
|
4672
5589
|
result = []
|
4673
5590
|
for i in range(0, len(addresses)):
|
4674
5591
|
address = self.extend(self.parse_deposit_address(addresses[i]), params)
|
@@ -4676,7 +5593,7 @@ class Exchange(object):
|
|
4676
5593
|
if codes is not None:
|
4677
5594
|
result = self.filter_by_array(result, 'currency', codes, False)
|
4678
5595
|
if indexed:
|
4679
|
-
|
5596
|
+
result = self.filter_by_array(result, 'currency', None, indexed)
|
4680
5597
|
return result
|
4681
5598
|
|
4682
5599
|
def parse_borrow_interests(self, response, market: Market = None):
|
@@ -4686,6 +5603,27 @@ class Exchange(object):
|
|
4686
5603
|
interests.append(self.parse_borrow_interest(row, market))
|
4687
5604
|
return interests
|
4688
5605
|
|
5606
|
+
def parse_borrow_rate(self, info, currency: Currency = None):
|
5607
|
+
raise NotSupported(self.id + ' parseBorrowRate() is not supported yet')
|
5608
|
+
|
5609
|
+
def parse_borrow_rate_history(self, response, code: Str, since: Int, limit: Int):
|
5610
|
+
result = []
|
5611
|
+
for i in range(0, len(response)):
|
5612
|
+
item = response[i]
|
5613
|
+
borrowRate = self.parse_borrow_rate(item)
|
5614
|
+
result.append(borrowRate)
|
5615
|
+
sorted = self.sort_by(result, 'timestamp')
|
5616
|
+
return self.filter_by_currency_since_limit(sorted, code, since, limit)
|
5617
|
+
|
5618
|
+
def parse_isolated_borrow_rates(self, info: Any):
|
5619
|
+
result = {}
|
5620
|
+
for i in range(0, len(info)):
|
5621
|
+
item = info[i]
|
5622
|
+
borrowRate = self.parse_isolated_borrow_rate(item)
|
5623
|
+
symbol = self.safe_string(borrowRate, 'symbol')
|
5624
|
+
result[symbol] = borrowRate
|
5625
|
+
return result
|
5626
|
+
|
4689
5627
|
def parse_funding_rate_histories(self, response, market=None, since: Int = None, limit: Int = None):
|
4690
5628
|
rates = []
|
4691
5629
|
for i in range(0, len(response)):
|
@@ -4702,22 +5640,39 @@ class Exchange(object):
|
|
4702
5640
|
def parse_funding_rate(self, contract: str, market: Market = None):
|
4703
5641
|
raise NotSupported(self.id + ' parseFundingRate() is not supported yet')
|
4704
5642
|
|
4705
|
-
def parse_funding_rates(self, response,
|
4706
|
-
|
5643
|
+
def parse_funding_rates(self, response, symbols: Strings = None):
|
5644
|
+
fundingRates = {}
|
4707
5645
|
for i in range(0, len(response)):
|
4708
|
-
|
4709
|
-
|
4710
|
-
|
5646
|
+
entry = response[i]
|
5647
|
+
parsed = self.parse_funding_rate(entry)
|
5648
|
+
fundingRates[parsed['symbol']] = parsed
|
5649
|
+
return self.filter_by_array(fundingRates, 'symbol', symbols)
|
4711
5650
|
|
4712
|
-
def
|
5651
|
+
def parse_long_short_ratio(self, info: dict, market: Market = None):
|
5652
|
+
raise NotSupported(self.id + ' parseLongShortRatio() is not supported yet')
|
5653
|
+
|
5654
|
+
def parse_long_short_ratio_history(self, response, market=None, since: Int = None, limit: Int = None):
|
5655
|
+
rates = []
|
5656
|
+
for i in range(0, len(response)):
|
5657
|
+
entry = response[i]
|
5658
|
+
rates.append(self.parse_long_short_ratio(entry, market))
|
5659
|
+
sorted = self.sort_by(rates, 'timestamp')
|
5660
|
+
symbol = None if (market is None) else market['symbol']
|
5661
|
+
return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
|
5662
|
+
|
5663
|
+
def handle_trigger_and_params(self, params):
|
4713
5664
|
isTrigger = self.safe_bool_2(params, 'trigger', 'stop')
|
4714
5665
|
if isTrigger:
|
4715
5666
|
params = self.omit(params, ['trigger', 'stop'])
|
4716
5667
|
return [isTrigger, params]
|
4717
5668
|
|
5669
|
+
def is_trigger_order(self, params):
|
5670
|
+
# for backwards compatibility
|
5671
|
+
return self.handle_trigger_and_params(params)
|
5672
|
+
|
4718
5673
|
def is_post_only(self, isMarketOrder: bool, exchangeSpecificParam, params={}):
|
4719
5674
|
"""
|
4720
|
-
|
5675
|
+
@ignore
|
4721
5676
|
:param str type: Order type
|
4722
5677
|
:param boolean exchangeSpecificParam: exchange specific postOnly
|
4723
5678
|
:param dict [params]: exchange specific params
|
@@ -4742,7 +5697,7 @@ class Exchange(object):
|
|
4742
5697
|
|
4743
5698
|
def handle_post_only(self, isMarketOrder: bool, exchangeSpecificPostOnlyOption: bool, params: Any = {}):
|
4744
5699
|
"""
|
4745
|
-
|
5700
|
+
@ignore
|
4746
5701
|
:param str type: Order type
|
4747
5702
|
:param boolean exchangeSpecificBoolean: exchange specific postOnly
|
4748
5703
|
:param dict [params]: exchange specific params
|
@@ -4766,7 +5721,7 @@ class Exchange(object):
|
|
4766
5721
|
return [True, params]
|
4767
5722
|
return [False, params]
|
4768
5723
|
|
4769
|
-
def fetch_last_prices(self, symbols:
|
5724
|
+
def fetch_last_prices(self, symbols: Strings = None, params={}):
|
4770
5725
|
raise NotSupported(self.id + ' fetchLastPrices() is not supported yet')
|
4771
5726
|
|
4772
5727
|
def fetch_trading_fees(self, params={}):
|
@@ -4778,12 +5733,24 @@ class Exchange(object):
|
|
4778
5733
|
def fetch_trading_fee(self, symbol: str, params={}):
|
4779
5734
|
if not self.has['fetchTradingFees']:
|
4780
5735
|
raise NotSupported(self.id + ' fetchTradingFee() is not supported yet')
|
4781
|
-
|
5736
|
+
fees = self.fetch_trading_fees(params)
|
5737
|
+
return self.safe_dict(fees, symbol)
|
5738
|
+
|
5739
|
+
def fetch_convert_currencies(self, params={}):
|
5740
|
+
raise NotSupported(self.id + ' fetchConvertCurrencies() is not supported yet')
|
4782
5741
|
|
4783
5742
|
def parse_open_interest(self, interest, market: Market = None):
|
4784
5743
|
raise NotSupported(self.id + ' parseOpenInterest() is not supported yet')
|
4785
5744
|
|
4786
|
-
def parse_open_interests(self, response,
|
5745
|
+
def parse_open_interests(self, response, symbols: Strings = None):
|
5746
|
+
result = {}
|
5747
|
+
for i in range(0, len(response)):
|
5748
|
+
entry = response[i]
|
5749
|
+
parsed = self.parse_open_interest(entry)
|
5750
|
+
result[parsed['symbol']] = parsed
|
5751
|
+
return self.filter_by_array(result, 'symbol', symbols)
|
5752
|
+
|
5753
|
+
def parse_open_interests_history(self, response, market=None, since: Int = None, limit: Int = None):
|
4787
5754
|
interests = []
|
4788
5755
|
for i in range(0, len(response)):
|
4789
5756
|
entry = response[i]
|
@@ -4800,7 +5767,7 @@ class Exchange(object):
|
|
4800
5767
|
symbol = market['symbol']
|
4801
5768
|
if not market['contract']:
|
4802
5769
|
raise BadSymbol(self.id + ' fetchFundingRate() supports contract markets only')
|
4803
|
-
rates = self.
|
5770
|
+
rates = self.fetch_funding_rates([symbol], params)
|
4804
5771
|
rate = self.safe_value(rates, symbol)
|
4805
5772
|
if rate is None:
|
4806
5773
|
raise NullResponse(self.id + ' fetchFundingRate() returned no data for ' + symbol)
|
@@ -4809,6 +5776,22 @@ class Exchange(object):
|
|
4809
5776
|
else:
|
4810
5777
|
raise NotSupported(self.id + ' fetchFundingRate() is not supported yet')
|
4811
5778
|
|
5779
|
+
def fetch_funding_interval(self, symbol: str, params={}):
|
5780
|
+
if self.has['fetchFundingIntervals']:
|
5781
|
+
self.load_markets()
|
5782
|
+
market = self.market(symbol)
|
5783
|
+
symbol = market['symbol']
|
5784
|
+
if not market['contract']:
|
5785
|
+
raise BadSymbol(self.id + ' fetchFundingInterval() supports contract markets only')
|
5786
|
+
rates = self.fetch_funding_intervals([symbol], params)
|
5787
|
+
rate = self.safe_value(rates, symbol)
|
5788
|
+
if rate is None:
|
5789
|
+
raise NullResponse(self.id + ' fetchFundingInterval() returned no data for ' + symbol)
|
5790
|
+
else:
|
5791
|
+
return rate
|
5792
|
+
else:
|
5793
|
+
raise NotSupported(self.id + ' fetchFundingInterval() is not supported yet')
|
5794
|
+
|
4812
5795
|
def fetch_mark_ohlcv(self, symbol, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
4813
5796
|
"""
|
4814
5797
|
fetches historical mark price candlestick data containing the open, high, low, and close price of a market
|
@@ -4820,7 +5803,7 @@ class Exchange(object):
|
|
4820
5803
|
:returns float[][]: A list of candles ordered, open, high, low, close, None
|
4821
5804
|
"""
|
4822
5805
|
if self.has['fetchMarkOHLCV']:
|
4823
|
-
request = {
|
5806
|
+
request: dict = {
|
4824
5807
|
'price': 'mark',
|
4825
5808
|
}
|
4826
5809
|
return self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))
|
@@ -4835,10 +5818,10 @@ class Exchange(object):
|
|
4835
5818
|
:param int [since]: timestamp in ms of the earliest candle to fetch
|
4836
5819
|
:param int [limit]: the maximum amount of candles to fetch
|
4837
5820
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4838
|
-
|
5821
|
+
@returns {} A list of candles ordered, open, high, low, close, None
|
4839
5822
|
"""
|
4840
5823
|
if self.has['fetchIndexOHLCV']:
|
4841
|
-
request = {
|
5824
|
+
request: dict = {
|
4842
5825
|
'price': 'index',
|
4843
5826
|
}
|
4844
5827
|
return self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))
|
@@ -4856,7 +5839,7 @@ class Exchange(object):
|
|
4856
5839
|
:returns float[][]: A list of candles ordered, open, high, low, close, None
|
4857
5840
|
"""
|
4858
5841
|
if self.has['fetchPremiumIndexOHLCV']:
|
4859
|
-
request = {
|
5842
|
+
request: dict = {
|
4860
5843
|
'price': 'premiumIndex',
|
4861
5844
|
}
|
4862
5845
|
return self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))
|
@@ -4865,8 +5848,8 @@ class Exchange(object):
|
|
4865
5848
|
|
4866
5849
|
def handle_time_in_force(self, params={}):
|
4867
5850
|
"""
|
4868
|
-
|
4869
|
-
|
5851
|
+
@ignore
|
5852
|
+
Must add timeInForce to self.options to use self method
|
4870
5853
|
:returns str: returns the exchange specific value for timeInForce
|
4871
5854
|
"""
|
4872
5855
|
timeInForce = self.safe_string_upper(params, 'timeInForce') # supported values GTC, IOC, PO
|
@@ -4879,8 +5862,8 @@ class Exchange(object):
|
|
4879
5862
|
|
4880
5863
|
def convert_type_to_account(self, account):
|
4881
5864
|
"""
|
4882
|
-
|
4883
|
-
|
5865
|
+
@ignore
|
5866
|
+
Must add accountsByType to self.options to use self method
|
4884
5867
|
:param str account: key for account name in self.options['accountsByType']
|
4885
5868
|
:returns: the exchange specific account name or the isolated margin id for transfers
|
4886
5869
|
"""
|
@@ -4896,7 +5879,7 @@ class Exchange(object):
|
|
4896
5879
|
|
4897
5880
|
def check_required_argument(self, methodName: str, argument, argumentName, options=[]):
|
4898
5881
|
"""
|
4899
|
-
|
5882
|
+
@ignore
|
4900
5883
|
:param str methodName: the name of the method that the argument is being checked for
|
4901
5884
|
:param str argument: the argument's actual value provided
|
4902
5885
|
:param str argumentName: the name of the argument being checked(for logging purposes)
|
@@ -4913,7 +5896,7 @@ class Exchange(object):
|
|
4913
5896
|
|
4914
5897
|
def check_required_margin_argument(self, methodName: str, symbol: Str, marginMode: str):
|
4915
5898
|
"""
|
4916
|
-
|
5899
|
+
@ignore
|
4917
5900
|
:param str symbol: unified symbol of the market
|
4918
5901
|
:param str methodName: name of the method that requires a symbol
|
4919
5902
|
:param str marginMode: is either 'isolated' or 'cross'
|
@@ -4923,16 +5906,15 @@ class Exchange(object):
|
|
4923
5906
|
elif (marginMode == 'cross') and (symbol is not None):
|
4924
5907
|
raise ArgumentsRequired(self.id + ' ' + methodName + '() cannot have a symbol argument for cross margin')
|
4925
5908
|
|
4926
|
-
def parse_deposit_withdraw_fees(self, response, codes:
|
5909
|
+
def parse_deposit_withdraw_fees(self, response, codes: Strings = None, currencyIdKey=None):
|
4927
5910
|
"""
|
4928
|
-
|
5911
|
+
@ignore
|
4929
5912
|
:param object[]|dict response: unparsed response from the exchange
|
4930
5913
|
:param str[]|None codes: the unified currency codes to fetch transactions fees for, returns all currencies when None
|
4931
5914
|
:param str currencyIdKey: *should only be None when response is a dictionary* the object key that corresponds to the currency id
|
4932
5915
|
:returns dict: objects with withdraw and deposit fees, indexed by currency codes
|
4933
5916
|
"""
|
4934
5917
|
depositWithdrawFees = {}
|
4935
|
-
codes = self.marketCodes(codes)
|
4936
5918
|
isArray = isinstance(response, list)
|
4937
5919
|
responseKeys = response
|
4938
5920
|
if not isArray:
|
@@ -4941,10 +5923,10 @@ class Exchange(object):
|
|
4941
5923
|
entry = responseKeys[i]
|
4942
5924
|
dictionary = entry if isArray else response[entry]
|
4943
5925
|
currencyId = self.safe_string(dictionary, currencyIdKey) if isArray else entry
|
4944
|
-
currency = self.
|
4945
|
-
code = self.safe_string(currency, 'code'
|
5926
|
+
currency = self.safe_currency(currencyId)
|
5927
|
+
code = self.safe_string(currency, 'code')
|
4946
5928
|
if (codes is None) or (self.in_array(code, codes)):
|
4947
|
-
depositWithdrawFees[code] = self.
|
5929
|
+
depositWithdrawFees[code] = self.parse_deposit_withdraw_fee(dictionary, currency)
|
4948
5930
|
return depositWithdrawFees
|
4949
5931
|
|
4950
5932
|
def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
|
@@ -4966,7 +5948,7 @@ class Exchange(object):
|
|
4966
5948
|
|
4967
5949
|
def assign_default_deposit_withdraw_fees(self, fee, currency=None):
|
4968
5950
|
"""
|
4969
|
-
|
5951
|
+
@ignore
|
4970
5952
|
Takes a depositWithdrawFee structure and assigns the default values for withdraw and deposit
|
4971
5953
|
:param dict fee: A deposit withdraw fee structure
|
4972
5954
|
:param dict currency: A currency structure, the response from self.currency()
|
@@ -4991,7 +5973,7 @@ class Exchange(object):
|
|
4991
5973
|
|
4992
5974
|
def parse_incomes(self, incomes, market=None, since: Int = None, limit: Int = None):
|
4993
5975
|
"""
|
4994
|
-
|
5976
|
+
@ignore
|
4995
5977
|
parses funding fee info from exchange response
|
4996
5978
|
:param dict[] incomes: each item describes once instance of currency being received or paid
|
4997
5979
|
:param dict market: ccxt market
|
@@ -5007,7 +5989,7 @@ class Exchange(object):
|
|
5007
5989
|
sorted = self.sort_by(result, 'timestamp')
|
5008
5990
|
return self.filter_by_since_limit(sorted, since, limit)
|
5009
5991
|
|
5010
|
-
def get_market_from_symbols(self, symbols:
|
5992
|
+
def get_market_from_symbols(self, symbols: Strings = None):
|
5011
5993
|
if symbols is None:
|
5012
5994
|
return None
|
5013
5995
|
firstMarket = self.safe_string(symbols, 0)
|
@@ -5022,7 +6004,7 @@ class Exchange(object):
|
|
5022
6004
|
|
5023
6005
|
def fetch_transactions(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
5024
6006
|
"""
|
5025
|
-
|
6007
|
+
@deprecated
|
5026
6008
|
*DEPRECATED* use fetchDepositsWithdrawals instead
|
5027
6009
|
:param str code: unified currency code for the currency of the deposit/withdrawals, default is None
|
5028
6010
|
:param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
|
@@ -5031,20 +6013,20 @@ class Exchange(object):
|
|
5031
6013
|
:returns dict: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
5032
6014
|
"""
|
5033
6015
|
if self.has['fetchDepositsWithdrawals']:
|
5034
|
-
return self.
|
6016
|
+
return self.fetch_deposits_withdrawals(code, since, limit, params)
|
5035
6017
|
else:
|
5036
6018
|
raise NotSupported(self.id + ' fetchTransactions() is not supported yet')
|
5037
6019
|
|
5038
6020
|
def filter_by_array_positions(self, objects, key: IndexType, values=None, indexed=True):
|
5039
6021
|
"""
|
5040
|
-
|
6022
|
+
@ignore
|
5041
6023
|
Typed wrapper for filterByArray that returns a list of positions
|
5042
6024
|
"""
|
5043
6025
|
return self.filter_by_array(objects, key, values, indexed)
|
5044
6026
|
|
5045
6027
|
def filter_by_array_tickers(self, objects, key: IndexType, values=None, indexed=True):
|
5046
6028
|
"""
|
5047
|
-
|
6029
|
+
@ignore
|
5048
6030
|
Typed wrapper for filterByArray that returns a dictionary of tickers
|
5049
6031
|
"""
|
5050
6032
|
return self.filter_by_array(objects, key, values, indexed)
|
@@ -5064,7 +6046,7 @@ class Exchange(object):
|
|
5064
6046
|
maxEntriesPerRequest = 1000 # default to 1000
|
5065
6047
|
return [maxEntriesPerRequest, params]
|
5066
6048
|
|
5067
|
-
def fetch_paginated_call_dynamic(self, method: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}, maxEntriesPerRequest: Int = None):
|
6049
|
+
def fetch_paginated_call_dynamic(self, method: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}, maxEntriesPerRequest: Int = None, removeRepeated=True):
|
5068
6050
|
maxCalls = None
|
5069
6051
|
maxCalls, params = self.handle_option_and_params(params, method, 'paginationCalls', 10)
|
5070
6052
|
maxRetries = None
|
@@ -5072,6 +6054,8 @@ class Exchange(object):
|
|
5072
6054
|
paginationDirection = None
|
5073
6055
|
paginationDirection, params = self.handle_option_and_params(params, method, 'paginationDirection', 'backward')
|
5074
6056
|
paginationTimestamp = None
|
6057
|
+
removeRepeatedOption = removeRepeated
|
6058
|
+
removeRepeatedOption, params = self.handle_option_and_params(params, method, 'removeRepeated', removeRepeated)
|
5075
6059
|
calls = 0
|
5076
6060
|
result = []
|
5077
6061
|
errors = 0
|
@@ -5118,14 +6102,16 @@ class Exchange(object):
|
|
5118
6102
|
errors = 0
|
5119
6103
|
result = self.array_concat(result, response)
|
5120
6104
|
last = self.safe_value(response, responseLength - 1)
|
5121
|
-
paginationTimestamp = self.safe_integer(last, 'timestamp')
|
6105
|
+
paginationTimestamp = self.safe_integer(last, 'timestamp') + 1
|
5122
6106
|
if (until is not None) and (paginationTimestamp >= until):
|
5123
6107
|
break
|
5124
6108
|
except Exception as e:
|
5125
6109
|
errors += 1
|
5126
6110
|
if errors > maxRetries:
|
5127
6111
|
raise e
|
5128
|
-
uniqueResults =
|
6112
|
+
uniqueResults = result
|
6113
|
+
if removeRepeatedOption:
|
6114
|
+
uniqueResults = self.remove_repeated_elements_from_array(result)
|
5129
6115
|
key = 0 if (method == 'fetchOHLCV') else 'timestamp'
|
5130
6116
|
return self.filter_by_since_limit(uniqueResults, since, limit, key)
|
5131
6117
|
|
@@ -5133,18 +6119,19 @@ class Exchange(object):
|
|
5133
6119
|
maxRetries = None
|
5134
6120
|
maxRetries, params = self.handle_option_and_params(params, method, 'maxRetries', 3)
|
5135
6121
|
errors = 0
|
5136
|
-
|
5137
|
-
|
5138
|
-
|
5139
|
-
|
5140
|
-
|
5141
|
-
|
5142
|
-
|
5143
|
-
|
5144
|
-
|
5145
|
-
|
5146
|
-
|
5147
|
-
|
6122
|
+
while(errors <= maxRetries):
|
6123
|
+
try:
|
6124
|
+
if timeframe and method != 'fetchFundingRateHistory':
|
6125
|
+
return getattr(self, method)(symbol, timeframe, since, limit, params)
|
6126
|
+
else:
|
6127
|
+
return getattr(self, method)(symbol, since, limit, params)
|
6128
|
+
except Exception as e:
|
6129
|
+
if isinstance(e, RateLimitExceeded):
|
6130
|
+
raise e # if we are rate limited, we should not retry and fail fast
|
6131
|
+
errors += 1
|
6132
|
+
if errors > maxRetries:
|
6133
|
+
raise e
|
6134
|
+
return []
|
5148
6135
|
|
5149
6136
|
def fetch_paginated_call_deterministic(self, method: str, symbol: Str = None, since: Int = None, limit: Int = None, timeframe: Str = None, params={}, maxEntriesPerRequest=None):
|
5150
6137
|
maxCalls = None
|
@@ -5157,6 +6144,8 @@ class Exchange(object):
|
|
5157
6144
|
currentSince = current - (maxCalls * step) - 1
|
5158
6145
|
if since is not None:
|
5159
6146
|
currentSince = max(currentSince, since)
|
6147
|
+
else:
|
6148
|
+
currentSince = max(currentSince, 1241440531000) # avoid timestamps older than 2009
|
5160
6149
|
until = self.safe_integer_2(params, 'until', 'till') # do not omit it here
|
5161
6150
|
if until is not None:
|
5162
6151
|
requiredCalls = int(math.ceil((until - since)) / step)
|
@@ -5165,6 +6154,8 @@ class Exchange(object):
|
|
5165
6154
|
for i in range(0, maxCalls):
|
5166
6155
|
if (until is not None) and (currentSince >= until):
|
5167
6156
|
break
|
6157
|
+
if currentSince >= current:
|
6158
|
+
break
|
5168
6159
|
tasks.append(self.safe_deterministic_call(method, symbol, currentSince, maxEntriesPerRequest, timeframe, params))
|
5169
6160
|
currentSince = self.sum(currentSince, step) - 1
|
5170
6161
|
results = tasks
|
@@ -5185,28 +6176,44 @@ class Exchange(object):
|
|
5185
6176
|
i = 0
|
5186
6177
|
errors = 0
|
5187
6178
|
result = []
|
6179
|
+
timeframe = self.safe_string(params, 'timeframe')
|
6180
|
+
params = self.omit(params, 'timeframe') # reading the timeframe from the method arguments to avoid changing the signature
|
5188
6181
|
while(i < maxCalls):
|
5189
6182
|
try:
|
5190
6183
|
if cursorValue is not None:
|
5191
6184
|
if cursorIncrement is not None:
|
5192
|
-
cursorValue = self.
|
6185
|
+
cursorValue = self.parse_to_int(cursorValue) + cursorIncrement
|
5193
6186
|
params[cursorSent] = cursorValue
|
5194
6187
|
response = None
|
5195
6188
|
if method == 'fetchAccounts':
|
5196
6189
|
response = getattr(self, method)(params)
|
6190
|
+
elif method == 'getLeverageTiersPaginated' or method == 'fetchPositions':
|
6191
|
+
response = getattr(self, method)(symbol, params)
|
6192
|
+
elif method == 'fetchOpenInterestHistory':
|
6193
|
+
response = getattr(self, method)(symbol, timeframe, since, maxEntriesPerRequest, params)
|
5197
6194
|
else:
|
5198
6195
|
response = getattr(self, method)(symbol, since, maxEntriesPerRequest, params)
|
5199
6196
|
errors = 0
|
5200
6197
|
responseLength = len(response)
|
5201
6198
|
if self.verbose:
|
5202
|
-
|
5203
|
-
|
6199
|
+
cursorString = '' if (cursorValue is None) else cursorValue
|
6200
|
+
iteration = (i + 1)
|
6201
|
+
cursorMessage = 'Cursor pagination call ' + str(iteration) + ' method ' + method + ' response length ' + str(responseLength) + ' cursor ' + cursorString
|
5204
6202
|
self.log(cursorMessage)
|
5205
6203
|
if responseLength == 0:
|
5206
6204
|
break
|
5207
6205
|
result = self.array_concat(result, response)
|
5208
|
-
last = self.
|
5209
|
-
cursorValue = self.safe_value(last['info'], cursorReceived)
|
6206
|
+
last = self.safe_dict(response, responseLength - 1)
|
6207
|
+
# cursorValue = self.safe_value(last['info'], cursorReceived)
|
6208
|
+
cursorValue = None # search for the cursor
|
6209
|
+
for j in range(0, responseLength):
|
6210
|
+
index = responseLength - j - 1
|
6211
|
+
entry = self.safe_dict(response, index)
|
6212
|
+
info = self.safe_dict(entry, 'info')
|
6213
|
+
cursor = self.safe_value(info, cursorReceived)
|
6214
|
+
if cursor is not None:
|
6215
|
+
cursorValue = cursor
|
6216
|
+
break
|
5210
6217
|
if cursorValue is None:
|
5211
6218
|
break
|
5212
6219
|
lastTimestamp = self.safe_integer(last, 'timestamp')
|
@@ -5217,7 +6224,7 @@ class Exchange(object):
|
|
5217
6224
|
if errors > maxRetries:
|
5218
6225
|
raise e
|
5219
6226
|
i += 1
|
5220
|
-
sorted = self.
|
6227
|
+
sorted = self.sort_cursor_paginated_result(result)
|
5221
6228
|
key = 0 if (method == 'fetchOHLCV') else 'timestamp'
|
5222
6229
|
return self.filter_by_since_limit(sorted, since, limit, key)
|
5223
6230
|
|
@@ -5248,7 +6255,7 @@ class Exchange(object):
|
|
5248
6255
|
if errors > maxRetries:
|
5249
6256
|
raise e
|
5250
6257
|
i += 1
|
5251
|
-
sorted = self.
|
6258
|
+
sorted = self.sort_cursor_paginated_result(result)
|
5252
6259
|
key = 0 if (method == 'fetchOHLCV') else 'timestamp'
|
5253
6260
|
return self.filter_by_since_limit(sorted, since, limit, key)
|
5254
6261
|
|
@@ -5283,13 +6290,16 @@ class Exchange(object):
|
|
5283
6290
|
def handle_until_option(self, key: str, request, params, multiplier=1):
|
5284
6291
|
until = self.safe_integer_2(params, 'until', 'till')
|
5285
6292
|
if until is not None:
|
5286
|
-
request[key] = self.
|
6293
|
+
request[key] = self.parse_to_int(until * multiplier)
|
5287
6294
|
params = self.omit(params, ['until', 'till'])
|
5288
6295
|
return [request, params]
|
5289
6296
|
|
5290
|
-
def safe_open_interest(self, interest, market: Market = None):
|
6297
|
+
def safe_open_interest(self, interest: dict, market: Market = None):
|
6298
|
+
symbol = self.safe_string(interest, 'symbol')
|
6299
|
+
if symbol is None:
|
6300
|
+
symbol = self.safe_string(market, 'symbol')
|
5291
6301
|
return self.extend(interest, {
|
5292
|
-
'symbol':
|
6302
|
+
'symbol': symbol,
|
5293
6303
|
'baseVolume': self.safe_number(interest, 'baseVolume'), # deprecated
|
5294
6304
|
'quoteVolume': self.safe_number(interest, 'quoteVolume'), # deprecated
|
5295
6305
|
'openInterestAmount': self.safe_number(interest, 'openInterestAmount'),
|
@@ -5302,9 +6312,9 @@ class Exchange(object):
|
|
5302
6312
|
def parse_liquidation(self, liquidation, market: Market = None):
|
5303
6313
|
raise NotSupported(self.id + ' parseLiquidation() is not supported yet')
|
5304
6314
|
|
5305
|
-
def parse_liquidations(self, liquidations, market=None, since: Int = None, limit: Int = None):
|
6315
|
+
def parse_liquidations(self, liquidations: List[dict], market: Market = None, since: Int = None, limit: Int = None):
|
5306
6316
|
"""
|
5307
|
-
|
6317
|
+
@ignore
|
5308
6318
|
parses liquidation info from the exchange response
|
5309
6319
|
:param dict[] liquidations: each item describes an instance of a liquidation event
|
5310
6320
|
:param dict market: ccxt market
|
@@ -5315,17 +6325,33 @@ class Exchange(object):
|
|
5315
6325
|
result = []
|
5316
6326
|
for i in range(0, len(liquidations)):
|
5317
6327
|
entry = liquidations[i]
|
5318
|
-
parsed = self.
|
6328
|
+
parsed = self.parse_liquidation(entry, market)
|
5319
6329
|
result.append(parsed)
|
5320
6330
|
sorted = self.sort_by(result, 'timestamp')
|
5321
6331
|
symbol = self.safe_string(market, 'symbol')
|
5322
6332
|
return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
|
5323
6333
|
|
5324
|
-
def parse_greeks(self, greeks, market: Market = None):
|
6334
|
+
def parse_greeks(self, greeks: dict, market: Market = None):
|
5325
6335
|
raise NotSupported(self.id + ' parseGreeks() is not supported yet')
|
5326
6336
|
|
6337
|
+
def parse_option(self, chain: dict, currency: Currency = None, market: Market = None):
|
6338
|
+
raise NotSupported(self.id + ' parseOption() is not supported yet')
|
6339
|
+
|
6340
|
+
def parse_option_chain(self, response: List[object], currencyKey: Str = None, symbolKey: Str = None):
|
6341
|
+
optionStructures = {}
|
6342
|
+
for i in range(0, len(response)):
|
6343
|
+
info = response[i]
|
6344
|
+
currencyId = self.safe_string(info, currencyKey)
|
6345
|
+
currency = self.safe_currency(currencyId)
|
6346
|
+
marketId = self.safe_string(info, symbolKey)
|
6347
|
+
market = self.safe_market(marketId, None, None, 'option')
|
6348
|
+
optionStructures[market['symbol']] = self.parse_option(info, currency, market)
|
6349
|
+
return optionStructures
|
6350
|
+
|
5327
6351
|
def parse_margin_modes(self, response: List[object], symbols: List[str] = None, symbolKey: Str = None, marketType: MarketType = None):
|
5328
6352
|
marginModeStructures = {}
|
6353
|
+
if marketType is None:
|
6354
|
+
marketType = 'swap' # default to swap
|
5329
6355
|
for i in range(0, len(response)):
|
5330
6356
|
info = response[i]
|
5331
6357
|
marketId = self.safe_string(info, symbolKey)
|
@@ -5334,11 +6360,13 @@ class Exchange(object):
|
|
5334
6360
|
marginModeStructures[market['symbol']] = self.parse_margin_mode(info, market)
|
5335
6361
|
return marginModeStructures
|
5336
6362
|
|
5337
|
-
def parse_margin_mode(self, marginMode, market: Market = None):
|
6363
|
+
def parse_margin_mode(self, marginMode: dict, market: Market = None):
|
5338
6364
|
raise NotSupported(self.id + ' parseMarginMode() is not supported yet')
|
5339
6365
|
|
5340
6366
|
def parse_leverages(self, response: List[object], symbols: List[str] = None, symbolKey: Str = None, marketType: MarketType = None):
|
5341
6367
|
leverageStructures = {}
|
6368
|
+
if marketType is None:
|
6369
|
+
marketType = 'swap' # default to swap
|
5342
6370
|
for i in range(0, len(response)):
|
5343
6371
|
info = response[i]
|
5344
6372
|
marketId = self.safe_string(info, symbolKey)
|
@@ -5347,5 +6375,219 @@ class Exchange(object):
|
|
5347
6375
|
leverageStructures[market['symbol']] = self.parse_leverage(info, market)
|
5348
6376
|
return leverageStructures
|
5349
6377
|
|
5350
|
-
def parse_leverage(self, leverage, market: Market = None):
|
6378
|
+
def parse_leverage(self, leverage: dict, market: Market = None):
|
5351
6379
|
raise NotSupported(self.id + ' parseLeverage() is not supported yet')
|
6380
|
+
|
6381
|
+
def parse_conversions(self, conversions: List[Any], code: Str = None, fromCurrencyKey: Str = None, toCurrencyKey: Str = None, since: Int = None, limit: Int = None, params={}):
|
6382
|
+
conversions = self.to_array(conversions)
|
6383
|
+
result = []
|
6384
|
+
fromCurrency = None
|
6385
|
+
toCurrency = None
|
6386
|
+
for i in range(0, len(conversions)):
|
6387
|
+
entry = conversions[i]
|
6388
|
+
fromId = self.safe_string(entry, fromCurrencyKey)
|
6389
|
+
toId = self.safe_string(entry, toCurrencyKey)
|
6390
|
+
if fromId is not None:
|
6391
|
+
fromCurrency = self.safe_currency(fromId)
|
6392
|
+
if toId is not None:
|
6393
|
+
toCurrency = self.safe_currency(toId)
|
6394
|
+
conversion = self.extend(self.parse_conversion(entry, fromCurrency, toCurrency), params)
|
6395
|
+
result.append(conversion)
|
6396
|
+
sorted = self.sort_by(result, 'timestamp')
|
6397
|
+
currency = None
|
6398
|
+
if code is not None:
|
6399
|
+
currency = self.safe_currency(code)
|
6400
|
+
code = currency['code']
|
6401
|
+
if code is None:
|
6402
|
+
return self.filter_by_since_limit(sorted, since, limit)
|
6403
|
+
fromConversion = self.filter_by(sorted, 'fromCurrency', code)
|
6404
|
+
toConversion = self.filter_by(sorted, 'toCurrency', code)
|
6405
|
+
both = self.array_concat(fromConversion, toConversion)
|
6406
|
+
return self.filter_by_since_limit(both, since, limit)
|
6407
|
+
|
6408
|
+
def parse_conversion(self, conversion: dict, fromCurrency: Currency = None, toCurrency: Currency = None):
|
6409
|
+
raise NotSupported(self.id + ' parseConversion() is not supported yet')
|
6410
|
+
|
6411
|
+
def convert_expire_date(self, date: str):
|
6412
|
+
# parse YYMMDD to datetime string
|
6413
|
+
year = date[0:2]
|
6414
|
+
month = date[2:4]
|
6415
|
+
day = date[4:6]
|
6416
|
+
reconstructedDate = '20' + year + '-' + month + '-' + day + 'T00:00:00Z'
|
6417
|
+
return reconstructedDate
|
6418
|
+
|
6419
|
+
def convert_expire_date_to_market_id_date(self, date: str):
|
6420
|
+
# parse 240119 to 19JAN24
|
6421
|
+
year = date[0:2]
|
6422
|
+
monthRaw = date[2:4]
|
6423
|
+
month = None
|
6424
|
+
day = date[4:6]
|
6425
|
+
if monthRaw == '01':
|
6426
|
+
month = 'JAN'
|
6427
|
+
elif monthRaw == '02':
|
6428
|
+
month = 'FEB'
|
6429
|
+
elif monthRaw == '03':
|
6430
|
+
month = 'MAR'
|
6431
|
+
elif monthRaw == '04':
|
6432
|
+
month = 'APR'
|
6433
|
+
elif monthRaw == '05':
|
6434
|
+
month = 'MAY'
|
6435
|
+
elif monthRaw == '06':
|
6436
|
+
month = 'JUN'
|
6437
|
+
elif monthRaw == '07':
|
6438
|
+
month = 'JUL'
|
6439
|
+
elif monthRaw == '08':
|
6440
|
+
month = 'AUG'
|
6441
|
+
elif monthRaw == '09':
|
6442
|
+
month = 'SEP'
|
6443
|
+
elif monthRaw == '10':
|
6444
|
+
month = 'OCT'
|
6445
|
+
elif monthRaw == '11':
|
6446
|
+
month = 'NOV'
|
6447
|
+
elif monthRaw == '12':
|
6448
|
+
month = 'DEC'
|
6449
|
+
reconstructedDate = day + month + year
|
6450
|
+
return reconstructedDate
|
6451
|
+
|
6452
|
+
def convert_market_id_expire_date(self, date: str):
|
6453
|
+
# parse 03JAN24 to 240103
|
6454
|
+
monthMappping = {
|
6455
|
+
'JAN': '01',
|
6456
|
+
'FEB': '02',
|
6457
|
+
'MAR': '03',
|
6458
|
+
'APR': '04',
|
6459
|
+
'MAY': '05',
|
6460
|
+
'JUN': '06',
|
6461
|
+
'JUL': '07',
|
6462
|
+
'AUG': '08',
|
6463
|
+
'SEP': '09',
|
6464
|
+
'OCT': '10',
|
6465
|
+
'NOV': '11',
|
6466
|
+
'DEC': '12',
|
6467
|
+
}
|
6468
|
+
# if exchange omits first zero and provides i.e. '3JAN24' instead of '03JAN24'
|
6469
|
+
if len(date) == 6:
|
6470
|
+
date = '0' + date
|
6471
|
+
year = date[0:2]
|
6472
|
+
monthName = date[2:5]
|
6473
|
+
month = self.safe_string(monthMappping, monthName)
|
6474
|
+
day = date[5:7]
|
6475
|
+
reconstructedDate = day + month + year
|
6476
|
+
return reconstructedDate
|
6477
|
+
|
6478
|
+
def fetch_position_history(self, symbol: str, since: Int = None, limit: Int = None, params={}):
|
6479
|
+
"""
|
6480
|
+
fetches the history of margin added or reduced from contract isolated positions
|
6481
|
+
:param str [symbol]: unified market symbol
|
6482
|
+
:param int [since]: timestamp in ms of the position
|
6483
|
+
:param int [limit]: the maximum amount of candles to fetch, default=1000
|
6484
|
+
:param dict params: extra parameters specific to the exchange api endpoint
|
6485
|
+
:returns dict[]: a list of `position structures <https://docs.ccxt.com/#/?id=position-structure>`
|
6486
|
+
"""
|
6487
|
+
if self.has['fetchPositionsHistory']:
|
6488
|
+
positions = self.fetch_positions_history([symbol], since, limit, params)
|
6489
|
+
return positions
|
6490
|
+
else:
|
6491
|
+
raise NotSupported(self.id + ' fetchPositionHistory() is not supported yet')
|
6492
|
+
|
6493
|
+
def fetch_positions_history(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}):
|
6494
|
+
"""
|
6495
|
+
fetches the history of margin added or reduced from contract isolated positions
|
6496
|
+
:param str [symbol]: unified market symbol
|
6497
|
+
:param int [since]: timestamp in ms of the position
|
6498
|
+
:param int [limit]: the maximum amount of candles to fetch, default=1000
|
6499
|
+
:param dict params: extra parameters specific to the exchange api endpoint
|
6500
|
+
:returns dict[]: a list of `position structures <https://docs.ccxt.com/#/?id=position-structure>`
|
6501
|
+
"""
|
6502
|
+
raise NotSupported(self.id + ' fetchPositionsHistory() is not supported yet')
|
6503
|
+
|
6504
|
+
def parse_margin_modification(self, data: dict, market: Market = None):
|
6505
|
+
raise NotSupported(self.id + ' parseMarginModification() is not supported yet')
|
6506
|
+
|
6507
|
+
def parse_margin_modifications(self, response: List[object], symbols: Strings = None, symbolKey: Str = None, marketType: MarketType = None):
|
6508
|
+
marginModifications = []
|
6509
|
+
for i in range(0, len(response)):
|
6510
|
+
info = response[i]
|
6511
|
+
marketId = self.safe_string(info, symbolKey)
|
6512
|
+
market = self.safe_market(marketId, None, None, marketType)
|
6513
|
+
if (symbols is None) or self.in_array(market['symbol'], symbols):
|
6514
|
+
marginModifications.append(self.parse_margin_modification(info, market))
|
6515
|
+
return marginModifications
|
6516
|
+
|
6517
|
+
def fetch_transfer(self, id: str, code: Str = None, params={}):
|
6518
|
+
"""
|
6519
|
+
fetches a transfer
|
6520
|
+
:param str id: transfer id
|
6521
|
+
:param [str] code: unified currency code
|
6522
|
+
:param dict params: extra parameters specific to the exchange api endpoint
|
6523
|
+
:returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
|
6524
|
+
"""
|
6525
|
+
raise NotSupported(self.id + ' fetchTransfer() is not supported yet')
|
6526
|
+
|
6527
|
+
def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
6528
|
+
"""
|
6529
|
+
fetches a transfer
|
6530
|
+
:param str id: transfer id
|
6531
|
+
:param int [since]: timestamp in ms of the earliest transfer to fetch
|
6532
|
+
:param int [limit]: the maximum amount of transfers to fetch
|
6533
|
+
:param dict params: extra parameters specific to the exchange api endpoint
|
6534
|
+
:returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
|
6535
|
+
"""
|
6536
|
+
raise NotSupported(self.id + ' fetchTransfers() is not supported yet')
|
6537
|
+
|
6538
|
+
def clean_unsubscription(self, client, subHash: str, unsubHash: str):
|
6539
|
+
if unsubHash in client.subscriptions:
|
6540
|
+
del client.subscriptions[unsubHash]
|
6541
|
+
if subHash in client.subscriptions:
|
6542
|
+
del client.subscriptions[subHash]
|
6543
|
+
if subHash in client.futures:
|
6544
|
+
error = UnsubscribeError(self.id + ' ' + subHash)
|
6545
|
+
client.reject(error, subHash)
|
6546
|
+
client.resolve(True, unsubHash)
|
6547
|
+
|
6548
|
+
def clean_cache(self, subscription: dict):
|
6549
|
+
topic = self.safe_string(subscription, 'topic')
|
6550
|
+
symbols = self.safe_list(subscription, 'symbols', [])
|
6551
|
+
symbolsLength = len(symbols)
|
6552
|
+
if topic == 'ohlcv':
|
6553
|
+
symbolsAndTimeFrames = self.safe_list(subscription, 'symbolsAndTimeframes', [])
|
6554
|
+
for i in range(0, len(symbolsAndTimeFrames)):
|
6555
|
+
symbolAndTimeFrame = symbolsAndTimeFrames[i]
|
6556
|
+
symbol = self.safe_string(symbolAndTimeFrame, 0)
|
6557
|
+
timeframe = self.safe_string(symbolAndTimeFrame, 1)
|
6558
|
+
if symbol in self.ohlcvs:
|
6559
|
+
if timeframe in self.ohlcvs[symbol]:
|
6560
|
+
del self.ohlcvs[symbol][timeframe]
|
6561
|
+
elif symbolsLength > 0:
|
6562
|
+
for i in range(0, len(symbols)):
|
6563
|
+
symbol = symbols[i]
|
6564
|
+
if topic == 'trades':
|
6565
|
+
if symbol in self.trades:
|
6566
|
+
del self.trades[symbol]
|
6567
|
+
elif topic == 'orderbook':
|
6568
|
+
if symbol in self.orderbooks:
|
6569
|
+
del self.orderbooks[symbol]
|
6570
|
+
elif topic == 'ticker':
|
6571
|
+
if symbol in self.tickers:
|
6572
|
+
del self.tickers[symbol]
|
6573
|
+
else:
|
6574
|
+
if topic == 'myTrades':
|
6575
|
+
# don't reset self.myTrades directly here
|
6576
|
+
# because in c# we need to use a different object(thread-safe dict)
|
6577
|
+
keys = list(self.myTrades.keys())
|
6578
|
+
for i in range(0, len(keys)):
|
6579
|
+
key = keys[i]
|
6580
|
+
if key in self.myTrades:
|
6581
|
+
del self.myTrades[key]
|
6582
|
+
elif topic == 'orders':
|
6583
|
+
orderSymbols = list(self.orders.keys())
|
6584
|
+
for i in range(0, len(orderSymbols)):
|
6585
|
+
orderSymbol = orderSymbols[i]
|
6586
|
+
if orderSymbol in self.orders:
|
6587
|
+
del self.orders[orderSymbol]
|
6588
|
+
elif topic == 'ticker':
|
6589
|
+
tickerSymbols = list(self.tickers.keys())
|
6590
|
+
for i in range(0, len(tickerSymbols)):
|
6591
|
+
tickerSymbol = tickerSymbols[i]
|
6592
|
+
if tickerSymbol in self.tickers:
|
6593
|
+
del self.tickers[tickerSymbol]
|