ccxt 4.3.32__py2.py3-none-any.whl → 4.3.34__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/abstract/binance.py +1 -0
  3. ccxt/abstract/binancecoinm.py +1 -0
  4. ccxt/abstract/binanceus.py +1 -0
  5. ccxt/abstract/binanceusdm.py +1 -0
  6. ccxt/abstract/bitget.py +1 -1
  7. ccxt/ace.py +2 -2
  8. ccxt/alpaca.py +1 -1
  9. ccxt/ascendex.py +2 -2
  10. ccxt/async_support/__init__.py +1 -1
  11. ccxt/async_support/ace.py +2 -2
  12. ccxt/async_support/alpaca.py +1 -1
  13. ccxt/async_support/ascendex.py +2 -2
  14. ccxt/async_support/base/exchange.py +17 -1
  15. ccxt/async_support/bigone.py +2 -2
  16. ccxt/async_support/binance.py +4 -3
  17. ccxt/async_support/bingx.py +2 -2
  18. ccxt/async_support/bit2c.py +1 -1
  19. ccxt/async_support/bitbank.py +1 -1
  20. ccxt/async_support/bitbns.py +1 -1
  21. ccxt/async_support/bitfinex.py +2 -2
  22. ccxt/async_support/bitfinex2.py +1 -1
  23. ccxt/async_support/bitflyer.py +1 -1
  24. ccxt/async_support/bitget.py +4 -4
  25. ccxt/async_support/bithumb.py +1 -1
  26. ccxt/async_support/bitmart.py +2 -2
  27. ccxt/async_support/bitmex.py +3 -3
  28. ccxt/async_support/bitopro.py +3 -3
  29. ccxt/async_support/bitrue.py +2 -2
  30. ccxt/async_support/bitso.py +2 -2
  31. ccxt/async_support/bitstamp.py +2 -2
  32. ccxt/async_support/bitteam.py +3 -3
  33. ccxt/async_support/bitvavo.py +2 -2
  34. ccxt/async_support/blockchaincom.py +1 -1
  35. ccxt/async_support/blofin.py +3 -3
  36. ccxt/async_support/btcalpha.py +3 -3
  37. ccxt/async_support/btcbox.py +1 -1
  38. ccxt/async_support/btcmarkets.py +3 -3
  39. ccxt/async_support/btcturk.py +1 -1
  40. ccxt/async_support/bybit.py +2 -2
  41. ccxt/async_support/cex.py +1 -1
  42. ccxt/async_support/coinbase.py +96 -55
  43. ccxt/async_support/coinbaseexchange.py +1 -1
  44. ccxt/async_support/coinbaseinternational.py +3 -3
  45. ccxt/async_support/coincheck.py +2 -2
  46. ccxt/async_support/coinex.py +2 -2
  47. ccxt/async_support/coinlist.py +2 -2
  48. ccxt/async_support/coinmate.py +2 -2
  49. ccxt/async_support/coinmetro.py +2 -2
  50. ccxt/async_support/coinone.py +1 -1
  51. ccxt/async_support/coinsph.py +2 -2
  52. ccxt/async_support/cryptocom.py +1 -1
  53. ccxt/async_support/currencycom.py +2 -2
  54. ccxt/async_support/delta.py +1 -1
  55. ccxt/async_support/deribit.py +2 -2
  56. ccxt/async_support/digifinex.py +2 -2
  57. ccxt/async_support/exmo.py +2 -2
  58. ccxt/async_support/gate.py +2 -2
  59. ccxt/async_support/gemini.py +2 -2
  60. ccxt/async_support/hitbtc.py +2 -2
  61. ccxt/async_support/hollaex.py +1 -1
  62. ccxt/async_support/htx.py +2 -2
  63. ccxt/async_support/huobijp.py +2 -2
  64. ccxt/async_support/hyperliquid.py +2 -2
  65. ccxt/async_support/idex.py +2 -2
  66. ccxt/async_support/indodax.py +2 -2
  67. ccxt/async_support/kraken.py +2 -2
  68. ccxt/async_support/krakenfutures.py +1 -1
  69. ccxt/async_support/kucoin.py +2 -2
  70. ccxt/async_support/kuna.py +2 -2
  71. ccxt/async_support/latoken.py +2 -2
  72. ccxt/async_support/lbank.py +1 -1
  73. ccxt/async_support/luno.py +1 -1
  74. ccxt/async_support/lykke.py +1 -1
  75. ccxt/async_support/mercado.py +1 -1
  76. ccxt/async_support/mexc.py +1 -1
  77. ccxt/async_support/ndax.py +2 -2
  78. ccxt/async_support/novadax.py +3 -3
  79. ccxt/async_support/oceanex.py +2 -2
  80. ccxt/async_support/okcoin.py +3 -3
  81. ccxt/async_support/okx.py +3 -3
  82. ccxt/async_support/onetrading.py +2 -2
  83. ccxt/async_support/p2b.py +2 -2
  84. ccxt/async_support/paymium.py +1 -1
  85. ccxt/async_support/phemex.py +4 -4
  86. ccxt/async_support/poloniex.py +3 -3
  87. ccxt/async_support/poloniexfutures.py +2 -2
  88. ccxt/async_support/probit.py +3 -3
  89. ccxt/async_support/timex.py +2 -2
  90. ccxt/async_support/tokocrypto.py +1 -1
  91. ccxt/async_support/tradeogre.py +1 -1
  92. ccxt/async_support/upbit.py +3 -3
  93. ccxt/async_support/wavesexchange.py +1 -1
  94. ccxt/async_support/wazirx.py +3 -3
  95. ccxt/async_support/whitebit.py +3 -3
  96. ccxt/async_support/woo.py +3 -3
  97. ccxt/async_support/woofipro.py +3 -3
  98. ccxt/async_support/yobit.py +1 -1
  99. ccxt/async_support/zaif.py +2 -2
  100. ccxt/async_support/zonda.py +1 -1
  101. ccxt/base/exchange.py +25 -3
  102. ccxt/base/types.py +0 -1
  103. ccxt/bigone.py +2 -2
  104. ccxt/binance.py +4 -3
  105. ccxt/bingx.py +2 -2
  106. ccxt/bit2c.py +1 -1
  107. ccxt/bitbank.py +1 -1
  108. ccxt/bitbns.py +1 -1
  109. ccxt/bitfinex.py +2 -2
  110. ccxt/bitfinex2.py +1 -1
  111. ccxt/bitflyer.py +1 -1
  112. ccxt/bitget.py +4 -4
  113. ccxt/bithumb.py +1 -1
  114. ccxt/bitmart.py +2 -2
  115. ccxt/bitmex.py +3 -3
  116. ccxt/bitopro.py +3 -3
  117. ccxt/bitrue.py +2 -2
  118. ccxt/bitso.py +2 -2
  119. ccxt/bitstamp.py +2 -2
  120. ccxt/bitteam.py +3 -3
  121. ccxt/bitvavo.py +2 -2
  122. ccxt/blockchaincom.py +1 -1
  123. ccxt/blofin.py +3 -3
  124. ccxt/btcalpha.py +3 -3
  125. ccxt/btcbox.py +1 -1
  126. ccxt/btcmarkets.py +3 -3
  127. ccxt/btcturk.py +1 -1
  128. ccxt/bybit.py +2 -2
  129. ccxt/cex.py +1 -1
  130. ccxt/coinbase.py +96 -55
  131. ccxt/coinbaseexchange.py +1 -1
  132. ccxt/coinbaseinternational.py +3 -3
  133. ccxt/coincheck.py +2 -2
  134. ccxt/coinex.py +2 -2
  135. ccxt/coinlist.py +2 -2
  136. ccxt/coinmate.py +2 -2
  137. ccxt/coinmetro.py +2 -2
  138. ccxt/coinone.py +1 -1
  139. ccxt/coinsph.py +2 -2
  140. ccxt/cryptocom.py +1 -1
  141. ccxt/currencycom.py +2 -2
  142. ccxt/delta.py +1 -1
  143. ccxt/deribit.py +2 -2
  144. ccxt/digifinex.py +2 -2
  145. ccxt/exmo.py +2 -2
  146. ccxt/gate.py +2 -2
  147. ccxt/gemini.py +2 -2
  148. ccxt/hitbtc.py +2 -2
  149. ccxt/hollaex.py +1 -1
  150. ccxt/htx.py +2 -2
  151. ccxt/huobijp.py +2 -2
  152. ccxt/hyperliquid.py +2 -2
  153. ccxt/idex.py +2 -2
  154. ccxt/indodax.py +2 -2
  155. ccxt/kraken.py +2 -2
  156. ccxt/krakenfutures.py +1 -1
  157. ccxt/kucoin.py +2 -2
  158. ccxt/kuna.py +2 -2
  159. ccxt/latoken.py +2 -2
  160. ccxt/lbank.py +1 -1
  161. ccxt/luno.py +1 -1
  162. ccxt/lykke.py +1 -1
  163. ccxt/mercado.py +1 -1
  164. ccxt/mexc.py +1 -1
  165. ccxt/ndax.py +2 -2
  166. ccxt/novadax.py +3 -3
  167. ccxt/oceanex.py +2 -2
  168. ccxt/okcoin.py +3 -3
  169. ccxt/okx.py +3 -3
  170. ccxt/onetrading.py +2 -2
  171. ccxt/p2b.py +2 -2
  172. ccxt/paymium.py +1 -1
  173. ccxt/phemex.py +4 -4
  174. ccxt/poloniex.py +3 -3
  175. ccxt/poloniexfutures.py +2 -2
  176. ccxt/pro/__init__.py +1 -1
  177. ccxt/pro/binance.py +331 -2
  178. ccxt/pro/bitmex.py +98 -1
  179. ccxt/pro/bybit.py +82 -1
  180. ccxt/pro/gate.py +173 -1
  181. ccxt/pro/kucoinfutures.py +4 -0
  182. ccxt/pro/okx.py +245 -2
  183. ccxt/probit.py +3 -3
  184. ccxt/test/base/__init__.py +1 -0
  185. ccxt/test/base/test_liquidation.py +50 -0
  186. ccxt/timex.py +2 -2
  187. ccxt/tokocrypto.py +1 -1
  188. ccxt/tradeogre.py +1 -1
  189. ccxt/upbit.py +3 -3
  190. ccxt/wavesexchange.py +1 -1
  191. ccxt/wazirx.py +3 -3
  192. ccxt/whitebit.py +3 -3
  193. ccxt/woo.py +3 -3
  194. ccxt/woofipro.py +3 -3
  195. ccxt/yobit.py +1 -1
  196. ccxt/zaif.py +2 -2
  197. ccxt/zonda.py +1 -1
  198. {ccxt-4.3.32.dist-info → ccxt-4.3.34.dist-info}/METADATA +4 -4
  199. {ccxt-4.3.32.dist-info → ccxt-4.3.34.dist-info}/RECORD +201 -200
  200. {ccxt-4.3.32.dist-info → ccxt-4.3.34.dist-info}/WHEEL +0 -0
  201. {ccxt-4.3.32.dist-info → ccxt-4.3.34.dist-info}/top_level.txt +0 -0
