gate-io-api 0.0.13__py3-none-any.whl → 0.0.14__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 gate-io-api might be problematic. Click here for more details.

Files changed (289) hide show
  1. gate/__init__.py +7 -0
  2. gate/ccxt/__init__.py +101 -0
  3. gate/ccxt/abstract/gate.py +284 -0
  4. gate/ccxt/async_support/__init__.py +80 -0
  5. gate/ccxt/async_support/base/__init__.py +1 -0
  6. gate/ccxt/async_support/base/exchange.py +2100 -0
  7. gate/ccxt/async_support/base/throttler.py +50 -0
  8. gate/ccxt/async_support/base/ws/__init__.py +38 -0
  9. gate/ccxt/async_support/base/ws/aiohttp_client.py +147 -0
  10. gate/ccxt/async_support/base/ws/cache.py +213 -0
  11. gate/ccxt/async_support/base/ws/client.py +214 -0
  12. gate/ccxt/async_support/base/ws/fast_client.py +97 -0
  13. gate/ccxt/async_support/base/ws/functions.py +59 -0
  14. gate/ccxt/async_support/base/ws/future.py +69 -0
  15. gate/ccxt/async_support/base/ws/order_book.py +78 -0
  16. gate/ccxt/async_support/base/ws/order_book_side.py +174 -0
  17. gate/ccxt/async_support/gate.py +7739 -0
  18. gate/ccxt/base/__init__.py +27 -0
  19. gate/ccxt/base/decimal_to_precision.py +174 -0
  20. gate/ccxt/base/errors.py +267 -0
  21. gate/ccxt/base/exchange.py +6769 -0
  22. gate/ccxt/base/precise.py +297 -0
  23. gate/ccxt/base/types.py +577 -0
  24. gate/ccxt/gate.py +7738 -0
  25. gate/ccxt/pro/__init__.py +21 -0
  26. gate/ccxt/pro/gate.py +1950 -0
  27. gate/ccxt/static_dependencies/README.md +1 -0
  28. gate/ccxt/static_dependencies/__init__.py +1 -0
  29. gate/ccxt/static_dependencies/ecdsa/__init__.py +14 -0
  30. gate/ccxt/static_dependencies/ecdsa/_version.py +520 -0
  31. gate/ccxt/static_dependencies/ecdsa/curves.py +56 -0
  32. gate/ccxt/static_dependencies/ecdsa/der.py +221 -0
  33. gate/ccxt/static_dependencies/ecdsa/ecdsa.py +310 -0
  34. gate/ccxt/static_dependencies/ecdsa/ellipticcurve.py +197 -0
  35. gate/ccxt/static_dependencies/ecdsa/keys.py +332 -0
  36. gate/ccxt/static_dependencies/ecdsa/numbertheory.py +531 -0
  37. gate/ccxt/static_dependencies/ecdsa/rfc6979.py +100 -0
  38. gate/ccxt/static_dependencies/ecdsa/util.py +266 -0
  39. gate/ccxt/static_dependencies/ethereum/__init__.py +7 -0
  40. gate/ccxt/static_dependencies/ethereum/abi/__init__.py +16 -0
  41. gate/ccxt/static_dependencies/ethereum/abi/abi.py +19 -0
  42. gate/ccxt/static_dependencies/ethereum/abi/base.py +152 -0
  43. gate/ccxt/static_dependencies/ethereum/abi/codec.py +217 -0
  44. gate/ccxt/static_dependencies/ethereum/abi/constants.py +3 -0
  45. gate/ccxt/static_dependencies/ethereum/abi/decoding.py +565 -0
  46. gate/ccxt/static_dependencies/ethereum/abi/encoding.py +720 -0
  47. gate/ccxt/static_dependencies/ethereum/abi/exceptions.py +139 -0
  48. gate/ccxt/static_dependencies/ethereum/abi/grammar.py +443 -0
  49. gate/ccxt/static_dependencies/ethereum/abi/packed.py +13 -0
  50. gate/ccxt/static_dependencies/ethereum/abi/py.typed +0 -0
  51. gate/ccxt/static_dependencies/ethereum/abi/registry.py +643 -0
  52. gate/ccxt/static_dependencies/ethereum/abi/tools/__init__.py +3 -0
  53. gate/ccxt/static_dependencies/ethereum/abi/tools/_strategies.py +230 -0
  54. gate/ccxt/static_dependencies/ethereum/abi/utils/__init__.py +0 -0
  55. gate/ccxt/static_dependencies/ethereum/abi/utils/numeric.py +83 -0
  56. gate/ccxt/static_dependencies/ethereum/abi/utils/padding.py +27 -0
  57. gate/ccxt/static_dependencies/ethereum/abi/utils/string.py +19 -0
  58. gate/ccxt/static_dependencies/ethereum/account/__init__.py +3 -0
  59. gate/ccxt/static_dependencies/ethereum/account/encode_typed_data/__init__.py +4 -0
  60. gate/ccxt/static_dependencies/ethereum/account/encode_typed_data/encoding_and_hashing.py +239 -0
  61. gate/ccxt/static_dependencies/ethereum/account/encode_typed_data/helpers.py +40 -0
  62. gate/ccxt/static_dependencies/ethereum/account/messages.py +263 -0
  63. gate/ccxt/static_dependencies/ethereum/account/py.typed +0 -0
  64. gate/ccxt/static_dependencies/ethereum/hexbytes/__init__.py +5 -0
  65. gate/ccxt/static_dependencies/ethereum/hexbytes/_utils.py +54 -0
  66. gate/ccxt/static_dependencies/ethereum/hexbytes/main.py +65 -0
  67. gate/ccxt/static_dependencies/ethereum/hexbytes/py.typed +0 -0
  68. gate/ccxt/static_dependencies/ethereum/typing/__init__.py +63 -0
  69. gate/ccxt/static_dependencies/ethereum/typing/abi.py +6 -0
  70. gate/ccxt/static_dependencies/ethereum/typing/bls.py +7 -0
  71. gate/ccxt/static_dependencies/ethereum/typing/discovery.py +5 -0
  72. gate/ccxt/static_dependencies/ethereum/typing/encoding.py +7 -0
  73. gate/ccxt/static_dependencies/ethereum/typing/enums.py +17 -0
  74. gate/ccxt/static_dependencies/ethereum/typing/ethpm.py +9 -0
  75. gate/ccxt/static_dependencies/ethereum/typing/evm.py +20 -0
  76. gate/ccxt/static_dependencies/ethereum/typing/networks.py +1122 -0
  77. gate/ccxt/static_dependencies/ethereum/typing/py.typed +0 -0
  78. gate/ccxt/static_dependencies/ethereum/utils/__init__.py +115 -0
  79. gate/ccxt/static_dependencies/ethereum/utils/abi.py +72 -0
  80. gate/ccxt/static_dependencies/ethereum/utils/address.py +171 -0
  81. gate/ccxt/static_dependencies/ethereum/utils/applicators.py +151 -0
  82. gate/ccxt/static_dependencies/ethereum/utils/conversions.py +190 -0
  83. gate/ccxt/static_dependencies/ethereum/utils/currency.py +107 -0
  84. gate/ccxt/static_dependencies/ethereum/utils/curried/__init__.py +269 -0
  85. gate/ccxt/static_dependencies/ethereum/utils/debug.py +20 -0
  86. gate/ccxt/static_dependencies/ethereum/utils/decorators.py +132 -0
  87. gate/ccxt/static_dependencies/ethereum/utils/encoding.py +6 -0
  88. gate/ccxt/static_dependencies/ethereum/utils/exceptions.py +4 -0
  89. gate/ccxt/static_dependencies/ethereum/utils/functional.py +75 -0
  90. gate/ccxt/static_dependencies/ethereum/utils/hexadecimal.py +74 -0
  91. gate/ccxt/static_dependencies/ethereum/utils/humanize.py +188 -0
  92. gate/ccxt/static_dependencies/ethereum/utils/logging.py +159 -0
  93. gate/ccxt/static_dependencies/ethereum/utils/module_loading.py +31 -0
  94. gate/ccxt/static_dependencies/ethereum/utils/numeric.py +43 -0
  95. gate/ccxt/static_dependencies/ethereum/utils/py.typed +0 -0
  96. gate/ccxt/static_dependencies/ethereum/utils/toolz.py +76 -0
  97. gate/ccxt/static_dependencies/ethereum/utils/types.py +54 -0
  98. gate/ccxt/static_dependencies/ethereum/utils/typing/__init__.py +18 -0
  99. gate/ccxt/static_dependencies/ethereum/utils/typing/misc.py +14 -0
  100. gate/ccxt/static_dependencies/ethereum/utils/units.py +31 -0
  101. gate/ccxt/static_dependencies/keccak/__init__.py +3 -0
  102. gate/ccxt/static_dependencies/keccak/keccak.py +197 -0
  103. gate/ccxt/static_dependencies/lark/__init__.py +38 -0
  104. gate/ccxt/static_dependencies/lark/__pyinstaller/__init__.py +6 -0
  105. gate/ccxt/static_dependencies/lark/__pyinstaller/hook-lark.py +14 -0
  106. gate/ccxt/static_dependencies/lark/ast_utils.py +59 -0
  107. gate/ccxt/static_dependencies/lark/common.py +86 -0
  108. gate/ccxt/static_dependencies/lark/exceptions.py +292 -0
  109. gate/ccxt/static_dependencies/lark/grammar.py +130 -0
  110. gate/ccxt/static_dependencies/lark/grammars/__init__.py +0 -0
  111. gate/ccxt/static_dependencies/lark/grammars/common.lark +59 -0
  112. gate/ccxt/static_dependencies/lark/grammars/lark.lark +62 -0
  113. gate/ccxt/static_dependencies/lark/grammars/python.lark +302 -0
  114. gate/ccxt/static_dependencies/lark/grammars/unicode.lark +7 -0
  115. gate/ccxt/static_dependencies/lark/indenter.py +143 -0
  116. gate/ccxt/static_dependencies/lark/lark.py +658 -0
  117. gate/ccxt/static_dependencies/lark/lexer.py +678 -0
  118. gate/ccxt/static_dependencies/lark/load_grammar.py +1428 -0
  119. gate/ccxt/static_dependencies/lark/parse_tree_builder.py +391 -0
  120. gate/ccxt/static_dependencies/lark/parser_frontends.py +257 -0
  121. gate/ccxt/static_dependencies/lark/parsers/__init__.py +0 -0
  122. gate/ccxt/static_dependencies/lark/parsers/cyk.py +340 -0
  123. gate/ccxt/static_dependencies/lark/parsers/earley.py +314 -0
  124. gate/ccxt/static_dependencies/lark/parsers/earley_common.py +42 -0
  125. gate/ccxt/static_dependencies/lark/parsers/earley_forest.py +801 -0
  126. gate/ccxt/static_dependencies/lark/parsers/grammar_analysis.py +203 -0
  127. gate/ccxt/static_dependencies/lark/parsers/lalr_analysis.py +332 -0
  128. gate/ccxt/static_dependencies/lark/parsers/lalr_interactive_parser.py +158 -0
  129. gate/ccxt/static_dependencies/lark/parsers/lalr_parser.py +122 -0
  130. gate/ccxt/static_dependencies/lark/parsers/lalr_parser_state.py +110 -0
  131. gate/ccxt/static_dependencies/lark/parsers/xearley.py +165 -0
  132. gate/ccxt/static_dependencies/lark/py.typed +0 -0
  133. gate/ccxt/static_dependencies/lark/reconstruct.py +107 -0
  134. gate/ccxt/static_dependencies/lark/tools/__init__.py +70 -0
  135. gate/ccxt/static_dependencies/lark/tools/nearley.py +202 -0
  136. gate/ccxt/static_dependencies/lark/tools/serialize.py +32 -0
  137. gate/ccxt/static_dependencies/lark/tools/standalone.py +196 -0
  138. gate/ccxt/static_dependencies/lark/tree.py +267 -0
  139. gate/ccxt/static_dependencies/lark/tree_matcher.py +186 -0
  140. gate/ccxt/static_dependencies/lark/tree_templates.py +180 -0
  141. gate/ccxt/static_dependencies/lark/utils.py +343 -0
  142. gate/ccxt/static_dependencies/lark/visitors.py +596 -0
  143. gate/ccxt/static_dependencies/marshmallow/__init__.py +81 -0
  144. gate/ccxt/static_dependencies/marshmallow/base.py +65 -0
  145. gate/ccxt/static_dependencies/marshmallow/class_registry.py +94 -0
  146. gate/ccxt/static_dependencies/marshmallow/decorators.py +231 -0
  147. gate/ccxt/static_dependencies/marshmallow/error_store.py +60 -0
  148. gate/ccxt/static_dependencies/marshmallow/exceptions.py +71 -0
  149. gate/ccxt/static_dependencies/marshmallow/fields.py +2114 -0
  150. gate/ccxt/static_dependencies/marshmallow/orderedset.py +89 -0
  151. gate/ccxt/static_dependencies/marshmallow/py.typed +0 -0
  152. gate/ccxt/static_dependencies/marshmallow/schema.py +1228 -0
  153. gate/ccxt/static_dependencies/marshmallow/types.py +12 -0
  154. gate/ccxt/static_dependencies/marshmallow/utils.py +378 -0
  155. gate/ccxt/static_dependencies/marshmallow/validate.py +678 -0
  156. gate/ccxt/static_dependencies/marshmallow/warnings.py +2 -0
  157. gate/ccxt/static_dependencies/marshmallow_dataclass/__init__.py +1047 -0
  158. gate/ccxt/static_dependencies/marshmallow_dataclass/collection_field.py +51 -0
  159. gate/ccxt/static_dependencies/marshmallow_dataclass/lazy_class_attribute.py +45 -0
  160. gate/ccxt/static_dependencies/marshmallow_dataclass/mypy.py +71 -0
  161. gate/ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
  162. gate/ccxt/static_dependencies/marshmallow_dataclass/typing.py +14 -0
  163. gate/ccxt/static_dependencies/marshmallow_dataclass/union_field.py +82 -0
  164. gate/ccxt/static_dependencies/marshmallow_oneofschema/__init__.py +1 -0
  165. gate/ccxt/static_dependencies/marshmallow_oneofschema/one_of_schema.py +193 -0
  166. gate/ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
  167. gate/ccxt/static_dependencies/msgpack/__init__.py +55 -0
  168. gate/ccxt/static_dependencies/msgpack/_cmsgpack.pyx +11 -0
  169. gate/ccxt/static_dependencies/msgpack/_packer.pyx +374 -0
  170. gate/ccxt/static_dependencies/msgpack/_unpacker.pyx +547 -0
  171. gate/ccxt/static_dependencies/msgpack/buff_converter.h +8 -0
  172. gate/ccxt/static_dependencies/msgpack/exceptions.py +48 -0
  173. gate/ccxt/static_dependencies/msgpack/ext.py +168 -0
  174. gate/ccxt/static_dependencies/msgpack/fallback.py +951 -0
  175. gate/ccxt/static_dependencies/msgpack/pack.h +89 -0
  176. gate/ccxt/static_dependencies/msgpack/pack_template.h +820 -0
  177. gate/ccxt/static_dependencies/msgpack/sysdep.h +194 -0
  178. gate/ccxt/static_dependencies/msgpack/unpack.h +391 -0
  179. gate/ccxt/static_dependencies/msgpack/unpack_define.h +95 -0
  180. gate/ccxt/static_dependencies/msgpack/unpack_template.h +464 -0
  181. gate/ccxt/static_dependencies/parsimonious/__init__.py +10 -0
  182. gate/ccxt/static_dependencies/parsimonious/exceptions.py +105 -0
  183. gate/ccxt/static_dependencies/parsimonious/expressions.py +479 -0
  184. gate/ccxt/static_dependencies/parsimonious/grammar.py +487 -0
  185. gate/ccxt/static_dependencies/parsimonious/nodes.py +325 -0
  186. gate/ccxt/static_dependencies/parsimonious/utils.py +40 -0
  187. gate/ccxt/static_dependencies/starknet/__init__.py +0 -0
  188. gate/ccxt/static_dependencies/starknet/abi/v0/__init__.py +2 -0
  189. gate/ccxt/static_dependencies/starknet/abi/v0/model.py +44 -0
  190. gate/ccxt/static_dependencies/starknet/abi/v0/parser.py +216 -0
  191. gate/ccxt/static_dependencies/starknet/abi/v0/schemas.py +72 -0
  192. gate/ccxt/static_dependencies/starknet/abi/v0/shape.py +63 -0
  193. gate/ccxt/static_dependencies/starknet/abi/v1/__init__.py +2 -0
  194. gate/ccxt/static_dependencies/starknet/abi/v1/core_structures.json +14 -0
  195. gate/ccxt/static_dependencies/starknet/abi/v1/model.py +39 -0
  196. gate/ccxt/static_dependencies/starknet/abi/v1/parser.py +220 -0
  197. gate/ccxt/static_dependencies/starknet/abi/v1/parser_transformer.py +179 -0
  198. gate/ccxt/static_dependencies/starknet/abi/v1/schemas.py +66 -0
  199. gate/ccxt/static_dependencies/starknet/abi/v1/shape.py +47 -0
  200. gate/ccxt/static_dependencies/starknet/abi/v2/__init__.py +2 -0
  201. gate/ccxt/static_dependencies/starknet/abi/v2/model.py +89 -0
  202. gate/ccxt/static_dependencies/starknet/abi/v2/parser.py +293 -0
  203. gate/ccxt/static_dependencies/starknet/abi/v2/parser_transformer.py +192 -0
  204. gate/ccxt/static_dependencies/starknet/abi/v2/schemas.py +132 -0
  205. gate/ccxt/static_dependencies/starknet/abi/v2/shape.py +107 -0
  206. gate/ccxt/static_dependencies/starknet/cairo/__init__.py +0 -0
  207. gate/ccxt/static_dependencies/starknet/cairo/data_types.py +123 -0
  208. gate/ccxt/static_dependencies/starknet/cairo/deprecated_parse/__init__.py +0 -0
  209. gate/ccxt/static_dependencies/starknet/cairo/deprecated_parse/cairo_types.py +77 -0
  210. gate/ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser.py +46 -0
  211. gate/ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser_transformer.py +138 -0
  212. gate/ccxt/static_dependencies/starknet/cairo/felt.py +64 -0
  213. gate/ccxt/static_dependencies/starknet/cairo/type_parser.py +121 -0
  214. gate/ccxt/static_dependencies/starknet/cairo/v1/__init__.py +0 -0
  215. gate/ccxt/static_dependencies/starknet/cairo/v1/type_parser.py +59 -0
  216. gate/ccxt/static_dependencies/starknet/cairo/v2/__init__.py +0 -0
  217. gate/ccxt/static_dependencies/starknet/cairo/v2/type_parser.py +77 -0
  218. gate/ccxt/static_dependencies/starknet/ccxt_utils.py +7 -0
  219. gate/ccxt/static_dependencies/starknet/common.py +15 -0
  220. gate/ccxt/static_dependencies/starknet/constants.py +39 -0
  221. gate/ccxt/static_dependencies/starknet/hash/__init__.py +0 -0
  222. gate/ccxt/static_dependencies/starknet/hash/address.py +79 -0
  223. gate/ccxt/static_dependencies/starknet/hash/compiled_class_hash_objects.py +111 -0
  224. gate/ccxt/static_dependencies/starknet/hash/selector.py +16 -0
  225. gate/ccxt/static_dependencies/starknet/hash/storage.py +12 -0
  226. gate/ccxt/static_dependencies/starknet/hash/utils.py +78 -0
  227. gate/ccxt/static_dependencies/starknet/models/__init__.py +0 -0
  228. gate/ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
  229. gate/ccxt/static_dependencies/starknet/serialization/__init__.py +24 -0
  230. gate/ccxt/static_dependencies/starknet/serialization/_calldata_reader.py +40 -0
  231. gate/ccxt/static_dependencies/starknet/serialization/_context.py +142 -0
  232. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/__init__.py +10 -0
  233. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/_common.py +82 -0
  234. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/array_serializer.py +43 -0
  235. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/bool_serializer.py +37 -0
  236. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/byte_array_serializer.py +66 -0
  237. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/cairo_data_serializer.py +71 -0
  238. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/enum_serializer.py +71 -0
  239. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/felt_serializer.py +50 -0
  240. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/named_tuple_serializer.py +58 -0
  241. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/option_serializer.py +43 -0
  242. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/output_serializer.py +40 -0
  243. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/payload_serializer.py +72 -0
  244. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/struct_serializer.py +36 -0
  245. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/tuple_serializer.py +36 -0
  246. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/uint256_serializer.py +76 -0
  247. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/uint_serializer.py +100 -0
  248. gate/ccxt/static_dependencies/starknet/serialization/data_serializers/unit_serializer.py +32 -0
  249. gate/ccxt/static_dependencies/starknet/serialization/errors.py +10 -0
  250. gate/ccxt/static_dependencies/starknet/serialization/factory.py +229 -0
  251. gate/ccxt/static_dependencies/starknet/serialization/function_serialization_adapter.py +110 -0
  252. gate/ccxt/static_dependencies/starknet/serialization/tuple_dataclass.py +59 -0
  253. gate/ccxt/static_dependencies/starknet/utils/__init__.py +0 -0
  254. gate/ccxt/static_dependencies/starknet/utils/constructor_args_translator.py +86 -0
  255. gate/ccxt/static_dependencies/starknet/utils/iterable.py +13 -0
  256. gate/ccxt/static_dependencies/starknet/utils/schema.py +13 -0
  257. gate/ccxt/static_dependencies/starknet/utils/typed_data.py +182 -0
  258. gate/ccxt/static_dependencies/starkware/__init__.py +0 -0
  259. gate/ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
  260. gate/ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
  261. gate/ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
  262. gate/ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
  263. gate/ccxt/static_dependencies/starkware/crypto/utils.py +63 -0
  264. gate/ccxt/static_dependencies/sympy/__init__.py +0 -0
  265. gate/ccxt/static_dependencies/sympy/core/__init__.py +0 -0
  266. gate/ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
  267. gate/ccxt/static_dependencies/sympy/external/__init__.py +0 -0
  268. gate/ccxt/static_dependencies/sympy/external/gmpy.py +345 -0
  269. gate/ccxt/static_dependencies/sympy/external/importtools.py +187 -0
  270. gate/ccxt/static_dependencies/sympy/external/ntheory.py +637 -0
  271. gate/ccxt/static_dependencies/sympy/external/pythonmpq.py +341 -0
  272. gate/ccxt/static_dependencies/toolz/__init__.py +26 -0
  273. gate/ccxt/static_dependencies/toolz/_signatures.py +784 -0
  274. gate/ccxt/static_dependencies/toolz/_version.py +520 -0
  275. gate/ccxt/static_dependencies/toolz/compatibility.py +30 -0
  276. gate/ccxt/static_dependencies/toolz/curried/__init__.py +101 -0
  277. gate/ccxt/static_dependencies/toolz/curried/exceptions.py +22 -0
  278. gate/ccxt/static_dependencies/toolz/curried/operator.py +22 -0
  279. gate/ccxt/static_dependencies/toolz/dicttoolz.py +339 -0
  280. gate/ccxt/static_dependencies/toolz/functoolz.py +1049 -0
  281. gate/ccxt/static_dependencies/toolz/itertoolz.py +1057 -0
  282. gate/ccxt/static_dependencies/toolz/recipes.py +46 -0
  283. gate/ccxt/static_dependencies/toolz/utils.py +9 -0
  284. gate/ccxt/static_dependencies/typing_inspect/__init__.py +0 -0
  285. gate/ccxt/static_dependencies/typing_inspect/typing_inspect.py +851 -0
  286. {gate_io_api-0.0.13.dist-info → gate_io_api-0.0.14.dist-info}/METADATA +1 -1
  287. gate_io_api-0.0.14.dist-info/RECORD +288 -0
  288. gate_io_api-0.0.13.dist-info/RECORD +0 -3
  289. {gate_io_api-0.0.13.dist-info → gate_io_api-0.0.14.dist-info}/WHEEL +0 -0
