coinex-api 0.0.89__py3-none-any.whl → 0.0.110__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.

Potentially problematic release.


This version of coinex-api might be problematic. Click here for more details.

Files changed (197) hide show
  1. coinex/ccxt/__init__.py +1 -1
  2. coinex/ccxt/async_support/__init__.py +1 -1
  3. coinex/ccxt/async_support/base/exchange.py +139 -10
  4. coinex/ccxt/async_support/base/throttler.py +1 -1
  5. coinex/ccxt/async_support/base/ws/cache.py +1 -0
  6. coinex/ccxt/async_support/base/ws/client.py +26 -4
  7. coinex/ccxt/async_support/coinex.py +2 -2
  8. coinex/ccxt/base/exchange.py +587 -91
  9. coinex/ccxt/base/types.py +11 -2
  10. coinex/ccxt/coinex.py +2 -2
  11. coinex/ccxt/pro/__init__.py +1 -1
  12. coinex/ccxt/pro/coinex.py +10 -7
  13. coinex/ccxt/static_dependencies/bip/__init__.py +6 -0
  14. coinex/ccxt/static_dependencies/bip/addr/P2PKH_addr.py +205 -0
  15. coinex/ccxt/static_dependencies/bip/addr/__init__.py +5 -0
  16. coinex/ccxt/static_dependencies/bip/addr/addr_dec_utils.py +125 -0
  17. coinex/ccxt/static_dependencies/bip/addr/addr_key_validator.py +162 -0
  18. coinex/ccxt/static_dependencies/bip/addr/iaddr_decoder.py +48 -0
  19. coinex/ccxt/static_dependencies/bip/addr/iaddr_encoder.py +50 -0
  20. coinex/ccxt/static_dependencies/bip/base58/__init__.py +3 -0
  21. coinex/ccxt/static_dependencies/bip/base58/base58.py +207 -0
  22. coinex/ccxt/static_dependencies/bip/base58/base58_ex.py +25 -0
  23. coinex/ccxt/static_dependencies/bip/base58/base58_xmr.py +155 -0
  24. coinex/ccxt/static_dependencies/bip/bech32/__init__.py +4 -0
  25. coinex/ccxt/static_dependencies/bip/bech32/bch_bech32.py +220 -0
  26. coinex/ccxt/static_dependencies/bip/bech32/bech32.py +235 -0
  27. coinex/ccxt/static_dependencies/bip/bech32/bech32_base.py +246 -0
  28. coinex/ccxt/static_dependencies/bip/bech32/bech32_ex.py +25 -0
  29. coinex/ccxt/static_dependencies/bip/bech32/segwit_bech32.py +173 -0
  30. coinex/ccxt/static_dependencies/bip/bip32/__init__.py +14 -0
  31. coinex/ccxt/static_dependencies/bip/bip32/base/__init__.py +3 -0
  32. coinex/ccxt/static_dependencies/bip/bip32/base/bip32_base.py +581 -0
  33. coinex/ccxt/static_dependencies/bip/bip32/base/ibip32_key_derivator.py +83 -0
  34. coinex/ccxt/static_dependencies/bip/bip32/base/ibip32_mst_key_generator.py +47 -0
  35. coinex/ccxt/static_dependencies/bip/bip32/bip32_const.py +35 -0
  36. coinex/ccxt/static_dependencies/bip/bip32/bip32_ex.py +29 -0
  37. coinex/ccxt/static_dependencies/bip/bip32/bip32_key_data.py +500 -0
  38. coinex/ccxt/static_dependencies/bip/bip32/bip32_key_net_ver.py +83 -0
  39. coinex/ccxt/static_dependencies/bip/bip32/bip32_key_ser.py +294 -0
  40. coinex/ccxt/static_dependencies/bip/bip32/bip32_keys.py +457 -0
  41. coinex/ccxt/static_dependencies/bip/bip32/bip32_path.py +247 -0
  42. coinex/ccxt/static_dependencies/bip/bip32/bip32_utils.py +72 -0
  43. coinex/ccxt/static_dependencies/bip/bip32/kholaw/__init__.py +4 -0
  44. coinex/ccxt/static_dependencies/bip/bip32/kholaw/bip32_kholaw_ed25519.py +82 -0
  45. coinex/ccxt/static_dependencies/bip/bip32/kholaw/bip32_kholaw_ed25519_key_derivator.py +118 -0
  46. coinex/ccxt/static_dependencies/bip/bip32/kholaw/bip32_kholaw_key_derivator_base.py +204 -0
  47. coinex/ccxt/static_dependencies/bip/bip32/kholaw/bip32_kholaw_mst_key_generator.py +119 -0
  48. coinex/ccxt/static_dependencies/bip/bip32/slip10/__init__.py +1 -0
  49. coinex/ccxt/static_dependencies/bip/bip32/slip10/bip32_slip10_key_derivator.py +200 -0
  50. coinex/ccxt/static_dependencies/bip/bip32/slip10/bip32_slip10_mst_key_generator.py +168 -0
  51. coinex/ccxt/static_dependencies/bip/bip32/slip10/bip32_slip10_secp256k1.py +82 -0
  52. coinex/ccxt/static_dependencies/bip/bip44/__init__.py +1 -0
  53. coinex/ccxt/static_dependencies/bip/bip44/bip44.py +265 -0
  54. coinex/ccxt/static_dependencies/bip/bip44_base/__init__.py +3 -0
  55. coinex/ccxt/static_dependencies/bip/bip44_base/bip44_base.py +624 -0
  56. coinex/ccxt/static_dependencies/bip/bip44_base/bip44_base_ex.py +25 -0
  57. coinex/ccxt/static_dependencies/bip/bip44_base/bip44_keys.py +225 -0
  58. coinex/ccxt/static_dependencies/bip/coin_conf/__init__.py +2 -0
  59. coinex/ccxt/static_dependencies/bip/coin_conf/coin_conf.py +68 -0
  60. coinex/ccxt/static_dependencies/bip/coin_conf/coins_conf.py +890 -0
  61. coinex/ccxt/static_dependencies/bip/conf/__init__.py +0 -0
  62. coinex/ccxt/static_dependencies/bip/conf/bip44/__init__.py +3 -0
  63. coinex/ccxt/static_dependencies/bip/conf/bip44/bip44_coins.py +126 -0
  64. coinex/ccxt/static_dependencies/bip/conf/bip44/bip44_conf.py +1360 -0
  65. coinex/ccxt/static_dependencies/bip/conf/bip44/bip44_conf_getter.py +153 -0
  66. coinex/ccxt/static_dependencies/bip/conf/bip49/__init__.py +3 -0
  67. coinex/ccxt/static_dependencies/bip/conf/bip49/bip49_coins.py +53 -0
  68. coinex/ccxt/static_dependencies/bip/conf/bip49/bip49_conf.py +366 -0
  69. coinex/ccxt/static_dependencies/bip/conf/bip49/bip49_conf_getter.py +80 -0
  70. coinex/ccxt/static_dependencies/bip/conf/bip84/__init__.py +3 -0
  71. coinex/ccxt/static_dependencies/bip/conf/bip84/bip84_coins.py +39 -0
  72. coinex/ccxt/static_dependencies/bip/conf/bip84/bip84_conf.py +113 -0
  73. coinex/ccxt/static_dependencies/bip/conf/bip84/bip84_conf_getter.py +66 -0
  74. coinex/ccxt/static_dependencies/bip/conf/bip86/__init__.py +3 -0
  75. coinex/ccxt/static_dependencies/bip/conf/bip86/bip86_coins.py +37 -0
  76. coinex/ccxt/static_dependencies/bip/conf/bip86/bip86_conf.py +83 -0
  77. coinex/ccxt/static_dependencies/bip/conf/bip86/bip86_conf_getter.py +64 -0
  78. coinex/ccxt/static_dependencies/bip/conf/common/__init__.py +8 -0
  79. coinex/ccxt/static_dependencies/bip/conf/common/atom_addr.py +104 -0
  80. coinex/ccxt/static_dependencies/bip/conf/common/bip_bitcoin_cash_conf.py +106 -0
  81. coinex/ccxt/static_dependencies/bip/conf/common/bip_coin_conf.py +217 -0
  82. coinex/ccxt/static_dependencies/bip/conf/common/bip_coins.py +28 -0
  83. coinex/ccxt/static_dependencies/bip/conf/common/bip_conf_const.py +30 -0
  84. coinex/ccxt/static_dependencies/bip/conf/common/bip_litecoin_conf.py +121 -0
  85. coinex/ccxt/static_dependencies/bip/ecc/__init__.py +42 -0
  86. coinex/ccxt/static_dependencies/bip/ecc/common/__init__.py +0 -0
  87. coinex/ccxt/static_dependencies/bip/ecc/common/dummy_point.py +219 -0
  88. coinex/ccxt/static_dependencies/bip/ecc/common/ikeys.py +263 -0
  89. coinex/ccxt/static_dependencies/bip/ecc/common/ipoint.py +190 -0
  90. coinex/ccxt/static_dependencies/bip/ecc/conf.py +28 -0
  91. coinex/ccxt/static_dependencies/bip/ecc/curve/__init__.py +0 -0
  92. coinex/ccxt/static_dependencies/bip/ecc/curve/elliptic_curve.py +121 -0
  93. coinex/ccxt/static_dependencies/bip/ecc/curve/elliptic_curve_getter.py +74 -0
  94. coinex/ccxt/static_dependencies/bip/ecc/curve/elliptic_curve_types.py +37 -0
  95. coinex/ccxt/static_dependencies/bip/ecc/ecdsa/__init__.py +0 -0
  96. coinex/ccxt/static_dependencies/bip/ecc/ecdsa/ecdsa_keys.py +36 -0
  97. coinex/ccxt/static_dependencies/bip/ecc/secp256k1/__init__.py +0 -0
  98. coinex/ccxt/static_dependencies/bip/ecc/secp256k1/secp256k1.py +36 -0
  99. coinex/ccxt/static_dependencies/bip/ecc/secp256k1/secp256k1_const.py +59 -0
  100. coinex/ccxt/static_dependencies/bip/ecc/secp256k1/secp256k1_keys_ecdsa.py +248 -0
  101. coinex/ccxt/static_dependencies/bip/ecc/secp256k1/secp256k1_point_ecdsa.py +234 -0
  102. coinex/ccxt/static_dependencies/bip/slip/__init__.py +0 -0
  103. coinex/ccxt/static_dependencies/bip/slip/slip173/__init__.py +1 -0
  104. coinex/ccxt/static_dependencies/bip/slip/slip173/slip173.py +60 -0
  105. coinex/ccxt/static_dependencies/bip/slip/slip32/__init__.py +4 -0
  106. coinex/ccxt/static_dependencies/bip/slip/slip32/slip32.py +322 -0
  107. coinex/ccxt/static_dependencies/bip/slip/slip32/slip32_key_net_ver.py +62 -0
  108. coinex/ccxt/static_dependencies/bip/slip/slip44/__init__.py +1 -0
  109. coinex/ccxt/static_dependencies/bip/slip/slip44/slip44.py +81 -0
  110. coinex/ccxt/static_dependencies/bip/utils/__init__.py +0 -0
  111. coinex/ccxt/static_dependencies/bip/utils/conf/__init__.py +1 -0
  112. coinex/ccxt/static_dependencies/bip/utils/conf/coin_names.py +59 -0
  113. coinex/ccxt/static_dependencies/bip/utils/crypto/__init__.py +10 -0
  114. coinex/ccxt/static_dependencies/bip/utils/crypto/aes_ecb.py +152 -0
  115. coinex/ccxt/static_dependencies/bip/utils/crypto/blake2.py +191 -0
  116. coinex/ccxt/static_dependencies/bip/utils/crypto/chacha20_poly1305.py +101 -0
  117. coinex/ccxt/static_dependencies/bip/utils/crypto/hash160.py +57 -0
  118. coinex/ccxt/static_dependencies/bip/utils/crypto/hmac.py +118 -0
  119. coinex/ccxt/static_dependencies/bip/utils/crypto/pbkdf2.py +66 -0
  120. coinex/ccxt/static_dependencies/bip/utils/crypto/ripemd.py +58 -0
  121. coinex/ccxt/static_dependencies/bip/utils/crypto/scrypt.py +66 -0
  122. coinex/ccxt/static_dependencies/bip/utils/crypto/sha2.py +182 -0
  123. coinex/ccxt/static_dependencies/bip/utils/crypto/sha3.py +99 -0
  124. coinex/ccxt/static_dependencies/bip/utils/misc/__init__.py +7 -0
  125. coinex/ccxt/static_dependencies/bip/utils/misc/algo.py +108 -0
  126. coinex/ccxt/static_dependencies/bip/utils/misc/base32.py +151 -0
  127. coinex/ccxt/static_dependencies/bip/utils/misc/bit.py +115 -0
  128. coinex/ccxt/static_dependencies/bip/utils/misc/bytes.py +200 -0
  129. coinex/ccxt/static_dependencies/bip/utils/misc/data_bytes.py +181 -0
  130. coinex/ccxt/static_dependencies/bip/utils/misc/integer.py +97 -0
  131. coinex/ccxt/static_dependencies/bip/utils/misc/string.py +54 -0
  132. coinex/ccxt/static_dependencies/bip/utils/typing/__init__.py +1 -0
  133. coinex/ccxt/static_dependencies/bip/utils/typing/literal.py +27 -0
  134. coinex/ccxt/static_dependencies/bip/wif/__init__.py +1 -0
  135. coinex/ccxt/static_dependencies/bip/wif/wif.py +144 -0
  136. coinex/ccxt/static_dependencies/dydx_v4_client/amino/amino_pb2.py +31 -0
  137. coinex/ccxt/static_dependencies/dydx_v4_client/cosmos/base/v1beta1/coin_pb2.py +47 -0
  138. coinex/ccxt/static_dependencies/dydx_v4_client/cosmos/crypto/multisig/keys_pb2.py +33 -0
  139. coinex/ccxt/static_dependencies/dydx_v4_client/cosmos/crypto/multisig/v1beta1/multisig_pb2.py +33 -0
  140. coinex/ccxt/static_dependencies/dydx_v4_client/cosmos/crypto/secp256k1/keys_pb2.py +34 -0
  141. coinex/ccxt/static_dependencies/dydx_v4_client/cosmos/msg/v1/msg_pb2.py +27 -0
  142. coinex/ccxt/static_dependencies/dydx_v4_client/cosmos/tx/signing/v1beta1/signing_pb2.py +38 -0
  143. coinex/ccxt/static_dependencies/dydx_v4_client/cosmos/tx/v1beta1/tx_pb2.py +75 -0
  144. coinex/ccxt/static_dependencies/dydx_v4_client/cosmos_proto/cosmos_pb2.py +36 -0
  145. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/accountplus/accountplus_pb2.py +31 -0
  146. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/accountplus/genesis_pb2.py +40 -0
  147. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/accountplus/models_pb2.py +26 -0
  148. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/accountplus/params_pb2.py +29 -0
  149. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/accountplus/query_pb2.py +57 -0
  150. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/accountplus/tx_pb2.py +51 -0
  151. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/block_rate_limit_config_pb2.py +37 -0
  152. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/clob_pair_pb2.py +41 -0
  153. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/equity_tier_limit_config_pb2.py +35 -0
  154. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/finalize_block_pb2.py +27 -0
  155. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/liquidations_config_pb2.py +39 -0
  156. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/liquidations_pb2.py +38 -0
  157. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/matches_pb2.py +55 -0
  158. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/mev_pb2.py +49 -0
  159. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/operation_pb2.py +32 -0
  160. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/order_pb2.py +86 -0
  161. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/order_removals_pb2.py +32 -0
  162. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/process_proposer_matches_events_pb2.py +42 -0
  163. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/query_pb2.py +124 -0
  164. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/streaming_pb2.py +29 -0
  165. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/clob/tx_pb2.py +117 -0
  166. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/sending/genesis_pb2.py +26 -0
  167. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/sending/query_pb2.py +26 -0
  168. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/sending/transfer_pb2.py +61 -0
  169. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/sending/tx_pb2.py +37 -0
  170. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/subaccounts/asset_position_pb2.py +29 -0
  171. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/subaccounts/genesis_pb2.py +30 -0
  172. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/subaccounts/perpetual_position_pb2.py +33 -0
  173. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/subaccounts/query_pb2.py +63 -0
  174. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/subaccounts/streaming_pb2.py +31 -0
  175. coinex/ccxt/static_dependencies/dydx_v4_client/dydxprotocol/subaccounts/subaccount_pb2.py +33 -0
  176. coinex/ccxt/static_dependencies/dydx_v4_client/gogoproto/gogo_pb2.py +102 -0
  177. coinex/ccxt/static_dependencies/dydx_v4_client/registry.py +38 -0
  178. coinex/ccxt/static_dependencies/ecdsa/ellipticcurve.py +842 -0
  179. coinex/ccxt/static_dependencies/ecdsa/keys.py +15 -4
  180. coinex/ccxt/static_dependencies/mnemonic/__init__.py +4 -0
  181. coinex/ccxt/static_dependencies/mnemonic/mnemonic.py +282 -0
  182. coinex/ccxt/static_dependencies/mnemonic/py.typed +1 -0
  183. coinex/ccxt/static_dependencies/mnemonic/wordlist/chinese_simplified.txt +2048 -0
  184. coinex/ccxt/static_dependencies/mnemonic/wordlist/chinese_traditional.txt +2048 -0
  185. coinex/ccxt/static_dependencies/mnemonic/wordlist/czech.txt +2048 -0
  186. coinex/ccxt/static_dependencies/mnemonic/wordlist/english.txt +2048 -0
  187. coinex/ccxt/static_dependencies/mnemonic/wordlist/french.txt +2048 -0
  188. coinex/ccxt/static_dependencies/mnemonic/wordlist/italian.txt +2048 -0
  189. coinex/ccxt/static_dependencies/mnemonic/wordlist/japanese.txt +2048 -0
  190. coinex/ccxt/static_dependencies/mnemonic/wordlist/korean.txt +2048 -0
  191. coinex/ccxt/static_dependencies/mnemonic/wordlist/portuguese.txt +2048 -0
  192. coinex/ccxt/static_dependencies/mnemonic/wordlist/russian.txt +2048 -0
  193. coinex/ccxt/static_dependencies/mnemonic/wordlist/spanish.txt +2048 -0
  194. coinex/ccxt/static_dependencies/mnemonic/wordlist/turkish.txt +2048 -0
  195. {coinex_api-0.0.89.dist-info → coinex_api-0.0.110.dist-info}/METADATA +3 -3
  196. {coinex_api-0.0.89.dist-info → coinex_api-0.0.110.dist-info}/RECORD +197 -17
  197. {coinex_api-0.0.89.dist-info → coinex_api-0.0.110.dist-info}/WHEEL +1 -1
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.5.5'
7
+ __version__ = '4.5.25'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -33,7 +33,7 @@ from ccxt.base.decimal_to_precision import decimal_to_precision
33
33
  from ccxt.base.decimal_to_precision import DECIMAL_PLACES, TICK_SIZE, NO_PADDING, TRUNCATE, ROUND, ROUND_UP, ROUND_DOWN, SIGNIFICANT_DIGITS