ccxt/pro/gate.py CHANGED
@@ -6,13 +6,14 @@
6
6
  import ccxt.async_support
7
7
  from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
8
8
  import hashlib
9
- from ccxt.base.types import Balances, Int, Order, OrderBook, Position, Str, Strings, Ticker, Tickers, Trade
9
+ from ccxt.base.types import Balances, Int, Liquidation, Order, OrderBook, Position, Str, Strings, Ticker, Tickers, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
12
  from ccxt.base.errors import AuthenticationError
13
13
  from ccxt.base.errors import ArgumentsRequired
14
14
  from ccxt.base.errors import BadRequest
15
15
  from ccxt.base.errors import InvalidNonce
16
+ from ccxt.base.precise import Precise
16
17
 
17
18
 
18
19
  class gate(ccxt.async_support.gate):
@@ -30,6 +31,10 @@ class gate(ccxt.async_support.gate):
30
31
  'watchOHLCV': True,
31
32
  'watchBalance': True,
32
33
  'watchOrders': True,
34
+ 'watchLiquidations': False,
35
+ 'watchLiquidationsForSymbols': False,
36
+ 'watchMyLiquidations': True,
37
+ 'watchMyLiquidationsForSymbols': True,
33
38
  'watchPositions': True,
34
39
  },
