ccxt 4.2.77__py2.py3-none-any.whl → 4.4.48__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccxt/__init__.py +36 -14
- ccxt/abstract/alpaca.py +4 -0
- ccxt/abstract/bigone.py +1 -1
- ccxt/abstract/binance.py +112 -48
- ccxt/abstract/binancecoinm.py +112 -48
- ccxt/abstract/binanceus.py +147 -83
- ccxt/abstract/binanceusdm.py +112 -48
- ccxt/abstract/bingx.py +133 -78
- ccxt/abstract/bitbank.py +5 -0
- ccxt/abstract/bitfinex.py +136 -65
- ccxt/abstract/bitfinex1.py +69 -0
- ccxt/abstract/bitflyer.py +1 -0
- ccxt/abstract/bitget.py +8 -1
- ccxt/abstract/bitmart.py +13 -1
- ccxt/abstract/bitopro.py +1 -0
- ccxt/abstract/bitpanda.py +0 -12
- ccxt/abstract/bitrue.py +3 -3
- ccxt/abstract/bitstamp.py +26 -3
- ccxt/abstract/blofin.py +24 -0
- ccxt/abstract/btcbox.py +1 -0
- ccxt/abstract/bybit.py +29 -14
- ccxt/abstract/cex.py +28 -29
- ccxt/abstract/coinbase.py +6 -0
- ccxt/abstract/coinbaseadvanced.py +94 -0
- ccxt/abstract/{coinbasepro.py → coinbaseexchange.py} +1 -0
- ccxt/abstract/coinbaseinternational.py +1 -1
- ccxt/abstract/coincatch.py +94 -0
- ccxt/abstract/coinex.py +233 -123
- ccxt/abstract/coinmetro.py +1 -0
- ccxt/abstract/cryptocom.py +14 -0
- ccxt/abstract/defx.py +69 -0
- ccxt/abstract/deribit.py +1 -0
- ccxt/abstract/digifinex.py +1 -0
- ccxt/abstract/ellipx.py +25 -0
- ccxt/abstract/gate.py +20 -0
- ccxt/abstract/gateio.py +20 -0
- ccxt/abstract/gemini.py +1 -0
- ccxt/abstract/hashkey.py +67 -0
- ccxt/abstract/hyperliquid.py +1 -1
- ccxt/abstract/independentreserve.py +6 -0
- ccxt/abstract/kraken.py +4 -3
- ccxt/abstract/krakenfutures.py +4 -0
- ccxt/abstract/kucoin.py +24 -0
- ccxt/abstract/kucoinfutures.py +34 -0
- ccxt/abstract/luno.py +2 -0
- ccxt/abstract/mexc.py +4 -0
- ccxt/abstract/myokx.py +340 -0
- ccxt/abstract/oceanex.py +5 -0
- ccxt/abstract/okx.py +30 -0
- ccxt/abstract/onetrading.py +0 -12
- ccxt/abstract/oxfun.py +34 -0
- ccxt/abstract/paradex.py +40 -0
- ccxt/abstract/phemex.py +1 -0
- ccxt/abstract/upbit.py +4 -0
- ccxt/abstract/vertex.py +19 -0
- ccxt/abstract/whitebit.py +31 -1
- ccxt/abstract/woo.py +6 -2
- ccxt/abstract/woofipro.py +119 -0
- ccxt/abstract/xt.py +153 -0
- ccxt/abstract/zonda.py +6 -0
- ccxt/ace.py +164 -60
- ccxt/alpaca.py +727 -63
- ccxt/ascendex.py +395 -249
- ccxt/async_support/__init__.py +36 -14
- ccxt/async_support/ace.py +164 -60
- ccxt/async_support/alpaca.py +727 -63
- ccxt/async_support/ascendex.py +396 -249
- ccxt/async_support/base/exchange.py +531 -155
- ccxt/async_support/base/ws/aiohttp_client.py +28 -5
- ccxt/async_support/base/ws/cache.py +3 -2
- ccxt/async_support/base/ws/client.py +26 -5
- ccxt/async_support/base/ws/fast_client.py +4 -3
- ccxt/async_support/base/ws/functions.py +1 -1
- ccxt/async_support/base/ws/future.py +40 -31
- ccxt/async_support/base/ws/order_book_side.py +3 -0
- ccxt/async_support/bequant.py +1 -1
- ccxt/async_support/bigone.py +329 -202
- ccxt/async_support/binance.py +3030 -1087
- ccxt/async_support/binancecoinm.py +2 -1
- ccxt/async_support/binanceus.py +12 -1
- ccxt/async_support/binanceusdm.py +3 -1
- ccxt/async_support/bingx.py +3104 -880
- ccxt/async_support/bit2c.py +119 -38
- ccxt/async_support/bitbank.py +215 -76
- ccxt/async_support/bitbns.py +124 -53
- ccxt/async_support/bitfinex.py +3236 -1078
- ccxt/async_support/bitfinex1.py +1711 -0
- ccxt/async_support/bitflyer.py +238 -49
- ccxt/async_support/bitget.py +1513 -563
- ccxt/async_support/bithumb.py +199 -65
- ccxt/async_support/bitmart.py +1320 -435
- ccxt/async_support/bitmex.py +308 -111
- ccxt/async_support/bitopro.py +256 -96
- ccxt/async_support/bitrue.py +365 -233
- ccxt/async_support/bitso.py +201 -89
- ccxt/async_support/bitstamp.py +438 -269
- ccxt/async_support/bitteam.py +179 -73
- ccxt/async_support/bitvavo.py +180 -70
- ccxt/async_support/bl3p.py +92 -25
- ccxt/async_support/blockchaincom.py +193 -79
- ccxt/async_support/blofin.py +392 -148
- ccxt/async_support/btcalpha.py +161 -55
- ccxt/async_support/btcbox.py +250 -34
- ccxt/async_support/btcmarkets.py +232 -85
- ccxt/async_support/btcturk.py +159 -60
- ccxt/async_support/bybit.py +2231 -1193
- ccxt/async_support/cex.py +1409 -1329
- ccxt/async_support/coinbase.py +1454 -287
- ccxt/async_support/coinbaseadvanced.py +17 -0
- ccxt/async_support/{coinbasepro.py → coinbaseexchange.py} +233 -99
- ccxt/async_support/coinbaseinternational.py +428 -88
- ccxt/async_support/coincatch.py +5152 -0
- ccxt/async_support/coincheck.py +121 -38
- ccxt/async_support/coinex.py +4020 -3339
- ccxt/async_support/coinlist.py +273 -116
- ccxt/async_support/coinmate.py +204 -97
- ccxt/async_support/coinmetro.py +203 -110
- ccxt/async_support/coinone.py +142 -68
- ccxt/async_support/coinsph.py +206 -89
- ccxt/async_support/coinspot.py +137 -62
- ccxt/async_support/cryptocom.py +515 -185
- ccxt/async_support/currencycom.py +203 -85
- ccxt/async_support/defx.py +2066 -0
- ccxt/async_support/delta.py +404 -109
- ccxt/async_support/deribit.py +557 -323
- ccxt/async_support/digifinex.py +340 -223
- ccxt/async_support/ellipx.py +1826 -0
- ccxt/async_support/exmo.py +259 -128
- ccxt/async_support/gate.py +1472 -463
- ccxt/async_support/gemini.py +206 -84
- ccxt/async_support/hashkey.py +4164 -0
- ccxt/async_support/hitbtc.py +334 -178
- ccxt/async_support/hollaex.py +134 -83
- ccxt/async_support/htx.py +1095 -563
- ccxt/async_support/huobijp.py +105 -56
- ccxt/async_support/hyperliquid.py +1633 -268
- ccxt/async_support/idex.py +148 -95
- ccxt/async_support/independentreserve.py +236 -31
- ccxt/async_support/indodax.py +165 -62
- ccxt/async_support/kraken.py +871 -354
- ccxt/async_support/krakenfutures.py +324 -100
- ccxt/async_support/kucoin.py +917 -357
- ccxt/async_support/kucoinfutures.py +1004 -149
- ccxt/async_support/kuna.py +138 -106
- ccxt/async_support/latoken.py +135 -79
- ccxt/async_support/lbank.py +290 -113
- ccxt/async_support/luno.py +112 -62
- ccxt/async_support/lykke.py +104 -55
- ccxt/async_support/mercado.py +36 -29
- ccxt/async_support/mexc.py +995 -429
- ccxt/async_support/myokx.py +43 -0
- ccxt/async_support/ndax.py +163 -82
- ccxt/async_support/novadax.py +121 -75
- ccxt/async_support/oceanex.py +175 -59
- ccxt/async_support/okcoin.py +222 -163
- ccxt/async_support/okx.py +1776 -454
- ccxt/async_support/onetrading.py +132 -414
- ccxt/async_support/oxfun.py +2832 -0
- ccxt/async_support/p2b.py +79 -51
- ccxt/async_support/paradex.py +2017 -0
- ccxt/async_support/paymium.py +56 -32
- ccxt/async_support/phemex.py +572 -196
- ccxt/async_support/poloniex.py +218 -95
- ccxt/async_support/poloniexfutures.py +260 -92
- ccxt/async_support/probit.py +143 -110
- ccxt/async_support/timex.py +123 -70
- ccxt/async_support/tokocrypto.py +129 -93
- ccxt/async_support/tradeogre.py +39 -25
- ccxt/async_support/upbit.py +322 -113
- ccxt/async_support/vertex.py +2983 -0
- ccxt/async_support/wavesexchange.py +227 -173
- ccxt/async_support/wazirx.py +145 -65
- ccxt/async_support/whitebit.py +533 -138
- ccxt/async_support/woo.py +1137 -296
- ccxt/async_support/woofipro.py +2716 -0
- ccxt/async_support/xt.py +4628 -0
- ccxt/async_support/yobit.py +160 -92
- ccxt/async_support/zaif.py +80 -33
- ccxt/async_support/zonda.py +140 -69
- ccxt/base/errors.py +51 -20
- ccxt/base/exchange.py +1722 -480
- ccxt/base/precise.py +10 -0
- ccxt/base/types.py +223 -4
- ccxt/bequant.py +1 -1
- ccxt/bigone.py +329 -202
- ccxt/binance.py +3030 -1087
- ccxt/binancecoinm.py +2 -1
- ccxt/binanceus.py +12 -1
- ccxt/binanceusdm.py +3 -1
- ccxt/bingx.py +3104 -880
- ccxt/bit2c.py +119 -38
- ccxt/bitbank.py +215 -76
- ccxt/bitbns.py +124 -53
- ccxt/bitfinex.py +3235 -1078
- ccxt/bitfinex1.py +1710 -0
- ccxt/bitflyer.py +238 -49
- ccxt/bitget.py +1513 -563
- ccxt/bithumb.py +198 -65
- ccxt/bitmart.py +1320 -435
- ccxt/bitmex.py +308 -111
- ccxt/bitopro.py +256 -96
- ccxt/bitrue.py +365 -233
- ccxt/bitso.py +201 -89
- ccxt/bitstamp.py +438 -269
- ccxt/bitteam.py +179 -73
- ccxt/bitvavo.py +180 -70
- ccxt/bl3p.py +92 -25
- ccxt/blockchaincom.py +193 -79
- ccxt/blofin.py +392 -148
- ccxt/btcalpha.py +161 -55
- ccxt/btcbox.py +250 -34
- ccxt/btcmarkets.py +232 -85
- ccxt/btcturk.py +159 -60
- ccxt/bybit.py +2231 -1193
- ccxt/cex.py +1408 -1329
- ccxt/coinbase.py +1454 -287
- ccxt/coinbaseadvanced.py +17 -0
- ccxt/{coinbasepro.py → coinbaseexchange.py} +233 -99
- ccxt/coinbaseinternational.py +428 -88
- ccxt/coincatch.py +5152 -0
- ccxt/coincheck.py +121 -38
- ccxt/coinex.py +4020 -3339
- ccxt/coinlist.py +273 -116
- ccxt/coinmate.py +204 -97
- ccxt/coinmetro.py +203 -110
- ccxt/coinone.py +142 -68
- ccxt/coinsph.py +206 -89
- ccxt/coinspot.py +137 -62
- ccxt/cryptocom.py +515 -185
- ccxt/currencycom.py +203 -85
- ccxt/defx.py +2065 -0
- ccxt/delta.py +404 -109
- ccxt/deribit.py +557 -323
- ccxt/digifinex.py +340 -223
- ccxt/ellipx.py +1826 -0
- ccxt/exmo.py +259 -128
- ccxt/gate.py +1472 -463
- ccxt/gemini.py +206 -84
- ccxt/hashkey.py +4164 -0
- ccxt/hitbtc.py +334 -178
- ccxt/hollaex.py +134 -83
- ccxt/htx.py +1095 -563
- ccxt/huobijp.py +105 -56
- ccxt/hyperliquid.py +1632 -268
- ccxt/idex.py +148 -95
- ccxt/independentreserve.py +235 -31
- ccxt/indodax.py +165 -62
- ccxt/kraken.py +871 -354
- ccxt/krakenfutures.py +324 -100
- ccxt/kucoin.py +917 -357
- ccxt/kucoinfutures.py +1004 -149
- ccxt/kuna.py +138 -106
- ccxt/latoken.py +135 -79
- ccxt/lbank.py +290 -113
- ccxt/luno.py +112 -62
- ccxt/lykke.py +104 -55
- ccxt/mercado.py +36 -29
- ccxt/mexc.py +994 -429
- ccxt/myokx.py +43 -0
- ccxt/ndax.py +163 -82
- ccxt/novadax.py +121 -75
- ccxt/oceanex.py +175 -59
- ccxt/okcoin.py +222 -163
- ccxt/okx.py +1776 -454
- ccxt/onetrading.py +132 -414
- ccxt/oxfun.py +2831 -0
- ccxt/p2b.py +79 -51
- ccxt/paradex.py +2017 -0
- ccxt/paymium.py +56 -32
- ccxt/phemex.py +572 -196
- ccxt/poloniex.py +218 -95
- ccxt/poloniexfutures.py +260 -92
- ccxt/pro/__init__.py +29 -5
- ccxt/pro/alpaca.py +32 -17
- ccxt/pro/ascendex.py +62 -14
- ccxt/pro/bequant.py +4 -0
- ccxt/pro/binance.py +1596 -329
- ccxt/pro/binancecoinm.py +1 -0
- ccxt/pro/binanceus.py +2 -9
- ccxt/pro/binanceusdm.py +2 -0
- ccxt/pro/bingx.py +527 -134
- ccxt/pro/bitcoincom.py +4 -1
- ccxt/pro/bitfinex.py +731 -266
- ccxt/pro/bitfinex1.py +635 -0
- ccxt/pro/bitget.py +726 -357
- ccxt/pro/bithumb.py +380 -0
- ccxt/pro/bitmart.py +138 -39
- ccxt/pro/bitmex.py +199 -40
- ccxt/pro/bitopro.py +25 -13
- ccxt/pro/bitrue.py +31 -32
- ccxt/pro/bitstamp.py +7 -6
- ccxt/pro/bitvavo.py +203 -81
- ccxt/pro/blockchaincom.py +30 -17
- ccxt/pro/blofin.py +692 -0
- ccxt/pro/bybit.py +791 -82
- ccxt/pro/cex.py +99 -51
- ccxt/pro/coinbase.py +220 -30
- ccxt/{async_support/hitbtc3.py → pro/coinbaseadvanced.py} +5 -5
- ccxt/pro/{coinbasepro.py → coinbaseexchange.py} +19 -19
- ccxt/pro/coinbaseinternational.py +193 -30
- ccxt/pro/coincatch.py +1464 -0
- ccxt/pro/coincheck.py +11 -6
- ccxt/pro/coinex.py +965 -665
- ccxt/pro/coinone.py +17 -10
- ccxt/pro/cryptocom.py +446 -66
- ccxt/pro/currencycom.py +11 -10
- ccxt/pro/defx.py +832 -0
- ccxt/pro/deribit.py +167 -31
- ccxt/pro/exmo.py +252 -20
- ccxt/pro/gate.py +729 -64
- ccxt/pro/gemini.py +44 -26
- ccxt/pro/hashkey.py +802 -0
- ccxt/pro/hitbtc.py +208 -103
- ccxt/pro/hollaex.py +25 -9
- ccxt/pro/htx.py +83 -39
- ccxt/pro/huobijp.py +17 -16
- ccxt/pro/hyperliquid.py +502 -31
- ccxt/pro/idex.py +28 -13
- ccxt/pro/independentreserve.py +21 -16
- ccxt/pro/kraken.py +298 -51
- ccxt/pro/krakenfutures.py +166 -75
- ccxt/pro/kucoin.py +395 -77
- ccxt/pro/kucoinfutures.py +400 -99
- ccxt/pro/lbank.py +52 -31
- ccxt/pro/luno.py +12 -10
- ccxt/pro/mexc.py +400 -50
- ccxt/pro/myokx.py +28 -0
- ccxt/pro/ndax.py +25 -12
- ccxt/pro/okcoin.py +28 -9
- ccxt/pro/okx.py +935 -124
- ccxt/pro/onetrading.py +41 -24
- ccxt/pro/oxfun.py +1054 -0
- ccxt/pro/p2b.py +100 -24
- ccxt/pro/paradex.py +352 -0
- ccxt/pro/phemex.py +92 -33
- ccxt/pro/poloniex.py +128 -49
- ccxt/pro/poloniexfutures.py +53 -32
- ccxt/pro/probit.py +92 -85
- ccxt/pro/upbit.py +401 -8
- ccxt/pro/vertex.py +943 -0
- ccxt/pro/wazirx.py +46 -28
- ccxt/pro/whitebit.py +65 -12
- ccxt/pro/woo.py +437 -65
- ccxt/pro/woofipro.py +1271 -0
- ccxt/pro/xt.py +1067 -0
- ccxt/probit.py +143 -110
- ccxt/static_dependencies/__init__.py +1 -1
- ccxt/static_dependencies/lark/__init__.py +38 -0
- ccxt/static_dependencies/lark/__pyinstaller/__init__.py +6 -0
- ccxt/static_dependencies/lark/__pyinstaller/hook-lark.py +14 -0
- ccxt/static_dependencies/lark/ast_utils.py +59 -0
- ccxt/static_dependencies/lark/common.py +86 -0
- ccxt/static_dependencies/lark/exceptions.py +292 -0
- ccxt/static_dependencies/lark/grammar.py +130 -0
- ccxt/static_dependencies/lark/grammars/__init__.py +0 -0
- ccxt/static_dependencies/lark/indenter.py +143 -0
- ccxt/static_dependencies/lark/lark.py +658 -0
- ccxt/static_dependencies/lark/lexer.py +678 -0
- ccxt/static_dependencies/lark/load_grammar.py +1428 -0
- ccxt/static_dependencies/lark/parse_tree_builder.py +391 -0
- ccxt/static_dependencies/lark/parser_frontends.py +257 -0
- ccxt/static_dependencies/lark/parsers/__init__.py +0 -0
- ccxt/static_dependencies/lark/parsers/cyk.py +340 -0
- ccxt/static_dependencies/lark/parsers/earley.py +314 -0
- ccxt/static_dependencies/lark/parsers/earley_common.py +42 -0
- ccxt/static_dependencies/lark/parsers/earley_forest.py +801 -0
- ccxt/static_dependencies/lark/parsers/grammar_analysis.py +203 -0
- ccxt/static_dependencies/lark/parsers/lalr_analysis.py +332 -0
- ccxt/static_dependencies/lark/parsers/lalr_interactive_parser.py +158 -0
- ccxt/static_dependencies/lark/parsers/lalr_parser.py +122 -0
- ccxt/static_dependencies/lark/parsers/lalr_parser_state.py +110 -0
- ccxt/static_dependencies/lark/parsers/xearley.py +165 -0
- ccxt/static_dependencies/lark/py.typed +0 -0
- ccxt/static_dependencies/lark/reconstruct.py +107 -0
- ccxt/static_dependencies/lark/tools/__init__.py +70 -0
- ccxt/static_dependencies/lark/tools/nearley.py +202 -0
- ccxt/static_dependencies/lark/tools/serialize.py +32 -0
- ccxt/static_dependencies/lark/tools/standalone.py +196 -0
- ccxt/static_dependencies/lark/tree.py +267 -0
- ccxt/static_dependencies/lark/tree_matcher.py +186 -0
- ccxt/static_dependencies/lark/tree_templates.py +180 -0
- ccxt/static_dependencies/lark/utils.py +343 -0
- ccxt/static_dependencies/lark/visitors.py +596 -0
- ccxt/static_dependencies/marshmallow/__init__.py +81 -0
- ccxt/static_dependencies/marshmallow/base.py +65 -0
- ccxt/static_dependencies/marshmallow/class_registry.py +94 -0
- ccxt/static_dependencies/marshmallow/decorators.py +231 -0
- ccxt/static_dependencies/marshmallow/error_store.py +60 -0
- ccxt/static_dependencies/marshmallow/exceptions.py +71 -0
- ccxt/static_dependencies/marshmallow/fields.py +2114 -0
- ccxt/static_dependencies/marshmallow/orderedset.py +89 -0
- ccxt/static_dependencies/marshmallow/py.typed +0 -0
- ccxt/static_dependencies/marshmallow/schema.py +1228 -0
- ccxt/static_dependencies/marshmallow/types.py +12 -0
- ccxt/static_dependencies/marshmallow/utils.py +378 -0
- ccxt/static_dependencies/marshmallow/validate.py +678 -0
- ccxt/static_dependencies/marshmallow/warnings.py +2 -0
- ccxt/static_dependencies/marshmallow_dataclass/__init__.py +1047 -0
- ccxt/static_dependencies/marshmallow_dataclass/collection_field.py +51 -0
- ccxt/static_dependencies/marshmallow_dataclass/lazy_class_attribute.py +45 -0
- ccxt/static_dependencies/marshmallow_dataclass/mypy.py +71 -0
- ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
- ccxt/static_dependencies/marshmallow_dataclass/typing.py +14 -0
- ccxt/static_dependencies/marshmallow_dataclass/union_field.py +82 -0
- ccxt/static_dependencies/marshmallow_oneofschema/__init__.py +1 -0
- ccxt/static_dependencies/marshmallow_oneofschema/one_of_schema.py +193 -0
- ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
- ccxt/static_dependencies/starknet/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/data_types.py +123 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/cairo_types.py +77 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser.py +46 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser_transformer.py +138 -0
- ccxt/static_dependencies/starknet/cairo/felt.py +64 -0
- ccxt/static_dependencies/starknet/cairo/type_parser.py +121 -0
- ccxt/static_dependencies/starknet/cairo/v1/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/v1/type_parser.py +59 -0
- ccxt/static_dependencies/starknet/cairo/v2/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/v2/type_parser.py +77 -0
- ccxt/static_dependencies/starknet/ccxt_utils.py +7 -0
- ccxt/static_dependencies/starknet/common.py +15 -0
- ccxt/static_dependencies/starknet/constants.py +39 -0
- ccxt/static_dependencies/starknet/hash/__init__.py +0 -0
- ccxt/static_dependencies/starknet/hash/address.py +79 -0
- ccxt/static_dependencies/starknet/hash/compiled_class_hash_objects.py +111 -0
- ccxt/static_dependencies/starknet/hash/selector.py +16 -0
- ccxt/static_dependencies/starknet/hash/storage.py +12 -0
- ccxt/static_dependencies/starknet/hash/utils.py +78 -0
- ccxt/static_dependencies/starknet/models/__init__.py +0 -0
- ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
- ccxt/static_dependencies/starknet/serialization/__init__.py +24 -0
- ccxt/static_dependencies/starknet/serialization/_calldata_reader.py +40 -0
- ccxt/static_dependencies/starknet/serialization/_context.py +142 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/__init__.py +10 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/_common.py +82 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/array_serializer.py +43 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/bool_serializer.py +37 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/byte_array_serializer.py +66 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/cairo_data_serializer.py +71 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/enum_serializer.py +71 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/felt_serializer.py +50 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/named_tuple_serializer.py +58 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/option_serializer.py +43 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/output_serializer.py +40 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/payload_serializer.py +72 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/struct_serializer.py +36 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/tuple_serializer.py +36 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/uint256_serializer.py +76 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/uint_serializer.py +100 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/unit_serializer.py +32 -0
- ccxt/static_dependencies/starknet/serialization/errors.py +10 -0
- ccxt/static_dependencies/starknet/serialization/factory.py +229 -0
- ccxt/static_dependencies/starknet/serialization/function_serialization_adapter.py +110 -0
- ccxt/static_dependencies/starknet/serialization/tuple_dataclass.py +59 -0
- ccxt/static_dependencies/starknet/utils/__init__.py +0 -0
- ccxt/static_dependencies/starknet/utils/constructor_args_translator.py +86 -0
- ccxt/static_dependencies/starknet/utils/iterable.py +13 -0
- ccxt/static_dependencies/starknet/utils/schema.py +13 -0
- ccxt/static_dependencies/starknet/utils/typed_data.py +182 -0
- ccxt/static_dependencies/starkware/__init__.py +0 -0
- ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
- ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
- ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
- ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
- ccxt/static_dependencies/starkware/crypto/utils.py +63 -0
- ccxt/static_dependencies/sympy/__init__.py +0 -0
- ccxt/static_dependencies/sympy/core/__init__.py +0 -0
- ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
- ccxt/static_dependencies/sympy/external/__init__.py +0 -0
- ccxt/static_dependencies/sympy/external/gmpy.py +345 -0
- ccxt/static_dependencies/sympy/external/importtools.py +187 -0
- ccxt/static_dependencies/sympy/external/ntheory.py +637 -0
- ccxt/static_dependencies/sympy/external/pythonmpq.py +341 -0
- ccxt/static_dependencies/typing_inspect/__init__.py +0 -0
- ccxt/static_dependencies/typing_inspect/typing_inspect.py +851 -0
- ccxt/test/{test_async.py → tests_async.py} +456 -391
- ccxt/test/tests_helpers.py +285 -0
- ccxt/test/tests_init.py +39 -0
- ccxt/test/{test_sync.py → tests_sync.py} +456 -393
- ccxt/timex.py +123 -70
- ccxt/tokocrypto.py +129 -93
- ccxt/tradeogre.py +39 -25
- ccxt/upbit.py +322 -113
- ccxt/vertex.py +2983 -0
- ccxt/wavesexchange.py +227 -173
- ccxt/wazirx.py +145 -65
- ccxt/whitebit.py +533 -138
- ccxt/woo.py +1137 -296
- ccxt/woofipro.py +2716 -0
- ccxt/xt.py +4627 -0
- ccxt/yobit.py +159 -92
- ccxt/zaif.py +80 -33
- ccxt/zonda.py +140 -69
- ccxt-4.4.48.dist-info/LICENSE.txt +21 -0
- ccxt-4.4.48.dist-info/METADATA +646 -0
- ccxt-4.4.48.dist-info/RECORD +669 -0
- {ccxt-4.2.77.dist-info → ccxt-4.4.48.dist-info}/WHEEL +1 -1
- ccxt/abstract/bitbay.py +0 -47
- ccxt/abstract/bitfinex2.py +0 -139
- ccxt/abstract/hitbtc3.py +0 -115
- ccxt/async_support/bitbay.py +0 -17
- ccxt/async_support/bitfinex2.py +0 -3496
- ccxt/async_support/flowbtc.py +0 -34
- ccxt/bitbay.py +0 -17
- ccxt/bitfinex2.py +0 -3496
- ccxt/flowbtc.py +0 -34
- ccxt/hitbtc3.py +0 -16
- ccxt/pro/bitfinex2.py +0 -1081
- ccxt/test/base/__init__.py +0 -28
- ccxt/test/base/test_account.py +0 -26
- ccxt/test/base/test_balance.py +0 -56
- ccxt/test/base/test_borrow_interest.py +0 -35
- ccxt/test/base/test_borrow_rate.py +0 -32
- ccxt/test/base/test_calculate_fee.py +0 -51
- ccxt/test/base/test_crypto.py +0 -127
- ccxt/test/base/test_currency.py +0 -76
- ccxt/test/base/test_datetime.py +0 -103
- ccxt/test/base/test_decimal_to_precision.py +0 -392
- ccxt/test/base/test_deep_extend.py +0 -68
- ccxt/test/base/test_deposit_withdrawal.py +0 -50
- ccxt/test/base/test_exchange_datetime_functions.py +0 -76
- ccxt/test/base/test_funding_rate_history.py +0 -29
- ccxt/test/base/test_last_price.py +0 -32
- ccxt/test/base/test_ledger_entry.py +0 -45
- ccxt/test/base/test_ledger_item.py +0 -48
- ccxt/test/base/test_leverage_tier.py +0 -33
- ccxt/test/base/test_margin_mode.py +0 -24
- ccxt/test/base/test_margin_modification.py +0 -35
- ccxt/test/base/test_market.py +0 -190
- ccxt/test/base/test_number.py +0 -411
- ccxt/test/base/test_ohlcv.py +0 -32
- ccxt/test/base/test_open_interest.py +0 -32
- ccxt/test/base/test_order.py +0 -64
- ccxt/test/base/test_order_book.py +0 -63
- ccxt/test/base/test_position.py +0 -60
- ccxt/test/base/test_shared_methods.py +0 -345
- ccxt/test/base/test_status.py +0 -24
- ccxt/test/base/test_throttle.py +0 -126
- ccxt/test/base/test_ticker.py +0 -86
- ccxt/test/base/test_trade.py +0 -47
- ccxt/test/base/test_trading_fee.py +0 -26
- ccxt/test/base/test_transaction.py +0 -39
- ccxt-4.2.77.dist-info/METADATA +0 -626
- ccxt-4.2.77.dist-info/RECORD +0 -534
- {ccxt-4.2.77.dist-info → ccxt-4.4.48.dist-info}/top_level.txt +0 -0
ccxt/pro/coincatch.py
ADDED
@@ -0,0 +1,1464 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
4
|
+
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
5
|
+
|
6
|
+
import ccxt.async_support
|
7
|
+
from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
|
8
|
+
import hashlib
|
9
|
+
from ccxt.base.types import Balances, Bool, Int, Market, Order, OrderBook, Position, Str, Strings, Ticker, Tickers, Trade
|
10
|
+
from ccxt.async_support.base.ws.client import Client
|
11
|
+
from typing import List
|
12
|
+
from typing import Any
|
13
|
+
from ccxt.base.errors import ExchangeError
|
14
|
+
from ccxt.base.errors import AuthenticationError
|
15
|
+
from ccxt.base.errors import ArgumentsRequired
|
16
|
+
from ccxt.base.errors import BadRequest
|
17
|
+
from ccxt.base.errors import NotSupported
|
18
|
+
from ccxt.base.errors import RateLimitExceeded
|
19
|
+
from ccxt.base.errors import ChecksumError
|
20
|
+
from ccxt.base.errors import UnsubscribeError
|
21
|
+
from ccxt.base.precise import Precise
|
22
|
+
|
23
|
+
|
24
|
+
class coincatch(ccxt.async_support.coincatch):
|
25
|
+
|
26
|
+
def describe(self):
|
27
|
+
return self.deep_extend(super(coincatch, self).describe(), {
|
28
|
+
'has': {
|
29
|
+
'ws': True,
|
30
|
+
'watchTrades': True,
|
31
|
+
'watchTradesForSymbols': True,
|
32
|
+
'watchOrderBook': True,
|
33
|
+
'watchOrderBookForSymbols': True,
|
34
|
+
'watchOHLCV': True,
|
35
|
+
'watchOHLCVForSymbols': False, # todo
|
36
|
+
'watchOrders': True,
|
37
|
+
'watchMyTrades': False,
|
38
|
+
'watchTicker': True,
|
39
|
+
'watchTickers': True,
|
40
|
+
'watchBalance': True,
|
41
|
+
'watchPositions': True,
|
42
|
+
},
|
43
|
+
'urls': {
|
44
|
+
'api': {
|
45
|
+
'ws': {
|
46
|
+
'public': 'wss://ws.coincatch.com/public/v1/stream',
|
47
|
+
'private': 'wss://ws.coincatch.com/private/v1/stream',
|
48
|
+
},
|
49
|
+
},
|
50
|
+
},
|
51
|
+
'options': {
|
52
|
+
'tradesLimit': 1000,
|
53
|
+
'OHLCVLimit': 200,
|
54
|
+
'timeframesForWs': {
|
55
|
+
'1m': '1m',
|
56
|
+
'5m': '5m',
|
57
|
+
'15m': '15m',
|
58
|
+
'30m': '30m',
|
59
|
+
'1h': '1H',
|
60
|
+
'4h': '4H',
|
61
|
+
'12h': '12H',
|
62
|
+
'1d': '1D',
|
63
|
+
'1w': '1W',
|
64
|
+
},
|
65
|
+
'watchOrderBook': {
|
66
|
+
'checksum': True,
|
67
|
+
},
|
68
|
+
},
|
69
|
+
'streaming': {
|
70
|
+
'ping': self.ping,
|
71
|
+
},
|
72
|
+
'exceptions': {
|
73
|
+
'ws': {
|
74
|
+
'exact': {
|
75
|
+
'30001': BadRequest, # Channel does not exist
|
76
|
+
'30002': AuthenticationError, # illegal request
|
77
|
+
'30003': BadRequest, # invalid op
|
78
|
+
'30004': AuthenticationError, # User needs to log in
|
79
|
+
'30005': AuthenticationError, # login failed
|
80
|
+
'30006': RateLimitExceeded, # request too many
|
81
|
+
'30007': RateLimitExceeded, # request over limit,connection close
|
82
|
+
'30011': AuthenticationError, # invalid ACCESS_KEY
|
83
|
+
'30012': AuthenticationError, # invalid ACCESS_PASSPHRASE
|
84
|
+
'30013': AuthenticationError, # invalid ACCESS_TIMESTAMP
|
85
|
+
'30014': BadRequest, # Request timestamp expired
|
86
|
+
'30015': AuthenticationError, # {event: 'error', code: 30015, msg: 'Invalid sign'}
|
87
|
+
},
|
88
|
+
'broad': {},
|
89
|
+
},
|
90
|
+
},
|
91
|
+
})
|
92
|
+
|
93
|
+
def get_market_from_arg(self, entry):
|
94
|
+
instId = self.safe_string(entry, 'instId')
|
95
|
+
instType = self.safe_string(entry, 'instType')
|
96
|
+
baseAndQuote = self.parseSpotMarketId(instId)
|
97
|
+
baseId = baseAndQuote['baseId']
|
98
|
+
quoteId = baseAndQuote['quoteId']
|
99
|
+
suffix = '_SPBL' # spot suffix
|
100
|
+
if instType == 'mc':
|
101
|
+
if quoteId == 'USD':
|
102
|
+
suffix = '_DMCBL'
|
103
|
+
else:
|
104
|
+
suffix = '_UMCBL'
|
105
|
+
marketId = self.safe_currency_code(baseId) + self.safe_currency_code(quoteId) + suffix
|
106
|
+
return self.safeMarketCustom(marketId)
|
107
|
+
|
108
|
+
async def authenticate(self, params={}):
|
109
|
+
self.check_required_credentials()
|
110
|
+
url = self.urls['api']['ws']['private']
|
111
|
+
client = self.client(url)
|
112
|
+
messageHash = 'authenticated'
|
113
|
+
future = client.future(messageHash)
|
114
|
+
authenticated = self.safe_value(client.subscriptions, messageHash)
|
115
|
+
if authenticated is None:
|
116
|
+
timestamp = str(self.seconds())
|
117
|
+
auth = timestamp + 'GET' + '/user/verify'
|
118
|
+
signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256, 'base64')
|
119
|
+
operation = 'login'
|
120
|
+
request: dict = {
|
121
|
+
'op': operation,
|
122
|
+
'args': [
|
123
|
+
{
|
124
|
+
'apiKey': self.apiKey,
|
125
|
+
'passphrase': self.password,
|
126
|
+
'timestamp': timestamp,
|
127
|
+
'sign': signature,
|
128
|
+
},
|
129
|
+
],
|
130
|
+
}
|
131
|
+
message = self.extend(request, params)
|
132
|
+
self.watch(url, messageHash, message, messageHash)
|
133
|
+
return await future
|
134
|
+
|
135
|
+
async def watch_public(self, messageHash, subscribeHash, args, params={}):
|
136
|
+
url = self.urls['api']['ws']['public']
|
137
|
+
request: dict = {
|
138
|
+
'op': 'subscribe',
|
139
|
+
'args': [args],
|
140
|
+
}
|
141
|
+
message = self.extend(request, params)
|
142
|
+
return await self.watch(url, messageHash, message, subscribeHash)
|
143
|
+
|
144
|
+
async def un_watch_public(self, messageHash, args, params={}):
|
145
|
+
url = self.urls['api']['ws']['public']
|
146
|
+
request: dict = {
|
147
|
+
'op': 'unsubscribe',
|
148
|
+
'args': [args],
|
149
|
+
}
|
150
|
+
message = self.extend(request, params)
|
151
|
+
return await self.watch(url, messageHash, message, messageHash)
|
152
|
+
|
153
|
+
async def watch_private(self, messageHash, subscribeHash, args, params={}):
|
154
|
+
await self.authenticate()
|
155
|
+
url = self.urls['api']['ws']['private']
|
156
|
+
request: dict = {
|
157
|
+
'op': 'subscribe',
|
158
|
+
'args': [args],
|
159
|
+
}
|
160
|
+
message = self.extend(request, params)
|
161
|
+
return await self.watch(url, messageHash, message, subscribeHash)
|
162
|
+
|
163
|
+
async def watch_private_multiple(self, messageHashes, subscribeHashes, args, params={}):
|
164
|
+
await self.authenticate()
|
165
|
+
url = self.urls['api']['ws']['private']
|
166
|
+
request: dict = {
|
167
|
+
'op': 'subscribe',
|
168
|
+
'args': args,
|
169
|
+
}
|
170
|
+
message = self.extend(request, params)
|
171
|
+
return await self.watch_multiple(url, messageHashes, message, subscribeHashes)
|
172
|
+
|
173
|
+
def handle_authenticate(self, client: Client, message):
|
174
|
+
#
|
175
|
+
# {event: "login", code: 0}
|
176
|
+
#
|
177
|
+
messageHash = 'authenticated'
|
178
|
+
future = self.safe_value(client.futures, messageHash)
|
179
|
+
future.resolve(True)
|
180
|
+
|
181
|
+
async def watch_public_multiple(self, messageHashes, subscribeHashes, argsArray, params={}):
|
182
|
+
url = self.urls['api']['ws']['public']
|
183
|
+
request: dict = {
|
184
|
+
'op': 'subscribe',
|
185
|
+
'args': argsArray,
|
186
|
+
}
|
187
|
+
message = self.extend(request, params)
|
188
|
+
return await self.watch_multiple(url, messageHashes, message, subscribeHashes)
|
189
|
+
|
190
|
+
async def un_watch_channel(self, symbol: str, channel: str, messageHashTopic: str, params={}) -> Any:
|
191
|
+
await self.load_markets()
|
192
|
+
market = self.market(symbol)
|
193
|
+
instType, instId = self.get_public_inst_type_and_id(market)
|
194
|
+
messageHash = 'unsubscribe:' + messageHashTopic + ':' + symbol
|
195
|
+
args: dict = {
|
196
|
+
'instType': instType,
|
197
|
+
'channel': channel,
|
198
|
+
'instId': instId,
|
199
|
+
}
|
200
|
+
return await self.un_watch_public(messageHash, args, params)
|
201
|
+
|
202
|
+
def get_public_inst_type_and_id(self, market: Market):
|
203
|
+
instId = market['baseId'] + market['quoteId']
|
204
|
+
instType = None
|
205
|
+
if market['spot']:
|
206
|
+
instType = 'SP'
|
207
|
+
elif market['swap']:
|
208
|
+
instType = 'MC'
|
209
|
+
else:
|
210
|
+
raise NotSupported(self.id + ' supports only spot and swap markets')
|
211
|
+
return [instType, instId]
|
212
|
+
|
213
|
+
def handle_dmcbl_market_by_message_hashes(self, market: Market, hash: str, client: Client, timeframe: Str = None):
|
214
|
+
marketId = market['id']
|
215
|
+
messageHashes = self.find_message_hashes(client, hash)
|
216
|
+
# the exchange counts DMCBL markets same market with different quote currencies
|
217
|
+
# for example symbols ETHUSD:ETH and ETH/USD:BTC both have the same marketId ETHUSD_DMCBL
|
218
|
+
# we need to check all markets with the same marketId to find the correct market that is in messageHashes
|
219
|
+
marketsWithCurrentId = self.safe_list(self.markets_by_id, marketId, [])
|
220
|
+
suffix = ''
|
221
|
+
if timeframe is not None:
|
222
|
+
suffix = ':' + timeframe
|
223
|
+
for i in range(0, len(marketsWithCurrentId)):
|
224
|
+
market = marketsWithCurrentId[i]
|
225
|
+
symbol = market['symbol']
|
226
|
+
messageHash = hash + symbol + suffix
|
227
|
+
if self.in_array(messageHash, messageHashes):
|
228
|
+
return market
|
229
|
+
return market
|
230
|
+
|
231
|
+
async def watch_ticker(self, symbol: str, params={}) -> Ticker:
|
232
|
+
"""
|
233
|
+
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
234
|
+
|
235
|
+
https://coincatch.github.io/github.io/en/spot/#tickers-channel
|
236
|
+
|
237
|
+
:param str symbol: unified symbol of the market to fetch the ticker for
|
238
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
239
|
+
:param str [params.instType]: the type of the instrument to fetch the ticker for, 'SP' for spot markets, 'MC' for futures markets(default is 'SP')
|
240
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
241
|
+
"""
|
242
|
+
await self.load_markets()
|
243
|
+
market = self.market(symbol)
|
244
|
+
instType, instId = self.get_public_inst_type_and_id(market)
|
245
|
+
channel = 'ticker'
|
246
|
+
messageHash = channel + ':' + symbol
|
247
|
+
args: dict = {
|
248
|
+
'instType': instType,
|
249
|
+
'channel': channel,
|
250
|
+
'instId': instId,
|
251
|
+
}
|
252
|
+
return await self.watch_public(messageHash, messageHash, args, params)
|
253
|
+
|
254
|
+
async def un_watch_ticker(self, symbol: str, params={}) -> Any:
|
255
|
+
"""
|
256
|
+
unsubscribe from the ticker channel
|
257
|
+
|
258
|
+
https://coincatch.github.io/github.io/en/mix/#tickers-channel
|
259
|
+
|
260
|
+
:param str symbol: unified symbol of the market to unwatch the ticker for
|
261
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
262
|
+
:returns any: status of the unwatch request
|
263
|
+
"""
|
264
|
+
await self.load_markets()
|
265
|
+
return await self.un_watch_channel(symbol, 'ticker', 'ticker', params)
|
266
|
+
|
267
|
+
async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
268
|
+
"""
|
269
|
+
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
|
270
|
+
|
271
|
+
https://coincatch.github.io/github.io/en/mix/#tickers-channel
|
272
|
+
|
273
|
+
:param str[] symbols: unified symbol of the market to watch the tickers for
|
274
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
275
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
276
|
+
"""
|
277
|
+
await self.load_markets()
|
278
|
+
if symbols is None:
|
279
|
+
symbols = self.symbols
|
280
|
+
topics = []
|
281
|
+
messageHashes = []
|
282
|
+
for i in range(0, len(symbols)):
|
283
|
+
symbol = symbols[i]
|
284
|
+
market = self.market(symbol)
|
285
|
+
instType, instId = self.get_public_inst_type_and_id(market)
|
286
|
+
args: dict = {
|
287
|
+
'instType': instType,
|
288
|
+
'channel': 'ticker',
|
289
|
+
'instId': instId,
|
290
|
+
}
|
291
|
+
topics.append(args)
|
292
|
+
messageHashes.append('ticker:' + symbol)
|
293
|
+
tickers = await self.watch_public_multiple(messageHashes, messageHashes, topics, params)
|
294
|
+
if self.newUpdates:
|
295
|
+
result: dict = {}
|
296
|
+
result[tickers['symbol']] = tickers
|
297
|
+
return result
|
298
|
+
return self.filter_by_array(self.tickers, 'symbol', symbols)
|
299
|
+
|
300
|
+
def handle_ticker(self, client: Client, message):
|
301
|
+
#
|
302
|
+
# action: 'snapshot',
|
303
|
+
# arg: {instType: 'sp', channel: 'ticker', instId: 'ETHUSDT'},
|
304
|
+
# data: [
|
305
|
+
# {
|
306
|
+
# instId: 'ETHUSDT',
|
307
|
+
# last: '2421.06',
|
308
|
+
# open24h: '2416.93',
|
309
|
+
# high24h: '2441.47',
|
310
|
+
# low24h: '2352.99',
|
311
|
+
# bestBid: '2421.03',
|
312
|
+
# bestAsk: '2421.06',
|
313
|
+
# baseVolume: '9445.2043',
|
314
|
+
# quoteVolume: '22807159.1148',
|
315
|
+
# ts: 1728131730687,
|
316
|
+
# labeId: 0,
|
317
|
+
# openUtc: '2414.50',
|
318
|
+
# chgUTC: '0.00272',
|
319
|
+
# bidSz: '3.866',
|
320
|
+
# askSz: '0.124'
|
321
|
+
# }
|
322
|
+
# ],
|
323
|
+
# ts: 1728131730688
|
324
|
+
#
|
325
|
+
arg = self.safe_dict(message, 'arg', {})
|
326
|
+
market = self.get_market_from_arg(arg)
|
327
|
+
marketId = market['id']
|
328
|
+
hash = 'ticker:'
|
329
|
+
if marketId.find('_DMCBL') >= 0:
|
330
|
+
market = self.handle_dmcbl_market_by_message_hashes(market, hash, client)
|
331
|
+
data = self.safe_list(message, 'data', [])
|
332
|
+
ticker = self.parse_ws_ticker(self.safe_dict(data, 0, {}), market)
|
333
|
+
symbol = market['symbol']
|
334
|
+
self.tickers[symbol] = ticker
|
335
|
+
messageHash = hash + symbol
|
336
|
+
client.resolve(self.tickers[symbol], messageHash)
|
337
|
+
|
338
|
+
def parse_ws_ticker(self, ticker, market=None):
|
339
|
+
#
|
340
|
+
# spot
|
341
|
+
# {
|
342
|
+
# instId: 'ETHUSDT',
|
343
|
+
# last: '2421.06',
|
344
|
+
# open24h: '2416.93',
|
345
|
+
# high24h: '2441.47',
|
346
|
+
# low24h: '2352.99',
|
347
|
+
# bestBid: '2421.03',
|
348
|
+
# bestAsk: '2421.06',
|
349
|
+
# baseVolume: '9445.2043',
|
350
|
+
# quoteVolume: '22807159.1148',
|
351
|
+
# ts: 1728131730687,
|
352
|
+
# labeId: 0,
|
353
|
+
# openUtc: '2414.50',
|
354
|
+
# chgUTC: '0.00272',
|
355
|
+
# bidSz: '3.866',
|
356
|
+
# askSz: '0.124'
|
357
|
+
# }
|
358
|
+
#
|
359
|
+
# swap
|
360
|
+
# {
|
361
|
+
# instId: 'ETHUSDT',
|
362
|
+
# last: '2434.47',
|
363
|
+
# bestAsk: '2434.48',
|
364
|
+
# bestBid: '2434.47',
|
365
|
+
# high24h: '2471.68',
|
366
|
+
# low24h: '2400.01',
|
367
|
+
# priceChangePercent: '0.00674',
|
368
|
+
# capitalRate: '0.000082',
|
369
|
+
# nextSettleTime: 1728489600000,
|
370
|
+
# systemTime: 1728471993602,
|
371
|
+
# markPrice: '2434.46',
|
372
|
+
# indexPrice: '2435.44',
|
373
|
+
# holding: '171450.25',
|
374
|
+
# baseVolume: '1699298.91',
|
375
|
+
# quoteVolume: '4144522832.32',
|
376
|
+
# openUtc: '2439.67',
|
377
|
+
# chgUTC: '-0.00213',
|
378
|
+
# symbolType: 1,
|
379
|
+
# symbolId: 'ETHUSDT_UMCBL',
|
380
|
+
# deliveryPrice: '0',
|
381
|
+
# bidSz: '26.12',
|
382
|
+
# askSz: '49.6'
|
383
|
+
# }
|
384
|
+
#
|
385
|
+
last = self.safe_string(ticker, 'last')
|
386
|
+
timestamp = self.safe_integer_2(ticker, 'ts', 'systemTime')
|
387
|
+
return self.safe_ticker({
|
388
|
+
'symbol': market['symbol'],
|
389
|
+
'timestamp': timestamp,
|
390
|
+
'datetime': self.iso8601(timestamp),
|
391
|
+
'high': self.safe_string(ticker, 'high24h'),
|
392
|
+
'low': self.safe_string(ticker, 'low24h'),
|
393
|
+
'bid': self.safe_string(ticker, 'bestBid'),
|
394
|
+
'bidVolume': self.safe_string(ticker, 'bidSz'),
|
395
|
+
'ask': self.safe_string(ticker, 'bestAsk'),
|
396
|
+
'askVolume': self.safe_string(ticker, 'askSz'),
|
397
|
+
'vwap': None,
|
398
|
+
'open': self.safe_string_2(ticker, 'open24h', 'openUtc'),
|
399
|
+
'close': last,
|
400
|
+
'last': last,
|
401
|
+
'previousClose': None,
|
402
|
+
'change': None,
|
403
|
+
'percentage': Precise.string_mul(self.safe_string(ticker, 'chgUTC'), '100'),
|
404
|
+
'average': None,
|
405
|
+
'baseVolume': self.safe_number(ticker, 'baseVolume'),
|
406
|
+
'quoteVolume': self.safe_number(ticker, 'quoteVolume'),
|
407
|
+
'indexPrice': self.safe_string(ticker, 'indexPrice'),
|
408
|
+
'markPrice': self.safe_string(ticker, 'markPrice'),
|
409
|
+
'info': ticker,
|
410
|
+
}, market)
|
411
|
+
|
412
|
+
async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
413
|
+
"""
|
414
|
+
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
415
|
+
|
416
|
+
https://coincatch.github.io/github.io/en/spot/#candlesticks-channel
|
417
|
+
|
418
|
+
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
419
|
+
:param str timeframe: the length of time each candle represents
|
420
|
+
:param int [since]: timestamp in ms of the earliest candle to fetch(not including)
|
421
|
+
:param int [limit]: the maximum amount of candles to fetch(not including)
|
422
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
423
|
+
:param bool [params.instType]: the type of the instrument to fetch the OHLCV data for, 'SP' for spot markets, 'MC' for futures markets(default is 'SP')
|
424
|
+
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
425
|
+
"""
|
426
|
+
await self.load_markets()
|
427
|
+
market = self.market(symbol)
|
428
|
+
timeframes = self.options['timeframesForWs']
|
429
|
+
channel = 'candle' + self.safe_string(timeframes, timeframe)
|
430
|
+
instType, instId = self.get_public_inst_type_and_id(market)
|
431
|
+
args: dict = {
|
432
|
+
'instType': instType,
|
433
|
+
'channel': channel,
|
434
|
+
'instId': instId,
|
435
|
+
}
|
436
|
+
messageHash = 'ohlcv:' + symbol + ':' + timeframe
|
437
|
+
ohlcv = await self.watch_public(messageHash, messageHash, args, params)
|
438
|
+
if self.newUpdates:
|
439
|
+
limit = ohlcv.getLimit(symbol, limit)
|
440
|
+
return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
|
441
|
+
|
442
|
+
async def un_watch_ohlcv(self, symbol: str, timeframe='1m', params={}) -> Any:
|
443
|
+
"""
|
444
|
+
unsubscribe from the ohlcv channel
|
445
|
+
|
446
|
+
https://www.bitget.com/api-doc/spot/websocket/public/Candlesticks-Channel
|
447
|
+
|
448
|
+
:param str symbol: unified symbol of the market to unwatch the ohlcv for
|
449
|
+
@param timeframe
|
450
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
451
|
+
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
452
|
+
"""
|
453
|
+
await self.load_markets()
|
454
|
+
timeframes = self.options['timeframesForWs']
|
455
|
+
interval = self.safe_string(timeframes, timeframe)
|
456
|
+
channel = 'candle' + interval
|
457
|
+
return await self.un_watch_channel(symbol, channel, 'ohlcv:' + interval, params)
|
458
|
+
|
459
|
+
def handle_ohlcv(self, client: Client, message):
|
460
|
+
#
|
461
|
+
# {
|
462
|
+
# action: 'update',
|
463
|
+
# arg: {instType: 'sp', channel: 'candle1D', instId: 'ETHUSDT'},
|
464
|
+
# data: [
|
465
|
+
# [
|
466
|
+
# '1728316800000',
|
467
|
+
# '2474.5',
|
468
|
+
# '2478.21',
|
469
|
+
# '2459.8',
|
470
|
+
# '2463.51',
|
471
|
+
# '86.0551'
|
472
|
+
# ]
|
473
|
+
# ],
|
474
|
+
# ts: 1728317607657
|
475
|
+
# }
|
476
|
+
#
|
477
|
+
arg = self.safe_dict(message, 'arg', {})
|
478
|
+
market = self.get_market_from_arg(arg)
|
479
|
+
marketId = market['id']
|
480
|
+
hash = 'ohlcv:'
|
481
|
+
data = self.safe_list(message, 'data', [])
|
482
|
+
channel = self.safe_string(arg, 'channel')
|
483
|
+
klineType = channel[6:]
|
484
|
+
timeframe = self.find_timeframe(klineType)
|
485
|
+
if marketId.find('_DMCBL') >= 0:
|
486
|
+
market = self.handle_dmcbl_market_by_message_hashes(market, hash, client, timeframe)
|
487
|
+
symbol = market['symbol']
|
488
|
+
if not (symbol in self.ohlcvs):
|
489
|
+
self.ohlcvs[symbol] = {}
|
490
|
+
if not (timeframe in self.ohlcvs[symbol]):
|
491
|
+
limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
|
492
|
+
self.ohlcvs[symbol][timeframe] = ArrayCacheByTimestamp(limit)
|
493
|
+
stored = self.ohlcvs[symbol][timeframe]
|
494
|
+
for i in range(0, len(data)):
|
495
|
+
candle = self.safe_list(data, i, [])
|
496
|
+
parsed = self.parse_ws_ohlcv(candle, market)
|
497
|
+
stored.append(parsed)
|
498
|
+
messageHash = hash + symbol + ':' + timeframe
|
499
|
+
client.resolve(stored, messageHash)
|
500
|
+
|
501
|
+
def parse_ws_ohlcv(self, ohlcv, market: Market = None) -> list:
|
502
|
+
#
|
503
|
+
# [
|
504
|
+
# '1728316800000',
|
505
|
+
# '2474.5',
|
506
|
+
# '2478.21',
|
507
|
+
# '2459.8',
|
508
|
+
# '2463.51',
|
509
|
+
# '86.0551'
|
510
|
+
# ]
|
511
|
+
#
|
512
|
+
return [
|
513
|
+
self.safe_integer(ohlcv, 0),
|
514
|
+
self.safe_number(ohlcv, 1),
|
515
|
+
self.safe_number(ohlcv, 2),
|
516
|
+
self.safe_number(ohlcv, 3),
|
517
|
+
self.safe_number(ohlcv, 4),
|
518
|
+
self.safe_number(ohlcv, 5),
|
519
|
+
]
|
520
|
+
|
521
|
+
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
522
|
+
"""
|
523
|
+
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
524
|
+
|
525
|
+
https://coincatch.github.io/github.io/en/spot/#depth-channel
|
526
|
+
|
527
|
+
:param str symbol: unified symbol of the market to fetch the order book for
|
528
|
+
:param int [limit]: the maximum amount of order book entries to return
|
529
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
530
|
+
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
531
|
+
"""
|
532
|
+
return await self.watch_order_book_for_symbols([symbol], limit, params)
|
533
|
+
|
534
|
+
async def un_watch_order_book(self, symbol: str, params={}) -> Any:
|
535
|
+
"""
|
536
|
+
unsubscribe from the orderbook channel
|
537
|
+
|
538
|
+
https://coincatch.github.io/github.io/en/spot/#depth-channel
|
539
|
+
|
540
|
+
:param str symbol: unified symbol of the market to fetch the order book for
|
541
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
542
|
+
:param int [params.limit]: orderbook limit, default is None
|
543
|
+
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
544
|
+
"""
|
545
|
+
await self.load_markets()
|
546
|
+
channel = 'books'
|
547
|
+
limit = self.safe_integer(params, 'limit')
|
548
|
+
if (limit == 5) or (limit == 15):
|
549
|
+
params = self.omit(params, 'limit')
|
550
|
+
channel += str(limit)
|
551
|
+
return await self.un_watch_channel(symbol, channel, channel, params)
|
552
|
+
|
553
|
+
async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
|
554
|
+
"""
|
555
|
+
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
556
|
+
|
557
|
+
https://coincatch.github.io/github.io/en/spot/#depth-channel
|
558
|
+
|
559
|
+
@param symbols
|
560
|
+
:param int [limit]: the maximum amount of order book entries to return
|
561
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
562
|
+
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
563
|
+
"""
|
564
|
+
await self.load_markets()
|
565
|
+
symbols = self.market_symbols(symbols)
|
566
|
+
channel = 'books'
|
567
|
+
topics = []
|
568
|
+
messageHashes = []
|
569
|
+
for i in range(0, len(symbols)):
|
570
|
+
symbol = symbols[i]
|
571
|
+
market = self.market(symbol)
|
572
|
+
instType, instId = self.get_public_inst_type_and_id(market)
|
573
|
+
args: dict = {
|
574
|
+
'instType': instType,
|
575
|
+
'channel': channel,
|
576
|
+
'instId': instId,
|
577
|
+
}
|
578
|
+
topics.append(args)
|
579
|
+
messageHashes.append(channel + ':' + symbol)
|
580
|
+
orderbook = await self.watch_public_multiple(messageHashes, messageHashes, topics, params)
|
581
|
+
return orderbook.limit()
|
582
|
+
|
583
|
+
def handle_order_book(self, client: Client, message):
|
584
|
+
#
|
585
|
+
# {
|
586
|
+
# action: 'update',
|
587
|
+
# arg: {instType: 'sp', channel: 'books', instId: 'ETHUSDT'},
|
588
|
+
# data: [
|
589
|
+
# {
|
590
|
+
# asks: [[2507.07, 0.4248]],
|
591
|
+
# bids: [[2507.05, 0.1198]],
|
592
|
+
# checksum: -1400923312,
|
593
|
+
# ts: '1728339446908'
|
594
|
+
# }
|
595
|
+
# ],
|
596
|
+
# ts: 1728339446908
|
597
|
+
# }
|
598
|
+
#
|
599
|
+
arg = self.safe_dict(message, 'arg', {})
|
600
|
+
market = self.get_market_from_arg(arg)
|
601
|
+
marketId = market['id']
|
602
|
+
hash = 'books:'
|
603
|
+
if marketId.find('_DMCBL') >= 0:
|
604
|
+
market = self.handle_dmcbl_market_by_message_hashes(market, hash, client)
|
605
|
+
symbol = market['symbol']
|
606
|
+
channel = self.safe_string(arg, 'channel')
|
607
|
+
messageHash = hash + symbol
|
608
|
+
data = self.safe_list(message, 'data', [])
|
609
|
+
rawOrderBook = self.safe_dict(data, 0)
|
610
|
+
timestamp = self.safe_integer(rawOrderBook, 'ts')
|
611
|
+
incrementalBook = channel
|
612
|
+
if incrementalBook:
|
613
|
+
if not (symbol in self.orderbooks):
|
614
|
+
ob = self.counted_order_book({})
|
615
|
+
ob['symbol'] = symbol
|
616
|
+
self.orderbooks[symbol] = ob
|
617
|
+
storedOrderBook = self.orderbooks[symbol]
|
618
|
+
asks = self.safe_list(rawOrderBook, 'asks', [])
|
619
|
+
bids = self.safe_list(rawOrderBook, 'bids', [])
|
620
|
+
self.handle_deltas(storedOrderBook['asks'], asks)
|
621
|
+
self.handle_deltas(storedOrderBook['bids'], bids)
|
622
|
+
storedOrderBook['timestamp'] = timestamp
|
623
|
+
storedOrderBook['datetime'] = self.iso8601(timestamp)
|
624
|
+
checksum = self.safe_bool(self.options, 'checksum', True)
|
625
|
+
isSnapshot = self.safe_string(message, 'action') == 'snapshot'
|
626
|
+
if not isSnapshot and checksum:
|
627
|
+
storedAsks = storedOrderBook['asks']
|
628
|
+
storedBids = storedOrderBook['bids']
|
629
|
+
asksLength = len(storedAsks)
|
630
|
+
bidsLength = len(storedBids)
|
631
|
+
payloadArray = []
|
632
|
+
for i in range(0, 25):
|
633
|
+
if i < bidsLength:
|
634
|
+
payloadArray.append(storedBids[i][2][0])
|
635
|
+
payloadArray.append(storedBids[i][2][1])
|
636
|
+
if i < asksLength:
|
637
|
+
payloadArray.append(storedAsks[i][2][0])
|
638
|
+
payloadArray.append(storedAsks[i][2][1])
|
639
|
+
payload = ':'.join(payloadArray)
|
640
|
+
calculatedChecksum = self.crc32(payload, True)
|
641
|
+
responseChecksum = self.safe_integer(rawOrderBook, 'checksum')
|
642
|
+
if calculatedChecksum != responseChecksum:
|
643
|
+
self.spawn(self.handle_check_sum_error, client, symbol, messageHash)
|
644
|
+
return
|
645
|
+
else:
|
646
|
+
orderbook = self.order_book({})
|
647
|
+
parsedOrderbook = self.parse_order_book(rawOrderBook, symbol, timestamp)
|
648
|
+
orderbook.reset(parsedOrderbook)
|
649
|
+
self.orderbooks[symbol] = orderbook
|
650
|
+
client.resolve(self.orderbooks[symbol], messageHash)
|
651
|
+
|
652
|
+
async def handle_check_sum_error(self, client: Client, symbol: str, messageHash: str):
|
653
|
+
await self.un_watch_order_book(symbol)
|
654
|
+
error = ChecksumError(self.id + ' ' + self.orderbook_checksum_message(symbol))
|
655
|
+
client.reject(error, messageHash)
|
656
|
+
|
657
|
+
def handle_delta(self, bookside, delta):
|
658
|
+
bidAsk = self.parse_bid_ask(delta, 0, 1)
|
659
|
+
bidAsk.append(delta)
|
660
|
+
bookside.storeArray(bidAsk)
|
661
|
+
|
662
|
+
def handle_deltas(self, bookside, deltas):
|
663
|
+
for i in range(0, len(deltas)):
|
664
|
+
self.handle_delta(bookside, deltas[i])
|
665
|
+
|
666
|
+
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
667
|
+
"""
|
668
|
+
get the list of most recent trades for a particular symbol
|
669
|
+
|
670
|
+
https://coincatch.github.io/github.io/en/spot/#trades-channel
|
671
|
+
|
672
|
+
:param str symbol: unified symbol of the market to fetch trades for
|
673
|
+
:param int [since]: timestamp in ms of the earliest trade to fetch
|
674
|
+
:param int [limit]: the maximum amount of trades to fetch
|
675
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
676
|
+
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
677
|
+
"""
|
678
|
+
return await self.watch_trades_for_symbols([symbol], since, limit, params)
|
679
|
+
|
680
|
+
async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
681
|
+
"""
|
682
|
+
watches information on multiple trades made in a market
|
683
|
+
|
684
|
+
https://coincatch.github.io/github.io/en/spot/#trades-channel
|
685
|
+
|
686
|
+
@param symbols
|
687
|
+
:param int [since]: the earliest time in ms to fetch orders for
|
688
|
+
:param int [limit]: the maximum number of trade structures to retrieve
|
689
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
690
|
+
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
|
691
|
+
"""
|
692
|
+
symbolsLength = len(symbols)
|
693
|
+
if symbolsLength == 0:
|
694
|
+
raise ArgumentsRequired(self.id + ' watchTradesForSymbols() requires a non-empty array of symbols')
|
695
|
+
await self.load_markets()
|
696
|
+
symbols = self.market_symbols(symbols)
|
697
|
+
topics = []
|
698
|
+
messageHashes = []
|
699
|
+
for i in range(0, len(symbols)):
|
700
|
+
symbol = symbols[i]
|
701
|
+
market = self.market(symbol)
|
702
|
+
instType, instId = self.get_public_inst_type_and_id(market)
|
703
|
+
args: dict = {
|
704
|
+
'instType': instType,
|
705
|
+
'channel': 'trade',
|
706
|
+
'instId': instId,
|
707
|
+
}
|
708
|
+
topics.append(args)
|
709
|
+
messageHashes.append('trade:' + symbol)
|
710
|
+
trades = await self.watch_public_multiple(messageHashes, messageHashes, topics, params)
|
711
|
+
if self.newUpdates:
|
712
|
+
first = self.safe_dict(trades, 0)
|
713
|
+
tradeSymbol = self.safe_string(first, 'symbol')
|
714
|
+
limit = trades.getLimit(tradeSymbol, limit)
|
715
|
+
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
716
|
+
|
717
|
+
async def un_watch_trades(self, symbol: str, params={}) -> Any:
|
718
|
+
"""
|
719
|
+
unsubscribe from the trades channel
|
720
|
+
|
721
|
+
https://coincatch.github.io/github.io/en/spot/#trades-channel
|
722
|
+
|
723
|
+
:param str symbol: unified symbol of the market to unwatch the trades for
|
724
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
725
|
+
:returns any: status of the unwatch request
|
726
|
+
"""
|
727
|
+
await self.load_markets()
|
728
|
+
return await self.un_watch_channel(symbol, 'trade', 'trade', params)
|
729
|
+
|
730
|
+
def handle_trades(self, client: Client, message):
|
731
|
+
#
|
732
|
+
# {
|
733
|
+
# action: 'update',
|
734
|
+
# arg: {instType: 'sp', channel: 'trade', instId: 'ETHUSDT'},
|
735
|
+
# data: [['1728341807469', '2421.41', '0.478', 'sell']],
|
736
|
+
# ts: 1728341807482
|
737
|
+
# }
|
738
|
+
#
|
739
|
+
arg = self.safe_dict(message, 'arg', {})
|
740
|
+
market = self.get_market_from_arg(arg)
|
741
|
+
marketId = market['id']
|
742
|
+
hash = 'trade:'
|
743
|
+
if marketId.find('_DMCBL') >= 0:
|
744
|
+
market = self.handle_dmcbl_market_by_message_hashes(market, hash, client)
|
745
|
+
symbol = market['symbol']
|
746
|
+
if not (symbol in self.trades):
|
747
|
+
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
748
|
+
self.trades[symbol] = ArrayCache(limit)
|
749
|
+
stored = self.trades[symbol]
|
750
|
+
data = self.safe_list(message, 'data', [])
|
751
|
+
if data is not None:
|
752
|
+
data = self.sort_by(data, 0)
|
753
|
+
for i in range(0, len(data)):
|
754
|
+
trade = self.safe_list(data, i)
|
755
|
+
parsed = self.parse_ws_trade(trade, market)
|
756
|
+
stored.append(parsed)
|
757
|
+
messageHash = hash + symbol
|
758
|
+
client.resolve(stored, messageHash)
|
759
|
+
|
760
|
+
def parse_ws_trade(self, trade, market=None) -> Trade:
|
761
|
+
#
|
762
|
+
# [
|
763
|
+
# '1728341807469',
|
764
|
+
# '2421.41',
|
765
|
+
# '0.478',
|
766
|
+
# 'sell'
|
767
|
+
# ]
|
768
|
+
#
|
769
|
+
timestamp = self.safe_integer(trade, 0)
|
770
|
+
return self.safe_trade({
|
771
|
+
'id': None,
|
772
|
+
'timestamp': timestamp,
|
773
|
+
'datetime': self.iso8601(timestamp),
|
774
|
+
'symbol': market['symbol'],
|
775
|
+
'side': self.safe_string_lower(trade, 3),
|
776
|
+
'price': self.safe_string(trade, 1),
|
777
|
+
'amount': self.safe_string(trade, 2),
|
778
|
+
'cost': None,
|
779
|
+
'takerOrMaker': None,
|
780
|
+
'type': None,
|
781
|
+
'order': None,
|
782
|
+
'fee': None,
|
783
|
+
'info': trade,
|
784
|
+
}, market)
|
785
|
+
|
786
|
+
async def watch_balance(self, params={}) -> Balances:
|
787
|
+
"""
|
788
|
+
watch balance and get the amount of funds available for trading or funds locked in orders
|
789
|
+
|
790
|
+
https://coincatch.github.io/github.io/en/spot/#account-channel
|
791
|
+
https://coincatch.github.io/github.io/en/mix/#account-channel
|
792
|
+
|
793
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
794
|
+
:param str [params.type]: 'spot' or 'swap'(default is 'spot')
|
795
|
+
:param str [params.instType]: *swap only* 'umcbl' or 'dmcbl'(default is 'umcbl')
|
796
|
+
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
|
797
|
+
"""
|
798
|
+
type = None
|
799
|
+
type, params = self.handle_market_type_and_params('watchBalance', None, params)
|
800
|
+
instType = 'spbl' # must be lower case for spot
|
801
|
+
if type == 'swap':
|
802
|
+
instType = 'umcbl'
|
803
|
+
channel = 'account'
|
804
|
+
instType, params = self.handle_option_and_params(params, 'watchBalance', 'instType', instType)
|
805
|
+
args: dict = {
|
806
|
+
'instType': instType,
|
807
|
+
'channel': channel,
|
808
|
+
'instId': 'default',
|
809
|
+
}
|
810
|
+
messageHash = 'balance:' + instType.lower()
|
811
|
+
return await self.watch_private(messageHash, messageHash, args, params)
|
812
|
+
|
813
|
+
def handle_balance(self, client: Client, message):
|
814
|
+
#
|
815
|
+
# spot
|
816
|
+
# {
|
817
|
+
# action: 'snapshot',
|
818
|
+
# arg: {instType: 'spbl', channel: 'account', instId: 'default'},
|
819
|
+
# data: [
|
820
|
+
# {
|
821
|
+
# coinId: '3',
|
822
|
+
# coinName: 'ETH',
|
823
|
+
# available: '0.0000832',
|
824
|
+
# frozen: '0',
|
825
|
+
# lock: '0'
|
826
|
+
# }
|
827
|
+
# ],
|
828
|
+
# ts: 1728464548725
|
829
|
+
# }
|
830
|
+
#
|
831
|
+
# # swap
|
832
|
+
# {
|
833
|
+
# action: 'snapshot',
|
834
|
+
# arg: {instType: 'dmcbl', channel: 'account', instId: 'default'},
|
835
|
+
# data: [
|
836
|
+
# {
|
837
|
+
# marginCoin: 'ETH',
|
838
|
+
# locked: '0.00000000',
|
839
|
+
# available: '0.00001203',
|
840
|
+
# maxOpenPosAvailable: '0.00001203',
|
841
|
+
# maxTransferOut: '0.00001203',
|
842
|
+
# equity: '0.00001203',
|
843
|
+
# usdtEquity: '0.029092328738',
|
844
|
+
# coinDisplayName: 'ETH'
|
845
|
+
# }
|
846
|
+
# ],
|
847
|
+
# ts: 1728650777643
|
848
|
+
# }
|
849
|
+
#
|
850
|
+
data = self.safe_list(message, 'data', [])
|
851
|
+
for i in range(0, len(data)):
|
852
|
+
rawBalance = data[i]
|
853
|
+
currencyId = self.safe_string_2(rawBalance, 'coinName', 'marginCoin')
|
854
|
+
code = self.safe_currency_code(currencyId)
|
855
|
+
account = self.balance[code] if (code in self.balance) else self.account()
|
856
|
+
freeQuery = 'maxTransferOut' if ('maxTransferOut' in rawBalance) else 'available'
|
857
|
+
account['free'] = self.safe_string(rawBalance, freeQuery)
|
858
|
+
account['total'] = self.safe_string(rawBalance, 'equity')
|
859
|
+
account['used'] = self.safe_string(rawBalance, 'frozen')
|
860
|
+
self.balance[code] = account
|
861
|
+
self.balance = self.safe_balance(self.balance)
|
862
|
+
arg = self.safe_dict(message, 'arg')
|
863
|
+
instType = self.safe_string_lower(arg, 'instType')
|
864
|
+
messageHash = 'balance:' + instType
|
865
|
+
client.resolve(self.balance, messageHash)
|
866
|
+
|
867
|
+
async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
868
|
+
"""
|
869
|
+
watches information on multiple orders made by the user
|
870
|
+
|
871
|
+
https://coincatch.github.io/github.io/en/spot/#order-channel
|
872
|
+
https://coincatch.github.io/github.io/en/mix/#order-channel
|
873
|
+
https://coincatch.github.io/github.io/en/mix/#plan-order-channel
|
874
|
+
|
875
|
+
:param str symbol: unified market symbol of the market orders were made in
|
876
|
+
:param int [since]: the earliest time in ms to fetch orders for
|
877
|
+
:param int [limit]: the maximum number of order structures to retrieve
|
878
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
879
|
+
:param str [params.type]: 'spot' or 'swap'
|
880
|
+
:param str [params.instType]: *swap only* 'umcbl' or 'dmcbl'(default is 'umcbl')
|
881
|
+
:param bool [params.trigger]: *swap only* whether to watch trigger orders(default is False)
|
882
|
+
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
883
|
+
"""
|
884
|
+
methodName = 'watchOrders'
|
885
|
+
await self.load_markets()
|
886
|
+
market = None
|
887
|
+
marketId = None
|
888
|
+
if symbol is not None:
|
889
|
+
market = self.market(symbol)
|
890
|
+
symbol = market['symbol']
|
891
|
+
marketId = market['id']
|
892
|
+
marketType = None
|
893
|
+
marketType, params = self.handle_market_type_and_params(methodName, market, params)
|
894
|
+
instType = 'spbl'
|
895
|
+
instId = marketId
|
896
|
+
if marketType == 'spot':
|
897
|
+
if symbol is None:
|
898
|
+
raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a symbol argument for ' + marketType + ' markets.')
|
899
|
+
else:
|
900
|
+
instId = 'default'
|
901
|
+
instType = 'umcbl'
|
902
|
+
if symbol is None:
|
903
|
+
instType, params = self.handle_option_and_params(params, methodName, 'instType', instType)
|
904
|
+
else:
|
905
|
+
if marketId.find('_DMCBL') >= 0:
|
906
|
+
instType = 'dmcbl'
|
907
|
+
channel = 'orders'
|
908
|
+
isTrigger = self.safe_bool(params, 'trigger')
|
909
|
+
if isTrigger:
|
910
|
+
channel = 'ordersAlgo' # channel does not return any data
|
911
|
+
params = self.omit(params, 'trigger')
|
912
|
+
args: dict = {
|
913
|
+
'instType': instType,
|
914
|
+
'channel': channel,
|
915
|
+
'instId': instId,
|
916
|
+
}
|
917
|
+
messageHash = 'orders'
|
918
|
+
if symbol is not None:
|
919
|
+
messageHash += ':' + symbol
|
920
|
+
orders = await self.watch_private(messageHash, messageHash, args, params)
|
921
|
+
if self.newUpdates:
|
922
|
+
limit = orders.getLimit(symbol, limit)
|
923
|
+
return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)
|
924
|
+
|
925
|
+
def handle_order(self, client: Client, message):
|
926
|
+
#
|
927
|
+
# spot
|
928
|
+
#
|
929
|
+
# {
|
930
|
+
# action: 'snapshot',
|
931
|
+
# arg: {instType: 'spbl', channel: 'orders', instId: 'ETHUSDT_SPBL'},
|
932
|
+
# data: [
|
933
|
+
# {
|
934
|
+
# instId: 'ETHUSDT_SPBL',
|
935
|
+
# ordId: '1228627925964996608',
|
936
|
+
# clOrdId: 'f0cccf74-c535-4523-a53d-dbe3b9958559',
|
937
|
+
# px: '2000',
|
938
|
+
# sz: '0.001',
|
939
|
+
# notional: '2',
|
940
|
+
# ordType: 'limit',
|
941
|
+
# force: 'normal',
|
942
|
+
# side: 'buy',
|
943
|
+
# accFillSz: '0',
|
944
|
+
# avgPx: '0',
|
945
|
+
# status: 'new',
|
946
|
+
# cTime: 1728653645030,
|
947
|
+
# uTime: 1728653645030,
|
948
|
+
# orderFee: [],
|
949
|
+
# eps: 'API'
|
950
|
+
# }
|
951
|
+
# ],
|
952
|
+
# ts: 1728653645046
|
953
|
+
# }
|
954
|
+
#
|
955
|
+
# swap
|
956
|
+
#
|
957
|
+
# {
|
958
|
+
# action: 'snapshot',
|
959
|
+
# arg: {instType: 'umcbl', channel: 'orders', instId: 'default'},
|
960
|
+
# data: [
|
961
|
+
# {
|
962
|
+
# accFillSz: '0',
|
963
|
+
# cTime: 1728653796976,
|
964
|
+
# clOrdId: '1228628563272753152',
|
965
|
+
# eps: 'API',
|
966
|
+
# force: 'normal',
|
967
|
+
# hM: 'single_hold',
|
968
|
+
# instId: 'ETHUSDT_UMCBL',
|
969
|
+
# lever: '5',
|
970
|
+
# low: False,
|
971
|
+
# notionalUsd: '20',
|
972
|
+
# ordId: '1228628563188867072',
|
973
|
+
# ordType: 'limit',
|
974
|
+
# orderFee: [],
|
975
|
+
# posSide: 'net',
|
976
|
+
# px: '2000',
|
977
|
+
# side: 'buy',
|
978
|
+
# status: 'new',
|
979
|
+
# sz: '0.01',
|
980
|
+
# tS: 'buy_single',
|
981
|
+
# tdMode: 'cross',
|
982
|
+
# tgtCcy: 'USDT',
|
983
|
+
# uTime: 1728653796976
|
984
|
+
# }
|
985
|
+
# ],
|
986
|
+
# ts: 1728653797002
|
987
|
+
# }
|
988
|
+
#
|
989
|
+
#
|
990
|
+
arg = self.safe_dict(message, 'arg', {})
|
991
|
+
instType = self.safe_string(arg, 'instType')
|
992
|
+
argInstId = self.safe_string(arg, 'instId')
|
993
|
+
marketType = None
|
994
|
+
if instType == 'spbl':
|
995
|
+
marketType = 'spot'
|
996
|
+
else:
|
997
|
+
marketType = 'swap'
|
998
|
+
data = self.safe_list(message, 'data', [])
|
999
|
+
if self.orders is None:
|
1000
|
+
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
|
1001
|
+
self.orders = ArrayCacheBySymbolById(limit)
|
1002
|
+
hash = 'orders'
|
1003
|
+
stored = self.orders
|
1004
|
+
symbol: Str = None
|
1005
|
+
for i in range(0, len(data)):
|
1006
|
+
order = data[i]
|
1007
|
+
marketId = self.safe_string(order, 'instId', argInstId)
|
1008
|
+
market = self.safe_market(marketId, None, None, marketType)
|
1009
|
+
parsed = self.parse_ws_order(order, market)
|
1010
|
+
stored.append(parsed)
|
1011
|
+
symbol = parsed['symbol']
|
1012
|
+
messageHash = 'orders:' + symbol
|
1013
|
+
client.resolve(stored, messageHash)
|
1014
|
+
client.resolve(stored, hash)
|
1015
|
+
|
1016
|
+
def parse_ws_order(self, order: dict, market: Market = None) -> Order:
|
1017
|
+
#
|
1018
|
+
# spot
|
1019
|
+
# {
|
1020
|
+
# instId: 'ETHUSDT_SPBL',
|
1021
|
+
# ordId: '1228627925964996608',
|
1022
|
+
# clOrdId: 'f0cccf74-c535-4523-a53d-dbe3b9958559',
|
1023
|
+
# px: '2000',
|
1024
|
+
# sz: '0.001',
|
1025
|
+
# notional: '2',
|
1026
|
+
# ordType: 'limit',
|
1027
|
+
# force: 'normal',
|
1028
|
+
# side: 'buy',
|
1029
|
+
# accFillSz: '0',
|
1030
|
+
# avgPx: '0',
|
1031
|
+
# status: 'new',
|
1032
|
+
# cTime: 1728653645030,
|
1033
|
+
# uTime: 1728653645030,
|
1034
|
+
# orderFee: orderFee: [{fee: '0', feeCcy: 'USDT'}],
|
1035
|
+
# eps: 'API'
|
1036
|
+
# }
|
1037
|
+
#
|
1038
|
+
# swap
|
1039
|
+
# {
|
1040
|
+
# accFillSz: '0',
|
1041
|
+
# cTime: 1728653796976,
|
1042
|
+
# clOrdId: '1228628563272753152',
|
1043
|
+
# eps: 'API',
|
1044
|
+
# force: 'normal',
|
1045
|
+
# hM: 'single_hold',
|
1046
|
+
# instId: 'ETHUSDT_UMCBL',
|
1047
|
+
# lever: '5',
|
1048
|
+
# low: False,
|
1049
|
+
# notionalUsd: '20',
|
1050
|
+
# ordId: '1228628563188867072',
|
1051
|
+
# ordType: 'limit',
|
1052
|
+
# orderFee: [{fee: '0', feeCcy: 'USDT'}],
|
1053
|
+
# posSide: 'net',
|
1054
|
+
# px: '2000',
|
1055
|
+
# side: 'buy',
|
1056
|
+
# status: 'new',
|
1057
|
+
# sz: '0.01',
|
1058
|
+
# tS: 'buy_single',
|
1059
|
+
# tdMode: 'cross',
|
1060
|
+
# tgtCcy: 'USDT',
|
1061
|
+
# uTime: 1728653796976
|
1062
|
+
# }
|
1063
|
+
#
|
1064
|
+
marketId = self.safe_string(order, 'instId')
|
1065
|
+
settleId = self.safe_string(order, 'tgtCcy')
|
1066
|
+
market = self.safeMarketCustom(marketId, market, settleId)
|
1067
|
+
timestamp = self.safe_integer(order, 'cTime')
|
1068
|
+
symbol = market['symbol']
|
1069
|
+
rawStatus = self.safe_string(order, 'status')
|
1070
|
+
orderFee = self.safe_list(order, 'orderFee', [])
|
1071
|
+
fee = self.safe_dict(orderFee, 0)
|
1072
|
+
feeCost = Precise.string_mul(self.safe_string(fee, 'fee'), '-1')
|
1073
|
+
feeCurrency = self.safe_string(fee, 'feeCcy')
|
1074
|
+
price = self.omit_zero(self.safe_string(order, 'px'))
|
1075
|
+
priceAvg = self.omit_zero(self.safe_string(order, 'avgPx'))
|
1076
|
+
if price is None:
|
1077
|
+
price = priceAvg
|
1078
|
+
type = self.safe_string_lower(order, 'ordType')
|
1079
|
+
return self.safe_order({
|
1080
|
+
'id': self.safe_string(order, 'ordId'),
|
1081
|
+
'clientOrderId': self.safe_string(order, 'clOrdId'),
|
1082
|
+
'timestamp': timestamp,
|
1083
|
+
'datetime': self.iso8601(timestamp),
|
1084
|
+
'lastTradeTimestamp': None,
|
1085
|
+
'lastUpdateTimestamp': self.safe_integer(order, 'uTime'),
|
1086
|
+
'status': self.parse_order_status(rawStatus),
|
1087
|
+
'symbol': symbol,
|
1088
|
+
'type': type,
|
1089
|
+
'timeInForce': self.parseOrderTimeInForce(self.safe_string_lower(order, 'force')),
|
1090
|
+
'side': self.safe_string_lower(order, 'side'),
|
1091
|
+
'price': price,
|
1092
|
+
'average': self.safe_string(order, 'avgPx'),
|
1093
|
+
'amount': self.safe_string(order, 'sz'),
|
1094
|
+
'filled': self.safe_string(order, 'accFillSz'),
|
1095
|
+
'remaining': None,
|
1096
|
+
'triggerPrice': None,
|
1097
|
+
'takeProfitPrice': None,
|
1098
|
+
'stopLossPrice': None,
|
1099
|
+
'cost': self.safe_string(order, 'notional'),
|
1100
|
+
'trades': None,
|
1101
|
+
'fee': {
|
1102
|
+
'currency': feeCurrency,
|
1103
|
+
'cost': feeCost,
|
1104
|
+
},
|
1105
|
+
'reduceOnly': self.safe_bool(order, 'low'),
|
1106
|
+
'postOnly': None,
|
1107
|
+
'info': order,
|
1108
|
+
}, market)
|
1109
|
+
|
1110
|
+
async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
|
1111
|
+
"""
|
1112
|
+
watch all open positions
|
1113
|
+
|
1114
|
+
https://coincatch.github.io/github.io/en/mix/#positions-channel
|
1115
|
+
|
1116
|
+
:param str[]|None symbols: list of unified market symbols
|
1117
|
+
@param since
|
1118
|
+
@param limit
|
1119
|
+
:param dict params: extra parameters specific to the exchange API endpoint
|
1120
|
+
:returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
|
1121
|
+
"""
|
1122
|
+
await self.load_markets()
|
1123
|
+
symbols = self.market_symbols(symbols, 'swap')
|
1124
|
+
messageHashes = []
|
1125
|
+
hash = 'positions'
|
1126
|
+
instTypes = []
|
1127
|
+
if symbols is not None:
|
1128
|
+
for i in range(0, len(symbols)):
|
1129
|
+
symbol = symbols[i]
|
1130
|
+
market = self.market(symbol)
|
1131
|
+
instType = self.get_private_inst_type(market)
|
1132
|
+
if not self.in_array(instType, instTypes):
|
1133
|
+
instTypes.append(instType)
|
1134
|
+
messageHashes.append(hash + '::' + symbol)
|
1135
|
+
else:
|
1136
|
+
instTypes = ['umcbl', 'dmcbl']
|
1137
|
+
messageHashes.append(hash)
|
1138
|
+
args = []
|
1139
|
+
subscribeHashes = []
|
1140
|
+
for i in range(0, len(instTypes)):
|
1141
|
+
instType = instTypes[i]
|
1142
|
+
arg: dict = {
|
1143
|
+
'instType': instType,
|
1144
|
+
'channel': hash,
|
1145
|
+
'instId': 'default',
|
1146
|
+
}
|
1147
|
+
subscribeHashes.append(hash + '::' + instType)
|
1148
|
+
args.append(arg)
|
1149
|
+
newPositions = await self.watch_private_multiple(messageHashes, subscribeHashes, args, params)
|
1150
|
+
if self.newUpdates:
|
1151
|
+
return newPositions
|
1152
|
+
return self.filter_by_symbols_since_limit(newPositions, symbols, since, limit, True)
|
1153
|
+
|
1154
|
+
def get_private_inst_type(self, market: Market):
|
1155
|
+
marketId = market['id']
|
1156
|
+
if marketId.find('_DMCBL') >= 0:
|
1157
|
+
return 'dmcbl'
|
1158
|
+
return 'umcbl'
|
1159
|
+
|
1160
|
+
def handle_positions(self, client: Client, message):
|
1161
|
+
#
|
1162
|
+
# {
|
1163
|
+
# action: 'snapshot',
|
1164
|
+
# arg: {instType: 'umcbl', channel: 'positions', instId: 'default'},
|
1165
|
+
# data: [
|
1166
|
+
# {
|
1167
|
+
# posId: '1221355728745619456',
|
1168
|
+
# instId: 'ETHUSDT_UMCBL',
|
1169
|
+
# instName: 'ETHUSDT',
|
1170
|
+
# marginCoin: 'USDT',
|
1171
|
+
# margin: '5.27182',
|
1172
|
+
# marginMode: 'crossed',
|
1173
|
+
# holdSide: 'long',
|
1174
|
+
# holdMode: 'single_hold',
|
1175
|
+
# total: '0.01',
|
1176
|
+
# available: '0.01',
|
1177
|
+
# locked: '0',
|
1178
|
+
# averageOpenPrice: '2635.91',
|
1179
|
+
# leverage: 5,
|
1180
|
+
# achievedProfits: '0',
|
1181
|
+
# upl: '-0.0267',
|
1182
|
+
# uplRate: '-0.005064664576',
|
1183
|
+
# liqPx: '-3110.66866033',
|
1184
|
+
# keepMarginRate: '0.0033',
|
1185
|
+
# marginRate: '0.002460827254',
|
1186
|
+
# cTime: '1726919818102',
|
1187
|
+
# uTime: '1728919604312',
|
1188
|
+
# markPrice: '2633.24',
|
1189
|
+
# autoMargin: 'off'
|
1190
|
+
# }
|
1191
|
+
# ],
|
1192
|
+
# ts: 1728919604329
|
1193
|
+
# }
|
1194
|
+
#
|
1195
|
+
if self.positions is None:
|
1196
|
+
self.positions = ArrayCacheBySymbolBySide()
|
1197
|
+
cache = self.positions
|
1198
|
+
rawPositions = self.safe_value(message, 'data', [])
|
1199
|
+
dataLength = len(rawPositions)
|
1200
|
+
if dataLength == 0:
|
1201
|
+
return
|
1202
|
+
newPositions = []
|
1203
|
+
symbols = []
|
1204
|
+
for i in range(0, len(rawPositions)):
|
1205
|
+
rawPosition = rawPositions[i]
|
1206
|
+
position = self.parse_ws_position(rawPosition)
|
1207
|
+
symbols.append(position['symbol'])
|
1208
|
+
newPositions.append(position)
|
1209
|
+
cache.append(position)
|
1210
|
+
hash = 'positions'
|
1211
|
+
messageHashes = self.find_message_hashes(client, hash)
|
1212
|
+
for i in range(0, len(messageHashes)):
|
1213
|
+
messageHash = messageHashes[i]
|
1214
|
+
parts = messageHash.split('::')
|
1215
|
+
symbol = parts[1]
|
1216
|
+
if self.in_array(symbol, symbols):
|
1217
|
+
positionsForSymbol = []
|
1218
|
+
for j in range(0, len(newPositions)):
|
1219
|
+
position = newPositions[j]
|
1220
|
+
if position['symbol'] == symbol:
|
1221
|
+
positionsForSymbol.append(position)
|
1222
|
+
client.resolve(positionsForSymbol, messageHash)
|
1223
|
+
client.resolve(newPositions, hash)
|
1224
|
+
|
1225
|
+
def parse_ws_position(self, position, market=None):
|
1226
|
+
#
|
1227
|
+
# {
|
1228
|
+
# posId: '1221355728745619456',
|
1229
|
+
# instId: 'ETHUSDT_UMCBL',
|
1230
|
+
# instName: 'ETHUSDT',
|
1231
|
+
# marginCoin: 'USDT',
|
1232
|
+
# margin: '5.27182',
|
1233
|
+
# marginMode: 'crossed',
|
1234
|
+
# holdSide: 'long',
|
1235
|
+
# holdMode: 'single_hold',
|
1236
|
+
# total: '0.01',
|
1237
|
+
# available: '0.01',
|
1238
|
+
# locked: '0',
|
1239
|
+
# averageOpenPrice: '2635.91',
|
1240
|
+
# leverage: 5,
|
1241
|
+
# achievedProfits: '0',
|
1242
|
+
# upl: '-0.0267',
|
1243
|
+
# uplRate: '-0.005064664576',
|
1244
|
+
# liqPx: '-3110.66866033',
|
1245
|
+
# keepMarginRate: '0.0033',
|
1246
|
+
# marginRate: '0.002460827254',
|
1247
|
+
# cTime: '1726919818102',
|
1248
|
+
# uTime: '1728919604312',
|
1249
|
+
# markPrice: '2633.24',
|
1250
|
+
# autoMargin: 'off'
|
1251
|
+
# }
|
1252
|
+
#
|
1253
|
+
marketId = self.safe_string(position, 'symbol')
|
1254
|
+
settleId = self.safe_string(position, 'marginCoin')
|
1255
|
+
market = self.safeMarketCustom(marketId, market, settleId)
|
1256
|
+
timestamp = self.safe_integer(position, 'cTime')
|
1257
|
+
marginModeId = self.safe_string(position, 'marginMode')
|
1258
|
+
marginMode = self.get_supported_mapping(marginModeId, {
|
1259
|
+
'crossed': 'cross',
|
1260
|
+
'isolated': 'isolated',
|
1261
|
+
})
|
1262
|
+
isHedged: Bool = None
|
1263
|
+
holdMode = self.safe_string(position, 'holdMode')
|
1264
|
+
if holdMode == 'double_hold':
|
1265
|
+
isHedged = True
|
1266
|
+
elif holdMode == 'single_hold':
|
1267
|
+
isHedged = False
|
1268
|
+
percentageDecimal = self.safe_string(position, 'uplRate')
|
1269
|
+
percentage = Precise.string_mul(percentageDecimal, '100')
|
1270
|
+
margin = self.safe_number(position, 'margin')
|
1271
|
+
return self.safe_position({
|
1272
|
+
'symbol': market['symbol'],
|
1273
|
+
'id': None,
|
1274
|
+
'timestamp': timestamp,
|
1275
|
+
'datetime': self.iso8601(timestamp),
|
1276
|
+
'contracts': self.safe_number(position, 'total'),
|
1277
|
+
'contractSize': None,
|
1278
|
+
'side': self.safe_string_lower(position, 'holdSide'),
|
1279
|
+
'notional': margin, # todo check
|
1280
|
+
'leverage': self.safe_integer(position, 'leverage'),
|
1281
|
+
'unrealizedPnl': self.safe_number(position, 'upl'),
|
1282
|
+
'realizedPnl': self.safe_number(position, 'achievedProfits'),
|
1283
|
+
'collateral': None, # todo check
|
1284
|
+
'entryPrice': self.safe_number(position, 'averageOpenPrice'),
|
1285
|
+
'markPrice': self.safe_number(position, 'markPrice'),
|
1286
|
+
'liquidationPrice': self.safe_number(position, 'liqPx'),
|
1287
|
+
'marginMode': marginMode,
|
1288
|
+
'hedged': isHedged,
|
1289
|
+
'maintenanceMargin': None, # todo check
|
1290
|
+
'maintenanceMarginPercentage': self.safe_number(position, 'keepMarginRate'),
|
1291
|
+
'initialMargin': margin, # todo check
|
1292
|
+
'initialMarginPercentage': None,
|
1293
|
+
'marginRatio': self.safe_number(position, 'marginRate'),
|
1294
|
+
'lastUpdateTimestamp': self.safe_integer(position, 'uTime'),
|
1295
|
+
'lastPrice': None,
|
1296
|
+
'stopLossPrice': None,
|
1297
|
+
'takeProfitPrice': None,
|
1298
|
+
'percentage': percentage,
|
1299
|
+
'info': position,
|
1300
|
+
})
|
1301
|
+
|
1302
|
+
def handle_error_message(self, client: Client, message):
|
1303
|
+
#
|
1304
|
+
# {event: "error", code: 30001, msg: "Channel does not exist"}
|
1305
|
+
#
|
1306
|
+
event = self.safe_string(message, 'event')
|
1307
|
+
try:
|
1308
|
+
if event == 'error':
|
1309
|
+
code = self.safe_string(message, 'code')
|
1310
|
+
feedback = self.id + ' ' + self.json(message)
|
1311
|
+
self.throw_exactly_matched_exception(self.exceptions['ws']['exact'], code, feedback)
|
1312
|
+
msg = self.safe_string(message, 'msg', '')
|
1313
|
+
self.throw_broadly_matched_exception(self.exceptions['ws']['broad'], msg, feedback)
|
1314
|
+
raise ExchangeError(feedback)
|
1315
|
+
return False
|
1316
|
+
except Exception as e:
|
1317
|
+
if isinstance(e, AuthenticationError):
|
1318
|
+
messageHash = 'authenticated'
|
1319
|
+
client.reject(e, messageHash)
|
1320
|
+
if messageHash in client.subscriptions:
|
1321
|
+
del client.subscriptions[messageHash]
|
1322
|
+
else:
|
1323
|
+
client.reject(e)
|
1324
|
+
return True
|
1325
|
+
|
1326
|
+
def handle_message(self, client: Client, message):
|
1327
|
+
# todo handle with subscribe and unsubscribe
|
1328
|
+
if self.handle_error_message(client, message):
|
1329
|
+
return
|
1330
|
+
content = self.safe_string(message, 'message')
|
1331
|
+
if content == 'pong':
|
1332
|
+
self.handle_pong(client, message)
|
1333
|
+
return
|
1334
|
+
if message == 'pong':
|
1335
|
+
self.handle_pong(client, message)
|
1336
|
+
return
|
1337
|
+
event = self.safe_string(message, 'event')
|
1338
|
+
if event == 'login':
|
1339
|
+
self.handle_authenticate(client, message)
|
1340
|
+
return
|
1341
|
+
if event == 'subscribe':
|
1342
|
+
self.handle_subscription_status(client, message)
|
1343
|
+
return
|
1344
|
+
if event == 'unsubscribe':
|
1345
|
+
self.handle_un_subscription_status(client, message)
|
1346
|
+
return
|
1347
|
+
data = self.safe_dict(message, 'arg', {})
|
1348
|
+
channel = self.safe_string(data, 'channel')
|
1349
|
+
if channel == 'ticker':
|
1350
|
+
self.handle_ticker(client, message)
|
1351
|
+
if channel.find('candle') >= 0:
|
1352
|
+
self.handle_ohlcv(client, message)
|
1353
|
+
if channel.find('books') >= 0:
|
1354
|
+
self.handle_order_book(client, message)
|
1355
|
+
if channel == 'trade':
|
1356
|
+
self.handle_trades(client, message)
|
1357
|
+
if channel == 'account':
|
1358
|
+
self.handle_balance(client, message)
|
1359
|
+
if (channel == 'orders') or (channel == 'ordersAlgo'):
|
1360
|
+
self.handle_order(client, message)
|
1361
|
+
if channel == 'positions':
|
1362
|
+
self.handle_positions(client, message)
|
1363
|
+
|
1364
|
+
def ping(self, client: Client):
|
1365
|
+
return 'ping'
|
1366
|
+
|
1367
|
+
def handle_pong(self, client: Client, message):
|
1368
|
+
client.lastPong = self.milliseconds()
|
1369
|
+
return message
|
1370
|
+
|
1371
|
+
def handle_subscription_status(self, client: Client, message):
|
1372
|
+
return message
|
1373
|
+
|
1374
|
+
def handle_un_subscription_status(self, client: Client, message):
|
1375
|
+
argsList = self.safe_list(message, 'args')
|
1376
|
+
if argsList is None:
|
1377
|
+
argsList = [self.safe_dict(message, 'arg', {})]
|
1378
|
+
for i in range(0, len(argsList)):
|
1379
|
+
arg = argsList[i]
|
1380
|
+
channel = self.safe_string(arg, 'channel')
|
1381
|
+
if channel == 'books':
|
1382
|
+
self.handle_order_book_un_subscription(client, message)
|
1383
|
+
elif channel == 'trade':
|
1384
|
+
self.handle_trades_un_subscription(client, message)
|
1385
|
+
elif channel == 'ticker':
|
1386
|
+
self.handle_ticker_un_subscription(client, message)
|
1387
|
+
elif channel.startswith('candle'):
|
1388
|
+
self.handle_ohlcv_un_subscription(client, message)
|
1389
|
+
return message
|
1390
|
+
|
1391
|
+
def handle_order_book_un_subscription(self, client: Client, message):
|
1392
|
+
arg = self.safe_dict(message, 'arg', {})
|
1393
|
+
instType = self.safe_string_lower(arg, 'instType')
|
1394
|
+
type = 'spot' if (instType == 'sp') else 'swap'
|
1395
|
+
instId = self.safe_string(arg, 'instId')
|
1396
|
+
market = self.safe_market(instId, None, None, type)
|
1397
|
+
symbol = market['symbol']
|
1398
|
+
messageHash = 'unsubscribe:orderbook:' + market['symbol']
|
1399
|
+
subMessageHash = 'orderbook:' + symbol
|
1400
|
+
if symbol in self.orderbooks:
|
1401
|
+
del self.orderbooks[symbol]
|
1402
|
+
if subMessageHash in client.subscriptions:
|
1403
|
+
del client.subscriptions[subMessageHash]
|
1404
|
+
if messageHash in client.subscriptions:
|
1405
|
+
del client.subscriptions[messageHash]
|
1406
|
+
error = UnsubscribeError(self.id + 'orderbook ' + symbol)
|
1407
|
+
client.reject(error, subMessageHash)
|
1408
|
+
client.resolve(True, messageHash)
|
1409
|
+
|
1410
|
+
def handle_trades_un_subscription(self, client: Client, message):
|
1411
|
+
arg = self.safe_dict(message, 'arg', {})
|
1412
|
+
instType = self.safe_string_lower(arg, 'instType')
|
1413
|
+
type = 'spot' if (instType == 'sp') else 'swap'
|
1414
|
+
instId = self.safe_string(arg, 'instId')
|
1415
|
+
market = self.safe_market(instId, None, None, type)
|
1416
|
+
symbol = market['symbol']
|
1417
|
+
messageHash = 'unsubscribe:trade:' + market['symbol']
|
1418
|
+
subMessageHash = 'trade:' + symbol
|
1419
|
+
if symbol in self.trades:
|
1420
|
+
del self.trades[symbol]
|
1421
|
+
if subMessageHash in client.subscriptions:
|
1422
|
+
del client.subscriptions[subMessageHash]
|
1423
|
+
if messageHash in client.subscriptions:
|
1424
|
+
del client.subscriptions[messageHash]
|
1425
|
+
error = UnsubscribeError(self.id + 'trades ' + symbol)
|
1426
|
+
client.reject(error, subMessageHash)
|
1427
|
+
client.resolve(True, messageHash)
|
1428
|
+
|
1429
|
+
def handle_ticker_un_subscription(self, client: Client, message):
|
1430
|
+
arg = self.safe_dict(message, 'arg', {})
|
1431
|
+
instType = self.safe_string_lower(arg, 'instType')
|
1432
|
+
type = 'spot' if (instType == 'sp') else 'swap'
|
1433
|
+
instId = self.safe_string(arg, 'instId')
|
1434
|
+
market = self.safe_market(instId, None, None, type)
|
1435
|
+
symbol = market['symbol']
|
1436
|
+
messageHash = 'unsubscribe:ticker:' + market['symbol']
|
1437
|
+
subMessageHash = 'ticker:' + symbol
|
1438
|
+
if symbol in self.tickers:
|
1439
|
+
del self.tickers[symbol]
|
1440
|
+
if subMessageHash in client.subscriptions:
|
1441
|
+
del client.subscriptions[subMessageHash]
|
1442
|
+
if messageHash in client.subscriptions:
|
1443
|
+
del client.subscriptions[messageHash]
|
1444
|
+
error = UnsubscribeError(self.id + 'ticker ' + symbol)
|
1445
|
+
client.reject(error, subMessageHash)
|
1446
|
+
client.resolve(True, messageHash)
|
1447
|
+
|
1448
|
+
def handle_ohlcv_un_subscription(self, client: Client, message):
|
1449
|
+
arg = self.safe_dict(message, 'arg', {})
|
1450
|
+
instType = self.safe_string_lower(arg, 'instType')
|
1451
|
+
type = 'spot' if (instType == 'sp') else 'swap'
|
1452
|
+
instId = self.safe_string(arg, 'instId')
|
1453
|
+
channel = self.safe_string(arg, 'channel')
|
1454
|
+
interval = channel.replace('candle', '')
|
1455
|
+
timeframes = self.safe_dict(self.options, 'timeframesForWs')
|
1456
|
+
timeframe = self.find_timeframe(interval, timeframes)
|
1457
|
+
market = self.safe_market(instId, None, None, type)
|
1458
|
+
symbol = market['symbol']
|
1459
|
+
messageHash = 'unsubscribe:ohlcv:' + timeframe + ':' + market['symbol']
|
1460
|
+
subMessageHash = 'ohlcv:' + symbol + ':' + timeframe
|
1461
|
+
if symbol in self.ohlcvs:
|
1462
|
+
if timeframe in self.ohlcvs[symbol]:
|
1463
|
+
del self.ohlcvs[symbol][timeframe]
|
1464
|
+
self.clean_unsubscription(client, subMessageHash, messageHash)
|