34
34
  from ccxt.base.decimal_to_precision import number_to_string
35
35
  from ccxt.base.precise import Precise
36
- from ccxt.base.types import ConstructorArgs, BalanceAccount, Currency, IndexType, OrderSide, OrderType, Trade, OrderRequest, Market, MarketType, Str, Num, Strings, CancellationRequest, Bool
36
+ from ccxt.base.types import ConstructorArgs, BalanceAccount, Currency, IndexType, OrderSide, OrderType, Trade, OrderRequest, Market, MarketType, Str, Num, Strings, CancellationRequest, Bool, Order
37
37
 
38
38
  # -----------------------------------------------------------------------------
39
39
 
@@ -50,6 +50,11 @@ from cryptography.hazmat.primitives.serialization import load_pem_private_key
50
50
  from ccxt.static_dependencies import ecdsa
51
51
  from ccxt.static_dependencies import keccak
52
52
 
53
+ try:
54
+ import coincurve
55
+ except ImportError:
56
+ coincurve = None
57
+
53
58
  # eddsa signing
54
59
  try:
55
60
  import axolotl_curve25519 as eddsa
@@ -67,6 +72,28 @@ from ccxt.static_dependencies.starknet.hash.address import compute_address
67
72
  from ccxt.static_dependencies.starknet.hash.selector import get_selector_from_name