35
40
  'urls': {
@@ -963,6 +968,172 @@ class gate(ccxt.async_support.gate):
963
968
  client.resolve(self.orders, messageHash)
964
969
  client.resolve(self.orders, 'orders')
965
970
 
971
+ async def watch_my_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
972
+ """
973
+ watch the public liquidations of a trading pair
974
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#liquidates-api
975
+ :see: https://www.gate.io/docs/developers/delivery/ws/en/#liquidates-api
976
+ :see: https://www.gate.io/docs/developers/options/ws/en/#liquidates-channel
977
+ :param str symbol: unified CCXT market symbol
978
+ :param int [since]: the earliest time in ms to fetch liquidations for
979
+ :param int [limit]: the maximum number of liquidation structures to retrieve
980
+ :param dict [params]: exchange specific parameters for the bitmex api endpoint
981
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
982
+ """
983
+ return self.watch_my_liquidations_for_symbols([symbol], since, limit, params)
984
+
985
+ async def watch_my_liquidations_for_symbols(self, symbols: List[str] = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
986
+ """
987
+ watch the private liquidations of a trading pair
988
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#liquidates-api
989
+ :see: https://www.gate.io/docs/developers/delivery/ws/en/#liquidates-api
990
+ :see: https://www.gate.io/docs/developers/options/ws/en/#liquidates-channel
991
+ :param str symbol: unified CCXT market symbol
992
+ :param int [since]: the earliest time in ms to fetch liquidations for
993
+ :param int [limit]: the maximum number of liquidation structures to retrieve
994
+ :param dict [params]: exchange specific parameters for the gate api endpoint
995
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
996
+ """
997
+ await self.load_markets()
998
+ symbols = self.market_symbols(symbols, None, True, True)
999
+ market = self.get_market_from_symbols(symbols)
1000
+ type = None
1001
+ query = None
1002
+ type, query = self.handle_market_type_and_params('watchMyLiquidationsForSymbols', market, params)
1003
+ typeId = self.get_supported_mapping(type, {
1004
+ 'future': 'futures',
1005
+ 'swap': 'futures',
1006
+ 'option': 'options',
1007
+ })
1008
+ subType = None
1009
+ subType, query = self.handle_sub_type_and_params('watchMyLiquidationsForSymbols', market, query)
1010
+ isInverse = (subType == 'inverse')
1011
+ url = self.get_url_by_market_type(type, isInverse)
1012
+ payload = []
1013
+ messageHash = ''
1014
+ if self.is_empty(symbols):
1015
+ if typeId != 'futures' and not isInverse:
1016
+ raise BadRequest(self.id + ' watchMyLiquidationsForSymbols() does not support listening to all symbols, you must call watchMyLiquidations() instead for each symbol you wish to watch.')
1017
+ messageHash = 'myLiquidations'
1018
+ payload.append('not all')
1019
+ else:
1020
+ symbolsLength = len(symbols)
1021
+ if symbolsLength != 1:
1022
+ raise BadRequest(self.id + ' watchMyLiquidationsForSymbols() only allows one symbol at a time. To listen to several symbols call watchMyLiquidationsForSymbols() several times.')
1023
+ messageHash = 'myLiquidations::' + symbols[0]
1024
+ payload.append(market['id'])
1025
+ channel = typeId + '.liquidates'
1026
+ newLiquidations = await self.subscribe_private(url, messageHash, payload, channel, query, True)
1027
+ if self.newUpdates:
1028
+ return newLiquidations
1029
+ return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True)
1030
+
1031
+ def handle_liquidation(self, client: Client, message):
1032
+ #
1033
+ # future / delivery
1034
+ # {
1035
+ # "channel":"futures.liquidates",
1036
+ # "event":"update",
1037
+ # "time":1541505434,
1038
+ # "time_ms":1541505434123,
1039
+ # "result":[
1040
+ # {
1041
+ # "entry_price":209,
1042
+ # "fill_price":215.1,
1043
+ # "left":0,
1044
+ # "leverage":0.0,
1045
+ # "liq_price":213,
1046
+ # "margin":0.007816722941,
1047
+ # "mark_price":213,
1048
+ # "order_id":4093362,
1049
+ # "order_price":215.1,
1050
+ # "size":-124,
1051
+ # "time":1541486601,
1052
+ # "time_ms":1541486601123,
1053
+ # "contract":"BTC_USD",
1054
+ # "user":"1040xxxx"
1055
+ # }
1056
+ # ]
1057
+ # }
1058
+ # option
1059
+ # {
1060
+ # "channel":"options.liquidates",
1061
+ # "event":"update",
1062
+ # "time":1630654851,
1063
+ # "result":[
1064
+ # {
1065
+ # "user":"1xxxx",
1066
+ # "init_margin":1190,
1067
+ # "maint_margin":1042.5,
1068
+ # "order_margin":0,
1069
+ # "time":1639051907,
1070
+ # "time_ms":1639051907000
1071
+ # }
1072
+ # ]
1073
+ # }
1074
+ #
1075
+ rawLiquidations = self.safe_list(message, 'result', [])
1076
+ newLiquidations = []
1077
+ for i in range(0, len(rawLiquidations)):
1078
+ rawLiquidation = rawLiquidations[i]
1079
+ liquidation = self.parse_ws_liquidation(rawLiquidation)
1080
+ symbol = self.safe_string(liquidation, 'symbol')
1081
+ liquidations = self.safe_value(self.liquidations, symbol)
1082
+ if liquidations is None:
1083
+ limit = self.safe_integer(self.options, 'liquidationsLimit', 1000)
1084
+ liquidations = ArrayCache(limit)
1085
+ liquidations.append(liquidation)
1086
+ self.liquidations[symbol] = liquidations
1087
+ client.resolve(liquidations, 'myLiquidations::' + symbol)
1088
+ client.resolve(newLiquidations, 'myLiquidations')
1089
+
1090
+ def parse_ws_liquidation(self, liquidation, market=None):
1091
+ #
1092
+ # future / delivery
1093
+ # {
1094
+ # "entry_price": 209,
1095
+ # "fill_price": 215.1,
1096
+ # "left": 0,
1097
+ # "leverage": 0.0,
1098
+ # "liq_price": 213,
1099
+ # "margin": 0.007816722941,
1100
+ # "mark_price": 213,
1101
+ # "order_id": 4093362,
1102
+ # "order_price": 215.1,
1103
+ # "size": -124,
1104
+ # "time": 1541486601,
1105
+ # "time_ms": 1541486601123,
1106
+ # "contract": "BTC_USD",
1107
+ # "user": "1040xxxx"
1108
+ # }
1109
+ # option
1110
+ # {
1111
+ # "user": "1xxxx",
1112
+ # "init_margin": 1190,
1113
+ # "maint_margin": 1042.5,
1114
+ # "order_margin": 0,
1115
+ # "time": 1639051907,
1116
+ # "time_ms": 1639051907000
1117
+ # }
1118
+ #
1119
+ marketId = self.safe_string(liquidation, 'contract')
1120
+ market = self.safe_market(marketId, market)
1121
+ timestamp = self.safe_integer(liquidation, 'time_ms')
1122
+ originalSize = self.safe_string(liquidation, 'size')
1123
+ left = self.safe_string(liquidation, 'left')
1124
+ amount = Precise.string_abs(Precise.string_sub(originalSize, left))
1125
+ return self.safe_liquidation({
1126
+ 'info': liquidation,
1127
+ 'symbol': self.safe_symbol(marketId, market),
1128
+ 'contracts': self.parse_number(amount),
1129
+ 'contractSize': self.safe_number(market, 'contractSize'),
1130
+ 'price': self.safe_number(liquidation, 'fill_price'),
1131
+ 'baseValue': None,
1132
+ 'quoteValue': None,
1133
+ 'timestamp': timestamp,
1134
+ 'datetime': self.iso8601(timestamp),
1135
+ })
1136
+
966
1137
  def handle_error_message(self, client: Client, message):
967
1138
  # {
968
1139
  # "time": 1647274664,
@@ -1126,6 +1297,7 @@ class gate(ccxt.async_support.gate):
1126
1297
  'trades': self.handle_trades,
1127
1298
  'order_book_update': self.handle_order_book,
1128
1299
  'balances': self.handle_balance,
1300
+ 'liquidates': self.handle_liquidation,
1129
1301
  }
