ccxt 4.2.76__py2.py3-none-any.whl → 4.4.48__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccxt/__init__.py +36 -14
- ccxt/abstract/alpaca.py +4 -0
- ccxt/abstract/bigone.py +1 -1
- ccxt/abstract/binance.py +112 -48
- ccxt/abstract/binancecoinm.py +112 -48
- ccxt/abstract/binanceus.py +147 -83
- ccxt/abstract/binanceusdm.py +112 -48
- ccxt/abstract/bingx.py +133 -78
- ccxt/abstract/bitbank.py +5 -0
- ccxt/abstract/bitfinex.py +136 -65
- ccxt/abstract/bitfinex1.py +69 -0
- ccxt/abstract/bitflyer.py +1 -0
- ccxt/abstract/bitget.py +8 -1
- ccxt/abstract/bitmart.py +13 -1
- ccxt/abstract/bitopro.py +1 -0
- ccxt/abstract/bitpanda.py +0 -12
- ccxt/abstract/bitrue.py +3 -3
- ccxt/abstract/bitstamp.py +26 -3
- ccxt/abstract/blofin.py +24 -0
- ccxt/abstract/btcbox.py +1 -0
- ccxt/abstract/bybit.py +29 -14
- ccxt/abstract/cex.py +28 -29
- ccxt/abstract/coinbase.py +6 -0
- ccxt/abstract/coinbaseadvanced.py +94 -0
- ccxt/abstract/{coinbasepro.py → coinbaseexchange.py} +1 -0
- ccxt/abstract/coinbaseinternational.py +1 -1
- ccxt/abstract/coincatch.py +94 -0
- ccxt/abstract/coinex.py +233 -123
- ccxt/abstract/coinmetro.py +1 -0
- ccxt/abstract/cryptocom.py +14 -0
- ccxt/abstract/defx.py +69 -0
- ccxt/abstract/deribit.py +1 -0
- ccxt/abstract/digifinex.py +1 -0
- ccxt/abstract/ellipx.py +25 -0
- ccxt/abstract/gate.py +20 -0
- ccxt/abstract/gateio.py +20 -0
- ccxt/abstract/gemini.py +1 -0
- ccxt/abstract/hashkey.py +67 -0
- ccxt/abstract/hyperliquid.py +1 -1
- ccxt/abstract/independentreserve.py +6 -0
- ccxt/abstract/kraken.py +4 -3
- ccxt/abstract/krakenfutures.py +4 -0
- ccxt/abstract/kucoin.py +25 -0
- ccxt/abstract/kucoinfutures.py +35 -0
- ccxt/abstract/luno.py +2 -0
- ccxt/abstract/mexc.py +4 -0
- ccxt/abstract/myokx.py +340 -0
- ccxt/abstract/oceanex.py +5 -0
- ccxt/abstract/okx.py +30 -0
- ccxt/abstract/onetrading.py +0 -12
- ccxt/abstract/oxfun.py +34 -0
- ccxt/abstract/paradex.py +40 -0
- ccxt/abstract/phemex.py +1 -0
- ccxt/abstract/upbit.py +4 -0
- ccxt/abstract/vertex.py +19 -0
- ccxt/abstract/whitebit.py +31 -1
- ccxt/abstract/woo.py +6 -2
- ccxt/abstract/woofipro.py +119 -0
- ccxt/abstract/xt.py +153 -0
- ccxt/abstract/zonda.py +6 -0
- ccxt/ace.py +164 -60
- ccxt/alpaca.py +727 -63
- ccxt/ascendex.py +395 -249
- ccxt/async_support/__init__.py +36 -14
- ccxt/async_support/ace.py +164 -60
- ccxt/async_support/alpaca.py +727 -63
- ccxt/async_support/ascendex.py +396 -249
- ccxt/async_support/base/exchange.py +531 -155
- ccxt/async_support/base/ws/aiohttp_client.py +28 -5
- ccxt/async_support/base/ws/cache.py +3 -2
- ccxt/async_support/base/ws/client.py +26 -5
- ccxt/async_support/base/ws/fast_client.py +4 -3
- ccxt/async_support/base/ws/functions.py +1 -1
- ccxt/async_support/base/ws/future.py +40 -31
- ccxt/async_support/base/ws/order_book_side.py +3 -0
- ccxt/async_support/bequant.py +1 -1
- ccxt/async_support/bigone.py +329 -202
- ccxt/async_support/binance.py +3513 -1511
- ccxt/async_support/binancecoinm.py +2 -1
- ccxt/async_support/binanceus.py +12 -1
- ccxt/async_support/binanceusdm.py +3 -1
- ccxt/async_support/bingx.py +3105 -881
- ccxt/async_support/bit2c.py +119 -38
- ccxt/async_support/bitbank.py +215 -76
- ccxt/async_support/bitbns.py +124 -53
- ccxt/async_support/bitfinex.py +3236 -1078
- ccxt/async_support/bitfinex1.py +1711 -0
- ccxt/async_support/bitflyer.py +239 -50
- ccxt/async_support/bitget.py +1513 -563
- ccxt/async_support/bithumb.py +201 -67
- ccxt/async_support/bitmart.py +1320 -435
- ccxt/async_support/bitmex.py +308 -111
- ccxt/async_support/bitopro.py +256 -96
- ccxt/async_support/bitrue.py +365 -233
- ccxt/async_support/bitso.py +201 -89
- ccxt/async_support/bitstamp.py +438 -269
- ccxt/async_support/bitteam.py +179 -73
- ccxt/async_support/bitvavo.py +180 -70
- ccxt/async_support/bl3p.py +92 -25
- ccxt/async_support/blockchaincom.py +193 -79
- ccxt/async_support/blofin.py +403 -150
- ccxt/async_support/btcalpha.py +161 -55
- ccxt/async_support/btcbox.py +250 -34
- ccxt/async_support/btcmarkets.py +232 -85
- ccxt/async_support/btcturk.py +159 -60
- ccxt/async_support/bybit.py +2326 -1255
- ccxt/async_support/cex.py +1409 -1329
- ccxt/async_support/coinbase.py +1455 -288
- ccxt/async_support/coinbaseadvanced.py +17 -0
- ccxt/async_support/{coinbasepro.py → coinbaseexchange.py} +233 -99
- ccxt/async_support/coinbaseinternational.py +428 -88
- ccxt/async_support/coincatch.py +5152 -0
- ccxt/async_support/coincheck.py +121 -38
- ccxt/async_support/coinex.py +4020 -3339
- ccxt/async_support/coinlist.py +273 -116
- ccxt/async_support/coinmate.py +204 -97
- ccxt/async_support/coinmetro.py +203 -110
- ccxt/async_support/coinone.py +142 -68
- ccxt/async_support/coinsph.py +206 -89
- ccxt/async_support/coinspot.py +137 -62
- ccxt/async_support/cryptocom.py +515 -185
- ccxt/async_support/currencycom.py +203 -85
- ccxt/async_support/defx.py +2066 -0
- ccxt/async_support/delta.py +467 -158
- ccxt/async_support/deribit.py +558 -324
- ccxt/async_support/digifinex.py +340 -223
- ccxt/async_support/ellipx.py +1826 -0
- ccxt/async_support/exmo.py +259 -128
- ccxt/async_support/gate.py +1473 -464
- ccxt/async_support/gemini.py +206 -84
- ccxt/async_support/hashkey.py +4164 -0
- ccxt/async_support/hitbtc.py +334 -178
- ccxt/async_support/hollaex.py +134 -83
- ccxt/async_support/htx.py +1095 -563
- ccxt/async_support/huobijp.py +105 -56
- ccxt/async_support/hyperliquid.py +1634 -269
- ccxt/async_support/idex.py +148 -95
- ccxt/async_support/independentreserve.py +236 -31
- ccxt/async_support/indodax.py +165 -62
- ccxt/async_support/kraken.py +871 -354
- ccxt/async_support/krakenfutures.py +324 -100
- ccxt/async_support/kucoin.py +1050 -355
- ccxt/async_support/kucoinfutures.py +1004 -149
- ccxt/async_support/kuna.py +138 -106
- ccxt/async_support/latoken.py +135 -79
- ccxt/async_support/lbank.py +290 -113
- ccxt/async_support/luno.py +112 -62
- ccxt/async_support/lykke.py +104 -55
- ccxt/async_support/mercado.py +36 -29
- ccxt/async_support/mexc.py +995 -429
- ccxt/async_support/myokx.py +43 -0
- ccxt/async_support/ndax.py +163 -82
- ccxt/async_support/novadax.py +121 -75
- ccxt/async_support/oceanex.py +175 -59
- ccxt/async_support/okcoin.py +222 -163
- ccxt/async_support/okx.py +1777 -455
- ccxt/async_support/onetrading.py +132 -414
- ccxt/async_support/oxfun.py +2832 -0
- ccxt/async_support/p2b.py +79 -51
- ccxt/async_support/paradex.py +2017 -0
- ccxt/async_support/paymium.py +56 -32
- ccxt/async_support/phemex.py +572 -196
- ccxt/async_support/poloniex.py +218 -95
- ccxt/async_support/poloniexfutures.py +260 -92
- ccxt/async_support/probit.py +143 -110
- ccxt/async_support/timex.py +123 -70
- ccxt/async_support/tokocrypto.py +129 -93
- ccxt/async_support/tradeogre.py +39 -25
- ccxt/async_support/upbit.py +322 -113
- ccxt/async_support/vertex.py +2983 -0
- ccxt/async_support/wavesexchange.py +227 -173
- ccxt/async_support/wazirx.py +145 -65
- ccxt/async_support/whitebit.py +533 -138
- ccxt/async_support/woo.py +1155 -295
- ccxt/async_support/woofipro.py +2716 -0
- ccxt/async_support/xt.py +4628 -0
- ccxt/async_support/yobit.py +160 -92
- ccxt/async_support/zaif.py +80 -33
- ccxt/async_support/zonda.py +140 -69
- ccxt/base/errors.py +51 -20
- ccxt/base/exchange.py +1729 -482
- ccxt/base/precise.py +10 -0
- ccxt/base/types.py +223 -4
- ccxt/bequant.py +1 -1
- ccxt/bigone.py +329 -202
- ccxt/binance.py +3513 -1511
- ccxt/binancecoinm.py +2 -1
- ccxt/binanceus.py +12 -1
- ccxt/binanceusdm.py +3 -1
- ccxt/bingx.py +3105 -881
- ccxt/bit2c.py +119 -38
- ccxt/bitbank.py +215 -76
- ccxt/bitbns.py +124 -53
- ccxt/bitfinex.py +3235 -1078
- ccxt/bitfinex1.py +1710 -0
- ccxt/bitflyer.py +239 -50
- ccxt/bitget.py +1513 -563
- ccxt/bithumb.py +200 -67
- ccxt/bitmart.py +1320 -435
- ccxt/bitmex.py +308 -111
- ccxt/bitopro.py +256 -96
- ccxt/bitrue.py +365 -233
- ccxt/bitso.py +201 -89
- ccxt/bitstamp.py +438 -269
- ccxt/bitteam.py +179 -73
- ccxt/bitvavo.py +180 -70
- ccxt/bl3p.py +92 -25
- ccxt/blockchaincom.py +193 -79
- ccxt/blofin.py +403 -150
- ccxt/btcalpha.py +161 -55
- ccxt/btcbox.py +250 -34
- ccxt/btcmarkets.py +232 -85
- ccxt/btcturk.py +159 -60
- ccxt/bybit.py +2326 -1255
- ccxt/cex.py +1408 -1329
- ccxt/coinbase.py +1455 -288
- ccxt/coinbaseadvanced.py +17 -0
- ccxt/{coinbasepro.py → coinbaseexchange.py} +233 -99
- ccxt/coinbaseinternational.py +428 -88
- ccxt/coincatch.py +5152 -0
- ccxt/coincheck.py +121 -38
- ccxt/coinex.py +4020 -3339
- ccxt/coinlist.py +273 -116
- ccxt/coinmate.py +204 -97
- ccxt/coinmetro.py +203 -110
- ccxt/coinone.py +142 -68
- ccxt/coinsph.py +206 -89
- ccxt/coinspot.py +137 -62
- ccxt/cryptocom.py +515 -185
- ccxt/currencycom.py +203 -85
- ccxt/defx.py +2065 -0
- ccxt/delta.py +467 -158
- ccxt/deribit.py +558 -324
- ccxt/digifinex.py +340 -223
- ccxt/ellipx.py +1826 -0
- ccxt/exmo.py +259 -128
- ccxt/gate.py +1473 -464
- ccxt/gemini.py +206 -84
- ccxt/hashkey.py +4164 -0
- ccxt/hitbtc.py +334 -178
- ccxt/hollaex.py +134 -83
- ccxt/htx.py +1095 -563
- ccxt/huobijp.py +105 -56
- ccxt/hyperliquid.py +1633 -269
- ccxt/idex.py +148 -95
- ccxt/independentreserve.py +235 -31
- ccxt/indodax.py +165 -62
- ccxt/kraken.py +871 -354
- ccxt/krakenfutures.py +324 -100
- ccxt/kucoin.py +1050 -355
- ccxt/kucoinfutures.py +1004 -149
- ccxt/kuna.py +138 -106
- ccxt/latoken.py +135 -79
- ccxt/lbank.py +290 -113
- ccxt/luno.py +112 -62
- ccxt/lykke.py +104 -55
- ccxt/mercado.py +36 -29
- ccxt/mexc.py +994 -429
- ccxt/myokx.py +43 -0
- ccxt/ndax.py +163 -82
- ccxt/novadax.py +121 -75
- ccxt/oceanex.py +175 -59
- ccxt/okcoin.py +222 -163
- ccxt/okx.py +1777 -455
- ccxt/onetrading.py +132 -414
- ccxt/oxfun.py +2831 -0
- ccxt/p2b.py +79 -51
- ccxt/paradex.py +2017 -0
- ccxt/paymium.py +56 -32
- ccxt/phemex.py +572 -196
- ccxt/poloniex.py +218 -95
- ccxt/poloniexfutures.py +260 -92
- ccxt/pro/__init__.py +29 -5
- ccxt/pro/alpaca.py +32 -17
- ccxt/pro/ascendex.py +63 -15
- ccxt/pro/bequant.py +4 -0
- ccxt/pro/binance.py +1596 -329
- ccxt/pro/binancecoinm.py +1 -0
- ccxt/pro/binanceus.py +2 -9
- ccxt/pro/binanceusdm.py +2 -0
- ccxt/pro/bingx.py +527 -134
- ccxt/pro/bitcoincom.py +4 -1
- ccxt/pro/bitfinex.py +731 -266
- ccxt/pro/bitfinex1.py +635 -0
- ccxt/pro/bitget.py +726 -357
- ccxt/pro/bithumb.py +380 -0
- ccxt/pro/bitmart.py +138 -39
- ccxt/pro/bitmex.py +199 -40
- ccxt/pro/bitopro.py +25 -13
- ccxt/pro/bitrue.py +31 -32
- ccxt/pro/bitstamp.py +7 -6
- ccxt/pro/bitvavo.py +204 -82
- ccxt/pro/blockchaincom.py +30 -17
- ccxt/pro/blofin.py +692 -0
- ccxt/pro/bybit.py +791 -82
- ccxt/pro/cex.py +99 -51
- ccxt/pro/coinbase.py +220 -30
- ccxt/{async_support/hitbtc3.py → pro/coinbaseadvanced.py} +5 -5
- ccxt/pro/{coinbasepro.py → coinbaseexchange.py} +19 -19
- ccxt/pro/coinbaseinternational.py +193 -30
- ccxt/pro/coincatch.py +1464 -0
- ccxt/pro/coincheck.py +11 -6
- ccxt/pro/coinex.py +967 -661
- ccxt/pro/coinone.py +17 -10
- ccxt/pro/cryptocom.py +446 -66
- ccxt/pro/currencycom.py +11 -10
- ccxt/pro/defx.py +832 -0
- ccxt/pro/deribit.py +168 -32
- ccxt/pro/exmo.py +253 -21
- ccxt/pro/gate.py +729 -64
- ccxt/pro/gemini.py +44 -26
- ccxt/pro/hashkey.py +802 -0
- ccxt/pro/hitbtc.py +208 -103
- ccxt/pro/hollaex.py +25 -9
- ccxt/pro/htx.py +83 -39
- ccxt/pro/huobijp.py +17 -16
- ccxt/pro/hyperliquid.py +502 -31
- ccxt/pro/idex.py +28 -13
- ccxt/pro/independentreserve.py +21 -16
- ccxt/pro/kraken.py +298 -51
- ccxt/pro/krakenfutures.py +166 -75
- ccxt/pro/kucoin.py +395 -77
- ccxt/pro/kucoinfutures.py +400 -99
- ccxt/pro/lbank.py +52 -31
- ccxt/pro/luno.py +12 -10
- ccxt/pro/mexc.py +400 -50
- ccxt/pro/myokx.py +28 -0
- ccxt/pro/ndax.py +25 -12
- ccxt/pro/okcoin.py +28 -9
- ccxt/pro/okx.py +935 -124
- ccxt/pro/onetrading.py +41 -24
- ccxt/pro/oxfun.py +1054 -0
- ccxt/pro/p2b.py +100 -24
- ccxt/pro/paradex.py +352 -0
- ccxt/pro/phemex.py +93 -34
- ccxt/pro/poloniex.py +129 -50
- ccxt/pro/poloniexfutures.py +53 -32
- ccxt/pro/probit.py +93 -86
- ccxt/pro/upbit.py +401 -8
- ccxt/pro/vertex.py +943 -0
- ccxt/pro/wazirx.py +46 -28
- ccxt/pro/whitebit.py +65 -12
- ccxt/pro/woo.py +486 -70
- ccxt/pro/woofipro.py +1271 -0
- ccxt/pro/xt.py +1067 -0
- ccxt/probit.py +143 -110
- ccxt/static_dependencies/__init__.py +1 -1
- ccxt/static_dependencies/lark/__init__.py +38 -0
- ccxt/static_dependencies/lark/__pyinstaller/__init__.py +6 -0
- ccxt/static_dependencies/lark/__pyinstaller/hook-lark.py +14 -0
- ccxt/static_dependencies/lark/ast_utils.py +59 -0
- ccxt/static_dependencies/lark/common.py +86 -0
- ccxt/static_dependencies/lark/exceptions.py +292 -0
- ccxt/static_dependencies/lark/grammar.py +130 -0
- ccxt/static_dependencies/lark/grammars/__init__.py +0 -0
- ccxt/static_dependencies/lark/indenter.py +143 -0
- ccxt/static_dependencies/lark/lark.py +658 -0
- ccxt/static_dependencies/lark/lexer.py +678 -0
- ccxt/static_dependencies/lark/load_grammar.py +1428 -0
- ccxt/static_dependencies/lark/parse_tree_builder.py +391 -0
- ccxt/static_dependencies/lark/parser_frontends.py +257 -0
- ccxt/static_dependencies/lark/parsers/__init__.py +0 -0
- ccxt/static_dependencies/lark/parsers/cyk.py +340 -0
- ccxt/static_dependencies/lark/parsers/earley.py +314 -0
- ccxt/static_dependencies/lark/parsers/earley_common.py +42 -0
- ccxt/static_dependencies/lark/parsers/earley_forest.py +801 -0
- ccxt/static_dependencies/lark/parsers/grammar_analysis.py +203 -0
- ccxt/static_dependencies/lark/parsers/lalr_analysis.py +332 -0
- ccxt/static_dependencies/lark/parsers/lalr_interactive_parser.py +158 -0
- ccxt/static_dependencies/lark/parsers/lalr_parser.py +122 -0
- ccxt/static_dependencies/lark/parsers/lalr_parser_state.py +110 -0
- ccxt/static_dependencies/lark/parsers/xearley.py +165 -0
- ccxt/static_dependencies/lark/py.typed +0 -0
- ccxt/static_dependencies/lark/reconstruct.py +107 -0
- ccxt/static_dependencies/lark/tools/__init__.py +70 -0
- ccxt/static_dependencies/lark/tools/nearley.py +202 -0
- ccxt/static_dependencies/lark/tools/serialize.py +32 -0
- ccxt/static_dependencies/lark/tools/standalone.py +196 -0
- ccxt/static_dependencies/lark/tree.py +267 -0
- ccxt/static_dependencies/lark/tree_matcher.py +186 -0
- ccxt/static_dependencies/lark/tree_templates.py +180 -0
- ccxt/static_dependencies/lark/utils.py +343 -0
- ccxt/static_dependencies/lark/visitors.py +596 -0
- ccxt/static_dependencies/marshmallow/__init__.py +81 -0
- ccxt/static_dependencies/marshmallow/base.py +65 -0
- ccxt/static_dependencies/marshmallow/class_registry.py +94 -0
- ccxt/static_dependencies/marshmallow/decorators.py +231 -0
- ccxt/static_dependencies/marshmallow/error_store.py +60 -0
- ccxt/static_dependencies/marshmallow/exceptions.py +71 -0
- ccxt/static_dependencies/marshmallow/fields.py +2114 -0
- ccxt/static_dependencies/marshmallow/orderedset.py +89 -0
- ccxt/static_dependencies/marshmallow/py.typed +0 -0
- ccxt/static_dependencies/marshmallow/schema.py +1228 -0
- ccxt/static_dependencies/marshmallow/types.py +12 -0
- ccxt/static_dependencies/marshmallow/utils.py +378 -0
- ccxt/static_dependencies/marshmallow/validate.py +678 -0
- ccxt/static_dependencies/marshmallow/warnings.py +2 -0
- ccxt/static_dependencies/marshmallow_dataclass/__init__.py +1047 -0
- ccxt/static_dependencies/marshmallow_dataclass/collection_field.py +51 -0
- ccxt/static_dependencies/marshmallow_dataclass/lazy_class_attribute.py +45 -0
- ccxt/static_dependencies/marshmallow_dataclass/mypy.py +71 -0
- ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
- ccxt/static_dependencies/marshmallow_dataclass/typing.py +14 -0
- ccxt/static_dependencies/marshmallow_dataclass/union_field.py +82 -0
- ccxt/static_dependencies/marshmallow_oneofschema/__init__.py +1 -0
- ccxt/static_dependencies/marshmallow_oneofschema/one_of_schema.py +193 -0
- ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
- ccxt/static_dependencies/starknet/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/data_types.py +123 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/cairo_types.py +77 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser.py +46 -0
- ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser_transformer.py +138 -0
- ccxt/static_dependencies/starknet/cairo/felt.py +64 -0
- ccxt/static_dependencies/starknet/cairo/type_parser.py +121 -0
- ccxt/static_dependencies/starknet/cairo/v1/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/v1/type_parser.py +59 -0
- ccxt/static_dependencies/starknet/cairo/v2/__init__.py +0 -0
- ccxt/static_dependencies/starknet/cairo/v2/type_parser.py +77 -0
- ccxt/static_dependencies/starknet/ccxt_utils.py +7 -0
- ccxt/static_dependencies/starknet/common.py +15 -0
- ccxt/static_dependencies/starknet/constants.py +39 -0
- ccxt/static_dependencies/starknet/hash/__init__.py +0 -0
- ccxt/static_dependencies/starknet/hash/address.py +79 -0
- ccxt/static_dependencies/starknet/hash/compiled_class_hash_objects.py +111 -0
- ccxt/static_dependencies/starknet/hash/selector.py +16 -0
- ccxt/static_dependencies/starknet/hash/storage.py +12 -0
- ccxt/static_dependencies/starknet/hash/utils.py +78 -0
- ccxt/static_dependencies/starknet/models/__init__.py +0 -0
- ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
- ccxt/static_dependencies/starknet/serialization/__init__.py +24 -0
- ccxt/static_dependencies/starknet/serialization/_calldata_reader.py +40 -0
- ccxt/static_dependencies/starknet/serialization/_context.py +142 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/__init__.py +10 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/_common.py +82 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/array_serializer.py +43 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/bool_serializer.py +37 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/byte_array_serializer.py +66 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/cairo_data_serializer.py +71 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/enum_serializer.py +71 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/felt_serializer.py +50 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/named_tuple_serializer.py +58 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/option_serializer.py +43 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/output_serializer.py +40 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/payload_serializer.py +72 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/struct_serializer.py +36 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/tuple_serializer.py +36 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/uint256_serializer.py +76 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/uint_serializer.py +100 -0
- ccxt/static_dependencies/starknet/serialization/data_serializers/unit_serializer.py +32 -0
- ccxt/static_dependencies/starknet/serialization/errors.py +10 -0
- ccxt/static_dependencies/starknet/serialization/factory.py +229 -0
- ccxt/static_dependencies/starknet/serialization/function_serialization_adapter.py +110 -0
- ccxt/static_dependencies/starknet/serialization/tuple_dataclass.py +59 -0
- ccxt/static_dependencies/starknet/utils/__init__.py +0 -0
- ccxt/static_dependencies/starknet/utils/constructor_args_translator.py +86 -0
- ccxt/static_dependencies/starknet/utils/iterable.py +13 -0
- ccxt/static_dependencies/starknet/utils/schema.py +13 -0
- ccxt/static_dependencies/starknet/utils/typed_data.py +182 -0
- ccxt/static_dependencies/starkware/__init__.py +0 -0
- ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
- ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
- ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
- ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
- ccxt/static_dependencies/starkware/crypto/utils.py +63 -0
- ccxt/static_dependencies/sympy/__init__.py +0 -0
- ccxt/static_dependencies/sympy/core/__init__.py +0 -0
- ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
- ccxt/static_dependencies/sympy/external/__init__.py +0 -0
- ccxt/static_dependencies/sympy/external/gmpy.py +345 -0
- ccxt/static_dependencies/sympy/external/importtools.py +187 -0
- ccxt/static_dependencies/sympy/external/ntheory.py +637 -0
- ccxt/static_dependencies/sympy/external/pythonmpq.py +341 -0
- ccxt/static_dependencies/typing_inspect/__init__.py +0 -0
- ccxt/static_dependencies/typing_inspect/typing_inspect.py +851 -0
- ccxt/test/{test_async.py → tests_async.py} +465 -407
- ccxt/test/tests_helpers.py +285 -0
- ccxt/test/tests_init.py +39 -0
- ccxt/test/{test_sync.py → tests_sync.py} +465 -409
- ccxt/timex.py +123 -70
- ccxt/tokocrypto.py +129 -93
- ccxt/tradeogre.py +39 -25
- ccxt/upbit.py +322 -113
- ccxt/vertex.py +2983 -0
- ccxt/wavesexchange.py +227 -173
- ccxt/wazirx.py +145 -65
- ccxt/whitebit.py +533 -138
- ccxt/woo.py +1155 -295
- ccxt/woofipro.py +2716 -0
- ccxt/xt.py +4627 -0
- ccxt/yobit.py +159 -92
- ccxt/zaif.py +80 -33
- ccxt/zonda.py +140 -69
- ccxt-4.4.48.dist-info/LICENSE.txt +21 -0
- ccxt-4.4.48.dist-info/METADATA +646 -0
- ccxt-4.4.48.dist-info/RECORD +669 -0
- {ccxt-4.2.76.dist-info → ccxt-4.4.48.dist-info}/WHEEL +1 -1
- ccxt/abstract/bitbay.py +0 -47
- ccxt/abstract/bitfinex2.py +0 -139
- ccxt/abstract/hitbtc3.py +0 -115
- ccxt/async_support/bitbay.py +0 -17
- ccxt/async_support/bitfinex2.py +0 -3496
- ccxt/async_support/flowbtc.py +0 -34
- ccxt/bitbay.py +0 -17
- ccxt/bitfinex2.py +0 -3496
- ccxt/flowbtc.py +0 -34
- ccxt/hitbtc3.py +0 -16
- ccxt/pro/bitfinex2.py +0 -1081
- ccxt/test/base/__init__.py +0 -28
- ccxt/test/base/test_account.py +0 -26
- ccxt/test/base/test_balance.py +0 -56
- ccxt/test/base/test_borrow_interest.py +0 -35
- ccxt/test/base/test_borrow_rate.py +0 -32
- ccxt/test/base/test_calculate_fee.py +0 -51
- ccxt/test/base/test_crypto.py +0 -127
- ccxt/test/base/test_currency.py +0 -76
- ccxt/test/base/test_datetime.py +0 -103
- ccxt/test/base/test_decimal_to_precision.py +0 -392
- ccxt/test/base/test_deep_extend.py +0 -68
- ccxt/test/base/test_deposit_withdrawal.py +0 -50
- ccxt/test/base/test_exchange_datetime_functions.py +0 -76
- ccxt/test/base/test_funding_rate_history.py +0 -29
- ccxt/test/base/test_last_price.py +0 -32
- ccxt/test/base/test_ledger_entry.py +0 -45
- ccxt/test/base/test_ledger_item.py +0 -48
- ccxt/test/base/test_leverage_tier.py +0 -33
- ccxt/test/base/test_margin_mode.py +0 -24
- ccxt/test/base/test_margin_modification.py +0 -35
- ccxt/test/base/test_market.py +0 -190
- ccxt/test/base/test_number.py +0 -411
- ccxt/test/base/test_ohlcv.py +0 -32
- ccxt/test/base/test_open_interest.py +0 -32
- ccxt/test/base/test_order.py +0 -64
- ccxt/test/base/test_order_book.py +0 -63
- ccxt/test/base/test_position.py +0 -60
- ccxt/test/base/test_shared_methods.py +0 -345
- ccxt/test/base/test_status.py +0 -24
- ccxt/test/base/test_throttle.py +0 -126
- ccxt/test/base/test_ticker.py +0 -86
- ccxt/test/base/test_trade.py +0 -47
- ccxt/test/base/test_trading_fee.py +0 -26
- ccxt/test/base/test_transaction.py +0 -39
- ccxt-4.2.76.dist-info/METADATA +0 -626
- ccxt-4.2.76.dist-info/RECORD +0 -534
- {ccxt-4.2.76.dist-info → ccxt-4.4.48.dist-info}/top_level.txt +0 -0
@@ -1,273 +1,39 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
import argparse
|
4
|
-
import json
|
5
|
-
# import logging
|
6
|
-
import os
|
7
|
-
import sys
|
8
|
-
from traceback import format_tb, format_exception
|
9
|
-
|
10
|
-
import importlib # noqa: E402
|
11
|
-
import re
|
12
|
-
|
13
|
-
# ------------------------------------------------------------------------------
|
14
|
-
# logging.basicConfig(level=logging.INFO)
|
15
|
-
# ------------------------------------------------------------------------------
|
16
|
-
DIR_NAME = os.path.dirname(os.path.abspath(__file__))
|
17
|
-
root = os.path.dirname(os.path.dirname(DIR_NAME))
|
18
|
-
sys.path.append(root)
|
19
|
-
|
20
|
-
import ccxt.async_support as ccxt # noqa: E402
|
21
|
-
import ccxt.pro as ccxtpro # noqa: E402
|
22
|
-
|
23
|
-
# ------------------------------------------------------------------------------
|
24
3
|
import asyncio
|
25
|
-
# from typing import Optional
|
26
|
-
# from typing import List
|
27
|
-
from ccxt.base.errors import NotSupported
|
28
|
-
from ccxt.base.errors import ProxyError
|
29
|
-
from ccxt.base.errors import OperationFailed
|
30
|
-
# from ccxt.base.errors import ExchangeError
|
31
|
-
from ccxt.base.errors import ExchangeNotAvailable
|
32
|
-
from ccxt.base.errors import OnMaintenance
|
33
|
-
from ccxt.base.errors import AuthenticationError
|
34
|
-
|
35
|
-
# ------------------------------------------------------------------------------
|
36
|
-
|
37
|
-
class Argv(object):
|
38
|
-
id_tests = False
|
39
|
-
static_tests = False
|
40
|
-
ws_tests = False
|
41
|
-
request_tests = False
|
42
|
-
response_tests = False
|
43
|
-
token_bucket = False
|
44
|
-
sandbox = False
|
45
|
-
privateOnly = False
|
46
|
-
private = False
|
47
|
-
ws = False
|
48
|
-
verbose = False
|
49
|
-
nonce = None
|
50
|
-
exchange = None
|
51
|
-
symbol = None
|
52
|
-
info = False
|
53
|
-
pass
|
54
|
-
|
55
|
-
|
56
|
-
argv = Argv()
|
57
|
-
parser = argparse.ArgumentParser()
|
58
|
-
parser.add_argument('--token_bucket', action='store_true', help='enable token bucket experimental test')
|
59
|
-
parser.add_argument('--sandbox', action='store_true', help='enable sandbox mode')
|
60
|
-
parser.add_argument('--privateOnly', action='store_true', help='run private tests only')
|
61
|
-
parser.add_argument('--private', action='store_true', help='run private tests')
|
62
|
-
parser.add_argument('--verbose', action='store_true', help='enable verbose output')
|
63
|
-
parser.add_argument('--ws', action='store_true', help='websockets version')
|
64
|
-
parser.add_argument('--info', action='store_true', help='enable info output')
|
65
|
-
parser.add_argument('--static', action='store_true', help='run static tests')
|
66
|
-
parser.add_argument('--useProxy', action='store_true', help='run static tests')
|
67
|
-
parser.add_argument('--idTests', action='store_true', help='run brokerId tests')
|
68
|
-
parser.add_argument('--responseTests', action='store_true', help='run response tests')
|
69
|
-
parser.add_argument('--requestTests', action='store_true', help='run response tests')
|
70
|
-
parser.add_argument('--nonce', type=int, help='integer')
|
71
|
-
parser.add_argument('exchange', type=str, help='exchange id in lowercase', nargs='?')
|
72
|
-
parser.add_argument('symbol', type=str, help='symbol in uppercase', nargs='?')
|
73
|
-
parser.parse_args(namespace=argv)
|
74
|
-
|
75
|
-
# ------------------------------------------------------------------------------
|
76
|
-
|
77
|
-
path = os.path.dirname(ccxt.__file__)
|
78
|
-
if 'site-packages' in os.path.dirname(ccxt.__file__):
|
79
|
-
raise Exception("You are running test_async.py/test.py against a globally-installed version of the library! It was previously installed into your site-packages folder by pip or pip3. To ensure testing against the local folder uninstall it first with pip uninstall ccxt or pip3 uninstall ccxt")
|
80
|
-
|
81
|
-
# ------------------------------------------------------------------------------
|
82
|
-
|
83
|
-
Error = Exception
|
84
4
|
|
85
|
-
# # print an error string
|
86
|
-
# def dump_error(*args):
|
87
|
-
# string = ' '.join([str(arg) for arg in args])
|
88
|
-
# print(string)
|
89
|
-
# sys.stderr.write(string + "\n")
|
90
|
-
# sys.stderr.flush()
|
91
5
|
|
6
|
+
from tests_helpers import AuthenticationError, NotSupported, InvalidProxySettings, ExchangeNotAvailable, OperationFailed, OnMaintenance, get_cli_arg_value, get_root_dir, is_sync, dump, json_parse, json_stringify, convert_ascii, io_file_exists, io_file_read, io_dir_read, call_method, call_method_sync, call_exchange_method_dynamically, call_exchange_method_dynamically_sync, get_root_exception, exception_message, exit_script, get_exchange_prop, set_exchange_prop, init_exchange, get_test_files_sync, get_test_files, set_fetch_response, is_null_value, close, get_env_vars, get_lang, get_ext # noqa: F401
|
92
7
|
|
93
|
-
|
94
|
-
|
95
|
-
exit(1) # unrecoverable crash
|
96
|
-
|
97
|
-
|
98
|
-
sys.excepthook = handle_all_unhandled_exceptions
|
99
|
-
# ------------------------------------------------------------------------------
|
100
|
-
|
101
|
-
# non-transpiled part, but shared names among langs
|
102
|
-
|
103
|
-
is_synchronous = 'async' not in os.path.basename(__file__)
|
104
|
-
|
105
|
-
rootDir = DIR_NAME + '/../../../'
|
106
|
-
rootDirForSkips = DIR_NAME + '/../../../'
|
107
|
-
envVars = os.environ
|
108
|
-
LOG_CHARS_LENGTH = 10000
|
109
|
-
ext = 'py'
|
110
|
-
proxyTestFileName = 'proxies'
|
111
|
-
|
112
|
-
|
113
|
-
def get_cli_arg_value(arg):
|
114
|
-
arg_exists = getattr(argv, arg) if hasattr(argv, arg) else False
|
115
|
-
with_hyphen = '--' + arg
|
116
|
-
arg_exists_with_hyphen = getattr(argv, with_hyphen) if hasattr(argv, with_hyphen) else False
|
117
|
-
without_hyphen = arg.replace('--', '')
|
118
|
-
arg_exists_wo_hyphen = getattr(argv, without_hyphen) if hasattr(argv, without_hyphen) else False
|
119
|
-
return arg_exists or arg_exists_with_hyphen or arg_exists_wo_hyphen
|
120
|
-
|
121
|
-
isWsTests = get_cli_arg_value('--ws')
|
122
|
-
|
123
|
-
|
124
|
-
class baseMainTestClass():
|
125
|
-
lang = 'PY'
|
126
|
-
is_synchronous = is_synchronous
|
8
|
+
class testMainClass:
|
9
|
+
id_tests = False
|
127
10
|
request_tests_failed = False
|
128
11
|
response_tests_failed = False
|
129
|
-
|
12
|
+
request_tests = False
|
130
13
|
ws_tests = False
|
14
|
+
response_tests = False
|
15
|
+
static_tests = False
|
16
|
+
info = False
|
17
|
+
verbose = False
|
18
|
+
debug = False
|
19
|
+
private_test = False
|
20
|
+
private_test_only = False
|
131
21
|
load_keys = False
|
22
|
+
sandbox = False
|
23
|
+
only_specific_tests = []
|
24
|
+
skipped_settings_for_exchange = {}
|
132
25
|
skipped_methods = {}
|
133
|
-
|
26
|
+
checked_public_tests = {}
|
134
27
|
test_files = {}
|
135
28
|
public_tests = {}
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
ext = ext
|
140
|
-
root_dir_for_skips = rootDirForSkips
|
141
|
-
only_specific_tests = []
|
142
|
-
proxy_test_file_name = proxyTestFileName
|
143
|
-
pass
|
144
|
-
|
145
|
-
|
146
|
-
def dump(*args):
|
147
|
-
print(' '.join([str(arg) for arg in args]))
|
148
|
-
|
149
|
-
|
150
|
-
def convert_ascii(str):
|
151
|
-
return str # stub
|
152
|
-
|
153
|
-
def json_parse(elem):
|
154
|
-
return json.loads(elem)
|
155
|
-
|
156
|
-
|
157
|
-
def json_stringify(elem):
|
158
|
-
return json.dumps(elem)
|
159
|
-
|
160
|
-
|
161
|
-
def convert_to_snake_case(content):
|
162
|
-
res = re.sub(r'(?<!^)(?=[A-Z])', '_', content).lower()
|
163
|
-
return res.replace('o_h_l_c_v', 'ohlcv')
|
164
|
-
|
165
|
-
|
166
|
-
def get_test_name(methodName):
|
167
|
-
# stub
|
168
|
-
return methodName
|
169
|
-
|
170
|
-
|
171
|
-
def io_file_exists(path):
|
172
|
-
return os.path.isfile(path)
|
173
|
-
|
174
|
-
|
175
|
-
def io_file_read(path, decode=True):
|
176
|
-
fs = open(path, "r", encoding="utf-8")
|
177
|
-
content = fs.read()
|
178
|
-
if decode:
|
179
|
-
return json.loads(content)
|
180
|
-
else:
|
181
|
-
return content
|
182
|
-
|
183
|
-
|
184
|
-
def io_dir_read(path):
|
185
|
-
return os.listdir(path)
|
186
|
-
|
187
|
-
|
188
|
-
async def call_method(test_files, methodName, exchange, skippedProperties, args):
|
189
|
-
methodNameToCall = 'test_' + convert_to_snake_case(methodName)
|
190
|
-
return await getattr(test_files[methodName], methodNameToCall)(exchange, skippedProperties, *args)
|
29
|
+
ext = ''
|
30
|
+
lang = ''
|
31
|
+
proxy_test_file_name = 'proxies'
|
191
32
|
|
192
|
-
|
193
|
-
|
194
|
-
return await getattr(exchange, methodName)(*args)
|
195
|
-
|
196
|
-
async def call_overriden_method(exchange, methodName, args):
|
197
|
-
# needed for php
|
198
|
-
return await call_exchange_method_dynamically(exchange, methodName, args)
|
199
|
-
|
200
|
-
def exception_message(exc):
|
201
|
-
message = '[' + type(exc).__name__ + '] ' + "".join(format_exception(type(exc), exc, exc.__traceback__, limit=6))
|
202
|
-
if len(message) > LOG_CHARS_LENGTH:
|
203
|
-
# Accessing out of range element causes error
|
204
|
-
message = message[0:LOG_CHARS_LENGTH]
|
205
|
-
return message
|
206
|
-
|
207
|
-
|
208
|
-
def exit_script(code=0):
|
209
|
-
exit(code)
|
210
|
-
|
211
|
-
|
212
|
-
def get_exchange_prop(exchange, prop, defaultValue=None):
|
213
|
-
if hasattr(exchange, prop):
|
214
|
-
res = getattr(exchange, prop)
|
215
|
-
if res is not None and res != '':
|
216
|
-
return res
|
217
|
-
return defaultValue
|
218
|
-
|
219
|
-
|
220
|
-
def set_exchange_prop(exchange, prop, value):
|
221
|
-
setattr(exchange, prop, value)
|
222
|
-
# set snake case too
|
223
|
-
setattr(exchange, convert_to_snake_case(prop), value)
|
224
|
-
|
225
|
-
|
226
|
-
def init_exchange(exchangeId, args, is_ws=False):
|
227
|
-
if (is_ws):
|
228
|
-
return getattr(ccxtpro, exchangeId)(args)
|
229
|
-
return getattr(ccxt, exchangeId)(args)
|
230
|
-
|
231
|
-
|
232
|
-
async def get_test_files(properties, ws=False):
|
233
|
-
tests = {}
|
234
|
-
finalPropList = properties + [proxyTestFileName]
|
235
|
-
for i in range(0, len(finalPropList)):
|
236
|
-
methodName = finalPropList[i]
|
237
|
-
name_snake_case = convert_to_snake_case(methodName)
|
238
|
-
prefix = 'async' if not is_synchronous else 'sync'
|
239
|
-
dir_to_test = DIR_NAME + '/' + prefix + '/'
|
240
|
-
module_string = 'ccxt.test.' + prefix + '.test_' + name_snake_case
|
241
|
-
if (ws):
|
242
|
-
prefix = 'pro'
|
243
|
-
dir_to_test = DIR_NAME + '/../' + prefix + '/test/Exchange/'
|
244
|
-
module_string = 'ccxt.pro.test.Exchange.test_' + name_snake_case
|
245
|
-
filePathWithExt = dir_to_test + 'test_' + name_snake_case + '.py'
|
246
|
-
if (io_file_exists (filePathWithExt)):
|
247
|
-
imp = importlib.import_module(module_string)
|
248
|
-
tests[methodName] = imp # getattr(imp, finalName)
|
249
|
-
return tests
|
250
|
-
|
251
|
-
async def close(exchange):
|
252
|
-
if (not is_synchronous and hasattr(exchange, 'close')):
|
253
|
-
await exchange.close()
|
254
|
-
|
255
|
-
def is_null_value(value):
|
256
|
-
return value is None
|
257
|
-
|
258
|
-
def set_fetch_response(exchange: ccxt.Exchange, data):
|
259
|
-
async def fetch(url, method='GET', headers=None, body=None):
|
260
|
-
return data
|
261
|
-
exchange.fetch = fetch
|
262
|
-
return exchange
|
263
|
-
|
264
|
-
# *********************************
|
265
|
-
# ***** AUTO-TRANSPILER-START *****
|
266
|
-
class testMainClass(baseMainTestClass):
|
267
|
-
def parse_cli_args(self):
|
268
|
-
self.response_tests = get_cli_arg_value('--responseTests')
|
33
|
+
def parse_cli_args_and_props(self):
|
34
|
+
self.response_tests = get_cli_arg_value('--responseTests') or get_cli_arg_value('--response')
|
269
35
|
self.id_tests = get_cli_arg_value('--idTests')
|
270
|
-
self.request_tests = get_cli_arg_value('--requestTests')
|
36
|
+
self.request_tests = get_cli_arg_value('--requestTests') or get_cli_arg_value('--request')
|
271
37
|
self.info = get_cli_arg_value('--info')
|
272
38
|
self.verbose = get_cli_arg_value('--verbose')
|
273
39
|
self.debug = get_cli_arg_value('--debug')
|
@@ -276,9 +42,11 @@ class testMainClass(baseMainTestClass):
|
|
276
42
|
self.sandbox = get_cli_arg_value('--sandbox')
|
277
43
|
self.load_keys = get_cli_arg_value('--loadKeys')
|
278
44
|
self.ws_tests = get_cli_arg_value('--ws')
|
45
|
+
self.lang = get_lang()
|
46
|
+
self.ext = get_ext()
|
279
47
|
|
280
|
-
async def init(self, exchange_id, symbol_argv):
|
281
|
-
self.
|
48
|
+
async def init(self, exchange_id, symbol_argv, method_argv):
|
49
|
+
self.parse_cli_args_and_props()
|
282
50
|
if self.request_tests and self.response_tests:
|
283
51
|
await self.run_static_request_tests(exchange_id, symbol_argv)
|
284
52
|
await self.run_static_response_tests(exchange_id, symbol_argv)
|
@@ -292,13 +60,14 @@ class testMainClass(baseMainTestClass):
|
|
292
60
|
if self.id_tests:
|
293
61
|
await self.run_broker_id_tests()
|
294
62
|
return
|
295
|
-
|
296
|
-
|
63
|
+
new_line = '\n'
|
64
|
+
dump(new_line + '' + new_line + '' + '[INFO] TESTING ', self.ext, {
|
297
65
|
'exchange': exchange_id,
|
298
|
-
'symbol':
|
66
|
+
'symbol': symbol_argv,
|
67
|
+
'method': method_argv,
|
299
68
|
'isWs': self.ws_tests,
|
300
|
-
|
301
|
-
|
69
|
+
'useProxy': get_cli_arg_value('--useProxy'),
|
70
|
+
}, new_line)
|
302
71
|
exchange_args = {
|
303
72
|
'verbose': self.verbose,
|
304
73
|
'debug': self.debug,
|
@@ -311,30 +80,30 @@ class testMainClass(baseMainTestClass):
|
|
311
80
|
await self.import_files(exchange)
|
312
81
|
assert len(list(self.test_files.keys())) > 0, 'Test files were not loaded' # ensure test files are found & filled
|
313
82
|
self.expand_settings(exchange)
|
314
|
-
|
315
|
-
await self.start_test(exchange,
|
83
|
+
self.check_if_specific_test_is_chosen(method_argv)
|
84
|
+
await self.start_test(exchange, symbol_argv)
|
316
85
|
exit_script(0) # needed to be explicitly finished for WS tests
|
317
86
|
|
318
|
-
def check_if_specific_test_is_chosen(self,
|
319
|
-
if
|
87
|
+
def check_if_specific_test_is_chosen(self, method_argv):
|
88
|
+
if method_argv is not None:
|
320
89
|
test_file_names = list(self.test_files.keys())
|
321
|
-
possible_method_names =
|
90
|
+
possible_method_names = method_argv.split(',') # i.e. `test.ts binance fetchBalance,fetchDeposits`
|
322
91
|
if len(possible_method_names) >= 1:
|
323
92
|
for i in range(0, len(test_file_names)):
|
324
93
|
test_file_name = test_file_names[i]
|
325
94
|
for j in range(0, len(possible_method_names)):
|
326
95
|
method_name = possible_method_names[j]
|
96
|
+
method_name = method_name.replace('()', '')
|
327
97
|
if test_file_name == method_name:
|
328
98
|
self.only_specific_tests.append(test_file_name)
|
329
|
-
# if method names were found, then remove them from symbolArgv
|
330
|
-
if len(self.only_specific_tests) > 0:
|
331
|
-
return None
|
332
|
-
return symbol_argv
|
333
99
|
|
334
100
|
async def import_files(self, exchange):
|
335
101
|
properties = list(exchange.has.keys())
|
336
102
|
properties.append('loadMarkets')
|
337
|
-
|
103
|
+
if is_sync():
|
104
|
+
self.test_files = get_test_files_sync(properties, self.ws_tests)
|
105
|
+
else:
|
106
|
+
self.test_files = await get_test_files(properties, self.ws_tests)
|
338
107
|
|
339
108
|
def load_credentials_from_env(self, exchange):
|
340
109
|
exchange_id = exchange.id
|
@@ -346,14 +115,15 @@ class testMainClass(baseMainTestClass):
|
|
346
115
|
if is_required and get_exchange_prop(exchange, credential) is None:
|
347
116
|
full_key = exchange_id + '_' + credential
|
348
117
|
credential_env_name = full_key.upper() # example: KRAKEN_APIKEY
|
349
|
-
|
118
|
+
env_vars = get_env_vars()
|
119
|
+
credential_value = env_vars[credential_env_name] if (credential_env_name in env_vars) else None
|
350
120
|
if credential_value:
|
351
121
|
set_exchange_prop(exchange, credential, credential_value)
|
352
122
|
|
353
123
|
def expand_settings(self, exchange):
|
354
124
|
exchange_id = exchange.id
|
355
|
-
keys_global =
|
356
|
-
keys_local =
|
125
|
+
keys_global = get_root_dir() + 'keys.json'
|
126
|
+
keys_local = get_root_dir() + 'keys.local.json'
|
357
127
|
keys_global_exists = io_file_exists(keys_global)
|
358
128
|
keys_local_exists = io_file_exists(keys_local)
|
359
129
|
global_settings = io_file_read(keys_global) if keys_global_exists else {}
|
@@ -376,9 +146,10 @@ class testMainClass(baseMainTestClass):
|
|
376
146
|
if self.load_keys:
|
377
147
|
self.load_credentials_from_env(exchange)
|
378
148
|
# skipped tests
|
379
|
-
skipped_file =
|
149
|
+
skipped_file = get_root_dir() + 'skip-tests.json'
|
380
150
|
skipped_settings = io_file_read(skipped_file)
|
381
|
-
skipped_settings_for_exchange = exchange.safe_value(skipped_settings, exchange_id, {})
|
151
|
+
self.skipped_settings_for_exchange = exchange.safe_value(skipped_settings, exchange_id, {})
|
152
|
+
skipped_settings_for_exchange = self.skipped_settings_for_exchange
|
382
153
|
# others
|
383
154
|
timeout = exchange.safe_value(skipped_settings_for_exchange, 'timeout')
|
384
155
|
if timeout is not None:
|
@@ -401,31 +172,18 @@ class testMainClass(baseMainTestClass):
|
|
401
172
|
res += ' '
|
402
173
|
return message + res
|
403
174
|
|
404
|
-
def exchange_hint(self, exchange, market=None):
|
405
|
-
market_type = exchange.safe_string_2(exchange.options, 'defaultType', 'type', '')
|
406
|
-
market_sub_type = exchange.safe_string_2(exchange.options, 'defaultSubType', 'subType')
|
407
|
-
if market is not None:
|
408
|
-
market_type = market['type']
|
409
|
-
if market['linear']:
|
410
|
-
market_sub_type = 'linear'
|
411
|
-
elif market['inverse']:
|
412
|
-
market_sub_type = 'inverse'
|
413
|
-
elif exchange.safe_value(market, 'quanto'):
|
414
|
-
market_sub_type = 'quanto'
|
415
|
-
is_ws = ('ws' in exchange.has)
|
416
|
-
ws_flag = '(WS)' if is_ws else ''
|
417
|
-
result = exchange.id + ' ' + ws_flag + ' ' + market_type
|
418
|
-
if market_sub_type is not None:
|
419
|
-
result = result + ' [subType: ' + market_sub_type + '] '
|
420
|
-
return result
|
421
|
-
|
422
175
|
async def test_method(self, method_name, exchange, args, is_public):
|
176
|
+
# todo: temporary skip for c#
|
177
|
+
if 'OrderBook' in method_name and self.ext == 'cs':
|
178
|
+
exchange.options['checksum'] = False
|
423
179
|
# todo: temporary skip for php
|
424
180
|
if 'OrderBook' in method_name and self.ext == 'php':
|
425
181
|
return
|
182
|
+
skipped_properties_for_method = self.get_skips(exchange, method_name)
|
426
183
|
is_load_markets = (method_name == 'loadMarkets')
|
427
184
|
is_fetch_currencies = (method_name == 'fetchCurrencies')
|
428
185
|
is_proxy_test = (method_name == self.proxy_test_file_name)
|
186
|
+
is_feature_test = (method_name == 'features')
|
429
187
|
# if this is a private test, and the implementation was already tested in public, then no need to re-test it in private test (exception is fetchCurrencies, because our approach in base exchange)
|
430
188
|
if not is_public and (method_name in self.checked_public_tests) and not is_fetch_currencies:
|
431
189
|
return
|
@@ -433,31 +191,46 @@ class testMainClass(baseMainTestClass):
|
|
433
191
|
supported_by_exchange = (method_name in exchange.has) and exchange.has[method_name]
|
434
192
|
if not is_load_markets and (len(self.only_specific_tests) > 0 and not exchange.in_array(method_name, self.only_specific_tests)):
|
435
193
|
skip_message = '[INFO] IGNORED_TEST'
|
436
|
-
elif not is_load_markets and not supported_by_exchange and not is_proxy_test:
|
194
|
+
elif not is_load_markets and not supported_by_exchange and not is_proxy_test and not is_feature_test:
|
437
195
|
skip_message = '[INFO] UNSUPPORTED_TEST' # keep it aligned with the longest message
|
438
|
-
elif
|
196
|
+
elif isinstance(skipped_properties_for_method, str):
|
439
197
|
skip_message = '[INFO] SKIPPED_TEST'
|
440
198
|
elif not (method_name in self.test_files):
|
441
199
|
skip_message = '[INFO] UNIMPLEMENTED_TEST'
|
442
200
|
# exceptionally for `loadMarkets` call, we call it before it's even checked for "skip" as we need it to be called anyway (but can skip "test.loadMarket" for it)
|
443
201
|
if is_load_markets:
|
444
202
|
await exchange.load_markets(True)
|
203
|
+
name = exchange.id
|
445
204
|
if skip_message:
|
446
205
|
if self.info:
|
447
|
-
dump(self.add_padding(skip_message, 25),
|
206
|
+
dump(self.add_padding(skip_message, 25), name, method_name)
|
448
207
|
return
|
449
208
|
if self.info:
|
450
|
-
args_stringified = '(' +
|
451
|
-
dump(self.add_padding('[INFO] TESTING', 25),
|
452
|
-
|
453
|
-
|
209
|
+
args_stringified = '(' + exchange.json(args) + ')' # args.join() breaks when we provide a list of symbols or multidimensional array; "args.toString()" breaks bcz of "array to string conversion"
|
210
|
+
dump(self.add_padding('[INFO] TESTING', 25), name, method_name, args_stringified)
|
211
|
+
if is_sync():
|
212
|
+
call_method_sync(self.test_files, method_name, exchange, skipped_properties_for_method, args)
|
213
|
+
else:
|
214
|
+
await call_method(self.test_files, method_name, exchange, skipped_properties_for_method, args)
|
215
|
+
if self.info:
|
216
|
+
dump(self.add_padding('[INFO] TESTING DONE', 25), name, method_name)
|
217
|
+
# add to the list of successed tests
|
454
218
|
if is_public:
|
455
219
|
self.checked_public_tests[method_name] = True
|
456
220
|
return
|
457
221
|
|
458
222
|
def get_skips(self, exchange, method_name):
|
459
|
-
|
460
|
-
|
223
|
+
final_skips = {}
|
224
|
+
# check the exact method (i.e. `fetchTrades`) and language-specific (i.e. `fetchTrades.php`)
|
225
|
+
method_names = [method_name, method_name + '.' + self.ext]
|
226
|
+
for i in range(0, len(method_names)):
|
227
|
+
m_name = method_names[i]
|
228
|
+
if m_name in self.skipped_methods:
|
229
|
+
# if whole method is skipped, by assigning a string to it, i.e. "fetchOrders":"blabla"
|
230
|
+
if isinstance(self.skipped_methods[m_name], str):
|
231
|
+
return self.skipped_methods[m_name]
|
232
|
+
else:
|
233
|
+
final_skips = exchange.deep_extend(final_skips, self.skipped_methods[m_name])
|
461
234
|
# get "object-specific" skips
|
462
235
|
object_skips = {
|
463
236
|
'orderBook': ['fetchOrderBook', 'fetchOrderBooks', 'fetchL2OrderBook', 'watchOrderBook', 'watchOrderBookForSymbols'],
|
@@ -473,9 +246,21 @@ class testMainClass(baseMainTestClass):
|
|
473
246
|
object_name = object_names[i]
|
474
247
|
object_methods = object_skips[object_name]
|
475
248
|
if exchange.in_array(method_name, object_methods):
|
249
|
+
# if whole object is skipped, by assigning a string to it, i.e. "orderBook":"blabla"
|
250
|
+
if (object_name in self.skipped_methods) and (isinstance(self.skipped_methods[object_name], str)):
|
251
|
+
return self.skipped_methods[object_name]
|
476
252
|
extra_skips = exchange.safe_dict(self.skipped_methods, object_name, {})
|
477
|
-
|
478
|
-
|
253
|
+
final_skips = exchange.deep_extend(final_skips, extra_skips)
|
254
|
+
# extend related skips
|
255
|
+
# - if 'timestamp' is skipped, we should do so for 'datetime' too
|
256
|
+
# - if 'bid' is skipped, skip 'ask' too
|
257
|
+
if ('timestamp' in final_skips) and not ('datetime' in final_skips):
|
258
|
+
final_skips['datetime'] = final_skips['timestamp']
|
259
|
+
if ('bid' in final_skips) and not ('ask' in final_skips):
|
260
|
+
final_skips['ask'] = final_skips['bid']
|
261
|
+
if ('baseVolume' in final_skips) and not ('quoteVolume' in final_skips):
|
262
|
+
final_skips['quoteVolume'] = final_skips['baseVolume']
|
263
|
+
return final_skips
|
479
264
|
|
480
265
|
async def test_safe(self, method_name, exchange, args=[], is_public=False):
|
481
266
|
# `testSafe` method does not throw an exception, instead mutes it. The reason we
|
@@ -484,12 +269,13 @@ class testMainClass(baseMainTestClass):
|
|
484
269
|
# formatted message "[TEST_FAILURE] ..." and that output is then regex-matched by
|
485
270
|
# run-tests.js, so the exceptions are still printed out to console from there.
|
486
271
|
max_retries = 3
|
487
|
-
args_stringified = exchange.json(args) # args.join() breaks when we provide a list of symbols
|
272
|
+
args_stringified = exchange.json(args) # args.join() breaks when we provide a list of symbols or multidimensional array; "args.toString()" breaks bcz of "array to string conversion"
|
488
273
|
for i in range(0, max_retries):
|
489
274
|
try:
|
490
275
|
await self.test_method(method_name, exchange, args, is_public)
|
491
276
|
return True
|
492
|
-
except Exception as
|
277
|
+
except Exception as ex:
|
278
|
+
e = get_root_exception(ex)
|
493
279
|
is_load_markets = (method_name == 'loadMarkets')
|
494
280
|
is_auth_error = (isinstance(e, AuthenticationError))
|
495
281
|
is_not_supported = (isinstance(e, NotSupported))
|
@@ -521,36 +307,37 @@ class testMainClass(baseMainTestClass):
|
|
521
307
|
return_success = True
|
522
308
|
# output the message
|
523
309
|
fail_type = '[TEST_FAILURE]' if should_fail else '[TEST_WARNING]'
|
524
|
-
dump(fail_type, 'Method could not be tested due to a repeated Network/Availability issues', ' | ',
|
310
|
+
dump(fail_type, 'Method could not be tested due to a repeated Network/Availability issues', ' | ', exchange.id, method_name, args_stringified, exception_message(e))
|
525
311
|
return return_success
|
526
312
|
else:
|
527
313
|
# wait and retry again
|
528
314
|
# (increase wait time on every retry)
|
529
|
-
await exchange.sleep(i * 1000)
|
315
|
+
await exchange.sleep((i + 1) * 1000)
|
530
316
|
continue
|
531
317
|
else:
|
532
318
|
# if it's loadMarkets, then fail test, because it's mandatory for tests
|
533
319
|
if is_load_markets:
|
534
|
-
dump('[TEST_FAILURE]', 'Exchange can not load markets', exception_message(e),
|
320
|
+
dump('[TEST_FAILURE]', 'Exchange can not load markets', exception_message(e), exchange.id, method_name, args_stringified)
|
535
321
|
return False
|
536
322
|
# if the specific arguments to the test method throws "NotSupported" exception
|
537
323
|
# then let's don't fail the test
|
538
324
|
if is_not_supported:
|
539
325
|
if self.info:
|
540
|
-
dump('[INFO] NOT_SUPPORTED', exception_message(e),
|
326
|
+
dump('[INFO] NOT_SUPPORTED', exception_message(e), exchange.id, method_name, args_stringified)
|
541
327
|
return True
|
542
328
|
# If public test faces authentication error, we don't break (see comments under `testSafe` method)
|
543
329
|
if is_public and is_auth_error:
|
544
330
|
if self.info:
|
545
|
-
dump('[INFO]', 'Authentication problem for public method', exception_message(e),
|
331
|
+
dump('[INFO]', 'Authentication problem for public method', exception_message(e), exchange.id, method_name, args_stringified)
|
546
332
|
return True
|
547
333
|
else:
|
548
|
-
dump('[TEST_FAILURE]', exception_message(e),
|
334
|
+
dump('[TEST_FAILURE]', exception_message(e), exchange.id, method_name, args_stringified)
|
549
335
|
return False
|
550
336
|
return True
|
551
337
|
|
552
338
|
async def run_public_tests(self, exchange, symbol):
|
553
339
|
tests = {
|
340
|
+
'features': [],
|
554
341
|
'fetchCurrencies': [],
|
555
342
|
'fetchTicker': [symbol],
|
556
343
|
'fetchTickers': [symbol],
|
@@ -567,10 +354,14 @@ class testMainClass(baseMainTestClass):
|
|
567
354
|
if self.ws_tests:
|
568
355
|
tests = {
|
569
356
|
'watchOHLCV': [symbol],
|
357
|
+
'watchOHLCVForSymbols': [symbol],
|
570
358
|
'watchTicker': [symbol],
|
571
359
|
'watchTickers': [symbol],
|
360
|
+
'watchBidsAsks': [symbol],
|
572
361
|
'watchOrderBook': [symbol],
|
362
|
+
'watchOrderBookForSymbols': [[symbol]],
|
573
363
|
'watchTrades': [symbol],
|
364
|
+
'watchTradesForSymbols': [[symbol]],
|
574
365
|
}
|
575
366
|
market = exchange.market(symbol)
|
576
367
|
is_spot = market['spot']
|
@@ -607,34 +398,26 @@ class testMainClass(baseMainTestClass):
|
|
607
398
|
test_prefix_string = 'PUBLIC_TESTS' if is_public_test else 'PRIVATE_TESTS'
|
608
399
|
if len(failed_methods):
|
609
400
|
errors_string = ', '.join(failed_methods)
|
610
|
-
dump('[TEST_FAILURE]',
|
401
|
+
dump('[TEST_FAILURE]', exchange.id, test_prefix_string, 'Failed methods : ' + errors_string)
|
611
402
|
if self.info:
|
612
|
-
dump(self.add_padding('[INFO] END ' + test_prefix_string + ' ' +
|
403
|
+
dump(self.add_padding('[INFO] END ' + test_prefix_string + ' ' + exchange.id, 25))
|
613
404
|
|
614
405
|
async def load_exchange(self, exchange):
|
615
406
|
result = await self.test_safe('loadMarkets', exchange, [], True)
|
616
407
|
if not result:
|
617
408
|
return False
|
618
|
-
symbols = ['BTC/USDT', 'BTC/USDC', 'BTC/CNY', 'BTC/USD', 'BTC/EUR', 'BTC/ETH', 'ETH/BTC', 'BTC/JPY', 'ETH/EUR', 'ETH/JPY', 'ETH/CNY', 'ETH/USD', 'LTC/CNY', 'DASH/BTC', 'DOGE/BTC', 'BTC/AUD', 'BTC/PLN', 'USD/SLL', 'BTC/RUB', 'BTC/UAH', 'LTC/BTC', 'EUR/USD']
|
619
|
-
result_symbols = []
|
620
|
-
exchange_specific_symbols = exchange.symbols
|
621
|
-
for i in range(0, len(exchange_specific_symbols)):
|
622
|
-
symbol = exchange_specific_symbols[i]
|
623
|
-
if exchange.in_array(symbol, symbols):
|
624
|
-
result_symbols.append(symbol)
|
625
|
-
result_msg = ''
|
626
|
-
result_length = len(result_symbols)
|
627
409
|
exchange_symbols_length = len(exchange.symbols)
|
628
|
-
|
629
|
-
if exchange_symbols_length > result_length:
|
630
|
-
result_msg = ', '.join(result_symbols) + ' + more...'
|
631
|
-
else:
|
632
|
-
result_msg = ', '.join(result_symbols)
|
633
|
-
dump('[INFO:MAIN] Exchange loaded', exchange_symbols_length, 'symbols', result_msg)
|
410
|
+
dump('[INFO:MAIN] Exchange loaded', exchange_symbols_length, 'symbols')
|
634
411
|
return True
|
635
412
|
|
636
413
|
def get_test_symbol(self, exchange, is_spot, symbols):
|
637
414
|
symbol = None
|
415
|
+
preferred_spot_symbol = exchange.safe_string(self.skipped_settings_for_exchange, 'preferredSpotSymbol')
|
416
|
+
preferred_swap_symbol = exchange.safe_string(self.skipped_settings_for_exchange, 'preferredSwapSymbol')
|
417
|
+
if is_spot and preferred_spot_symbol:
|
418
|
+
return preferred_spot_symbol
|
419
|
+
elif not is_spot and preferred_swap_symbol:
|
420
|
+
return preferred_swap_symbol
|
638
421
|
for i in range(0, len(symbols)):
|
639
422
|
s = symbols[i]
|
640
423
|
market = exchange.safe_value(exchange.markets, s)
|
@@ -669,9 +452,9 @@ class testMainClass(baseMainTestClass):
|
|
669
452
|
|
670
453
|
def get_valid_symbol(self, exchange, spot=True):
|
671
454
|
current_type_markets = self.get_markets_from_exchange(exchange, spot)
|
672
|
-
codes = ['BTC', 'ETH', 'XRP', 'LTC', '
|
673
|
-
spot_symbols = ['BTC/USDT', 'BTC/USDC', 'BTC/USD', 'BTC/CNY', 'BTC/EUR', 'BTC/ETH', 'ETH/
|
674
|
-
swap_symbols = ['BTC/USDT:USDT', 'BTC/USDC:USDC', 'BTC/USD:USD', 'ETH/USDT:USDT', 'ETH/
|
455
|
+
codes = ['BTC', 'ETH', 'XRP', 'LTC', 'BNB', 'DASH', 'DOGE', 'ETC', 'TRX', 'USDT', 'USDC', 'USD', 'EUR', 'TUSD', 'CNY', 'JPY', 'BRL']
|
456
|
+
spot_symbols = ['BTC/USDT', 'BTC/USDC', 'BTC/USD', 'BTC/CNY', 'BTC/EUR', 'BTC/AUD', 'BTC/BRL', 'BTC/JPY', 'ETH/USDT', 'ETH/USDC', 'ETH/USD', 'ETH/CNY', 'ETH/EUR', 'ETH/AUD', 'ETH/BRL', 'ETH/JPY', 'EUR/USDT', 'EUR/USD', 'EUR/USDC', 'USDT/EUR', 'USD/EUR', 'USDC/EUR', 'BTC/ETH', 'ETH/BTC']
|
457
|
+
swap_symbols = ['BTC/USDT:USDT', 'BTC/USDC:USDC', 'BTC/USD:USD', 'ETH/USDT:USDT', 'ETH/USDC:USDC', 'ETH/USD:USD', 'BTC/USD:BTC', 'ETH/USD:ETH']
|
675
458
|
target_symbols = spot_symbols if spot else swap_symbols
|
676
459
|
symbol = self.get_test_symbol(exchange, spot, target_symbols)
|
677
460
|
# if symbols wasn't found from above hardcoded list, then try to locate any symbol which has our target hardcoded 'base' code
|
@@ -779,6 +562,8 @@ class testMainClass(baseMainTestClass):
|
|
779
562
|
'fetchBorrowRateHistory': [code],
|
780
563
|
'fetchLedgerEntry': [code],
|
781
564
|
}
|
565
|
+
if get_cli_arg_value('--fundedTests'):
|
566
|
+
tests['createOrder'] = [symbol]
|
782
567
|
if self.ws_tests:
|
783
568
|
tests = {
|
784
569
|
'watchBalance': [code],
|
@@ -809,7 +594,7 @@ class testMainClass(baseMainTestClass):
|
|
809
594
|
# these tests should be synchronously executed, because of conflicting nature of proxy settings
|
810
595
|
proxy_test_name = self.proxy_test_file_name
|
811
596
|
# todo: temporary skip for sync py
|
812
|
-
if self.ext == 'py' and
|
597
|
+
if self.ext == 'py' and is_sync():
|
813
598
|
return
|
814
599
|
# try proxy several times
|
815
600
|
max_retries = 3
|
@@ -817,12 +602,16 @@ class testMainClass(baseMainTestClass):
|
|
817
602
|
for j in range(0, max_retries):
|
818
603
|
try:
|
819
604
|
await self.test_method(proxy_test_name, exchange, [], True)
|
820
|
-
|
605
|
+
return # if successfull, then end the test
|
821
606
|
except Exception as e:
|
822
607
|
exception = e
|
608
|
+
await exchange.sleep(j * 1000)
|
823
609
|
# if exception was set, then throw it
|
824
|
-
if exception:
|
610
|
+
if exception is not None:
|
825
611
|
error_message = '[TEST_FAILURE] Failed ' + proxy_test_name + ' : ' + exception_message(exception)
|
612
|
+
# temporary comment the below, because c# transpilation failure
|
613
|
+
# throw new Exchange Error (errorMessage.toString ());
|
614
|
+
dump('[TEST_WARNING]' + str(error_message))
|
826
615
|
|
827
616
|
async def start_test(self, exchange, symbol):
|
828
617
|
# we do not need to test aliases
|
@@ -833,16 +622,19 @@ class testMainClass(baseMainTestClass):
|
|
833
622
|
try:
|
834
623
|
result = await self.load_exchange(exchange)
|
835
624
|
if not result:
|
836
|
-
|
625
|
+
if not is_sync():
|
626
|
+
await close(exchange)
|
837
627
|
return
|
838
628
|
# if (exchange.id === 'binance') {
|
839
629
|
# # we test proxies functionality just for one random exchange on each build, because proxy functionality is not exchange-specific, instead it's all done from base methods, so just one working sample would mean it works for all ccxt exchanges
|
840
630
|
# # await this.testProxies (exchange);
|
841
631
|
# }
|
842
632
|
await self.test_exchange(exchange, symbol)
|
843
|
-
|
633
|
+
if not is_sync():
|
634
|
+
await close(exchange)
|
844
635
|
except Exception as e:
|
845
|
-
|
636
|
+
if not is_sync():
|
637
|
+
await close(exchange)
|
846
638
|
raise e
|
847
639
|
|
848
640
|
def assert_static_error(self, cond, message, calculated_output, stored_output, key=None):
|
@@ -851,9 +643,10 @@ class testMainClass(baseMainTestClass):
|
|
851
643
|
# -----------------------------------------------------------------------------
|
852
644
|
calculated_string = json_stringify(calculated_output)
|
853
645
|
stored_string = json_stringify(stored_output)
|
854
|
-
error_message = message
|
646
|
+
error_message = message
|
855
647
|
if key is not None:
|
856
|
-
error_message = '
|
648
|
+
error_message = '[' + key + ']'
|
649
|
+
error_message += ' computed: ' + stored_string + ' stored: ' + calculated_string
|
857
650
|
assert cond, error_message
|
858
651
|
|
859
652
|
def load_markets_from_file(self, id):
|
@@ -861,12 +654,12 @@ class testMainClass(baseMainTestClass):
|
|
861
654
|
# to make this test as fast as possible
|
862
655
|
# and basically independent from the exchange
|
863
656
|
# so we can run it offline
|
864
|
-
filename =
|
657
|
+
filename = get_root_dir() + './ts/src/test/static/markets/' + id + '.json'
|
865
658
|
content = io_file_read(filename)
|
866
659
|
return content
|
867
660
|
|
868
661
|
def load_currencies_from_file(self, id):
|
869
|
-
filename =
|
662
|
+
filename = get_root_dir() + './ts/src/test/static/currencies/' + id + '.json'
|
870
663
|
content = io_file_read(filename)
|
871
664
|
return content
|
872
665
|
|
@@ -923,7 +716,7 @@ class testMainClass(baseMainTestClass):
|
|
923
716
|
result[key] = value
|
924
717
|
return result
|
925
718
|
|
926
|
-
def
|
719
|
+
def assert_new_and_stored_output_inner(self, exchange, skip_keys, new_output, stored_output, strict_type_check=True, asserting_key=None):
|
927
720
|
if is_null_value(new_output) and is_null_value(stored_output):
|
928
721
|
return True
|
929
722
|
if not new_output and not stored_output:
|
@@ -964,9 +757,17 @@ class testMainClass(baseMainTestClass):
|
|
964
757
|
# when comparing the response we want to allow some flexibility, because a 50.0 can be equal to 50 after saving it to the json file
|
965
758
|
self.assert_static_error(sanitized_new_output == sanitized_stored_output, message_error, stored_output, new_output, asserting_key)
|
966
759
|
else:
|
967
|
-
|
968
|
-
|
969
|
-
|
760
|
+
is_computed_bool = (isinstance(sanitized_new_output, bool))
|
761
|
+
is_stored_bool = (isinstance(sanitized_stored_output, bool))
|
762
|
+
is_computed_string = (isinstance(sanitized_new_output, str))
|
763
|
+
is_stored_string = (isinstance(sanitized_stored_output, str))
|
764
|
+
is_computed_undefined = (sanitized_new_output is None)
|
765
|
+
is_stored_undefined = (sanitized_stored_output is None)
|
766
|
+
should_be_same = (is_computed_bool == is_stored_bool) and (is_computed_string == is_stored_string) and (is_computed_undefined == is_stored_undefined)
|
767
|
+
self.assert_static_error(should_be_same, 'output type mismatch', stored_output, new_output, asserting_key)
|
768
|
+
is_boolean = is_computed_bool or is_stored_bool
|
769
|
+
is_string = is_computed_string or is_stored_string
|
770
|
+
is_undefined = is_computed_undefined or is_stored_undefined # undefined is a perfetly valid value
|
970
771
|
if is_boolean or is_string or is_undefined:
|
971
772
|
if self.lang == 'C#':
|
972
773
|
# tmp c# number comparsion
|
@@ -997,6 +798,25 @@ class testMainClass(baseMainTestClass):
|
|
997
798
|
self.assert_static_error(numeric_new_output == numeric_stored_output, message_error, stored_output, new_output, asserting_key)
|
998
799
|
return True # c# requ
|
999
800
|
|
801
|
+
def assert_new_and_stored_output(self, exchange, skip_keys, new_output, stored_output, strict_type_check=True, asserting_key=None):
|
802
|
+
try:
|
803
|
+
return self.assert_new_and_stored_output_inner(exchange, skip_keys, new_output, stored_output, strict_type_check, asserting_key)
|
804
|
+
except Exception as e:
|
805
|
+
if self.info:
|
806
|
+
error_message = self.var_to_string(new_output) + '(calculated)' + ' != ' + self.var_to_string(stored_output) + '(stored)'
|
807
|
+
dump('[TEST_FAILURE_DETAIL]' + error_message)
|
808
|
+
raise e
|
809
|
+
|
810
|
+
def var_to_string(self, obj=None):
|
811
|
+
new_string = None
|
812
|
+
if obj is None:
|
813
|
+
new_string = 'undefined'
|
814
|
+
elif is_null_value(obj):
|
815
|
+
new_string = 'null'
|
816
|
+
else:
|
817
|
+
new_string = json_stringify(obj)
|
818
|
+
return new_string
|
819
|
+
|
1000
820
|
def assert_static_request_output(self, exchange, type, skip_keys, stored_url, request_url, stored_output, new_output):
|
1001
821
|
if stored_url != request_url:
|
1002
822
|
# remove the host part from the url
|
@@ -1052,13 +872,16 @@ class testMainClass(baseMainTestClass):
|
|
1052
872
|
new_input.append(current)
|
1053
873
|
return new_input
|
1054
874
|
|
1055
|
-
async def
|
875
|
+
async def test_request_statically(self, exchange, method, data, type, skip_keys):
|
1056
876
|
output = None
|
1057
877
|
request_url = None
|
1058
878
|
try:
|
1059
|
-
|
879
|
+
if not is_sync():
|
880
|
+
await call_exchange_method_dynamically(exchange, method, self.sanitize_data_input(data['input']))
|
881
|
+
else:
|
882
|
+
call_exchange_method_dynamically_sync(exchange, method, self.sanitize_data_input(data['input']))
|
1060
883
|
except Exception as e:
|
1061
|
-
if not (isinstance(e,
|
884
|
+
if not (isinstance(e, InvalidProxySettings)):
|
1062
885
|
raise e
|
1063
886
|
output = exchange.last_request_body
|
1064
887
|
request_url = exchange.last_request_url
|
@@ -1067,18 +890,22 @@ class testMainClass(baseMainTestClass):
|
|
1067
890
|
self.assert_static_request_output(exchange, type, skip_keys, data['url'], request_url, call_output, output)
|
1068
891
|
except Exception as e:
|
1069
892
|
self.request_tests_failed = True
|
1070
|
-
error_message = '[' + self.lang + '][
|
893
|
+
error_message = '[' + self.lang + '][STATIC_REQUEST]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' + str(e)
|
1071
894
|
dump('[TEST_FAILURE]' + error_message)
|
1072
895
|
|
1073
896
|
async def test_response_statically(self, exchange, method, skip_keys, data):
|
1074
897
|
expected_result = exchange.safe_value(data, 'parsedResponse')
|
1075
898
|
mocked_exchange = set_fetch_response(exchange, data['httpResponse'])
|
1076
899
|
try:
|
1077
|
-
|
1078
|
-
|
900
|
+
if not is_sync():
|
901
|
+
unified_result = await call_exchange_method_dynamically(exchange, method, self.sanitize_data_input(data['input']))
|
902
|
+
self.assert_static_response_output(mocked_exchange, skip_keys, unified_result, expected_result)
|
903
|
+
else:
|
904
|
+
unified_result_sync = call_exchange_method_dynamically_sync(exchange, method, self.sanitize_data_input(data['input']))
|
905
|
+
self.assert_static_response_output(mocked_exchange, skip_keys, unified_result_sync, expected_result)
|
1079
906
|
except Exception as e:
|
1080
|
-
self.
|
1081
|
-
error_message = '[' + self.lang + '][
|
907
|
+
self.response_tests_failed = True
|
908
|
+
error_message = '[' + self.lang + '][STATIC_RESPONSE]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' + str(e)
|
1082
909
|
dump('[TEST_FAILURE]' + error_message)
|
1083
910
|
set_fetch_response(exchange, None) # reset state
|
1084
911
|
|
@@ -1099,6 +926,8 @@ class testMainClass(baseMainTestClass):
|
|
1099
926
|
'privateKey': '0xff3bdd43534543d421f05aec535965b5050ad6ac15345435345435453495e771',
|
1100
927
|
'uid': 'uid',
|
1101
928
|
'token': 'token',
|
929
|
+
'login': 'login',
|
930
|
+
'accountId': 'accountId',
|
1102
931
|
'accounts': [{
|
1103
932
|
'id': 'myAccount',
|
1104
933
|
'code': 'USDT',
|
@@ -1121,7 +950,24 @@ class testMainClass(baseMainTestClass):
|
|
1121
950
|
# instantiate the exchange and make sure that we sink the requests to avoid an actual request
|
1122
951
|
exchange = self.init_offline_exchange(exchange_name)
|
1123
952
|
global_options = exchange.safe_dict(exchange_data, 'options', {})
|
1124
|
-
|
953
|
+
# read apiKey/secret from the test file
|
954
|
+
api_key = exchange.safe_string(exchange_data, 'apiKey')
|
955
|
+
if api_key:
|
956
|
+
exchange.apiKey = str(api_key)
|
957
|
+
secret = exchange.safe_string(exchange_data, 'secret')
|
958
|
+
if secret:
|
959
|
+
exchange.secret = str(secret)
|
960
|
+
private_key = exchange.safe_string(exchange_data, 'privateKey')
|
961
|
+
if private_key:
|
962
|
+
exchange.privateKey = str(private_key)
|
963
|
+
wallet_address = exchange.safe_string(exchange_data, 'walletAddress')
|
964
|
+
if wallet_address:
|
965
|
+
exchange.walletAddress = str(wallet_address)
|
966
|
+
accounts = exchange.safe_list(exchange_data, 'accounts')
|
967
|
+
if accounts:
|
968
|
+
exchange.accounts = accounts
|
969
|
+
# exchange.options = exchange.deepExtend (exchange.options, globalOptions); # custom options to be used in the tests
|
970
|
+
exchange.extend_exchange_options(global_options)
|
1125
971
|
methods = exchange.safe_value(exchange_data, 'methods', {})
|
1126
972
|
methods_names = list(methods.keys())
|
1127
973
|
for i in range(0, len(methods_names)):
|
@@ -1131,26 +977,49 @@ class testMainClass(baseMainTestClass):
|
|
1131
977
|
result = results[j]
|
1132
978
|
old_exchange_options = exchange.options # snapshot options;
|
1133
979
|
test_exchange_options = exchange.safe_value(result, 'options', {})
|
1134
|
-
exchange.options = exchange.
|
980
|
+
# exchange.options = exchange.deepExtend (oldExchangeOptions, testExchangeOptions); # custom options to be used in the tests
|
981
|
+
exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, test_exchange_options))
|
1135
982
|
description = exchange.safe_value(result, 'description')
|
1136
983
|
if (test_name is not None) and (test_name != description):
|
1137
984
|
continue
|
1138
985
|
is_disabled = exchange.safe_bool(result, 'disabled', False)
|
1139
986
|
if is_disabled:
|
1140
987
|
continue
|
988
|
+
disabled_string = exchange.safe_string(result, 'disabled', '')
|
989
|
+
if disabled_string != '':
|
990
|
+
continue
|
991
|
+
is_disabled_c_sharp = exchange.safe_bool(result, 'disabledCS', False)
|
992
|
+
if is_disabled_c_sharp and (self.lang == 'C#'):
|
993
|
+
continue
|
1141
994
|
type = exchange.safe_string(exchange_data, 'outputType')
|
1142
995
|
skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
|
1143
|
-
await self.
|
996
|
+
await self.test_request_statically(exchange, method, result, type, skip_keys)
|
1144
997
|
# reset options
|
1145
|
-
exchange.options = exchange.
|
1146
|
-
|
998
|
+
# exchange.options = exchange.deepExtend (oldExchangeOptions, {});
|
999
|
+
exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, {}))
|
1000
|
+
if not is_sync():
|
1001
|
+
await close(exchange)
|
1147
1002
|
return True # in c# methods that will be used with promiseAll need to return something
|
1148
1003
|
|
1149
1004
|
async def test_exchange_response_statically(self, exchange_name, exchange_data, test_name=None):
|
1150
1005
|
exchange = self.init_offline_exchange(exchange_name)
|
1006
|
+
# read apiKey/secret from the test file
|
1007
|
+
api_key = exchange.safe_string(exchange_data, 'apiKey')
|
1008
|
+
if api_key:
|
1009
|
+
exchange.apiKey = str(api_key)
|
1010
|
+
secret = exchange.safe_string(exchange_data, 'secret')
|
1011
|
+
if secret:
|
1012
|
+
exchange.secret = str(secret)
|
1013
|
+
private_key = exchange.safe_string(exchange_data, 'privateKey')
|
1014
|
+
if private_key:
|
1015
|
+
exchange.privateKey = str(private_key)
|
1016
|
+
wallet_address = exchange.safe_string(exchange_data, 'walletAddress')
|
1017
|
+
if wallet_address:
|
1018
|
+
exchange.walletAddress = str(wallet_address)
|
1151
1019
|
methods = exchange.safe_value(exchange_data, 'methods', {})
|
1152
1020
|
options = exchange.safe_value(exchange_data, 'options', {})
|
1153
|
-
exchange.options = exchange.
|
1021
|
+
# exchange.options = exchange.deepExtend (exchange.options, options); # custom options to be used in the tests
|
1022
|
+
exchange.extend_exchange_options(options)
|
1154
1023
|
methods_names = list(methods.keys())
|
1155
1024
|
for i in range(0, len(methods_names)):
|
1156
1025
|
method = methods_names[i]
|
@@ -1160,7 +1029,8 @@ class testMainClass(baseMainTestClass):
|
|
1160
1029
|
description = exchange.safe_value(result, 'description')
|
1161
1030
|
old_exchange_options = exchange.options # snapshot options;
|
1162
1031
|
test_exchange_options = exchange.safe_value(result, 'options', {})
|
1163
|
-
exchange.options = exchange.
|
1032
|
+
# exchange.options = exchange.deepExtend (oldExchangeOptions, testExchangeOptions); # custom options to be used in the tests
|
1033
|
+
exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, test_exchange_options))
|
1164
1034
|
is_disabled = exchange.safe_bool(result, 'disabled', False)
|
1165
1035
|
if is_disabled:
|
1166
1036
|
continue
|
@@ -1175,8 +1045,10 @@ class testMainClass(baseMainTestClass):
|
|
1175
1045
|
skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
|
1176
1046
|
await self.test_response_statically(exchange, method, skip_keys, result)
|
1177
1047
|
# reset options
|
1178
|
-
exchange.options = exchange.
|
1179
|
-
|
1048
|
+
# exchange.options = exchange.deepExtend (oldExchangeOptions, {});
|
1049
|
+
exchange.extend_exchange_options(exchange.deep_extend(old_exchange_options, {}))
|
1050
|
+
if not is_sync():
|
1051
|
+
await close(exchange)
|
1180
1052
|
return True # in c# methods that will be used with promiseAll need to return something
|
1181
1053
|
|
1182
1054
|
def get_number_of_tests_from_exchange(self, exchange, exchange_data, test_name=None):
|
@@ -1196,7 +1068,7 @@ class testMainClass(baseMainTestClass):
|
|
1196
1068
|
await self.run_static_tests('request', target_exchange, test_name)
|
1197
1069
|
|
1198
1070
|
async def run_static_tests(self, type, target_exchange=None, test_name=None):
|
1199
|
-
folder =
|
1071
|
+
folder = get_root_dir() + './ts/src/test/static/' + type + '/'
|
1200
1072
|
static_data = self.load_static_data(folder, target_exchange)
|
1201
1073
|
if static_data is None:
|
1202
1074
|
return
|
@@ -1217,11 +1089,20 @@ class testMainClass(baseMainTestClass):
|
|
1217
1089
|
promises.append(self.test_exchange_request_statically(exchange_name, exchange_data, test_name))
|
1218
1090
|
else:
|
1219
1091
|
promises.append(self.test_exchange_response_statically(exchange_name, exchange_data, test_name))
|
1220
|
-
|
1092
|
+
try:
|
1093
|
+
await asyncio.gather(*promises)
|
1094
|
+
except Exception as e:
|
1095
|
+
if type == 'request':
|
1096
|
+
self.request_tests_failed = True
|
1097
|
+
else:
|
1098
|
+
self.response_tests_failed = True
|
1099
|
+
error_message = '[' + self.lang + '][STATIC_REQUEST]' + str(e)
|
1100
|
+
dump('[TEST_FAILURE]' + error_message)
|
1221
1101
|
if self.request_tests_failed or self.response_tests_failed:
|
1222
1102
|
exit_script(1)
|
1223
1103
|
else:
|
1224
|
-
|
1104
|
+
prefix = '[SYNC]' if (is_sync()) else ''
|
1105
|
+
success_message = '[' + self.lang + ']' + prefix + '[TEST_SUCCESS] ' + str(sum) + ' static ' + type + ' tests passed.'
|
1225
1106
|
dump('[INFO]' + success_message)
|
1226
1107
|
|
1227
1108
|
async def run_static_response_tests(self, exchange_name=None, test=None):
|
@@ -1234,7 +1115,7 @@ class testMainClass(baseMainTestClass):
|
|
1234
1115
|
# -----------------------------------------------------------------------------
|
1235
1116
|
# --- Init of brokerId tests functions-----------------------------------------
|
1236
1117
|
# -----------------------------------------------------------------------------
|
1237
|
-
promises = [self.test_binance(), self.test_okx(), self.test_cryptocom(), self.test_bybit(), self.test_kucoin(), self.test_kucoinfutures(), self.test_bitget(), self.test_mexc(), self.test_htx(), self.test_woo(), self.test_bitmart(), self.test_coinex(), self.test_bingx(), self.test_phemex(), self.test_blofin(), self.test_hyperliquid(), self.test_coinbaseinternational()]
|
1118
|
+
promises = [self.test_binance(), self.test_okx(), self.test_cryptocom(), self.test_bybit(), self.test_kucoin(), self.test_kucoinfutures(), self.test_bitget(), self.test_mexc(), self.test_htx(), self.test_woo(), self.test_bitmart(), self.test_coinex(), self.test_bingx(), self.test_phemex(), self.test_blofin(), self.test_hyperliquid(), self.test_coinbaseinternational(), self.test_coinbase_advanced(), self.test_woofi_pro(), self.test_oxfun(), self.test_xt(), self.test_vertex(), self.test_paradex(), self.test_hashkey(), self.test_coincatch(), self.test_defx()]
|
1238
1119
|
await asyncio.gather(*promises)
|
1239
1120
|
success_message = '[' + self.lang + '][TEST_SUCCESS] brokerId tests passed.'
|
1240
1121
|
dump('[INFO]' + success_message)
|
@@ -1267,7 +1148,8 @@ class testMainClass(baseMainTestClass):
|
|
1267
1148
|
assert client_order_id_swap.startswith(swap_id_string), 'binance - swap clientOrderId: ' + client_order_id_swap + ' does not start with swapId' + swap_id_string
|
1268
1149
|
client_order_id_inverse = swap_inverse_order_request['newClientOrderId']
|
1269
1150
|
assert client_order_id_inverse.startswith(swap_id_string), 'binance - swap clientOrderIdInverse: ' + client_order_id_inverse + ' does not start with swapId' + swap_id_string
|
1270
|
-
|
1151
|
+
if not is_sync():
|
1152
|
+
await close(exchange)
|
1271
1153
|
return True
|
1272
1154
|
|
1273
1155
|
async def test_okx(self):
|
@@ -1292,7 +1174,8 @@ class testMainClass(baseMainTestClass):
|
|
1292
1174
|
assert client_order_id_swap.startswith(id_string), 'okx - swap clientOrderId: ' + client_order_id_swap + ' does not start with id: ' + id_string
|
1293
1175
|
swap_tag = swap_order_request[0]['tag']
|
1294
1176
|
assert swap_tag == id, 'okx - id: ' + id + ' different from swap tag: ' + swap_tag
|
1295
|
-
|
1177
|
+
if not is_sync():
|
1178
|
+
await close(exchange)
|
1296
1179
|
return True
|
1297
1180
|
|
1298
1181
|
async def test_cryptocom(self):
|
@@ -1306,7 +1189,8 @@ class testMainClass(baseMainTestClass):
|
|
1306
1189
|
request = json_parse(exchange.last_request_body)
|
1307
1190
|
broker_id = request['params']['broker_id']
|
1308
1191
|
assert broker_id == id, 'cryptocom - id: ' + id + ' different from broker_id: ' + broker_id
|
1309
|
-
|
1192
|
+
if not is_sync():
|
1193
|
+
await close(exchange)
|
1310
1194
|
return True
|
1311
1195
|
|
1312
1196
|
async def test_bybit(self):
|
@@ -1320,17 +1204,17 @@ class testMainClass(baseMainTestClass):
|
|
1320
1204
|
# we expect an error here, we're only interested in the headers
|
1321
1205
|
req_headers = exchange.last_request_headers
|
1322
1206
|
assert req_headers['Referer'] == id, 'bybit - id: ' + id + ' not in headers.'
|
1323
|
-
|
1207
|
+
if not is_sync():
|
1208
|
+
await close(exchange)
|
1324
1209
|
return True
|
1325
1210
|
|
1326
1211
|
async def test_kucoin(self):
|
1327
1212
|
exchange = self.init_offline_exchange('kucoin')
|
1328
1213
|
req_headers = None
|
1329
|
-
options_string = str(exchange.options)
|
1330
1214
|
spot_id = exchange.options['partner']['spot']['id']
|
1331
1215
|
spot_key = exchange.options['partner']['spot']['key']
|
1332
|
-
assert spot_id == 'ccxt', 'kucoin - id: ' + spot_id + ' not in options
|
1333
|
-
assert spot_key == '9e58cc35-5b5e-4133-92ec-166e3f077cb8', 'kucoin - key: ' + spot_key + ' not in options
|
1216
|
+
assert spot_id == 'ccxt', 'kucoin - id: ' + spot_id + ' not in options'
|
1217
|
+
assert spot_key == '9e58cc35-5b5e-4133-92ec-166e3f077cb8', 'kucoin - key: ' + spot_key + ' not in options.'
|
1334
1218
|
try:
|
1335
1219
|
await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
|
1336
1220
|
except Exception as e:
|
@@ -1338,54 +1222,54 @@ class testMainClass(baseMainTestClass):
|
|
1338
1222
|
req_headers = exchange.last_request_headers
|
1339
1223
|
id = 'ccxt'
|
1340
1224
|
assert req_headers['KC-API-PARTNER'] == id, 'kucoin - id: ' + id + ' not in headers.'
|
1341
|
-
|
1225
|
+
if not is_sync():
|
1226
|
+
await close(exchange)
|
1342
1227
|
return True
|
1343
1228
|
|
1344
1229
|
async def test_kucoinfutures(self):
|
1345
1230
|
exchange = self.init_offline_exchange('kucoinfutures')
|
1346
1231
|
req_headers = None
|
1347
1232
|
id = 'ccxtfutures'
|
1348
|
-
options_string = str(exchange.options['partner']['future'])
|
1349
1233
|
future_id = exchange.options['partner']['future']['id']
|
1350
1234
|
future_key = exchange.options['partner']['future']['key']
|
1351
|
-
assert future_id == id, 'kucoinfutures - id: ' + future_id + ' not in options
|
1352
|
-
assert future_key == '1b327198-f30c-4f14-a0ac-918871282f15', 'kucoinfutures - key: ' + future_key + ' not in options
|
1235
|
+
assert future_id == id, 'kucoinfutures - id: ' + future_id + ' not in options.'
|
1236
|
+
assert future_key == '1b327198-f30c-4f14-a0ac-918871282f15', 'kucoinfutures - key: ' + future_key + ' not in options.'
|
1353
1237
|
try:
|
1354
1238
|
await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
|
1355
1239
|
except Exception as e:
|
1356
1240
|
req_headers = exchange.last_request_headers
|
1357
1241
|
assert req_headers['KC-API-PARTNER'] == id, 'kucoinfutures - id: ' + id + ' not in headers.'
|
1358
|
-
|
1242
|
+
if not is_sync():
|
1243
|
+
await close(exchange)
|
1359
1244
|
return True
|
1360
1245
|
|
1361
1246
|
async def test_bitget(self):
|
1362
1247
|
exchange = self.init_offline_exchange('bitget')
|
1363
1248
|
req_headers = None
|
1364
1249
|
id = 'p4sve'
|
1365
|
-
|
1366
|
-
assert exchange.options['broker'] == id, 'bitget - id: ' + id + ' not in options: ' + options_string
|
1250
|
+
assert exchange.options['broker'] == id, 'bitget - id: ' + id + ' not in options'
|
1367
1251
|
try:
|
1368
1252
|
await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
|
1369
1253
|
except Exception as e:
|
1370
1254
|
req_headers = exchange.last_request_headers
|
1371
1255
|
assert req_headers['X-CHANNEL-API-CODE'] == id, 'bitget - id: ' + id + ' not in headers.'
|
1372
|
-
|
1256
|
+
if not is_sync():
|
1257
|
+
await close(exchange)
|
1373
1258
|
return True
|
1374
1259
|
|
1375
1260
|
async def test_mexc(self):
|
1376
1261
|
exchange = self.init_offline_exchange('mexc')
|
1377
1262
|
req_headers = None
|
1378
1263
|
id = 'CCXT'
|
1379
|
-
|
1380
|
-
assert exchange.options['broker'] == id, 'mexc - id: ' + id + ' not in options: ' + options_string
|
1264
|
+
assert exchange.options['broker'] == id, 'mexc - id: ' + id + ' not in options'
|
1381
1265
|
await exchange.load_markets()
|
1382
1266
|
try:
|
1383
1267
|
await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
|
1384
1268
|
except Exception as e:
|
1385
1269
|
req_headers = exchange.last_request_headers
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1270
|
+
assert req_headers['source'] == id, 'mexc - id: ' + id + ' not in headers.'
|
1271
|
+
if not is_sync():
|
1272
|
+
await close(exchange)
|
1389
1273
|
return True
|
1390
1274
|
|
1391
1275
|
async def test_htx(self):
|
@@ -1415,7 +1299,8 @@ class testMainClass(baseMainTestClass):
|
|
1415
1299
|
assert client_order_id_swap.startswith(id_string), 'htx - swap channel_code ' + client_order_id_swap + ' does not start with id: ' + id_string
|
1416
1300
|
client_order_id_inverse = swap_inverse_order_request['channel_code']
|
1417
1301
|
assert client_order_id_inverse.startswith(id_string), 'htx - swap inverse channel_code ' + client_order_id_inverse + ' does not start with id: ' + id_string
|
1418
|
-
|
1302
|
+
if not is_sync():
|
1303
|
+
await close(exchange)
|
1419
1304
|
return True
|
1420
1305
|
|
1421
1306
|
async def test_woo(self):
|
@@ -1440,7 +1325,8 @@ class testMainClass(baseMainTestClass):
|
|
1440
1325
|
stop_order_request = json_parse(exchange.last_request_body)
|
1441
1326
|
client_order_id_stop = stop_order_request['brokerId']
|
1442
1327
|
assert client_order_id_stop.startswith(id_string), 'woo - brokerId: ' + client_order_id_stop + ' does not start with id: ' + id_string
|
1443
|
-
|
1328
|
+
if not is_sync():
|
1329
|
+
await close(exchange)
|
1444
1330
|
return True
|
1445
1331
|
|
1446
1332
|
async def test_bitmart(self):
|
@@ -1454,7 +1340,8 @@ class testMainClass(baseMainTestClass):
|
|
1454
1340
|
except Exception as e:
|
1455
1341
|
req_headers = exchange.last_request_headers
|
1456
1342
|
assert req_headers['X-BM-BROKER-ID'] == id, 'bitmart - id: ' + id + ' not in headers'
|
1457
|
-
|
1343
|
+
if not is_sync():
|
1344
|
+
await close(exchange)
|
1458
1345
|
return True
|
1459
1346
|
|
1460
1347
|
async def test_coinex(self):
|
@@ -1469,23 +1356,23 @@ class testMainClass(baseMainTestClass):
|
|
1469
1356
|
client_order_id = spot_order_request['client_id']
|
1470
1357
|
id_string = str(id)
|
1471
1358
|
assert client_order_id.startswith(id_string), 'coinex - clientOrderId: ' + client_order_id + ' does not start with id: ' + id_string
|
1472
|
-
|
1359
|
+
if not is_sync():
|
1360
|
+
await close(exchange)
|
1473
1361
|
return True
|
1474
1362
|
|
1475
1363
|
async def test_bingx(self):
|
1476
1364
|
exchange = self.init_offline_exchange('bingx')
|
1477
1365
|
req_headers = None
|
1478
1366
|
id = 'CCXT'
|
1479
|
-
|
1480
|
-
assert exchange.options['broker'] == id, 'bingx - id: ' + id + ' not in options: ' + options_string
|
1367
|
+
assert exchange.options['broker'] == id, 'bingx - id: ' + id + ' not in options'
|
1481
1368
|
try:
|
1482
1369
|
await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
|
1483
1370
|
except Exception as e:
|
1484
1371
|
# we expect an error here, we're only interested in the headers
|
1485
1372
|
req_headers = exchange.last_request_headers
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1373
|
+
assert req_headers['X-SOURCE-KEY'] == id, 'bingx - id: ' + id + ' not in headers.'
|
1374
|
+
if not is_sync():
|
1375
|
+
await close(exchange)
|
1489
1376
|
|
1490
1377
|
async def test_phemex(self):
|
1491
1378
|
exchange = self.init_offline_exchange('phemex')
|
@@ -1498,7 +1385,8 @@ class testMainClass(baseMainTestClass):
|
|
1498
1385
|
client_order_id = request['clOrdID']
|
1499
1386
|
id_string = str(id)
|
1500
1387
|
assert client_order_id.startswith(id_string), 'phemex - clOrdID: ' + client_order_id + ' does not start with id: ' + id_string
|
1501
|
-
|
1388
|
+
if not is_sync():
|
1389
|
+
await close(exchange)
|
1502
1390
|
|
1503
1391
|
async def test_blofin(self):
|
1504
1392
|
exchange = self.init_offline_exchange('blofin')
|
@@ -1511,7 +1399,8 @@ class testMainClass(baseMainTestClass):
|
|
1511
1399
|
broker_id = request['brokerId']
|
1512
1400
|
id_string = str(id)
|
1513
1401
|
assert broker_id.startswith(id_string), 'blofin - brokerId: ' + broker_id + ' does not start with id: ' + id_string
|
1514
|
-
|
1402
|
+
if not is_sync():
|
1403
|
+
await close(exchange)
|
1515
1404
|
|
1516
1405
|
async def test_hyperliquid(self):
|
1517
1406
|
exchange = self.init_offline_exchange('hyperliquid')
|
@@ -1523,7 +1412,8 @@ class testMainClass(baseMainTestClass):
|
|
1523
1412
|
request = json_parse(exchange.last_request_body)
|
1524
1413
|
broker_id = str((request['action']['brokerCode']))
|
1525
1414
|
assert broker_id == id, 'hyperliquid - brokerId: ' + broker_id + ' does not start with id: ' + id
|
1526
|
-
|
1415
|
+
if not is_sync():
|
1416
|
+
await close(exchange)
|
1527
1417
|
|
1528
1418
|
async def test_coinbaseinternational(self):
|
1529
1419
|
exchange = self.init_offline_exchange('coinbaseinternational')
|
@@ -1537,13 +1427,181 @@ class testMainClass(baseMainTestClass):
|
|
1537
1427
|
request = json_parse(exchange.last_request_body)
|
1538
1428
|
client_order_id = request['client_order_id']
|
1539
1429
|
assert client_order_id.startswith(str(id)), 'clientOrderId does not start with id'
|
1540
|
-
|
1430
|
+
if not is_sync():
|
1431
|
+
await close(exchange)
|
1432
|
+
return True
|
1433
|
+
|
1434
|
+
async def test_coinbase_advanced(self):
|
1435
|
+
exchange = self.init_offline_exchange('coinbase')
|
1436
|
+
id = 'ccxt'
|
1437
|
+
assert exchange.options['brokerId'] == id, 'id not in options'
|
1438
|
+
request = None
|
1439
|
+
try:
|
1440
|
+
await exchange.create_order('BTC/USDC', 'limit', 'buy', 1, 20000)
|
1441
|
+
except Exception as e:
|
1442
|
+
request = json_parse(exchange.last_request_body)
|
1443
|
+
client_order_id = request['client_order_id']
|
1444
|
+
assert client_order_id.startswith(str(id)), 'clientOrderId does not start with id'
|
1445
|
+
if not is_sync():
|
1446
|
+
await close(exchange)
|
1541
1447
|
return True
|
1542
1448
|
|
1543
|
-
|
1544
|
-
|
1449
|
+
async def test_woofi_pro(self):
|
1450
|
+
exchange = self.init_offline_exchange('woofipro')
|
1451
|
+
exchange.secret = 'secretsecretsecretsecretsecretsecretsecrets'
|
1452
|
+
id = 'CCXT'
|
1453
|
+
await exchange.load_markets()
|
1454
|
+
request = None
|
1455
|
+
try:
|
1456
|
+
await exchange.create_order('BTC/USDC:USDC', 'limit', 'buy', 1, 20000)
|
1457
|
+
except Exception as e:
|
1458
|
+
request = json_parse(exchange.last_request_body)
|
1459
|
+
broker_id = request['order_tag']
|
1460
|
+
assert broker_id == id, 'woofipro - id: ' + id + ' different from broker_id: ' + broker_id
|
1461
|
+
if not is_sync():
|
1462
|
+
await close(exchange)
|
1463
|
+
return True
|
1545
1464
|
|
1465
|
+
async def test_oxfun(self):
|
1466
|
+
exchange = self.init_offline_exchange('oxfun')
|
1467
|
+
exchange.secret = 'secretsecretsecretsecretsecretsecretsecrets'
|
1468
|
+
id = 1000
|
1469
|
+
await exchange.load_markets()
|
1470
|
+
request = None
|
1471
|
+
try:
|
1472
|
+
await exchange.create_order('BTC/USD:OX', 'limit', 'buy', 1, 20000)
|
1473
|
+
except Exception as e:
|
1474
|
+
request = json_parse(exchange.last_request_body)
|
1475
|
+
orders = request['orders']
|
1476
|
+
first = orders[0]
|
1477
|
+
broker_id = first['source']
|
1478
|
+
assert broker_id == id, 'oxfun - id: ' + str(id) + ' different from broker_id: ' + str(broker_id)
|
1479
|
+
return True
|
1546
1480
|
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1481
|
+
async def test_xt(self):
|
1482
|
+
exchange = self.init_offline_exchange('xt')
|
1483
|
+
id = 'CCXT'
|
1484
|
+
spot_order_request = None
|
1485
|
+
try:
|
1486
|
+
await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
|
1487
|
+
except Exception as e:
|
1488
|
+
spot_order_request = json_parse(exchange.last_request_body)
|
1489
|
+
spot_media = spot_order_request['media']
|
1490
|
+
assert spot_media == id, 'xt - id: ' + id + ' different from swap tag: ' + spot_media
|
1491
|
+
swap_order_request = None
|
1492
|
+
try:
|
1493
|
+
await exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 1, 20000)
|
1494
|
+
except Exception as e:
|
1495
|
+
swap_order_request = json_parse(exchange.last_request_body)
|
1496
|
+
swap_media = swap_order_request['clientMedia']
|
1497
|
+
assert swap_media == id, 'xt - id: ' + id + ' different from swap tag: ' + swap_media
|
1498
|
+
if not is_sync():
|
1499
|
+
await close(exchange)
|
1500
|
+
return True
|
1501
|
+
|
1502
|
+
async def test_vertex(self):
|
1503
|
+
exchange = self.init_offline_exchange('vertex')
|
1504
|
+
exchange.walletAddress = '0xc751489d24a33172541ea451bc253d7a9e98c781'
|
1505
|
+
exchange.privateKey = 'c33b1eb4b53108bf52e10f636d8c1236c04c33a712357ba3543ab45f48a5cb0b'
|
1506
|
+
exchange.options['v1contracts'] = {
|
1507
|
+
'chain_id': '42161',
|
1508
|
+
'endpoint_addr': '0xbbee07b3e8121227afcfe1e2b82772246226128e',
|
1509
|
+
'book_addrs': ['0x0000000000000000000000000000000000000000', '0x70e5911371472e406f1291c621d1c8f207764d73', '0xf03f457a30e598d5020164a339727ef40f2b8fbc', '0x1c6281a78aa0ed88949c319cba5f0f0de2ce8353', '0xfe653438a1a4a7f56e727509c341d60a7b54fa91', '0xb6304e9a6ca241376a5fc9294daa8fca65ddcdcd', '0x01ec802ae0ab1b2cc4f028b9fe6eb954aef06ed1', '0x0000000000000000000000000000000000000000', '0x9c52d5c4df5a68955ad088a781b4ab364a861e9e', '0x0000000000000000000000000000000000000000', '0x2a3bcda1bb3ef649f3571c96c597c3d2b25edc79', '0x0000000000000000000000000000000000000000', '0x0492ff9807f82856781488015ef7aa5526c0edd6', '0x0000000000000000000000000000000000000000', '0xea884c82418ebc21cd080b8f40ecc4d06a6a6883', '0x0000000000000000000000000000000000000000', '0x5ecf68f983253a818ca8c17a56a4f2fb48d6ec6b', '0x0000000000000000000000000000000000000000', '0xba3f57a977f099905531f7c2f294aad7b56ed254', '0x0000000000000000000000000000000000000000', '0x0ac8c26d207d0c6aabb3644fea18f530c4d6fc8e', '0x0000000000000000000000000000000000000000', '0x8bd80ad7630b3864bed66cf28f548143ea43dc3b', '0x0000000000000000000000000000000000000000', '0x045391227fc4b2cdd27b95f066864225afc9314e', '0x0000000000000000000000000000000000000000', '0x7d512bef2e6cfd7e7f5f6b2f8027e3728eb7b6c3', '0x0000000000000000000000000000000000000000', '0x678a6c5003b56b5e9a81559e9a0df880407c796f', '0x0000000000000000000000000000000000000000', '0x14b5a17208fa98843cc602b3f74e31c95ded3567', '0xe442a89a07b3888ab10579fbb2824aeceff3a282', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0xac28ac205275d7c2d6877bea8657cebe04fd9ae9', '0x0000000000000000000000000000000000000000', '0xed811409bfea901e75cb19ba347c08a154e860c9', '0x0000000000000000000000000000000000000000', '0x0f7afcb1612b305626cff84f84e4169ba2d0f12c', '0x0000000000000000000000000000000000000000', '0xe4b8d903db2ce2d3891ef04cfc3ac56330c1b0c3', '0x5f44362bad629846b7455ad9d36bbc3759a3ef62', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0xa64e04ed4b223a71e524dc7ebb7f28e422ccfdde', '0x0000000000000000000000000000000000000000', '0x2ee573caab73c1d8cf0ca6bd3589b67de79628a4', '0x0000000000000000000000000000000000000000', '0x01bb96883a8a478d4410387d4aaf11067edc2c74', '0x0000000000000000000000000000000000000000', '0xe7ed0c559d905436a867cddf07e06921d572363c', '0x0000000000000000000000000000000000000000', '0xa94f9e3433c92a5cd1925494811a67b1943557d9', '0x0000000000000000000000000000000000000000', '0xa63de7f89ba1270b85f3dcc193ff1a1390a7c7c7', '0x0000000000000000000000000000000000000000', '0xc8b0b37dffe3a711a076dc86dd617cc203f36121', '0x0000000000000000000000000000000000000000', '0x646df48947ff785fe609969ff634e7be9d1c34cd', '0x0000000000000000000000000000000000000000', '0x42582b404b0bec4a266631a0e178840b107a0c69', '0x0000000000000000000000000000000000000000', '0x36a94bc3edb1b629d1413091e22dc65fa050f17f', '0x0000000000000000000000000000000000000000', '0xb398d00b5a336f0ad33cfb352fd7646171cec442', '0x0000000000000000000000000000000000000000', '0xb4bc3b00de98e1c0498699379f6607b1f00bd5a1', '0x0000000000000000000000000000000000000000', '0xfe8b7baf68952bac2c04f386223d2013c1b4c601', '0x0000000000000000000000000000000000000000', '0x9c8764ec71f175c97c6c2fd558eb6546fcdbea32', '0x0000000000000000000000000000000000000000', '0x94d31188982c8eccf243e555b22dc57de1dba4e1', '0x0000000000000000000000000000000000000000', '0x407c5e2fadd7555be927c028bc358daa907c797a', '0x0000000000000000000000000000000000000000', '0x7e97da2dbbbdd7fb313cf9dc0581ac7cec999c70', '0x0000000000000000000000000000000000000000', '0x7f8d2662f64dd468c423805f98a6579ad59b28fa', '0x0000000000000000000000000000000000000000', '0x3398adf63fed17cbadd6080a1fb771e6a2a55958', '0x0000000000000000000000000000000000000000', '0xba8910a1d7ab62129729047d453091a1e6356170', '0x0000000000000000000000000000000000000000', '0xdc054bce222fe725da0f17abcef38253bd8bb745', '0x0000000000000000000000000000000000000000', '0xca21693467d0a5ea9e10a5a7c5044b9b3837e694', '0x0000000000000000000000000000000000000000', '0xe0b02de2139256dbae55cf350094b882fbe629ea', '0x0000000000000000000000000000000000000000', '0x02c38368a6f53858aab5a3a8d91d73eb59edf9b9', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000', '0xfe8c4778843c3cb047ffe7c0c0154a724c05cab9', '0x0000000000000000000000000000000000000000', '0xe2e88862d9b7379e21c82fc4aec8d71bddbcdb4b', '0x0000000000000000000000000000000000000000', '0xbbaff9e73b30f9cea5c01481f12de75050947fd6', '0x0000000000000000000000000000000000000000', '0xa20f6f381fe0fec5a1035d37ebf8890726377ab9', '0x0000000000000000000000000000000000000000', '0xbad68032d012bf35d3a2a177b242e86684027ed0', '0x0000000000000000000000000000000000000000', '0x0e61ca37f0c67e8a8794e45e264970a2a23a513c', '0x0000000000000000000000000000000000000000', '0xa77b7048e378c5270b15918449ededf87c3a3db3', '0x0000000000000000000000000000000000000000', '0x15afca1e6f02b556fa6551021b3493a1e4a7f44f'],
|
1510
|
+
}
|
1511
|
+
id = 5930043274845996
|
1512
|
+
await exchange.load_markets()
|
1513
|
+
request = None
|
1514
|
+
try:
|
1515
|
+
await exchange.create_order('BTC/USDC:USDC', 'limit', 'buy', 1, 20000)
|
1516
|
+
except Exception as e:
|
1517
|
+
request = json_parse(exchange.last_request_body)
|
1518
|
+
order = request['place_order']
|
1519
|
+
broker_id = order['id']
|
1520
|
+
assert broker_id == id, 'vertex - id: ' + str(id) + ' different from broker_id: ' + str(broker_id)
|
1521
|
+
if not is_sync():
|
1522
|
+
await close(exchange)
|
1523
|
+
return True
|
1524
|
+
|
1525
|
+
async def test_paradex(self):
|
1526
|
+
exchange = self.init_offline_exchange('paradex')
|
1527
|
+
exchange.walletAddress = '0xc751489d24a33172541ea451bc253d7a9e98c781'
|
1528
|
+
exchange.privateKey = 'c33b1eb4b53108bf52e10f636d8c1236c04c33a712357ba3543ab45f48a5cb0b'
|
1529
|
+
exchange.options['authToken'] = 'token'
|
1530
|
+
exchange.options['systemConfig'] = {
|
1531
|
+
'starknet_gateway_url': 'https://potc-testnet-sepolia.starknet.io',
|
1532
|
+
'starknet_fullnode_rpc_url': 'https://pathfinder.api.testnet.paradex.trade/rpc/v0_7',
|
1533
|
+
'starknet_chain_id': 'PRIVATE_SN_POTC_SEPOLIA',
|
1534
|
+
'block_explorer_url': 'https://voyager.testnet.paradex.trade/',
|
1535
|
+
'paraclear_address': '0x286003f7c7bfc3f94e8f0af48b48302e7aee2fb13c23b141479ba00832ef2c6',
|
1536
|
+
'paraclear_decimals': 8,
|
1537
|
+
'paraclear_account_proxy_hash': '0x3530cc4759d78042f1b543bf797f5f3d647cde0388c33734cf91b7f7b9314a9',
|
1538
|
+
'paraclear_account_hash': '0x41cb0280ebadaa75f996d8d92c6f265f6d040bb3ba442e5f86a554f1765244e',
|
1539
|
+
'oracle_address': '0x2c6a867917ef858d6b193a0ff9e62b46d0dc760366920d631715d58baeaca1f',
|
1540
|
+
'bridged_tokens': [{
|
1541
|
+
'name': 'TEST USDC',
|
1542
|
+
'symbol': 'USDC',
|
1543
|
+
'decimals': 6,
|
1544
|
+
'l1_token_address': '0x29A873159D5e14AcBd63913D4A7E2df04570c666',
|
1545
|
+
'l1_bridge_address': '0x8586e05adc0C35aa11609023d4Ae6075Cb813b4C',
|
1546
|
+
'l2_token_address': '0x6f373b346561036d98ea10fb3e60d2f459c872b1933b50b21fe6ef4fda3b75e',
|
1547
|
+
'l2_bridge_address': '0x46e9237f5408b5f899e72125dd69bd55485a287aaf24663d3ebe00d237fc7ef',
|
1548
|
+
}],
|
1549
|
+
'l1_core_contract_address': '0x582CC5d9b509391232cd544cDF9da036e55833Af',
|
1550
|
+
'l1_operator_address': '0x11bACdFbBcd3Febe5e8CEAa75E0Ef6444d9B45FB',
|
1551
|
+
'l1_chain_id': '11155111',
|
1552
|
+
'liquidation_fee': '0.2',
|
1553
|
+
}
|
1554
|
+
req_headers = None
|
1555
|
+
id = 'CCXT'
|
1556
|
+
assert exchange.options['broker'] == id, 'paradex - id: ' + id + ' not in options'
|
1557
|
+
await exchange.load_markets()
|
1558
|
+
try:
|
1559
|
+
await exchange.create_order('BTC/USD:USDC', 'limit', 'buy', 1, 20000)
|
1560
|
+
except Exception as e:
|
1561
|
+
req_headers = exchange.last_request_headers
|
1562
|
+
assert req_headers['PARADEX-PARTNER'] == id, 'paradex - id: ' + id + ' not in headers'
|
1563
|
+
if not is_sync():
|
1564
|
+
await close(exchange)
|
1565
|
+
return True
|
1566
|
+
|
1567
|
+
async def test_hashkey(self):
|
1568
|
+
exchange = self.init_offline_exchange('hashkey')
|
1569
|
+
req_headers = None
|
1570
|
+
id = '10000700011'
|
1571
|
+
try:
|
1572
|
+
await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
|
1573
|
+
except Exception as e:
|
1574
|
+
# we expect an error here, we're only interested in the headers
|
1575
|
+
req_headers = exchange.last_request_headers
|
1576
|
+
assert req_headers['INPUT-SOURCE'] == id, 'hashkey - id: ' + id + ' not in headers.'
|
1577
|
+
if not is_sync():
|
1578
|
+
await close(exchange)
|
1579
|
+
return True
|
1580
|
+
|
1581
|
+
async def test_coincatch(self):
|
1582
|
+
exchange = self.init_offline_exchange('coincatch')
|
1583
|
+
req_headers = None
|
1584
|
+
id = '47cfy'
|
1585
|
+
try:
|
1586
|
+
await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
|
1587
|
+
except Exception as e:
|
1588
|
+
# we expect an error here, we're only interested in the headers
|
1589
|
+
req_headers = exchange.last_request_headers
|
1590
|
+
assert req_headers['X-CHANNEL-API-CODE'] == id, 'coincatch - id: ' + id + ' not in headers.'
|
1591
|
+
if not is_sync():
|
1592
|
+
await close(exchange)
|
1593
|
+
return True
|
1594
|
+
|
1595
|
+
async def test_defx(self):
|
1596
|
+
exchange = self.init_offline_exchange('defx')
|
1597
|
+
req_headers = None
|
1598
|
+
try:
|
1599
|
+
await exchange.create_order('DOGE/USDC:USDC', 'limit', 'buy', 100, 1)
|
1600
|
+
except Exception as e:
|
1601
|
+
# we expect an error here, we're only interested in the headers
|
1602
|
+
req_headers = exchange.last_request_headers
|
1603
|
+
id = 'ccxt'
|
1604
|
+
assert req_headers['X-DEFX-SOURCE'] == id, 'defx - id: ' + id + ' not in headers.'
|
1605
|
+
if not is_sync():
|
1606
|
+
await close(exchange)
|
1607
|
+
return True
|