68
73
  from ccxt.static_dependencies.starknet.hash.utils import message_signature, private_to_stark_key
69
74
  from ccxt.static_dependencies.starknet.utils.typed_data import TypedData as TypedDataDataclass
75
+
76
+ # dydx
77
+ try:
78
+ from ccxt.static_dependencies.mnemonic import Mnemonic
79
+ from ccxt.static_dependencies.bip import Bip44
80
+ from ccxt.static_dependencies.dydx_v4_client.cosmos.tx.signing.v1beta1.signing_pb2 import SignMode
81
+ from ccxt.static_dependencies.dydx_v4_client.cosmos.tx.v1beta1.tx_pb2 import (
82
+ AuthInfo,
83
+ Fee,
84
+ ModeInfo,
85
+ SignDoc,
86
+ SignerInfo,
87
+ Tx,
88
+ TxBody,
89
+ TxRaw,
90
+ )
91
+ from ccxt.static_dependencies.dydx_v4_client.registry import (
92
+ encode_as_any,
93
+ )
94
+ except ImportError:
95
+ encode_as_any = None
96
+
70
97
  try:
71
98
  import apexpro.zklink_sdk as zklink_sdk
72
99
  except ImportError:
@@ -311,8 +338,7 @@ class Exchange(object):
311
338
  bidsasks = None
312
339
  base_currencies = None
313
340
  quote_currencies = None
314
- currencies = None
315
-
341
+ currencies = {}
316
342
  options = None # Python does not allow to define properties in run-time with setattr
317
343
  isSandboxModeEnabled = False
318
344
  accounts = None
@@ -650,71 +676,66 @@ class Exchange(object):
650
676
 
651
677
  @staticmethod
652
678
  def key_exists(dictionary, key):
653
- if hasattr(dictionary, '__getitem__') and not isinstance(dictionary, str):
654
- if isinstance(dictionary, list) and type(key) is not int:
655
- return False
656
- try:
657
- value = dictionary[key]
658
- return value is not None and value != ''
659
- except LookupError:
660
- return False
661
- return False
679
+ try:
680
+ value = dictionary[key]
681
+ return value is not None and value != ''
682
+ except Exception:
683
+ # catch any exception, not only (KeyError, IndexError, TypeError):
684
+ return False
662
685
 
663
686
  @staticmethod
664
687
  def safe_float(dictionary, key, default_value=None):
665
- value = default_value
666
688
  try:
667
- if Exchange.key_exists(dictionary, key):
668
- value = float(dictionary[key])
669
- except ValueError as e:
670
- value = default_value
671
- return value
689
+ return float(dictionary[key])
690
+ except Exception:
691
+ return default_value
672
692
 
673
693
  @staticmethod
674
694
  def safe_string(dictionary, key, default_value=None):
675
- return str(dictionary[key]) if Exchange.key_exists(dictionary, key) else default_value
695
+ try:
696
+ value = dictionary[key]
697
+ if value is not None and value != '':
698
+ return str(value)
699
+ except Exception:
700
+ pass
701
+ return default_value
676
702
 
677
703
  @staticmethod
678
704
  def safe_string_lower(dictionary, key, default_value=None):
679
- if Exchange.key_exists(dictionary, key):
680
- return str(dictionary[key]).lower()
681
- else:
682
- return default_value.lower() if default_value is not None else default_value
705
+ try:
706
+ value = dictionary[key]
707
+ if value is not None and value != '':
708
+ return str(value).lower()
709
+ except Exception:
710
+ pass
711
+ return default_value.lower() if default_value is not None else default_value
683
712
 
684
713
  @staticmethod
685
714
  def safe_string_upper(dictionary, key, default_value=None):
686
- if Exchange.key_exists(dictionary, key):
687
- return str(dictionary[key]).upper()
688
- else:
689
- return default_value.upper() if default_value is not None else default_value
715
+ try:
716
+ value = dictionary[key]
717
+ if value is not None and value != '':
718
+ return str(value).upper()
719
+ except Exception:
720
+ pass
721
+ return default_value.upper() if default_value is not None else default_value
690
722
 
691
723
  @staticmethod
692
724
  def safe_integer(dictionary, key, default_value=None):
693
- if not Exchange.key_exists(dictionary, key):
694
- return default_value
695
- value = dictionary[key]
696
725
  try:
697
726
  # needed to avoid breaking on "100.0"
698
727
  # https://stackoverflow.com/questions/1094717/convert-a-string-to-integer-with-decimal-in-python#1094721
699
- return int(float(value))
700
- except ValueError:
701
- return default_value
702
- except TypeError:
728
+ return int(float(dictionary[key]))
729
+ except Exception:
730
+ # catch any exception, not only (KeyError, IndexError, TypeError, ValueError):
703
731
  return default_value
704
732
 
705
733
  @staticmethod
706
734
  def safe_integer_product(dictionary, key, factor, default_value=None):
707
- if not Exchange.key_exists(dictionary, key):
735
+ try:
736
+ return int(float(dictionary[key]) * factor)
737
+ except Exception:
708
738
  return default_value
709
- value = dictionary[key]
710
- if isinstance(value, Number):
711
- return int(value * factor)
712
- elif isinstance(value, str):
713
- try:
714
- return int(float(value) * factor)
715
- except ValueError:
716
- pass
717
- return default_value
718
739
 
719
740
  @staticmethod
720
741
  def safe_timestamp(dictionary, key, default_value=None):
@@ -722,35 +743,72 @@ class Exchange(object):
722
743
 
723
744
  @staticmethod
724
745
  def safe_value(dictionary, key, default_value=None):
725
- return dictionary[key] if Exchange.key_exists(dictionary, key) else default_value
746
+ try:
747
+ value = dictionary[key]
748
+ if value is not None and value != '':
749
+ return value
750
+ except Exception:
751
+ pass
752
+ return default_value
726
753
 