1130
1302
  method = self.safe_value(v4Methods, channelType)
1131
1303
  if method is not None:
ccxt/pro/kucoinfutures.py CHANGED
@@ -18,6 +18,10 @@ class kucoinfutures(ccxt.async_support.kucoinfutures):
18
18
  return self.deep_extend(super(kucoinfutures, self).describe(), {
19
19
  'has': {
20
20
  'ws': True,
21
+ 'watchLiquidations': False,
22
+ 'watchLiquidatinsForSymbols': False,
23
+ 'watchMyLiquidations': None,
24
+ 'watchMyLiquidationsForSymbols': None,
21
25
  'watchTicker': True,
22
26
  'watchTickers': True,
23
27
  'watchBidsAsks': True,
ccxt/pro/okx.py CHANGED
@@ -6,7 +6,7 @@
6
6
  import ccxt.async_support
7
7
  from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
8
8
  import hashlib
9
- from ccxt.base.types import Balances, Int, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade
9
+ from ccxt.base.types import Balances, Int, Liquidation, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
12
  from ccxt.base.errors import ExchangeError
@@ -29,6 +29,10 @@ class okx(ccxt.async_support.okx):
29
29
  'watchTradesForSymbols': True,
30
30
  'watchOrderBookForSymbols': True,
31
31
  'watchBalance': True,
32
+ 'watchLiquidations': 'emulated',
33
+ 'watchLiquidationsForSymbols': True,
34
+ 'watchMyLiquidations': 'emulated',
35
+ 'watchMyLiquidationsForSymbols': True,
32
36
  'watchOHLCV': True,
33
37
  'watchOHLCVForSymbols': True,
34
38
  'watchOrders': True,
@@ -160,7 +164,7 @@ class okx(ccxt.async_support.okx):
160
164
  self.deep_extend(firstArgument, params),
161
165
  ],
162
166
  }
163
- return await self.watch(url, messageHash, request, messageHash)
167
+ return self.watch(url, messageHash, request, messageHash)
164
168
 
165
169
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
166
170
  """
@@ -399,6 +403,240 @@ class okx(ccxt.async_support.okx):
399
403
  client.resolve(tickers, messageHash)
400
404
  return message
401
405
 
406
+ async def watch_liquidations_for_symbols(self, symbols: List[str] = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
407
+ """
408
+ watch the public liquidations of a trading pair
409
+ :see: https://www.okx.com/docs-v5/en/#public-data-websocket-liquidation-orders-channel
410
+ :param str symbol: unified CCXT market symbol
411
+ :param int [since]: the earliest time in ms to fetch liquidations for
412
+ :param int [limit]: the maximum number of liquidation structures to retrieve
413
+ :param dict [params]: exchange specific parameters for the okx api endpoint
414
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
415
+ """
416
+ await self.load_markets()
417
+ symbols = self.market_symbols(symbols, None, True, True)
418
+ messageHash = 'liquidations'
419
+ if symbols is not None:
420
+ messageHash += '::' + ','.join(symbols)
421
+ market = self.get_market_from_symbols(symbols)
422
+ type = None
423
+ type, params = self.handle_market_type_and_params('watchliquidationsForSymbols', market, params)
424
+ channel = 'liquidation-orders'
425
+ if type == 'spot':
426
+ type = 'SWAP'
427
+ elif type == 'future':
428
+ type = 'futures'
429
+ uppercaseType = type.upper()
430
+ request = {
431
+ 'instType': uppercaseType,
432
+ }
433
+ newLiquidations = await self.subscribe('public', messageHash, channel, None, self.extend(request, params))
434
+ if self.newUpdates:
435
+ return newLiquidations
436
+ return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True)
437
+
438
+ def handle_liquidation(self, client: Client, message):
439
+ #
440
+ # {
441
+ # "arg": {
442
+ # "channel": "liquidation-orders",
443
+ # "instType": "SWAP"
444
+ # },
445
+ # "data": [
446
+ # {
447
+ # "details": [
448
+ # {
449
+ # "bkLoss": "0",
450
+ # "bkPx": "0.007831",
451
+ # "ccy": "",
452
+ # "posSide": "short",
453
+ # "side": "buy",
454
+ # "sz": "13",
455
+ # "ts": "1692266434010"
456
+ # }
457
+ # ],
458
+ # "instFamily": "IOST-USDT",
459
+ # "instId": "IOST-USDT-SWAP",
460
+ # "instType": "SWAP",
461
+ # "uly": "IOST-USDT"
462
+ # }
463
+ # ]
464
+ # }
465
+ #
466
+ rawLiquidations = self.safe_list(message, 'data', [])
467
+ for i in range(0, len(rawLiquidations)):
468
+ rawLiquidation = rawLiquidations[i]
469
+ liquidation = self.parse_ws_liquidation(rawLiquidation)
470
+ symbol = self.safe_string(liquidation, 'symbol')
471
+ liquidations = self.safe_value(self.liquidations, symbol)
472
+ if liquidations is None:
473
+ limit = self.safe_integer(self.options, 'liquidationsLimit', 1000)
474
+ liquidations = ArrayCache(limit)
475
+ liquidations.append(liquidation)
476
+ self.liquidations[symbol] = liquidations
477
+ client.resolve([liquidation], 'liquidations')
478
+ client.resolve([liquidation], 'liquidations::' + symbol)
479
+
480
+ async def watch_my_liquidations_for_symbols(self, symbols: List[str] = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
481
+ """
482
+ watch the private liquidations of a trading pair
483
+ :see: https://www.okx.com/docs-v5/en/#trading-account-websocket-balance-and-position-channel
484
+ :param str symbol: unified CCXT market symbol
485
+ :param int [since]: the earliest time in ms to fetch liquidations for
486
+ :param int [limit]: the maximum number of liquidation structures to retrieve
487
+ :param dict [params]: exchange specific parameters for the okx api endpoint
488
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
489
+ """
490
+ await self.load_markets()
491
+ isStop = self.safe_value_2(params, 'stop', 'trigger', False)
492
+ params = self.omit(params, ['stop', 'trigger'])
493
+ await self.authenticate({'access': 'business' if isStop else 'private'})
494
+ symbols = self.market_symbols(symbols, None, True, True)
495
+ messageHash = 'myLiquidations'
496
+ if symbols is not None:
497
+ messageHash += '::' + ','.join(symbols)
498
+ channel = 'balance_and_position'
499
+ newLiquidations = await self.subscribe('private', messageHash, channel, None, params)
500
+ if self.newUpdates:
501
+ return newLiquidations
502
+ return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True)
503
+
504
+ def handle_my_liquidation(self, client: Client, message):
505
+ #
506
+ # {
507
+ # "arg": {
508
+ # "channel": "balance_and_position",
509
+ # "uid": "77982378738415879"
510
+ # },
511
+ # "data": [{
512
+ # "pTime": "1597026383085",
513
+ # "eventType": "snapshot",
514
+ # "balData": [{
515
+ # "ccy": "BTC",
516
+ # "cashBal": "1",
517
+ # "uTime": "1597026383085"
518
+ # }],
519
+ # "posData": [{
520
+ # "posId": "1111111111",
521
+ # "tradeId": "2",
522
+ # "instId": "BTC-USD-191018",
523
+ # "instType": "FUTURES",
524
+ # "mgnMode": "cross",
525
+ # "posSide": "long",
526
+ # "pos": "10",
527
+ # "ccy": "BTC",
528
+ # "posCcy": "",
529
+ # "avgPx": "3320",
530
+ # "uTIme": "1597026383085"
531
+ # }],
532
+ # "trades": [{
533
+ # "instId": "BTC-USD-191018",
534
+ # "tradeId": "2",
535
+ # }]
536
+ # }]
537
+ # }
538
+ #
539
+ rawLiquidations = self.safe_list(message, 'data', [])
540
+ for i in range(0, len(rawLiquidations)):
541
+ rawLiquidation = rawLiquidations[i]
542
+ eventType = self.safe_string(rawLiquidation, 'eventType')
543
+ if eventType != 'liquidation':
544
+ return
545
+ liquidation = self.parse_ws_my_liquidation(rawLiquidation)
546
+ symbol = self.safe_string(liquidation, 'symbol')
547
+ liquidations = self.safe_value(self.liquidations, symbol)
548
+ if liquidations is None:
549
+ limit = self.safe_integer(self.options, 'myLiquidationsLimit', 1000)
550
+ liquidations = ArrayCache(limit)
551
+ liquidations.append(liquidation)
552
+ self.liquidations[symbol] = liquidations
553
+ client.resolve([liquidation], 'myLiquidations')
554
+ client.resolve([liquidation], 'myLiquidations::' + symbol)
555
+
556
+ def parse_ws_my_liquidation(self, liquidation, market=None):
557
+ #
558
+ # {
559
+ # "pTime": "1597026383085",
560
+ # "eventType": "snapshot",
561
+ # "balData": [{
562
+ # "ccy": "BTC",
563
+ # "cashBal": "1",
564
+ # "uTime": "1597026383085"
565
+ # }],
566
+ # "posData": [{
567
+ # "posId": "1111111111",
568
+ # "tradeId": "2",
569
+ # "instId": "BTC-USD-191018",
570
+ # "instType": "FUTURES",
571
+ # "mgnMode": "cross",
572
+ # "posSide": "long",
573
+ # "pos": "10",
574
+ # "ccy": "BTC",
575
+ # "posCcy": "",
576
+ # "avgPx": "3320",
577
+ # "uTIme": "1597026383085"
578
+ # }],
579
+ # "trades": [{
580
+ # "instId": "BTC-USD-191018",
581
+ # "tradeId": "2",
582
+ # }]
583
+ # }
584
+ #
585
+ posData = self.safe_list(liquidation, 'posData', [])
586
+ firstPosData = self.safe_dict(posData, 0, {})
587
+ marketId = self.safe_string(firstPosData, 'instId')
588
+ market = self.safe_market(marketId, market)
589
+ timestamp = self.safe_integer(firstPosData, 'uTIme')
590
+ return self.safe_liquidation({
591
+ 'info': liquidation,
592
+ 'symbol': self.safe_symbol(marketId, market),
593
+ 'contracts': self.safe_number(firstPosData, 'pos'),
594
+ 'contractSize': self.safe_number(market, 'contractSize'),
595
+ 'price': self.safe_number(liquidation, 'avgPx'),
596
+ 'baseValue': None,
597
+ 'quoteValue': None,
598
+ 'timestamp': timestamp,
599
+ 'datetime': self.iso8601(timestamp),
600
+ })
601
+
602
+ def parse_ws_liquidation(self, liquidation, market=None):
603
+ #
604
+ # public liquidation
605
+ # {
606
+ # "details": [
607
+ # {
608
+ # "bkLoss": "0",
609
+ # "bkPx": "0.007831",
610
+ # "ccy": "",
611
+ # "posSide": "short",
612
+ # "side": "buy",
613
+ # "sz": "13",
614
+ # "ts": "1692266434010"
615
+ # }
616
+ # ],
617
+ # "instFamily": "IOST-USDT",
618
+ # "instId": "IOST-USDT-SWAP",
619
+ # "instType": "SWAP",
620
+ # "uly": "IOST-USDT"
621
+ # }
622
+ #
623
+ details = self.safe_list(liquidation, 'details', [])
624
+ liquidationDetails = self.safe_dict(details, 0, {})
625
+ marketId = self.safe_string(liquidation, 'instId')
626
+ market = self.safe_market(marketId, market)
627
+ timestamp = self.safe_integer(liquidationDetails, 'ts')
628
+ return self.safe_liquidation({
629
+ 'info': liquidation,
630
+ 'symbol': self.safe_symbol(marketId, market),
631
+ 'contracts': self.safe_number(liquidationDetails, 'sz'),
632
+ 'contractSize': self.safe_number(market, 'contractSize'),
633
+ 'price': self.safe_number(liquidationDetails, 'bkPx'),
634
+ 'baseValue': None,
635
+ 'quoteValue': None,
636
+ 'timestamp': timestamp,
637
+ 'datetime': self.iso8601(timestamp),
638
+ })
639
+
402
640
  async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
403
641
  """
404
642
  watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
@@ -819,6 +1057,9 @@ class okx(ccxt.async_support.okx):
819
1057
  await self.authenticate()
820
1058
  return await self.subscribe('private', 'account', 'account', None, params)
821
1059
 
1060
+ def handle_balance_and_position(self, client: Client, message):
1061
+ self.handle_my_liquidation(client, message)
1062
+
822
1063
  def handle_balance(self, client: Client, message):
823
1064
  #
824
1065
  # {
@@ -1600,6 +1841,8 @@ class okx(ccxt.async_support.okx):
1600
1841
  # 'margin_account': self.handle_balance,
1601
1842
  'orders': self.handle_orders,
1602
1843
  'orders-algo': self.handle_orders,
1844
+ 'liquidation-orders': self.handle_liquidation,
1845
+ 'balance_and_position': self.handle_balance_and_position,
1603
1846
  }
