ccxt 4.2.76__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 +25 -0
- ccxt/abstract/kucoinfutures.py +35 -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 +3513 -1511
- 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 +3105 -881
- 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 +239 -50
- ccxt/async_support/bitget.py +1513 -563
- ccxt/async_support/bithumb.py +201 -67
- 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 +403 -150
- 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 +2326 -1255
- ccxt/async_support/cex.py +1409 -1329
- ccxt/async_support/coinbase.py +1455 -288
- 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 +467 -158
- ccxt/async_support/deribit.py +558 -324
- 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 +1473 -464
- 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 +1634 -269
- 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 +1050 -355
- 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 +1777 -455
- 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 +1155 -295
- 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 +1729 -482
- 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 +3513 -1511
- ccxt/binancecoinm.py +2 -1
- ccxt/binanceus.py +12 -1
- ccxt/binanceusdm.py +3 -1
- ccxt/bingx.py +3105 -881
- 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 +239 -50
- ccxt/bitget.py +1513 -563
- ccxt/bithumb.py +200 -67
- 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 +403 -150
- ccxt/btcalpha.py +161 -55
- ccxt/btcbox.py +250 -34
- ccxt/btcmarkets.py +232 -85
- ccxt/btcturk.py +159 -60
- ccxt/bybit.py +2326 -1255
- ccxt/cex.py +1408 -1329
- ccxt/coinbase.py +1455 -288
- 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 +467 -158
- ccxt/deribit.py +558 -324
- ccxt/digifinex.py +340 -223
- ccxt/ellipx.py +1826 -0
- ccxt/exmo.py +259 -128
- ccxt/gate.py +1473 -464
- 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 +1633 -269
- 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 +1050 -355
- 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 +1777 -455
- 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 +63 -15
- 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 +204 -82
- 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 +967 -661
- 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 +168 -32
- ccxt/pro/exmo.py +253 -21
- 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 +93 -34
- ccxt/pro/poloniex.py +129 -50
- ccxt/pro/poloniexfutures.py +53 -32
- ccxt/pro/probit.py +93 -86
- 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 +486 -70
- 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} +465 -407
- ccxt/test/tests_helpers.py +285 -0
- ccxt/test/tests_init.py +39 -0
- ccxt/test/{test_sync.py → tests_sync.py} +465 -409
- 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 +1155 -295
- 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.76.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.76.dist-info/METADATA +0 -626
- ccxt-4.2.76.dist-info/RECORD +0 -534
- {ccxt-4.2.76.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
|
|
@@ -2287,14 +2718,107 @@ class Exchange(object):
|
|
2287
2718
|
return float(stringVersion)
|
2288
2719
|
return int(stringVersion)
|
2289
2720
|
|
2290
|
-
def is_round_number(self, value):
|
2721
|
+
def is_round_number(self, value: float):
|
2291
2722
|
# self method is similar to isInteger, but self is more loyal and does not check for types.
|
2292
2723
|
# i.e. isRoundNumber(1.000) returns True, while isInteger(1.000) returns False
|
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={}):
|
@@ -3109,7 +3671,7 @@ class Exchange(object):
|
|
3109
3671
|
def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
3110
3672
|
raise NotSupported(self.id + ' watchOHLCV() is not supported yet')
|
3111
3673
|
|
3112
|
-
def convert_trading_view_to_ohlcv(self, ohlcvs, timestamp='t', open='o', high='h', low='l', close='c', volume='v', ms=False):
|
3674
|
+
def convert_trading_view_to_ohlcv(self, ohlcvs: List[List[float]], timestamp='t', open='o', high='h', low='l', close='c', volume='v', ms=False):
|
3113
3675
|
result = []
|
3114
3676
|
timestamps = self.safe_list(ohlcvs, timestamp, [])
|
3115
3677
|
opens = self.safe_list(ohlcvs, open, [])
|
@@ -3128,7 +3690,7 @@ class Exchange(object):
|
|
3128
3690
|
])
|
3129
3691
|
return result
|
3130
3692
|
|
3131
|
-
def convert_ohlcv_to_trading_view(self, ohlcvs, timestamp='t', open='o', high='h', low='l', close='c', volume='v', ms=False):
|
3693
|
+
def convert_ohlcv_to_trading_view(self, ohlcvs: List[List[float]], timestamp='t', open='o', high='h', low='l', close='c', volume='v', ms=False):
|
3132
3694
|
result = {}
|
3133
3695
|
result[timestamp] = []
|
3134
3696
|
result[open] = []
|
@@ -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={}):
|
4356
|
+
raise NotSupported(self.id + ' fetchPositions() is not supported yet')
|
4357
|
+
|
4358
|
+
def fetch_positions_ws(self, symbols: Strings = None, params={}):
|
3704
4359
|
raise NotSupported(self.id + ' fetchPositions() is not supported yet')
|
3705
4360
|
|
3706
|
-
def fetch_positions_risk(self, symbols:
|
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({
|
@@ -3779,6 +4434,11 @@ class Exchange(object):
|
|
3779
4434
|
return result
|
3780
4435
|
|
3781
4436
|
def check_required_credentials(self, error=True):
|
4437
|
+
"""
|
4438
|
+
@ignore
|
4439
|
+
:param boolean error: raise an error that a credential is required if True
|
4440
|
+
:returns boolean: True if all required credentials have been set, otherwise False or an error is thrown is param error=true
|
4441
|
+
"""
|
3782
4442
|
keys = list(self.requiredCredentials.keys())
|
3783
4443
|
for i in range(0, len(keys)):
|
3784
4444
|
key = keys[i]
|
@@ -3823,33 +4483,21 @@ class Exchange(object):
|
|
3823
4483
|
def fetch_status(self, params={}):
|
3824
4484
|
raise NotSupported(self.id + ' fetchStatus() is not supported yet')
|
3825
4485
|
|
3826
|
-
def fetch_funding_fee(self, code: str, params={}):
|
3827
|
-
warnOnFetchFundingFee = self.safe_bool(self.options, 'warnOnFetchFundingFee', True)
|
3828
|
-
if warnOnFetchFundingFee:
|
3829
|
-
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')
|
3830
|
-
return self.fetch_transaction_fee(code, params)
|
3831
|
-
|
3832
|
-
def fetch_funding_fees(self, codes: List[str] = None, params={}):
|
3833
|
-
warnOnFetchFundingFees = self.safe_bool(self.options, 'warnOnFetchFundingFees', True)
|
3834
|
-
if warnOnFetchFundingFees:
|
3835
|
-
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')
|
3836
|
-
return self.fetch_transaction_fees(codes, params)
|
3837
|
-
|
3838
4486
|
def fetch_transaction_fee(self, code: str, params={}):
|
3839
4487
|
if not self.has['fetchTransactionFees']:
|
3840
4488
|
raise NotSupported(self.id + ' fetchTransactionFee() is not supported yet')
|
3841
4489
|
return self.fetch_transaction_fees([code], params)
|
3842
4490
|
|
3843
|
-
def fetch_transaction_fees(self, codes:
|
4491
|
+
def fetch_transaction_fees(self, codes: Strings = None, params={}):
|
3844
4492
|
raise NotSupported(self.id + ' fetchTransactionFees() is not supported yet')
|
3845
4493
|
|
3846
|
-
def fetch_deposit_withdraw_fees(self, codes:
|
4494
|
+
def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
|
3847
4495
|
raise NotSupported(self.id + ' fetchDepositWithdrawFees() is not supported yet')
|
3848
4496
|
|
3849
4497
|
def fetch_deposit_withdraw_fee(self, code: str, params={}):
|
3850
4498
|
if not self.has['fetchDepositWithdrawFees']:
|
3851
4499
|
raise NotSupported(self.id + ' fetchDepositWithdrawFee() is not supported yet')
|
3852
|
-
fees = self.
|
4500
|
+
fees = self.fetch_deposit_withdraw_fees([code], params)
|
3853
4501
|
return self.safe_value(fees, code)
|
3854
4502
|
|
3855
4503
|
def get_supported_mapping(self, key, mapping={}):
|
@@ -3862,7 +4510,7 @@ class Exchange(object):
|
|
3862
4510
|
self.load_markets()
|
3863
4511
|
if not self.has['fetchBorrowRates']:
|
3864
4512
|
raise NotSupported(self.id + ' fetchCrossBorrowRate() is not supported yet')
|
3865
|
-
borrowRates = self.
|
4513
|
+
borrowRates = self.fetch_cross_borrow_rates(params)
|
3866
4514
|
rate = self.safe_value(borrowRates, code)
|
3867
4515
|
if rate is None:
|
3868
4516
|
raise ExchangeError(self.id + ' fetchCrossBorrowRate() could not find the borrow rate for currency code ' + code)
|
@@ -3900,36 +4548,40 @@ class Exchange(object):
|
|
3900
4548
|
value = value if (value is not None) else defaultValue
|
3901
4549
|
return [value, params]
|
3902
4550
|
|
3903
|
-
def handle_option_and_params_2(self, params: object,
|
3904
|
-
|
3905
|
-
|
3906
|
-
# check if params contain the key
|
3907
|
-
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)
|
3908
4554
|
if value is not None:
|
3909
|
-
|
3910
|
-
|
3911
|
-
|
3912
|
-
|
3913
|
-
|
3914
|
-
|
3915
|
-
|
3916
|
-
if value is None:
|
3917
|
-
# if it's still None, check if global exchange-wide option exists
|
3918
|
-
value = self.safe_value_2(self.options, optionName, defaultOptionName)
|
3919
|
-
# if it's still None, use the default value
|
3920
|
-
value = value if (value is not None) else defaultValue
|
3921
|
-
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]
|
3922
4562
|
|
3923
4563
|
def handle_option(self, methodName: str, optionName: str, defaultValue=None):
|
3924
4564
|
# eslint-disable-next-line no-unused-vars
|
3925
4565
|
result, empty = self.handle_option_and_params({}, methodName, optionName, defaultValue)
|
3926
4566
|
return result
|
3927
4567
|
|
3928
|
-
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
|
+
"""
|
3929
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
|
3930
4582
|
methodOptions = self.safe_dict(self.options, methodName)
|
3931
|
-
methodType =
|
3932
|
-
if methodOptions is not None:
|
4583
|
+
methodType = defaultValue
|
4584
|
+
if methodOptions is not None: # user defined methodType takes precedence over defaultValue
|
3933
4585
|
if isinstance(methodOptions, str):
|
3934
4586
|
methodType = methodOptions
|
3935
4587
|
else:
|
@@ -3962,7 +4614,7 @@ class Exchange(object):
|
|
3962
4614
|
|
3963
4615
|
def handle_margin_mode_and_params(self, methodName: str, params={}, defaultValue=None):
|
3964
4616
|
"""
|
3965
|
-
|
4617
|
+
@ignore
|
3966
4618
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
3967
4619
|
:returns Array: the marginMode in lowercase by params["marginMode"], params["defaultMarginMode"] self.options["marginMode"] or self.options["defaultMarginMode"]
|
3968
4620
|
"""
|
@@ -3989,7 +4641,7 @@ class Exchange(object):
|
|
3989
4641
|
return key
|
3990
4642
|
return None
|
3991
4643
|
|
3992
|
-
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):
|
3993
4645
|
# it is a stub method that must be overrided in the derived exchange classes
|
3994
4646
|
# raise NotSupported(self.id + ' handleErrors() not implemented yet')
|
3995
4647
|
return None
|
@@ -4011,18 +4663,58 @@ class Exchange(object):
|
|
4011
4663
|
else:
|
4012
4664
|
raise NotSupported(self.id + ' fetchTicker() is not supported yet')
|
4013
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
|
+
|
4014
4694
|
def watch_ticker(self, symbol: str, params={}):
|
4015
4695
|
raise NotSupported(self.id + ' watchTicker() is not supported yet')
|
4016
4696
|
|
4017
|
-
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={}):
|
4018
4704
|
raise NotSupported(self.id + ' fetchTickers() is not supported yet')
|
4019
4705
|
|
4020
|
-
def fetch_order_books(self, symbols:
|
4706
|
+
def fetch_order_books(self, symbols: Strings = None, limit: Int = None, params={}):
|
4021
4707
|
raise NotSupported(self.id + ' fetchOrderBooks() is not supported yet')
|
4022
4708
|
|
4023
|
-
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={}):
|
4024
4713
|
raise NotSupported(self.id + ' watchTickers() is not supported yet')
|
4025
4714
|
|
4715
|
+
def un_watch_tickers(self, symbols: Strings = None, params={}):
|
4716
|
+
raise NotSupported(self.id + ' unWatchTickers() is not supported yet')
|
4717
|
+
|
4026
4718
|
def fetch_order(self, id: str, symbol: Str = None, params={}):
|
4027
4719
|
raise NotSupported(self.id + ' fetchOrder() is not supported yet')
|
4028
4720
|
|
@@ -4063,6 +4755,28 @@ class Exchange(object):
|
|
4063
4755
|
return self.create_order(symbol, type, side, amount, price, params)
|
4064
4756
|
raise NotSupported(self.id + ' createTrailingAmountOrder() is not supported yet')
|
4065
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
|
+
|
4066
4780
|
def create_trailing_percent_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent=None, trailingTriggerPrice=None, params={}):
|
4067
4781
|
"""
|
4068
4782
|
create a trailing order by providing the symbol, type, side, amount, price and trailingPercent
|
@@ -4085,6 +4799,28 @@ class Exchange(object):
|
|
4085
4799
|
return self.create_order(symbol, type, side, amount, price, params)
|
4086
4800
|
raise NotSupported(self.id + ' createTrailingPercentOrder() is not supported yet')
|
4087
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
|
+
|
4088
4824
|
def create_market_order_with_cost(self, symbol: str, side: OrderSide, cost: float, params={}):
|
4089
4825
|
"""
|
4090
4826
|
create a market order by providing the symbol, side and cost
|
@@ -4122,6 +4858,19 @@ class Exchange(object):
|
|
4122
4858
|
return self.create_order(symbol, 'market', 'sell', cost, 1, params)
|
4123
4859
|
raise NotSupported(self.id + ' createMarketSellOrderWithCost() is not supported yet')
|
4124
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
|
+
|
4125
4874
|
def create_trigger_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, triggerPrice: Num = None, params={}):
|
4126
4875
|
"""
|
4127
4876
|
create a trigger stop order(type 1)
|
@@ -4141,6 +4890,25 @@ class Exchange(object):
|
|
4141
4890
|
return self.create_order(symbol, type, side, amount, price, params)
|
4142
4891
|
raise NotSupported(self.id + ' createTriggerOrder() is not supported yet')
|
4143
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
|
+
|
4144
4912
|
def create_stop_loss_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, stopLossPrice: Num = None, params={}):
|
4145
4913
|
"""
|
4146
4914
|
create a trigger stop loss order(type 2)
|
@@ -4160,6 +4928,25 @@ class Exchange(object):
|
|
4160
4928
|
return self.create_order(symbol, type, side, amount, price, params)
|
4161
4929
|
raise NotSupported(self.id + ' createStopLossOrder() is not supported yet')
|
4162
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
|
+
|
4163
4950
|
def create_take_profit_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, takeProfitPrice: Num = None, params={}):
|
4164
4951
|
"""
|
4165
4952
|
create a trigger take profit order(type 2)
|
@@ -4179,6 +4966,25 @@ class Exchange(object):
|
|
4179
4966
|
return self.create_order(symbol, type, side, amount, price, params)
|
4180
4967
|
raise NotSupported(self.id + ' createTakeProfitOrder() is not supported yet')
|
4181
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
|
+
|
4182
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={}):
|
4183
4989
|
"""
|
4184
4990
|
create an order with a stop loss or take profit attached(type 3)
|
@@ -4200,6 +5006,12 @@ class Exchange(object):
|
|
4200
5006
|
:param float [params.stopLossAmount]: *not available on all exchanges* the amount for a stop loss
|
4201
5007
|
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
4202
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={}):
|
4203
5015
|
if (takeProfit is None) and (stopLoss is None):
|
4204
5016
|
raise ArgumentsRequired(self.id + ' createOrderWithTakeProfitAndStopLoss() requires either a takeProfit or stopLoss argument')
|
4205
5017
|
if takeProfit is not None:
|
@@ -4235,9 +5047,33 @@ class Exchange(object):
|
|
4235
5047
|
if stopLossAmount is not None:
|
4236
5048
|
params['stopLoss']['amount'] = self.parse_to_numeric(stopLossAmount)
|
4237
5049
|
params = self.omit(params, ['takeProfitType', 'takeProfitPriceType', 'takeProfitLimitPrice', 'takeProfitAmount', 'stopLossType', 'stopLossPriceType', 'stopLossLimitPrice', 'stopLossAmount'])
|
4238
|
-
|
4239
|
-
|
4240
|
-
|
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')
|
4241
5077
|
|
4242
5078
|
def create_orders(self, orders: List[OrderRequest], params={}):
|
4243
5079
|
raise NotSupported(self.id + ' createOrders() is not supported yet')
|
@@ -4257,11 +5093,17 @@ class Exchange(object):
|
|
4257
5093
|
def cancel_all_orders(self, symbol: Str = None, params={}):
|
4258
5094
|
raise NotSupported(self.id + ' cancelAllOrders() is not supported yet')
|
4259
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
|
+
|
4260
5102
|
def cancel_all_orders_ws(self, symbol: Str = None, params={}):
|
4261
5103
|
raise NotSupported(self.id + ' cancelAllOrdersWs() is not supported yet')
|
4262
5104
|
|
4263
5105
|
def cancel_unified_order(self, order, params={}):
|
4264
|
-
return self.
|
5106
|
+
return self.cancel_order(self.safe_string(order, 'id'), self.safe_string(order, 'symbol'), params)
|
4265
5107
|
|
4266
5108
|
def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
4267
5109
|
if self.has['fetchOpenOrders'] and self.has['fetchClosedOrders']:
|
@@ -4285,7 +5127,7 @@ class Exchange(object):
|
|
4285
5127
|
|
4286
5128
|
def fetch_open_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
4287
5129
|
if self.has['fetchOrdersWs']:
|
4288
|
-
orders = self.
|
5130
|
+
orders = self.fetch_orders_ws(symbol, since, limit, params)
|
4289
5131
|
return self.filter_by(orders, 'status', 'open')
|
4290
5132
|
raise NotSupported(self.id + ' fetchOpenOrdersWs() is not supported yet')
|
4291
5133
|
|
@@ -4300,7 +5142,7 @@ class Exchange(object):
|
|
4300
5142
|
|
4301
5143
|
def fetch_closed_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
4302
5144
|
if self.has['fetchOrdersWs']:
|
4303
|
-
orders = self.
|
5145
|
+
orders = self.fetch_orders_ws(symbol, since, limit, params)
|
4304
5146
|
return self.filter_by(orders, 'status', 'closed')
|
4305
5147
|
raise NotSupported(self.id + ' fetchClosedOrdersWs() is not supported yet')
|
4306
5148
|
|
@@ -4322,6 +5164,15 @@ class Exchange(object):
|
|
4322
5164
|
def fetch_greeks(self, symbol: str, params={}):
|
4323
5165
|
raise NotSupported(self.id + ' fetchGreeks() is not supported yet')
|
4324
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
|
+
|
4325
5176
|
def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
4326
5177
|
"""
|
4327
5178
|
fetch history of deposits and withdrawals
|
@@ -4365,7 +5216,7 @@ class Exchange(object):
|
|
4365
5216
|
|
4366
5217
|
def fetch_deposit_address(self, code: str, params={}):
|
4367
5218
|
if self.has['fetchDepositAddresses']:
|
4368
|
-
depositAddresses = self.
|
5219
|
+
depositAddresses = self.fetch_deposit_addresses([code], params)
|
4369
5220
|
depositAddress = self.safe_value(depositAddresses, code)
|
4370
5221
|
if depositAddress is None:
|
4371
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')
|
@@ -4374,7 +5225,7 @@ class Exchange(object):
|
|
4374
5225
|
elif self.has['fetchDepositAddressesByNetwork']:
|
4375
5226
|
network = self.safe_string(params, 'network')
|
4376
5227
|
params = self.omit(params, 'network')
|
4377
|
-
addressStructures = self.
|
5228
|
+
addressStructures = self.fetch_deposit_addresses_by_network(code, params)
|
4378
5229
|
if network is not None:
|
4379
5230
|
return self.safe_dict(addressStructures, network)
|
4380
5231
|
else:
|
@@ -4391,10 +5242,10 @@ class Exchange(object):
|
|
4391
5242
|
'total': None,
|
4392
5243
|
}
|
4393
5244
|
|
4394
|
-
def common_currency_code(self,
|
5245
|
+
def common_currency_code(self, code: str):
|
4395
5246
|
if not self.substituteCommonCurrencyCodes:
|
4396
|
-
return
|
4397
|
-
return self.safe_string(self.commonCurrencies,
|
5247
|
+
return code
|
5248
|
+
return self.safe_string(self.commonCurrencies, code, code)
|
4398
5249
|
|
4399
5250
|
def currency(self, code: str):
|
4400
5251
|
if self.currencies is None:
|
@@ -4420,7 +5271,7 @@ class Exchange(object):
|
|
4420
5271
|
return market
|
4421
5272
|
return markets[0]
|
4422
5273
|
elif (symbol.endswith('-C')) or (symbol.endswith('-P')) or (symbol.startswith('C-')) or (symbol.startswith('P-')):
|
4423
|
-
return self.
|
5274
|
+
return self.create_expired_option_market(symbol)
|
4424
5275
|
raise BadSymbol(self.id + ' does not have market symbol ' + symbol)
|
4425
5276
|
|
4426
5277
|
def create_expired_option_market(self, symbol: str):
|
@@ -4439,21 +5290,39 @@ class Exchange(object):
|
|
4439
5290
|
def create_limit_order(self, symbol: str, side: OrderSide, amount: float, price: float, params={}):
|
4440
5291
|
return self.create_order(symbol, 'limit', side, amount, price, params)
|
4441
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
|
+
|
4442
5296
|
def create_market_order(self, symbol: str, side: OrderSide, amount: float, price: Num = None, params={}):
|
4443
5297
|
return self.create_order(symbol, 'market', side, amount, price, params)
|
4444
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
|
+
|
4445
5302
|
def create_limit_buy_order(self, symbol: str, amount: float, price: float, params={}):
|
4446
5303
|
return self.create_order(symbol, 'limit', 'buy', amount, price, params)
|
4447
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
|
+
|
4448
5308
|
def create_limit_sell_order(self, symbol: str, amount: float, price: float, params={}):
|
4449
5309
|
return self.create_order(symbol, 'limit', 'sell', amount, price, params)
|
4450
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
|
+
|
4451
5314
|
def create_market_buy_order(self, symbol: str, amount: float, params={}):
|
4452
5315
|
return self.create_order(symbol, 'market', 'buy', amount, None, params)
|
4453
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
|
+
|
4454
5320
|
def create_market_sell_order(self, symbol: str, amount: float, params={}):
|
4455
5321
|
return self.create_order(symbol, 'market', 'sell', amount, None, params)
|
4456
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
|
+
|
4457
5326
|
def cost_to_precision(self, symbol: str, cost):
|
4458
5327
|
market = self.market(symbol)
|
4459
5328
|
return self.decimal_to_precision(cost, TRUNCATE, market['precision']['price'], self.precisionMode, self.paddingMode)
|
@@ -4484,9 +5353,10 @@ class Exchange(object):
|
|
4484
5353
|
networkItem = self.safe_dict(networks, networkCode, {})
|
4485
5354
|
precision = self.safe_value(networkItem, 'precision', precision)
|
4486
5355
|
if precision is None:
|
4487
|
-
return self.
|
5356
|
+
return self.force_string(fee)
|
4488
5357
|
else:
|
4489
|
-
|
5358
|
+
roundingMode = self.safe_integer(self.options, 'currencyToPrecisionRoundingMode', ROUND)
|
5359
|
+
return self.decimal_to_precision(fee, roundingMode, precision, self.precisionMode, self.paddingMode)
|
4490
5360
|
|
4491
5361
|
def force_string(self, value):
|
4492
5362
|
if not isinstance(value, str):
|
@@ -4502,7 +5372,7 @@ class Exchange(object):
|
|
4502
5372
|
def is_significant_precision(self):
|
4503
5373
|
return self.precisionMode == SIGNIFICANT_DIGITS
|
4504
5374
|
|
4505
|
-
def safe_number(self, obj
|
5375
|
+
def safe_number(self, obj, key: IndexType, defaultNumber: Num = None):
|
4506
5376
|
value = self.safe_string(obj, key)
|
4507
5377
|
return self.parse_number(value, defaultNumber)
|
4508
5378
|
|
@@ -4512,7 +5382,7 @@ class Exchange(object):
|
|
4512
5382
|
|
4513
5383
|
def parse_precision(self, precision: str):
|
4514
5384
|
"""
|
4515
|
-
|
5385
|
+
@ignore
|
4516
5386
|
:param str precision: The number of digits to the right of the decimal
|
4517
5387
|
:returns str: a string number equal to 1e-precision
|
4518
5388
|
"""
|
@@ -4526,6 +5396,25 @@ class Exchange(object):
|
|
4526
5396
|
parsedPrecision = parsedPrecision + '0'
|
4527
5397
|
return parsedPrecision + '1'
|
4528
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
|
+
|
4529
5418
|
def load_time_difference(self, params={}):
|
4530
5419
|
serverTime = self.fetch_time(params)
|
4531
5420
|
after = self.milliseconds()
|
@@ -4551,32 +5440,64 @@ class Exchange(object):
|
|
4551
5440
|
query = self.extend(params, {'postOnly': True})
|
4552
5441
|
return self.create_order(symbol, type, side, amount, price, query)
|
4553
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
|
+
|
4554
5449
|
def create_reduce_only_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
4555
5450
|
if not self.has['createReduceOnlyOrder']:
|
4556
5451
|
raise NotSupported(self.id + 'createReduceOnlyOrder() is not supported yet')
|
4557
5452
|
query = self.extend(params, {'reduceOnly': True})
|
4558
5453
|
return self.create_order(symbol, type, side, amount, price, query)
|
4559
5454
|
|
4560
|
-
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={}):
|
4561
5462
|
if not self.has['createStopOrder']:
|
4562
5463
|
raise NotSupported(self.id + ' createStopOrder() is not supported yet')
|
4563
|
-
if
|
5464
|
+
if triggerPrice is None:
|
4564
5465
|
raise ArgumentsRequired(self.id + ' create_stop_order() requires a stopPrice argument')
|
4565
|
-
query = self.extend(params, {'stopPrice':
|
5466
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
4566
5467
|
return self.create_order(symbol, type, side, amount, price, query)
|
4567
5468
|
|
4568
|
-
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={}):
|
4569
5478
|
if not self.has['createStopLimitOrder']:
|
4570
5479
|
raise NotSupported(self.id + ' createStopLimitOrder() is not supported yet')
|
4571
|
-
query = self.extend(params, {'stopPrice':
|
5480
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
4572
5481
|
return self.create_order(symbol, 'limit', side, amount, price, query)
|
4573
5482
|
|
4574
|
-
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={}):
|
4575
5490
|
if not self.has['createStopMarketOrder']:
|
4576
5491
|
raise NotSupported(self.id + ' createStopMarketOrder() is not supported yet')
|
4577
|
-
query = self.extend(params, {'stopPrice':
|
5492
|
+
query = self.extend(params, {'stopPrice': triggerPrice})
|
4578
5493
|
return self.create_order(symbol, 'market', side, amount, None, query)
|
4579
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
|
+
|
4580
5501
|
def safe_currency_code(self, currencyId: Str, currency: Currency = None):
|
4581
5502
|
currency = self.safe_currency(currencyId, currency)
|
4582
5503
|
return currency['code']
|
@@ -4614,22 +5535,23 @@ class Exchange(object):
|
|
4614
5535
|
results = []
|
4615
5536
|
if isinstance(pricesData, list):
|
4616
5537
|
for i in range(0, len(pricesData)):
|
4617
|
-
priceData = self.extend(self.
|
5538
|
+
priceData = self.extend(self.parse_last_price(pricesData[i]), params)
|
4618
5539
|
results.append(priceData)
|
4619
5540
|
else:
|
4620
5541
|
marketIds = list(pricesData.keys())
|
4621
5542
|
for i in range(0, len(marketIds)):
|
4622
5543
|
marketId = marketIds[i]
|
4623
5544
|
market = self.safe_market(marketId)
|
4624
|
-
priceData = self.extend(self.
|
5545
|
+
priceData = self.extend(self.parse_last_price(pricesData[marketId], market), params)
|
4625
5546
|
results.append(priceData)
|
4626
5547
|
symbols = self.market_symbols(symbols)
|
4627
5548
|
return self.filter_by_array(results, 'symbol', symbols)
|
4628
5549
|
|
4629
|
-
def parse_tickers(self, tickers, symbols:
|
5550
|
+
def parse_tickers(self, tickers, symbols: Strings = None, params={}):
|
4630
5551
|
#
|
4631
5552
|
# the value of tickers is either a dict or a list
|
4632
5553
|
#
|
5554
|
+
#
|
4633
5555
|
# dict
|
4634
5556
|
#
|
4635
5557
|
# {
|
@@ -4663,7 +5585,7 @@ class Exchange(object):
|
|
4663
5585
|
symbols = self.market_symbols(symbols)
|
4664
5586
|
return self.filter_by_array(results, 'symbol', symbols)
|
4665
5587
|
|
4666
|
-
def parse_deposit_addresses(self, addresses, codes:
|
5588
|
+
def parse_deposit_addresses(self, addresses, codes: Strings = None, indexed=True, params={}):
|
4667
5589
|
result = []
|
4668
5590
|
for i in range(0, len(addresses)):
|
4669
5591
|
address = self.extend(self.parse_deposit_address(addresses[i]), params)
|
@@ -4671,7 +5593,7 @@ class Exchange(object):
|
|
4671
5593
|
if codes is not None:
|
4672
5594
|
result = self.filter_by_array(result, 'currency', codes, False)
|
4673
5595
|
if indexed:
|
4674
|
-
|
5596
|
+
result = self.filter_by_array(result, 'currency', None, indexed)
|
4675
5597
|
return result
|
4676
5598
|
|
4677
5599
|
def parse_borrow_interests(self, response, market: Market = None):
|
@@ -4681,6 +5603,27 @@ class Exchange(object):
|
|
4681
5603
|
interests.append(self.parse_borrow_interest(row, market))
|
4682
5604
|
return interests
|
4683
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
|
+
|
4684
5627
|
def parse_funding_rate_histories(self, response, market=None, since: Int = None, limit: Int = None):
|
4685
5628
|
rates = []
|
4686
5629
|
for i in range(0, len(response)):
|
@@ -4697,22 +5640,39 @@ class Exchange(object):
|
|
4697
5640
|
def parse_funding_rate(self, contract: str, market: Market = None):
|
4698
5641
|
raise NotSupported(self.id + ' parseFundingRate() is not supported yet')
|
4699
5642
|
|
4700
|
-
def parse_funding_rates(self, response,
|
4701
|
-
|
5643
|
+
def parse_funding_rates(self, response, symbols: Strings = None):
|
5644
|
+
fundingRates = {}
|
4702
5645
|
for i in range(0, len(response)):
|
4703
|
-
|
4704
|
-
|
4705
|
-
|
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)
|
4706
5650
|
|
4707
|
-
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):
|
4708
5664
|
isTrigger = self.safe_bool_2(params, 'trigger', 'stop')
|
4709
5665
|
if isTrigger:
|
4710
5666
|
params = self.omit(params, ['trigger', 'stop'])
|
4711
5667
|
return [isTrigger, params]
|
4712
5668
|
|
5669
|
+
def is_trigger_order(self, params):
|
5670
|
+
# for backwards compatibility
|
5671
|
+
return self.handle_trigger_and_params(params)
|
5672
|
+
|
4713
5673
|
def is_post_only(self, isMarketOrder: bool, exchangeSpecificParam, params={}):
|
4714
5674
|
"""
|
4715
|
-
|
5675
|
+
@ignore
|
4716
5676
|
:param str type: Order type
|
4717
5677
|
:param boolean exchangeSpecificParam: exchange specific postOnly
|
4718
5678
|
:param dict [params]: exchange specific params
|
@@ -4737,7 +5697,7 @@ class Exchange(object):
|
|
4737
5697
|
|
4738
5698
|
def handle_post_only(self, isMarketOrder: bool, exchangeSpecificPostOnlyOption: bool, params: Any = {}):
|
4739
5699
|
"""
|
4740
|
-
|
5700
|
+
@ignore
|
4741
5701
|
:param str type: Order type
|
4742
5702
|
:param boolean exchangeSpecificBoolean: exchange specific postOnly
|
4743
5703
|
:param dict [params]: exchange specific params
|
@@ -4761,7 +5721,7 @@ class Exchange(object):
|
|
4761
5721
|
return [True, params]
|
4762
5722
|
return [False, params]
|
4763
5723
|
|
4764
|
-
def fetch_last_prices(self, symbols:
|
5724
|
+
def fetch_last_prices(self, symbols: Strings = None, params={}):
|
4765
5725
|
raise NotSupported(self.id + ' fetchLastPrices() is not supported yet')
|
4766
5726
|
|
4767
5727
|
def fetch_trading_fees(self, params={}):
|
@@ -4773,12 +5733,24 @@ class Exchange(object):
|
|
4773
5733
|
def fetch_trading_fee(self, symbol: str, params={}):
|
4774
5734
|
if not self.has['fetchTradingFees']:
|
4775
5735
|
raise NotSupported(self.id + ' fetchTradingFee() is not supported yet')
|
4776
|
-
|
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')
|
4777
5741
|
|
4778
5742
|
def parse_open_interest(self, interest, market: Market = None):
|
4779
5743
|
raise NotSupported(self.id + ' parseOpenInterest() is not supported yet')
|
4780
5744
|
|
4781
|
-
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):
|
4782
5754
|
interests = []
|
4783
5755
|
for i in range(0, len(response)):
|
4784
5756
|
entry = response[i]
|
@@ -4795,7 +5767,7 @@ class Exchange(object):
|
|
4795
5767
|
symbol = market['symbol']
|
4796
5768
|
if not market['contract']:
|
4797
5769
|
raise BadSymbol(self.id + ' fetchFundingRate() supports contract markets only')
|
4798
|
-
rates = self.
|
5770
|
+
rates = self.fetch_funding_rates([symbol], params)
|
4799
5771
|
rate = self.safe_value(rates, symbol)
|
4800
5772
|
if rate is None:
|
4801
5773
|
raise NullResponse(self.id + ' fetchFundingRate() returned no data for ' + symbol)
|
@@ -4804,6 +5776,22 @@ class Exchange(object):
|
|
4804
5776
|
else:
|
4805
5777
|
raise NotSupported(self.id + ' fetchFundingRate() is not supported yet')
|
4806
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
|
+
|
4807
5795
|
def fetch_mark_ohlcv(self, symbol, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
4808
5796
|
"""
|
4809
5797
|
fetches historical mark price candlestick data containing the open, high, low, and close price of a market
|
@@ -4815,7 +5803,7 @@ class Exchange(object):
|
|
4815
5803
|
:returns float[][]: A list of candles ordered, open, high, low, close, None
|
4816
5804
|
"""
|
4817
5805
|
if self.has['fetchMarkOHLCV']:
|
4818
|
-
request = {
|
5806
|
+
request: dict = {
|
4819
5807
|
'price': 'mark',
|
4820
5808
|
}
|
4821
5809
|
return self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))
|
@@ -4830,10 +5818,10 @@ class Exchange(object):
|
|
4830
5818
|
:param int [since]: timestamp in ms of the earliest candle to fetch
|
4831
5819
|
:param int [limit]: the maximum amount of candles to fetch
|
4832
5820
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4833
|
-
|
5821
|
+
@returns {} A list of candles ordered, open, high, low, close, None
|
4834
5822
|
"""
|
4835
5823
|
if self.has['fetchIndexOHLCV']:
|
4836
|
-
request = {
|
5824
|
+
request: dict = {
|
4837
5825
|
'price': 'index',
|
4838
5826
|
}
|
4839
5827
|
return self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))
|
@@ -4851,7 +5839,7 @@ class Exchange(object):
|
|
4851
5839
|
:returns float[][]: A list of candles ordered, open, high, low, close, None
|
4852
5840
|
"""
|
4853
5841
|
if self.has['fetchPremiumIndexOHLCV']:
|
4854
|
-
request = {
|
5842
|
+
request: dict = {
|
4855
5843
|
'price': 'premiumIndex',
|
4856
5844
|
}
|
4857
5845
|
return self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))
|
@@ -4860,8 +5848,8 @@ class Exchange(object):
|
|
4860
5848
|
|
4861
5849
|
def handle_time_in_force(self, params={}):
|
4862
5850
|
"""
|
4863
|
-
|
4864
|
-
|
5851
|
+
@ignore
|
5852
|
+
Must add timeInForce to self.options to use self method
|
4865
5853
|
:returns str: returns the exchange specific value for timeInForce
|
4866
5854
|
"""
|
4867
5855
|
timeInForce = self.safe_string_upper(params, 'timeInForce') # supported values GTC, IOC, PO
|
@@ -4874,8 +5862,8 @@ class Exchange(object):
|
|
4874
5862
|
|
4875
5863
|
def convert_type_to_account(self, account):
|
4876
5864
|
"""
|
4877
|
-
|
4878
|
-
|
5865
|
+
@ignore
|
5866
|
+
Must add accountsByType to self.options to use self method
|
4879
5867
|
:param str account: key for account name in self.options['accountsByType']
|
4880
5868
|
:returns: the exchange specific account name or the isolated margin id for transfers
|
4881
5869
|
"""
|
@@ -4891,7 +5879,7 @@ class Exchange(object):
|
|
4891
5879
|
|
4892
5880
|
def check_required_argument(self, methodName: str, argument, argumentName, options=[]):
|
4893
5881
|
"""
|
4894
|
-
|
5882
|
+
@ignore
|
4895
5883
|
:param str methodName: the name of the method that the argument is being checked for
|
4896
5884
|
:param str argument: the argument's actual value provided
|
4897
5885
|
:param str argumentName: the name of the argument being checked(for logging purposes)
|
@@ -4908,7 +5896,7 @@ class Exchange(object):
|
|
4908
5896
|
|
4909
5897
|
def check_required_margin_argument(self, methodName: str, symbol: Str, marginMode: str):
|
4910
5898
|
"""
|
4911
|
-
|
5899
|
+
@ignore
|
4912
5900
|
:param str symbol: unified symbol of the market
|
4913
5901
|
:param str methodName: name of the method that requires a symbol
|
4914
5902
|
:param str marginMode: is either 'isolated' or 'cross'
|
@@ -4918,16 +5906,15 @@ class Exchange(object):
|
|
4918
5906
|
elif (marginMode == 'cross') and (symbol is not None):
|
4919
5907
|
raise ArgumentsRequired(self.id + ' ' + methodName + '() cannot have a symbol argument for cross margin')
|
4920
5908
|
|
4921
|
-
def parse_deposit_withdraw_fees(self, response, codes:
|
5909
|
+
def parse_deposit_withdraw_fees(self, response, codes: Strings = None, currencyIdKey=None):
|
4922
5910
|
"""
|
4923
|
-
|
5911
|
+
@ignore
|
4924
5912
|
:param object[]|dict response: unparsed response from the exchange
|
4925
5913
|
:param str[]|None codes: the unified currency codes to fetch transactions fees for, returns all currencies when None
|
4926
5914
|
:param str currencyIdKey: *should only be None when response is a dictionary* the object key that corresponds to the currency id
|
4927
5915
|
:returns dict: objects with withdraw and deposit fees, indexed by currency codes
|
4928
5916
|
"""
|
4929
5917
|
depositWithdrawFees = {}
|
4930
|
-
codes = self.marketCodes(codes)
|
4931
5918
|
isArray = isinstance(response, list)
|
4932
5919
|
responseKeys = response
|
4933
5920
|
if not isArray:
|
@@ -4936,10 +5923,10 @@ class Exchange(object):
|
|
4936
5923
|
entry = responseKeys[i]
|
4937
5924
|
dictionary = entry if isArray else response[entry]
|
4938
5925
|
currencyId = self.safe_string(dictionary, currencyIdKey) if isArray else entry
|
4939
|
-
currency = self.
|
4940
|
-
code = self.safe_string(currency, 'code'
|
5926
|
+
currency = self.safe_currency(currencyId)
|
5927
|
+
code = self.safe_string(currency, 'code')
|
4941
5928
|
if (codes is None) or (self.in_array(code, codes)):
|
4942
|
-
depositWithdrawFees[code] = self.
|
5929
|
+
depositWithdrawFees[code] = self.parse_deposit_withdraw_fee(dictionary, currency)
|
4943
5930
|
return depositWithdrawFees
|
4944
5931
|
|
4945
5932
|
def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
|
@@ -4961,7 +5948,7 @@ class Exchange(object):
|
|
4961
5948
|
|
4962
5949
|
def assign_default_deposit_withdraw_fees(self, fee, currency=None):
|
4963
5950
|
"""
|
4964
|
-
|
5951
|
+
@ignore
|
4965
5952
|
Takes a depositWithdrawFee structure and assigns the default values for withdraw and deposit
|
4966
5953
|
:param dict fee: A deposit withdraw fee structure
|
4967
5954
|
:param dict currency: A currency structure, the response from self.currency()
|
@@ -4986,7 +5973,7 @@ class Exchange(object):
|
|
4986
5973
|
|
4987
5974
|
def parse_incomes(self, incomes, market=None, since: Int = None, limit: Int = None):
|
4988
5975
|
"""
|
4989
|
-
|
5976
|
+
@ignore
|
4990
5977
|
parses funding fee info from exchange response
|
4991
5978
|
:param dict[] incomes: each item describes once instance of currency being received or paid
|
4992
5979
|
:param dict market: ccxt market
|
@@ -5002,7 +5989,7 @@ class Exchange(object):
|
|
5002
5989
|
sorted = self.sort_by(result, 'timestamp')
|
5003
5990
|
return self.filter_by_since_limit(sorted, since, limit)
|
5004
5991
|
|
5005
|
-
def get_market_from_symbols(self, symbols:
|
5992
|
+
def get_market_from_symbols(self, symbols: Strings = None):
|
5006
5993
|
if symbols is None:
|
5007
5994
|
return None
|
5008
5995
|
firstMarket = self.safe_string(symbols, 0)
|
@@ -5017,7 +6004,7 @@ class Exchange(object):
|
|
5017
6004
|
|
5018
6005
|
def fetch_transactions(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
5019
6006
|
"""
|
5020
|
-
|
6007
|
+
@deprecated
|
5021
6008
|
*DEPRECATED* use fetchDepositsWithdrawals instead
|
5022
6009
|
:param str code: unified currency code for the currency of the deposit/withdrawals, default is None
|
5023
6010
|
:param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
|
@@ -5026,20 +6013,20 @@ class Exchange(object):
|
|
5026
6013
|
:returns dict: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
|
5027
6014
|
"""
|
5028
6015
|
if self.has['fetchDepositsWithdrawals']:
|
5029
|
-
return self.
|
6016
|
+
return self.fetch_deposits_withdrawals(code, since, limit, params)
|
5030
6017
|
else:
|
5031
6018
|
raise NotSupported(self.id + ' fetchTransactions() is not supported yet')
|
5032
6019
|
|
5033
6020
|
def filter_by_array_positions(self, objects, key: IndexType, values=None, indexed=True):
|
5034
6021
|
"""
|
5035
|
-
|
6022
|
+
@ignore
|
5036
6023
|
Typed wrapper for filterByArray that returns a list of positions
|
5037
6024
|
"""
|
5038
6025
|
return self.filter_by_array(objects, key, values, indexed)
|
5039
6026
|
|
5040
6027
|
def filter_by_array_tickers(self, objects, key: IndexType, values=None, indexed=True):
|
5041
6028
|
"""
|
5042
|
-
|
6029
|
+
@ignore
|
5043
6030
|
Typed wrapper for filterByArray that returns a dictionary of tickers
|
5044
6031
|
"""
|
5045
6032
|
return self.filter_by_array(objects, key, values, indexed)
|
@@ -5059,7 +6046,7 @@ class Exchange(object):
|
|
5059
6046
|
maxEntriesPerRequest = 1000 # default to 1000
|
5060
6047
|
return [maxEntriesPerRequest, params]
|
5061
6048
|
|
5062
|
-
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):
|
5063
6050
|
maxCalls = None
|
5064
6051
|
maxCalls, params = self.handle_option_and_params(params, method, 'paginationCalls', 10)
|
5065
6052
|
maxRetries = None
|
@@ -5067,6 +6054,8 @@ class Exchange(object):
|
|
5067
6054
|
paginationDirection = None
|
5068
6055
|
paginationDirection, params = self.handle_option_and_params(params, method, 'paginationDirection', 'backward')
|
5069
6056
|
paginationTimestamp = None
|
6057
|
+
removeRepeatedOption = removeRepeated
|
6058
|
+
removeRepeatedOption, params = self.handle_option_and_params(params, method, 'removeRepeated', removeRepeated)
|
5070
6059
|
calls = 0
|
5071
6060
|
result = []
|
5072
6061
|
errors = 0
|
@@ -5113,14 +6102,16 @@ class Exchange(object):
|
|
5113
6102
|
errors = 0
|
5114
6103
|
result = self.array_concat(result, response)
|
5115
6104
|
last = self.safe_value(response, responseLength - 1)
|
5116
|
-
paginationTimestamp = self.safe_integer(last, 'timestamp')
|
6105
|
+
paginationTimestamp = self.safe_integer(last, 'timestamp') + 1
|
5117
6106
|
if (until is not None) and (paginationTimestamp >= until):
|
5118
6107
|
break
|
5119
6108
|
except Exception as e:
|
5120
6109
|
errors += 1
|
5121
6110
|
if errors > maxRetries:
|
5122
6111
|
raise e
|
5123
|
-
uniqueResults =
|
6112
|
+
uniqueResults = result
|
6113
|
+
if removeRepeatedOption:
|
6114
|
+
uniqueResults = self.remove_repeated_elements_from_array(result)
|
5124
6115
|
key = 0 if (method == 'fetchOHLCV') else 'timestamp'
|
5125
6116
|
return self.filter_by_since_limit(uniqueResults, since, limit, key)
|
5126
6117
|
|
@@ -5128,18 +6119,19 @@ class Exchange(object):
|
|
5128
6119
|
maxRetries = None
|
5129
6120
|
maxRetries, params = self.handle_option_and_params(params, method, 'maxRetries', 3)
|
5130
6121
|
errors = 0
|
5131
|
-
|
5132
|
-
|
5133
|
-
|
5134
|
-
|
5135
|
-
|
5136
|
-
|
5137
|
-
|
5138
|
-
|
5139
|
-
|
5140
|
-
|
5141
|
-
|
5142
|
-
|
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 []
|
5143
6135
|
|
5144
6136
|
def fetch_paginated_call_deterministic(self, method: str, symbol: Str = None, since: Int = None, limit: Int = None, timeframe: Str = None, params={}, maxEntriesPerRequest=None):
|
5145
6137
|
maxCalls = None
|
@@ -5152,6 +6144,8 @@ class Exchange(object):
|
|
5152
6144
|
currentSince = current - (maxCalls * step) - 1
|
5153
6145
|
if since is not None:
|
5154
6146
|
currentSince = max(currentSince, since)
|
6147
|
+
else:
|
6148
|
+
currentSince = max(currentSince, 1241440531000) # avoid timestamps older than 2009
|
5155
6149
|
until = self.safe_integer_2(params, 'until', 'till') # do not omit it here
|
5156
6150
|
if until is not None:
|
5157
6151
|
requiredCalls = int(math.ceil((until - since)) / step)
|
@@ -5160,6 +6154,8 @@ class Exchange(object):
|
|
5160
6154
|
for i in range(0, maxCalls):
|
5161
6155
|
if (until is not None) and (currentSince >= until):
|
5162
6156
|
break
|
6157
|
+
if currentSince >= current:
|
6158
|
+
break
|
5163
6159
|
tasks.append(self.safe_deterministic_call(method, symbol, currentSince, maxEntriesPerRequest, timeframe, params))
|
5164
6160
|
currentSince = self.sum(currentSince, step) - 1
|
5165
6161
|
results = tasks
|
@@ -5180,28 +6176,44 @@ class Exchange(object):
|
|
5180
6176
|
i = 0
|
5181
6177
|
errors = 0
|
5182
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
|
5183
6181
|
while(i < maxCalls):
|
5184
6182
|
try:
|
5185
6183
|
if cursorValue is not None:
|
5186
6184
|
if cursorIncrement is not None:
|
5187
|
-
cursorValue = self.
|
6185
|
+
cursorValue = self.parse_to_int(cursorValue) + cursorIncrement
|
5188
6186
|
params[cursorSent] = cursorValue
|
5189
6187
|
response = None
|
5190
6188
|
if method == 'fetchAccounts':
|
5191
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)
|
5192
6194
|
else:
|
5193
6195
|
response = getattr(self, method)(symbol, since, maxEntriesPerRequest, params)
|
5194
6196
|
errors = 0
|
5195
6197
|
responseLength = len(response)
|
5196
6198
|
if self.verbose:
|
5197
|
-
|
5198
|
-
|
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
|
5199
6202
|
self.log(cursorMessage)
|
5200
6203
|
if responseLength == 0:
|
5201
6204
|
break
|
5202
6205
|
result = self.array_concat(result, response)
|
5203
|
-
last = self.
|
5204
|
-
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
|
5205
6217
|
if cursorValue is None:
|
5206
6218
|
break
|
5207
6219
|
lastTimestamp = self.safe_integer(last, 'timestamp')
|
@@ -5212,7 +6224,7 @@ class Exchange(object):
|
|
5212
6224
|
if errors > maxRetries:
|
5213
6225
|
raise e
|
5214
6226
|
i += 1
|
5215
|
-
sorted = self.
|
6227
|
+
sorted = self.sort_cursor_paginated_result(result)
|
5216
6228
|
key = 0 if (method == 'fetchOHLCV') else 'timestamp'
|
5217
6229
|
return self.filter_by_since_limit(sorted, since, limit, key)
|
5218
6230
|
|
@@ -5243,7 +6255,7 @@ class Exchange(object):
|
|
5243
6255
|
if errors > maxRetries:
|
5244
6256
|
raise e
|
5245
6257
|
i += 1
|
5246
|
-
sorted = self.
|
6258
|
+
sorted = self.sort_cursor_paginated_result(result)
|
5247
6259
|
key = 0 if (method == 'fetchOHLCV') else 'timestamp'
|
5248
6260
|
return self.filter_by_since_limit(sorted, since, limit, key)
|
5249
6261
|
|
@@ -5278,13 +6290,16 @@ class Exchange(object):
|
|
5278
6290
|
def handle_until_option(self, key: str, request, params, multiplier=1):
|
5279
6291
|
until = self.safe_integer_2(params, 'until', 'till')
|
5280
6292
|
if until is not None:
|
5281
|
-
request[key] = self.
|
6293
|
+
request[key] = self.parse_to_int(until * multiplier)
|
5282
6294
|
params = self.omit(params, ['until', 'till'])
|
5283
6295
|
return [request, params]
|
5284
6296
|
|
5285
|
-
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')
|
5286
6301
|
return self.extend(interest, {
|
5287
|
-
'symbol':
|
6302
|
+
'symbol': symbol,
|
5288
6303
|
'baseVolume': self.safe_number(interest, 'baseVolume'), # deprecated
|
5289
6304
|
'quoteVolume': self.safe_number(interest, 'quoteVolume'), # deprecated
|
5290
6305
|
'openInterestAmount': self.safe_number(interest, 'openInterestAmount'),
|
@@ -5297,9 +6312,9 @@ class Exchange(object):
|
|
5297
6312
|
def parse_liquidation(self, liquidation, market: Market = None):
|
5298
6313
|
raise NotSupported(self.id + ' parseLiquidation() is not supported yet')
|
5299
6314
|
|
5300
|
-
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):
|
5301
6316
|
"""
|
5302
|
-
|
6317
|
+
@ignore
|
5303
6318
|
parses liquidation info from the exchange response
|
5304
6319
|
:param dict[] liquidations: each item describes an instance of a liquidation event
|
5305
6320
|
:param dict market: ccxt market
|
@@ -5310,17 +6325,33 @@ class Exchange(object):
|
|
5310
6325
|
result = []
|
5311
6326
|
for i in range(0, len(liquidations)):
|
5312
6327
|
entry = liquidations[i]
|
5313
|
-
parsed = self.
|
6328
|
+
parsed = self.parse_liquidation(entry, market)
|
5314
6329
|
result.append(parsed)
|
5315
6330
|
sorted = self.sort_by(result, 'timestamp')
|
5316
6331
|
symbol = self.safe_string(market, 'symbol')
|
5317
6332
|
return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
|
5318
6333
|
|
5319
|
-
def parse_greeks(self, greeks, market: Market = None):
|
6334
|
+
def parse_greeks(self, greeks: dict, market: Market = None):
|
5320
6335
|
raise NotSupported(self.id + ' parseGreeks() is not supported yet')
|
5321
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
|
+
|
5322
6351
|
def parse_margin_modes(self, response: List[object], symbols: List[str] = None, symbolKey: Str = None, marketType: MarketType = None):
|
5323
6352
|
marginModeStructures = {}
|
6353
|
+
if marketType is None:
|
6354
|
+
marketType = 'swap' # default to swap
|
5324
6355
|
for i in range(0, len(response)):
|
5325
6356
|
info = response[i]
|
5326
6357
|
marketId = self.safe_string(info, symbolKey)
|
@@ -5329,11 +6360,13 @@ class Exchange(object):
|
|
5329
6360
|
marginModeStructures[market['symbol']] = self.parse_margin_mode(info, market)
|
5330
6361
|
return marginModeStructures
|
5331
6362
|
|
5332
|
-
def parse_margin_mode(self, marginMode, market: Market = None):
|
6363
|
+
def parse_margin_mode(self, marginMode: dict, market: Market = None):
|
5333
6364
|
raise NotSupported(self.id + ' parseMarginMode() is not supported yet')
|
5334
6365
|
|
5335
6366
|
def parse_leverages(self, response: List[object], symbols: List[str] = None, symbolKey: Str = None, marketType: MarketType = None):
|
5336
6367
|
leverageStructures = {}
|
6368
|
+
if marketType is None:
|
6369
|
+
marketType = 'swap' # default to swap
|
5337
6370
|
for i in range(0, len(response)):
|
5338
6371
|
info = response[i]
|
5339
6372
|
marketId = self.safe_string(info, symbolKey)
|
@@ -5342,5 +6375,219 @@ class Exchange(object):
|
|
5342
6375
|
leverageStructures[market['symbol']] = self.parse_leverage(info, market)
|
5343
6376
|
return leverageStructures
|
5344
6377
|
|
5345
|
-
def parse_leverage(self, leverage, market: Market = None):
|
6378
|
+
def parse_leverage(self, leverage: dict, market: Market = None):
|
5346
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]
|