727
754
  # we're not using safe_floats with a list argument as we're trying to save some cycles here
728
755
  # we're not using safe_float_3 either because those cases are too rare to deserve their own optimization
729
756
 
730
757
  @staticmethod
731
758
  def safe_float_2(dictionary, key1, key2, default_value=None):
732
- return Exchange.safe_either(Exchange.safe_float, dictionary, key1, key2, default_value)
759
+ try:
760
+ return float(dictionary[key1])
761
+ except Exception:
762
+ try:
763
+ return float(dictionary[key2])
764
+ except Exception:
765
+ return default_value
733
766
 
734
767
  @staticmethod
735
768
  def safe_string_2(dictionary, key1, key2, default_value=None):
736
- return Exchange.safe_either(Exchange.safe_string, dictionary, key1, key2, default_value)
769
+ try:
770
+ value = dictionary[key1]
771
+ if value is not None and value != '':
772
+ return str(value)
773
+ except Exception:
774
+ pass
775
+ try:
776
+ value = dictionary[key2]
777
+ if value is not None and value != '':
778
+ return str(value)
779
+ except Exception:
780
+ pass
781
+ return default_value
737
782
 
738
783
  @staticmethod
739
784
  def safe_string_lower_2(dictionary, key1, key2, default_value=None):
740
- return Exchange.safe_either(Exchange.safe_string_lower, dictionary, key1, key2, default_value)
785
+ value = Exchange.safe_string_2(dictionary, key1, key2, default_value)
786
+ return value.lower() if value is not None else value
741
787
 
742
788
  @staticmethod
743
789
  def safe_string_upper_2(dictionary, key1, key2, default_value=None):
744
- return Exchange.safe_either(Exchange.safe_string_upper, dictionary, key1, key2, default_value)
790
+ value = Exchange.safe_string_2(dictionary, key1, key2, default_value)
791
+ return value.upper() if value is not None else value
745
792
 
746
793
  @staticmethod
747
794
  def safe_integer_2(dictionary, key1, key2, default_value=None):
748
- return Exchange.safe_either(Exchange.safe_integer, dictionary, key1, key2, default_value)
795
+ try:
796
+ return int(float(dictionary[key1]))
797
+ except Exception:
798
+ try:
799
+ return int(float(dictionary[key2]))
800
+ except Exception:
801
+ return default_value
749
802
 
750
803
  @staticmethod
751
804
  def safe_integer_product_2(dictionary, key1, key2, factor, default_value=None):
752
- value = Exchange.safe_integer_product(dictionary, key1, factor)
753
- return value if value is not None else Exchange.safe_integer_product(dictionary, key2, factor, default_value)
805
+ try:
806
+ return int(float(dictionary[key1]) * factor)
807
+ except Exception:
808
+ try:
809
+ return int(float(dictionary[key2]) * factor)
810
+ except Exception:
811
+ return default_value
754
812
 
755
813
  @staticmethod
756
814
  def safe_timestamp_2(dictionary, key1, key2, default_value=None):
@@ -758,7 +816,19 @@ class Exchange(object):
758
816
 
759
817
  @staticmethod
760
818
  def safe_value_2(dictionary, key1, key2, default_value=None):
761
- return Exchange.safe_either(Exchange.safe_value, dictionary, key1, key2, default_value)
819
+ try:
820
+ value = dictionary[key1]
821
+ if value is not None and value != '':
822
+ return value
823
+ except Exception:
824
+ pass
825
+ try:
826
+ value = dictionary[key2]
827
+ if value is not None and value != '':
828
+ return value
829
+ except Exception:
830
+ pass
831
+ return default_value
762
832
 
763
833
  # safe_method_n methods family
764
834
 
@@ -768,10 +838,9 @@ class Exchange(object):
768
838
  if value is None:
769
839
  return default_value
770
840
  try:
771
- value = float(value)
841
+ return float(value)
772
842
  except ValueError as e:
773
- value = default_value
774
- return value
843
+ return default_value
775
844
 
776
845
  @staticmethod
777
846
  def safe_string_n(dictionary, key_list, default_value=None):
@@ -892,6 +961,10 @@ class Exchange(object):
892
961
  def uuidv1():
893
962
  return str(uuid.uuid1()).replace('-', '')
894
963
 
964
+ @staticmethod
965
+ def uuid5(namespace: str, name):
966
+ return str(uuid.uuid5(uuid.UUID(namespace), name))
967
+
895
968
  @staticmethod
896
969
  def capitalize(string): # first character only, rest characters unchanged
897
970
  # the native pythonic .capitalize() method lowercases all other characters
@@ -1356,7 +1429,7 @@ class Exchange(object):
1356
1429
  )
1357
1430
  return {
1358
1431
  'privateKey': privateKey,
1359
- 'publicKey': publicKey,
1432
+ 'publicKey': hex(publicKey),
1360
1433
  'address': hex(address)
1361
1434
  }
1362
1435
 
@@ -1385,6 +1458,8 @@ class Exchange(object):
1385
1458
  @staticmethod
1386
1459
  def starknet_sign (msg_hash, pri):
1387
1460
  # // TODO: unify to ecdsa
1461
+ if isinstance(pri, str):
1462
+ pri = int(pri, 16)
1388
1463
  r, s = message_signature(msg_hash, pri)
1389
1464
  return Exchange.json([hex(r), hex(s)])
1390
1465
 
@@ -1402,6 +1477,21 @@ class Exchange(object):
1402
1477
 
1403
1478
  @staticmethod
1404
1479
  def ecdsa(request, secret, algorithm='p256', hash=None, fixed_length=False):
1480
+ """
1481
+ ECDSA signing with support for multiple algorithms and coincurve for SECP256K1.
1482
+ Args:
1483
+ request: The message to sign
1484
+ secret: The private key (hex string or PEM format)
1485
+ algorithm: The elliptic curve algorithm ('p192', 'p224', 'p256', 'p384', 'p521', 'secp256k1')
1486
+ hash: The hash function to use (defaults to algorithm-specific hash)
1487
+ fixed_length: Whether to ensure fixed-length signatures (for deterministic signing)
1488
+ Note: coincurve produces non-deterministic signatures
1489
+ Returns:
1490
+ dict: {'r': r_value, 's': s_value, 'v': v_value}
1491
+ Note:
1492
+ If coincurve is not available or fails for SECP256K1, the method automatically
1493
+ falls back to the standard ecdsa implementation.
1494
+ """
1405
1495
  # your welcome - frosty00
1406
1496
  algorithms = {
1407
1497
  'p192': [ecdsa.NIST192p, 'sha256'],
@@ -1413,6 +1503,14 @@ class Exchange(object):
1413
1503
  }
1414
1504
  if algorithm not in algorithms:
1415
1505
  raise ArgumentsRequired(algorithm + ' is not a supported algorithm')
1506
+ # Use coincurve for SECP256K1 if available
1507
+ if algorithm == 'secp256k1' and coincurve is not None:
1508
+ try:
1509
+ return Exchange._ecdsa_secp256k1_coincurve(request, secret, hash, fixed_length)
1510
+ except Exception:
1511
+ # If coincurve fails, fall back to ecdsa implementation
1512
+ pass
1513
+ # Fall back to original ecdsa implementation for other algorithms or when deterministic signing is needed
1416
1514
  curve_info = algorithms[algorithm]
1417
1515
  hash_function = getattr(hashlib, curve_info[1])
1418
1516
  encoded_request = Exchange.encode(request)
@@ -1448,6 +1546,53 @@ class Exchange(object):
1448
1546
 
1449
1547
 
1450
1548
  @staticmethod