1604
1847
  method = self.safe_value(methods, channel)
1605
1848
  if method is None:
ccxt/probit.py CHANGED
@@ -276,7 +276,7 @@ class probit(Exchange, ImplicitAPI):
276
276
  markets = self.safe_value(response, 'data', [])
277
277
  return self.parse_markets(markets)
278
278
 
279
- def parse_market(self, market) -> Market:
279
+ def parse_market(self, market: dict) -> Market:
280
280
  id = self.safe_string(market, 'id')
281
281
  baseId = self.safe_string(market, 'base_currency_id')
282
282
  quoteId = self.safe_string(market, 'quote_currency_id')
@@ -1511,7 +1511,7 @@ class probit(Exchange, ImplicitAPI):
1511
1511
  'info': transaction,
1512
1512
  }
1513
1513
 
1514
- def parse_transaction_status(self, status):
1514
+ def parse_transaction_status(self, status: Str):
1515
1515
  statuses: dict = {
1516
1516
  'requested': 'pending',
1517
1517
  'pending': 'pending',
@@ -1720,7 +1720,7 @@ class probit(Exchange, ImplicitAPI):
1720
1720
  self.options['expires'] = self.sum(self.milliseconds(), expiresIn * 1000)
1721
1721
  return response
1722
1722
 
1723
- def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
1723
+ def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
1724
1724
  if response is None:
1725
1725
  return None # fallback to default error handler
1726
1726
  if 'errorCode' in response:
@@ -14,6 +14,7 @@ from ccxt.test.base.test_funding_rate_history import test_funding_rate_history #
14
14
  from ccxt.test.base.test_last_price import test_last_price # noqa E402
15
15
  from ccxt.test.base.test_ledger_entry import test_ledger_entry # noqa E402
16
16
  from ccxt.test.base.test_leverage_tier import test_leverage_tier # noqa E402
17
+ from ccxt.test.base.test_liquidation import test_liquidation # noqa E402
17
18
  from ccxt.test.base.test_margin_mode import test_margin_mode # noqa E402
18
19
  from ccxt.test.base.test_margin_modification import test_margin_modification # noqa E402
19
20
  from ccxt.test.base.test_market import test_market # noqa E402
@@ -0,0 +1,50 @@
1
+ import os
2
+ import sys
3
+
4
+ root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
5
+ sys.path.append(root)
6
+
7
+ # ----------------------------------------------------------------------------
8
+
9
+ # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
10
+ # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
11
+
12
+ # ----------------------------------------------------------------------------
13
+ # -*- coding: utf-8 -*-
14
+
15
+ from ccxt.base.precise import Precise # noqa E402
16
+ from ccxt.test.base import test_shared_methods # noqa E402
17
+
18
+ def test_liquidation(exchange, skipped_properties, method, entry, symbol):
19
+ format = {
20
+ 'info': {},
21
+ 'symbol': 'ETH/BTC',
22
+ 'contracts': exchange.parse_number('1.234'),
23
+ 'contractSize': exchange.parse_number('1.234'),
24
+ 'price': exchange.parse_number('1.234'),
25
+ 'baseValue': exchange.parse_number('1.234'),
26
+ 'quoteValue': exchange.parse_number('1.234'),
27
+ 'timestamp': 1502962946216,
28
+ 'datetime': '2017-09-01T00:00:00',
29
+ }
30
+ # todo: atm, many exchanges fail, so temporarily decrease stict mode
31
+ empty_allowed_for = ['timestamp', 'datetime', 'quoteValue', 'baseValue', 'previousClose', 'price', 'contractSize', 'contracts']
32
+ test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
33
+ test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry)
34
+ log_text = test_shared_methods.log_template(exchange, method, entry)
35
+ test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'contracts', '0')
36
+ test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'contractSize', '0')
37
+ test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'price', '0')
38
+ test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'baseValue', '0')
39
+ test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'quoteValue', '0')
40
+ contracts = exchange.safe_string(entry, 'contracts')
41
+ contract_size = exchange.safe_string(entry, 'contractSize')
42
+ price = exchange.safe_string(entry, 'price')
43
+ base_value = exchange.safe_string(entry, 'baseValue')
44
+ if contracts and contract_size:
45
+ assert Precise.string_eq(base_value, Precise.string_mul(contracts, contract_size)), 'baseValue == contracts * contractSize' + log_text
46
+ if price:
47
+ assert Precise.string_eq(base_value, Precise.string_mul(Precise.string_mul(contracts, contract_size), price)), 'quoteValue == contracts * contractSize * price' + log_text
48
+ # if singular was called, then symbol needs to be asserted
49
+ if method == 'watchLiquidations' or method == 'fetchLiquidations':
50
+ test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, 'symbol', symbol)
ccxt/timex.py CHANGED
@@ -1142,7 +1142,7 @@ class timex(Exchange, ImplicitAPI):
1142
1142
  result = self.safe_value(response, 0, {})