gate/ccxt/pro/gate.py ADDED
@@ -0,0 +1,1950 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+
6
+ import ccxt.async_support
7
+ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
8
+ import hashlib
9
+ from ccxt.base.types import Any, Balances, Int, Liquidation, Market, MarketType, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade
10
+ from ccxt.async_support.base.ws.client import Client
11
+ from typing import List
12
+ from ccxt.base.errors import ExchangeError
13
+ from ccxt.base.errors import AuthenticationError
14
+ from ccxt.base.errors import ArgumentsRequired
15
+ from ccxt.base.errors import BadRequest
16
+ from ccxt.base.errors import NotSupported
17
+ from ccxt.base.errors import ChecksumError
18
+ from ccxt.base.precise import Precise
19
+
20
+
21
+ from ccxt.async_support import gate as gateAsync
22
+
23
+
24
+ class gate(gateAsync):
25
+
26
+ def describe(self) -> Any:
27
+ return self.deep_extend(super(gate, self).describe(), {
28
+ 'has': {
29
+ 'ws': True,
30
+ 'cancelAllOrdersWs': True,
31
+ 'cancelOrderWs': True,
32
+ 'createMarketBuyOrderWithCostWs': True,
33
+ 'createMarketOrderWs': True,
34
+ 'createMarketOrderWithCostWs': False,
35
+ 'createMarketSellOrderWithCostWs': False,
36
+ 'createOrderWs': True,
37
+ 'createOrdersWs': True,
38
+ 'createPostOnlyOrderWs': True,
39
+ 'createReduceOnlyOrderWs': True,
40
+ 'createStopLimitOrderWs': True,
41
+ 'createStopLossOrderWs': True,
42
+ 'createStopMarketOrderWs': False,
43
+ 'createStopOrderWs': True,
44
+ 'createTakeProfitOrderWs': True,
45
+ 'createTriggerOrderWs': True,
46
+ 'editOrderWs': True,
47
+ 'fetchOrderWs': True,
48
+ 'fetchOrdersWs': False,
49
+ 'fetchOpenOrdersWs': True,
50
+ 'fetchClosedOrdersWs': True,
51
+ 'watchOrderBook': True,
52
+ 'watchBidsAsks': True,
53
+ 'watchTicker': True,
54
+ 'watchTickers': True,
55
+ 'watchTrades': True,
56
+ 'watchTradesForSymbols': True,
57
+ 'watchMyTrades': True,
58
+ 'watchOHLCV': True,
59
+ 'watchBalance': True,
60
+ 'watchOrders': True,
61
+ 'watchLiquidations': False,
62
+ 'watchLiquidationsForSymbols': False,
63
+ 'watchMyLiquidations': True,
64
+ 'watchMyLiquidationsForSymbols': True,
65
+ 'watchPositions': True,
66
+ },
67
+ 'urls': {
68
+ 'api': {
69
+ 'ws': 'wss://ws.gate.io/v4',
70
+ 'spot': 'wss://api.gateio.ws/ws/v4/',
71
+ 'swap': {
72
+ 'usdt': 'wss://fx-ws.gateio.ws/v4/ws/usdt',
73
+ 'btc': 'wss://fx-ws.gateio.ws/v4/ws/btc',
74
+ },
75
+ 'future': {
76
+ 'usdt': 'wss://fx-ws.gateio.ws/v4/ws/delivery/usdt',
77
+ 'btc': 'wss://fx-ws.gateio.ws/v4/ws/delivery/btc',
78
+ },
79
+ 'option': {
80
+ 'usdt': 'wss://op-ws.gateio.live/v4/ws/usdt',
81
+ 'btc': 'wss://op-ws.gateio.live/v4/ws/btc',
82
+ },
83
+ },
84
+ 'test': {
85
+ 'swap': {
86
+ 'usdt': 'wss://fx-ws-testnet.gateio.ws/v4/ws/usdt',
87
+ 'btc': 'wss://fx-ws-testnet.gateio.ws/v4/ws/btc',
88
+ },
89
+ 'future': {
90
+ 'usdt': 'wss://fx-ws-testnet.gateio.ws/v4/ws/usdt',
91
+ 'btc': 'wss://fx-ws-testnet.gateio.ws/v4/ws/btc',
92
+ },
93
+ 'option': {
94
+ 'usdt': 'wss://op-ws-testnet.gateio.live/v4/ws/usdt',
95
+ 'btc': 'wss://op-ws-testnet.gateio.live/v4/ws/btc',
96
+ },
97
+ },
98
+ },
99
+ 'options': {
100
+ 'tradesLimit': 1000,
101
+ 'OHLCVLimit': 1000,
102
+ 'watchTradesSubscriptions': {},
103
+ 'watchTickerSubscriptions': {},
104
+ 'watchOrderBookSubscriptions': {},
105
+ 'watchTicker': {
106
+ 'name': 'tickers', # or book_ticker
107
+ },
108
+ 'watchOrderBook': {
109
+ 'interval': '100ms',
110
+ 'snapshotDelay': 10, # how many deltas to cache before fetching a snapshot
111
+ 'snapshotMaxRetries': 3,
112
+ 'checksum': True,
113
+ },
114
+ 'watchBalance': {
115
+ 'settle': 'usdt', # or btc
116
+ 'spot': 'spot.balances', # spot.margin_balances, spot.funding_balances or spot.cross_balances
117
+ },
118
+ 'watchPositions': {
119
+ 'fetchPositionsSnapshot': True, # or False
120
+ 'awaitPositionsSnapshot': True, # whether to wait for the positions snapshot before providing updates
121
+ },
122
+ },
123
+ 'exceptions': {
124
+ 'ws': {
125
+ 'exact': {
126
+ '1': BadRequest,
127
+ '2': BadRequest,
128
+ '4': AuthenticationError,
129
+ '6': AuthenticationError,
130
+ '11': AuthenticationError,
131
+ },
132
+ 'broad': {},
133
+ },
134
+ },
135
+ })
136
+
137
+ async def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
138
+ """
139
+
140
+ https://www.gate.io/docs/developers/apiv4/ws/en/#order-place
141
+ https://www.gate.io/docs/developers/futures/ws/en/#order-place
142
+
143
+ Create an order on the exchange
144
+ :param str symbol: Unified CCXT market symbol
145
+ :param str type: 'limit' or 'market' *"market" is contract only*
146
+ :param str side: 'buy' or 'sell'
147
+ :param float amount: the amount of currency to trade
148
+ :param float [price]: *ignored in "market" orders* the price at which the order is to be fulfilled at in units of the quote currency
149
+ :param dict [params]: extra parameters specific to the exchange API endpoint
150
+ :param float [params.stopPrice]: The price at which a trigger order is triggered at
151
+ :param str [params.timeInForce]: "GTC", "IOC", or "PO"
152
+ :param float [params.stopLossPrice]: The price at which a stop loss order is triggered at
153
+ :param float [params.takeProfitPrice]: The price at which a take profit order is triggered at
154
+ :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
155
+ :param int [params.iceberg]: Amount to display for the iceberg order, Null or 0 for normal orders, Set to -1 to hide the order completely
156
+ :param str [params.text]: User defined information
157
+ :param str [params.account]: *spot and margin only* "spot", "margin" or "cross_margin"
158
+ :param bool [params.auto_borrow]: *margin only* Used in margin or cross margin trading to allow automatic loan of insufficient amount if balance is not enough
159
+ :param str [params.settle]: *contract only* Unified Currency Code for settle currency
160
+ :param bool [params.reduceOnly]: *contract only* Indicates if self order is to reduce the size of a position
161
+ :param bool [params.close]: *contract only* Set to close the position, with size set to 0
162
+ :param bool [params.auto_size]: *contract only* Set side to close dual-mode position, close_long closes the long side, while close_short the short one, size also needs to be set to 0
163
+ :param int [params.price_type]: *contract only* 0 latest deal price, 1 mark price, 2 index price
164
+ :param float [params.cost]: *spot market buy only* the quote quantity that can be used alternative for the amount
165
+ :returns dict|None: `An order structure <https://docs.ccxt.com/#/?id=order-structure>`
166
+ """
167
+ await self.load_markets()
168
+ market = self.market(symbol)
169
+ symbol = market['symbol']
170
+ messageType = self.get_type_by_market(market)
171
+ channel = messageType + '.order_place'
172
+ url = self.get_url_by_market(market)
173
+ params['textIsRequired'] = True
174
+ request = self.create_order_request(symbol, type, side, amount, price, params)
175
+ await self.authenticate(url, messageType)
176
+ rawOrder = await self.request_private(url, request, channel)
177
+ order = self.parse_order(rawOrder, market)
178
+ return order
179
+
180
+ async def create_orders_ws(self, orders: List[OrderRequest], params={}):
181
+ """
182
+ create a list of trade orders
183
+
184
+ https://www.gate.io/docs/developers/futures/ws/en/#order-batch-place
185
+
186
+ :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
187
+ :param dict [params]: extra parameters specific to the exchange API endpoint
188
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
189
+ """
190
+ await self.load_markets()
191
+ request = self.createOrdersRequest(orders, params)
192
+ firstOrder = orders[0]
193
+ market = self.market(firstOrder['symbol'])
194
+ if market['swap'] is not True:
195
+ raise NotSupported(self.id + ' createOrdersWs is not supported for swap markets')
196
+ messageType = self.get_type_by_market(market)
197
+ channel = messageType + '.order_batch_place'
198
+ url = self.get_url_by_market(market)
199
+ await self.authenticate(url, messageType)
200
+ rawOrders = await self.request_private(url, request, channel)
201
+ return self.parse_orders(rawOrders, market)
202
+
203
+ async def cancel_all_orders_ws(self, symbol: Str = None, params={}):
204
+ """
205
+ cancel all open orders
206
+
207
+ https://www.gate.io/docs/developers/futures/ws/en/#cancel-all-open-orders-matched
208
+ https://www.gate.io/docs/developers/apiv4/ws/en/#order-cancel-all-with-specified-currency-pair
209
+
210
+ :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
211
+ :param dict [params]: extra parameters specific to the exchange API endpoint
212
+ :param str [params.channel]: the channel to use, defaults to spot.order_cancel_cp or futures.order_cancel_cp
213
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
214
+ """
215
+ await self.load_markets()
216
+ market = None if (symbol is None) else self.market(symbol)
217
+ trigger = self.safe_bool_2(params, 'stop', 'trigger')
218
+ messageType = self.get_type_by_market(market)
219
+ channel = messageType + '.order_cancel_cp'
220
+ channel, params = self.handle_option_and_params(params, 'cancelAllOrdersWs', 'channel', channel)
221
+ url = self.get_url_by_market(market)
222
+ params = self.omit(params, ['stop', 'trigger'])
223
+ type, query = self.handle_market_type_and_params('cancelAllOrders', market, params)
224
+ request, requestParams = self.multiOrderSpotPrepareRequest(market, trigger, query) if (type == 'spot') else self.prepareRequest(market, type, query)
225
+ await self.authenticate(url, messageType)
226
+ rawOrders = await self.request_private(url, self.extend(request, requestParams), channel)
227
+ return self.parse_orders(rawOrders, market)
228
+
229
+ async def cancel_order_ws(self, id: str, symbol: Str = None, params={}):
230
+ """
231
+ Cancels an open order
232
+
233
+ https://www.gate.io/docs/developers/apiv4/ws/en/#order-cancel
234
+ https://www.gate.io/docs/developers/futures/ws/en/#order-cancel
235
+
236
+ :param str id: Order id
237
+ :param str symbol: Unified market symbol
238
+ :param dict [params]: Parameters specified by the exchange api
239
+ :param bool [params.trigger]: True if the order to be cancelled is a trigger order
240
+ :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
241
+ """
242
+ await self.load_markets()
243
+ market = None if (symbol is None) else self.market(symbol)
244
+ trigger = self.safe_value_n(params, ['is_stop_order', 'stop', 'trigger'], False)
245
+ params = self.omit(params, ['is_stop_order', 'stop', 'trigger'])
246
+ type, query = self.handle_market_type_and_params('cancelOrder', market, params)
247
+ request, requestParams = self.spotOrderPrepareRequest(market, trigger, query) if (type == 'spot' or type == 'margin') else self.prepareRequest(market, type, query)
248
+ messageType = self.get_type_by_market(market)
249
+ channel = messageType + '.order_cancel'
250
+ url = self.get_url_by_market(market)
251
+ await self.authenticate(url, messageType)
252
+ request['order_id'] = str(id)
253
+ res = await self.request_private(url, self.extend(request, requestParams), channel)
254
+ return self.parse_order(res, market)
255
+
256
+ async def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
257
+ """
258
+ edit a trade order, gate currently only supports the modification of the price or amount fields
259
+
260
+ https://www.gate.io/docs/developers/apiv4/ws/en/#order-amend
261
+ https://www.gate.io/docs/developers/futures/ws/en/#order-amend
262
+
263
+ :param str id: order id
264
+ :param str symbol: unified symbol of the market to create an order in
265
+ :param str type: 'market' or 'limit'
266
+ :param str side: 'buy' or 'sell'
267
+ :param float amount: how much of the currency you want to trade in units of the base currency
268
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
269
+ :param dict [params]: extra parameters specific to the exchange API endpoint
270
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
271
+ """
272
+ await self.load_markets()
273
+ market = self.market(symbol)
274
+ extendedRequest = self.edit_order_request(id, symbol, type, side, amount, price, params)
275
+ messageType = self.get_type_by_market(market)
276
+ channel = messageType + '.order_amend'
277
+ url = self.get_url_by_market(market)
278
+ await self.authenticate(url, messageType)
279
+ rawOrder = await self.request_private(url, extendedRequest, channel)
280
+ return self.parse_order(rawOrder, market)
281
+
282
+ async def fetch_order_ws(self, id: str, symbol: Str = None, params={}):
283
+ """
284
+ Retrieves information on an order
285
+
286
+ https://www.gate.io/docs/developers/apiv4/ws/en/#order-status
287
+ https://www.gate.io/docs/developers/futures/ws/en/#order-status
288
+
289
+ :param str id: Order id
290
+ :param str symbol: Unified market symbol, *required for spot and margin*
291
+ :param dict [params]: Parameters specified by the exchange api
292
+ :param bool [params.trigger]: True if the order being fetched is a trigger order
293
+ :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
294
+ :param str [params.type]: 'spot', 'swap', or 'future', if not provided self.options['defaultMarginMode'] is used
295
+ :param str [params.settle]: 'btc' or 'usdt' - settle currency for perpetual swap and future - market settle currency is used if symbol is not None, default="usdt" for swap and "btc" for future
296
+ :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
297
+ """
298
+ await self.load_markets()
299
+ market = None if (symbol is None) else self.market(symbol)
300
+ request, requestParams = self.fetchOrderRequest(id, symbol, params)
301
+ messageType = self.get_type_by_market(market)
302
+ channel = messageType + '.order_status'
303
+ url = self.get_url_by_market(market)
304
+ await self.authenticate(url, messageType)
305
+ rawOrder = await self.request_private(url, self.extend(request, requestParams), channel)
306
+ return self.parse_order(rawOrder, market)
307
+
308
+ async def fetch_open_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
309
+ """
310
+ fetch all unfilled currently open orders
311
+
312
+ https://www.gate.io/docs/developers/futures/ws/en/#order-list
313
+
314
+ :param str symbol: unified market symbol
315
+ :param int [since]: the earliest time in ms to fetch open orders for
316
+ :param int [limit]: the maximum number of open orders structures to retrieve
317
+ :param dict [params]: extra parameters specific to the exchange API endpoint
318
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
319
+ """
320
+ return await self.fetch_orders_by_status_ws('open', symbol, since, limit, params)
321
+
322
+ async def fetch_closed_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
323
+ """
324
+ fetches information on multiple closed orders made by the user
325
+
326
+ https://www.gate.io/docs/developers/futures/ws/en/#order-list
327
+
328
+ :param str symbol: unified market symbol of the market orders were made in
329
+ :param int [since]: the earliest time in ms to fetch orders for
330
+ :param int [limit]: the maximum number of order structures to retrieve
331
+ :param dict [params]: extra parameters specific to the exchange API endpoint
332
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
333
+ """
334
+ return await self.fetch_orders_by_status_ws('finished', symbol, since, limit, params)
335
+
336
+ async def fetch_orders_by_status_ws(self, status: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
337
+ """
338
+
339
+ https://www.gate.io/docs/developers/futures/ws/en/#order-list
340
+
341
+ fetches information on multiple orders made by the user by status
342
+ :param str status: requested order status
343
+ :param str symbol: unified market symbol of the market orders were made in
344
+ :param int|None [since]: the earliest time in ms to fetch orders for
345
+ :param int|None [limit]: the maximum number of order structures to retrieve
346
+ :param dict [params]: extra parameters specific to the exchange API endpoint
347
+ :param int [params.orderId]: order id to begin at
348
+ :param int [params.limit]: the maximum number of order structures to retrieve
349
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
350
+ """
351
+ await self.load_markets()
352
+ market = None
353
+ if symbol is not None:
354
+ market = self.market(symbol)
355
+ symbol = market['symbol']
356
+ if market['swap'] is not True:
357
+ raise NotSupported(self.id + ' fetchOrdersByStatusWs is only supported by swap markets. Use rest API for other markets')
358
+ request, requestParams = self.prepareOrdersByStatusRequest(status, symbol, since, limit, params)
359
+ newRequest = self.omit(request, ['settle'])
360
+ messageType = self.get_type_by_market(market)
361
+ channel = messageType + '.order_list'
362
+ url = self.get_url_by_market(market)
363
+ await self.authenticate(url, messageType)
364
+ rawOrders = await self.request_private(url, self.extend(newRequest, requestParams), channel)
365
+ orders = self.parse_orders(rawOrders, market)
366
+ return self.filter_by_symbol_since_limit(orders, symbol, since, limit)
367
+
368
+ async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
369
+ """
370
+ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
371
+ :param str symbol: unified symbol of the market to fetch the order book for
372
+ :param int [limit]: the maximum amount of order book entries to return
373
+ :param dict [params]: extra parameters specific to the exchange API endpoint
374
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
375
+ """
376
+ await self.load_markets()
377
+ market = self.market(symbol)
378
+ symbol = market['symbol']
379
+ marketId = market['id']
380
+ interval, query = self.handle_option_and_params(params, 'watchOrderBook', 'interval', '100ms')
381
+ messageType = self.get_type_by_market(market)
382
+ channel = messageType + '.order_book_update'
383
+ messageHash = 'orderbook' + ':' + symbol
384
+ url = self.get_url_by_market(market)
385
+ payload = [marketId, interval]
386
+ if limit is None:
387
+ limit = 100
388
+ if market['contract']:
389
+ stringLimit = str(limit)
390
+ payload.append(stringLimit)
391
+ subscription: dict = {
392
+ 'symbol': symbol,
393
+ 'limit': limit,
394
+ }
395
+ orderbook = await self.subscribe_public(url, messageHash, payload, channel, query, subscription)
396
+ return orderbook.limit()
397
+
398
+ async def un_watch_order_book(self, symbol: str, params={}) -> Any:
399
+ """
400
+ unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
401
+ :param str symbol: unified symbol of the market to fetch the order book for
402
+ :param dict [params]: extra parameters specific to the exchange API endpoint
403
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
404
+ """
405
+ await self.load_markets()
406
+ market = self.market(symbol)
407
+ symbol = market['symbol']
408
+ marketId = market['id']
409
+ interval = '100ms'
410
+ interval, params = self.handle_option_and_params(params, 'watchOrderBook', 'interval', interval)
411
+ messageType = self.get_type_by_market(market)
412
+ channel = messageType + '.order_book_update'
413
+ subMessageHash = 'orderbook' + ':' + symbol
414
+ messageHash = 'unsubscribe:orderbook' + ':' + symbol
415
+ url = self.get_url_by_market(market)
416
+ payload = [marketId, interval]
417
+ limit = self.safe_integer(params, 'limit', 100)
418
+ if market['contract']:
419
+ stringLimit = str(limit)
420
+ payload.append(stringLimit)
421
+ return await self.un_subscribe_public_multiple(url, 'orderbook', [symbol], [messageHash], [subMessageHash], payload, channel, params)
422
+
423
+ def handle_order_book_subscription(self, client: Client, message, subscription):
424
+ symbol = self.safe_string(subscription, 'symbol')
425
+ limit = self.safe_integer(subscription, 'limit')
426
+ self.orderbooks[symbol] = self.order_book({}, limit)
427
+
428
+ def handle_order_book(self, client: Client, message):
429
+ #
430
+ # spot
431
+ #
432
+ # {
433
+ # "time": 1650189272,
434
+ # "channel": "spot.order_book_update",
435
+ # "event": "update",
436
+ # "result": {
437
+ # "t": 1650189272515,
438
+ # "e": "depthUpdate",
439
+ # "E": 1650189272,
440
+ # "s": "GMT_USDT",
441
+ # "U": 140595902,
442
+ # "u": 140595902,
443
+ # "b": [
444
+ # ['2.51518', "228.119"],
445
+ # ['2.50587', "1510.11"],
446
+ # ['2.49944', "67.6"],
447
+ # ],
448
+ # "a": [
449
+ # ['2.5182', "4.199"],
450
+ # ["2.51926", "1874"],
451
+ # ['2.53528', "96.529"],
452
+ # ]
453
+ # }
454
+ # }
455
+ #
456
+ # swap
457
+ #
458
+ # {
459
+ # "id": null,
460
+ # "time": 1650188898,
461
+ # "channel": "futures.order_book_update",
462
+ # "event": "update",
463
+ # "error": null,
464
+ # "result": {
465
+ # "t": 1650188898938,
466
+ # "s": "GMT_USDT",
467
+ # "U": 1577718307,
468
+ # "u": 1577719254,
469
+ # "b": [
470
+ # {p: "2.5178", s: 0},
471
+ # {p: "2.5179", s: 0},
472
+ # {p: "2.518", s: 0},
473
+ # ],
474
+ # "a": [
475
+ # {p: "2.52", s: 0},
476
+ # {p: "2.5201", s: 0},
477
+ # {p: "2.5203", s: 0},
478
+ # ]
479
+ # }
480
+ # }
481
+ #
482
+ channel = self.safe_string(message, 'channel')
483
+ channelParts = channel.split('.')
484
+ rawMarketType = self.safe_string(channelParts, 0)
485
+ isSpot = rawMarketType == 'spot'
486
+ marketType = 'spot' if isSpot else 'contract'
487
+ delta = self.safe_value(message, 'result')
488
+ deltaStart = self.safe_integer(delta, 'U')
489
+ deltaEnd = self.safe_integer(delta, 'u')
490
+ marketId = self.safe_string(delta, 's')
491
+ symbol = self.safe_symbol(marketId, None, '_', marketType)
492
+ messageHash = 'orderbook:' + symbol
493
+ storedOrderBook = self.safe_value(self.orderbooks, symbol, self.order_book({}))
494
+ nonce = self.safe_integer(storedOrderBook, 'nonce')
495
+ if nonce is None:
496
+ cacheLength = 0
497
+ if storedOrderBook is not None:
498
+ cacheLength = len(storedOrderBook.cache)
499
+ snapshotDelay = self.handle_option('watchOrderBook', 'snapshotDelay', 10)
500
+ waitAmount = snapshotDelay if isSpot else 0
501
+ if cacheLength == waitAmount:
502
+ # max limit is 100
503
+ subscription = client.subscriptions[messageHash]
504
+ limit = self.safe_integer(subscription, 'limit')
505
+ self.spawn(self.load_order_book, client, messageHash, symbol, limit, {}) # needed for c#, number of args needs to match
506
+ storedOrderBook.cache.append(delta)
507
+ return
508
+ elif nonce >= deltaEnd:
509
+ return
510
+ elif nonce >= deltaStart - 1:
511
+ self.handle_delta(storedOrderBook, delta)
512
+ else:
513
+ del client.subscriptions[messageHash]
514
+ del self.orderbooks[symbol]
515
+ checksum = self.handle_option('watchOrderBook', 'checksum', True)
516
+ if checksum:
517
+ error = ChecksumError(self.id + ' ' + self.orderbook_checksum_message(symbol))
518
+ client.reject(error, messageHash)
519
+ client.resolve(storedOrderBook, messageHash)
520
+
521
+ def get_cache_index(self, orderBook, cache):
522
+ nonce = self.safe_integer(orderBook, 'nonce')
523
+ firstDelta = cache[0]
524
+ firstDeltaStart = self.safe_integer(firstDelta, 'U')
525
+ if nonce < firstDeltaStart:
526
+ return -1
527
+ for i in range(0, len(cache)):
528
+ delta = cache[i]
529
+ deltaStart = self.safe_integer(delta, 'U')
530
+ deltaEnd = self.safe_integer(delta, 'u')
531
+ if (nonce >= deltaStart - 1) and (nonce < deltaEnd):
532
+ return i
533
+ return len(cache)
534
+
535
+ def handle_bid_asks(self, bookSide, bidAsks):
536
+ for i in range(0, len(bidAsks)):
537
+ bidAsk = bidAsks[i]
538
+ if isinstance(bidAsk, list):
539
+ bookSide.storeArray(self.parse_bid_ask(bidAsk))
540
+ else:
541
+ price = self.safe_float(bidAsk, 'p')
542
+ amount = self.safe_float(bidAsk, 's')
543
+ bookSide.store(price, amount)
544
+
545
+ def handle_delta(self, orderbook, delta):
546
+ timestamp = self.safe_integer(delta, 't')
547
+ orderbook['timestamp'] = timestamp
548
+ orderbook['datetime'] = self.iso8601(timestamp)
549
+ orderbook['nonce'] = self.safe_integer(delta, 'u')
550
+ bids = self.safe_value(delta, 'b', [])
551
+ asks = self.safe_value(delta, 'a', [])
552
+ storedBids = orderbook['bids']
553
+ storedAsks = orderbook['asks']
554
+ self.handle_bid_asks(storedBids, bids)
555
+ self.handle_bid_asks(storedAsks, asks)
556
+
557
+ async def watch_ticker(self, symbol: str, params={}) -> Ticker:
558
+ """
559
+
560
+ https://www.gate.io/docs/developers/apiv4/ws/en/#tickers-channel
561
+
562
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
563
+ :param str symbol: unified symbol of the market to fetch the ticker for
564
+ :param dict [params]: extra parameters specific to the exchange API endpoint
565
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
566
+ """
567
+ await self.load_markets()
568
+ market = self.market(symbol)
569
+ symbol = market['symbol']
570
+ params['callerMethodName'] = 'watchTicker'
571
+ result = await self.watch_tickers([symbol], params)
572
+ return self.safe_value(result, symbol)
573
+
574
+ async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
575
+ """
576
+
577
+ https://www.gate.io/docs/developers/apiv4/ws/en/#tickers-channel
578
+
579
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
580
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
581
+ :param dict [params]: extra parameters specific to the exchange API endpoint
582
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
583
+ """
584
+ return await self.subscribe_watch_tickers_and_bids_asks(symbols, 'watchTickers', self.extend({'method': 'tickers'}, params))
585
+
586
+ def handle_ticker(self, client: Client, message):
587
+ #
588
+ # {
589
+ # "time": 1649326221,
590
+ # "channel": "spot.tickers",
591
+ # "event": "update",
592
+ # "result": {
593
+ # "currency_pair": "BTC_USDT",
594
+ # "last": "43444.82",
595
+ # "lowest_ask": "43444.82",
596
+ # "highest_bid": "43444.81",
597
+ # "change_percentage": "-4.0036",
598
+ # "base_volume": "5182.5412425462",
599
+ # "quote_volume": "227267634.93123952",
600
+ # "high_24h": "47698",
601
+ # "low_24h": "42721.03"
602
+ # }
603
+ # }
604
+ #
605
+ self.handle_ticker_and_bid_ask('ticker', client, message)
606
+
607
+ async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
608
+ """
609
+
610
+ https://www.gate.io/docs/developers/apiv4/ws/en/#best-bid-or-ask-price
611
+ https://www.gate.io/docs/developers/apiv4/ws/en/#order-book-channel
612
+
613
+ watches best bid & ask for symbols
614
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
615
+ :param dict [params]: extra parameters specific to the exchange API endpoint
616
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
617
+ """
618
+ return await self.subscribe_watch_tickers_and_bids_asks(symbols, 'watchBidsAsks', self.extend({'method': 'book_ticker'}, params))
619
+
620
+ def handle_bid_ask(self, client: Client, message):
621
+ #
622
+ # {
623
+ # "time": 1671363004,
624
+ # "time_ms": 1671363004235,
625
+ # "channel": "spot.book_ticker",
626
+ # "event": "update",
627
+ # "result": {
628
+ # "t": 1671363004228,
629
+ # "u": 9793320464,
630
+ # "s": "BTC_USDT",
631
+ # "b": "16716.8",
632
+ # "B": "0.0134",
633
+ # "a": "16716.9",
634
+ # "A": "0.0353"
635
+ # }
636
+ # }
637
+ #
638
+ self.handle_ticker_and_bid_ask('bidask', client, message)
639
+
640
+ async def subscribe_watch_tickers_and_bids_asks(self, symbols: Strings = None, callerMethodName: Str = None, params={}) -> Tickers:
641
+ await self.load_markets()
642
+ callerMethodName, params = self.handle_param_string(params, 'callerMethodName', callerMethodName)
643
+ symbols = self.market_symbols(symbols, None, False)
644
+ market = self.market(symbols[0])
645
+ messageType = self.get_type_by_market(market)
646
+ marketIds = self.market_ids(symbols)
647
+ channelName = None
648
+ channelName, params = self.handle_option_and_params(params, callerMethodName, 'method')
649
+ url = self.get_url_by_market(market)
650
+ channel = messageType + '.' + channelName
651
+ isWatchTickers = callerMethodName.find('watchTicker') >= 0
652
+ prefix = 'ticker' if isWatchTickers else 'bidask'
653
+ messageHashes = []
654
+ for i in range(0, len(symbols)):
655
+ symbol = symbols[i]
656
+ messageHashes.append(prefix + ':' + symbol)
657
+ tickerOrBidAsk = await self.subscribe_public_multiple(url, messageHashes, marketIds, channel, params)
658
+ if self.newUpdates:
659
+ items: dict = {}
660
+ items[tickerOrBidAsk['symbol']] = tickerOrBidAsk
661
+ return items
662
+ result = self.tickers if isWatchTickers else self.bidsasks
663
+ return self.filter_by_array(result, 'symbol', symbols, True)
664
+
665
+ def handle_ticker_and_bid_ask(self, objectName: str, client: Client, message):
666
+ channel = self.safe_string(message, 'channel')
667
+ parts = channel.split('.')
668
+ rawMarketType = self.safe_string(parts, 0)
669
+ marketType = 'contract' if (rawMarketType == 'futures') else 'spot'
670
+ result = self.safe_value(message, 'result')
671
+ results = []
672
+ if isinstance(result, list):
673
+ results = self.safe_list(message, 'result', [])
674
+ else:
675
+ rawTicker = self.safe_dict(message, 'result', {})
676
+ results = [rawTicker]
677
+ isTicker = (objectName == 'ticker') # whether ticker or bid-ask
678
+ for i in range(0, len(results)):
679
+ rawTicker = results[i]
680
+ marketId = self.safe_string(rawTicker, 's')
681
+ market = self.safe_market(marketId, None, '_', marketType)
682
+ parsedItem = self.parse_ticker(rawTicker, market)
683
+ symbol = parsedItem['symbol']
684
+ if isTicker:
685
+ self.tickers[symbol] = parsedItem
686
+ else:
687
+ self.bidsasks[symbol] = parsedItem
688
+ messageHash = objectName + ':' + symbol
689
+ client.resolve(parsedItem, messageHash)
690
+
691
+ async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
692
+ """
693
+ get the list of most recent trades for a particular symbol
694
+ :param str symbol: unified symbol of the market to fetch trades for
695
+ :param int [since]: timestamp in ms of the earliest trade to fetch
696
+ :param int [limit]: the maximum amount of trades to fetch
697
+ :param dict [params]: extra parameters specific to the exchange API endpoint
698
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
699
+ """
700
+ return await self.watch_trades_for_symbols([symbol], since, limit, params)
701
+
702
+ async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
703
+ """
704
+ get the list of most recent trades for a particular symbol
705
+ :param str[] symbols: unified symbol of the market to fetch trades for
706
+ :param int [since]: timestamp in ms of the earliest trade to fetch
707
+ :param int [limit]: the maximum amount of trades to fetch
708
+ :param dict [params]: extra parameters specific to the exchange API endpoint
709
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
710
+ """
711
+ await self.load_markets()
712
+ symbols = self.market_symbols(symbols)
713
+ marketIds = self.market_ids(symbols)
714
+ market = self.market(symbols[0])
715
+ messageType = self.get_type_by_market(market)
716
+ channel = messageType + '.trades'
717
+ messageHashes = []
718
+ for i in range(0, len(symbols)):
719
+ symbol = symbols[i]
720
+ messageHashes.append('trades:' + symbol)
721
+ url = self.get_url_by_market(market)
722
+ trades = await self.subscribe_public_multiple(url, messageHashes, marketIds, channel, params)
723
+ if self.newUpdates:
724
+ first = self.safe_value(trades, 0)
725
+ tradeSymbol = self.safe_string(first, 'symbol')
726
+ limit = trades.getLimit(tradeSymbol, limit)
727
+ return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
728
+
729
+ async def un_watch_trades_for_symbols(self, symbols: List[str], params={}) -> Any:
730
+ """
731
+ get the list of most recent trades for a particular symbol
732
+ :param str[] symbols: unified symbol of the market to fetch trades for
733
+ :param dict [params]: extra parameters specific to the exchange API endpoint
734
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
735
+ """
736
+ await self.load_markets()
737
+ symbols = self.market_symbols(symbols)
738
+ marketIds = self.market_ids(symbols)
739
+ market = self.market(symbols[0])
740
+ messageType = self.get_type_by_market(market)
741
+ channel = messageType + '.trades'
742
+ subMessageHashes = []
743
+ messageHashes = []
744
+ for i in range(0, len(symbols)):
745
+ symbol = symbols[i]
746
+ subMessageHashes.append('trades:' + symbol)
747
+ messageHashes.append('unsubscribe:trades:' + symbol)
748
+ url = self.get_url_by_market(market)
749
+ return await self.un_subscribe_public_multiple(url, 'trades', symbols, messageHashes, subMessageHashes, marketIds, channel, params)
750
+
751
+ async def un_watch_trades(self, symbol: str, params={}) -> Any:
752
+ """
753
+ get the list of most recent trades for a particular symbol
754
+ :param str symbol: unified symbol of the market to fetch trades for
755
+ :param dict [params]: extra parameters specific to the exchange API endpoint
756
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
757
+ """
758
+ return await self.un_watch_trades_for_symbols([symbol], params)
759
+
760
+ def handle_trades(self, client: Client, message):
761
+ #
762
+ # {
763
+ # "time": 1648725035,
764
+ # "channel": "spot.trades",
765
+ # "event": "update",
766
+ # "result": [{
767
+ # "id": 3130257995,
768
+ # "create_time": 1648725035,
769
+ # "create_time_ms": "1648725035923.0",
770
+ # "side": "sell",
771
+ # "currency_pair": "LTC_USDT",
772
+ # "amount": "0.0116",
773
+ # "price": "130.11"
774
+ # }]
775
+ # }
776
+ #
777
+ result = self.safe_value(message, 'result')
778
+ if not isinstance(result, list):
779
+ result = [result]
780
+ parsedTrades = self.parse_trades(result)
781
+ for i in range(0, len(parsedTrades)):
782
+ trade = parsedTrades[i]
783
+ symbol = trade['symbol']
784
+ cachedTrades = self.safe_value(self.trades, symbol)
785
+ if cachedTrades is None:
786
+ limit = self.safe_integer(self.options, 'tradesLimit', 1000)
787
+ cachedTrades = ArrayCache(limit)
788
+ self.trades[symbol] = cachedTrades
789
+ cachedTrades.append(trade)
790
+ hash = 'trades:' + symbol
791
+ client.resolve(cachedTrades, hash)
792
+
793
+ async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
794
+ """
795
+ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
796
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
797
+ :param str timeframe: the length of time each candle represents
798
+ :param int [since]: timestamp in ms of the earliest candle to fetch
799
+ :param int [limit]: the maximum amount of candles to fetch
800
+ :param dict [params]: extra parameters specific to the exchange API endpoint
801
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
802
+ """
803
+ await self.load_markets()
804
+ market = self.market(symbol)
805
+ symbol = market['symbol']
806
+ marketId = market['id']
807
+ interval = self.safe_string(self.timeframes, timeframe, timeframe)
808
+ messageType = self.get_type_by_market(market)
809
+ channel = messageType + '.candlesticks'
810
+ messageHash = 'candles:' + interval + ':' + market['symbol']
811
+ url = self.get_url_by_market(market)
812
+ payload = [interval, marketId]
813
+ ohlcv = await self.subscribe_public(url, messageHash, payload, channel, params)
814
+ if self.newUpdates:
815
+ limit = ohlcv.getLimit(symbol, limit)
816
+ return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
817
+
818
+ def handle_ohlcv(self, client: Client, message):
819
+ #
820
+ # {
821
+ # "time": 1606292600,
822
+ # "channel": "spot.candlesticks",
823
+ # "event": "update",
824
+ # "result": {
825
+ # "t": "1606292580", # total volume
826
+ # "v": "2362.32035", # volume
827
+ # "c": "19128.1", # close
828
+ # "h": "19128.1", # high
829
+ # "l": "19128.1", # low
830
+ # "o": "19128.1", # open
831
+ # "n": "1m_BTC_USDT" # sub
832
+ # }
833
+ # }
834
+ #
835
+ channel = self.safe_string(message, 'channel')
836
+ channelParts = channel.split('.')
837
+ rawMarketType = self.safe_string(channelParts, 0)
838
+ marketType = 'spot' if (rawMarketType == 'spot') else 'contract'
839
+ result = self.safe_value(message, 'result')
840
+ if not isinstance(result, list):
841
+ result = [result]
842
+ marketIds: dict = {}
843
+ for i in range(0, len(result)):
844
+ ohlcv = result[i]
845
+ subscription = self.safe_string(ohlcv, 'n', '')
846
+ parts = subscription.split('_')
847
+ timeframe = self.safe_string(parts, 0)
848
+ timeframeId = self.find_timeframe(timeframe)
849
+ prefix = timeframe + '_'
850
+ marketId = subscription.replace(prefix, '')
851
+ symbol = self.safe_symbol(marketId, None, '_', marketType)
852
+ parsed = self.parse_ohlcv(ohlcv)
853
+ self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
854
+ stored = self.safe_value(self.ohlcvs[symbol], timeframe)
855
+ if stored is None:
856
+ limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
857
+ stored = ArrayCacheByTimestamp(limit)
858
+ self.ohlcvs[symbol][timeframeId] = stored
859
+ stored.append(parsed)
860
+ marketIds[symbol] = timeframe
861
+ keys = list(marketIds.keys())
862
+ for i in range(0, len(keys)):
863
+ symbol = keys[i]
864
+ timeframe = marketIds[symbol]
865
+ interval = self.find_timeframe(timeframe)
866
+ hash = 'candles' + ':' + interval + ':' + symbol
867
+ stored = self.safe_value(self.ohlcvs[symbol], interval)
868
+ client.resolve(stored, hash)
869
+
870
+ async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
871
+ """
872
+ watches information on multiple trades made by the user
873
+ :param str symbol: unified market symbol of the market trades were made in
874
+ :param int [since]: the earliest time in ms to fetch trades for
875
+ :param int [limit]: the maximum number of trade structures to retrieve
876
+ :param dict [params]: extra parameters specific to the exchange API endpoint
877
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
878
+ """
879
+ await self.load_markets()
880
+ subType = None
881
+ type = None
882
+ marketId = '!' + 'all'
883
+ market = None
884
+ if symbol is not None:
885
+ market = self.market(symbol)
886
+ marketId = market['id']
887
+ type, params = self.handle_market_type_and_params('watchMyTrades', market, params)
888
+ subType, params = self.handle_sub_type_and_params('watchMyTrades', market, params)
889
+ messageType = self.get_supported_mapping(type, {
890
+ 'spot': 'spot',
891
+ 'margin': 'spot',
892
+ 'future': 'futures',
893
+ 'swap': 'futures',
894
+ 'option': 'options',
895
+ })
896
+ channel = messageType + '.usertrades'
897
+ messageHash = 'myTrades'
898
+ if symbol is not None:
899
+ messageHash += ':' + symbol
900
+ isInverse = (subType == 'inverse')
901
+ url = self.get_url_by_market_type(type, isInverse)
902
+ payload = [marketId]
903
+ # uid required for non spot markets
904
+ requiresUid = (type != 'spot')
905
+ trades = await self.subscribe_private(url, messageHash, payload, channel, params, requiresUid)
906
+ if self.newUpdates:
907
+ limit = trades.getLimit(symbol, limit)
908
+ return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
909
+
910
+ def handle_my_trades(self, client: Client, message):
911
+ #
912
+ # {
913
+ # "time": 1543205083,
914
+ # "channel": "futures.usertrades",
915
+ # "event": "update",
916
+ # "error": null,
917
+ # "result": [
918
+ # {
919
+ # "id": "3335259",
920
+ # "create_time": 1628736848,
921
+ # "create_time_ms": 1628736848321,
922
+ # "contract": "BTC_USD",
923
+ # "order_id": "4872460",
924
+ # "size": 1,
925
+ # "price": "40000.4",
926
+ # "role": "maker"
927
+ # }
928
+ # ]
929
+ # }
930
+ #
931
+ result = self.safe_value(message, 'result', [])
932
+ tradesLength = len(result)
933
+ if tradesLength == 0:
934
+ return
935
+ cachedTrades = self.myTrades
936
+ if cachedTrades is None:
937
+ limit = self.safe_integer(self.options, 'tradesLimit', 1000)
938
+ cachedTrades = ArrayCacheBySymbolById(limit)
939
+ self.myTrades = cachedTrades
940
+ parsed = self.parse_trades(result)
941
+ marketIds: dict = {}
942
+ for i in range(0, len(parsed)):
943
+ trade = parsed[i]
944
+ cachedTrades.append(trade)
945
+ symbol = trade['symbol']
946
+ marketIds[symbol] = True
947
+ keys = list(marketIds.keys())
948
+ for i in range(0, len(keys)):
949
+ market = keys[i]
950
+ hash = 'myTrades:' + market
951
+ client.resolve(cachedTrades, hash)
952
+ client.resolve(cachedTrades, 'myTrades')
953
+
954
+ async def watch_balance(self, params={}) -> Balances:
955
+ """
956
+ watch balance and get the amount of funds available for trading or funds locked in orders
957
+ :param dict [params]: extra parameters specific to the exchange API endpoint
958
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
959
+ """
960
+ await self.load_markets()
961
+ type = None
962
+ subType = None
963
+ type, params = self.handle_market_type_and_params('watchBalance', None, params)
964
+ subType, params = self.handle_sub_type_and_params('watchBalance', None, params)
965
+ isInverse = (subType == 'inverse')
966
+ url = self.get_url_by_market_type(type, isInverse)
967
+ requiresUid = (type != 'spot')
968
+ channelType = self.get_supported_mapping(type, {
969
+ 'spot': 'spot',
970
+ 'margin': 'spot',
971
+ 'future': 'futures',
972
+ 'swap': 'futures',
973
+ 'option': 'options',
974
+ })
975
+ channel = channelType + '.balances'
976
+ messageHash = type + '.balance'
977
+ return await self.subscribe_private(url, messageHash, None, channel, params, requiresUid)
978
+
979
+ def handle_balance(self, client: Client, message):
980
+ #
981
+ # spot order fill
982
+ # {
983
+ # "time": 1653664351,
984
+ # "channel": "spot.balances",
985
+ # "event": "update",
986
+ # "result": [
987
+ # {
988
+ # "timestamp": "1653664351",
989
+ # "timestamp_ms": "1653664351017",
990
+ # "user": "10406147",
991
+ # "currency": "LTC",
992
+ # "change": "-0.0002000000000000",
993
+ # "total": "0.09986000000000000000",
994
+ # "available": "0.09986000000000000000"
995
+ # }
996
+ # ]
997
+ # }
998
+ #
999
+ # account transfer
1000
+ #
1001
+ # {
1002
+ # "id": null,
1003
+ # "time": 1653665088,
1004
+ # "channel": "futures.balances",
1005
+ # "event": "update",
1006
+ # "error": null,
1007
+ # "result": [
1008
+ # {
1009
+ # "balance": 25.035008537,
1010
+ # "change": 25,
1011
+ # "text": "-",
1012
+ # "time": 1653665088,
1013
+ # "time_ms": 1653665088286,
1014
+ # "type": "dnw",
1015
+ # "user": "10406147"
1016
+ # }
1017
+ # ]
1018
+ # }
1019
+ #
1020
+ # swap order fill
1021
+ # {
1022
+ # "id": null,
1023
+ # "time": 1653665311,
1024
+ # "channel": "futures.balances",
1025
+ # "event": "update",
1026
+ # "error": null,
1027
+ # "result": [
1028
+ # {
1029
+ # "balance": 20.031873037,
1030
+ # "change": -0.0031355,
1031
+ # "text": "LTC_USDT:165551103273",
1032
+ # "time": 1653665311,
1033
+ # "time_ms": 1653665311437,
1034
+ # "type": "fee",
1035
+ # "user": "10406147"
1036
+ # }
1037
+ # ]
1038
+ # }
1039
+ #
1040
+ result = self.safe_value(message, 'result', [])
1041
+ timestamp = self.safe_integer(message, 'time_ms')
1042
+ self.balance['info'] = result
1043
+ self.balance['timestamp'] = timestamp
1044
+ self.balance['datetime'] = self.iso8601(timestamp)
1045
+ for i in range(0, len(result)):
1046
+ rawBalance = result[i]
1047
+ account = self.account()
1048
+ currencyId = self.safe_string(rawBalance, 'currency', 'USDT') # when not present it is USDT
1049
+ code = self.safe_currency_code(currencyId)
1050
+ account['free'] = self.safe_string(rawBalance, 'available')
1051
+ account['total'] = self.safe_string_2(rawBalance, 'total', 'balance')
1052
+ self.balance[code] = account
1053
+ channel = self.safe_string(message, 'channel')
1054
+ parts = channel.split('.')
1055
+ rawType = self.safe_string(parts, 0)
1056
+ channelType = self.get_supported_mapping(rawType, {
1057
+ 'spot': 'spot',
1058
+ 'futures': 'swap',
1059
+ 'options': 'option',
1060
+ })
1061
+ messageHash = channelType + '.balance'
1062
+ self.balance = self.safe_balance(self.balance)
1063
+ client.resolve(self.balance, messageHash)
1064
+
1065
+ async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
1066
+ """
1067
+
1068
+ https://www.gate.io/docs/developers/futures/ws/en/#positions-subscription
1069
+ https://www.gate.io/docs/developers/delivery/ws/en/#positions-subscription
1070
+ https://www.gate.io/docs/developers/options/ws/en/#positions-channel
1071
+
1072
+ watch all open positions
1073
+ :param str[] [symbols]: list of unified market symbols to watch positions for
1074
+ :param int [since]: the earliest time in ms to fetch positions for
1075
+ :param int [limit]: the maximum number of positions to retrieve
1076
+ :param dict params: extra parameters specific to the exchange API endpoint
1077
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
1078
+ """
1079
+ await self.load_markets()
1080
+ market = None
1081
+ symbols = self.market_symbols(symbols)
1082
+ payload = ['!' + 'all']
1083
+ if not self.is_empty(symbols):
1084
+ market = self.get_market_from_symbols(symbols)
1085
+ type = None
1086
+ query = None
1087
+ type, query = self.handle_market_type_and_params('watchPositions', market, params)
1088
+ if type == 'spot':
1089
+ type = 'swap'
1090
+ typeId = self.get_supported_mapping(type, {
1091
+ 'future': 'futures',
1092
+ 'swap': 'futures',
1093
+ 'option': 'options',
1094
+ })
1095
+ messageHash = type + ':positions'
1096
+ if not self.is_empty(symbols):
1097
+ messageHash += '::' + ','.join(symbols)
1098
+ channel = typeId + '.positions'
1099
+ subType = None
1100
+ subType, query = self.handle_sub_type_and_params('watchPositions', market, query)
1101
+ isInverse = (subType == 'inverse')
1102
+ url = self.get_url_by_market_type(type, isInverse)
1103
+ client = self.client(url)
1104
+ self.set_positions_cache(client, type, symbols)
1105
+ fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', True)
1106
+ awaitPositionsSnapshot = self.handle_option('watchPositions', 'awaitPositionsSnapshot', True)
1107
+ cache = self.safe_value(self.positions, type)
1108
+ if fetchPositionsSnapshot and awaitPositionsSnapshot and cache is None:
1109
+ return await client.future(type + ':fetchPositionsSnapshot')
1110
+ positions = await self.subscribe_private(url, messageHash, payload, channel, query, True)
1111
+ if self.newUpdates:
1112
+ return positions
1113
+ return self.filter_by_symbols_since_limit(self.positions[type], symbols, since, limit, True)
1114
+
1115
+ def set_positions_cache(self, client: Client, type, symbols: Strings = None):
1116
+ if self.positions is None:
1117
+ self.positions = {}
1118
+ if type in self.positions:
1119
+ return
1120
+ fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', False)
1121
+ if fetchPositionsSnapshot:
1122
+ messageHash = type + ':fetchPositionsSnapshot'
1123
+ if not (messageHash in client.futures):
1124
+ client.future(messageHash)
1125
+ self.spawn(self.load_positions_snapshot, client, messageHash, type)
1126
+ else:
1127
+ self.positions[type] = ArrayCacheBySymbolBySide()
1128
+
1129
+ async def load_positions_snapshot(self, client, messageHash, type):
1130
+ positions = await self.fetch_positions(None, {'type': type})
1131
+ self.positions[type] = ArrayCacheBySymbolBySide()
1132
+ cache = self.positions[type]
1133
+ for i in range(0, len(positions)):
1134
+ position = positions[i]
1135
+ contracts = self.safe_number(position, 'contracts', 0)
1136
+ if contracts > 0:
1137
+ cache.append(position)
1138
+ # don't remove the future from the .futures cache
1139
+ future = client.futures[messageHash]
1140
+ future.resolve(cache)
1141
+ client.resolve(cache, type + ':position')
1142
+
1143
+ def handle_positions(self, client, message):
1144
+ #
1145
+ # {
1146
+ # time: 1693158497,
1147
+ # time_ms: 1693158497204,
1148
+ # channel: 'futures.positions',
1149
+ # event: 'update',
1150
+ # result: [{
1151
+ # contract: 'XRP_USDT',
1152
+ # cross_leverage_limit: 0,
1153
+ # entry_price: 0.5253,
1154
+ # history_pnl: 0,
1155
+ # history_point: 0,
1156
+ # last_close_pnl: 0,
1157
+ # leverage: 0,
1158
+ # leverage_max: 50,
1159
+ # liq_price: 0.0361,
1160
+ # maintenance_rate: 0.01,
1161
+ # margin: 4.89609962852,
1162
+ # mode: 'single',
1163
+ # realised_pnl: -0.0026265,
1164
+ # realised_point: 0,
1165
+ # risk_limit: 500000,
1166
+ # size: 1,
1167
+ # time: 1693158497,
1168
+ # time_ms: 1693158497195,
1169
+ # update_id: 1,
1170
+ # user: '10444586'
1171
+ # }]
1172
+ # }
1173
+ #
1174
+ type = self.get_market_type_by_url(client.url)
1175
+ data = self.safe_value(message, 'result', [])
1176
+ cache = self.positions[type]
1177
+ newPositions = []
1178
+ for i in range(0, len(data)):
1179
+ rawPosition = data[i]
1180
+ position = self.parse_position(rawPosition)
1181
+ newPositions.append(position)
1182
+ cache.append(position)
1183
+ messageHashes = self.find_message_hashes(client, type + ':positions::')
1184
+ for i in range(0, len(messageHashes)):
1185
+ messageHash = messageHashes[i]
1186
+ parts = messageHash.split('::')
1187
+ symbolsString = parts[1]
1188
+ symbols = symbolsString.split(',')
1189
+ positions = self.filter_by_array(newPositions, 'symbol', symbols, False)
1190
+ if not self.is_empty(positions):
1191
+ client.resolve(positions, messageHash)
1192
+ client.resolve(newPositions, type + ':positions')
1193
+
1194
+ async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1195
+ """
1196
+ watches information on multiple orders made by the user
1197
+ :param str symbol: unified market symbol of the market orders were made in
1198
+ :param int [since]: the earliest time in ms to fetch orders for
1199
+ :param int [limit]: the maximum number of order structures to retrieve
1200
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1201
+ :param str [params.type]: spot, margin, swap, future, or option. Required if listening to all symbols.
1202
+ :param boolean [params.isInverse]: if future, listen to inverse or linear contracts
1203
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1204
+ """
1205
+ await self.load_markets()
1206
+ market = None
1207
+ if symbol is not None:
1208
+ market = self.market(symbol)
1209
+ symbol = market['symbol']
1210
+ type = None
1211
+ query = None
1212
+ type, query = self.handle_market_type_and_params('watchOrders', market, params)
1213
+ typeId = self.get_supported_mapping(type, {
1214
+ 'spot': 'spot',
1215
+ 'margin': 'spot',
1216
+ 'future': 'futures',
1217
+ 'swap': 'futures',
1218
+ 'option': 'options',
1219
+ })
1220
+ channel = typeId + '.orders'
1221
+ messageHash = 'orders'
1222
+ payload = ['!' + 'all']
1223
+ if symbol is not None:
1224
+ messageHash += ':' + market['id']
1225
+ payload = [market['id']]
1226
+ subType = None
1227
+ subType, query = self.handle_sub_type_and_params('watchOrders', market, query)
1228
+ isInverse = (subType == 'inverse')
1229
+ url = self.get_url_by_market_type(type, isInverse)
1230
+ # uid required for non spot markets
1231
+ requiresUid = (type != 'spot')
1232
+ orders = await self.subscribe_private(url, messageHash, payload, channel, query, requiresUid)
1233
+ if self.newUpdates:
1234
+ limit = orders.getLimit(symbol, limit)
1235
+ return self.filter_by_since_limit(orders, since, limit, 'timestamp', True)
1236
+
1237
+ def handle_order(self, client: Client, message):
1238
+ #
1239
+ # {
1240
+ # "time": 1605175506,
1241
+ # "channel": "spot.orders",
1242
+ # "event": "update",
1243
+ # "result": [
1244
+ # {
1245
+ # "id": "30784435",
1246
+ # "user": 123456,
1247
+ # "text": "t-abc",
1248
+ # "create_time": "1605175506",
1249
+ # "create_time_ms": "1605175506123",
1250
+ # "update_time": "1605175506",
1251
+ # "update_time_ms": "1605175506123",
1252
+ # "event": "put",
1253
+ # "currency_pair": "BTC_USDT",
1254
+ # "type": "limit",
1255
+ # "account": "spot",
1256
+ # "side": "sell",
1257
+ # "amount": "1",
1258
+ # "price": "10001",
1259
+ # "time_in_force": "gtc",
1260
+ # "left": "1",
1261
+ # "filled_total": "0",
1262
+ # "fee": "0",
1263
+ # "fee_currency": "USDT",
1264
+ # "point_fee": "0",
1265
+ # "gt_fee": "0",
1266
+ # "gt_discount": True,
1267
+ # "rebated_fee": "0",
1268
+ # "rebated_fee_currency": "USDT"
1269
+ # }
1270
+ # ]
1271
+ # }
1272
+ #
1273
+ orders = self.safe_value(message, 'result', [])
1274
+ limit = self.safe_integer(self.options, 'ordersLimit', 1000)
1275
+ if self.orders is None:
1276
+ self.orders = ArrayCacheBySymbolById(limit)
1277
+ stored = self.orders
1278
+ marketIds: dict = {}
1279
+ parsedOrders = self.parse_orders(orders)
1280
+ for i in range(0, len(parsedOrders)):
1281
+ parsed = parsedOrders[i]
1282
+ # inject order status
1283
+ info = self.safe_value(parsed, 'info')
1284
+ event = self.safe_string(info, 'event')
1285
+ if event == 'put' or event == 'update':
1286
+ parsed['status'] = 'open'
1287
+ elif event == 'finish':
1288
+ status = self.safe_string(parsed, 'status')
1289
+ if status is None:
1290
+ left = self.safe_integer(info, 'left')
1291
+ parsed['status'] = 'closed' if (left == 0) else 'canceled'
1292
+ stored.append(parsed)
1293
+ symbol = parsed['symbol']
1294
+ market = self.market(symbol)
1295
+ marketIds[market['id']] = True
1296
+ keys = list(marketIds.keys())
1297
+ for i in range(0, len(keys)):
1298
+ messageHash = 'orders:' + keys[i]
1299
+ client.resolve(self.orders, messageHash)
1300
+ client.resolve(self.orders, 'orders')
1301
+
1302
+ async def watch_my_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
1303
+ """
1304
+ watch the public liquidations of a trading pair
1305
+
1306
+ https://www.gate.io/docs/developers/futures/ws/en/#liquidates-api
1307
+ https://www.gate.io/docs/developers/delivery/ws/en/#liquidates-api
1308
+ https://www.gate.io/docs/developers/options/ws/en/#liquidates-channel
1309
+
1310
+ :param str symbol: unified CCXT market symbol
1311
+ :param int [since]: the earliest time in ms to fetch liquidations for
1312
+ :param int [limit]: the maximum number of liquidation structures to retrieve
1313
+ :param dict [params]: exchange specific parameters for the bitmex api endpoint
1314
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
1315
+ """
1316
+ return self.watch_my_liquidations_for_symbols([symbol], since, limit, params)
1317
+
1318
+ async def watch_my_liquidations_for_symbols(self, symbols: List[str] = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
1319
+ """
1320
+ watch the private liquidations of a trading pair
1321
+
1322
+ https://www.gate.io/docs/developers/futures/ws/en/#liquidates-api
1323
+ https://www.gate.io/docs/developers/delivery/ws/en/#liquidates-api
1324
+ https://www.gate.io/docs/developers/options/ws/en/#liquidates-channel
1325
+
1326
+ :param str[] symbols: unified CCXT market symbols
1327
+ :param int [since]: the earliest time in ms to fetch liquidations for
1328
+ :param int [limit]: the maximum number of liquidation structures to retrieve
1329
+ :param dict [params]: exchange specific parameters for the gate api endpoint
1330
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
1331
+ """
1332
+ await self.load_markets()
1333
+ symbols = self.market_symbols(symbols, None, True, True)
1334
+ market = self.get_market_from_symbols(symbols)
1335
+ type = None
1336
+ query = None
1337
+ type, query = self.handle_market_type_and_params('watchMyLiquidationsForSymbols', market, params)
1338
+ typeId = self.get_supported_mapping(type, {
1339
+ 'future': 'futures',
1340
+ 'swap': 'futures',
1341
+ 'option': 'options',
1342
+ })
1343
+ subType = None
1344
+ subType, query = self.handle_sub_type_and_params('watchMyLiquidationsForSymbols', market, query)
1345
+ isInverse = (subType == 'inverse')
1346
+ url = self.get_url_by_market_type(type, isInverse)
1347
+ payload = []
1348
+ messageHash = ''
1349
+ if self.is_empty(symbols):
1350
+ if typeId != 'futures' and not isInverse:
1351
+ raise BadRequest(self.id + ' watchMyLiquidationsForSymbols() does not support listening to all symbols, you must call watchMyLiquidations() instead for each symbol you wish to watch.')
1352
+ messageHash = 'myLiquidations'
1353
+ payload.append('not all')
1354
+ else:
1355
+ symbolsLength = len(symbols)
1356
+ if symbolsLength != 1:
1357
+ raise BadRequest(self.id + ' watchMyLiquidationsForSymbols() only allows one symbol at a time. To listen to several symbols call watchMyLiquidationsForSymbols() several times.')
1358
+ messageHash = 'myLiquidations::' + symbols[0]
1359
+ payload.append(market['id'])
1360
+ channel = typeId + '.liquidates'
1361
+ newLiquidations = await self.subscribe_private(url, messageHash, payload, channel, query, True)
1362
+ if self.newUpdates:
1363
+ return newLiquidations
1364
+ return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True)
1365
+
1366
+ def handle_liquidation(self, client: Client, message):
1367
+ #
1368
+ # future / delivery
1369
+ # {
1370
+ # "channel":"futures.liquidates",
1371
+ # "event":"update",
1372
+ # "time":1541505434,
1373
+ # "time_ms":1541505434123,
1374
+ # "result":[
1375
+ # {
1376
+ # "entry_price":209,
1377
+ # "fill_price":215.1,
1378
+ # "left":0,
1379
+ # "leverage":0.0,
1380
+ # "liq_price":213,
1381
+ # "margin":0.007816722941,
1382
+ # "mark_price":213,
1383
+ # "order_id":4093362,
1384
+ # "order_price":215.1,
1385
+ # "size":-124,
1386
+ # "time":1541486601,
1387
+ # "time_ms":1541486601123,
1388
+ # "contract":"BTC_USD",
1389
+ # "user":"1040xxxx"
1390
+ # }
1391
+ # ]
1392
+ # }
1393
+ # option
1394
+ # {
1395
+ # "channel":"options.liquidates",
1396
+ # "event":"update",
1397
+ # "time":1630654851,
1398
+ # "result":[
1399
+ # {
1400
+ # "user":"1xxxx",
1401
+ # "init_margin":1190,
1402
+ # "maint_margin":1042.5,
1403
+ # "order_margin":0,
1404
+ # "time":1639051907,
1405
+ # "time_ms":1639051907000
1406
+ # }
1407
+ # ]
1408
+ # }
1409
+ #
1410
+ rawLiquidations = self.safe_list(message, 'result', [])
1411
+ newLiquidations = []
1412
+ for i in range(0, len(rawLiquidations)):
1413
+ rawLiquidation = rawLiquidations[i]
1414
+ liquidation = self.parse_ws_liquidation(rawLiquidation)
1415
+ symbol = self.safe_string(liquidation, 'symbol')
1416
+ liquidations = self.safe_value(self.liquidations, symbol)
1417
+ if liquidations is None:
1418
+ limit = self.safe_integer(self.options, 'liquidationsLimit', 1000)
1419
+ liquidations = ArrayCache(limit)
1420
+ liquidations.append(liquidation)
1421
+ self.liquidations[symbol] = liquidations
1422
+ client.resolve(liquidations, 'myLiquidations::' + symbol)
1423
+ client.resolve(newLiquidations, 'myLiquidations')
1424
+
1425
+ def parse_ws_liquidation(self, liquidation, market=None):
1426
+ #
1427
+ # future / delivery
1428
+ # {
1429
+ # "entry_price": 209,
1430
+ # "fill_price": 215.1,
1431
+ # "left": 0,
1432
+ # "leverage": 0.0,
1433
+ # "liq_price": 213,
1434
+ # "margin": 0.007816722941,
1435
+ # "mark_price": 213,
1436
+ # "order_id": 4093362,
1437
+ # "order_price": 215.1,
1438
+ # "size": -124,
1439
+ # "time": 1541486601,
1440
+ # "time_ms": 1541486601123,
1441
+ # "contract": "BTC_USD",
1442
+ # "user": "1040xxxx"
1443
+ # }
1444
+ # option
1445
+ # {
1446
+ # "user": "1xxxx",
1447
+ # "init_margin": 1190,
1448
+ # "maint_margin": 1042.5,
1449
+ # "order_margin": 0,
1450
+ # "time": 1639051907,
1451
+ # "time_ms": 1639051907000
1452
+ # }
1453
+ #
1454
+ marketId = self.safe_string(liquidation, 'contract')
1455
+ market = self.safe_market(marketId, market)
1456
+ timestamp = self.safe_integer(liquidation, 'time_ms')
1457
+ originalSize = self.safe_string(liquidation, 'size')
1458
+ left = self.safe_string(liquidation, 'left')
1459
+ amount = Precise.string_abs(Precise.string_sub(originalSize, left))
1460
+ return self.safe_liquidation({
1461
+ 'info': liquidation,
1462
+ 'symbol': self.safe_symbol(marketId, market),
1463
+ 'contracts': self.parse_number(amount),
1464
+ 'contractSize': self.safe_number(market, 'contractSize'),
1465
+ 'price': self.safe_number(liquidation, 'fill_price'),
1466
+ 'baseValue': None,
1467
+ 'quoteValue': None,
1468
+ 'timestamp': timestamp,
1469
+ 'datetime': self.iso8601(timestamp),
1470
+ })
1471
+
1472
+ def handle_error_message(self, client: Client, message):
1473
+ #
1474
+ # {
1475
+ # "time": 1647274664,
1476
+ # "channel": "futures.orders",
1477
+ # "event": "subscribe",
1478
+ # "error": {code: 2, message: "unknown contract BTC_USDT_20220318"},
1479
+ # }
1480
+ # {
1481
+ # "time": 1647276473,
1482
+ # "channel": "futures.orders",
1483
+ # "event": "subscribe",
1484
+ # "error": {
1485
+ # "code": 4,
1486
+ # "message": "{"label":"INVALID_KEY","message":"Invalid key provided"}\n"
1487
+ # },
1488
+ # "result": null
1489
+ # }
1490
+ # {
1491
+ # header: {
1492
+ # response_time: '1718551891329',
1493
+ # status: '400',
1494
+ # channel: 'spot.order_place',
1495
+ # event: 'api',
1496
+ # client_id: '81.34.68.6-0xc16375e2c0',
1497
+ # conn_id: '9539116e0e09678f'
1498
+ # },
1499
+ # data: {errs: {label: 'AUTHENTICATION_FAILED', message: 'Not login'}},
1500
+ # request_id: '10406147'
1501
+ # }
1502
+ # {
1503
+ # "time": 1739853211,
1504
+ # "time_ms": 1739853211201,
1505
+ # "id": 1,
1506
+ # "conn_id": "62f2c1dabbe186d7",
1507
+ # "trace_id": "cdb02a8c0b61086b2fe6f8fad2f98c54",
1508
+ # "channel": "spot.trades",
1509
+ # "event": "subscribe",
1510
+ # "payload": [
1511
+ # "LUNARLENS_USDT",
1512
+ # "ETH_USDT"
1513
+ # ],
1514
+ # "error": {
1515
+ # "code": 2,
1516
+ # "message": "unknown currency pair: LUNARLENS_USDT"
1517
+ # },
1518
+ # "result": {
1519
+ # "status": "fail"
1520
+ # },
1521
+ # "requestId": "cdb02a8c0b61086b2fe6f8fad2f98c54"
1522
+ # }
1523
+ #
1524
+ data = self.safe_dict(message, 'data')
1525
+ errs = self.safe_dict(data, 'errs')
1526
+ error = self.safe_dict(message, 'error', errs)
1527
+ code = self.safe_string_2(error, 'code', 'label')
1528
+ id = self.safe_string_n(message, ['id', 'requestId', 'request_id'])
1529
+ if error is not None:
1530
+ messageHash = self.safe_string(client.subscriptions, id)
1531
+ try:
1532
+ self.throw_exactly_matched_exception(self.exceptions['ws']['exact'], code, self.json(message))
1533
+ self.throw_exactly_matched_exception(self.exceptions['exact'], code, self.json(errs))
1534
+ errorMessage = self.safe_string(error, 'message', self.safe_string(errs, 'message'))
1535
+ self.throw_broadly_matched_exception(self.exceptions['ws']['broad'], errorMessage, self.json(message))
1536
+ raise ExchangeError(self.json(message))
1537
+ except Exception as e:
1538
+ client.reject(e, messageHash)
1539
+ if (messageHash is not None) and (messageHash in client.subscriptions):
1540
+ del client.subscriptions[messageHash]
1541
+ # remove subscriptions for watchSymbols
1542
+ channel = self.safe_string(message, 'channel')
1543
+ if (channel is not None) and (channel.find('.') > 0):
1544
+ parsedChannel = channel.split('.')
1545
+ payload = self.safe_list(message, 'payload', [])
1546
+ for i in range(0, len(payload)):
1547
+ marketType = parsedChannel[0] == 'swap' if 'futures' else parsedChannel[0]
1548
+ symbol = self.safe_symbol(payload[i], None, '_', marketType)
1549
+ messageHashSymbol = parsedChannel[1] + ':' + symbol
1550
+ if (messageHashSymbol is not None) and (messageHashSymbol in client.subscriptions):
1551
+ del client.subscriptions[messageHashSymbol]
1552
+ if (id is not None) and (id in client.subscriptions):
1553
+ del client.subscriptions[id]
1554
+ return True
1555
+ return False
1556
+
1557
+ def handle_balance_subscription(self, client: Client, message, subscription=None):
1558
+ self.balance = {}
1559
+
1560
+ def handle_subscription_status(self, client: Client, message):
1561
+ channel = self.safe_string(message, 'channel')
1562
+ methods: dict = {
1563
+ 'balance': self.handle_balance_subscription,
1564
+ 'spot.order_book_update': self.handle_order_book_subscription,
1565
+ 'futures.order_book_update': self.handle_order_book_subscription,
1566
+ }
1567
+ id = self.safe_string(message, 'id')
1568
+ if channel in methods:
1569
+ subscriptionHash = self.safe_string(client.subscriptions, id)
1570
+ subscription = self.safe_value(client.subscriptions, subscriptionHash)
1571
+ method = methods[channel]
1572
+ method(client, message, subscription)
1573
+ if id in client.subscriptions:
1574
+ del client.subscriptions[id]
1575
+
1576
+ def handle_un_subscribe(self, client: Client, message):
1577
+ #
1578
+ # {
1579
+ # "time":1725534679,
1580
+ # "time_ms":1725534679786,
1581
+ # "id":2,
1582
+ # "conn_id":"fac539b443fd7002",
1583
+ # "trace_id":"efe1d282b630b4aa266b84bee177791a",
1584
+ # "channel":"spot.trades",
1585
+ # "event":"unsubscribe",
1586
+ # "payload":[
1587
+ # "LTC_USDT"
1588
+ # ],
1589
+ # "result":{
1590
+ # "status":"success"
1591
+ # },
1592
+ # "requestId":"efe1d282b630b4aa266b84bee177791a"
1593
+ # }
1594
+ #
1595
+ id = self.safe_string(message, 'id')
1596
+ keys = list(client.subscriptions.keys())
1597
+ for i in range(0, len(keys)):
1598
+ messageHash = keys[i]
1599
+ if not (messageHash in client.subscriptions):
1600
+ continue
1601
+ # the previous iteration can have deleted the messageHash from the subscriptions
1602
+ if messageHash.startswith('unsubscribe'):
1603
+ subscription = client.subscriptions[messageHash]
1604
+ subId = self.safe_string(subscription, 'id')
1605
+ if id != subId:
1606
+ continue
1607
+ messageHashes = self.safe_list(subscription, 'messageHashes', [])
1608
+ subMessageHashes = self.safe_list(subscription, 'subMessageHashes', [])
1609
+ for j in range(0, len(messageHashes)):
1610
+ unsubHash = messageHashes[j]
1611
+ subHash = subMessageHashes[j]
1612
+ self.clean_unsubscription(client, subHash, unsubHash)
1613
+ self.clean_cache(subscription)
1614
+
1615
+ def clean_cache(self, subscription: dict):
1616
+ topic = self.safe_string(subscription, 'topic', '')
1617
+ symbols = self.safe_list(subscription, 'symbols', [])
1618
+ symbolsLength = len(symbols)
1619
+ if topic == 'ohlcv':
1620
+ symbolsAndTimeFrames = self.safe_list(subscription, 'symbolsAndTimeframes', [])
1621
+ for i in range(0, len(symbolsAndTimeFrames)):
1622
+ symbolAndTimeFrame = symbolsAndTimeFrames[i]
1623
+ symbol = self.safe_string(symbolAndTimeFrame, 0)
1624
+ timeframe = self.safe_string(symbolAndTimeFrame, 1)
1625
+ del self.ohlcvs[symbol][timeframe]
1626
+ elif symbolsLength > 0:
1627
+ for i in range(0, len(symbols)):
1628
+ symbol = symbols[i]
1629
+ if topic.endswith('trades'):
1630
+ del self.trades[symbol]
1631
+ elif topic == 'orderbook':
1632
+ del self.orderbooks[symbol]
1633
+ elif topic == 'ticker':
1634
+ del self.tickers[symbol]
1635
+ else:
1636
+ if topic.endswith('trades'):
1637
+ # don't reset self.myTrades directly here
1638
+ # because in c# we need to use a different object
1639
+ keys = list(self.trades.keys())
1640
+ for i in range(0, len(keys)):
1641
+ del self.trades[keys[i]]
1642
+
1643
+ def handle_message(self, client: Client, message):
1644
+ #
1645
+ # subscribe
1646
+ # {
1647
+ # "time": 1649062304,
1648
+ # "id": 1649062303,
1649
+ # "channel": "spot.candlesticks",
1650
+ # "event": "subscribe",
1651
+ # "result": {status: "success"}
1652
+ # }
1653
+ #
1654
+ # candlestick
1655
+ # {
1656
+ # "time": 1649063328,
1657
+ # "channel": "spot.candlesticks",
1658
+ # "event": "update",
1659
+ # "result": {
1660
+ # "t": "1649063280",
1661
+ # "v": "58932.23174896",
1662
+ # "c": "45966.47",
1663
+ # "h": "45997.24",
1664
+ # "l": "45966.47",
1665
+ # "o": "45975.18",
1666
+ # "n": "1m_BTC_USDT",
1667
+ # "a": "1.281699"
1668
+ # }
1669
+ # }
1670
+ #
1671
+ # orders
1672
+ # {
1673
+ # "time": 1630654851,
1674
+ # "channel": "options.orders", or futures.orders or spot.orders
1675
+ # "event": "update",
1676
+ # "result": [
1677
+ # {
1678
+ # "contract": "BTC_USDT-20211130-65000-C",
1679
+ # "create_time": 1637897000,
1680
+ # (...)
1681
+ # ]
1682
+ # }
1683
+ # orderbook
1684
+ # {
1685
+ # "time": 1649770525,
1686
+ # "channel": "spot.order_book_update",
1687
+ # "event": "update",
1688
+ # "result": {
1689
+ # "t": 1649770525653,
1690
+ # "e": "depthUpdate",
1691
+ # "E": 1649770525,
1692
+ # "s": "LTC_USDT",
1693
+ # "U": 2622525645,
1694
+ # "u": 2622525665,
1695
+ # "b": [
1696
+ # [Array], [Array],
1697
+ # [Array], [Array],
1698
+ # [Array], [Array],
1699
+ # [Array], [Array],
1700
+ # [Array], [Array],
1701
+ # [Array]
1702
+ # ],
1703
+ # "a": [
1704
+ # [Array], [Array],
1705
+ # [Array], [Array],
1706
+ # [Array], [Array],
1707
+ # [Array], [Array],
1708
+ # [Array], [Array],
1709
+ # [Array]
1710
+ # ]
1711
+ # }
1712
+ # }
1713
+ #
1714
+ # balance update
1715
+ #
1716
+ # {
1717
+ # "time": 1653664351,
1718
+ # "channel": "spot.balances",
1719
+ # "event": "update",
1720
+ # "result": [
1721
+ # {
1722
+ # "timestamp": "1653664351",
1723
+ # "timestamp_ms": "1653664351017",
1724
+ # "user": "10406147",
1725
+ # "currency": "LTC",
1726
+ # "change": "-0.0002000000000000",
1727
+ # "total": "0.09986000000000000000",
1728
+ # "available": "0.09986000000000000000"
1729
+ # }
1730
+ # ]
1731
+ # }
1732
+ #
1733
+ if self.handle_error_message(client, message):
1734
+ return
1735
+ event = self.safe_string(message, 'event')
1736
+ if event == 'subscribe':
1737
+ self.handle_subscription_status(client, message)
1738
+ return
1739
+ if event == 'unsubscribe':
1740
+ self.handle_un_subscribe(client, message)
1741
+ return
1742
+ channel = self.safe_string(message, 'channel', '')
1743
+ channelParts = channel.split('.')
1744
+ channelType = self.safe_value(channelParts, 1)
1745
+ v4Methods: dict = {
1746
+ 'usertrades': self.handle_my_trades,
1747
+ 'candlesticks': self.handle_ohlcv,
1748
+ 'orders': self.handle_order,
1749
+ 'positions': self.handle_positions,
1750
+ 'tickers': self.handle_ticker,
1751
+ 'book_ticker': self.handle_bid_ask,
1752
+ 'trades': self.handle_trades,
1753
+ 'order_book_update': self.handle_order_book,
1754
+ 'balances': self.handle_balance,
1755
+ 'liquidates': self.handle_liquidation,
1756
+ }
1757
+ method = self.safe_value(v4Methods, channelType)
1758
+ if method is not None:
1759
+ method(client, message)
1760
+ requestId = self.safe_string(message, 'request_id')
1761
+ if requestId == 'authenticated':
1762
+ self.handle_authentication_message(client, message)
1763
+ return
1764
+ if requestId is not None:
1765
+ data = self.safe_dict(message, 'data')
1766
+ # use safeValue may be Array or an Object
1767
+ result = self.safe_value(data, 'result')
1768
+ ack = self.safe_bool(message, 'ack')
1769
+ if ack is not True:
1770
+ client.resolve(result, requestId)
1771
+
1772
+ def get_url_by_market(self, market):
1773
+ baseUrl = self.urls['api'][market['type']]
1774
+ if market['contract']:
1775
+ return baseUrl['usdt'] if market['linear'] else baseUrl['btc']
1776
+ else:
1777
+ return baseUrl
1778
+
1779
+ def get_type_by_market(self, market: Market):
1780
+ if market['spot']:
1781
+ return 'spot'
1782
+ elif market['option']:
1783
+ return 'options'
1784
+ else:
1785
+ return 'futures'
1786
+
1787
+ def get_url_by_market_type(self, type: MarketType, isInverse=False):
1788
+ api = self.urls['api']
1789
+ url = api[type]
1790
+ if (type == 'swap') or (type == 'future'):
1791
+ return url['btc'] if isInverse else url['usdt']
1792
+ else:
1793
+ return url
1794
+
1795
+ def get_market_type_by_url(self, url: str):
1796
+ findBy: dict = {
1797
+ 'op-': 'option',
1798
+ 'delivery': 'future',
1799
+ 'fx': 'swap',
1800
+ }
1801
+ keys = list(findBy.keys())
1802
+ for i in range(0, len(keys)):
1803
+ key = keys[i]
1804
+ value = findBy[key]
1805
+ if url.find(key) >= 0:
1806
+ return value
1807
+ return 'spot'
1808
+
1809
+ def request_id(self):
1810
+ # their support said that reqid must be an int32, not documented
1811
+ reqid = self.sum(self.safe_integer(self.options, 'reqid', 0), 1)
1812
+ self.options['reqid'] = reqid
1813
+ return reqid
1814
+
1815
+ async def subscribe_public(self, url, messageHash, payload, channel, params={}, subscription=None):
1816
+ requestId = self.request_id()
1817
+ time = self.seconds()
1818
+ request: dict = {
1819
+ 'id': requestId,
1820
+ 'time': time,
1821
+ 'channel': channel,
1822
+ 'event': 'subscribe',
1823
+ 'payload': payload,
1824
+ }
1825
+ if subscription is not None:
1826
+ client = self.client(url)
1827
+ if not (messageHash in client.subscriptions):
1828
+ tempSubscriptionHash = str(requestId)
1829
+ client.subscriptions[tempSubscriptionHash] = messageHash
1830
+ message = self.extend(request, params)
1831
+ return await self.watch(url, messageHash, message, messageHash, subscription)
1832
+
1833
+ async def subscribe_public_multiple(self, url, messageHashes, payload, channel, params={}):
1834
+ requestId = self.request_id()
1835
+ time = self.seconds()
1836
+ request: dict = {
1837
+ 'id': requestId,
1838
+ 'time': time,
1839
+ 'channel': channel,
1840
+ 'event': 'subscribe',
1841
+ 'payload': payload,
1842
+ }
1843
+ message = self.extend(request, params)
1844
+ return await self.watch_multiple(url, messageHashes, message, messageHashes)
1845
+
1846
+ async def un_subscribe_public_multiple(self, url, topic, symbols, messageHashes, subMessageHashes, payload, channel, params={}):
1847
+ requestId = self.request_id()
1848
+ time = self.seconds()
1849
+ request: dict = {
1850
+ 'id': requestId,
1851
+ 'time': time,
1852
+ 'channel': channel,
1853
+ 'event': 'unsubscribe',
1854
+ 'payload': payload,
1855
+ }
1856
+ sub = {
1857
+ 'id': str(requestId),
1858
+ 'topic': topic,
1859
+ 'unsubscribe': True,
1860
+ 'messageHashes': messageHashes,
1861
+ 'subMessageHashes': subMessageHashes,
1862
+ 'symbols': symbols,
1863
+ }
1864
+ message = self.extend(request, params)
1865
+ return await self.watch_multiple(url, messageHashes, message, messageHashes, sub)
1866
+
1867
+ async def authenticate(self, url, messageType):
1868
+ channel = messageType + '.login'
1869
+ client = self.client(url)
1870
+ messageHash = 'authenticated'
1871
+ future = client.future(messageHash)
1872
+ authenticated = self.safe_value(client.subscriptions, messageHash)
1873
+ if authenticated is None:
1874
+ return await self.request_private(url, {}, channel, messageHash)
1875
+ return future
1876
+
1877
+ def handle_authentication_message(self, client: Client, message):
1878
+ messageHash = 'authenticated'
1879
+ future = self.safe_value(client.futures, messageHash)
1880
+ future.resolve(True)
1881
+
1882
+ async def request_private(self, url, reqParams, channel, requestId: Str = None):
1883
+ self.check_required_credentials()
1884
+ # uid is required for some subscriptions only so it's not a part of required credentials
1885
+ event = 'api'
1886
+ if requestId is None:
1887
+ reqId = self.request_id()
1888
+ requestId = str(reqId)
1889
+ messageHash = requestId
1890
+ time = self.seconds()
1891
+ # unfortunately, PHP demands double quotes for the escaped newline symbol
1892
+ signatureString = "\n".join([event, channel, self.json(reqParams), str(time)]) # eslint-disable-line quotes
1893
+ signature = self.hmac(self.encode(signatureString), self.encode(self.secret), hashlib.sha512, 'hex')
1894
+ payload: dict = {
1895
+ 'req_id': requestId,
1896
+ 'timestamp': str(time),
1897
+ 'api_key': self.apiKey,
1898
+ 'signature': signature,
1899
+ 'req_param': reqParams,
1900
+ }
1901
+ if (channel == 'spot.order_place') or (channel == 'futures.order_place'):
1902
+ payload['req_header'] = {
1903
+ 'X-Gate-Channel-Id': 'ccxt',
1904
+ }
1905
+ request: dict = {
1906
+ 'id': requestId,
1907
+ 'time': time,
1908
+ 'channel': channel,
1909
+ 'event': event,
1910
+ 'payload': payload,
1911
+ }
1912
+ return await self.watch(url, messageHash, request, messageHash, requestId)
1913
+
1914
+ async def subscribe_private(self, url, messageHash, payload, channel, params, requiresUid=False):
1915
+ self.check_required_credentials()
1916
+ # uid is required for some subscriptions only so it's not a part of required credentials
1917
+ if requiresUid:
1918
+ if self.uid is None or len(self.uid) == 0:
1919
+ raise ArgumentsRequired(self.id + ' requires uid to subscribe')
1920
+ idArray = [self.uid]
1921
+ if payload is None:
1922
+ payload = idArray
1923
+ else:
1924
+ payload = self.array_concat(idArray, payload)
1925
+ time = self.seconds()
1926
+ event = 'subscribe'
1927
+ signaturePayload = 'channel=' + channel + '&' + 'event=' + event + '&' + 'time=' + str(time)
1928
+ signature = self.hmac(self.encode(signaturePayload), self.encode(self.secret), hashlib.sha512, 'hex')
1929
+ auth: dict = {
1930
+ 'method': 'api_key',
1931
+ 'KEY': self.apiKey,
1932
+ 'SIGN': signature,
1933
+ }
1934
+ requestId = self.request_id()
1935
+ request: dict = {
1936
+ 'id': requestId,
1937
+ 'time': time,
1938
+ 'channel': channel,
1939
+ 'event': event,
1940
+ 'auth': auth,
1941
+ }
1942
+ if payload is not None:
1943
+ request['payload'] = payload
1944
+ client = self.client(url)
1945
+ if not (messageHash in client.subscriptions):
1946
+ tempSubscriptionHash = str(requestId)
1947
+ # in case of authenticationError we will throw
1948
+ client.subscriptions[tempSubscriptionHash] = messageHash
1949
+ message = self.extend(request, params)
1950
+ return await self.watch(url, messageHash, message, messageHash, messageHash)