1549
+ def _ecdsa_secp256k1_coincurve(request, secret, hash=None, fixed_length=False):
1550
+ """
1551
+ Use coincurve library for SECP256K1 ECDSA signing.
1552
+ This method provides faster SECP256K1 signing using the coincurve library,
1553
+ which is a Python binding to libsecp256k1. This implementation produces
1554
+ deterministic signatures (RFC 6979) using coincurve's sign_recoverable method.
1555
+ Args:
1556
+ request: The message to sign
1557
+ secret: The private key (hex string or PEM format)
1558
+ hash: The hash function to use
1559
+ fixed_length: Not used in coincurve implementation (signatures are always fixed length)
1560
+ Returns:
1561
+ dict: {'r': r_value, 's': s_value, 'v': v_value}
1562
+ Raises:
1563
+ ArgumentsRequired: If coincurve library is not available
1564
+ """
1565
+ encoded_request = Exchange.encode(request)
1566
+ if hash is not None:
1567
+ digest = Exchange.hash(encoded_request, hash, 'binary')
1568
+ else:
1569
+ digest = base64.b16decode(encoded_request, casefold=True)
1570
+ if isinstance(secret, str):
1571
+ secret = Exchange.encode(secret)
1572
+ # Handle PEM format
1573
+ if secret.find(b'-----BEGIN EC PRIVATE KEY-----') > -1:
1574
+ secret = base64.b16decode(secret.replace(b'-----BEGIN EC PRIVATE KEY-----', b'').replace(b'-----END EC PRIVATE KEY-----', b'').replace(b'\n', b'').replace(b'\r', b''), casefold=True)
1575
+ else:
1576
+ # Assume hex format
1577
+ secret = base64.b16decode(secret, casefold=True)
1578
+ # Create coincurve PrivateKey
1579
+ private_key = coincurve.PrivateKey(secret)
1580
+ # Sign the digest using sign_recoverable which produces deterministic signatures (RFC 6979)
1581
+ # The signature format is: 32 bytes r + 32 bytes s + 1 byte recovery_id (v)
1582
+ signature = private_key.sign_recoverable(digest, hasher=None)
1583
+ # Extract r, s, and v from the recoverable signature (65 bytes total)
1584
+ r_binary = signature[:32]
1585
+ s_binary = signature[32:64]
1586
+ v = signature[64]
1587
+ # Convert to hex strings
1588
+ r = Exchange.decode(base64.b16encode(r_binary)).lower()
1589
+ s = Exchange.decode(base64.b16encode(s_binary)).lower()
1590
+ return {
1591
+ 'r': r,
1592
+ 's': s,
1593
+ 'v': v,
1594
+ }
1595
+
1451
1596
  def binary_to_urlencoded_base64(data: bytes) -> str:
1452
1597
  encoded = base64.urlsafe_b64encode(data).decode("utf-8")
1453
1598
  return encoded.rstrip("=")
@@ -1479,9 +1624,10 @@ class Exchange(object):
1479
1624
 
1480
1625
  @staticmethod
1481
1626
  def is_json_encoded_object(input):
1482
- return (isinstance(input, str) and
1483
- (len(input) >= 2) and
1484
- ((input[0] == '{') or (input[0] == '[')))
1627
+ try:
1628
+ return (isinstance(input, str) and ((input[0] == '{') or (input[0] == '[')))
1629
+ except Exception:
1630
+ return False
1485
1631
 
1486
1632
  @staticmethod
1487
1633
  def encode(string):
@@ -1879,6 +2025,121 @@ class Exchange(object):
1879
2025
  def is_binary_message(self, message):
1880
2026
  return isinstance(message, bytes) or isinstance(message, bytearray)
1881
2027
 
2028
+ def retrieve_dydx_credentials(self, entropy):
2029
+ mnemo = Mnemonic("english")
2030
+ if ' ' in entropy:
2031
+ mnemonic = entropy
2032
+ else:
2033
+ mnemonic = mnemo.to_mnemonic(self.base16_to_binary(entropy))
2034
+ seed = mnemo.to_seed(mnemonic)
2035
+ keyPair = Bip44.FromSeed(seed).DeriveDefaultPath()
2036
+ privateKey = keyPair.PrivateKey().Raw().ToBytes()
2037
+ publicKey = keyPair.PublicKey().RawCompressed().ToBytes()
2038
+ return {
2039
+ 'mnemonic': mnemonic,
2040
+ 'privateKey': privateKey,
2041
+ 'publicKey': publicKey,
2042
+ }
2043
+
2044
+ def load_dydx_protos(self):
2045
+ return
2046
+
2047
+ def to_dydx_long(self, num):
2048
+ return num
2049
+
2050
+ def encode_dydx_tx_for_simulation(self, message, memo, sequence, publicKey):
2051
+ if not encode_as_any:
2052
+ raise NotSupported(self.id + ' requires protobuf to encode messages, please install it with `pip install "protobuf==5.29.5"`')
2053
+ messages = [
2054
+ encode_as_any(
2055
+ message,
2056
+ )
2057
+ ]
2058
+ body = TxBody(
2059
+ messages=messages,
2060
+ memo=memo,
2061
+ )
2062
+ auth_info = AuthInfo(
2063
+ signer_infos=[
2064
+ SignerInfo(
2065
+ public_key=encode_as_any({
2066
+ 'typeUrl': '/cosmos.crypto.secp256k1.PubKey',
2067
+ 'value': publicKey,
2068
+ }),
2069
+ sequence=self.parse_to_int(sequence),
2070
+ mode_info=ModeInfo(single=ModeInfo.Single(mode=SignMode.SIGN_MODE_UNSPECIFIED))
2071
+ )
2072
+ ],
2073
+ fee={},
2074
+ )
2075
+ tx = Tx(body=body, auth_info=auth_info, signatures=[b''])
2076
+ return self.binary_to_base64(tx.SerializeToString())
2077
+
2078
+ def encode_dydx_tx_for_signing(self, message, memo, chainId, account, authenticators, fee=None):
2079
+ if not encode_as_any:
2080
+ raise NotSupported(self.id + ' requires protobuf to encode messages, please install it with `pip install "protobuf==5.29.5"`')
2081
+ if fee is None:
2082
+ fee = {
2083
+ 'amount': [],
2084
+ 'gasLimit': 1000000,
2085
+ }
2086
+ non_critical_extension_options = []
2087
+ if authenticators is not None:
2088
+ non_critical_extension_options.append(encode_as_any({
2089
+ 'typeUrl': '/dydxprotocol.accountplus.TxExtension',
2090
+ 'value': {
2091
+ 'selected_authenticators': authenticators
2092
+ }
2093
+ }))
2094
+ messages = [
2095
+ encode_as_any(
2096
+ message
2097
+ )
2098
+ ]
2099
+ sequence = self.milliseconds()
2100
+ body = TxBody(
2101
+ messages=messages,
2102
+ memo=memo,
2103
+ non_critical_extension_options=non_critical_extension_options,
2104
+ )
2105
+ auth_info = AuthInfo(
2106
+ signer_infos=[
2107
+ SignerInfo(
2108
+ public_key=encode_as_any({
2109
+ 'typeUrl': '/cosmos.crypto.secp256k1.PubKey',
2110
+ 'value': account['pub_key'],
2111
+ }),
2112
+ sequence=self.parse_to_int(sequence),
2113
+ mode_info=ModeInfo(single=ModeInfo.Single(mode=SignMode.SIGN_MODE_DIRECT))
2114
+ )
2115
+ ],
2116
+ fee=Fee(amount=fee['amount'], gas_limit=fee['gasLimit']),
2117
+ )
2118
+ signDoc = SignDoc(
2119
+ account_number=self.parse_to_int(account['account_number']),
2120
+ auth_info_bytes=auth_info.SerializeToString(),
2121
+ body_bytes=body.SerializeToString(),
2122
+ chain_id=chainId,
2123
+ )
2124
+ signingHash = self.hash(signDoc.SerializeToString(), 'sha256', 'hex')
2125
+ return [signingHash, signDoc]
2126
+
2127
+ def encode_dydx_tx_raw(self, signDoc, signature):
2128
+ if not encode_as_any:
2129
+ raise NotSupported(self.id + ' requires protobuf to encode messages, please install it with `pip install "protobuf==5.29.5"`')
2130
+ tx = TxRaw(
2131
+ auth_info_bytes=signDoc.auth_info_bytes,
2132
+ body_bytes=signDoc.body_bytes,
2133
+ signatures=[self.base16ToBinary(signature)],
2134
+ )
2135
+ return '0x' + self.binary_to_base16(tx.SerializeToString())
2136
+
2137
+ def lock_id(self):
2138
+ return None
2139
+
2140
+ def unlock_id(self):
2141
+ return None
2142
+
1882
2143
  # ########################################################################
1883
2144
  # ########################################################################
1884
2145
  # ########################################################################
@@ -1947,8 +2208,10 @@ class Exchange(object):
1947
2208
  'cancelAllOrders': None,
1948
2209
  'cancelAllOrdersWs': None,
1949
2210
  'cancelOrder': True,
2211
+ 'cancelOrderWithClientOrderId': None,
1950
2212
  'cancelOrderWs': None,
1951
2213
  'cancelOrders': None,
2214
+ 'cancelOrdersWithClientOrderId': None,
1952
2215
  'cancelOrdersWs': None,
1953
2216
  'closeAllPositions': None,
1954
2217
  'closePosition': None,
@@ -1998,6 +2261,7 @@ class Exchange(object):
1998
2261
  'createTriggerOrderWs': None,
1999
2262
  'deposit': None,
2000
2263
  'editOrder': 'emulated',
2264
+ 'editOrderWithClientOrderId': None,
2001
2265
  'editOrders': None,
2002
2266
  'editOrderWs': None,
2003
2267
  'fetchAccounts': None,
@@ -2076,6 +2340,7 @@ class Exchange(object):
2076
2340
  'fetchOption': None,
2077
2341
  'fetchOptionChain': None,