1143
1143
  return self.parse_trading_fee(result, market)
1144
1144
 
1145
- def parse_market(self, market) -> Market:
1145
+ def parse_market(self, market: dict) -> Market:
1146
1146
  #
1147
1147
  # {
1148
1148
  # "symbol": "ETHBTC",
@@ -1572,7 +1572,7 @@ class timex(Exchange, ImplicitAPI):
1572
1572
  headers = {'authorization': secret}
1573
1573
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
1574
1574
 
1575
- def handle_errors(self, statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody):
1575
+ def handle_errors(self, statusCode: int, statusText: str, url: str, method: str, responseHeaders: dict, responseBody, response, requestHeaders, requestBody):
1576
1576
  if response is None:
1577
1577
  return None
1578
1578
  if statusCode >= 400:
ccxt/tokocrypto.py CHANGED
@@ -2331,7 +2331,7 @@ class tokocrypto(Exchange, ImplicitAPI):
2331
2331
  url += '?' + self.urlencode(params)
2332
2332
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
2333
2333
 
2334
- def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
2334
+ def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
2335
2335
  if (code == 418) or (code == 429):
2336
2336
  raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body)
2337
2337
  # error response in a form: {"code": -1013, "msg": "Invalid quantity."}
ccxt/tradeogre.py CHANGED
@@ -581,7 +581,7 @@ class tradeogre(Exchange, ImplicitAPI):
581
581
  body = self.urlencode(params)
