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/async_support/cex.py
CHANGED
@@ -5,21 +5,17 @@
|
|
5
5
|
|
6
6
|
from ccxt.async_support.base.exchange import Exchange
|
7
7
|
from ccxt.abstract.cex import ImplicitAPI
|
8
|
+
import asyncio
|
8
9
|
import hashlib
|
9
|
-
import
|
10
|
-
from ccxt.base.types import Balances, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade
|
10
|
+
from ccxt.base.types import Account, Balances, Currencies, Currency, DepositAddress, Int, LedgerEntry, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, TradingFees, Transaction, TransferEntry
|
11
11
|
from typing import List
|
12
12
|
from ccxt.base.errors import ExchangeError
|
13
|
+
from ccxt.base.errors import AuthenticationError
|
14
|
+
from ccxt.base.errors import PermissionDenied
|
13
15
|
from ccxt.base.errors import ArgumentsRequired
|
14
|
-
from ccxt.base.errors import
|
15
|
-
from ccxt.base.errors import NullResponse
|
16
|
+
from ccxt.base.errors import BadRequest
|
16
17
|
from ccxt.base.errors import InsufficientFunds
|
17
|
-
from ccxt.base.errors import
|
18
|
-
from ccxt.base.errors import OrderNotFound
|
19
|
-
from ccxt.base.errors import DDoSProtection
|
20
|
-
from ccxt.base.errors import RateLimitExceeded
|
21
|
-
from ccxt.base.errors import InvalidNonce
|
22
|
-
from ccxt.base.errors import AuthenticationError
|
18
|
+
from ccxt.base.errors import NullResponse
|
23
19
|
from ccxt.base.decimal_to_precision import TICK_SIZE
|
24
20
|
from ccxt.base.precise import Precise
|
25
21
|
|
@@ -31,1569 +27,1653 @@ class cex(Exchange, ImplicitAPI):
|
|
31
27
|
'id': 'cex',
|
32
28
|
'name': 'CEX.IO',
|
33
29
|
'countries': ['GB', 'EU', 'CY', 'RU'],
|
34
|
-
'rateLimit':
|
30
|
+
'rateLimit': 300, # 200 req/min
|
35
31
|
'pro': True,
|
36
32
|
'has': {
|
37
33
|
'CORS': None,
|
38
34
|
'spot': True,
|
39
|
-
'margin': False, # has but not through api
|
35
|
+
'margin': False, # has, but not through api
|
40
36
|
'swap': False,
|
41
37
|
'future': False,
|
42
38
|
'option': False,
|
43
|
-
'addMargin': False,
|
44
39
|
'cancelAllOrders': True,
|
45
40
|
'cancelOrder': True,
|
46
|
-
'cancelOrders': False,
|
47
|
-
'createDepositAddress': False,
|
48
|
-
'createMarketBuyOrderWithCost': True,
|
49
|
-
'createMarketOrderWithCost': False,
|
50
|
-
'createMarketSellOrderWithCost': False,
|
51
41
|
'createOrder': True,
|
52
|
-
'
|
53
|
-
'
|
54
|
-
'
|
55
|
-
'editOrder': True,
|
42
|
+
'createStopOrder': True,
|
43
|
+
'createTriggerOrder': True,
|
44
|
+
'fetchAccounts': True,
|
56
45
|
'fetchBalance': True,
|
46
|
+
'fetchClosedOrder': True,
|
57
47
|
'fetchClosedOrders': True,
|
58
48
|
'fetchCurrencies': True,
|
59
|
-
'fetchDeposit': False,
|
60
49
|
'fetchDepositAddress': True,
|
61
|
-
'
|
62
|
-
'fetchDeposits': False,
|
63
|
-
'fetchDepositsWithdrawals': False,
|
50
|
+
'fetchDepositsWithdrawals': True,
|
64
51
|
'fetchFundingHistory': False,
|
65
52
|
'fetchFundingRate': False,
|
66
53
|
'fetchFundingRateHistory': False,
|
67
54
|
'fetchFundingRates': False,
|
68
|
-
'
|
69
|
-
'fetchMarginMode': False,
|
55
|
+
'fetchLedger': True,
|
70
56
|
'fetchMarkets': True,
|
71
|
-
'fetchMarkOHLCV': False,
|
72
57
|
'fetchOHLCV': True,
|
73
|
-
'
|
58
|
+
'fetchOpenOrder': True,
|
74
59
|
'fetchOpenOrders': True,
|
75
|
-
'fetchOrder': True,
|
76
60
|
'fetchOrderBook': True,
|
77
|
-
'fetchOrders': True,
|
78
|
-
'fetchPositionMode': False,
|
79
|
-
'fetchPremiumIndexOHLCV': False,
|
80
61
|
'fetchTicker': True,
|
81
62
|
'fetchTickers': True,
|
63
|
+
'fetchTime': True,
|
82
64
|
'fetchTrades': True,
|
83
|
-
'fetchTradingFee': False,
|
84
65
|
'fetchTradingFees': True,
|
85
|
-
'
|
86
|
-
'fetchTransfer': False,
|
87
|
-
'fetchTransfers': False,
|
88
|
-
'fetchWithdrawal': False,
|
89
|
-
'fetchWithdrawals': False,
|
90
|
-
'fetchWithdrawalWhitelist': False,
|
91
|
-
'reduceMargin': False,
|
92
|
-
'setLeverage': False,
|
93
|
-
'setMargin': False,
|
94
|
-
'setMarginMode': False,
|
95
|
-
'transfer': False,
|
96
|
-
'withdraw': False,
|
97
|
-
},
|
98
|
-
'timeframes': {
|
99
|
-
'1m': '1m',
|
100
|
-
'1h': '1h',
|
101
|
-
'1d': '1d',
|
66
|
+
'transfer': True,
|
102
67
|
},
|
103
68
|
'urls': {
|
104
69
|
'logo': 'https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg',
|
105
70
|
'api': {
|
106
|
-
'
|
71
|
+
'public': 'https://trade.cex.io/api/spot/rest-public',
|
72
|
+
'private': 'https://trade.cex.io/api/spot/rest',
|
107
73
|
},
|
108
74
|
'www': 'https://cex.io',
|
109
|
-
'doc': 'https://cex.io/
|
75
|
+
'doc': 'https://trade.cex.io/docs/',
|
110
76
|
'fees': [
|
111
77
|
'https://cex.io/fee-schedule',
|
112
78
|
'https://cex.io/limits-commissions',
|
113
79
|
],
|
114
80
|
'referral': 'https://cex.io/r/0/up105393824/0/',
|
115
81
|
},
|
116
|
-
'requiredCredentials': {
|
117
|
-
'apiKey': True,
|
118
|
-
'secret': True,
|
119
|
-
'uid': True,
|
120
|
-
},
|
121
82
|
'api': {
|
122
83
|
'public': {
|
123
|
-
'get':
|
124
|
-
|
125
|
-
'
|
126
|
-
'
|
127
|
-
'
|
128
|
-
'
|
129
|
-
'
|
130
|
-
'
|
131
|
-
'
|
132
|
-
'
|
133
|
-
|
134
|
-
'post': [
|
135
|
-
'convert/{pair}',
|
136
|
-
'price_stats/{pair}',
|
137
|
-
],
|
84
|
+
'get': {},
|
85
|
+
'post': {
|
86
|
+
'get_server_time': 1,
|
87
|
+
'get_pairs_info': 1,
|
88
|
+
'get_currencies_info': 1,
|
89
|
+
'get_processing_info': 10,
|
90
|
+
'get_ticker': 1,
|
91
|
+
'get_trade_history': 1,
|
92
|
+
'get_order_book': 1,
|
93
|
+
'get_candles': 1,
|
94
|
+
},
|
138
95
|
},
|
139
96
|
'private': {
|
140
|
-
'
|
141
|
-
|
142
|
-
'
|
143
|
-
'
|
144
|
-
'
|
145
|
-
'
|
146
|
-
'
|
147
|
-
'
|
148
|
-
'
|
149
|
-
'
|
150
|
-
'
|
151
|
-
'
|
152
|
-
'
|
153
|
-
'
|
154
|
-
'
|
155
|
-
'
|
156
|
-
'
|
157
|
-
'
|
158
|
-
'
|
159
|
-
|
97
|
+
'get': {},
|
98
|
+
'post': {
|
99
|
+
'get_my_current_fee': 5,
|
100
|
+
'get_fee_strategy': 1,
|
101
|
+
'get_my_volume': 5,
|
102
|
+
'do_create_account': 1,
|
103
|
+
'get_my_account_status_v3': 5,
|
104
|
+
'get_my_wallet_balance': 5,
|
105
|
+
'get_my_orders': 5,
|
106
|
+
'do_my_new_order': 1,
|
107
|
+
'do_cancel_my_order': 1,
|
108
|
+
'do_cancel_all_orders': 5,
|
109
|
+
'get_order_book': 1,
|
110
|
+
'get_candles': 1,
|
111
|
+
'get_trade_history': 1,
|
112
|
+
'get_my_transaction_history': 1,
|
113
|
+
'get_my_funding_history': 5,
|
114
|
+
'do_my_internal_transfer': 1,
|
115
|
+
'get_processing_info': 10,
|
116
|
+
'get_deposit_address': 5,
|
117
|
+
'do_deposit_funds_from_wallet': 1,
|
118
|
+
'do_withdrawal_funds_to_wallet': 1,
|
119
|
+
},
|
160
120
|
},
|
161
121
|
},
|
162
|
-
'
|
163
|
-
'
|
164
|
-
'
|
165
|
-
'
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
'
|
180
|
-
'
|
181
|
-
'
|
122
|
+
'features': {
|
123
|
+
'spot': {
|
124
|
+
'sandbox': False,
|
125
|
+
'createOrder': {
|
126
|
+
'marginMode': False,
|
127
|
+
'triggerPrice': True,
|
128
|
+
'triggerPriceType': None,
|
129
|
+
'triggerDirection': False,
|
130
|
+
'stopLossPrice': False, # todo
|
131
|
+
'takeProfitPrice': False, # todo
|
132
|
+
'attachedStopLossTakeProfit': None,
|
133
|
+
'timeInForce': {
|
134
|
+
'IOC': True,
|
135
|
+
'FOK': True,
|
136
|
+
'PO': False, # todo check
|
137
|
+
'GTD': True,
|
138
|
+
},
|
139
|
+
'hedged': False,
|
140
|
+
'leverage': False,
|
141
|
+
'marketBuyRequiresPrice': False,
|
142
|
+
'marketBuyByCost': True, # todo check
|
143
|
+
'selfTradePrevention': False,
|
144
|
+
'trailing': False,
|
145
|
+
'iceberg': False,
|
146
|
+
},
|
147
|
+
'createOrders': None,
|
148
|
+
'fetchMyTrades': None,
|
149
|
+
'fetchOrder': None,
|
150
|
+
'fetchOpenOrders': {
|
151
|
+
'marginMode': False,
|
152
|
+
'limit': 1000,
|
153
|
+
'trigger': False,
|
154
|
+
'trailing': False,
|
182
155
|
},
|
156
|
+
'fetchOrders': None,
|
157
|
+
'fetchClosedOrders': {
|
158
|
+
'marginMode': False,
|
159
|
+
'limit': 1000,
|
160
|
+
'daysBack': 100000,
|
161
|
+
'daysBackCanceled': 1,
|
162
|
+
'untilDays': 100000,
|
163
|
+
'trigger': False,
|
164
|
+
'trailing': False,
|
165
|
+
},
|
166
|
+
'fetchOHLCV': {
|
167
|
+
'limit': 1000,
|
168
|
+
},
|
169
|
+
},
|
170
|
+
'swap': {
|
171
|
+
'linear': None,
|
172
|
+
'inverse': None,
|
173
|
+
},
|
174
|
+
'future': {
|
175
|
+
'linear': None,
|
176
|
+
'inverse': None,
|
183
177
|
},
|
184
178
|
},
|
185
179
|
'precisionMode': TICK_SIZE,
|
186
180
|
'exceptions': {
|
187
181
|
'exact': {},
|
188
182
|
'broad': {
|
183
|
+
'You have negative balance on following accounts': InsufficientFunds,
|
184
|
+
'Mandatory parameter side should be one of BUY,SELL': BadRequest,
|
185
|
+
'API orders from Main account are not allowed': BadRequest,
|
186
|
+
'check failed': BadRequest,
|
189
187
|
'Insufficient funds': InsufficientFunds,
|
190
|
-
'
|
191
|
-
'
|
192
|
-
'
|
193
|
-
'limit exceeded': RateLimitExceeded, # {"error":"rate limit exceeded"}
|
194
|
-
'Invalid API key': AuthenticationError,
|
195
|
-
'There was an error while placing your order': InvalidOrder,
|
196
|
-
'Sorry, too many clients already': DDoSProtection,
|
197
|
-
'Invalid Symbols Pair': BadSymbol,
|
198
|
-
'Wrong currency pair': BadSymbol, # {"error":"There was an error while placing your order: Wrong currency pair.","safe":true}
|
188
|
+
'Get deposit address for main account is not allowed': PermissionDenied,
|
189
|
+
'Market Trigger orders are not allowed': BadRequest, # for some reason, triggerPrice does not work for market orders
|
190
|
+
'key not passed or incorrect': AuthenticationError,
|
199
191
|
},
|
200
192
|
},
|
193
|
+
'timeframes': {
|
194
|
+
'1m': '1m',
|
195
|
+
'5m': '5m',
|
196
|
+
'15m': '15m',
|
197
|
+
'30m': '30m',
|
198
|
+
'1h': '1h',
|
199
|
+
'2h': '2h',
|
200
|
+
'4h': '4h',
|
201
|
+
'1d': '1d',
|
202
|
+
},
|
201
203
|
'options': {
|
202
|
-
'fetchOHLCVWarning': True,
|
203
|
-
'createMarketBuyOrderRequiresPrice': True,
|
204
|
-
'order': {
|
205
|
-
'status': {
|
206
|
-
'c': 'canceled',
|
207
|
-
'd': 'closed',
|
208
|
-
'cd': 'canceled',
|
209
|
-
'a': 'open',
|
210
|
-
},
|
211
|
-
},
|
212
|
-
'defaultNetwork': 'ERC20',
|
213
|
-
'defaultNetworks': {
|
214
|
-
'USDT': 'TRC20',
|
215
|
-
},
|
216
204
|
'networks': {
|
217
|
-
'
|
218
|
-
'
|
219
|
-
'
|
220
|
-
'
|
205
|
+
'BTC': 'bitcoin',
|
206
|
+
'ERC20': 'ERC20',
|
207
|
+
'BSC20': 'binancesmartchain',
|
208
|
+
'DOGE': 'dogecoin',
|
209
|
+
'ALGO': 'algorand',
|
210
|
+
'XLM': 'stellar',
|
211
|
+
'ATOM': 'cosmos',
|
212
|
+
'LTC': 'litecoin',
|
213
|
+
'XRP': 'ripple',
|
214
|
+
'FTM': 'fantom',
|
215
|
+
'MINA': 'mina',
|
216
|
+
'THETA': 'theta',
|
217
|
+
'XTZ': 'tezos',
|
218
|
+
'TIA': 'celestia',
|
219
|
+
'CRONOS': 'cronos', # CRC20
|
220
|
+
'MATIC': 'polygon',
|
221
|
+
'TON': 'ton',
|
222
|
+
'TRC20': 'tron',
|
223
|
+
'SOLANA': 'solana',
|
224
|
+
'SGB': 'songbird',
|
225
|
+
'DYDX': 'dydx',
|
226
|
+
'DASH': 'dash',
|
227
|
+
'ZIL': 'zilliqa',
|
228
|
+
'EOS': 'eos',
|
229
|
+
'AVALANCHEC': 'avalanche',
|
230
|
+
'ETHPOW': 'ethereumpow',
|
231
|
+
'NEAR': 'near',
|
232
|
+
'ARB': 'arbitrum',
|
233
|
+
'DOT': 'polkadot',
|
234
|
+
'OPT': 'optimism',
|
235
|
+
'INJ': 'injective',
|
236
|
+
'ADA': 'cardano',
|
237
|
+
'ONT': 'ontology',
|
238
|
+
'ICP': 'icp',
|
239
|
+
'KAVA': 'kava',
|
240
|
+
'KSM': 'kusama',
|
241
|
+
'SEI': 'sei',
|
242
|
+
# 'OSM': 'osmosis',
|
243
|
+
'NEO': 'neo',
|
244
|
+
'NEO3': 'neo3',
|
245
|
+
# 'TERRAOLD': 'terra', # tbd
|
246
|
+
# 'TERRA': 'terra2', # tbd
|
247
|
+
# 'EVER': 'everscale', # tbd
|
248
|
+
'XDC': 'xdc',
|
221
249
|
},
|
222
250
|
},
|
223
251
|
})
|
224
252
|
|
225
|
-
async def
|
226
|
-
# self method is now redundant
|
227
|
-
# currencies are now fetched before markets
|
228
|
-
options = self.safe_value(self.options, 'fetchCurrencies', {})
|
229
|
-
timestamp = self.safe_integer(options, 'timestamp')
|
230
|
-
expires = self.safe_integer(options, 'expires', 1000)
|
231
|
-
now = self.milliseconds()
|
232
|
-
if (timestamp is None) or ((now - timestamp) > expires):
|
233
|
-
response = await self.publicGetCurrencyProfile(params)
|
234
|
-
self.options['fetchCurrencies'] = self.extend(options, {
|
235
|
-
'response': response,
|
236
|
-
'timestamp': now,
|
237
|
-
})
|
238
|
-
return self.safe_value(self.options['fetchCurrencies'], 'response')
|
239
|
-
|
240
|
-
async def fetch_currencies(self, params={}):
|
253
|
+
async def fetch_currencies(self, params={}) -> Currencies:
|
241
254
|
"""
|
242
255
|
fetches all available currencies on an exchange
|
256
|
+
|
257
|
+
https://trade.cex.io/docs/#rest-public-api-calls-currencies-info
|
258
|
+
|
243
259
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
244
260
|
:returns dict: an associative dictionary of currencies
|
245
261
|
"""
|
246
|
-
|
247
|
-
self.
|
248
|
-
'timestamp': self.milliseconds(),
|
249
|
-
'response': response,
|
250
|
-
}
|
262
|
+
promises = []
|
263
|
+
promises.append(self.publicPostGetCurrenciesInfo(params))
|
251
264
|
#
|
252
|
-
#
|
253
|
-
#
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
257
|
-
#
|
258
|
-
#
|
259
|
-
#
|
260
|
-
#
|
261
|
-
#
|
262
|
-
#
|
263
|
-
#
|
264
|
-
#
|
265
|
-
|
266
|
-
#
|
267
|
-
#
|
268
|
-
#
|
269
|
-
#
|
270
|
-
#
|
271
|
-
#
|
272
|
-
#
|
273
|
-
#
|
274
|
-
#
|
275
|
-
#
|
276
|
-
#
|
277
|
-
#
|
278
|
-
#
|
279
|
-
#
|
280
|
-
#
|
281
|
-
#
|
282
|
-
#
|
283
|
-
#
|
284
|
-
#
|
285
|
-
#
|
286
|
-
# "scale":2,
|
287
|
-
# "minimumCurrencyAmount":"0.00000100",
|
288
|
-
# "minimalWithdrawalAmount":0.01
|
289
|
-
# }
|
290
|
-
# ],
|
291
|
-
# "pairs":[
|
292
|
-
# {
|
293
|
-
# "symbol1":"BTC",
|
294
|
-
# "symbol2":"USD",
|
295
|
-
# "pricePrecision":1,
|
296
|
-
# "priceScale":"/1000000",
|
297
|
-
# "minLotSize":0.002,
|
298
|
-
# "minLotSizeS2":20
|
299
|
-
# },
|
300
|
-
# {
|
301
|
-
# "symbol1":"ETH",
|
302
|
-
# "symbol2":"USD",
|
303
|
-
# "pricePrecision":2,
|
304
|
-
# "priceScale":"/10000",
|
305
|
-
# "minLotSize":0.1,
|
306
|
-
# "minLotSizeS2":20
|
307
|
-
# }
|
308
|
-
# ]
|
309
|
-
# }
|
310
|
-
# }
|
265
|
+
# {
|
266
|
+
# "ok": "ok",
|
267
|
+
# "data": [
|
268
|
+
# {
|
269
|
+
# "currency": "ZAP",
|
270
|
+
# "fiat": False,
|
271
|
+
# "precision": "8",
|
272
|
+
# "walletPrecision": "6",
|
273
|
+
# "walletDeposit": True,
|
274
|
+
# "walletWithdrawal": True
|
275
|
+
# },
|
276
|
+
# ...
|
277
|
+
#
|
278
|
+
promises.append(self.publicPostGetProcessingInfo(params))
|
279
|
+
#
|
280
|
+
# {
|
281
|
+
# "ok": "ok",
|
282
|
+
# "data": {
|
283
|
+
# "ADA": {
|
284
|
+
# "name": "Cardano",
|
285
|
+
# "blockchains": {
|
286
|
+
# "cardano": {
|
287
|
+
# "type": "coin",
|
288
|
+
# "deposit": "enabled",
|
289
|
+
# "minDeposit": "1",
|
290
|
+
# "withdrawal": "enabled",
|
291
|
+
# "minWithdrawal": "5",
|
292
|
+
# "withdrawalFee": "1",
|
293
|
+
# "withdrawalFeePercent": "0",
|
294
|
+
# "depositConfirmations": "15"
|
295
|
+
# }
|
296
|
+
# }
|
297
|
+
# },
|
298
|
+
# ...
|
311
299
|
#
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
300
|
+
responses = await asyncio.gather(*promises)
|
301
|
+
dataCurrencies = self.safe_list(responses[0], 'data', [])
|
302
|
+
dataNetworks = self.safe_dict(responses[1], 'data', {})
|
303
|
+
currenciesIndexed = self.index_by(dataCurrencies, 'currency')
|
304
|
+
data = self.deep_extend(currenciesIndexed, dataNetworks)
|
305
|
+
return self.parse_currencies(self.to_array(data))
|
306
|
+
|
307
|
+
def parse_currency(self, rawCurrency: dict) -> Currency:
|
308
|
+
id = self.safe_string(rawCurrency, 'currency')
|
309
|
+
code = self.safe_currency_code(id)
|
310
|
+
type = 'fiat' if self.safe_bool(rawCurrency, 'fiat') else 'crypto'
|
311
|
+
currencyDepositEnabled = self.safe_bool(rawCurrency, 'walletDeposit')
|
312
|
+
currencyWithdrawEnabled = self.safe_bool(rawCurrency, 'walletWithdrawal')
|
313
|
+
currencyPrecision = self.parse_number(self.parse_precision(self.safe_string(rawCurrency, 'precision')))
|
314
|
+
networks: dict = {}
|
315
|
+
rawNetworks = self.safe_dict(rawCurrency, 'blockchains', {})
|
316
|
+
keys = list(rawNetworks.keys())
|
317
|
+
for j in range(0, len(keys)):
|
318
|
+
networkId = keys[j]
|
319
|
+
rawNetwork = rawNetworks[networkId]
|
320
|
+
networkCode = self.network_id_to_code(networkId)
|
321
|
+
deposit = self.safe_string(rawNetwork, 'deposit') == 'enabled'
|
322
|
+
withdraw = self.safe_string(rawNetwork, 'withdrawal') == 'enabled'
|
323
|
+
networks[networkCode] = {
|
324
|
+
'id': networkId,
|
325
|
+
'network': networkCode,
|
326
|
+
'margin': None,
|
327
|
+
'deposit': deposit,
|
328
|
+
'withdraw': withdraw,
|
329
|
+
'fee': self.safe_number(rawNetwork, 'withdrawalFee'),
|
330
|
+
'precision': currencyPrecision,
|
329
331
|
'limits': {
|
330
|
-
'
|
331
|
-
'min': self.safe_number(
|
332
|
+
'deposit': {
|
333
|
+
'min': self.safe_number(rawNetwork, 'minDeposit'),
|
332
334
|
'max': None,
|
333
335
|
},
|
334
336
|
'withdraw': {
|
335
|
-
'min': self.safe_number(
|
337
|
+
'min': self.safe_number(rawNetwork, 'minWithdrawal'),
|
336
338
|
'max': None,
|
337
339
|
},
|
338
340
|
},
|
339
|
-
'info':
|
341
|
+
'info': rawNetwork,
|
340
342
|
}
|
341
|
-
return
|
343
|
+
return self.safe_currency_structure({
|
344
|
+
'id': id,
|
345
|
+
'code': code,
|
346
|
+
'name': None,
|
347
|
+
'type': type,
|
348
|
+
'active': None,
|
349
|
+
'deposit': currencyDepositEnabled,
|
350
|
+
'withdraw': currencyWithdrawEnabled,
|
351
|
+
'fee': None,
|
352
|
+
'precision': currencyPrecision,
|
353
|
+
'limits': {
|
354
|
+
'amount': {
|
355
|
+
'min': None,
|
356
|
+
'max': None,
|
357
|
+
},
|
358
|
+
'withdraw': {
|
359
|
+
'min': None,
|
360
|
+
'max': None,
|
361
|
+
},
|
362
|
+
},
|
363
|
+
'networks': networks,
|
364
|
+
'info': rawCurrency,
|
365
|
+
})
|
342
366
|
|
343
|
-
async def fetch_markets(self, params={}):
|
367
|
+
async def fetch_markets(self, params={}) -> List[Market]:
|
344
368
|
"""
|
345
|
-
retrieves data on all markets for
|
369
|
+
retrieves data on all markets for ace
|
370
|
+
|
371
|
+
https://trade.cex.io/docs/#rest-public-api-calls-pairs-info
|
372
|
+
|
346
373
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
347
374
|
:returns dict[]: an array of objects representing market data
|
348
375
|
"""
|
349
|
-
|
350
|
-
currenciesData = self.safe_value(currenciesResponse, 'data', {})
|
351
|
-
currencies = self.safe_value(currenciesData, 'symbols', [])
|
352
|
-
currenciesById = self.index_by(currencies, 'code')
|
353
|
-
pairs = self.safe_value(currenciesData, 'pairs', [])
|
354
|
-
response = await self.publicGetCurrencyLimits(params)
|
376
|
+
response = await self.publicPostGetPairsInfo(params)
|
355
377
|
#
|
356
|
-
#
|
357
|
-
#
|
358
|
-
#
|
359
|
-
#
|
360
|
-
#
|
361
|
-
#
|
362
|
-
#
|
363
|
-
#
|
364
|
-
#
|
365
|
-
#
|
366
|
-
#
|
367
|
-
#
|
368
|
-
#
|
369
|
-
#
|
370
|
-
#
|
371
|
-
#
|
372
|
-
#
|
373
|
-
#
|
374
|
-
#
|
375
|
-
# "maxLotSize":null,
|
376
|
-
# "minPrice":"25",
|
377
|
-
# "maxPrice":"8192"
|
378
|
-
# }
|
379
|
-
# ]
|
380
|
-
# }
|
381
|
-
# }
|
378
|
+
# {
|
379
|
+
# "ok": "ok",
|
380
|
+
# "data": [
|
381
|
+
# {
|
382
|
+
# "base": "AI",
|
383
|
+
# "quote": "USD",
|
384
|
+
# "baseMin": "30",
|
385
|
+
# "baseMax": "2516000",
|
386
|
+
# "baseLotSize": "0.000001",
|
387
|
+
# "quoteMin": "10",
|
388
|
+
# "quoteMax": "1000000",
|
389
|
+
# "quoteLotSize": "0.01000000",
|
390
|
+
# "basePrecision": "6",
|
391
|
+
# "quotePrecision": "8",
|
392
|
+
# "pricePrecision": "4",
|
393
|
+
# "minPrice": "0.0377",
|
394
|
+
# "maxPrice": "19.5000"
|
395
|
+
# },
|
396
|
+
# ...
|
382
397
|
#
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
'
|
418
|
-
|
419
|
-
|
420
|
-
'inverse': None,
|
421
|
-
'contractSize': None,
|
422
|
-
'expiry': None,
|
423
|
-
'expiryDatetime': None,
|
424
|
-
'strike': None,
|
425
|
-
'optionType': None,
|
426
|
-
'precision': {
|
427
|
-
'amount': self.parse_number(self.parse_precision(amountPrecisionString)),
|
428
|
-
'price': self.parse_number(self.parse_precision(pricePrecisionString)),
|
398
|
+
data = self.safe_list(response, 'data', [])
|
399
|
+
return self.parse_markets(data)
|
400
|
+
|
401
|
+
def parse_market(self, market: dict) -> Market:
|
402
|
+
baseId = self.safe_string(market, 'base')
|
403
|
+
base = self.safe_currency_code(baseId)
|
404
|
+
quoteId = self.safe_string(market, 'quote')
|
405
|
+
quote = self.safe_currency_code(quoteId)
|
406
|
+
id = base + '-' + quote # not actual id, but for self exchange we can use self abbreviation, because e.g. tickers have hyphen in between
|
407
|
+
symbol = base + '/' + quote
|
408
|
+
return self.safe_market_structure({
|
409
|
+
'id': id,
|
410
|
+
'symbol': symbol,
|
411
|
+
'base': base,
|
412
|
+
'baseId': baseId,
|
413
|
+
'quote': quote,
|
414
|
+
'quoteId': quoteId,
|
415
|
+
'settle': None,
|
416
|
+
'settleId': None,
|
417
|
+
'type': 'spot',
|
418
|
+
'spot': True,
|
419
|
+
'margin': False,
|
420
|
+
'swap': False,
|
421
|
+
'future': False,
|
422
|
+
'option': False,
|
423
|
+
'contract': False,
|
424
|
+
'linear': None,
|
425
|
+
'inverse': None,
|
426
|
+
'contractSize': None,
|
427
|
+
'expiry': None,
|
428
|
+
'expiryDatetime': None,
|
429
|
+
'strike': None,
|
430
|
+
'optionType': None,
|
431
|
+
'limits': {
|
432
|
+
'amount': {
|
433
|
+
'min': self.safe_number(market, 'baseMin'),
|
434
|
+
'max': self.safe_number(market, 'baseMax'),
|
429
435
|
},
|
430
|
-
'
|
431
|
-
'
|
432
|
-
|
433
|
-
'max': None,
|
434
|
-
},
|
435
|
-
'amount': {
|
436
|
-
'min': self.safe_number(market, 'minLotSize'),
|
437
|
-
'max': self.safe_number(market, 'maxLotSize'),
|
438
|
-
},
|
439
|
-
'price': {
|
440
|
-
'min': self.safe_number(market, 'minPrice'),
|
441
|
-
'max': self.safe_number(market, 'maxPrice'),
|
442
|
-
},
|
443
|
-
'cost': {
|
444
|
-
'min': self.safe_number(market, 'minLotSizeS2'),
|
445
|
-
'max': None,
|
446
|
-
},
|
436
|
+
'price': {
|
437
|
+
'min': self.safe_number(market, 'minPrice'),
|
438
|
+
'max': self.safe_number(market, 'maxPrice'),
|
447
439
|
},
|
448
|
-
'
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
440
|
+
'cost': {
|
441
|
+
'min': self.safe_number(market, 'quoteMin'),
|
442
|
+
'max': self.safe_number(market, 'quoteMax'),
|
443
|
+
},
|
444
|
+
'leverage': {
|
445
|
+
'min': None,
|
446
|
+
'max': None,
|
447
|
+
},
|
448
|
+
},
|
449
|
+
'precision': {
|
450
|
+
'amount': self.safe_string(market, 'baseLotSize'),
|
451
|
+
'price': self.parse_number(self.parse_precision(self.safe_string(market, 'pricePrecision'))),
|
452
|
+
# 'cost': self.parse_number(self.parse_precision(self.safe_string(market, 'quoteLotSize'))), # buggy, doesn't reflect their documentation
|
453
|
+
'base': self.parse_number(self.parse_precision(self.safe_string(market, 'basePrecision'))),
|
454
|
+
'quote': self.parse_number(self.parse_precision(self.safe_string(market, 'quotePrecision'))),
|
455
|
+
},
|
456
|
+
'active': None,
|
457
|
+
'created': None,
|
458
|
+
'info': market,
|
459
|
+
})
|
468
460
|
|
469
|
-
async def
|
461
|
+
async def fetch_time(self, params={}):
|
470
462
|
"""
|
471
|
-
|
472
|
-
query for balance and get the amount of funds available for trading or funds locked in orders
|
463
|
+
fetches the current integer timestamp in milliseconds from the exchange server
|
473
464
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
474
|
-
:returns
|
465
|
+
:returns int: the current integer timestamp in milliseconds from the exchange server
|
475
466
|
"""
|
476
|
-
await self.
|
477
|
-
|
478
|
-
|
467
|
+
response = await self.publicPostGetServerTime(params)
|
468
|
+
#
|
469
|
+
# {
|
470
|
+
# "ok": "ok",
|
471
|
+
# "data": {
|
472
|
+
# "timestamp": "1728472063472",
|
473
|
+
# "ISODate": "2024-10-09T11:07:43.472Z"
|
474
|
+
# }
|
475
|
+
# }
|
476
|
+
#
|
477
|
+
data = self.safe_dict(response, 'data')
|
478
|
+
timestamp = self.safe_integer(data, 'timestamp')
|
479
|
+
return timestamp
|
479
480
|
|
480
|
-
async def
|
481
|
+
async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
|
481
482
|
"""
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
483
|
+
fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
484
|
+
|
485
|
+
https://trade.cex.io/docs/#rest-public-api-calls-ticker
|
486
|
+
|
487
|
+
:param str symbol:
|
486
488
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
487
|
-
:returns dict:
|
489
|
+
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
488
490
|
"""
|
489
491
|
await self.load_markets()
|
490
|
-
|
491
|
-
|
492
|
-
'pair': market['id'],
|
493
|
-
}
|
494
|
-
if limit is not None:
|
495
|
-
request['depth'] = limit
|
496
|
-
response = await self.publicGetOrderBookPair(self.extend(request, params))
|
497
|
-
timestamp = self.safe_timestamp(response, 'timestamp')
|
498
|
-
return self.parse_order_book(response, market['symbol'], timestamp)
|
492
|
+
response = await self.fetch_tickers([symbol], params)
|
493
|
+
return self.safe_dict(response, symbol, {})
|
499
494
|
|
500
|
-
def
|
501
|
-
#
|
502
|
-
# [
|
503
|
-
# 1591403940,
|
504
|
-
# 0.024972,
|
505
|
-
# 0.024972,
|
506
|
-
# 0.024969,
|
507
|
-
# 0.024969,
|
508
|
-
# 0.49999900
|
509
|
-
# ]
|
510
|
-
#
|
511
|
-
return [
|
512
|
-
self.safe_timestamp(ohlcv, 0),
|
513
|
-
self.safe_number(ohlcv, 1),
|
514
|
-
self.safe_number(ohlcv, 2),
|
515
|
-
self.safe_number(ohlcv, 3),
|
516
|
-
self.safe_number(ohlcv, 4),
|
517
|
-
self.safe_number(ohlcv, 5),
|
518
|
-
]
|
519
|
-
|
520
|
-
async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
495
|
+
async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
521
496
|
"""
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
:param
|
527
|
-
:param int [limit]: the maximum amount of candles to fetch
|
497
|
+
fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
498
|
+
|
499
|
+
https://trade.cex.io/docs/#rest-public-api-calls-ticker
|
500
|
+
|
501
|
+
:param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
|
528
502
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
529
|
-
:returns
|
503
|
+
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
530
504
|
"""
|
531
505
|
await self.load_markets()
|
532
|
-
|
533
|
-
if
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
506
|
+
request = {}
|
507
|
+
if symbols is not None:
|
508
|
+
request['pairs'] = self.market_ids(symbols)
|
509
|
+
response = await self.publicPostGetTicker(self.extend(request, params))
|
510
|
+
#
|
511
|
+
# {
|
512
|
+
# "ok": "ok",
|
513
|
+
# "data": {
|
514
|
+
# "AI-USD": {
|
515
|
+
# "bestBid": "0.3917",
|
516
|
+
# "bestAsk": "0.3949",
|
517
|
+
# "bestBidChange": "0.0035",
|
518
|
+
# "bestBidChangePercentage": "0.90",
|
519
|
+
# "bestAskChange": "0.0038",
|
520
|
+
# "bestAskChangePercentage": "0.97",
|
521
|
+
# "low": "0.3787",
|
522
|
+
# "high": "0.3925",
|
523
|
+
# "volume30d": "2945.722277",
|
524
|
+
# "lastTradeDateISO": "2024-10-11T06:18:42.077Z",
|
525
|
+
# "volume": "120.736000",
|
526
|
+
# "quoteVolume": "46.65654070",
|
527
|
+
# "lastTradeVolume": "67.914000",
|
528
|
+
# "volumeUSD": "46.65",
|
529
|
+
# "last": "0.3949",
|
530
|
+
# "lastTradePrice": "0.3925",
|
531
|
+
# "priceChange": "0.0038",
|
532
|
+
# "priceChangePercentage": "0.97"
|
533
|
+
# },
|
534
|
+
# ...
|
535
|
+
#
|
536
|
+
data = self.safe_dict(response, 'data', {})
|
537
|
+
return self.parse_tickers(data, symbols)
|
558
538
|
|
559
|
-
def parse_ticker(self, ticker, market: Market = None) -> Ticker:
|
560
|
-
|
561
|
-
|
562
|
-
high = self.safe_string(ticker, 'high')
|
563
|
-
low = self.safe_string(ticker, 'low')
|
564
|
-
bid = self.safe_string(ticker, 'bid')
|
565
|
-
ask = self.safe_string(ticker, 'ask')
|
566
|
-
last = self.safe_string(ticker, 'last')
|
567
|
-
symbol = self.safe_symbol(None, market)
|
539
|
+
def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
540
|
+
marketId = self.safe_string(ticker, 'id')
|
541
|
+
symbol = self.safe_symbol(marketId, market)
|
568
542
|
return self.safe_ticker({
|
569
543
|
'symbol': symbol,
|
570
|
-
'timestamp':
|
571
|
-
'datetime':
|
572
|
-
'high': high,
|
573
|
-
'low': low,
|
574
|
-
'bid':
|
544
|
+
'timestamp': None,
|
545
|
+
'datetime': None,
|
546
|
+
'high': self.safe_number(ticker, 'high'),
|
547
|
+
'low': self.safe_number(ticker, 'low'),
|
548
|
+
'bid': self.safe_number(ticker, 'bestBid'),
|
575
549
|
'bidVolume': None,
|
576
|
-
'ask':
|
550
|
+
'ask': self.safe_number(ticker, 'bestAsk'),
|
577
551
|
'askVolume': None,
|
578
552
|
'vwap': None,
|
579
553
|
'open': None,
|
580
|
-
'close':
|
581
|
-
'last': last,
|
554
|
+
'close': self.safe_string(ticker, 'lastTradePrice'),
|
582
555
|
'previousClose': None,
|
583
|
-
'change':
|
584
|
-
'percentage':
|
556
|
+
'change': self.safe_number(ticker, 'priceChange'),
|
557
|
+
'percentage': self.safe_number(ticker, 'priceChangePercentage'),
|
585
558
|
'average': None,
|
586
|
-
'baseVolume': volume,
|
587
|
-
'quoteVolume':
|
559
|
+
'baseVolume': self.safe_string(ticker, 'volume'),
|
560
|
+
'quoteVolume': self.safe_string(ticker, 'quoteVolume'),
|
588
561
|
'info': ticker,
|
589
562
|
}, market)
|
590
563
|
|
591
|
-
async def
|
592
|
-
"""
|
593
|
-
fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
594
|
-
:param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
|
595
|
-
:param dict [params]: extra parameters specific to the exchange API endpoint
|
596
|
-
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
564
|
+
async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
597
565
|
"""
|
598
|
-
|
599
|
-
symbols = self.market_symbols(symbols)
|
600
|
-
currencies = list(self.currencies.keys())
|
601
|
-
request = {
|
602
|
-
'currencies': '/'.join(currencies),
|
603
|
-
}
|
604
|
-
response = await self.publicGetTickersCurrencies(self.extend(request, params))
|
605
|
-
tickers = self.safe_value(response, 'data', [])
|
606
|
-
result = {}
|
607
|
-
for t in range(0, len(tickers)):
|
608
|
-
ticker = tickers[t]
|
609
|
-
marketId = self.safe_string(ticker, 'pair')
|
610
|
-
market = self.safe_market(marketId, None, ':')
|
611
|
-
symbol = market['symbol']
|
612
|
-
result[symbol] = self.parse_ticker(ticker, market)
|
613
|
-
return self.filter_by_array_tickers(result, 'symbol', symbols)
|
566
|
+
get the list of most recent trades for a particular symbol
|
614
567
|
|
615
|
-
|
616
|
-
|
617
|
-
:
|
618
|
-
|
619
|
-
:param
|
568
|
+
https://trade.cex.io/docs/#rest-public-api-calls-trade-history
|
569
|
+
|
570
|
+
:param str symbol: unified symbol of the market to fetch trades for
|
571
|
+
:param int [since]: timestamp in ms of the earliest trade to fetch
|
572
|
+
:param int [limit]: the maximum amount of trades to fetch
|
620
573
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
621
|
-
:
|
574
|
+
:param int [params.until]: timestamp in ms of the latest entry
|
575
|
+
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
622
576
|
"""
|
623
577
|
await self.load_markets()
|
624
578
|
market = self.market(symbol)
|
625
|
-
request = {
|
579
|
+
request: dict = {
|
626
580
|
'pair': market['id'],
|
627
581
|
}
|
628
|
-
|
629
|
-
|
582
|
+
if since is not None:
|
583
|
+
request['fromDateISO'] = self.iso8601(since)
|
584
|
+
until = None
|
585
|
+
until, params = self.handle_param_integer_2(params, 'until', 'till')
|
586
|
+
if until is not None:
|
587
|
+
request['toDateISO'] = self.iso8601(until)
|
588
|
+
if limit is not None:
|
589
|
+
request['pageSize'] = min(limit, 10000) # has a bug, still returns more trades
|
590
|
+
response = await self.publicPostGetTradeHistory(self.extend(request, params))
|
591
|
+
#
|
592
|
+
# {
|
593
|
+
# "ok": "ok",
|
594
|
+
# "data": {
|
595
|
+
# "pageSize": "10",
|
596
|
+
# "trades": [
|
597
|
+
# {
|
598
|
+
# "tradeId": "1728630559823-0",
|
599
|
+
# "dateISO": "2024-10-11T07:09:19.823Z",
|
600
|
+
# "side": "SELL",
|
601
|
+
# "price": "60879.5",
|
602
|
+
# "amount": "0.00165962"
|
603
|
+
# },
|
604
|
+
# ... followed by older trades
|
605
|
+
#
|
606
|
+
data = self.safe_dict(response, 'data', {})
|
607
|
+
trades = self.safe_list(data, 'trades', [])
|
608
|
+
return self.parse_trades(trades, market, since, limit)
|
630
609
|
|
631
|
-
def parse_trade(self, trade, market: Market = None) -> Trade:
|
610
|
+
def parse_trade(self, trade: dict, market: Market = None) -> Trade:
|
632
611
|
#
|
633
|
-
# fetchTrades
|
612
|
+
# public fetchTrades
|
634
613
|
#
|
635
|
-
#
|
636
|
-
#
|
637
|
-
#
|
638
|
-
#
|
639
|
-
#
|
640
|
-
#
|
641
|
-
#
|
614
|
+
# {
|
615
|
+
# "tradeId": "1728630559823-0",
|
616
|
+
# "dateISO": "2024-10-11T07:09:19.823Z",
|
617
|
+
# "side": "SELL",
|
618
|
+
# "price": "60879.5",
|
619
|
+
# "amount": "0.00165962"
|
620
|
+
# },
|
642
621
|
#
|
643
|
-
|
644
|
-
|
645
|
-
type = None
|
646
|
-
side = self.safe_string(trade, 'type')
|
647
|
-
priceString = self.safe_string(trade, 'price')
|
648
|
-
amountString = self.safe_string(trade, 'amount')
|
622
|
+
dateStr = self.safe_string(trade, 'dateISO')
|
623
|
+
timestamp = self.parse8601(dateStr)
|
649
624
|
market = self.safe_market(None, market)
|
650
625
|
return self.safe_trade({
|
651
626
|
'info': trade,
|
652
|
-
'id': id,
|
653
627
|
'timestamp': timestamp,
|
654
628
|
'datetime': self.iso8601(timestamp),
|
655
629
|
'symbol': market['symbol'],
|
656
|
-
'
|
657
|
-
'side': side,
|
630
|
+
'id': self.safe_string(trade, 'tradeId'),
|
658
631
|
'order': None,
|
632
|
+
'type': None,
|
659
633
|
'takerOrMaker': None,
|
660
|
-
'
|
661
|
-
'
|
634
|
+
'side': self.safe_string_lower(trade, 'side'),
|
635
|
+
'price': self.safe_string(trade, 'price'),
|
636
|
+
'amount': self.safe_string(trade, 'amount'),
|
662
637
|
'cost': None,
|
663
638
|
'fee': None,
|
664
639
|
}, market)
|
665
640
|
|
666
|
-
async def
|
641
|
+
async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
667
642
|
"""
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
:param
|
643
|
+
fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
644
|
+
|
645
|
+
https://trade.cex.io/docs/#rest-public-api-calls-order-book
|
646
|
+
|
647
|
+
:param str symbol: unified symbol of the market to fetch the order book for
|
648
|
+
:param int [limit]: the maximum amount of order book entries to return
|
673
649
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
674
|
-
:returns
|
650
|
+
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
651
|
+
"""
|
652
|
+
await self.load_markets()
|
653
|
+
market = self.market(symbol)
|
654
|
+
request: dict = {
|
655
|
+
'pair': market['id'],
|
656
|
+
}
|
657
|
+
response = await self.publicPostGetOrderBook(self.extend(request, params))
|
658
|
+
#
|
659
|
+
# {
|
660
|
+
# "ok": "ok",
|
661
|
+
# "data": {
|
662
|
+
# "timestamp": "1728636922648",
|
663
|
+
# "currency1": "BTC",
|
664
|
+
# "currency2": "USDT",
|
665
|
+
# "bids": [
|
666
|
+
# [
|
667
|
+
# "60694.1",
|
668
|
+
# "13.12849761"
|
669
|
+
# ],
|
670
|
+
# [
|
671
|
+
# "60694.0",
|
672
|
+
# "0.71829244"
|
673
|
+
# ],
|
674
|
+
# ...
|
675
|
+
#
|
676
|
+
orderBook = self.safe_dict(response, 'data', {})
|
677
|
+
timestamp = self.safe_integer(orderBook, 'timestamp')
|
678
|
+
return self.parse_order_book(orderBook, market['symbol'], timestamp)
|
679
|
+
|
680
|
+
async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
675
681
|
"""
|
682
|
+
fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
683
|
+
|
684
|
+
https://trade.cex.io/docs/#rest-public-api-calls-candles
|
685
|
+
|
686
|
+
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
687
|
+
:param str timeframe: the length of time each candle represents
|
688
|
+
:param int [since]: timestamp in ms of the earliest candle to fetch
|
689
|
+
:param int [limit]: the maximum amount of candles to fetch
|
690
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
691
|
+
:param int [params.until]: timestamp in ms of the latest entry
|
692
|
+
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
693
|
+
"""
|
694
|
+
dataType = None
|
695
|
+
dataType, params = self.handle_option_and_params(params, 'fetchOHLCV', 'dataType')
|
696
|
+
if dataType is None:
|
697
|
+
raise ArgumentsRequired(self.id + ' fetchOHLCV requires a parameter "dataType" to be either "bestBid" or "bestAsk"')
|
676
698
|
await self.load_markets()
|
677
699
|
market = self.market(symbol)
|
678
|
-
request = {
|
700
|
+
request: dict = {
|
679
701
|
'pair': market['id'],
|
702
|
+
'resolution': self.timeframes[timeframe],
|
703
|
+
'dataType': dataType,
|
680
704
|
}
|
681
|
-
|
682
|
-
|
705
|
+
if since is not None:
|
706
|
+
request['fromISO'] = self.iso8601(since)
|
707
|
+
until = None
|
708
|
+
until, params = self.handle_param_integer_2(params, 'until', 'till')
|
709
|
+
if until is not None:
|
710
|
+
request['toISO'] = self.iso8601(until)
|
711
|
+
elif since is None:
|
712
|
+
# exchange still requires that we provide one of them
|
713
|
+
request['toISO'] = self.iso8601(self.milliseconds())
|
714
|
+
if since is not None and until is not None and limit is not None:
|
715
|
+
raise ArgumentsRequired(self.id + ' fetchOHLCV does not support fetching candles with both a limit and since/until')
|
716
|
+
elif (since is not None or until is not None) and limit is None:
|
717
|
+
raise ArgumentsRequired(self.id + ' fetchOHLCV requires a limit parameter when fetching candles with since or until')
|
718
|
+
if limit is not None:
|
719
|
+
request['limit'] = limit
|
720
|
+
response = await self.publicPostGetCandles(self.extend(request, params))
|
721
|
+
#
|
722
|
+
# {
|
723
|
+
# "ok": "ok",
|
724
|
+
# "data": [
|
725
|
+
# {
|
726
|
+
# "timestamp": "1728643320000",
|
727
|
+
# "open": "61061",
|
728
|
+
# "high": "61095.1",
|
729
|
+
# "low": "61048.5",
|
730
|
+
# "close": "61087.8",
|
731
|
+
# "volume": "0",
|
732
|
+
# "resolution": "1m",
|
733
|
+
# "isClosed": True,
|
734
|
+
# "timestampISO": "2024-10-11T10:42:00.000Z"
|
735
|
+
# },
|
736
|
+
# ...
|
737
|
+
#
|
738
|
+
data = self.safe_list(response, 'data', [])
|
739
|
+
return self.parse_ohlcvs(data, market, timeframe, since, limit)
|
740
|
+
|
741
|
+
def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
|
742
|
+
return [
|
743
|
+
self.safe_integer(ohlcv, 'timestamp'),
|
744
|
+
self.safe_number(ohlcv, 'open'),
|
745
|
+
self.safe_number(ohlcv, 'high'),
|
746
|
+
self.safe_number(ohlcv, 'low'),
|
747
|
+
self.safe_number(ohlcv, 'close'),
|
748
|
+
self.safe_number(ohlcv, 'volume'),
|
749
|
+
]
|
683
750
|
|
684
|
-
async def fetch_trading_fees(self, params={}):
|
751
|
+
async def fetch_trading_fees(self, params={}) -> TradingFees:
|
685
752
|
"""
|
686
|
-
:see: https://docs.cex.io/#get-my-fee
|
687
753
|
fetch the trading fees for multiple markets
|
754
|
+
|
755
|
+
https://trade.cex.io/docs/#rest-public-api-calls-candles
|
756
|
+
|
688
757
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
689
758
|
:returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
|
690
759
|
"""
|
691
760
|
await self.load_markets()
|
692
|
-
response = await self.
|
761
|
+
response = await self.privatePostGetMyCurrentFee(params)
|
693
762
|
#
|
694
|
-
#
|
695
|
-
#
|
696
|
-
#
|
697
|
-
#
|
698
|
-
#
|
699
|
-
#
|
700
|
-
#
|
701
|
-
#
|
702
|
-
# }
|
763
|
+
# {
|
764
|
+
# "ok": "ok",
|
765
|
+
# "data": {
|
766
|
+
# "tradingFee": {
|
767
|
+
# "AI-USD": {
|
768
|
+
# "percent": "0.25"
|
769
|
+
# },
|
770
|
+
# ...
|
703
771
|
#
|
704
|
-
data = self.
|
705
|
-
|
772
|
+
data = self.safe_dict(response, 'data', {})
|
773
|
+
fees = self.safe_dict(data, 'tradingFee', {})
|
774
|
+
return self.parse_trading_fees(fees, True)
|
775
|
+
|
776
|
+
def parse_trading_fees(self, response, useKeyAsId=False) -> TradingFees:
|
777
|
+
result: dict = {}
|
778
|
+
keys = list(response.keys())
|
779
|
+
for i in range(0, len(keys)):
|
780
|
+
key = keys[i]
|
781
|
+
market = None
|
782
|
+
if useKeyAsId:
|
783
|
+
market = self.safe_market(key)
|
784
|
+
parsed = self.parse_trading_fee(response[key], market)
|
785
|
+
result[parsed['symbol']] = parsed
|
706
786
|
for i in range(0, len(self.symbols)):
|
707
787
|
symbol = self.symbols[i]
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
takerString = self.safe_string(fee, 'buy')
|
712
|
-
maker = self.parse_number(Precise.string_div(makerString, '100'))
|
713
|
-
taker = self.parse_number(Precise.string_div(takerString, '100'))
|
714
|
-
result[symbol] = {
|
715
|
-
'info': fee,
|
716
|
-
'symbol': symbol,
|
717
|
-
'maker': maker,
|
718
|
-
'taker': taker,
|
719
|
-
'percentage': True,
|
720
|
-
}
|
788
|
+
if not (symbol in result):
|
789
|
+
market = self.market(symbol)
|
790
|
+
result[symbol] = self.parse_trading_fee(response, market)
|
721
791
|
return result
|
722
792
|
|
793
|
+
def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
|
794
|
+
return {
|
795
|
+
'info': fee,
|
796
|
+
'symbol': self.safe_string(market, 'symbol'),
|
797
|
+
'maker': self.safe_number(fee, 'percent'),
|
798
|
+
'taker': self.safe_number(fee, 'percent'),
|
799
|
+
'percentage': None,
|
800
|
+
'tierBased': None,
|
801
|
+
}
|
802
|
+
|
803
|
+
async def fetch_accounts(self, params={}) -> List[Account]:
|
804
|
+
await self.load_markets()
|
805
|
+
response = await self.privatePostGetMyAccountStatusV3(params)
|
806
|
+
#
|
807
|
+
# {
|
808
|
+
# "ok": "ok",
|
809
|
+
# "data": {
|
810
|
+
# "convertedCurrency": "USD",
|
811
|
+
# "balancesPerAccounts": {
|
812
|
+
# "": {
|
813
|
+
# "AI": {
|
814
|
+
# "balance": "0.000000",
|
815
|
+
# "balanceOnHold": "0.000000"
|
816
|
+
# },
|
817
|
+
# "USDT": {
|
818
|
+
# "balance": "0.00000000",
|
819
|
+
# "balanceOnHold": "0.00000000"
|
820
|
+
# }
|
821
|
+
# }
|
822
|
+
# }
|
823
|
+
# }
|
824
|
+
# }
|
825
|
+
#
|
826
|
+
data = self.safe_dict(response, 'data', {})
|
827
|
+
balances = self.safe_dict(data, 'balancesPerAccounts', {})
|
828
|
+
arrays = self.to_array(balances)
|
829
|
+
return self.parse_accounts(arrays, params)
|
830
|
+
|
831
|
+
def parse_account(self, account: dict) -> Account:
|
832
|
+
return {
|
833
|
+
'id': None,
|
834
|
+
'type': None,
|
835
|
+
'code': None,
|
836
|
+
'info': account,
|
837
|
+
}
|
838
|
+
|
839
|
+
async def fetch_balance(self, params={}) -> Balances:
|
840
|
+
"""
|
841
|
+
query for balance and get the amount of funds available for trading or funds locked in orders
|
842
|
+
|
843
|
+
https://trade.cex.io/docs/#rest-private-api-calls-account-status-v3
|
844
|
+
|
845
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
846
|
+
:param dict [params.method]: 'privatePostGetMyWalletBalance' or 'privatePostGetMyAccountStatusV3'
|
847
|
+
:param dict [params.account]: in case 'privatePostGetMyAccountStatusV3' is chosen, self can specify the account name(default is empty string)
|
848
|
+
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
|
849
|
+
"""
|
850
|
+
accountName = None
|
851
|
+
accountName, params = self.handle_param_string(params, 'account', '') # default is empty string
|
852
|
+
method = None
|
853
|
+
method, params = self.handle_param_string(params, 'method', 'privatePostGetMyWalletBalance')
|
854
|
+
accountBalance = None
|
855
|
+
if method == 'privatePostGetMyAccountStatusV3':
|
856
|
+
response = await self.privatePostGetMyAccountStatusV3(params)
|
857
|
+
#
|
858
|
+
# {
|
859
|
+
# "ok": "ok",
|
860
|
+
# "data": {
|
861
|
+
# "convertedCurrency": "USD",
|
862
|
+
# "balancesPerAccounts": {
|
863
|
+
# "": {
|
864
|
+
# "AI": {
|
865
|
+
# "balance": "0.000000",
|
866
|
+
# "balanceOnHold": "0.000000"
|
867
|
+
# },
|
868
|
+
# ....
|
869
|
+
#
|
870
|
+
data = self.safe_dict(response, 'data', {})
|
871
|
+
balances = self.safe_dict(data, 'balancesPerAccounts', {})
|
872
|
+
accountBalance = self.safe_dict(balances, accountName, {})
|
873
|
+
else:
|
874
|
+
response = await self.privatePostGetMyWalletBalance(params)
|
875
|
+
#
|
876
|
+
# {
|
877
|
+
# "ok": "ok",
|
878
|
+
# "data": {
|
879
|
+
# "AI": {
|
880
|
+
# "balance": "25.606429"
|
881
|
+
# },
|
882
|
+
# "USDT": {
|
883
|
+
# "balance": "7.935449"
|
884
|
+
# },
|
885
|
+
# ...
|
886
|
+
#
|
887
|
+
accountBalance = self.safe_dict(response, 'data', {})
|
888
|
+
return self.parse_balance(accountBalance)
|
889
|
+
|
890
|
+
def parse_balance(self, response) -> Balances:
|
891
|
+
result: dict = {
|
892
|
+
'info': response,
|
893
|
+
}
|
894
|
+
keys = list(response.keys())
|
895
|
+
for i in range(0, len(keys)):
|
896
|
+
key = keys[i]
|
897
|
+
balance = self.safe_dict(response, key, {})
|
898
|
+
code = self.safe_currency_code(key)
|
899
|
+
account: dict = {
|
900
|
+
'used': self.safe_string(balance, 'balanceOnHold'),
|
901
|
+
'total': self.safe_string(balance, 'balance'),
|
902
|
+
}
|
903
|
+
result[code] = account
|
904
|
+
return self.safe_balance(result)
|
905
|
+
|
906
|
+
async def fetch_orders_by_status(self, status: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
907
|
+
"""
|
908
|
+
fetches information on multiple orders made by the user
|
909
|
+
|
910
|
+
https://trade.cex.io/docs/#rest-private-api-calls-orders
|
911
|
+
|
912
|
+
:param str status: order status to fetch for
|
913
|
+
:param str symbol: unified market symbol of the market orders were made in
|
914
|
+
:param int [since]: the earliest time in ms to fetch orders for
|
915
|
+
:param int [limit]: the maximum number of order structures to retrieve
|
916
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
917
|
+
:param int [params.until]: timestamp in ms of the latest entry
|
918
|
+
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
919
|
+
"""
|
920
|
+
await self.load_markets()
|
921
|
+
request: dict = {}
|
922
|
+
isClosedOrders = (status == 'closed')
|
923
|
+
if isClosedOrders:
|
924
|
+
request['archived'] = True
|
925
|
+
market = None
|
926
|
+
if symbol is not None:
|
927
|
+
market = self.market(symbol)
|
928
|
+
request['pair'] = market['id']
|
929
|
+
if limit is not None:
|
930
|
+
request['pageSize'] = limit
|
931
|
+
if since is not None:
|
932
|
+
request['serverCreateTimestampFrom'] = since
|
933
|
+
elif isClosedOrders:
|
934
|
+
# exchange requires a `since` parameter for closed orders, so set default to allowed 365
|
935
|
+
request['serverCreateTimestampFrom'] = self.milliseconds() - 364 * 24 * 60 * 60 * 1000
|
936
|
+
until = None
|
937
|
+
until, params = self.handle_param_integer_2(params, 'until', 'till')
|
938
|
+
if until is not None:
|
939
|
+
request['serverCreateTimestampTo'] = until
|
940
|
+
response = await self.privatePostGetMyOrders(self.extend(request, params))
|
941
|
+
#
|
942
|
+
# if called without `pair`
|
943
|
+
#
|
944
|
+
# {
|
945
|
+
# "ok": "ok",
|
946
|
+
# "data": [
|
947
|
+
# {
|
948
|
+
# "orderId": "1313003",
|
949
|
+
# "clientOrderId": "037F0AFEB93A",
|
950
|
+
# "clientId": "up421412345",
|
951
|
+
# "accountId": null,
|
952
|
+
# "status": "FILLED",
|
953
|
+
# "statusIsFinal": True,
|
954
|
+
# "currency1": "AI",
|
955
|
+
# "currency2": "USDT",
|
956
|
+
# "side": "BUY",
|
957
|
+
# "orderType": "Market",
|
958
|
+
# "timeInForce": "IOC",
|
959
|
+
# "comment": null,
|
960
|
+
# "rejectCode": null,
|
961
|
+
# "rejectReason": null,
|
962
|
+
# "initialOnHoldAmountCcy1": null,
|
963
|
+
# "initialOnHoldAmountCcy2": "10.23456700",
|
964
|
+
# "executedAmountCcy1": "25.606429",
|
965
|
+
# "executedAmountCcy2": "10.20904439",
|
966
|
+
# "requestedAmountCcy1": null,
|
967
|
+
# "requestedAmountCcy2": "10.20904439",
|
968
|
+
# "originalAmountCcy2": "10.23456700",
|
969
|
+
# "feeAmount": "0.02552261",
|
970
|
+
# "feeCurrency": "USDT",
|
971
|
+
# "price": null,
|
972
|
+
# "averagePrice": "0.3986",
|
973
|
+
# "clientCreateTimestamp": "1728474625320",
|
974
|
+
# "serverCreateTimestamp": "1728474624956",
|
975
|
+
# "lastUpdateTimestamp": "1728474628015",
|
976
|
+
# "expireTime": null,
|
977
|
+
# "effectiveTime": null
|
978
|
+
# },
|
979
|
+
# ...
|
980
|
+
#
|
981
|
+
data = self.safe_list(response, 'data', [])
|
982
|
+
return self.parse_orders(data, market, since, limit)
|
983
|
+
|
984
|
+
async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
985
|
+
"""
|
986
|
+
|
987
|
+
https://trade.cex.io/docs/#rest-private-api-calls-orders
|
988
|
+
|
989
|
+
fetches information on multiple canceled orders made by the user
|
990
|
+
:param str symbol: unified market symbol of the market orders were made in
|
991
|
+
:param int [since]: timestamp in ms of the earliest order, default is None
|
992
|
+
:param int [limit]: max number of orders to return, default is None
|
993
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
994
|
+
:returns dict: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
995
|
+
"""
|
996
|
+
return await self.fetch_orders_by_status('closed', symbol, since, limit, params)
|
997
|
+
|
998
|
+
async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
999
|
+
"""
|
1000
|
+
|
1001
|
+
https://trade.cex.io/docs/#rest-private-api-calls-orders
|
1002
|
+
|
1003
|
+
fetches information on multiple canceled orders made by the user
|
1004
|
+
:param str symbol: unified market symbol of the market orders were made in
|
1005
|
+
:param int [since]: timestamp in ms of the earliest order, default is None
|
1006
|
+
:param int [limit]: max number of orders to return, default is None
|
1007
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1008
|
+
:returns dict: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
1009
|
+
"""
|
1010
|
+
return await self.fetch_orders_by_status('open', symbol, since, limit, params)
|
1011
|
+
|
1012
|
+
async def fetch_open_order(self, id: str, symbol: Str = None, params={}):
|
1013
|
+
"""
|
1014
|
+
fetches information on an open order made by the user
|
1015
|
+
|
1016
|
+
https://trade.cex.io/docs/#rest-private-api-calls-orders
|
1017
|
+
|
1018
|
+
:param str id: order id
|
1019
|
+
:param str [symbol]: unified symbol of the market the order was made in
|
1020
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1021
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
1022
|
+
"""
|
1023
|
+
await self.load_markets()
|
1024
|
+
request: dict = {
|
1025
|
+
'orderId': int(id),
|
1026
|
+
}
|
1027
|
+
result = await self.fetch_open_orders(symbol, None, None, self.extend(request, params))
|
1028
|
+
return result[0]
|
1029
|
+
|
1030
|
+
async def fetch_closed_order(self, id: str, symbol: Str = None, params={}):
|
1031
|
+
"""
|
1032
|
+
fetches information on an closed order made by the user
|
1033
|
+
|
1034
|
+
https://trade.cex.io/docs/#rest-private-api-calls-orders
|
1035
|
+
|
1036
|
+
:param str id: order id
|
1037
|
+
:param str [symbol]: unified symbol of the market the order was made in
|
1038
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1039
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
1040
|
+
"""
|
1041
|
+
await self.load_markets()
|
1042
|
+
request: dict = {
|
1043
|
+
'orderId': int(id),
|
1044
|
+
}
|
1045
|
+
result = await self.fetch_closed_orders(symbol, None, None, self.extend(request, params))
|
1046
|
+
return result[0]
|
1047
|
+
|
1048
|
+
def parse_order_status(self, status: Str):
|
1049
|
+
statuses: dict = {
|
1050
|
+
'PENDING_NEW': 'open',
|
1051
|
+
'NEW': 'open',
|
1052
|
+
'PARTIALLY_FILLED': 'open',
|
1053
|
+
'FILLED': 'closed',
|
1054
|
+
'EXPIRED': 'expired',
|
1055
|
+
'REJECTED': 'rejected',
|
1056
|
+
'PENDING_CANCEL': 'canceling',
|
1057
|
+
'CANCELLED': 'canceled',
|
1058
|
+
}
|
1059
|
+
return self.safe_string(statuses, status, status)
|
1060
|
+
|
1061
|
+
def parse_order(self, order: dict, market: Market = None) -> Order:
|
1062
|
+
#
|
1063
|
+
# "orderId": "1313003",
|
1064
|
+
# "clientOrderId": "037F0AFEB93A",
|
1065
|
+
# "clientId": "up421412345",
|
1066
|
+
# "accountId": null,
|
1067
|
+
# "status": "FILLED",
|
1068
|
+
# "statusIsFinal": True,
|
1069
|
+
# "currency1": "AI",
|
1070
|
+
# "currency2": "USDT",
|
1071
|
+
# "side": "BUY",
|
1072
|
+
# "orderType": "Market",
|
1073
|
+
# "timeInForce": "IOC",
|
1074
|
+
# "comment": null,
|
1075
|
+
# "rejectCode": null,
|
1076
|
+
# "rejectReason": null,
|
1077
|
+
# "initialOnHoldAmountCcy1": null,
|
1078
|
+
# "initialOnHoldAmountCcy2": "10.23456700",
|
1079
|
+
# "executedAmountCcy1": "25.606429",
|
1080
|
+
# "executedAmountCcy2": "10.20904439",
|
1081
|
+
# "requestedAmountCcy1": null,
|
1082
|
+
# "requestedAmountCcy2": "10.20904439",
|
1083
|
+
# "originalAmountCcy2": "10.23456700",
|
1084
|
+
# "feeAmount": "0.02552261",
|
1085
|
+
# "feeCurrency": "USDT",
|
1086
|
+
# "price": null,
|
1087
|
+
# "averagePrice": "0.3986",
|
1088
|
+
# "clientCreateTimestamp": "1728474625320",
|
1089
|
+
# "serverCreateTimestamp": "1728474624956",
|
1090
|
+
# "lastUpdateTimestamp": "1728474628015",
|
1091
|
+
# "expireTime": null,
|
1092
|
+
# "effectiveTime": null
|
1093
|
+
#
|
1094
|
+
currency1 = self.safe_string(order, 'currency1')
|
1095
|
+
currency2 = self.safe_string(order, 'currency2')
|
1096
|
+
marketId = None
|
1097
|
+
if currency1 is not None and currency2 is not None:
|
1098
|
+
marketId = currency1 + '-' + currency2
|
1099
|
+
market = self.safe_market(marketId, market)
|
1100
|
+
symbol = market['symbol']
|
1101
|
+
status = self.parse_order_status(self.safe_string(order, 'status'))
|
1102
|
+
fee = {}
|
1103
|
+
feeAmount = self.safe_number(order, 'feeAmount')
|
1104
|
+
if feeAmount is not None:
|
1105
|
+
currencyId = self.safe_string(order, 'feeCurrency')
|
1106
|
+
feeCode = self.safe_currency_code(currencyId)
|
1107
|
+
fee['currency'] = feeCode
|
1108
|
+
fee['cost'] = feeAmount
|
1109
|
+
timestamp = self.safe_integer(order, 'serverCreateTimestamp')
|
1110
|
+
requestedBase = self.safe_number(order, 'requestedAmountCcy1')
|
1111
|
+
executedBase = self.safe_number(order, 'executedAmountCcy1')
|
1112
|
+
# requestedQuote = self.safe_number(order, 'requestedAmountCcy2')
|
1113
|
+
executedQuote = self.safe_number(order, 'executedAmountCcy2')
|
1114
|
+
return self.safe_order({
|
1115
|
+
'id': self.safe_string(order, 'orderId'),
|
1116
|
+
'clientOrderId': self.safe_string(order, 'clientOrderId'),
|
1117
|
+
'timestamp': timestamp,
|
1118
|
+
'datetime': self.iso8601(timestamp),
|
1119
|
+
'lastUpdateTimestamp': self.safe_integer(order, 'lastUpdateTimestamp'),
|
1120
|
+
'lastTradeTimestamp': None,
|
1121
|
+
'symbol': symbol,
|
1122
|
+
'type': self.safe_string_lower(order, 'orderType'),
|
1123
|
+
'timeInForce': self.safe_string(order, 'timeInForce'),
|
1124
|
+
'postOnly': None,
|
1125
|
+
'side': self.safe_string_lower(order, 'side'),
|
1126
|
+
'price': self.safe_number(order, 'price'),
|
1127
|
+
'triggerPrice': self.safe_number(order, 'stopPrice'),
|
1128
|
+
'amount': requestedBase,
|
1129
|
+
'cost': executedQuote,
|
1130
|
+
'average': self.safe_number(order, 'averagePrice'),
|
1131
|
+
'filled': executedBase,
|
1132
|
+
'remaining': None,
|
1133
|
+
'status': status,
|
1134
|
+
'fee': fee,
|
1135
|
+
'trades': None,
|
1136
|
+
'info': order,
|
1137
|
+
}, market)
|
1138
|
+
|
723
1139
|
async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
724
1140
|
"""
|
725
|
-
:see: https://docs.cex.io/#place-order
|
726
1141
|
create a trade order
|
727
|
-
|
1142
|
+
|
1143
|
+
https://trade.cex.io/docs/#rest-private-api-calls-new-order
|
1144
|
+
|
728
1145
|
:param str symbol: unified symbol of the market to create an order in
|
729
1146
|
:param str type: 'market' or 'limit'
|
730
1147
|
:param str side: 'buy' or 'sell'
|
731
1148
|
:param float amount: how much of currency you want to trade in units of base currency
|
732
|
-
:param float [price]: the price at which the order is to be
|
1149
|
+
:param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
|
733
1150
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
734
|
-
:param
|
1151
|
+
:param str [params.accountId]: account-id to use(default is empty string)
|
1152
|
+
:param float [params.triggerPrice]: the price at which a trigger order is triggered at
|
735
1153
|
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
736
1154
|
"""
|
1155
|
+
accountId = None
|
1156
|
+
accountId, params = self.handle_option_and_params(params, 'createOrder', 'accountId')
|
1157
|
+
if accountId is None:
|
1158
|
+
raise ArgumentsRequired(self.id + ' createOrder() : API trading is now allowed from main account, set params["accountId"] or .options["createOrder"]["accountId"] to the name of your sub-account')
|
737
1159
|
await self.load_markets()
|
738
1160
|
market = self.market(symbol)
|
739
|
-
request = {
|
740
|
-
'
|
741
|
-
'
|
1161
|
+
request: dict = {
|
1162
|
+
'clientOrderId': self.uuid(),
|
1163
|
+
'currency1': market['baseId'],
|
1164
|
+
'currency2': market['quoteId'],
|
1165
|
+
'accountId': accountId,
|
1166
|
+
'orderType': self.capitalize(type.lower()),
|
1167
|
+
'side': side.upper(),
|
1168
|
+
'timestamp': self.milliseconds(),
|
1169
|
+
'amountCcy1': self.amount_to_precision(symbol, amount),
|
742
1170
|
}
|
743
|
-
|
744
|
-
|
745
|
-
quoteAmount = None
|
746
|
-
createMarketBuyOrderRequiresPrice = True
|
747
|
-
createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
|
748
|
-
cost = self.safe_string(params, 'cost')
|
749
|
-
params = self.omit(params, 'cost')
|
750
|
-
if cost is not None:
|
751
|
-
quoteAmount = self.cost_to_precision(symbol, cost)
|
752
|
-
elif createMarketBuyOrderRequiresPrice:
|
753
|
-
if price is None:
|
754
|
-
raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend in the amount argument')
|
755
|
-
else:
|
756
|
-
amountString = self.number_to_string(amount)
|
757
|
-
priceString = self.number_to_string(price)
|
758
|
-
costRequest = Precise.string_mul(amountString, priceString)
|
759
|
-
quoteAmount = self.cost_to_precision(symbol, costRequest)
|
760
|
-
else:
|
761
|
-
quoteAmount = self.cost_to_precision(symbol, amount)
|
762
|
-
request['amount'] = quoteAmount
|
763
|
-
else:
|
764
|
-
request['amount'] = self.amount_to_precision(symbol, amount)
|
1171
|
+
timeInForce = None
|
1172
|
+
timeInForce, params = self.handle_option_and_params(params, 'createOrder', 'timeInForce', 'GTC')
|
765
1173
|
if type == 'limit':
|
766
|
-
request['price'] = self.
|
767
|
-
|
768
|
-
|
769
|
-
|
1174
|
+
request['price'] = self.price_to_precision(symbol, price)
|
1175
|
+
request['timeInForce'] = timeInForce
|
1176
|
+
triggerPrice = None
|
1177
|
+
triggerPrice, params = self.handle_param_string(params, 'triggerPrice')
|
1178
|
+
if triggerPrice is not None:
|
1179
|
+
request['type'] = 'Stop Limit'
|
1180
|
+
request['stopPrice'] = triggerPrice
|
1181
|
+
response = await self.privatePostDoMyNewOrder(self.extend(request, params))
|
770
1182
|
#
|
771
|
-
#
|
772
|
-
# "id": "12978363524",
|
773
|
-
# "time": 1586610022259,
|
774
|
-
# "type": "buy",
|
775
|
-
# "price": "0.033934",
|
776
|
-
# "amount": "0.10722802",
|
777
|
-
# "pending": "0.10722802",
|
778
|
-
# "complete": False
|
779
|
-
# }
|
1183
|
+
# on success
|
780
1184
|
#
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
1185
|
+
# {
|
1186
|
+
# "ok": "ok",
|
1187
|
+
# "data": {
|
1188
|
+
# "messageType": "executionReport",
|
1189
|
+
# "clientId": "up132245425",
|
1190
|
+
# "orderId": "1318485",
|
1191
|
+
# "clientOrderId": "b5b6cd40-154c-4c1c-bd51-4a442f3d50b9",
|
1192
|
+
# "accountId": "sub1",
|
1193
|
+
# "status": "FILLED",
|
1194
|
+
# "currency1": "LTC",
|
1195
|
+
# "currency2": "USDT",
|
1196
|
+
# "side": "BUY",
|
1197
|
+
# "executedAmountCcy1": "0.23000000",
|
1198
|
+
# "executedAmountCcy2": "15.09030000",
|
1199
|
+
# "requestedAmountCcy1": "0.23000000",
|
1200
|
+
# "requestedAmountCcy2": null,
|
1201
|
+
# "orderType": "Market",
|
1202
|
+
# "timeInForce": null,
|
1203
|
+
# "comment": null,
|
1204
|
+
# "executionType": "Trade",
|
1205
|
+
# "executionId": "1726747124624_101_41116",
|
1206
|
+
# "transactTime": "2024-10-15T15:08:12.794Z",
|
1207
|
+
# "expireTime": null,
|
1208
|
+
# "effectiveTime": null,
|
1209
|
+
# "averagePrice": "65.61",
|
1210
|
+
# "lastQuantity": "0.23000000",
|
1211
|
+
# "lastAmountCcy1": "0.23000000",
|
1212
|
+
# "lastAmountCcy2": "15.09030000",
|
1213
|
+
# "lastPrice": "65.61",
|
1214
|
+
# "feeAmount": "0.03772575",
|
1215
|
+
# "feeCurrency": "USDT",
|
1216
|
+
# "clientCreateTimestamp": "1729004892014",
|
1217
|
+
# "serverCreateTimestamp": "1729004891628",
|
1218
|
+
# "lastUpdateTimestamp": "1729004892786"
|
1219
|
+
# }
|
1220
|
+
# }
|
1221
|
+
#
|
1222
|
+
# on failure, there are extra fields
|
1223
|
+
#
|
1224
|
+
# "status": "REJECTED",
|
1225
|
+
# "requestedAmountCcy1": null,
|
1226
|
+
# "orderRejectReason": "{\\" code \\ ":405,\\" reason \\ ":\\" Either AmountCcy1(OrderQty)or AmountCcy2(CashOrderQty)should be specified for market order not both \\ "}",
|
1227
|
+
# "rejectCode": 405,
|
1228
|
+
# "rejectReason": "Either AmountCcy1(OrderQty) or AmountCcy2(CashOrderQty) should be specified for market order not both",
|
1229
|
+
#
|
1230
|
+
data = self.safe_dict(response, 'data')
|
1231
|
+
return self.parse_order(data, market)
|
809
1232
|
|
810
1233
|
async def cancel_order(self, id: str, symbol: Str = None, params={}):
|
811
1234
|
"""
|
812
|
-
:see: https://docs.cex.io/#cancel-order
|
813
1235
|
cancels an open order
|
1236
|
+
|
1237
|
+
https://trade.cex.io/docs/#rest-private-api-calls-cancel-order
|
1238
|
+
|
814
1239
|
:param str id: order id
|
815
|
-
:param str symbol:
|
1240
|
+
:param str symbol: unified symbol of the market the order was made in
|
816
1241
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
817
1242
|
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
818
1243
|
"""
|
819
1244
|
await self.load_markets()
|
820
|
-
request = {
|
821
|
-
'
|
1245
|
+
request: dict = {
|
1246
|
+
'orderId': int(id),
|
1247
|
+
'cancelRequestId': 'c_' + str((self.milliseconds())),
|
1248
|
+
'timestamp': self.milliseconds(),
|
822
1249
|
}
|
823
|
-
response = await self.
|
824
|
-
#
|
825
|
-
|
1250
|
+
response = await self.privatePostDoCancelMyOrder(self.extend(request, params))
|
1251
|
+
#
|
1252
|
+
# {"ok":"ok","data":{}}
|
1253
|
+
#
|
1254
|
+
data = self.safe_dict(response, 'data', {})
|
1255
|
+
return self.parse_order(data)
|
826
1256
|
|
827
1257
|
async def cancel_all_orders(self, symbol: Str = None, params={}):
|
828
1258
|
"""
|
829
|
-
:see: https://docs.cex.io/#cancel-all-orders-for-given-pair
|
830
1259
|
cancel all open orders in a market
|
831
|
-
|
832
|
-
|
833
|
-
|
1260
|
+
|
1261
|
+
https://trade.cex.io/docs/#rest-private-api-calls-cancel-all-orders
|
1262
|
+
|
1263
|
+
:param str symbol: alpaca cancelAllOrders cannot setting symbol, it will cancel all open orders
|
1264
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
834
1265
|
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
835
1266
|
"""
|
836
|
-
if symbol is None:
|
837
|
-
raise ArgumentsRequired(self.id + ' cancelAllOrders requires a symbol.')
|
838
1267
|
await self.load_markets()
|
839
|
-
|
840
|
-
request = {
|
841
|
-
'pair': market['id'],
|
842
|
-
}
|
843
|
-
orders = await self.privatePostCancelOrdersPair(self.extend(request, params))
|
1268
|
+
response = await self.privatePostDoCancelAllOrders(params)
|
844
1269
|
#
|
845
|
-
#
|
846
|
-
#
|
847
|
-
#
|
848
|
-
#
|
849
|
-
#
|
850
|
-
#
|
1270
|
+
# {
|
1271
|
+
# "ok": "ok",
|
1272
|
+
# "data": {
|
1273
|
+
# "clientOrderIds": [
|
1274
|
+
# "3AF77B67109F"
|
1275
|
+
# ]
|
1276
|
+
# }
|
1277
|
+
# }
|
851
1278
|
#
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
# ISO8601 string
|
860
|
-
timestamp = self.parse8601(timestamp)
|
861
|
-
elif timestamp is not None:
|
862
|
-
# either integer or string integer
|
863
|
-
timestamp = int(timestamp)
|
864
|
-
symbol = None
|
865
|
-
baseId = self.safe_string(order, 'symbol1')
|
866
|
-
quoteId = self.safe_string(order, 'symbol2')
|
867
|
-
if market is None and baseId is not None and quoteId is not None:
|
868
|
-
base = self.safe_currency_code(baseId)
|
869
|
-
quote = self.safe_currency_code(quoteId)
|
870
|
-
if (base is not None) and (quote is not None):
|
871
|
-
symbol = base + '/' + quote
|
872
|
-
if symbol in self.markets:
|
873
|
-
market = self.market(symbol)
|
874
|
-
status = self.parse_order_status(self.safe_string(order, 'status'))
|
875
|
-
price = self.safe_string(order, 'price')
|
876
|
-
amount = self.omit_zero(self.safe_string(order, 'amount'))
|
877
|
-
# sell orders can have a negative amount
|
878
|
-
# https://github.com/ccxt/ccxt/issues/5338
|
879
|
-
if amount is not None:
|
880
|
-
amount = Precise.string_abs(amount)
|
881
|
-
elif market is not None:
|
882
|
-
amountKey = 'a:' + market['base'] + 'cds:'
|
883
|
-
amount = Precise.string_abs(self.safe_string(order, amountKey))
|
884
|
-
remaining = self.safe_string_2(order, 'pending', 'remains')
|
885
|
-
filled = Precise.string_sub(amount, remaining)
|
886
|
-
fee = None
|
887
|
-
cost = None
|
888
|
-
if market is not None:
|
889
|
-
symbol = market['symbol']
|
890
|
-
taCost = self.safe_string(order, 'ta:' + market['quote'])
|
891
|
-
ttaCost = self.safe_string(order, 'tta:' + market['quote'])
|
892
|
-
cost = Precise.string_add(taCost, ttaCost)
|
893
|
-
baseFee = 'fa:' + market['base']
|
894
|
-
baseTakerFee = 'tfa:' + market['base']
|
895
|
-
quoteFee = 'fa:' + market['quote']
|
896
|
-
quoteTakerFee = 'tfa:' + market['quote']
|
897
|
-
feeRate = self.safe_string(order, 'tradingFeeMaker')
|
898
|
-
if not feeRate:
|
899
|
-
feeRate = self.safe_string(order, 'tradingFeeTaker', feeRate)
|
900
|
-
if feeRate:
|
901
|
-
feeRate = Precise.string_div(feeRate, '100') # convert to mathematically-correct percentage coefficients: 1.0 = 100%
|
902
|
-
if (baseFee in order) or (baseTakerFee in order):
|
903
|
-
baseFeeCost = self.safe_number_2(order, baseFee, baseTakerFee)
|
904
|
-
fee = {
|
905
|
-
'currency': market['base'],
|
906
|
-
'rate': self.parse_number(feeRate),
|
907
|
-
'cost': baseFeeCost,
|
908
|
-
}
|
909
|
-
elif (quoteFee in order) or (quoteTakerFee in order):
|
910
|
-
quoteFeeCost = self.safe_number_2(order, quoteFee, quoteTakerFee)
|
911
|
-
fee = {
|
912
|
-
'currency': market['quote'],
|
913
|
-
'rate': self.parse_number(feeRate),
|
914
|
-
'cost': quoteFeeCost,
|
915
|
-
}
|
916
|
-
if not cost:
|
917
|
-
cost = Precise.string_mul(price, filled)
|
918
|
-
side = self.safe_string(order, 'type')
|
919
|
-
trades = None
|
920
|
-
orderId = self.safe_string(order, 'id')
|
921
|
-
if 'vtx' in order:
|
922
|
-
trades = []
|
923
|
-
for i in range(0, len(order['vtx'])):
|
924
|
-
item = order['vtx'][i]
|
925
|
-
tradeSide = self.safe_string(item, 'type')
|
926
|
-
if tradeSide == 'cancel':
|
927
|
-
# looks like self might represent the cancelled part of an order
|
928
|
-
# {"id": "4426729543",
|
929
|
-
# "type": "cancel",
|
930
|
-
# "time": "2017-09-22T00:24:30.476Z",
|
931
|
-
# "user": "up106404164",
|
932
|
-
# "c": "user:up106404164:a:BCH",
|
933
|
-
# "d": "order:4426728375:a:BCH",
|
934
|
-
# "a": "0.09935956",
|
935
|
-
# "amount": "0.09935956",
|
936
|
-
# "balance": "0.42580261",
|
937
|
-
# "symbol": "BCH",
|
938
|
-
# "order": "4426728375",
|
939
|
-
# "buy": null,
|
940
|
-
# "sell": null,
|
941
|
-
# "pair": null,
|
942
|
-
# "pos": null,
|
943
|
-
# "cs": "0.42580261",
|
944
|
-
# "ds": 0}
|
945
|
-
continue
|
946
|
-
tradePrice = self.safe_string(item, 'price')
|
947
|
-
if tradePrice is None:
|
948
|
-
# self represents the order
|
949
|
-
# {
|
950
|
-
# "a": "0.47000000",
|
951
|
-
# "c": "user:up106404164:a:EUR",
|
952
|
-
# "d": "order:6065499239:a:EUR",
|
953
|
-
# "cs": "1432.93",
|
954
|
-
# "ds": "476.72",
|
955
|
-
# "id": "6065499249",
|
956
|
-
# "buy": null,
|
957
|
-
# "pos": null,
|
958
|
-
# "pair": null,
|
959
|
-
# "sell": null,
|
960
|
-
# "time": "2018-04-22T13:07:22.152Z",
|
961
|
-
# "type": "buy",
|
962
|
-
# "user": "up106404164",
|
963
|
-
# "order": "6065499239",
|
964
|
-
# "amount": "-715.97000000",
|
965
|
-
# "symbol": "EUR",
|
966
|
-
# "balance": "1432.93000000"}
|
967
|
-
continue
|
968
|
-
# todo: deal with these
|
969
|
-
if tradeSide == 'costsNothing':
|
970
|
-
continue
|
971
|
-
# --
|
972
|
-
# if side != tradeSide:
|
973
|
-
# raise Error(json.dumps(order, null, 2))
|
974
|
-
# if orderId != item['order']:
|
975
|
-
# raise Error(json.dumps(order, null, 2))
|
976
|
-
# --
|
977
|
-
# partial buy trade
|
978
|
-
# {
|
979
|
-
# "a": "0.01589885",
|
980
|
-
# "c": "user:up106404164:a:BTC",
|
981
|
-
# "d": "order:6065499239:a:BTC",
|
982
|
-
# "cs": "0.36300000",
|
983
|
-
# "ds": 0,
|
984
|
-
# "id": "6067991213",
|
985
|
-
# "buy": "6065499239",
|
986
|
-
# "pos": null,
|
987
|
-
# "pair": null,
|
988
|
-
# "sell": "6067991206",
|
989
|
-
# "time": "2018-04-22T23:09:11.773Z",
|
990
|
-
# "type": "buy",
|
991
|
-
# "user": "up106404164",
|
992
|
-
# "order": "6065499239",
|
993
|
-
# "price": 7146.5,
|
994
|
-
# "amount": "0.01589885",
|
995
|
-
# "symbol": "BTC",
|
996
|
-
# "balance": "0.36300000",
|
997
|
-
# "symbol2": "EUR",
|
998
|
-
# "fee_amount": "0.19"}
|
999
|
-
# --
|
1000
|
-
# trade with zero amount, but non-zero fee
|
1001
|
-
# {
|
1002
|
-
# "a": "0.00000000",
|
1003
|
-
# "c": "user:up106404164:a:EUR",
|
1004
|
-
# "d": "order:5840654423:a:EUR",
|
1005
|
-
# "cs": 559744,
|
1006
|
-
# "ds": 0,
|
1007
|
-
# "id": "5840654429",
|
1008
|
-
# "buy": "5807238573",
|
1009
|
-
# "pos": null,
|
1010
|
-
# "pair": null,
|
1011
|
-
# "sell": "5840654423",
|
1012
|
-
# "time": "2018-03-15T03:20:14.010Z",
|
1013
|
-
# "type": "sell",
|
1014
|
-
# "user": "up106404164",
|
1015
|
-
# "order": "5840654423",
|
1016
|
-
# "price": 730,
|
1017
|
-
# "amount": "0.00000000",
|
1018
|
-
# "symbol": "EUR",
|
1019
|
-
# "balance": "5597.44000000",
|
1020
|
-
# "symbol2": "BCH",
|
1021
|
-
# "fee_amount": "0.01"}
|
1022
|
-
# --
|
1023
|
-
# trade which should have an amount of exactly 0.002BTC
|
1024
|
-
# {
|
1025
|
-
# "a": "16.70000000",
|
1026
|
-
# "c": "user:up106404164:a:GBP",
|
1027
|
-
# "d": "order:9927386681:a:GBP",
|
1028
|
-
# "cs": "86.90",
|
1029
|
-
# "ds": 0,
|
1030
|
-
# "id": "9927401610",
|
1031
|
-
# "buy": "9927401601",
|
1032
|
-
# "pos": null,
|
1033
|
-
# "pair": null,
|
1034
|
-
# "sell": "9927386681",
|
1035
|
-
# "time": "2019-08-21T15:25:37.777Z",
|
1036
|
-
# "type": "sell",
|
1037
|
-
# "user": "up106404164",
|
1038
|
-
# "order": "9927386681",
|
1039
|
-
# "price": 8365,
|
1040
|
-
# "amount": "16.70000000",
|
1041
|
-
# "office": "UK",
|
1042
|
-
# "symbol": "GBP",
|
1043
|
-
# "balance": "86.90000000",
|
1044
|
-
# "symbol2": "BTC",
|
1045
|
-
# "fee_amount": "0.03"
|
1046
|
-
# }
|
1047
|
-
tradeTimestamp = self.parse8601(self.safe_string(item, 'time'))
|
1048
|
-
tradeAmount = self.safe_string(item, 'amount')
|
1049
|
-
feeCost = self.safe_string(item, 'fee_amount')
|
1050
|
-
absTradeAmount = Precise.string_abs(tradeAmount)
|
1051
|
-
tradeCost = None
|
1052
|
-
if tradeSide == 'sell':
|
1053
|
-
tradeCost = absTradeAmount
|
1054
|
-
absTradeAmount = Precise.string_div(Precise.string_add(feeCost, tradeCost), tradePrice)
|
1055
|
-
else:
|
1056
|
-
tradeCost = Precise.string_mul(absTradeAmount, tradePrice)
|
1057
|
-
trades.append({
|
1058
|
-
'id': self.safe_string(item, 'id'),
|
1059
|
-
'timestamp': tradeTimestamp,
|
1060
|
-
'datetime': self.iso8601(tradeTimestamp),
|
1061
|
-
'order': orderId,
|
1062
|
-
'symbol': symbol,
|
1063
|
-
'price': self.parse_number(tradePrice),
|
1064
|
-
'amount': self.parse_number(absTradeAmount),
|
1065
|
-
'cost': self.parse_number(tradeCost),
|
1066
|
-
'side': tradeSide,
|
1067
|
-
'fee': {
|
1068
|
-
'cost': self.parse_number(feeCost),
|
1069
|
-
'currency': market['quote'],
|
1070
|
-
},
|
1071
|
-
'info': item,
|
1072
|
-
'type': None,
|
1073
|
-
'takerOrMaker': None,
|
1074
|
-
})
|
1075
|
-
return self.safe_order({
|
1076
|
-
'info': order,
|
1077
|
-
'id': orderId,
|
1078
|
-
'clientOrderId': None,
|
1079
|
-
'datetime': self.iso8601(timestamp),
|
1080
|
-
'timestamp': timestamp,
|
1081
|
-
'lastTradeTimestamp': None,
|
1082
|
-
'status': status,
|
1083
|
-
'symbol': symbol,
|
1084
|
-
'type': 'market' if (price is None) else 'limit',
|
1085
|
-
'timeInForce': None,
|
1086
|
-
'postOnly': None,
|
1087
|
-
'side': side,
|
1088
|
-
'price': price,
|
1089
|
-
'stopPrice': None,
|
1090
|
-
'triggerPrice': None,
|
1091
|
-
'cost': cost,
|
1092
|
-
'amount': amount,
|
1093
|
-
'filled': filled,
|
1094
|
-
'remaining': remaining,
|
1095
|
-
'trades': trades,
|
1096
|
-
'fee': fee,
|
1097
|
-
'average': None,
|
1098
|
-
})
|
1279
|
+
data = self.safe_dict(response, 'data', {})
|
1280
|
+
ids = self.safe_list(data, 'clientOrderIds', [])
|
1281
|
+
orders = []
|
1282
|
+
for i in range(0, len(ids)):
|
1283
|
+
id = ids[i]
|
1284
|
+
orders.append({'clientOrderId': id})
|
1285
|
+
return self.parse_orders(orders)
|
1099
1286
|
|
1100
|
-
async def
|
1287
|
+
async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
|
1101
1288
|
"""
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
:param
|
1289
|
+
fetch the history of changes, actions done by the user or operations that altered the balance of the user
|
1290
|
+
|
1291
|
+
https://trade.cex.io/docs/#rest-private-api-calls-transaction-history
|
1292
|
+
|
1293
|
+
:param str [code]: unified currency code
|
1294
|
+
:param int [since]: timestamp in ms of the earliest ledger entry
|
1295
|
+
:param int [limit]: max number of ledger entries to return
|
1107
1296
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1108
|
-
:
|
1297
|
+
:param int [params.until]: timestamp in ms of the latest ledger entry
|
1298
|
+
:returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
|
1109
1299
|
"""
|
1110
1300
|
await self.load_markets()
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1301
|
+
currency = None
|
1302
|
+
request: dict = {}
|
1303
|
+
if code is not None:
|
1304
|
+
currency = self.currency(code)
|
1305
|
+
request['currency'] = currency['id']
|
1306
|
+
if since is not None:
|
1307
|
+
request['dateFrom'] = since
|
1308
|
+
if limit is not None:
|
1309
|
+
request['pageSize'] = limit
|
1310
|
+
until = None
|
1311
|
+
until, params = self.handle_param_integer_2(params, 'until', 'till')
|
1312
|
+
if until is not None:
|
1313
|
+
request['dateTo'] = until
|
1314
|
+
response = await self.privatePostGetMyTransactionHistory(self.extend(request, params))
|
1315
|
+
#
|
1316
|
+
# {
|
1317
|
+
# "ok": "ok",
|
1318
|
+
# "data": [
|
1319
|
+
# {
|
1320
|
+
# "transactionId": "30367722",
|
1321
|
+
# "timestamp": "2024-10-14T14:08:49.987Z",
|
1322
|
+
# "accountId": "",
|
1323
|
+
# "type": "withdraw",
|
1324
|
+
# "amount": "-12.39060600",
|
1325
|
+
# "details": "Withdraw fundingId=1235039 clientId=up421412345 walletTxId=76337154166",
|
1326
|
+
# "currency": "USDT"
|
1327
|
+
# },
|
1328
|
+
# ...
|
1329
|
+
#
|
1330
|
+
data = self.safe_list(response, 'data', [])
|
1331
|
+
return self.parse_ledger(data, currency, since, limit)
|
1332
|
+
|
1333
|
+
def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
|
1334
|
+
amount = self.safe_string(item, 'amount')
|
1335
|
+
direction = None
|
1336
|
+
if Precise.string_le(amount, '0'):
|
1337
|
+
direction = 'out'
|
1338
|
+
amount = Precise.string_mul('-1', amount)
|
1118
1339
|
else:
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1340
|
+
direction = 'in'
|
1341
|
+
currencyId = self.safe_string(item, 'currency')
|
1342
|
+
currency = self.safe_currency(currencyId, currency)
|
1343
|
+
code = self.safe_currency_code(currencyId, currency)
|
1344
|
+
timestampString = self.safe_string(item, 'timestamp')
|
1345
|
+
timestamp = self.parse8601(timestampString)
|
1346
|
+
type = self.safe_string(item, 'type')
|
1347
|
+
return self.safe_ledger_entry({
|
1348
|
+
'info': item,
|
1349
|
+
'id': self.safe_string(item, 'transactionId'),
|
1350
|
+
'direction': direction,
|
1351
|
+
'account': self.safe_string(item, 'accountId', ''),
|
1352
|
+
'referenceAccount': None,
|
1353
|
+
'referenceId': None,
|
1354
|
+
'type': self.parse_ledger_entry_type(type),
|
1355
|
+
'currency': code,
|
1356
|
+
'amount': self.parse_number(amount),
|
1357
|
+
'timestamp': timestamp,
|
1358
|
+
'datetime': self.iso8601(timestamp),
|
1359
|
+
'before': None,
|
1360
|
+
'after': None,
|
1361
|
+
'status': None,
|
1362
|
+
'fee': None,
|
1363
|
+
}, currency)
|
1123
1364
|
|
1124
|
-
|
1365
|
+
def parse_ledger_entry_type(self, type):
|
1366
|
+
ledgerType: dict = {
|
1367
|
+
'deposit': 'deposit',
|
1368
|
+
'withdraw': 'withdrawal',
|
1369
|
+
'commission': 'fee',
|
1370
|
+
}
|
1371
|
+
return self.safe_string(ledgerType, type, type)
|
1372
|
+
|
1373
|
+
async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
|
1125
1374
|
"""
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
:param
|
1375
|
+
fetch history of deposits and withdrawals
|
1376
|
+
|
1377
|
+
https://trade.cex.io/docs/#rest-private-api-calls-funding-history
|
1378
|
+
|
1379
|
+
:param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
|
1380
|
+
:param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
|
1381
|
+
:param int [limit]: max number of deposit/withdrawals to return, default is None
|
1131
1382
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1132
|
-
:returns
|
1383
|
+
:returns dict: a list of `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
|
1133
1384
|
"""
|
1134
|
-
if symbol is None:
|
1135
|
-
raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
|
1136
1385
|
await self.load_markets()
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1386
|
+
request: dict = {}
|
1387
|
+
currency = None
|
1388
|
+
if code is not None:
|
1389
|
+
currency = self.currency(code)
|
1390
|
+
if since is not None:
|
1391
|
+
request['dateFrom'] = since
|
1392
|
+
if limit is not None:
|
1393
|
+
request['pageSize'] = limit
|
1394
|
+
until = None
|
1395
|
+
until, params = self.handle_param_integer_2(params, 'until', 'till')
|
1396
|
+
if until is not None:
|
1397
|
+
request['dateTo'] = until
|
1398
|
+
response = await self.privatePostGetMyFundingHistory(self.extend(request, params))
|
1399
|
+
#
|
1400
|
+
# {
|
1401
|
+
# "ok": "ok",
|
1402
|
+
# "data": [
|
1403
|
+
# {
|
1404
|
+
# "clientId": "up421412345",
|
1405
|
+
# "accountId": "",
|
1406
|
+
# "currency": "USDT",
|
1407
|
+
# "direction": "withdraw",
|
1408
|
+
# "amount": "12.39060600",
|
1409
|
+
# "commissionAmount": "0.00000000",
|
1410
|
+
# "status": "approved",
|
1411
|
+
# "updatedAt": "2024-10-14T14:08:50.013Z",
|
1412
|
+
# "txId": "30367718",
|
1413
|
+
# "details": {}
|
1414
|
+
# },
|
1415
|
+
# ...
|
1416
|
+
#
|
1417
|
+
data = self.safe_list(response, 'data', [])
|
1418
|
+
return self.parse_transactions(data, currency, since, limit)
|
1141
1419
|
|
1142
|
-
|
1420
|
+
def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
|
1421
|
+
currencyId = self.safe_string(transaction, 'currency')
|
1422
|
+
direction = self.safe_string(transaction, 'direction')
|
1423
|
+
type = 'withdrawal' if (direction == 'withdraw') else 'deposit'
|
1424
|
+
code = self.safe_currency_code(currencyId, currency)
|
1425
|
+
updatedAt = self.safe_string(transaction, 'updatedAt')
|
1426
|
+
timestamp = self.parse8601(updatedAt)
|
1427
|
+
return {
|
1428
|
+
'info': transaction,
|
1429
|
+
'id': self.safe_string(transaction, 'txId'),
|
1430
|
+
'txid': None,
|
1431
|
+
'type': type,
|
1432
|
+
'currency': code,
|
1433
|
+
'network': None,
|
1434
|
+
'amount': self.safe_number(transaction, 'amount'),
|
1435
|
+
'status': self.parse_transaction_status(self.safe_string(transaction, 'status')),
|
1436
|
+
'timestamp': timestamp,
|
1437
|
+
'datetime': self.iso8601(timestamp),
|
1438
|
+
'address': None,
|
1439
|
+
'addressFrom': None,
|
1440
|
+
'addressTo': None,
|
1441
|
+
'tag': None,
|
1442
|
+
'tagFrom': None,
|
1443
|
+
'tagTo': None,
|
1444
|
+
'updated': None,
|
1445
|
+
'comment': None,
|
1446
|
+
'fee': {
|
1447
|
+
'currency': code,
|
1448
|
+
'cost': self.safe_number(transaction, 'commissionAmount'),
|
1449
|
+
},
|
1450
|
+
'internal': None,
|
1451
|
+
}
|
1452
|
+
|
1453
|
+
def parse_transaction_status(self, status: Str):
|
1454
|
+
statuses: dict = {
|
1455
|
+
'rejected': 'rejected',
|
1456
|
+
'pending': 'pending',
|
1457
|
+
'approved': 'ok',
|
1458
|
+
}
|
1459
|
+
return self.safe_string(statuses, status, status)
|
1460
|
+
|
1461
|
+
async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
|
1143
1462
|
"""
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1463
|
+
transfer currency internally between wallets on the same account
|
1464
|
+
|
1465
|
+
https://trade.cex.io/docs/#rest-private-api-calls-internal-transfer
|
1466
|
+
|
1467
|
+
:param str code: unified currency code
|
1468
|
+
:param float amount: amount to transfer
|
1469
|
+
:param str fromAccount: 'SPOT', 'FUND', or 'CONTRACT'
|
1470
|
+
:param str toAccount: 'SPOT', 'FUND', or 'CONTRACT'
|
1147
1471
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1148
|
-
:returns dict:
|
1472
|
+
:returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
|
1149
1473
|
"""
|
1474
|
+
transfer = None
|
1475
|
+
if toAccount != '' and fromAccount != '':
|
1476
|
+
transfer = await self.transfer_between_sub_accounts(code, amount, fromAccount, toAccount, params)
|
1477
|
+
else:
|
1478
|
+
transfer = await self.transfer_between_main_and_sub_account(code, amount, fromAccount, toAccount, params)
|
1479
|
+
fillResponseFromRequest = self.handle_option('transfer', 'fillResponseFromRequest', True)
|
1480
|
+
if fillResponseFromRequest:
|
1481
|
+
transfer['fromAccount'] = fromAccount
|
1482
|
+
transfer['toAccount'] = toAccount
|
1483
|
+
return transfer
|
1484
|
+
|
1485
|
+
async def transfer_between_main_and_sub_account(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
|
1150
1486
|
await self.load_markets()
|
1151
|
-
|
1152
|
-
|
1487
|
+
currency = self.currency(code)
|
1488
|
+
fromMain = (fromAccount == '')
|
1489
|
+
targetAccount = toAccount if fromMain else fromAccount
|
1490
|
+
guid = self.safe_string(params, 'guid', self.uuid())
|
1491
|
+
request: dict = {
|
1492
|
+
'currency': currency['id'],
|
1493
|
+
'amount': self.currency_to_precision(code, amount),
|
1494
|
+
'accountId': targetAccount,
|
1495
|
+
'clientTxId': guid,
|
1153
1496
|
}
|
1154
|
-
response =
|
1155
|
-
|
1497
|
+
response = None
|
1498
|
+
if fromMain:
|
1499
|
+
response = await self.privatePostDoDepositFundsFromWallet(self.extend(request, params))
|
1500
|
+
else:
|
1501
|
+
response = await self.privatePostDoWithdrawalFundsToWallet(self.extend(request, params))
|
1502
|
+
# both endpoints return the same structure, the only difference is that
|
1503
|
+
# the "accountId" is filled with the "subAccount"
|
1156
1504
|
#
|
1157
1505
|
# {
|
1158
|
-
# "
|
1159
|
-
# "
|
1160
|
-
#
|
1161
|
-
#
|
1162
|
-
#
|
1163
|
-
#
|
1164
|
-
#
|
1165
|
-
# "status": "d",
|
1166
|
-
# "symbol1": "ETH",
|
1167
|
-
# "symbol2": "EUR",
|
1168
|
-
# "amount": "0.50000000",
|
1169
|
-
# "kind": "api",
|
1170
|
-
# "price": "923.3386",
|
1171
|
-
# "tfacf": "1",
|
1172
|
-
# "fa:EUR": "0.55",
|
1173
|
-
# "ta:EUR": "369.77",
|
1174
|
-
# "remains": "0.00000000",
|
1175
|
-
# "tfa:EUR": "0.22",
|
1176
|
-
# "tta:EUR": "91.95",
|
1177
|
-
# "a:ETH:cds": "0.50000000",
|
1178
|
-
# "a:EUR:cds": "461.72",
|
1179
|
-
# "f:EUR:cds": "0.77",
|
1180
|
-
# "tradingFeeMaker": "0.15",
|
1181
|
-
# "tradingFeeTaker": "0.23",
|
1182
|
-
# "tradingFeeStrategy": "userVolumeAmount",
|
1183
|
-
# "tradingFeeUserVolumeAmount": "2896912572",
|
1184
|
-
# "orderId": "5442731603",
|
1185
|
-
# "next": False,
|
1186
|
-
# "vtx": [
|
1187
|
-
# {
|
1188
|
-
# "id": "5442734452",
|
1189
|
-
# "type": "sell",
|
1190
|
-
# "time": "2018-01-16T19:52:58.452Z",
|
1191
|
-
# "user": "up106404164",
|
1192
|
-
# "c": "user:up106404164:a:EUR",
|
1193
|
-
# "d": "order:5442731603:a:EUR",
|
1194
|
-
# "a": "104.53000000",
|
1195
|
-
# "amount": "104.53000000",
|
1196
|
-
# "balance": "932.71000000",
|
1197
|
-
# "symbol": "EUR",
|
1198
|
-
# "order": "5442731603",
|
1199
|
-
# "buy": "5442734443",
|
1200
|
-
# "sell": "5442731603",
|
1201
|
-
# "pair": null,
|
1202
|
-
# "pos": null,
|
1203
|
-
# "office": null,
|
1204
|
-
# "cs": "932.71",
|
1205
|
-
# "ds": 0,
|
1206
|
-
# "price": 923.3386,
|
1207
|
-
# "symbol2": "ETH",
|
1208
|
-
# "fee_amount": "0.16"
|
1209
|
-
# },
|
1210
|
-
# {
|
1211
|
-
# "id": "5442731609",
|
1212
|
-
# "type": "sell",
|
1213
|
-
# "time": "2018-01-16T19:52:38.071Z",
|
1214
|
-
# "user": "up106404164",
|
1215
|
-
# "c": "user:up106404164:a:EUR",
|
1216
|
-
# "d": "order:5442731603:a:EUR",
|
1217
|
-
# "a": "91.73000000",
|
1218
|
-
# "amount": "91.73000000",
|
1219
|
-
# "balance": "563.49000000",
|
1220
|
-
# "symbol": "EUR",
|
1221
|
-
# "order": "5442731603",
|
1222
|
-
# "buy": "5442618127",
|
1223
|
-
# "sell": "5442731603",
|
1224
|
-
# "pair": null,
|
1225
|
-
# "pos": null,
|
1226
|
-
# "office": null,
|
1227
|
-
# "cs": "563.49",
|
1228
|
-
# "ds": 0,
|
1229
|
-
# "price": 924.0092,
|
1230
|
-
# "symbol2": "ETH",
|
1231
|
-
# "fee_amount": "0.22"
|
1232
|
-
# },
|
1233
|
-
# {
|
1234
|
-
# "id": "5442731604",
|
1235
|
-
# "type": "sell",
|
1236
|
-
# "time": "2018-01-16T19:52:38.071Z",
|
1237
|
-
# "user": "up106404164",
|
1238
|
-
# "c": "order:5442731603:a:ETH",
|
1239
|
-
# "d": "user:up106404164:a:ETH",
|
1240
|
-
# "a": "0.50000000",
|
1241
|
-
# "amount": "-0.50000000",
|
1242
|
-
# "balance": "15.80995000",
|
1243
|
-
# "symbol": "ETH",
|
1244
|
-
# "order": "5442731603",
|
1245
|
-
# "buy": null,
|
1246
|
-
# "sell": null,
|
1247
|
-
# "pair": null,
|
1248
|
-
# "pos": null,
|
1249
|
-
# "office": null,
|
1250
|
-
# "cs": "0.50000000",
|
1251
|
-
# "ds": "15.80995000"
|
1252
|
-
# }
|
1253
|
-
# ]
|
1506
|
+
# "ok": "ok",
|
1507
|
+
# "data": {
|
1508
|
+
# "accountId": "sub1",
|
1509
|
+
# "clientTxId": "27ba8284-67cf-4386-9ec7-80b3871abd45",
|
1510
|
+
# "currency": "USDT",
|
1511
|
+
# "status": "approved"
|
1512
|
+
# }
|
1254
1513
|
# }
|
1255
1514
|
#
|
1256
|
-
|
1515
|
+
data = self.safe_dict(response, 'data', {})
|
1516
|
+
return self.parse_transfer(data, currency)
|
1257
1517
|
|
1258
|
-
async def
|
1259
|
-
"""
|
1260
|
-
:see: https://docs.cex.io/#archived-orders
|
1261
|
-
fetches information on multiple orders made by the user
|
1262
|
-
:param str symbol: unified market symbol of the market orders were made in
|
1263
|
-
:param int [since]: the earliest time in ms to fetch orders for
|
1264
|
-
:param int [limit]: the maximum number of order structures to retrieve
|
1265
|
-
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1266
|
-
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
1267
|
-
"""
|
1518
|
+
async def transfer_between_sub_accounts(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
|
1268
1519
|
await self.load_markets()
|
1269
|
-
|
1270
|
-
request = {
|
1271
|
-
'
|
1272
|
-
'
|
1273
|
-
'
|
1520
|
+
currency = self.currency(code)
|
1521
|
+
request: dict = {
|
1522
|
+
'currency': currency['id'],
|
1523
|
+
'amount': self.currency_to_precision(code, amount),
|
1524
|
+
'fromAccountId': fromAccount,
|
1525
|
+
'toAccountId': toAccount,
|
1274
1526
|
}
|
1275
|
-
response = await self.
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
# "symbol1": "ETH",
|
1287
|
-
# "symbol2": "GBP",
|
1288
|
-
# "amount": "0.20000000",
|
1289
|
-
# "price": "200.5625",
|
1290
|
-
# "remains": "0.20000000",
|
1291
|
-
# 'a:ETH:cds': "0.20000000",
|
1292
|
-
# "tradingFeeMaker": "0",
|
1293
|
-
# "tradingFeeTaker": "0.16",
|
1294
|
-
# "tradingFeeUserVolumeAmount": "10155061217",
|
1295
|
-
# "orderId": "4005785516"}
|
1296
|
-
# --
|
1297
|
-
# cancelled(partially filled buy):
|
1298
|
-
# {"id": "4084911657",
|
1299
|
-
# "type": "buy",
|
1300
|
-
# "time": "2017-08-05T03:18:39.596Z",
|
1301
|
-
# "lastTxTime": "2019-03-19T17:37:46.404Z",
|
1302
|
-
# "lastTx": "8459265833",
|
1303
|
-
# "pos": null,
|
1304
|
-
# "status": "cd",
|
1305
|
-
# "symbol1": "BTC",
|
1306
|
-
# "symbol2": "GBP",
|
1307
|
-
# "amount": "0.05000000",
|
1308
|
-
# "price": "2241.4692",
|
1309
|
-
# "tfacf": "1",
|
1310
|
-
# "remains": "0.03910535",
|
1311
|
-
# 'tfa:GBP': "0.04",
|
1312
|
-
# 'tta:GBP': "24.39",
|
1313
|
-
# 'a:BTC:cds': "0.01089465",
|
1314
|
-
# 'a:GBP:cds': "112.26",
|
1315
|
-
# 'f:GBP:cds': "0.04",
|
1316
|
-
# "tradingFeeMaker": "0",
|
1317
|
-
# "tradingFeeTaker": "0.16",
|
1318
|
-
# "tradingFeeUserVolumeAmount": "13336396963",
|
1319
|
-
# "orderId": "4084911657"}
|
1320
|
-
# --
|
1321
|
-
# cancelled(partially filled sell):
|
1322
|
-
# {"id": "4426728375",
|
1323
|
-
# "type": "sell",
|
1324
|
-
# "time": "2017-09-22T00:24:20.126Z",
|
1325
|
-
# "lastTxTime": "2017-09-22T00:24:30.476Z",
|
1326
|
-
# "lastTx": "4426729543",
|
1327
|
-
# "pos": null,
|
1328
|
-
# "status": "cd",
|
1329
|
-
# "symbol1": "BCH",
|
1330
|
-
# "symbol2": "BTC",
|
1331
|
-
# "amount": "0.10000000",
|
1332
|
-
# "price": "0.11757182",
|
1333
|
-
# "tfacf": "1",
|
1334
|
-
# "remains": "0.09935956",
|
1335
|
-
# 'tfa:BTC': "0.00000014",
|
1336
|
-
# 'tta:BTC': "0.00007537",
|
1337
|
-
# 'a:BCH:cds': "0.10000000",
|
1338
|
-
# 'a:BTC:cds': "0.00007537",
|
1339
|
-
# 'f:BTC:cds': "0.00000014",
|
1340
|
-
# "tradingFeeMaker": "0",
|
1341
|
-
# "tradingFeeTaker": "0.18",
|
1342
|
-
# "tradingFeeUserVolumeAmount": "3466715450",
|
1343
|
-
# "orderId": "4426728375"}
|
1344
|
-
# --
|
1345
|
-
# filled:
|
1346
|
-
# {"id": "5342275378",
|
1347
|
-
# "type": "sell",
|
1348
|
-
# "time": "2018-01-04T00:28:12.992Z",
|
1349
|
-
# "lastTxTime": "2018-01-04T00:28:12.992Z",
|
1350
|
-
# "lastTx": "5342275393",
|
1351
|
-
# "pos": null,
|
1352
|
-
# "status": "d",
|
1353
|
-
# "symbol1": "BCH",
|
1354
|
-
# "symbol2": "BTC",
|
1355
|
-
# "amount": "0.10000000",
|
1356
|
-
# "kind": "api",
|
1357
|
-
# "price": "0.17",
|
1358
|
-
# "remains": "0.00000000",
|
1359
|
-
# 'tfa:BTC': "0.00003902",
|
1360
|
-
# 'tta:BTC': "0.01699999",
|
1361
|
-
# 'a:BCH:cds': "0.10000000",
|
1362
|
-
# 'a:BTC:cds': "0.01699999",
|
1363
|
-
# 'f:BTC:cds': "0.00003902",
|
1364
|
-
# "tradingFeeMaker": "0.15",
|
1365
|
-
# "tradingFeeTaker": "0.23",
|
1366
|
-
# "tradingFeeUserVolumeAmount": "1525951128",
|
1367
|
-
# "orderId": "5342275378"}
|
1368
|
-
# --
|
1369
|
-
# market order(buy):
|
1370
|
-
# {"id": "6281946200",
|
1371
|
-
# "pos": null,
|
1372
|
-
# "time": "2018-05-23T11:55:43.467Z",
|
1373
|
-
# "type": "buy",
|
1374
|
-
# "amount": "0.00000000",
|
1375
|
-
# "lastTx": "6281946210",
|
1376
|
-
# "status": "d",
|
1377
|
-
# "amount2": "20.00",
|
1378
|
-
# "orderId": "6281946200",
|
1379
|
-
# "remains": "0.00000000",
|
1380
|
-
# "symbol1": "ETH",
|
1381
|
-
# "symbol2": "EUR",
|
1382
|
-
# "tfa:EUR": "0.05",
|
1383
|
-
# "tta:EUR": "19.94",
|
1384
|
-
# "a:ETH:cds": "0.03764100",
|
1385
|
-
# "a:EUR:cds": "20.00",
|
1386
|
-
# "f:EUR:cds": "0.05",
|
1387
|
-
# "lastTxTime": "2018-05-23T11:55:43.467Z",
|
1388
|
-
# "tradingFeeTaker": "0.25",
|
1389
|
-
# "tradingFeeUserVolumeAmount": "55998097"}
|
1390
|
-
# --
|
1391
|
-
# market order(sell):
|
1392
|
-
# {"id": "6282200948",
|
1393
|
-
# "pos": null,
|
1394
|
-
# "time": "2018-05-23T12:42:58.315Z",
|
1395
|
-
# "type": "sell",
|
1396
|
-
# "amount": "-0.05000000",
|
1397
|
-
# "lastTx": "6282200958",
|
1398
|
-
# "status": "d",
|
1399
|
-
# "orderId": "6282200948",
|
1400
|
-
# "remains": "0.00000000",
|
1401
|
-
# "symbol1": "ETH",
|
1402
|
-
# "symbol2": "EUR",
|
1403
|
-
# "tfa:EUR": "0.07",
|
1404
|
-
# "tta:EUR": "26.49",
|
1405
|
-
# "a:ETH:cds": "0.05000000",
|
1406
|
-
# "a:EUR:cds": "26.49",
|
1407
|
-
# "f:EUR:cds": "0.07",
|
1408
|
-
# "lastTxTime": "2018-05-23T12:42:58.315Z",
|
1409
|
-
# "tradingFeeTaker": "0.25",
|
1410
|
-
# "tradingFeeUserVolumeAmount": "56294576"}
|
1411
|
-
order = response[i]
|
1412
|
-
status = self.parse_order_status(self.safe_string(order, 'status'))
|
1413
|
-
baseId = self.safe_string(order, 'symbol1')
|
1414
|
-
quoteId = self.safe_string(order, 'symbol2')
|
1415
|
-
base = self.safe_currency_code(baseId)
|
1416
|
-
quote = self.safe_currency_code(quoteId)
|
1417
|
-
symbolInner = base + '/' + quote
|
1418
|
-
side = self.safe_string(order, 'type')
|
1419
|
-
baseAmount = self.safe_number(order, 'a:' + baseId + ':cds')
|
1420
|
-
quoteAmount = self.safe_number(order, 'a:' + quoteId + ':cds')
|
1421
|
-
fee = self.safe_number(order, 'f:' + quoteId + ':cds')
|
1422
|
-
amount = self.safe_string(order, 'amount')
|
1423
|
-
price = self.safe_string(order, 'price')
|
1424
|
-
remaining = self.safe_string(order, 'remains')
|
1425
|
-
filled = Precise.string_sub(amount, remaining)
|
1426
|
-
orderAmount = None
|
1427
|
-
cost = None
|
1428
|
-
average = None
|
1429
|
-
type = None
|
1430
|
-
if not price:
|
1431
|
-
type = 'market'
|
1432
|
-
orderAmount = baseAmount
|
1433
|
-
cost = quoteAmount
|
1434
|
-
average = Precise.string_div(orderAmount, cost)
|
1435
|
-
else:
|
1436
|
-
ta = self.safe_string(order, 'ta:' + quoteId, '0')
|
1437
|
-
tta = self.safe_string(order, 'tta:' + quoteId, '0')
|
1438
|
-
fa = self.safe_string(order, 'fa:' + quoteId, '0')
|
1439
|
-
tfa = self.safe_string(order, 'tfa:' + quoteId, '0')
|
1440
|
-
if side == 'sell':
|
1441
|
-
cost = Precise.string_add(Precise.string_add(ta, tta), Precise.string_add(fa, tfa))
|
1442
|
-
else:
|
1443
|
-
cost = Precise.string_sub(Precise.string_add(ta, tta), Precise.string_add(fa, tfa))
|
1444
|
-
type = 'limit'
|
1445
|
-
orderAmount = amount
|
1446
|
-
average = Precise.string_div(cost, filled)
|
1447
|
-
time = self.safe_string(order, 'time')
|
1448
|
-
lastTxTime = self.safe_string(order, 'lastTxTime')
|
1449
|
-
timestamp = self.parse8601(time)
|
1450
|
-
safeOrder = self.safe_order({
|
1451
|
-
'info': order,
|
1452
|
-
'id': self.safe_string(order, 'id'),
|
1453
|
-
'timestamp': timestamp,
|
1454
|
-
'datetime': self.iso8601(timestamp),
|
1455
|
-
'lastUpdated': self.parse8601(lastTxTime),
|
1456
|
-
'status': status,
|
1457
|
-
'symbol': symbolInner,
|
1458
|
-
'side': side,
|
1459
|
-
'price': price,
|
1460
|
-
'amount': orderAmount,
|
1461
|
-
'average': average,
|
1462
|
-
'type': type,
|
1463
|
-
'filled': filled,
|
1464
|
-
'cost': cost,
|
1465
|
-
'remaining': remaining,
|
1466
|
-
'fee': {
|
1467
|
-
'cost': fee,
|
1468
|
-
'currency': quote,
|
1469
|
-
},
|
1470
|
-
})
|
1471
|
-
results.append(safeOrder)
|
1472
|
-
return results
|
1473
|
-
|
1474
|
-
def parse_order_status(self, status):
|
1475
|
-
return self.safe_string(self.options['order']['status'], status, status)
|
1527
|
+
response = await self.privatePostDoMyInternalTransfer(self.extend(request, params))
|
1528
|
+
#
|
1529
|
+
# {
|
1530
|
+
# "ok": "ok",
|
1531
|
+
# "data": {
|
1532
|
+
# "transactionId": "30225415"
|
1533
|
+
# }
|
1534
|
+
# }
|
1535
|
+
#
|
1536
|
+
data = self.safe_dict(response, 'data', {})
|
1537
|
+
return self.parse_transfer(data, currency)
|
1476
1538
|
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
:
|
1483
|
-
:
|
1484
|
-
:
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
#
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1539
|
+
def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
|
1540
|
+
#
|
1541
|
+
# transferBetweenSubAccounts
|
1542
|
+
#
|
1543
|
+
# {
|
1544
|
+
# "ok": "ok",
|
1545
|
+
# "data": {
|
1546
|
+
# "transactionId": "30225415"
|
1547
|
+
# }
|
1548
|
+
# }
|
1549
|
+
#
|
1550
|
+
# transfer between main/sub
|
1551
|
+
#
|
1552
|
+
# {
|
1553
|
+
# "ok": "ok",
|
1554
|
+
# "data": {
|
1555
|
+
# "accountId": "sub1",
|
1556
|
+
# "clientTxId": "27ba8284-67cf-4386-9ec7-80b3871abd45",
|
1557
|
+
# "currency": "USDT",
|
1558
|
+
# "status": "approved"
|
1559
|
+
# }
|
1560
|
+
# }
|
1561
|
+
#
|
1562
|
+
currencyId = self.safe_string(transfer, 'currency')
|
1563
|
+
currencyCode = self.safe_currency_code(currencyId, currency)
|
1564
|
+
return {
|
1565
|
+
'info': transfer,
|
1566
|
+
'id': self.safe_string_2(transfer, 'transactionId', 'clientTxId'),
|
1567
|
+
'timestamp': None,
|
1568
|
+
'datetime': None,
|
1569
|
+
'currency': currencyCode,
|
1570
|
+
'amount': None,
|
1571
|
+
'fromAccount': None,
|
1572
|
+
'toAccount': None,
|
1573
|
+
'status': self.parse_transaction_status(self.safe_string(transfer, 'status')),
|
1503
1574
|
}
|
1504
|
-
response = await self.privatePostCancelReplaceOrderPair(self.extend(request, params))
|
1505
|
-
return self.parse_order(response, market)
|
1506
1575
|
|
1507
|
-
async def fetch_deposit_address(self, code: str, params={}):
|
1576
|
+
async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
|
1508
1577
|
"""
|
1509
|
-
:see: https://docs.cex.io/#get-crypto-address
|
1510
1578
|
fetch the deposit address for a currency associated with self account
|
1579
|
+
|
1580
|
+
https://trade.cex.io/docs/#rest-private-api-calls-deposit-address
|
1581
|
+
|
1511
1582
|
:param str code: unified currency code
|
1512
1583
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1584
|
+
:param str [params.accountId]: account-id(default to empty string) to refer to(at self moment, only sub-accounts allowed by exchange)
|
1513
1585
|
:returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
|
1514
1586
|
"""
|
1587
|
+
accountId = None
|
1588
|
+
accountId, params = self.handle_option_and_params(params, 'createOrder', 'accountId')
|
1589
|
+
if accountId is None:
|
1590
|
+
raise ArgumentsRequired(self.id + ' fetchDepositAddress() : main account is not allowed to fetch deposit address from api, set params["accountId"] or .options["createOrder"]["accountId"] to the name of your sub-account')
|
1515
1591
|
await self.load_markets()
|
1592
|
+
networkCode = None
|
1593
|
+
networkCode, params = self.handle_network_code_and_params(params)
|
1516
1594
|
currency = self.currency(code)
|
1517
|
-
request = {
|
1518
|
-
'
|
1595
|
+
request: dict = {
|
1596
|
+
'accountId': accountId,
|
1597
|
+
'currency': currency['id'], # documentation is wrong about self param
|
1598
|
+
'blockchain': self.network_code_to_id(networkCode),
|
1519
1599
|
}
|
1520
|
-
|
1521
|
-
# atm, cex doesn't support network in the request
|
1522
|
-
response = await self.privatePostGetCryptoAddress(self.extend(request, query))
|
1600
|
+
response = await self.privatePostGetDepositAddress(self.extend(request, params))
|
1523
1601
|
#
|
1524
1602
|
# {
|
1525
|
-
#
|
1526
|
-
#
|
1527
|
-
#
|
1528
|
-
#
|
1529
|
-
#
|
1530
|
-
#
|
1531
|
-
#
|
1532
|
-
#
|
1533
|
-
#
|
1534
|
-
# # for others coins(i.e. XRP, XLM) other keys are present:
|
1535
|
-
# # "destination": "rF1sdh25BJX3qFwneeTBwaq3zPEWYcwjp2",
|
1536
|
-
# # "destinationTag": "7519113655",
|
1537
|
-
# # "memo": "XLM-memo12345",
|
1538
|
-
# }
|
1539
|
-
# ]
|
1540
|
-
# }
|
1541
|
-
# }
|
1603
|
+
# "ok": "ok",
|
1604
|
+
# "data": {
|
1605
|
+
# "address": "TCr..................1AE",
|
1606
|
+
# "accountId": "sub1",
|
1607
|
+
# "currency": "USDT",
|
1608
|
+
# "blockchain": "tron"
|
1609
|
+
# }
|
1610
|
+
# }
|
1542
1611
|
#
|
1543
|
-
data = self.
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1612
|
+
data = self.safe_dict(response, 'data', {})
|
1613
|
+
return self.parse_deposit_address(data, currency)
|
1614
|
+
|
1615
|
+
def parse_deposit_address(self, depositAddress, currency: Currency = None) -> DepositAddress:
|
1616
|
+
address = self.safe_string(depositAddress, 'address')
|
1617
|
+
currencyId = self.safe_string(depositAddress, 'currency')
|
1618
|
+
currency = self.safe_currency(currencyId, currency)
|
1549
1619
|
self.check_address(address)
|
1550
1620
|
return {
|
1551
|
-
'
|
1621
|
+
'info': depositAddress,
|
1622
|
+
'currency': currency['code'],
|
1623
|
+
'network': self.network_id_to_code(self.safe_string(depositAddress, 'blockchain')),
|
1552
1624
|
'address': address,
|
1553
|
-
'tag':
|
1554
|
-
'network': self.network_id_to_code(selectedNetworkId),
|
1555
|
-
'info': data,
|
1625
|
+
'tag': None,
|
1556
1626
|
}
|
1557
1627
|
|
1558
|
-
def nonce(self):
|
1559
|
-
return self.milliseconds()
|
1560
|
-
|
1561
1628
|
def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
|
1562
|
-
url = self.urls['api'][
|
1629
|
+
url = self.urls['api'][api] + '/' + self.implode_params(path, params)
|
1563
1630
|
query = self.omit(params, self.extract_params(path))
|
1564
1631
|
if api == 'public':
|
1565
|
-
if
|
1566
|
-
|
1632
|
+
if method == 'GET':
|
1633
|
+
if query:
|
1634
|
+
url += '?' + self.urlencode(query)
|
1635
|
+
else:
|
1636
|
+
body = self.json(query)
|
1637
|
+
headers = {
|
1638
|
+
'Content-Type': 'application/json',
|
1639
|
+
}
|
1567
1640
|
else:
|
1568
1641
|
self.check_required_credentials()
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
'key': self.apiKey,
|
1574
|
-
'signature': signature.upper(),
|
1575
|
-
'nonce': nonce,
|
1576
|
-
}, query))
|
1642
|
+
seconds = str(self.seconds())
|
1643
|
+
body = self.json(query)
|
1644
|
+
auth = path + seconds + body
|
1645
|
+
signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256, 'base64')
|
1577
1646
|
headers = {
|
1578
1647
|
'Content-Type': 'application/json',
|
1648
|
+
'X-AGGR-KEY': self.apiKey,
|
1649
|
+
'X-AGGR-TIMESTAMP': seconds,
|
1650
|
+
'X-AGGR-SIGNATURE': signature,
|
1579
1651
|
}
|
1580
1652
|
return {'url': url, 'method': method, 'body': body, 'headers': headers}
|
1581
1653
|
|
1582
|
-
def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
return None
|
1654
|
+
def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
|
1655
|
+
# in some cases, like from createOrder, exchange returns nested escaped JSON string:
|
1656
|
+
# {"ok":"ok","data":{"messageType":"executionReport", "orderRejectReason":"{\"code\":405}"}}
|
1657
|
+
# and because of `.parseJson` bug, we need extra fix
|
1587
1658
|
if response is None:
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1659
|
+
if body is None:
|
1660
|
+
raise NullResponse(self.id + ' returned empty response')
|
1661
|
+
elif body[0] == '{':
|
1662
|
+
fixed = self.fix_stringified_json_members(body)
|
1663
|
+
response = self.parse_json(fixed)
|
1664
|
+
else:
|
1665
|
+
raise NullResponse(self.id + ' returned unparsed response: ' + body)
|
1666
|
+
error = self.safe_string(response, 'error')
|
1667
|
+
if error is not None:
|
1595
1668
|
feedback = self.id + ' ' + body
|
1596
|
-
self.throw_exactly_matched_exception(self.exceptions['exact'],
|
1597
|
-
self.throw_broadly_matched_exception(self.exceptions['broad'],
|
1669
|
+
self.throw_exactly_matched_exception(self.exceptions['exact'], error, feedback)
|
1670
|
+
self.throw_broadly_matched_exception(self.exceptions['broad'], error, feedback)
|
1598
1671
|
raise ExchangeError(feedback)
|
1672
|
+
# check errors in order-engine(the responses are not standard, so we parse here)
|
1673
|
+
if url.find('do_my_new_order') >= 0:
|
1674
|
+
data = self.safe_dict(response, 'data', {})
|
1675
|
+
rejectReason = self.safe_string(data, 'rejectReason')
|
1676
|
+
if rejectReason is not None:
|
1677
|
+
self.throw_broadly_matched_exception(self.exceptions['broad'], rejectReason, rejectReason)
|
1678
|
+
raise ExchangeError(self.id + ' createOrder() ' + rejectReason)
|
1599
1679
|
return None
|