2078
2342
  'fetchOrder': None,
2343
+ 'fetchOrderWithClientOrderId': None,
2079
2344
  'fetchOrderBook': True,
2080
2345
  'fetchOrderBooks': None,
2081
2346
  'fetchOrderBookWs': None,
@@ -2285,9 +2550,8 @@ class Exchange(object):
2285
2550
  value = self.safe_value_n(dictionaryOrList, keys, defaultValue)
2286
2551
  if value is None:
2287
2552
  return defaultValue
2288
- if (isinstance(value, dict)):
2289
- if not isinstance(value, list):
2290
- return value
2553
+ if isinstance(value, dict):
2554
+ return value
2291
2555
  return defaultValue
2292
2556
 
2293
2557
  def safe_dict(self, dictionary, key: IndexType, defaultValue: dict = None):
@@ -2348,7 +2612,7 @@ class Exchange(object):
2348
2612
  bookSide.storeArray(bidAsk)
2349
2613
 
2350
2614
  def get_cache_index(self, orderbook, deltas):
2351
- # return the first index of the cache that can be applied to the orderbook or -1 if not possible
2615
+ # return the first index of the cache that can be applied to the orderbook or -1 if not possible.
2352
2616
  return -1
2353
2617
 
2354
2618
  def arrays_concat(self, arraysOfArrays: List[Any]):
@@ -2591,6 +2855,22 @@ class Exchange(object):
2591
2855
  # set flag
2592
2856
  self.isSandboxModeEnabled = False
2593
2857
 
2858
+ def enable_demo_trading(self, enable: bool):
2859
+ """
2860
+ enables or disables demo trading mode
2861
+ :param boolean [enable]: True if demo trading should be enabled, False otherwise
2862
+ """
2863
+ if self.isSandboxModeEnabled:
2864
+ raise NotSupported(self.id + ' demo trading does not support in sandbox environment. Please check https://www.binance.com/en/support/faq/detail/9be58f73e5e14338809e3b705b9687dd to see the differences')
2865
+ if enable:
2866
+ self.urls['apiBackupDemoTrading'] = self.urls['api']
2867
+ self.urls['api'] = self.urls['demo']
2868
+ elif 'apiBackupDemoTrading' in self.urls:
2869
+ self.urls['api'] = self.urls['apiBackupDemoTrading']
2870
+ newUrls = self.omit(self.urls, 'apiBackupDemoTrading')
2871
+ self.urls = newUrls
2872
+ self.options['enableDemoTrading'] = enable
2873
+
2594
2874
  def sign(self, path, api: Any = 'public', method='GET', params={}, headers: Any = None, body: Any = None):
2595
2875
  return {}
2596
2876
 
@@ -2658,6 +2938,12 @@ class Exchange(object):
2658
2938
  def un_watch_ticker(self, symbol: str, params={}):
2659
2939
  raise NotSupported(self.id + ' unWatchTicker() is not supported yet')
2660
2940
 
2941
+ def un_watch_mark_price(self, symbol: str, params={}):
2942
+ raise NotSupported(self.id + ' unWatchMarkPrice() is not supported yet')
2943
+
2944
+ def un_watch_mark_prices(self, symbols: Strings = None, params={}):
2945
+ raise NotSupported(self.id + ' unWatchMarkPrices() is not supported yet')
2946
+
2661
2947
  def fetch_deposit_addresses(self, codes: Strings = None, params={}):
2662
2948
  raise NotSupported(self.id + ' fetchDepositAddresses() is not supported yet')
2663
2949
 
@@ -2927,7 +3213,7 @@ class Exchange(object):
2927
3213
  'delay': 0.001,
2928
3214
  'capacity': 1,
2929
3215
  'cost': 1,
2930
- 'maxCapacity': 1000,
3216
+ 'maxCapacity': self.safe_integer(self.options, 'maxRequestsQueue', 1000),
2931
3217
  'refillRate': refillRate,
2932
3218
  }
2933
3219
  existingBucket = {} if (self.tokenBucket is None) else self.tokenBucket
@@ -3009,6 +3295,79 @@ class Exchange(object):
3009
3295
  featureBlock['symbolRequired'] = self.in_array(key, ['createOrder', 'createOrders', 'fetchOHLCV'])
3010
3296
  return featuresObj
3011
3297
 
3298
+ def feature_value(self, symbol: str, methodName: Str = None, paramName: Str = None, defaultValue: Any = None):
3299
+ """
3300
+ self method is a very deterministic to help users to know what feature is supported by the exchange
3301
+ :param str [symbol]: unified symbol
3302
+ :param str [methodName]: view currently supported methods: https://docs.ccxt.com/#/README?id=features
3303
+ :param str [paramName]: unified param value, like: `triggerPrice`, `stopLoss.triggerPrice`(check docs for supported param names)
3304
+ :param dict [defaultValue]: return default value if no result found
3305
+ :returns dict: returns feature value
3306
+ """
3307
+ market = self.market(symbol)
3308
+ return self.feature_value_by_type(market['type'], market['subType'], methodName, paramName, defaultValue)
3309
+
3310
+ def feature_value_by_type(self, marketType: str, subType: Str, methodName: Str = None, paramName: Str = None, defaultValue: Any = None):
3311
+ """
3312
+ self method is a very deterministic to help users to know what feature is supported by the exchange
3313
+ :param str [marketType]: supported only: "spot", "swap", "future"
3314
+ :param str [subType]: supported only: "linear", "inverse"
3315
+ :param str [methodName]: view currently supported methods: https://docs.ccxt.com/#/README?id=features
3316
+ :param str [paramName]: unified param value(check docs for supported param names)
3317
+ :param dict [defaultValue]: return default value if no result found
3318
+ :returns dict: returns feature value
3319
+ """
3320
+ # if exchange does not yet have features manually implemented
3321
+ if self.features is None:
3322
+ return defaultValue
3323
+ if marketType is None:
3324
+ return defaultValue # marketType is required
3325
+ # if marketType(e.g. 'option') does not exist in features
3326
+ if not (marketType in self.features):
3327
+ return defaultValue # unsupported marketType, check "exchange.features" for details
3328
+ # if marketType dict None
3329
+ if self.features[marketType] is None:
3330
+ return defaultValue
3331
+ methodsContainer = self.features[marketType]
3332
+ if subType is None:
3333
+ if marketType != 'spot':
3334
+ return defaultValue # subType is required for non-spot markets
3335
+ else:
3336
+ if not (subType in self.features[marketType]):
3337
+ return defaultValue # unsupported subType, check "exchange.features" for details
3338
+ # if subType dict None
3339
+ if self.features[marketType][subType] is None:
3340
+ return defaultValue
3341
+ methodsContainer = self.features[marketType][subType]
3342
+ # if user wanted only marketType and didn't provide methodName, eg: featureIsSupported('spot')
3343
+ if methodName is None:
3344
+ return defaultValue if (defaultValue is not None) else methodsContainer
3345
+ if not (methodName in methodsContainer):
3346
+ return defaultValue # unsupported method, check "exchange.features" for details')
3347
+ methodDict = methodsContainer[methodName]
3348
+ if methodDict is None:
3349
+ return defaultValue
3350
+ # if user wanted only method and didn't provide `paramName`, eg: featureIsSupported('swap', 'linear', 'createOrder')
3351
+ if paramName is None:
3352
+ return defaultValue if (defaultValue is not None) else methodDict
3353
+ splited = paramName.split('.') # can be only parent key(`stopLoss`) or with child(`stopLoss.triggerPrice`)
3354
+ parentKey = splited[0]
3355
+ subKey = self.safe_string(splited, 1)
3356
+ if not (parentKey in methodDict):
3357
+ return defaultValue # unsupported paramName, check "exchange.features" for details')
3358
+ dictionary = self.safe_dict(methodDict, parentKey)
3359
+ if dictionary is None:
3360
+ # if the value is not dictionary but a scalar value(or None), return
3361
+ return methodDict[parentKey]
3362
+ else:
3363
+ # return, when calling without subKey eg: featureValueByType('spot', None, 'createOrder', 'stopLoss')
3364
+ if subKey is None:
3365
+ return methodDict[parentKey]
3366
+ # raise an exception for unsupported subKey
3367
+ if not (subKey in methodDict[parentKey]):
3368
+ return defaultValue # unsupported subKey, check "exchange.features" for details
3369
+ return methodDict[parentKey][subKey]
3370
+
3012
3371
  def orderbook_checksum_message(self, symbol: Str):
3013
3372
  return symbol + ' = False'
3014
3373
 
@@ -3275,7 +3634,11 @@ class Exchange(object):
3275
3634
  marketsSortedById = self.keysort(self.markets_by_id)
3276
3635
  self.symbols = list(marketsSortedBySymbol.keys())
3277
3636
  self.ids = list(marketsSortedById.keys())