582
582
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
583
583
 
584
- def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
584
+ def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
585
585
  if response is None:
586
586
  return None
587
587
  if not ('success' in response):
ccxt/upbit.py CHANGED
@@ -423,7 +423,7 @@ class upbit(Exchange, ImplicitAPI):
423
423
  #
424
424
  return self.parse_markets(response)
425
425
 
426
- def parse_market(self, market) -> Market:
426
+ def parse_market(self, market: dict) -> Market:
427
427
  id = self.safe_string(market, 'market')
428
428
  quoteId, baseId = id.split('-')
429
429
  base = self.safe_currency_code(baseId)
@@ -1261,7 +1261,7 @@ class upbit(Exchange, ImplicitAPI):
1261
1261
  #
1262
1262
  return self.parse_transaction(response, currency)
1263
1263
 
1264
- def parse_transaction_status(self, status):
1264
+ def parse_transaction_status(self, status: Str):
1265
1265
  statuses: dict = {
1266
1266
  'submitting': 'pending', # 처리 중
1267
1267
  'submitted': 'pending', # 처리 완료
@@ -1801,7 +1801,7 @@ class upbit(Exchange, ImplicitAPI):
1801
1801
  headers['Content-Type'] = 'application/json'
1802
1802
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
1803
1803
 
1804
- def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
1804
+ def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
1805
1805
  if response is None:
1806
1806
  return None # fallback to default error handler
1807
1807
  #