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