3637
+ numCurrencies = 0
3278
3638
  if currencies is not None:
3639
+ keys = list(currencies.keys())
3640
+ numCurrencies = len(keys)
3641
+ if numCurrencies > 0:
3279
3642
  # currencies is always None when called in constructor but not when called from loadMarkets
3280
3643
  self.currencies = self.map_to_safe_map(self.deep_extend(self.currencies, currencies))
3281
3644
  else:
@@ -3340,6 +3703,7 @@ class Exchange(object):
3340
3703
  self.symbols = sourceExchange.symbols
3341
3704
  self.ids = sourceExchange.ids
3342
3705
  self.currencies = sourceExchange.currencies
3706
+ self.currencies_by_id = sourceExchange.currencies_by_id
3343
3707
  self.baseCurrencies = sourceExchange.baseCurrencies
3344
3708
  self.quoteCurrencies = sourceExchange.quoteCurrencies
3345
3709
  self.codes = sourceExchange.codes
@@ -3767,7 +4131,7 @@ class Exchange(object):
3767
4131
  if feeDefined:
3768
4132
  fee = self.parse_fee_numeric(fee)
3769
4133
  if not feesDefined:
3770
- # just set it directly, no further processing needed
4134
+ # just set it directly, no further processing needed.
3771
4135
  fees = [fee]
3772
4136
  # 'fees' were set, so reparse them
3773
4137
  reducedFees = self.reduce_fees_by_currency(fees) if self.reduceFees else fees
@@ -4613,7 +4977,8 @@ class Exchange(object):
4613
4977
  if isinstance(e, OperationFailed):
4614
4978
  if i < retries:
4615
4979
  if self.verbose:
4616
- self.log('Request failed with the error: ' + str(e) + ', retrying ' + (i + str(1)) + ' of ' + str(retries) + '...')
4980
+ index = i + 1
4981
+ self.log('Request failed with the error: ' + str(e) + ', retrying ' + str(index) + ' of ' + str(retries) + '...')
4617
4982
  if (retryDelay is not None) and (retryDelay != 0):
4618
4983
  self.sleep(retryDelay)
4619
4984
  else:
@@ -4650,9 +5015,12 @@ class Exchange(object):
4650
5015
  i_count = 6
4651
5016
  tradesLength = len(trades)
4652
5017
  oldest = min(tradesLength, limit)
5018
+ options = self.safe_dict(self.options, 'buildOHLCVC', {})
5019
+ skipZeroPrices = self.safe_bool(options, 'skipZeroPrices', True)
4653
5020
  for i in range(0, oldest):
4654
5021
  trade = trades[i]
4655
5022
  ts = trade['timestamp']
5023
+ price = trade['price']
4656
5024
  if ts < since:
4657
5025
  continue
4658
5026
  openingTime = int(math.floor(ts / ms)) * ms # shift to the edge of m/h/d(but not M)
@@ -4660,22 +5028,25 @@ class Exchange(object):
4660
5028
  continue
4661
5029
  ohlcv_length = len(ohlcvs)
4662
5030
  candle = ohlcv_length - 1
4663
- if (candle == -1) or (openingTime >= self.sum(ohlcvs[candle][i_timestamp], ms)):
5031
+ if skipZeroPrices and not (price > 0) and not (price < 0):
5032
+ continue
5033
+ isFirstCandle = candle == -1
5034
+ if isFirstCandle or openingTime >= self.sum(ohlcvs[candle][i_timestamp], ms):
4664
5035
  # moved to a new timeframe -> create a new candle from opening trade
4665
5036
  ohlcvs.append([
4666
5037
  openingTime, # timestamp
4667
- trade['price'], # O
4668
- trade['price'], # H
4669
- trade['price'], # L
4670
- trade['price'], # C
5038
+ price, # O
5039
+ price, # H
5040
+ price, # L
5041
+ price, # C
4671
5042
  trade['amount'], # V
4672
5043
  1, # count
4673
5044
  ])
4674
5045
  else:
4675
5046
  # still processing the same timeframe -> update opening trade
4676
- ohlcvs[candle][i_high] = max(ohlcvs[candle][i_high], trade['price'])
4677
- ohlcvs[candle][i_low] = min(ohlcvs[candle][i_low], trade['price'])
4678
- ohlcvs[candle][i_close] = trade['price']
5047
+ ohlcvs[candle][i_high] = max(ohlcvs[candle][i_high], price)
5048
+ ohlcvs[candle][i_low] = min(ohlcvs[candle][i_low], price)
5049
+ ohlcvs[candle][i_close] = price
4679
5050
  ohlcvs[candle][i_volume] = self.sum(ohlcvs[candle][i_volume], trade['amount'])
4680
5051
  ohlcvs[candle][i_count] = self.sum(ohlcvs[candle][i_count], 1)
4681
5052
  return ohlcvs
@@ -4697,6 +5068,9 @@ class Exchange(object):
4697
5068
  self.cancel_order(id, symbol)
4698
5069
  return self.create_order(symbol, type, side, amount, price, params)
4699
5070
 
5071
+ def edit_order_with_client_order_id(self, clientOrderId: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
5072
+ return self.edit_order('', symbol, type, side, amount, price, self.extend({'clientOrderId': clientOrderId}, params))
5073
+
4700
5074
  def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
4701
5075
  self.cancel_order_ws(id, symbol)
4702
5076
  return self.create_order_ws(symbol, type, side, amount, price, params)
@@ -4779,10 +5153,6 @@ class Exchange(object):
4779
5153
  })
4780
5154
 
4781
5155
  def safe_market(self, marketId: Str = None, market: Market = None, delimiter: Str = None, marketType: Str = None):
4782
- result = self.safe_market_structure({
4783
- 'symbol': marketId,
4784
- 'marketId': marketId,
4785
- })
4786
5156
  if marketId is not None:
4787
5157
  if (self.markets_by_id is not None) and (marketId in self.markets_by_id):
4788
5158
  markets = self.markets_by_id[marketId]
@@ -4802,20 +5172,22 @@ class Exchange(object):
4802
5172
  elif delimiter is not None and delimiter != '':
4803
5173
  parts = marketId.split(delimiter)
4804
5174
  partsLength = len(parts)
5175
+ result = self.safe_market_structure({
5176
+ 'symbol': marketId,
5177
+ 'marketId': marketId,
5178
+ })
4805
5179
  if partsLength == 2:
4806
5180
  result['baseId'] = self.safe_string(parts, 0)
4807
5181
  result['quoteId'] = self.safe_string(parts, 1)
4808
5182
  result['base'] = self.safe_currency_code(result['baseId'])
4809
5183
  result['quote'] = self.safe_currency_code(result['quoteId'])
4810
5184
  result['symbol'] = result['base'] + '/' + result['quote']
4811
- return result
4812
- else:
4813
- return result
5185
+ return result
4814
5186
  if market is not None:
4815
5187
  return market
4816
- return result
5188
+ return self.safe_market_structure({'symbol': marketId, 'marketId': marketId})
4817
5189
 
4818
- def market_or_null(self, symbol: str):
5190
+ def market_or_null(self, symbol: Str = None):
4819
5191
  if symbol is None:
4820
5192
  return None
4821
5193
  return self.market(symbol)
@@ -5111,6 +5483,17 @@ class Exchange(object):
5111
5483
  def fetch_order(self, id: str, symbol: Str = None, params={}):
5112
5484
  raise NotSupported(self.id + ' fetchOrder() is not supported yet')
5113
5485
 
5486
+ def fetch_order_with_client_order_id(self, clientOrderId: str, symbol: Str = None, params={}):
5487
+ """
5488
+ create a market order by providing the symbol, side and cost
5489
+ :param str clientOrderId: client order Id
5490
+ :param str symbol: unified symbol of the market to create an order in
5491
+ :param dict [params]: extra parameters specific to the exchange API endpoint
5492
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
5493
+ """
5494
+ extendedParams = self.extend(params, {'clientOrderId': clientOrderId})
5495
+ return self.fetch_order('', symbol, extendedParams)
5496
+
5114
5497
  def fetch_order_ws(self, id: str, symbol: Str = None, params={}):
5115
5498
  raise NotSupported(self.id + ' fetchOrderWs() is not supported yet')
5116
5499
 
@@ -5492,9 +5875,34 @@ class Exchange(object):
5492
5875
  def cancel_order(self, id: str, symbol: Str = None, params={}):
5493
5876
  raise NotSupported(self.id + ' cancelOrder() is not supported yet')
5494
5877
 
5878
+ def cancel_order_with_client_order_id(self, clientOrderId: str, symbol: Str = None, params={}):
5879
+ """
5880
+ create a market order by providing the symbol, side and cost
5881
+ :param str clientOrderId: client order Id
5882
+ :param str symbol: unified symbol of the market to create an order in
5883
+ :param dict [params]: extra parameters specific to the exchange API endpoint
5884
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
5885
+ """
5886
+ extendedParams = self.extend(params, {'clientOrderId': clientOrderId})
5887
+ return self.cancel_order('', symbol, extendedParams)
5888
+
5495
5889
  def cancel_order_ws(self, id: str, symbol: Str = None, params={}):
5496
5890
  raise NotSupported(self.id + ' cancelOrderWs() is not supported yet')
5497
5891
 
5892
+ def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
5893
+ raise NotSupported(self.id + ' cancelOrders() is not supported yet')
5894
+
5895
+ def cancel_orders_with_client_order_ids(self, clientOrderIds: List[str], symbol: Str = None, params={}):
5896
+ """
5897
+ create a market order by providing the symbol, side and cost
5898
+ :param str[] clientOrderIds: client order Ids
5899
+ :param str symbol: unified symbol of the market to create an order in
5900
+ :param dict [params]: extra parameters specific to the exchange API endpoint
5901
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
5902
+ """
5903
+ extendedParams = self.extend(params, {'clientOrderIds': clientOrderIds})
5904
+ return self.cancel_orders([], symbol, extendedParams)
5905
+
5498
5906
  def cancel_orders_ws(self, ids: List[str], symbol: Str = None, params={}):
5499
5907
  raise NotSupported(self.id + ' cancelOrdersWs() is not supported yet')
5500
5908
 
@@ -5510,7 +5918,7 @@ class Exchange(object):
5510
5918
  def cancel_all_orders_ws(self, symbol: Str = None, params={}):
5511
5919
  raise NotSupported(self.id + ' cancelAllOrdersWs() is not supported yet')
5512
5920
 
5513
- def cancel_unified_order(self, order, params={}):
5921
+ def cancel_unified_order(self, order: Order, params={}):
5514
5922
  return self.cancel_order(self.safe_string(order, 'id'), self.safe_string(order, 'symbol'), params)
5515
5923
 
5516
5924
  def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
@@ -5659,7 +6067,9 @@ class Exchange(object):
5659
6067
  return self.safe_string(self.commonCurrencies, code, code)
5660
6068
 
5661
6069
  def currency(self, code: str):
5662
- if self.currencies is None:
6070
+ keys = list(self.currencies.keys())
6071
+ numCurrencies = len(keys)
6072
+ if numCurrencies == 0:
5663
6073
  raise ExchangeError(self.id + ' currencies not loaded')
5664
6074
  if isinstance(code, str):
5665
6075
  if code in self.currencies:
@@ -7067,6 +7477,83 @@ class Exchange(object):
7067
7477
  """
7068
7478
  raise NotSupported(self.id + ' fetchTransfers() is not supported yet')
7069
7479
 
7480
+ def un_watch_ohlcv(self, symbol: str, timeframe: str = '1m', params={}):
7481
+ """
7482
+ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
7483
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
7484
+ :param str timeframe: the length of time each candle represents
7485
+ :param dict [params]: extra parameters specific to the exchange API endpoint
7486
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
7487
+ """
7488
+ raise NotSupported(self.id + ' unWatchOHLCV() is not supported yet')
7489
+
7490
+ def watch_mark_price(self, symbol: str, params={}):
7491
+ """
7492
+ watches a mark price for a specific market
7493
+ :param str symbol: unified symbol of the market to fetch the ticker for
7494
+ :param dict [params]: extra parameters specific to the exchange API endpoint
7495
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
7496
+ """
7497
+ raise NotSupported(self.id + ' watchMarkPrice() is not supported yet')
7498
+
7499
+ def watch_mark_prices(self, symbols: Strings = None, params={}):
7500
+ """
7501
+ watches the mark price for all markets
7502
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
7503
+ :param dict [params]: extra parameters specific to the exchange API endpoint
7504
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
7505
+ """
7506
+ raise NotSupported(self.id + ' watchMarkPrices() is not supported yet')
7507
+
7508
+ def withdraw_ws(self, code: str, amount: float, address: str, tag: Str = None, params={}):
7509
+ """
7510
+ make a withdrawal
7511
+ :param str code: unified currency code
7512
+ :param float amount: the amount to withdraw
7513
+ :param str address: the address to withdraw to
7514
+ :param str tag:
7515
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
7516
+ :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
7517
+ """
7518
+ raise NotSupported(self.id + ' withdrawWs() is not supported yet')
7519
+
7520
+ def un_watch_my_trades(self, symbol: Str = None, params={}):
7521
+ """
7522
+ unWatches information on multiple trades made by the user
7523
+ :param str symbol: unified market symbol of the market orders were made in
7524
+ :param dict [params]: extra parameters specific to the exchange API endpoint
7525
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
7526
+ """
7527
+ raise NotSupported(self.id + ' unWatchMyTrades() is not supported yet')
7528
+
7529
+ def create_orders_ws(self, orders: List[OrderRequest], params={}):
7530
+ """
7531
+ create a list of trade orders
7532
+ :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
7533
+ :param dict [params]: extra parameters specific to the exchange API endpoint
7534
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
7535
+ """
7536
+ raise NotSupported(self.id + ' createOrdersWs() is not supported yet')
7537
+
7538
+ def fetch_orders_by_status_ws(self, status: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
7539
+ """
7540
+ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
7541
+ :param str symbol: unified symbol of the market to fetch the order book for
7542
+ :param int [limit]: the maximum amount of order book entries to return
7543
+ :param dict [params]: extra parameters specific to the exchange API endpoint
7544
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
7545
+ """
7546
+ raise NotSupported(self.id + ' fetchOrdersByStatusWs() is not supported yet')
7547
+
7548
+ def un_watch_bids_asks(self, symbols: Strings = None, params={}):
7549
+ """
7550
+ unWatches best bid & ask for symbols
7551
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
7552
+ :param dict [params]: extra parameters specific to the exchange API endpoint
7553
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
7554
+ """
7555
+ raise NotSupported(self.id + ' unWatchBidsAsks() is not supported yet')
7556
+
7070
7557
  def clean_unsubscription(self, client, subHash: str, unsubHash: str, subHashIsPrefix=False):
7071
7558
  if unsubHash in client.subscriptions:
7072
7559
  del client.subscriptions[unsubHash]
@@ -7095,9 +7582,9 @@ class Exchange(object):
7095
7582
  symbols = self.safe_list(subscription, 'symbols', [])
7096
7583
  symbolsLength = len(symbols)
7097
7584
  if topic == 'ohlcv':
7098
- symbolsAndTimeFrames = self.safe_list(subscription, 'symbolsAndTimeframes', [])
7099
- for i in range(0, len(symbolsAndTimeFrames)):
7100
- symbolAndTimeFrame = symbolsAndTimeFrames[i]
7585
+ symbolsAndTimeframes = self.safe_list(subscription, 'symbolsAndTimeframes', [])
7586
+ for i in range(0, len(symbolsAndTimeframes)):
7587
+ symbolAndTimeFrame = symbolsAndTimeframes[i]
7101
7588
  symbol = self.safe_string(symbolAndTimeFrame, 0)
7102
7589
  timeframe = self.safe_string(symbolAndTimeFrame, 1)
7103
7590
  if (self.ohlcvs is not None) and (symbol in self.ohlcvs):
@@ -7115,6 +7602,9 @@ class Exchange(object):
7115
7602
  elif topic == 'ticker':
7116
7603
  if symbol in self.tickers:
7117
7604
  del self.tickers[symbol]
7605
+ elif topic == 'bidsasks':
7606
+ if symbol in self.bidsasks:
7607
+ del self.bidsasks[symbol]
7118
7608
  else:
7119
7609
  if topic == 'myTrades' and (self.myTrades is not None):
7120
7610
  self.myTrades = None
@@ -7128,9 +7618,15 @@ class Exchange(object):
7128
7618
  futures = client.futures
7129
7619
  if (futures is not None) and ('fetchPositionsSnapshot' in futures):
7130
7620
  del futures['fetchPositionsSnapshot']
7131
- elif topic == 'ticker' and (self.tickers is not None):
7621
+ elif (topic == 'ticker' or topic == 'markPrice') and (self.tickers is not None):
7132
7622
  tickerSymbols = list(self.tickers.keys())
7133
7623
  for i in range(0, len(tickerSymbols)):
7134
7624
  tickerSymbol = tickerSymbols[i]
7135
7625
  if tickerSymbol in self.tickers:
7136
7626
  del self.tickers[tickerSymbol]
7627
+ elif topic == 'bidsasks' and (self.bidsasks is not None):
7628
+ bidsaskSymbols = list(self.bidsasks.keys())
7629
+ for i in range(0, len(bidsaskSymbols)):
7630
+ bidsaskSymbol = bidsaskSymbols[i]
7631
+ if bidsaskSymbol in self.bidsasks:
7632
+ del self.bidsasks[bidsaskSymbol]