ccxt-ir 4.3.46.0.3__py2.py3-none-any.whl → 4.5.1__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 (529) hide show
  1. ccxt/__init__.py +39 -35
  2. ccxt/abantether.py +8 -8
  3. ccxt/abstract/alpaca.py +4 -0
  4. ccxt/abstract/apex.py +31 -0
  5. ccxt/abstract/bigone.py +1 -1
  6. ccxt/abstract/binance.py +106 -48
  7. ccxt/abstract/binancecoinm.py +106 -48
  8. ccxt/abstract/binanceus.py +141 -83
  9. ccxt/abstract/binanceusdm.py +106 -48
  10. ccxt/abstract/bingx.py +50 -1
  11. ccxt/abstract/bitbank.py +5 -0
  12. ccxt/abstract/bitfinex.py +136 -65
  13. ccxt/abstract/bitflyer.py +1 -0
  14. ccxt/abstract/bitget.py +67 -0
  15. ccxt/abstract/bitmart.py +19 -1
  16. ccxt/abstract/bitopro.py +1 -0
  17. ccxt/abstract/bitrue.py +68 -68
  18. ccxt/abstract/bitstamp.py +1 -0
  19. ccxt/abstract/blofin.py +30 -0
  20. ccxt/abstract/btcbox.py +2 -0
  21. ccxt/abstract/bybit.py +28 -13
  22. ccxt/abstract/cex.py +28 -29
  23. ccxt/abstract/coinbaseexchange.py +1 -0
  24. ccxt/abstract/coinbaseinternational.py +1 -1
  25. ccxt/abstract/cryptocom.py +16 -0
  26. ccxt/abstract/cryptomus.py +20 -0
  27. ccxt/abstract/defx.py +69 -0
  28. ccxt/abstract/deribit.py +1 -0
  29. ccxt/abstract/derive.py +117 -0
  30. ccxt/abstract/digifinex.py +1 -0
  31. ccxt/abstract/ellipx.py +25 -0
  32. ccxt/abstract/foxbit.py +26 -0
  33. ccxt/abstract/gate.py +19 -0
  34. ccxt/abstract/gateio.py +19 -0
  35. ccxt/abstract/gemini.py +1 -0
  36. ccxt/abstract/hibachi.py +26 -0
  37. ccxt/abstract/hyperliquid.py +1 -1
  38. ccxt/abstract/independentreserve.py +6 -0
  39. ccxt/abstract/kraken.py +1 -0
  40. ccxt/abstract/krakenfutures.py +4 -0
  41. ccxt/abstract/kucoin.py +10 -0
  42. ccxt/abstract/kucoinfutures.py +18 -0
  43. ccxt/abstract/lbank.py +2 -1
  44. ccxt/abstract/luno.py +1 -0
  45. ccxt/abstract/mexc.py +2 -0
  46. ccxt/abstract/modetrade.py +119 -0
  47. ccxt/abstract/myokx.py +349 -0
  48. ccxt/abstract/oceanex.py +5 -0
  49. ccxt/abstract/okx.py +25 -0
  50. ccxt/abstract/okxus.py +349 -0
  51. ccxt/abstract/onetrading.py +0 -12
  52. ccxt/abstract/paradex.py +23 -0
  53. ccxt/abstract/phemex.py +2 -0
  54. ccxt/abstract/poloniex.py +36 -0
  55. ccxt/abstract/tradeogre.py +3 -1
  56. ccxt/abstract/upbit.py +51 -34
  57. ccxt/abstract/whitebit.py +16 -0
  58. ccxt/abstract/woo.py +64 -6
  59. ccxt/abstract/xt.py +10 -5
  60. ccxt/afratether.py +8 -8
  61. ccxt/alpaca.py +828 -51
  62. ccxt/apex.py +1875 -0
  63. ccxt/arzinja.py +7 -7
  64. ccxt/arzplus.py +9 -9
  65. ccxt/ascendex.py +501 -306
  66. ccxt/async_support/__init__.py +39 -35
  67. ccxt/async_support/abantether.py +8 -8
  68. ccxt/async_support/afratether.py +10 -10
  69. ccxt/async_support/alpaca.py +828 -51
  70. ccxt/async_support/apex.py +1875 -0
  71. ccxt/async_support/arzinja.py +10 -10
  72. ccxt/async_support/arzplus.py +12 -12
  73. ccxt/async_support/ascendex.py +502 -306
  74. ccxt/async_support/base/exchange.py +303 -89
  75. ccxt/async_support/base/ws/cache.py +9 -3
  76. ccxt/async_support/base/ws/client.py +173 -38
  77. ccxt/async_support/base/ws/future.py +25 -37
  78. ccxt/async_support/bequant.py +5 -3
  79. ccxt/async_support/bigone.py +279 -144
  80. ccxt/async_support/binance.py +2347 -1158
  81. ccxt/async_support/binancecoinm.py +9 -3
  82. ccxt/async_support/binanceus.py +17 -3
  83. ccxt/async_support/binanceusdm.py +9 -4
  84. ccxt/async_support/bingx.py +2962 -920
  85. ccxt/async_support/bit2c.py +147 -27
  86. ccxt/async_support/bitbank.py +151 -23
  87. ccxt/async_support/bitbns.py +104 -30
  88. ccxt/async_support/bitfinex.py +3291 -1113
  89. ccxt/async_support/bitflyer.py +202 -27
  90. ccxt/async_support/bitget.py +3683 -1538
  91. ccxt/async_support/bithumb.py +195 -38
  92. ccxt/async_support/bitimen.py +12 -12
  93. ccxt/async_support/bitir.py +38 -38
  94. ccxt/async_support/bitmart.py +1288 -350
  95. ccxt/async_support/bitmex.py +260 -75
  96. ccxt/async_support/bitopro.py +262 -62
  97. ccxt/async_support/bitpin.py +17 -16
  98. ccxt/async_support/bitrue.py +459 -290
  99. ccxt/async_support/bitso.py +199 -54
  100. ccxt/async_support/bitstamp.py +230 -96
  101. ccxt/async_support/bitteam.py +167 -25
  102. ccxt/async_support/{huobijp.py → bittrade.py} +158 -30
  103. ccxt/async_support/bitvavo.py +213 -49
  104. ccxt/async_support/blockchaincom.py +160 -46
  105. ccxt/async_support/blofin.py +502 -120
  106. ccxt/async_support/btcalpha.py +169 -31
  107. ccxt/async_support/btcbox.py +292 -23
  108. ccxt/async_support/btcmarkets.py +211 -58
  109. ccxt/async_support/btcturk.py +161 -38
  110. ccxt/async_support/bybit.py +1775 -1030
  111. ccxt/async_support/cex.py +1440 -1303
  112. ccxt/async_support/coinbase.py +724 -212
  113. ccxt/async_support/coinbaseadvanced.py +2 -1
  114. ccxt/async_support/coinbaseexchange.py +388 -89
  115. ccxt/async_support/coinbaseinternational.py +412 -57
  116. ccxt/async_support/coincatch.py +177 -78
  117. ccxt/async_support/coincheck.py +135 -19
  118. ccxt/async_support/coinex.py +606 -232
  119. ccxt/async_support/coinmate.py +189 -63
  120. ccxt/async_support/coinmetro.py +195 -54
  121. ccxt/async_support/coinone.py +158 -51
  122. ccxt/async_support/coinsph.py +336 -61
  123. ccxt/async_support/coinspot.py +151 -52
  124. ccxt/async_support/cryptocom.py +661 -111
  125. ccxt/async_support/cryptomus.py +1137 -0
  126. ccxt/async_support/defx.py +2071 -0
  127. ccxt/async_support/delta.py +299 -99
  128. ccxt/async_support/deribit.py +348 -126
  129. ccxt/async_support/derive.py +2572 -0
  130. ccxt/async_support/digifinex.py +430 -214
  131. ccxt/async_support/ellipx.py +2029 -0
  132. ccxt/async_support/eterex.py +10 -10
  133. ccxt/async_support/excoino.py +31 -31
  134. ccxt/async_support/exir.py +14 -14
  135. ccxt/async_support/exmo.py +344 -131
  136. ccxt/async_support/exnovin.py +10 -10
  137. ccxt/async_support/farhadexchange.py +12 -12
  138. ccxt/async_support/fmfwio.py +2 -1
  139. ccxt/async_support/foxbit.py +1935 -0
  140. ccxt/async_support/gate.py +1351 -529
  141. ccxt/async_support/gateio.py +2 -1
  142. ccxt/async_support/gemini.py +144 -39
  143. ccxt/async_support/hashkey.py +152 -109
  144. ccxt/async_support/hibachi.py +2080 -0
  145. ccxt/async_support/hitbtc.py +395 -167
  146. ccxt/async_support/hitobit.py +12 -12
  147. ccxt/async_support/hollaex.py +307 -119
  148. ccxt/async_support/htx.py +851 -383
  149. ccxt/async_support/huobi.py +2 -1
  150. ccxt/async_support/hyperliquid.py +1848 -536
  151. ccxt/async_support/independentreserve.py +288 -15
  152. ccxt/async_support/indodax.py +190 -33
  153. ccxt/async_support/jibitex.py +12 -12
  154. ccxt/async_support/kraken.py +795 -351
  155. ccxt/async_support/krakenfutures.py +214 -62
  156. ccxt/async_support/kucoin.py +715 -396
  157. ccxt/async_support/kucoinfutures.py +652 -89
  158. ccxt/async_support/latoken.py +217 -113
  159. ccxt/async_support/lbank.py +425 -97
  160. ccxt/async_support/luno.py +382 -35
  161. ccxt/async_support/mercado.py +113 -6
  162. ccxt/async_support/mexc.py +874 -437
  163. ccxt/async_support/modetrade.py +2818 -0
  164. ccxt/async_support/myokx.py +54 -0
  165. ccxt/async_support/ndax.py +221 -64
  166. ccxt/async_support/nobitex.py +32 -38
  167. ccxt/async_support/novadax.py +190 -34
  168. ccxt/async_support/oceanex.py +217 -28
  169. ccxt/async_support/okcoin.py +253 -145
  170. ccxt/async_support/okexchange.py +11 -11
  171. ccxt/async_support/okx.py +1088 -351
  172. ccxt/async_support/okxus.py +54 -0
  173. ccxt/async_support/ompfinex.py +32 -27
  174. ccxt/async_support/onetrading.py +213 -392
  175. ccxt/async_support/oxfun.py +245 -166
  176. ccxt/async_support/p2b.py +151 -29
  177. ccxt/async_support/paradex.py +562 -49
  178. ccxt/async_support/paymium.py +82 -19
  179. ccxt/async_support/phemex.py +713 -172
  180. ccxt/async_support/poloniex.py +1602 -283
  181. ccxt/async_support/probit.py +224 -95
  182. ccxt/async_support/ramzinex.py +34 -30
  183. ccxt/async_support/sarmayex.py +9 -9
  184. ccxt/async_support/sarrafex.py +13 -13
  185. ccxt/async_support/tabdeal.py +15 -14
  186. ccxt/async_support/tetherland.py +9 -9
  187. ccxt/async_support/timex.py +210 -51
  188. ccxt/async_support/tokocrypto.py +167 -47
  189. ccxt/async_support/tradeogre.py +266 -31
  190. ccxt/async_support/twox.py +9 -9
  191. ccxt/async_support/ubitex.py +12 -12
  192. ccxt/async_support/upbit.py +568 -165
  193. ccxt/async_support/vertex.py +160 -32
  194. ccxt/async_support/wallex.py +12 -12
  195. ccxt/async_support/wavesexchange.py +165 -30
  196. ccxt/async_support/whitebit.py +975 -127
  197. ccxt/async_support/woo.py +1918 -1016
  198. ccxt/async_support/woofipro.py +433 -141
  199. ccxt/async_support/xt.py +649 -193
  200. ccxt/async_support/yobit.py +195 -70
  201. ccxt/async_support/zaif.py +91 -15
  202. ccxt/async_support/zonda.py +151 -36
  203. ccxt/base/decimal_to_precision.py +14 -10
  204. ccxt/base/errors.py +49 -18
  205. ccxt/base/exchange.py +1556 -450
  206. ccxt/base/precise.py +10 -0
  207. ccxt/base/types.py +114 -6
  208. ccxt/bequant.py +5 -3
  209. ccxt/bigone.py +279 -144
  210. ccxt/binance.py +2347 -1158
  211. ccxt/binancecoinm.py +9 -3
  212. ccxt/binanceus.py +17 -3
  213. ccxt/binanceusdm.py +9 -4
  214. ccxt/bingx.py +2962 -920
  215. ccxt/bit2c.py +147 -27
  216. ccxt/bitbank.py +151 -23
  217. ccxt/bitbns.py +104 -30
  218. ccxt/bitfinex.py +3290 -1113
  219. ccxt/bitflyer.py +202 -27
  220. ccxt/bitget.py +3683 -1538
  221. ccxt/bithumb.py +194 -38
  222. ccxt/bitimen.py +9 -9
  223. ccxt/bitir.py +35 -35
  224. ccxt/bitmart.py +1288 -350
  225. ccxt/bitmex.py +260 -75
  226. ccxt/bitopro.py +262 -62
  227. ccxt/bitpin.py +15 -14
  228. ccxt/bitrue.py +459 -290
  229. ccxt/bitso.py +199 -54
  230. ccxt/bitstamp.py +230 -96
  231. ccxt/bitteam.py +167 -25
  232. ccxt/{huobijp.py → bittrade.py} +158 -30
  233. ccxt/bitvavo.py +213 -49
  234. ccxt/blockchaincom.py +160 -46
  235. ccxt/blofin.py +502 -120
  236. ccxt/btcalpha.py +169 -31
  237. ccxt/btcbox.py +291 -23
  238. ccxt/btcmarkets.py +211 -58
  239. ccxt/btcturk.py +161 -38
  240. ccxt/bybit.py +1775 -1030
  241. ccxt/cex.py +1439 -1303
  242. ccxt/coinbase.py +724 -212
  243. ccxt/coinbaseadvanced.py +2 -1
  244. ccxt/coinbaseexchange.py +388 -89
  245. ccxt/coinbaseinternational.py +412 -57
  246. ccxt/coincatch.py +177 -78
  247. ccxt/coincheck.py +135 -19
  248. ccxt/coinex.py +606 -232
  249. ccxt/coinmate.py +189 -63
  250. ccxt/coinmetro.py +194 -54
  251. ccxt/coinone.py +158 -51
  252. ccxt/coinsph.py +336 -61
  253. ccxt/coinspot.py +151 -52
  254. ccxt/cryptocom.py +661 -111
  255. ccxt/cryptomus.py +1137 -0
  256. ccxt/defx.py +2070 -0
  257. ccxt/delta.py +299 -99
  258. ccxt/deribit.py +348 -126
  259. ccxt/derive.py +2571 -0
  260. ccxt/digifinex.py +430 -214
  261. ccxt/ellipx.py +2029 -0
  262. ccxt/eterex.py +7 -7
  263. ccxt/excoino.py +29 -29
  264. ccxt/exir.py +11 -11
  265. ccxt/exmo.py +343 -131
  266. ccxt/exnovin.py +8 -8
  267. ccxt/farhadexchange.py +10 -10
  268. ccxt/fmfwio.py +2 -1
  269. ccxt/foxbit.py +1935 -0
  270. ccxt/gate.py +1351 -529
  271. ccxt/gateio.py +2 -1
  272. ccxt/gemini.py +144 -39
  273. ccxt/hashkey.py +152 -109
  274. ccxt/hibachi.py +2079 -0
  275. ccxt/hitbtc.py +395 -167
  276. ccxt/hitobit.py +9 -9
  277. ccxt/hollaex.py +307 -119
  278. ccxt/htx.py +851 -383
  279. ccxt/huobi.py +2 -1
  280. ccxt/hyperliquid.py +1848 -536
  281. ccxt/independentreserve.py +287 -15
  282. ccxt/indodax.py +190 -33
  283. ccxt/jibitex.py +9 -9
  284. ccxt/kraken.py +794 -351
  285. ccxt/krakenfutures.py +214 -62
  286. ccxt/kucoin.py +715 -396
  287. ccxt/kucoinfutures.py +652 -89
  288. ccxt/latoken.py +217 -113
  289. ccxt/lbank.py +425 -97
  290. ccxt/luno.py +382 -35
  291. ccxt/mercado.py +113 -6
  292. ccxt/mexc.py +873 -437
  293. ccxt/modetrade.py +2818 -0
  294. ccxt/myokx.py +54 -0
  295. ccxt/ndax.py +221 -64
  296. ccxt/nobitex.py +30 -36
  297. ccxt/novadax.py +190 -34
  298. ccxt/oceanex.py +217 -28
  299. ccxt/okcoin.py +253 -145
  300. ccxt/okexchange.py +9 -9
  301. ccxt/okx.py +1088 -351
  302. ccxt/okxus.py +54 -0
  303. ccxt/ompfinex.py +29 -24
  304. ccxt/onetrading.py +213 -392
  305. ccxt/oxfun.py +245 -166
  306. ccxt/p2b.py +151 -29
  307. ccxt/paradex.py +562 -49
  308. ccxt/paymium.py +82 -19
  309. ccxt/phemex.py +712 -172
  310. ccxt/poloniex.py +1601 -283
  311. ccxt/pro/__init__.py +76 -17
  312. ccxt/pro/alpaca.py +21 -6
  313. ccxt/pro/apex.py +984 -0
  314. ccxt/pro/ascendex.py +58 -10
  315. ccxt/pro/bequant.py +6 -1
  316. ccxt/pro/binance.py +728 -156
  317. ccxt/pro/binancecoinm.py +6 -2
  318. ccxt/pro/binanceus.py +8 -4
  319. ccxt/pro/binanceusdm.py +7 -2
  320. ccxt/pro/bingx.py +333 -142
  321. ccxt/pro/bitfinex.py +727 -262
  322. ccxt/pro/bitget.py +570 -79
  323. ccxt/pro/bithumb.py +20 -6
  324. ccxt/pro/bitmart.py +216 -87
  325. ccxt/pro/bitmex.py +47 -9
  326. ccxt/pro/bitopro.py +26 -14
  327. ccxt/pro/bitrue.py +22 -22
  328. ccxt/pro/bitstamp.py +54 -21
  329. ccxt/pro/{huobijp.py → bittrade.py} +7 -6
  330. ccxt/pro/bitvavo.py +191 -67
  331. ccxt/pro/blockchaincom.py +21 -8
  332. ccxt/pro/blofin.py +9 -1
  333. ccxt/pro/bybit.py +632 -245
  334. ccxt/pro/cex.py +59 -24
  335. ccxt/pro/coinbase.py +102 -73
  336. ccxt/pro/coinbaseadvanced.py +2 -1
  337. ccxt/pro/coinbaseexchange.py +8 -8
  338. ccxt/pro/coinbaseinternational.py +181 -25
  339. ccxt/pro/coincatch.py +6 -7
  340. ccxt/pro/coincheck.py +11 -6
  341. ccxt/pro/coinex.py +967 -665
  342. ccxt/pro/coinone.py +16 -9
  343. ccxt/pro/cryptocom.py +448 -45
  344. ccxt/pro/defx.py +831 -0
  345. ccxt/pro/deribit.py +150 -14
  346. ccxt/pro/derive.py +704 -0
  347. ccxt/pro/exmo.py +239 -6
  348. ccxt/pro/gate.py +623 -65
  349. ccxt/pro/gateio.py +2 -1
  350. ccxt/pro/gemini.py +27 -11
  351. ccxt/pro/hashkey.py +2 -2
  352. ccxt/pro/hitbtc.py +196 -91
  353. ccxt/pro/hollaex.py +23 -7
  354. ccxt/pro/htx.py +51 -14
  355. ccxt/pro/huobi.py +2 -1
  356. ccxt/pro/hyperliquid.py +591 -27
  357. ccxt/pro/independentreserve.py +9 -6
  358. ccxt/pro/kraken.py +640 -320
  359. ccxt/pro/krakenfutures.py +62 -35
  360. ccxt/pro/kucoin.py +267 -46
  361. ccxt/pro/kucoinfutures.py +165 -21
  362. ccxt/pro/lbank.py +102 -21
  363. ccxt/pro/luno.py +12 -8
  364. ccxt/pro/mexc.py +877 -111
  365. ccxt/pro/modetrade.py +1271 -0
  366. ccxt/pro/myokx.py +38 -0
  367. ccxt/pro/ndax.py +15 -2
  368. ccxt/pro/okcoin.py +23 -4
  369. ccxt/pro/okx.py +573 -98
  370. ccxt/pro/okxus.py +38 -0
  371. ccxt/pro/onetrading.py +30 -13
  372. ccxt/pro/oxfun.py +131 -27
  373. ccxt/pro/p2b.py +88 -22
  374. ccxt/pro/paradex.py +3 -3
  375. ccxt/pro/phemex.py +75 -21
  376. ccxt/pro/poloniex.py +124 -41
  377. ccxt/pro/probit.py +87 -80
  378. ccxt/pro/tradeogre.py +272 -0
  379. ccxt/pro/upbit.py +152 -12
  380. ccxt/pro/vertex.py +8 -3
  381. ccxt/pro/whitebit.py +58 -5
  382. ccxt/pro/woo.py +228 -37
  383. ccxt/pro/woofipro.py +106 -18
  384. ccxt/pro/xt.py +111 -5
  385. ccxt/probit.py +224 -95
  386. ccxt/protobuf/__init__.py +0 -0
  387. ccxt/protobuf/mexc/PrivateAccountV3Api_pb2.py +37 -0
  388. ccxt/protobuf/mexc/PrivateDealsV3Api_pb2.py +37 -0
  389. ccxt/protobuf/mexc/PrivateOrdersV3Api_pb2.py +37 -0
  390. ccxt/protobuf/mexc/PublicAggreBookTickerV3Api_pb2.py +37 -0
  391. ccxt/protobuf/mexc/PublicAggreDealsV3Api_pb2.py +39 -0
  392. ccxt/protobuf/mexc/PublicAggreDepthsV3Api_pb2.py +39 -0
  393. ccxt/protobuf/mexc/PublicBookTickerBatchV3Api_pb2.py +38 -0
  394. ccxt/protobuf/mexc/PublicBookTickerV3Api_pb2.py +37 -0
  395. ccxt/protobuf/mexc/PublicDealsV3Api_pb2.py +39 -0
  396. ccxt/protobuf/mexc/PublicIncreaseDepthsBatchV3Api_pb2.py +38 -0
  397. ccxt/protobuf/mexc/PublicIncreaseDepthsV3Api_pb2.py +39 -0
  398. ccxt/protobuf/mexc/PublicLimitDepthsV3Api_pb2.py +39 -0
  399. ccxt/protobuf/mexc/PublicMiniTickerV3Api_pb2.py +37 -0
  400. ccxt/protobuf/mexc/PublicMiniTickersV3Api_pb2.py +38 -0
  401. ccxt/protobuf/mexc/PublicSpotKlineV3Api_pb2.py +37 -0
  402. ccxt/protobuf/mexc/PushDataV3ApiWrapper_pb2.py +52 -0
  403. ccxt/protobuf/mexc/__init__.py +0 -0
  404. ccxt/ramzinex.py +32 -28
  405. ccxt/sarmayex.py +7 -7
  406. ccxt/sarrafex.py +10 -10
  407. ccxt/static_dependencies/__init__.py +1 -1
  408. ccxt/static_dependencies/lark/py.typed +0 -0
  409. ccxt/static_dependencies/marshmallow/py.typed +0 -0
  410. ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
  411. ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
  412. ccxt/tabdeal.py +13 -12
  413. ccxt/test/tests_async.py +261 -57
  414. ccxt/test/tests_helpers.py +1 -3
  415. ccxt/test/tests_init.py +4 -3
  416. ccxt/test/tests_sync.py +261 -57
  417. ccxt/tetherland.py +7 -7
  418. ccxt/timex.py +210 -51
  419. ccxt/tokocrypto.py +167 -47
  420. ccxt/tradeogre.py +266 -31
  421. ccxt/twox.py +7 -7
  422. ccxt/ubitex.py +9 -9
  423. ccxt/upbit.py +568 -165
  424. ccxt/vertex.py +160 -32
  425. ccxt/wallex.py +9 -9
  426. ccxt/wavesexchange.py +165 -30
  427. ccxt/whitebit.py +975 -127
  428. ccxt/woo.py +1917 -1016
  429. ccxt/woofipro.py +432 -141
  430. ccxt/xt.py +649 -193
  431. ccxt/yobit.py +194 -70
  432. ccxt/zaif.py +91 -15
  433. ccxt/zonda.py +151 -36
  434. {ccxt_ir-4.3.46.0.3.dist-info → ccxt_ir-4.5.1.dist-info}/METADATA +225 -73
  435. ccxt_ir-4.5.1.dist-info/RECORD +743 -0
  436. {ccxt_ir-4.3.46.0.3.dist-info → ccxt_ir-4.5.1.dist-info}/WHEEL +1 -1
  437. ccxt/__test__.py +0 -7
  438. ccxt/abstract/ace.py +0 -15
  439. ccxt/abstract/bitbay.py +0 -53
  440. ccxt/abstract/bitcoincom.py +0 -115
  441. ccxt/abstract/bitfinex2.py +0 -139
  442. ccxt/abstract/bitpanda.py +0 -35
  443. ccxt/abstract/bl3p.py +0 -19
  444. ccxt/abstract/coinlist.py +0 -54
  445. ccxt/abstract/currencycom.py +0 -68
  446. ccxt/abstract/hitbtc3.py +0 -115
  447. ccxt/abstract/idex.py +0 -26
  448. ccxt/abstract/kuna.py +0 -182
  449. ccxt/abstract/lykke.py +0 -29
  450. ccxt/abstract/poloniexfutures.py +0 -48
  451. ccxt/abstract/wazirx.py +0 -30
  452. ccxt/ace.py +0 -1012
  453. ccxt/async_support/ace.py +0 -1012
  454. ccxt/async_support/base/ws/aiohttp_client.py +0 -125
  455. ccxt/async_support/base/ws/fast_client.py +0 -96
  456. ccxt/async_support/bitbay.py +0 -17
  457. ccxt/async_support/bitcoincom.py +0 -17
  458. ccxt/async_support/bitfinex2.py +0 -3552
  459. ccxt/async_support/bitpanda.py +0 -16
  460. ccxt/async_support/bl3p.py +0 -485
  461. ccxt/async_support/coinlist.py +0 -2243
  462. ccxt/async_support/currencycom.py +0 -1950
  463. ccxt/async_support/hitbtc3.py +0 -16
  464. ccxt/async_support/idex.py +0 -1766
  465. ccxt/async_support/kuna.py +0 -1841
  466. ccxt/async_support/lykke.py +0 -1270
  467. ccxt/async_support/poloniexfutures.py +0 -1717
  468. ccxt/async_support/wazirx.py +0 -1224
  469. ccxt/bitbay.py +0 -17
  470. ccxt/bitcoincom.py +0 -17
  471. ccxt/bitfinex2.py +0 -3552
  472. ccxt/bitpanda.py +0 -16
  473. ccxt/bl3p.py +0 -485
  474. ccxt/coinlist.py +0 -2243
  475. ccxt/currencycom.py +0 -1950
  476. ccxt/hitbtc3.py +0 -16
  477. ccxt/idex.py +0 -1766
  478. ccxt/kuna.py +0 -1841
  479. ccxt/lykke.py +0 -1270
  480. ccxt/poloniexfutures.py +0 -1717
  481. ccxt/pro/bitcoincom.py +0 -34
  482. ccxt/pro/bitfinex2.py +0 -1083
  483. ccxt/pro/bitpanda.py +0 -15
  484. ccxt/pro/currencycom.py +0 -536
  485. ccxt/pro/idex.py +0 -672
  486. ccxt/pro/poloniexfutures.py +0 -990
  487. ccxt/pro/wazirx.py +0 -749
  488. ccxt/test/base/__init__.py +0 -29
  489. ccxt/test/base/test_account.py +0 -26
  490. ccxt/test/base/test_balance.py +0 -56
  491. ccxt/test/base/test_borrow_interest.py +0 -35
  492. ccxt/test/base/test_borrow_rate.py +0 -32
  493. ccxt/test/base/test_calculate_fee.py +0 -51
  494. ccxt/test/base/test_crypto.py +0 -127
  495. ccxt/test/base/test_currency.py +0 -76
  496. ccxt/test/base/test_datetime.py +0 -109
  497. ccxt/test/base/test_decimal_to_precision.py +0 -392
  498. ccxt/test/base/test_deep_extend.py +0 -68
  499. ccxt/test/base/test_deposit_withdrawal.py +0 -50
  500. ccxt/test/base/test_exchange_datetime_functions.py +0 -76
  501. ccxt/test/base/test_funding_rate_history.py +0 -29
  502. ccxt/test/base/test_last_price.py +0 -31
  503. ccxt/test/base/test_ledger_entry.py +0 -45
  504. ccxt/test/base/test_ledger_item.py +0 -48
  505. ccxt/test/base/test_leverage_tier.py +0 -33
  506. ccxt/test/base/test_liquidation.py +0 -50
  507. ccxt/test/base/test_margin_mode.py +0 -24
  508. ccxt/test/base/test_margin_modification.py +0 -35
  509. ccxt/test/base/test_market.py +0 -193
  510. ccxt/test/base/test_number.py +0 -411
  511. ccxt/test/base/test_ohlcv.py +0 -33
  512. ccxt/test/base/test_open_interest.py +0 -32
  513. ccxt/test/base/test_order.py +0 -64
  514. ccxt/test/base/test_order_book.py +0 -69
  515. ccxt/test/base/test_position.py +0 -60
  516. ccxt/test/base/test_shared_methods.py +0 -353
  517. ccxt/test/base/test_status.py +0 -24
  518. ccxt/test/base/test_throttle.py +0 -126
  519. ccxt/test/base/test_ticker.py +0 -92
  520. ccxt/test/base/test_trade.py +0 -47
  521. ccxt/test/base/test_trading_fee.py +0 -26
  522. ccxt/test/base/test_transaction.py +0 -39
  523. ccxt/test/test_async.py +0 -1649
  524. ccxt/test/test_sync.py +0 -1648
  525. ccxt/wazirx.py +0 -1224
  526. ccxt_ir-4.3.46.0.3.dist-info/RECORD +0 -773
  527. /ccxt/abstract/{huobijp.py → bittrade.py} +0 -0
  528. {ccxt_ir-4.3.46.0.3.dist-info → ccxt_ir-4.5.1.dist-info/licenses}/LICENSE.txt +0 -0
  529. {ccxt_ir-4.3.46.0.3.dist-info → ccxt_ir-4.5.1.dist-info}/top_level.txt +0 -0
@@ -7,15 +7,18 @@ from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.bybit import ImplicitAPI
8
8
  import asyncio
9
9
  import hashlib
10
- from ccxt.base.types import Balances, CrossBorrowRate, Currencies, Currency, Greeks, Int, Leverage, LeverageTier, LeverageTiers, Market, MarketInterface, Num, Option, OptionChain, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, TradingFees, Transaction, TransferEntry, TransferEntries
10
+ from ccxt.base.types import Any, Balances, BorrowInterest, Conversion, CrossBorrowRate, Currencies, Currency, DepositAddress, FundingHistory, Greeks, Int, LedgerEntry, Leverage, LeverageTier, LeverageTiers, Liquidation, LongShortRatio, Market, Num, Option, OptionChain, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFeeInterface, TradingFees, Transaction, MarketInterface, TransferEntry
11
11
  from typing import List
12
12
  from ccxt.base.errors import ExchangeError
13
13
  from ccxt.base.errors import AuthenticationError
14
14
  from ccxt.base.errors import PermissionDenied
15
+ from ccxt.base.errors import AccountSuspended
15
16
  from ccxt.base.errors import ArgumentsRequired
16
17
  from ccxt.base.errors import BadRequest
18
+ from ccxt.base.errors import BadSymbol
17
19
  from ccxt.base.errors import NoChange
18
20
  from ccxt.base.errors import MarginModeAlreadySet
21
+ from ccxt.base.errors import ManualInteractionNeeded
19
22
  from ccxt.base.errors import InsufficientFunds
20
23
  from ccxt.base.errors import InvalidOrder
21
24
  from ccxt.base.errors import OrderNotFound
@@ -29,7 +32,7 @@ from ccxt.base.precise import Precise
29
32
 
30
33
  class bybit(Exchange, ImplicitAPI):
31
34
 
32
- def describe(self):
35
+ def describe(self) -> Any:
33
36
  return self.deep_extend(super(bybit, self).describe(), {
34
37
  'id': 'bybit',
35
38
  'name': 'Bybit',
@@ -49,11 +52,13 @@ class bybit(Exchange, ImplicitAPI):
49
52
  'option': True,
50
53
  'borrowCrossMargin': True,
51
54
  'cancelAllOrders': True,
55
+ 'cancelAllOrdersAfter': True,
52
56
  'cancelOrder': True,
53
57
  'cancelOrders': True,
54
58
  'cancelOrdersForSymbols': True,
55
59
  'closeAllPositions': False,
56
60
  'closePosition': False,
61
+ 'createConvertTrade': True,
57
62
  'createMarketBuyOrderWithCost': True,
58
63
  'createMarketSellOrderWithCost': True,
59
64
  'createOrder': True,
@@ -69,7 +74,10 @@ class bybit(Exchange, ImplicitAPI):
69
74
  'createTrailingAmountOrder': True,
70
75
  'createTriggerOrder': True,
71
76
  'editOrder': True,
77
+ 'editOrders': True,
78
+ 'fetchAllGreeks': True,
72
79
  'fetchBalance': True,
80
+ 'fetchBidsAsks': 'emulated',
73
81
  'fetchBorrowInterest': False, # temporarily disabled, doesn't work
74
82
  'fetchBorrowRateHistories': False,
75
83
  'fetchBorrowRateHistory': False,
@@ -77,6 +85,10 @@ class bybit(Exchange, ImplicitAPI):
77
85
  'fetchCanceledOrders': True,
78
86
  'fetchClosedOrder': True,
79
87
  'fetchClosedOrders': True,
88
+ 'fetchConvertCurrencies': True,
89
+ 'fetchConvertQuote': True,
90
+ 'fetchConvertTrade': True,
91
+ 'fetchConvertTradeHistory': True,
80
92
  'fetchCrossBorrowRate': True,
81
93
  'fetchCrossBorrowRates': False,
82
94
  'fetchCurrencies': True,
@@ -88,7 +100,7 @@ class bybit(Exchange, ImplicitAPI):
88
100
  'fetchDepositWithdrawFee': 'emulated',
89
101
  'fetchDepositWithdrawFees': True,
90
102
  'fetchFundingHistory': True,
91
- 'fetchFundingRate': True, # emulated in exchange
103
+ 'fetchFundingRate': 'emulated', # emulated in exchange
92
104
  'fetchFundingRateHistory': True,
93
105
  'fetchFundingRates': True,
94
106
  'fetchGreeks': True,
@@ -98,6 +110,8 @@ class bybit(Exchange, ImplicitAPI):
98
110
  'fetchLedger': True,
99
111
  'fetchLeverage': True,
100
112
  'fetchLeverageTiers': True,
113
+ 'fetchLongShortRatio': False,
114
+ 'fetchLongShortRatioHistory': True,
101
115
  'fetchMarginAdjustmentHistory': False,
102
116
  'fetchMarketLeverageTiers': True,
103
117
  'fetchMarkets': True,
@@ -112,7 +126,7 @@ class bybit(Exchange, ImplicitAPI):
112
126
  'fetchOpenOrders': True,
113
127
  'fetchOption': True,
114
128
  'fetchOptionChain': True,
115
- 'fetchOrder': False,
129
+ 'fetchOrder': True,
116
130
  'fetchOrderBook': True,
117
131
  'fetchOrders': False,
118
132
  'fetchOrderTrades': True,
@@ -164,7 +178,7 @@ class bybit(Exchange, ImplicitAPI):
164
178
  'public': 'https://api-testnet.{hostname}',
165
179
  'private': 'https://api-testnet.{hostname}',
166
180
  },
167
- 'logo': 'https://user-images.githubusercontent.com/51840849/76547799-daff5b80-649e-11ea-87fb-3be9bac08954.jpg',
181
+ 'logo': 'https://github.com/user-attachments/assets/97a5d0b3-de10-423d-90e1-6620960025ed',
168
182
  'api': {
169
183
  'spot': 'https://api.{hostname}',
170
184
  'futures': 'https://api.{hostname}',
@@ -244,16 +258,23 @@ class bybit(Exchange, ImplicitAPI):
244
258
  'v5/spot-lever-token/reference': 5,
245
259
  # spot margin trade
246
260
  'v5/spot-margin-trade/data': 5,
261
+ 'v5/spot-margin-trade/collateral': 5,
247
262
  'v5/spot-cross-margin-trade/data': 5,
248
263
  'v5/spot-cross-margin-trade/pledge-token': 5,
249
264
  'v5/spot-cross-margin-trade/borrow-token': 5,
265
+ # crypto loan
266
+ 'v5/crypto-loan/collateral-data': 5,
267
+ 'v5/crypto-loan/loanable-data': 5,
250
268
  # institutional lending
251
269
  'v5/ins-loan/product-infos': 5,
252
270
  'v5/ins-loan/ensure-tokens-convert': 5,
271
+ # earn
272
+ 'v5/earn/product': 5,
253
273
  },
254
274
  },
255
275
  'private': {
256
276
  'get': {
277
+ 'v5/market/instruments-info': 5,
257
278
  # Legacy inverse swap
258
279
  'v2/private/wallet/fund/records': 25, # 120 per minute = 2 per second => cost = 50 / 2 = 25
259
280
  # spot
@@ -341,7 +362,11 @@ class bybit(Exchange, ImplicitAPI):
341
362
  'v5/account/contract-transaction-log': 1,
342
363
  'v5/account/smp-group': 1,
343
364
  'v5/account/mmp-state': 5,
365
+ 'v5/account/withdrawal': 5,
344
366
  # asset
367
+ 'v5/asset/exchange/query-coin-list': 0.5, # 100/s => cost = 50 / 100 = 0.5
368
+ 'v5/asset/exchange/convert-result-query': 0.5, # 100/s => cost = 50 / 100 = 0.5
369
+ 'v5/asset/exchange/query-convert-history': 0.5, # 100/s => cost = 50 / 100 = 0.5
345
370
  'v5/asset/exchange/order-record': 5, # 10/s => cost = 50 / 10 = 5
346
371
  'v5/asset/delivery-record': 5,
347
372
  'v5/asset/settlement-record': 5,
@@ -370,14 +395,24 @@ class bybit(Exchange, ImplicitAPI):
370
395
  'v5/user/aff-customer-info': 5,
371
396
  'v5/user/del-submember': 5,
372
397
  'v5/user/submembers': 5,
398
+ # affilate
399
+ 'v5/affiliate/aff-user-list': 5,
373
400
  # spot leverage token
374
401
  'v5/spot-lever-token/order-record': 1, # 50/s => cost = 50 / 50 = 1
375
402
  # spot margin trade
403
+ 'v5/spot-margin-trade/interest-rate-history': 5,
376
404
  'v5/spot-margin-trade/state': 5,
377
405
  'v5/spot-cross-margin-trade/loan-info': 1, # 50/s => cost = 50 / 50 = 1
378
406
  'v5/spot-cross-margin-trade/account': 1, # 50/s => cost = 50 / 50 = 1
379
407
  'v5/spot-cross-margin-trade/orders': 1, # 50/s => cost = 50 / 50 = 1
380
408
  'v5/spot-cross-margin-trade/repay-history': 1, # 50/s => cost = 50 / 50 = 1
409
+ # crypto loan
410
+ 'v5/crypto-loan/borrowable-collateralisable-number': 5,
411
+ 'v5/crypto-loan/ongoing-orders': 5,
412
+ 'v5/crypto-loan/repayment-history': 5,
413
+ 'v5/crypto-loan/borrow-history': 5,
414
+ 'v5/crypto-loan/max-collateral-amount': 5,
415
+ 'v5/crypto-loan/adjustment-history': 5,
381
416
  # institutional lending
382
417
  'v5/ins-loan/product-infos': 5,
383
418
  'v5/ins-loan/ensure-tokens-convert': 5,
@@ -389,27 +424,15 @@ class bybit(Exchange, ImplicitAPI):
389
424
  'v5/lending/history-order': 5,
390
425
  'v5/lending/account': 5,
391
426
  # broker
392
- 'v5/broker/earning-record': 5,
427
+ 'v5/broker/earning-record': 5, # deprecated
393
428
  'v5/broker/earnings-info': 5,
394
429
  'v5/broker/account-info': 5,
395
430
  'v5/broker/asset/query-sub-member-deposit-record': 10,
431
+ # earn
432
+ 'v5/earn/order': 5,
433
+ 'v5/earn/position': 5,
396
434
  },
397
435
  'post': {
398
- # Legacy option USDC
399
- 'option/usdc/openapi/private/v1/place-order': 2.5,
400
- 'option/usdc/openapi/private/v1/replace-order': 2.5,
401
- 'option/usdc/openapi/private/v1/cancel-order': 2.5,
402
- 'option/usdc/openapi/private/v1/cancel-all': 2.5,
403
- 'option/usdc/openapi/private/v1/query-active-orders': 2.5,
404
- 'option/usdc/openapi/private/v1/query-order-history': 2.5,
405
- 'option/usdc/openapi/private/v1/execution-list': 2.5,
406
- 'option/usdc/openapi/private/v1/query-position': 2.5,
407
- # Legacy perpetual swap USDC
408
- 'perpetual/usdc/openapi/private/v1/place-order': 2.5,
409
- 'perpetual/usdc/openapi/private/v1/replace-order': 2.5,
410
- 'perpetual/usdc/openapi/private/v1/cancel-order': 2.5,
411
- 'perpetual/usdc/openapi/private/v1/cancel-all': 2.5,
412
- 'perpetual/usdc/openapi/private/v1/position/leverage/save': 2.5,
413
436
  # spot
414
437
  'spot/v3/private/order': 2.5,
415
438
  'spot/v3/private/cancel-order': 2.5,
@@ -500,6 +523,8 @@ class bybit(Exchange, ImplicitAPI):
500
523
  'v5/account/mmp-modify': 5,
501
524
  'v5/account/mmp-reset': 5,
502
525
  # asset
526
+ 'v5/asset/exchange/quote-apply': 1, # 50/s
527
+ 'v5/asset/exchange/convert-execute': 1, # 50/s
503
528
  'v5/asset/transfer/inter-transfer': 50, # 1/s => cost = 50 / 1 = 50
504
529
  'v5/asset/transfer/save-transfer-sub-member': 150, # 1/3/s => cost = 50 / 1/3 = 150
505
530
  'v5/asset/transfer/universal-transfer': 10, # 5/s => cost = 50 / 5 = 10
@@ -523,6 +548,10 @@ class bybit(Exchange, ImplicitAPI):
523
548
  'v5/spot-cross-margin-trade/loan': 2.5, # 20/s => cost = 50 / 20 = 2.5
524
549
  'v5/spot-cross-margin-trade/repay': 2.5, # 20/s => cost = 50 / 20 = 2.5
525
550
  'v5/spot-cross-margin-trade/switch': 2.5, # 20/s => cost = 50 / 20 = 2.5
551
+ # crypto loan
552
+ 'v5/crypto-loan/borrow': 5,
553
+ 'v5/crypto-loan/repay': 5,
554
+ 'v5/crypto-loan/adjust-ltv': 5,
526
555
  # institutional lending
527
556
  'v5/ins-loan/association-uid': 5,
528
557
  # c2c lending
@@ -533,6 +562,12 @@ class bybit(Exchange, ImplicitAPI):
533
562
  'v5/account/set-collateral-switch-batch': 5,
534
563
  # demo trading
535
564
  'v5/account/demo-apply-money': 5,
565
+ # broker
566
+ 'v5/broker/award/info': 5,
567
+ 'v5/broker/award/distribute-award': 5,
568
+ 'v5/broker/award/distribution-record': 5,
569
+ # earn
570
+ 'v5/earn/place-order': 5,
536
571
  },
537
572
  },
538
573
  },
@@ -566,7 +601,7 @@ class bybit(Exchange, ImplicitAPI):
566
601
  '10005': PermissionDenied, # permission denied for current apikey
567
602
  '10006': RateLimitExceeded, # too many requests
568
603
  '10007': AuthenticationError, # api_key not found in your request parameters
569
- '10008': AuthenticationError, # User had been banned
604
+ '10008': AccountSuspended, # User had been banned
570
605
  '10009': AuthenticationError, # IP had been banned
571
606
  '10010': PermissionDenied, # request ip mismatch
572
607
  '10014': BadRequest, # Request is duplicate
@@ -654,6 +689,9 @@ class bybit(Exchange, ImplicitAPI):
654
689
  '110071': ExchangeError, # Sorry, we're revamping the Unified Margin Account! Currently, new upgrades are not supported. If you have any questions, please contact our 24/7 customer support.
655
690
  '110072': InvalidOrder, # OrderLinkedID is duplicate
656
691
  '110073': ExchangeError, # Set margin mode failed
692
+ '110092': InvalidOrder, # expect Rising, but trigger_price[XXXXX] <= current[XXXXX]
693
+ '110093': InvalidOrder, # expect Falling, but trigger_price[XXXXX] >= current[XXXXX]
694
+ '110094': InvalidOrder, # Order notional value below the lower limit
657
695
  '130006': InvalidOrder, # {"ret_code":130006,"ret_msg":"The number of contracts exceeds maximum limit allowed: too large","ext_code":"","ext_info":"","result":null,"time_now":"1658397095.099030","rate_limit_status":99,"rate_limit_reset_ms":1658397095097,"rate_limit":100}
658
696
  '130021': InsufficientFunds, # {"ret_code":130021,"ret_msg":"orderfix price failed for CannotAffordOrderCost.","ext_code":"","ext_info":"","result":null,"time_now":"1644588250.204878","rate_limit_status":98,"rate_limit_reset_ms":1644588250200,"rate_limit":100} | {"ret_code":130021,"ret_msg":"oc_diff[1707966351], new_oc[1707966351] with ob[....]+AB[....]","ext_code":"","ext_info":"","result":null,"time_now":"1658395300.872766","rate_limit_status":99,"rate_limit_reset_ms":1658395300855,"rate_limit":100} caused issues/9149#issuecomment-1146559498
659
697
  '130074': InvalidOrder, # {"ret_code":130074,"ret_msg":"expect Rising, but trigger_price[190000000] \u003c= current[211280000]??LastPrice","ext_code":"","ext_info":"","result":null,"time_now":"1655386638.067076","rate_limit_status":97,"rate_limit_reset_ms":1655386638065,"rate_limit":100}
@@ -766,8 +804,11 @@ class bybit(Exchange, ImplicitAPI):
766
804
  '140069': PermissionDenied, # Do not allow OTC lending users to trade
767
805
  '140070': InvalidOrder, # ETP symbols are not allowed to be traded
768
806
  '170001': ExchangeError, # Internal error.
769
- '170007': RequestTimeout, # Timeout waiting for response from backend server.
770
807
  '170005': InvalidOrder, # Too many new orders; current limit is %s orders per %s.
808
+ '170007': RequestTimeout, # Timeout waiting for response from backend server.
809
+ '170010': InvalidOrder, # Purchase failed: Exceed the maximum position limit of leveraged tokens, the current available limit is %s USDT
810
+ '170011': InvalidOrder, # "Purchase failed: Exceed the maximum position limit of innovation tokens,
811
+ '170019': InvalidOrder, # the current available limit is replaceKey0 USDT"
771
812
  '170031': ExchangeError, # The feature has been suspended
772
813
  '170032': ExchangeError, # Network error. Please try again later
773
814
  '170033': InsufficientFunds, # margin Insufficient account balance
@@ -780,6 +821,7 @@ class bybit(Exchange, ImplicitAPI):
780
821
  '170116': InvalidOrder, # Invalid orderType.
781
822
  '170117': InvalidOrder, # Invalid side.
782
823
  '170121': InvalidOrder, # Invalid symbol.
824
+ '170124': InvalidOrder, # Order amount too large.
783
825
  '170130': BadRequest, # Data sent for paramter '%s' is not valid.
784
826
  '170131': InsufficientFunds, # Balance insufficient
785
827
  '170132': InvalidOrder, # Order price too high.
@@ -790,7 +832,6 @@ class bybit(Exchange, ImplicitAPI):
790
832
  '170137': InvalidOrder, # Order volume decimal too long
791
833
  '170139': InvalidOrder, # Order has been filled.
792
834
  '170140': InvalidOrder, # Transaction amount lower than the minimum.
793
- '170124': InvalidOrder, # Order amount too large.
794
835
  '170141': InvalidOrder, # Duplicate clientOrderId
795
836
  '170142': InvalidOrder, # Order has been canceled
796
837
  '170143': InvalidOrder, # Cannot be found on order book
@@ -815,6 +856,15 @@ class bybit(Exchange, ImplicitAPI):
815
856
  '170198': InvalidOrder, # Your order quantity to sell is too large. The filled price may deviate significantly from the market price. Please try again
816
857
  '170199': InvalidOrder, # Your order quantity to buy is too large. The filled price may deviate significantly from the nav. Please try again.
817
858
  '170200': InvalidOrder, # Your order quantity to sell is too large. The filled price may deviate significantly from the nav. Please try again.
859
+ '170201': PermissionDenied, # Your account has been restricted for trades. If you have any questions, please email us at support@bybit.com
860
+ '170202': InvalidOrder, # Invalid orderFilter parameter.
861
+ '170203': InvalidOrder, # Please enter the TP/SL price.
862
+ '170204': InvalidOrder, # trigger price cannot be higher than 110% price.
863
+ '170206': InvalidOrder, # trigger price cannot be lower than 90% of qty.
864
+ '170210': InvalidOrder, # New order rejected.
865
+ '170213': OrderNotFound, # Order does not exist.
866
+ '170217': InvalidOrder, # Only LIMIT-MAKER order is supported for the current pair.
867
+ '170218': InvalidOrder, # The LIMIT-MAKER order is rejected due to invalid price.
818
868
  '170221': BadRequest, # This coin does not exist.
819
869
  '170222': RateLimitExceeded, # Too many hasattr(self, requests) time frame.
820
870
  '170223': InsufficientFunds, # Your Spot Account with Institutional Lending triggers an alert or liquidation.
@@ -824,18 +874,7 @@ class bybit(Exchange, ImplicitAPI):
824
874
  '170228': InvalidOrder, # The purchase amount of each order exceeds the estimated maximum purchase amount.
825
875
  '170229': InvalidOrder, # The sell quantity per order exceeds the estimated maximum sell quantity.
826
876
  '170234': ExchangeError, # System Error
827
- '170210': InvalidOrder, # New order rejected.
828
- '170213': OrderNotFound, # Order does not exist.
829
- '170217': InvalidOrder, # Only LIMIT-MAKER order is supported for the current pair.
830
- '170218': InvalidOrder, # The LIMIT-MAKER order is rejected due to invalid price.
831
- '170010': InvalidOrder, # Purchase failed: Exceed the maximum position limit of leveraged tokens, the current available limit is %s USDT
832
- '170011': InvalidOrder, # "Purchase failed: Exceed the maximum position limit of innovation tokens,
833
- '170019': InvalidOrder, # the current available limit is replaceKey0 USDT"
834
- '170201': PermissionDenied, # Your account has been restricted for trades. If you have any questions, please email us at support@bybit.com
835
- '170202': InvalidOrder, # Invalid orderFilter parameter.
836
- '170203': InvalidOrder, # Please enter the TP/SL price.
837
- '170204': InvalidOrder, # trigger price cannot be higher than 110% price.
838
- '170206': InvalidOrder, # trigger price cannot be lower than 90% of qty.
877
+ '170241': ManualInteractionNeeded, # To proceed with trading, users must read through and confirm that they fully understand the project's risk disclosure document.
839
878
  '175000': InvalidOrder, # The serialNum is already in use.
840
879
  '175001': InvalidOrder, # Daily purchase limit has been exceeded. Please try again later.
841
880
  '175002': InvalidOrder, # There's a large number of purchase orders. Please try again later.
@@ -988,6 +1027,7 @@ class bybit(Exchange, ImplicitAPI):
988
1027
  '3200300': InsufficientFunds, # {"retCode":3200300,"retMsg":"Insufficient margin balance.","result":null,"retExtMap":{}}
989
1028
  },
990
1029
  'broad': {
1030
+ 'Not supported symbols': BadSymbol, # {"retCode":10001,"retMsg":"Not supported symbols","result":{},"retExtInfo":{},"time":1726147060461}
991
1031
  'Request timeout': RequestTimeout, # {"retCode":10016,"retMsg":"Request timeout, please try again later","result":{},"retExtInfo":{},"time":1675307914985}
992
1032
  'unknown orderInfo': OrderNotFound, # {"ret_code":-1,"ret_msg":"unknown orderInfo","ext_code":"","ext_info":"","result":null,"time_now":"1584030414.005545","rate_limit_status":99,"rate_limit_reset_ms":1584030414003,"rate_limit":100}
993
1033
  'invalid api_key': AuthenticationError, # {"ret_code":10003,"ret_msg":"invalid api_key","ext_code":"","ext_info":"","result":null,"time_now":"1599547085.415797"}
@@ -999,15 +1039,15 @@ class bybit(Exchange, ImplicitAPI):
999
1039
  },
1000
1040
  'precisionMode': TICK_SIZE,
1001
1041
  'options': {
1002
- 'sandboxMode': False,
1042
+ 'usePrivateInstrumentsInfo': False,
1003
1043
  'enableDemoTrading': False,
1004
- 'fetchMarkets': ['spot', 'linear', 'inverse', 'option'],
1005
- 'createOrder': {
1006
- 'method': 'privatePostV5OrderCreate', # 'privatePostV5PositionTradingStop'
1044
+ 'fetchMarkets': {
1045
+ 'types': ['spot', 'linear', 'inverse', 'option'],
1007
1046
  },
1008
1047
  'enableUnifiedMargin': None,
1009
1048
  'enableUnifiedAccount': None,
1010
- 'createMarketBuyOrderRequiresPrice': True, # only True for classic accounts
1049
+ 'unifiedMarginStatus': None,
1050
+ 'createMarketBuyOrderRequiresPrice': False, # only True for classic accounts
1011
1051
  'createUnifiedMarginAccount': False,
1012
1052
  'defaultType': 'swap', # 'swap', 'future', 'option', 'spot'
1013
1053
  'defaultSubType': 'linear', # 'linear', 'inverse'
@@ -1044,8 +1084,78 @@ class bybit(Exchange, ImplicitAPI):
1044
1084
  'ERC20': 'ETH',
1045
1085
  'TRC20': 'TRX',
1046
1086
  'BEP20': 'BSC',
1087
+ 'SOL': 'SOL',
1088
+ 'ACA': 'ACA',
1089
+ 'ADA': 'ADA',
1090
+ 'ALGO': 'ALGO',
1091
+ 'APT': 'APTOS',
1092
+ 'AR': 'AR',
1093
+ 'ARBONE': 'ARBI',
1094
+ 'AVAXC': 'CAVAX',
1095
+ 'AVAXX': 'XAVAX',
1096
+ 'ATOM': 'ATOM',
1097
+ 'BCH': 'BCH',
1098
+ 'BEP2': 'BNB',
1099
+ 'CHZ': 'CHZ',
1100
+ 'DCR': 'DCR',
1101
+ 'DGB': 'DGB',
1102
+ 'DOGE': 'DOGE',
1103
+ 'DOT': 'DOT',
1104
+ 'EGLD': 'EGLD',
1105
+ 'EOS': 'EOS',
1106
+ 'ETC': 'ETC',
1107
+ 'ETHF': 'ETHF',
1108
+ 'ETHW': 'ETHW',
1109
+ 'FIL': 'FIL',
1110
+ 'STEP': 'FITFI',
1111
+ 'FLOW': 'FLOW',
1112
+ 'FTM': 'FTM',
1113
+ 'GLMR': 'GLMR',
1114
+ 'HBAR': 'HBAR',
1115
+ 'HNT': 'HNT',
1116
+ 'ICP': 'ICP',
1117
+ 'ICX': 'ICX',
1118
+ 'KDA': 'KDA',
1119
+ 'KLAY': 'KLAY',
1120
+ 'KMA': 'KMA',
1121
+ 'KSM': 'KSM',
1122
+ 'LTC': 'LTC',
1123
+ # 'TERRA': 'LUNANEW',
1124
+ # 'TERRACLASSIC': 'LUNA',
1125
+ 'MATIC': 'MATIC',
1126
+ 'MINA': 'MINA',
1127
+ 'MOVR': 'MOVR',
1128
+ 'NEAR': 'NEAR',
1129
+ 'NEM': 'NEM',
1130
+ 'OASYS': 'OAS',
1131
+ 'OASIS': 'ROSE',
1047
1132
  'OMNI': 'OMNI',
1048
- 'SPL': 'SOL',
1133
+ 'ONE': 'ONE',
1134
+ 'OPTIMISM': 'OP',
1135
+ 'POKT': 'POKT',
1136
+ 'QTUM': 'QTUM',
1137
+ 'RVN': 'RVN',
1138
+ 'SC': 'SC',
1139
+ 'SCRT': 'SCRT',
1140
+ 'STX': 'STX',
1141
+ 'THETA': 'THETA',
1142
+ 'TON': 'TON',
1143
+ 'WAVES': 'WAVES',
1144
+ 'WAX': 'WAXP',
1145
+ 'XDC': 'XDC',
1146
+ 'XEC': 'XEC',
1147
+ 'XLM': 'XLM',
1148
+ 'XRP': 'XRP',
1149
+ 'XTZ': 'XTZ',
1150
+ 'XYM': 'XYM',
1151
+ 'ZEN': 'ZEN',
1152
+ 'ZIL': 'ZIL',
1153
+ 'ZKSYNC': 'ZKSYNC',
1154
+ # todo: uncomment after consensus
1155
+ # 'CADUCEUS': 'CMP',
1156
+ # 'KON': 'KON', # konpay, "konchain"
1157
+ # 'AURORA': 'AURORA',
1158
+ # 'BITCOINGOLD': 'BTG',
1049
1159
  },
1050
1160
  'networksById': {
1051
1161
  'ETH': 'ERC20',
@@ -1066,6 +1176,113 @@ class bybit(Exchange, ImplicitAPI):
1066
1176
  '4h': '4h',
1067
1177
  '1d': '1d',
1068
1178
  },
1179
+ 'useMarkPriceForPositionCollateral': False, # use mark price for position collateral
1180
+ },
1181
+ 'features': {
1182
+ 'default': {
1183
+ 'sandbox': True,
1184
+ 'createOrder': {
1185
+ 'marginMode': False,
1186
+ 'triggerPrice': True,
1187
+ 'triggerPriceType': {
1188
+ 'last': True,
1189
+ 'mark': True,
1190
+ 'index': True,
1191
+ },
1192
+ 'triggerDirection': True,
1193
+ 'stopLossPrice': True,
1194
+ 'takeProfitPrice': True,
1195
+ 'attachedStopLossTakeProfit': {
1196
+ 'triggerPriceType': {
1197
+ 'last': True,
1198
+ 'mark': True,
1199
+ 'index': True,
1200
+ },
1201
+ 'price': True,
1202
+ },
1203
+ 'timeInForce': {
1204
+ 'IOC': True,
1205
+ 'FOK': True,
1206
+ 'PO': True,
1207
+ 'GTD': False,
1208
+ },
1209
+ 'hedged': True,
1210
+ 'selfTradePrevention': True, # todo: implement
1211
+ 'trailing': True,
1212
+ 'iceberg': False,
1213
+ 'leverage': False,
1214
+ 'marketBuyRequiresPrice': False,
1215
+ 'marketBuyByCost': True,
1216
+ },
1217
+ 'createOrders': {
1218
+ 'max': 10,
1219
+ },
1220
+ 'fetchMyTrades': {
1221
+ 'marginMode': False,
1222
+ 'limit': 100,
1223
+ 'daysBack': 365 * 2, # 2 years
1224
+ 'untilDays': 7, # days between start-end
1225
+ 'symbolRequired': False,
1226
+ },
1227
+ 'fetchOrder': {
1228
+ 'marginMode': False,
1229
+ 'trigger': True,
1230
+ 'trailing': False,
1231
+ 'symbolRequired': True,
1232
+ },
1233
+ 'fetchOpenOrders': {
1234
+ 'marginMode': False,
1235
+ 'limit': 50,
1236
+ 'trigger': True,
1237
+ 'trailing': False,
1238
+ 'symbolRequired': False,
1239
+ },
1240
+ 'fetchOrders': None,
1241
+ 'fetchClosedOrders': {
1242
+ 'marginMode': False,
1243
+ 'limit': 50,
1244
+ 'daysBack': 365 * 2, # 2 years
1245
+ 'daysBackCanceled': 1,
1246
+ 'untilDays': 7,
1247
+ 'trigger': True,
1248
+ 'trailing': False,
1249
+ 'symbolRequired': False,
1250
+ },
1251
+ 'fetchOHLCV': {
1252
+ 'limit': 1000,
1253
+ },
1254
+ 'editOrders': {
1255
+ 'max': 10,
1256
+ },
1257
+ },
1258
+ 'spot': {
1259
+ 'extends': 'default',
1260
+ 'createOrder': {
1261
+ 'triggerPriceType': None,
1262
+ 'triggerDirection': False,
1263
+ 'attachedStopLossTakeProfit': {
1264
+ 'triggerPriceType': None,
1265
+ 'price': True,
1266
+ },
1267
+ 'marketBuyRequiresPrice': True,
1268
+ },
1269
+ },
1270
+ 'swap': {
1271
+ 'linear': {
1272
+ 'extends': 'default',
1273
+ },
1274
+ 'inverse': {
1275
+ 'extends': 'default',
1276
+ },
1277
+ },
1278
+ 'future': {
1279
+ 'linear': {
1280
+ 'extends': 'default',
1281
+ },
1282
+ 'inverse': {
1283
+ 'extends': 'default',
1284
+ },
1285
+ },
1069
1286
  },
1070
1287
  'fees': {
1071
1288
  'trading': {
@@ -1084,21 +1301,13 @@ class bybit(Exchange, ImplicitAPI):
1084
1301
  },
1085
1302
  })
1086
1303
 
1087
- def set_sandbox_mode(self, enable: bool):
1088
- """
1089
- enables or disables sandbox mode
1090
- :param boolean [enable]: True if demo trading should be enabled, False otherwise
1091
- """
1092
- super(bybit, self).set_sandbox_mode(enable)
1093
- self.options['sandboxMode'] = enable
1094
-
1095
1304
  def enable_demo_trading(self, enable: bool):
1096
1305
  """
1097
1306
  enables or disables demo trading mode
1098
- :see: https://bybit-exchange.github.io/docs/v5/demo
1307
+ https://bybit-exchange.github.io/docs/v5/demo
1099
1308
  :param boolean [enable]: True if demo trading should be enabled, False otherwise
1100
1309
  """
1101
- if self.options['sandboxMode']:
1310
+ if self.isSandboxModeEnabled:
1102
1311
  raise NotSupported(self.id + ' demo trading does not support in sandbox environment')
1103
1312
  # enable demo trading in bybit, see: https://bybit-exchange.github.io/docs/v5/demo
1104
1313
  if enable:
@@ -1115,7 +1324,7 @@ class bybit(Exchange, ImplicitAPI):
1115
1324
 
1116
1325
  def add_pagination_cursor_to_result(self, response):
1117
1326
  result = self.safe_dict(response, 'result', {})
1118
- data = self.safe_value_n(result, ['list', 'rows', 'data', 'dataList'], [])
1327
+ data = self.safe_list_n(result, ['list', 'rows', 'data', 'dataList'], [])
1119
1328
  paginationCursor = self.safe_string_2(result, 'nextPageCursor', 'cursor')
1120
1329
  dataLength = len(data)
1121
1330
  if (paginationCursor is not None) and (dataLength > 0):
@@ -1126,9 +1335,15 @@ class bybit(Exchange, ImplicitAPI):
1126
1335
 
1127
1336
  async def is_unified_enabled(self, params={}):
1128
1337
  """
1338
+
1339
+ https://bybit-exchange.github.io/docs/v5/user/apikey-info#http-request
1340
+ https://bybit-exchange.github.io/docs/v5/account/account-info
1341
+
1129
1342
  returns [enableUnifiedMargin, enableUnifiedAccount] so the user can check if unified account is enabled
1343
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1344
+ :returns any: [enableUnifiedMargin, enableUnifiedAccount]
1130
1345
  """
1131
- # The API key of user id must own one of permissions will be allowed to call following API endpoints.
1346
+ # The API key of user id must own one of permissions will be allowed to call following API endpoints:
1132
1347
  # SUB UID: "Account Transfer"
1133
1348
  # MASTER UID: "Account Transfer", "Subaccount Transfer", "Withdrawal"
1134
1349
  enableUnifiedMargin = self.safe_bool(self.options, 'enableUnifiedMargin')
@@ -1139,8 +1354,12 @@ class bybit(Exchange, ImplicitAPI):
1139
1354
  # so we're assuming UTA is enabled
1140
1355
  self.options['enableUnifiedMargin'] = False
1141
1356
  self.options['enableUnifiedAccount'] = True
1357
+ self.options['unifiedMarginStatus'] = 6
1142
1358
  return [self.options['enableUnifiedMargin'], self.options['enableUnifiedAccount']]
1143
- response = await self.privateGetV5UserQueryApi(params)
1359
+ rawPromises = [self.privateGetV5UserQueryApi(params), self.privateGetV5AccountInfo(params)]
1360
+ promises = await asyncio.gather(*rawPromises)
1361
+ response = promises[0]
1362
+ accountInfo = promises[1]
1144
1363
  #
1145
1364
  # {
1146
1365
  # "retCode": 0,
@@ -1180,19 +1399,44 @@ class bybit(Exchange, ImplicitAPI):
1180
1399
  # "retExtInfo": {},
1181
1400
  # "time": 1676891757649
1182
1401
  # }
1402
+ # account info
1403
+ # {
1404
+ # "retCode": 0,
1405
+ # "retMsg": "OK",
1406
+ # "result": {
1407
+ # "marginMode": "REGULAR_MARGIN",
1408
+ # "updatedTime": "1697078946000",
1409
+ # "unifiedMarginStatus": 4,
1410
+ # "dcpStatus": "OFF",
1411
+ # "timeWindow": 10,
1412
+ # "smpGroup": 0,
1413
+ # "isMasterTrader": False,
1414
+ # "spotHedgingStatus": "OFF"
1415
+ # }
1416
+ # }
1183
1417
  #
1184
1418
  result = self.safe_dict(response, 'result', {})
1419
+ accountResult = self.safe_dict(accountInfo, 'result', {})
1185
1420
  self.options['enableUnifiedMargin'] = self.safe_integer(result, 'unified') == 1
1186
1421
  self.options['enableUnifiedAccount'] = self.safe_integer(result, 'uta') == 1
1422
+ self.options['unifiedMarginStatus'] = self.safe_integer(accountResult, 'unifiedMarginStatus', 6) # default to uta 2.0 pro if not found
1187
1423
  return [self.options['enableUnifiedMargin'], self.options['enableUnifiedAccount']]
1188
1424
 
1189
1425
  async def upgrade_unified_trade_account(self, params={}):
1426
+ """
1427
+ upgrades the account to unified trade account *warning* self is irreversible
1428
+
1429
+ https://bybit-exchange.github.io/docs/v5/account/upgrade-unified-account
1430
+
1431
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1432
+ :returns any: nothing
1433
+ """
1190
1434
  return await self.privatePostV5AccountUpgradeToUta(params)
1191
1435
 
1192
1436
  def create_expired_option_market(self, symbol: str):
1193
1437
  # support expired option contracts
1194
- quote = 'USD'
1195
- settle = 'USDC'
1438
+ quote = None
1439
+ settle = None
1196
1440
  optionParts = symbol.split('-')
1197
1441
  symbolBase = symbol.split('/')
1198
1442
  base = None
@@ -1200,13 +1444,36 @@ class bybit(Exchange, ImplicitAPI):
1200
1444
  if symbol.find('/') > -1:
1201
1445
  base = self.safe_string(symbolBase, 0)
1202
1446
  expiry = self.safe_string(optionParts, 1)
1447
+ symbolQuoteAndSettle = self.safe_string(symbolBase, 1)
1448
+ splitQuote = symbolQuoteAndSettle.split(':')
1449
+ quoteAndSettle = self.safe_string(splitQuote, 0)
1450
+ quote = quoteAndSettle
1451
+ settle = quoteAndSettle
1203
1452
  else:
1204
1453
  base = self.safe_string(optionParts, 0)
1205
1454
  expiry = self.convert_market_id_expire_date(self.safe_string(optionParts, 1))
1455
+ if symbol.endswith('-USDT'):
1456
+ quote = 'USDT'
1457
+ settle = 'USDT'
1458
+ else:
1459
+ quote = 'USDC'
1460
+ settle = 'USDC'
1206
1461
  strike = self.safe_string(optionParts, 2)
1207
1462
  optionType = self.safe_string(optionParts, 3)
1208
1463
  datetime = self.convert_expire_date(expiry)
1209
1464
  timestamp = self.parse8601(datetime)
1465
+ amountPrecision = None
1466
+ pricePrecision = None
1467
+ # hard coded amount and price precisions from fetchOptionMarkets
1468
+ if base == 'BTC':
1469
+ amountPrecision = self.parse_number('0.01')
1470
+ pricePrecision = self.parse_number('5')
1471
+ elif base == 'ETH':
1472
+ amountPrecision = self.parse_number('0.1')
1473
+ pricePrecision = self.parse_number('0.1')
1474
+ elif base == 'SOL':
1475
+ amountPrecision = self.parse_number('1')
1476
+ pricePrecision = self.parse_number('0.01')
1210
1477
  return {
1211
1478
  'id': base + '-' + self.convert_expire_date_to_market_id_date(expiry) + '-' + strike + '-' + optionType,
1212
1479
  'symbol': base + '/' + quote + ':' + settle + '-' + expiry + '-' + strike + '-' + optionType,
@@ -1226,14 +1493,14 @@ class bybit(Exchange, ImplicitAPI):
1226
1493
  'option': True,
1227
1494
  'margin': False,
1228
1495
  'contract': True,
1229
- 'contractSize': None,
1496
+ 'contractSize': self.parse_number('1'),
1230
1497
  'expiry': timestamp,
1231
1498
  'expiryDatetime': datetime,
1232
1499
  'optionType': 'call' if (optionType == 'C') else 'put',
1233
1500
  'strike': self.parse_number(strike),
1234
1501
  'precision': {
1235
- 'amount': None,
1236
- 'price': None,
1502
+ 'amount': amountPrecision,
1503
+ 'price': pricePrecision,
1237
1504
  },
1238
1505
  'limits': {
1239
1506
  'amount': {
@@ -1268,10 +1535,38 @@ class bybit(Exchange, ImplicitAPI):
1268
1535
  return [type, params]
1269
1536
  return [subType, params]
1270
1537
 
1271
- async def fetch_time(self, params={}):
1538
+ def get_amount(self, symbol: str, amount: float):
1539
+ # some markets like options might not have the precision available
1540
+ # and we shouldn't crash in those cases
1541
+ market = self.market(symbol)
1542
+ emptyPrecisionAmount = (market['precision']['amount'] is None)
1543
+ amountString = self.number_to_string(amount)
1544
+ if not emptyPrecisionAmount and (amountString != '0'):
1545
+ return self.amount_to_precision(symbol, amount)
1546
+ return amountString
1547
+
1548
+ def get_price(self, symbol: str, price: str):
1549
+ if price is None:
1550
+ return price
1551
+ market = self.market(symbol)
1552
+ emptyPrecisionPrice = (market['precision']['price'] is None)
1553
+ if not emptyPrecisionPrice:
1554
+ return self.price_to_precision(symbol, price)
1555
+ return price
1556
+
1557
+ def get_cost(self, symbol: str, cost: str):
1558
+ market = self.market(symbol)
1559
+ emptyPrecisionPrice = (market['precision']['price'] is None)
1560
+ if not emptyPrecisionPrice:
1561
+ return self.cost_to_precision(symbol, cost)
1562
+ return cost
1563
+
1564
+ async def fetch_time(self, params={}) -> Int:
1272
1565
  """
1273
1566
  fetches the current integer timestamp in milliseconds from the exchange server
1274
- :see: https://bybit-exchange.github.io/docs/v5/market/time
1567
+
1568
+ https://bybit-exchange.github.io/docs/v5/market/time
1569
+
1275
1570
  :param dict [params]: extra parameters specific to the exchange API endpoint
1276
1571
  :returns int: the current integer timestamp in milliseconds from the exchange server
1277
1572
  """
@@ -1293,7 +1588,9 @@ class bybit(Exchange, ImplicitAPI):
1293
1588
  async def fetch_currencies(self, params={}) -> Currencies:
1294
1589
  """
1295
1590
  fetches all available currencies on an exchange
1296
- :see: https://bybit-exchange.github.io/docs/v5/asset/coin-info
1591
+
1592
+ https://bybit-exchange.github.io/docs/v5/asset/coin-info
1593
+
1297
1594
  :param dict [params]: extra parameters specific to the exchange API endpoint
1298
1595
  :returns dict: an associative dictionary of currencies
1299
1596
  """
@@ -1342,92 +1639,81 @@ class bybit(Exchange, ImplicitAPI):
1342
1639
  name = self.safe_string(currency, 'name')
1343
1640
  chains = self.safe_list(currency, 'chains', [])
1344
1641
  networks: dict = {}
1345
- minPrecision = None
1346
- minWithdrawFeeString = None
1347
- minWithdrawString = None
1348
- minDepositString = None
1349
- deposit = False
1350
- withdraw = False
1351
1642
  for j in range(0, len(chains)):
1352
1643
  chain = chains[j]
1353
1644
  networkId = self.safe_string(chain, 'chain')
1354
1645
  networkCode = self.network_id_to_code(networkId)
1355
- precision = self.parse_number(self.parse_precision(self.safe_string(chain, 'minAccuracy')))
1356
- minPrecision = precision if (minPrecision is None) else min(minPrecision, precision)
1357
- depositAllowed = self.safe_integer(chain, 'chainDeposit') == 1
1358
- deposit = depositAllowed if (depositAllowed) else deposit
1359
- withdrawAllowed = self.safe_integer(chain, 'chainWithdraw') == 1
1360
- withdraw = withdrawAllowed if (withdrawAllowed) else withdraw
1361
- withdrawFeeString = self.safe_string(chain, 'withdrawFee')
1362
- if withdrawFeeString is not None:
1363
- minWithdrawFeeString = withdrawFeeString if (minWithdrawFeeString is None) else Precise.string_min(withdrawFeeString, minWithdrawFeeString)
1364
- minNetworkWithdrawString = self.safe_string(chain, 'withdrawMin')
1365
- if minNetworkWithdrawString is not None:
1366
- minWithdrawString = minNetworkWithdrawString if (minWithdrawString is None) else Precise.string_min(minNetworkWithdrawString, minWithdrawString)
1367
- minNetworkDepositString = self.safe_string(chain, 'depositMin')
1368
- if minNetworkDepositString is not None:
1369
- minDepositString = minNetworkDepositString if (minDepositString is None) else Precise.string_min(minNetworkDepositString, minDepositString)
1370
1646
  networks[networkCode] = {
1371
1647
  'info': chain,
1372
1648
  'id': networkId,
1373
1649
  'network': networkCode,
1374
- 'active': depositAllowed and withdrawAllowed,
1375
- 'deposit': depositAllowed,
1376
- 'withdraw': withdrawAllowed,
1377
- 'fee': self.parse_number(withdrawFeeString),
1378
- 'precision': precision,
1650
+ 'active': None,
1651
+ 'deposit': self.safe_integer(chain, 'chainDeposit') == 1,
1652
+ 'withdraw': self.safe_integer(chain, 'chainWithdraw') == 1,
1653
+ 'fee': self.safe_number(chain, 'withdrawFee'),
1654
+ 'precision': self.parse_number(self.parse_precision(self.safe_string(chain, 'minAccuracy'))),
1379
1655
  'limits': {
1380
1656
  'withdraw': {
1381
- 'min': self.parse_number(minNetworkWithdrawString),
1657
+ 'min': self.safe_number(chain, 'withdrawMin'),
1382
1658
  'max': None,
1383
1659
  },
1384
1660
  'deposit': {
1385
- 'min': self.parse_number(minNetworkDepositString),
1661
+ 'min': self.safe_number(chain, 'depositMin'),
1386
1662
  'max': None,
1387
1663
  },
1388
1664
  },
1389
1665
  }
1390
- result[code] = {
1666
+ result[code] = self.safe_currency_structure({
1391
1667
  'info': currency,
1392
1668
  'code': code,
1393
1669
  'id': currencyId,
1394
1670
  'name': name,
1395
- 'active': deposit and withdraw,
1396
- 'deposit': deposit,
1397
- 'withdraw': withdraw,
1398
- 'fee': self.parse_number(minWithdrawFeeString),
1399
- 'precision': minPrecision,
1671
+ 'active': None,
1672
+ 'deposit': None,
1673
+ 'withdraw': None,
1674
+ 'fee': None,
1675
+ 'precision': None,
1400
1676
  'limits': {
1401
1677
  'amount': {
1402
1678
  'min': None,
1403
1679
  'max': None,
1404
1680
  },
1405
1681
  'withdraw': {
1406
- 'min': self.parse_number(minWithdrawString),
1682
+ 'min': None,
1407
1683
  'max': None,
1408
1684
  },
1409
1685
  'deposit': {
1410
- 'min': self.parse_number(minDepositString),
1686
+ 'min': None,
1411
1687
  'max': None,
1412
1688
  },
1413
1689
  },
1414
1690
  'networks': networks,
1415
- }
1691
+ 'type': 'crypto', # atm exchange api provides only cryptos
1692
+ })
1416
1693
  return result
1417
1694
 
1418
1695
  async def fetch_markets(self, params={}) -> List[Market]:
1419
1696
  """
1420
1697
  retrieves data on all markets for bybit
1421
- :see: https://bybit-exchange.github.io/docs/v5/market/instrument
1698
+
1699
+ https://bybit-exchange.github.io/docs/v5/market/instrument
1700
+
1422
1701
  :param dict [params]: extra parameters specific to the exchange API endpoint
1423
1702
  :returns dict[]: an array of objects representing market data
1424
1703
  """
1425
1704
  if self.options['adjustForTimeDifference']:
1426
1705
  await self.load_time_difference()
1427
1706
  promisesUnresolved = []
1428
- fetchMarkets = self.safe_list(self.options, 'fetchMarkets', ['spot', 'linear', 'inverse'])
1429
- for i in range(0, len(fetchMarkets)):
1430
- marketType = fetchMarkets[i]
1707
+ types = None
1708
+ defaultTypes = ['spot', 'linear', 'inverse', 'option']
1709
+ fetchMarketsOptions = self.safe_dict(self.options, 'fetchMarkets')
1710
+ if fetchMarketsOptions is not None:
1711
+ types = self.safe_list(fetchMarketsOptions, 'types', defaultTypes)
1712
+ else:
1713
+ # for backward-compatibility
1714
+ types = self.safe_list(self.options, 'fetchMarkets', defaultTypes)
1715
+ for i in range(0, len(types)):
1716
+ marketType = types[i]
1431
1717
  if marketType == 'spot':
1432
1718
  promisesUnresolved.append(self.fetch_spot_markets(params))
1433
1719
  elif marketType == 'linear':
@@ -1453,11 +1739,16 @@ class bybit(Exchange, ImplicitAPI):
1453
1739
  derivativeMarkets = self.array_concat(futureMarkets, optionMarkets)
1454
1740
  return self.array_concat(spotMarkets, derivativeMarkets)
1455
1741
 
1456
- async def fetch_spot_markets(self, params):
1742
+ async def fetch_spot_markets(self, params) -> List[Market]:
1457
1743
  request: dict = {
1458
1744
  'category': 'spot',
1459
1745
  }
1460
- response = await self.publicGetV5MarketInstrumentsInfo(self.extend(request, params))
1746
+ usePrivateInstrumentsInfo = self.safe_bool(self.options, 'usePrivateInstrumentsInfo', False)
1747
+ response: dict = None
1748
+ if usePrivateInstrumentsInfo:
1749
+ response = await self.privateGetV5MarketInstrumentsInfo(self.extend(request, params))
1750
+ else:
1751
+ response = await self.publicGetV5MarketInstrumentsInfo(self.extend(request, params))
1461
1752
  #
1462
1753
  # {
1463
1754
  # "retCode": 0,
@@ -1563,17 +1854,33 @@ class bybit(Exchange, ImplicitAPI):
1563
1854
  }))
1564
1855
  return result
1565
1856
 
1566
- async def fetch_future_markets(self, params):
1857
+ async def fetch_future_markets(self, params) -> List[Market]:
1567
1858
  params = self.extend(params)
1568
1859
  params['limit'] = 1000 # minimize number of requests
1569
- response = await self.publicGetV5MarketInstrumentsInfo(params)
1860
+ preLaunchMarkets = []
1861
+ usePrivateInstrumentsInfo = self.safe_bool(self.options, 'usePrivateInstrumentsInfo', False)
1862
+ response: dict = None
1863
+ if usePrivateInstrumentsInfo:
1864
+ response = await self.privateGetV5MarketInstrumentsInfo(params)
1865
+ else:
1866
+ linearPromises = [
1867
+ self.publicGetV5MarketInstrumentsInfo(params),
1868
+ self.publicGetV5MarketInstrumentsInfo(self.extend(params, {'status': 'PreLaunch'})),
1869
+ ]
1870
+ promises = await asyncio.gather(*linearPromises)
1871
+ response = self.safe_dict(promises, 0, {})
1872
+ preLaunchMarkets = self.safe_dict(promises, 1, {})
1570
1873
  data = self.safe_dict(response, 'result', {})
1571
1874
  markets = self.safe_list(data, 'list', [])
1572
1875
  paginationCursor = self.safe_string(data, 'nextPageCursor')
1573
1876
  if paginationCursor is not None:
1574
1877
  while(paginationCursor is not None):
1575
1878
  params['cursor'] = paginationCursor
1576
- responseInner = await self.publicGetV5MarketInstrumentsInfo(params)
1879
+ responseInner: dict = None
1880
+ if usePrivateInstrumentsInfo:
1881
+ responseInner = await self.privateGetV5MarketInstrumentsInfo(params)
1882
+ else:
1883
+ responseInner = await self.publicGetV5MarketInstrumentsInfo(params)
1577
1884
  dataNew = self.safe_dict(responseInner, 'result', {})
1578
1885
  rawMarkets = self.safe_list(dataNew, 'list', [])
1579
1886
  rawMarketsLength = len(rawMarkets)
@@ -1625,6 +1932,9 @@ class bybit(Exchange, ImplicitAPI):
1625
1932
  # "time": 1672712495660
1626
1933
  # }
1627
1934
  #
1935
+ preLaunchData = self.safe_dict(preLaunchMarkets, 'result', {})
1936
+ preLaunchMarketsList = self.safe_list(preLaunchData, 'list', [])
1937
+ markets = self.array_concat(markets, preLaunchMarketsList)
1628
1938
  result = []
1629
1939
  category = self.safe_string(data, 'category')
1630
1940
  for i in range(0, len(markets)):
@@ -1726,11 +2036,16 @@ class bybit(Exchange, ImplicitAPI):
1726
2036
  }))
1727
2037
  return result
1728
2038
 
1729
- async def fetch_option_markets(self, params):
2039
+ async def fetch_option_markets(self, params) -> List[Market]:
1730
2040
  request: dict = {
1731
2041
  'category': 'option',
1732
2042
  }
1733
- response = await self.publicGetV5MarketInstrumentsInfo(self.extend(request, params))
2043
+ usePrivateInstrumentsInfo = self.safe_bool(self.options, 'usePrivateInstrumentsInfo', False)
2044
+ response: dict = None
2045
+ if usePrivateInstrumentsInfo:
2046
+ response = await self.privateGetV5MarketInstrumentsInfo(self.extend(request, params))
2047
+ else:
2048
+ response = await self.publicGetV5MarketInstrumentsInfo(self.extend(request, params))
1734
2049
  data = self.safe_dict(response, 'result', {})
1735
2050
  markets = self.safe_list(data, 'list', [])
1736
2051
  if self.options['loadAllOptions']:
@@ -1739,7 +2054,11 @@ class bybit(Exchange, ImplicitAPI):
1739
2054
  if paginationCursor is not None:
1740
2055
  while(paginationCursor is not None):
1741
2056
  request['cursor'] = paginationCursor
1742
- responseInner = await self.publicGetV5MarketInstrumentsInfo(self.extend(request, params))
2057
+ responseInner: dict = None
2058
+ if usePrivateInstrumentsInfo:
2059
+ responseInner = await self.privateGetV5MarketInstrumentsInfo(self.extend(request, params))
2060
+ else:
2061
+ responseInner = await self.publicGetV5MarketInstrumentsInfo(self.extend(request, params))
1743
2062
  dataNew = self.safe_dict(responseInner, 'result', {})
1744
2063
  rawMarkets = self.safe_list(dataNew, 'list', [])
1745
2064
  rawMarketsLength = len(rawMarkets)
@@ -1800,6 +2119,7 @@ class bybit(Exchange, ImplicitAPI):
1800
2119
  strike = self.safe_string(splitId, 2)
1801
2120
  optionLetter = self.safe_string(splitId, 3)
1802
2121
  isActive = (status == 'Trading')
2122
+ isInverse = base == settle
1803
2123
  if isActive or (self.options['loadAllOptions']) or (self.options['loadExpiredOptions']):
1804
2124
  result.append(self.safe_market_structure({
1805
2125
  'id': id,
@@ -1811,6 +2131,7 @@ class bybit(Exchange, ImplicitAPI):
1811
2131
  'quoteId': quoteId,
1812
2132
  'settleId': settleId,
1813
2133
  'type': 'option',
2134
+ 'subType': None,
1814
2135
  'spot': False,
1815
2136
  'margin': False,
1816
2137
  'swap': False,
@@ -1818,11 +2139,11 @@ class bybit(Exchange, ImplicitAPI):
1818
2139
  'option': True,
1819
2140
  'active': isActive,
1820
2141
  'contract': True,
1821
- 'linear': None,
1822
- 'inverse': None,
2142
+ 'linear': not isInverse,
2143
+ 'inverse': isInverse,
1823
2144
  'taker': self.safe_number(market, 'takerFee', self.parse_number('0.0006')),
1824
2145
  'maker': self.safe_number(market, 'makerFee', self.parse_number('0.0001')),
1825
- 'contractSize': self.safe_number(lotSizeFilter, 'minOrderQty'),
2146
+ 'contractSize': self.parse_number('1'),
1826
2147
  'expiry': expiry,
1827
2148
  'expiryDatetime': self.iso8601(expiry),
1828
2149
  'strike': self.parse_number(strike),
@@ -1968,13 +2289,17 @@ class bybit(Exchange, ImplicitAPI):
1968
2289
  'average': None,
1969
2290
  'baseVolume': baseVolume,
1970
2291
  'quoteVolume': quoteVolume,
2292
+ 'markPrice': self.safe_string(ticker, 'markPrice'),
2293
+ 'indexPrice': self.safe_string(ticker, 'indexPrice'),
1971
2294
  'info': ticker,
1972
2295
  }, market)
1973
2296
 
1974
2297
  async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
1975
2298
  """
1976
2299
  fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
1977
- :see: https://bybit-exchange.github.io/docs/v5/market/tickers
2300
+
2301
+ https://bybit-exchange.github.io/docs/v5/market/tickers
2302
+
1978
2303
  :param str symbol: unified symbol of the market to fetch the ticker for
1979
2304
  :param dict [params]: extra parameters specific to the exchange API endpoint
1980
2305
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
@@ -1988,15 +2313,9 @@ class bybit(Exchange, ImplicitAPI):
1988
2313
  # 'baseCoin': '', Base coin. For option only
1989
2314
  # 'expDate': '', Expiry date. e.g., 25DEC22. For option only
1990
2315
  }
1991
- if market['spot']:
1992
- request['category'] = 'spot'
1993
- else:
1994
- if market['option']:
1995
- request['category'] = 'option'
1996
- elif market['linear']:
1997
- request['category'] = 'linear'
1998
- elif market['inverse']:
1999
- request['category'] = 'inverse'
2316
+ category = None
2317
+ category, params = self.get_bybit_type('fetchTicker', market, params)
2318
+ request['category'] = category
2000
2319
  response = await self.publicGetV5MarketTickers(self.extend(request, params))
2001
2320
  #
2002
2321
  # {
@@ -2044,13 +2363,17 @@ class bybit(Exchange, ImplicitAPI):
2044
2363
  async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
2045
2364
  """
2046
2365
  fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
2047
- :see: https://bybit-exchange.github.io/docs/v5/market/tickers
2366
+
2367
+ https://bybit-exchange.github.io/docs/v5/market/tickers
2368
+
2048
2369
  :param str[] symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
2049
2370
  :param dict [params]: extra parameters specific to the exchange API endpoint
2050
2371
  :param str [params.subType]: *contract only* 'linear', 'inverse'
2372
+ :param str [params.baseCoin]: *option only* base coin, default is 'BTC'
2051
2373
  :returns dict: an array of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
2052
2374
  """
2053
2375
  await self.load_markets()
2376
+ code = self.safe_string_n(params, ['code', 'currency', 'baseCoin'])
2054
2377
  market = None
2055
2378
  parsedSymbols = None
2056
2379
  if symbols is not None:
@@ -2072,27 +2395,26 @@ class bybit(Exchange, ImplicitAPI):
2072
2395
  currentType = market['type']
2073
2396
  elif market['type'] != currentType:
2074
2397
  raise BadRequest(self.id + ' fetchTickers can only accept a list of symbols of the same type')
2398
+ if market['option']:
2399
+ if code is not None and code != market['base']:
2400
+ raise BadRequest(self.id + ' fetchTickers the base currency must be the same for all symbols, self endpoint only supports one base currency at a time. Read more about it here: https://bybit-exchange.github.io/docs/v5/market/tickers')
2401
+ if code is None:
2402
+ code = market['base']
2403
+ params = self.omit(params, ['code', 'currency'])
2075
2404
  parsedSymbols.append(market['symbol'])
2076
2405
  request: dict = {
2077
2406
  # 'symbol': market['id'],
2078
2407
  # 'baseCoin': '', # Base coin. For option only
2079
2408
  # 'expDate': '', # Expiry date. e.g., 25DEC22. For option only
2080
2409
  }
2081
- type = None
2082
- type, params = self.handle_market_type_and_params('fetchTickers', market, params)
2083
- # Calls like `.fetchTickers(None, {subType:'inverse'})` should be supported for self exchange, so
2084
- # as "options.defaultSubType" is also set in exchange options, we should consider `params.subType`
2085
- # with higher priority and only default to spot, if `subType` is not set in params
2086
- passedSubType = self.safe_string(params, 'subType')
2087
- subType = None
2088
- subType, params = self.handle_sub_type_and_params('fetchTickers', market, params, 'linear')
2089
- # only if passedSubType is None, then use spot
2090
- if type == 'spot' and passedSubType is None:
2091
- request['category'] = 'spot'
2092
- elif type == 'swap' or type == 'future' or subType is not None:
2093
- request['category'] = subType
2094
- elif type == 'option':
2410
+ category = None
2411
+ category, params = self.get_bybit_type('fetchTickers', market, params)
2412
+ request['category'] = category
2413
+ if category == 'option':
2095
2414
  request['category'] = 'option'
2415
+ if code is None:
2416
+ code = 'BTC'
2417
+ request['baseCoin'] = code
2096
2418
  response = await self.publicGetV5MarketTickers(self.extend(request, params))
2097
2419
  #
2098
2420
  # {
@@ -2136,6 +2458,20 @@ class bybit(Exchange, ImplicitAPI):
2136
2458
  tickerList = self.safe_list(result, 'list', [])
2137
2459
  return self.parse_tickers(tickerList, parsedSymbols)
2138
2460
 
2461
+ async def fetch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
2462
+ """
2463
+ fetches the bid and ask price and volume for multiple markets
2464
+
2465
+ https://bybit-exchange.github.io/docs/v5/market/tickers
2466
+
2467
+ :param str[]|None symbols: unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
2468
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2469
+ :param str [params.subType]: *contract only* 'linear', 'inverse'
2470
+ :param str [params.baseCoin]: *option only* base coin, default is 'BTC'
2471
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
2472
+ """
2473
+ return await self.fetch_tickers(symbols, params)
2474
+
2139
2475
  def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
2140
2476
  #
2141
2477
  # [
@@ -2161,10 +2497,12 @@ class bybit(Exchange, ImplicitAPI):
2161
2497
  async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
2162
2498
  """
2163
2499
  fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
2164
- :see: https://bybit-exchange.github.io/docs/v5/market/kline
2165
- :see: https://bybit-exchange.github.io/docs/v5/market/mark-kline
2166
- :see: https://bybit-exchange.github.io/docs/v5/market/index-kline
2167
- :see: https://bybit-exchange.github.io/docs/v5/market/preimum-index-kline
2500
+
2501
+ https://bybit-exchange.github.io/docs/v5/market/kline
2502
+ https://bybit-exchange.github.io/docs/v5/market/mark-kline
2503
+ https://bybit-exchange.github.io/docs/v5/market/index-kline
2504
+ https://bybit-exchange.github.io/docs/v5/market/preimum-index-kline
2505
+
2168
2506
  :param str symbol: unified symbol of the market to fetch OHLCV data for
2169
2507
  :param str timeframe: the length of time each candle represents
2170
2508
  :param int [since]: timestamp in ms of the earliest candle to fetch
@@ -2259,7 +2597,8 @@ class bybit(Exchange, ImplicitAPI):
2259
2597
  ohlcvs = self.safe_list(result, 'list', [])
2260
2598
  return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit)
2261
2599
 
2262
- def parse_funding_rate(self, ticker, market: Market = None):
2600
+ def parse_funding_rate(self, ticker, market: Market = None) -> FundingRate:
2601
+ #
2263
2602
  # {
2264
2603
  # "symbol": "BTCUSDT",
2265
2604
  # "bidPrice": "19255",
@@ -2292,6 +2631,12 @@ class bybit(Exchange, ImplicitAPI):
2292
2631
  fundingTimestamp = self.safe_integer(ticker, 'nextFundingTime')
2293
2632
  markPrice = self.safe_number(ticker, 'markPrice')
2294
2633
  indexPrice = self.safe_number(ticker, 'indexPrice')
2634
+ info = self.safe_dict(self.safe_market(marketId, market, None, 'swap'), 'info')
2635
+ fundingInterval = self.safe_integer(info, 'fundingInterval')
2636
+ intervalString = None
2637
+ if fundingInterval is not None:
2638
+ interval = self.parse_to_int(fundingInterval / 60)
2639
+ intervalString = str(interval) + 'h'
2295
2640
  return {
2296
2641
  'info': ticker,
2297
2642
  'symbol': symbol,
@@ -2310,15 +2655,18 @@ class bybit(Exchange, ImplicitAPI):
2310
2655
  'previousFundingRate': None,
2311
2656
  'previousFundingTimestamp': None,
2312
2657
  'previousFundingDatetime': None,
2658
+ 'interval': intervalString,
2313
2659
  }
2314
2660
 
2315
- async def fetch_funding_rates(self, symbols: Strings = None, params={}):
2661
+ async def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
2316
2662
  """
2317
2663
  fetches funding rates for multiple markets
2318
- :see: https://bybit-exchange.github.io/docs/v5/market/tickers
2664
+
2665
+ https://bybit-exchange.github.io/docs/v5/market/tickers
2666
+
2319
2667
  :param str[] symbols: unified symbols of the markets to fetch the funding rates for, all market funding rates are returned if not assigned
2320
2668
  :param dict [params]: extra parameters specific to the exchange API endpoint
2321
- :returns dict: an array of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-structure>`
2669
+ :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-structure>`
2322
2670
  """
2323
2671
  await self.load_markets()
2324
2672
  market = None
@@ -2374,22 +2722,19 @@ class bybit(Exchange, ImplicitAPI):
2374
2722
  # "time": 1663670053454
2375
2723
  # }
2376
2724
  #
2377
- tickerList = self.safe_value(response, 'result', [])
2725
+ data = self.safe_dict(response, 'result', {})
2726
+ tickerList = self.safe_list(data, 'list', [])
2378
2727
  timestamp = self.safe_integer(response, 'time')
2379
- tickerList = self.safe_value(tickerList, 'list')
2380
- fundingRates: dict = {}
2381
2728
  for i in range(0, len(tickerList)):
2382
- rawTicker = tickerList[i]
2383
- rawTicker['timestamp'] = timestamp # will be removed inside the parser
2384
- ticker = self.parse_funding_rate(tickerList[i], None)
2385
- symbol = ticker['symbol']
2386
- fundingRates[symbol] = ticker
2387
- return self.filter_by_array(fundingRates, 'symbol', symbols)
2729
+ tickerList[i]['timestamp'] = timestamp # will be removed inside the parser
2730
+ return self.parse_funding_rates(tickerList, symbols)
2388
2731
 
2389
2732
  async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
2390
2733
  """
2391
2734
  fetches historical funding rate prices
2392
- :see: https://bybit-exchange.github.io/docs/v5/market/history-fund-rate
2735
+
2736
+ https://bybit-exchange.github.io/docs/v5/market/history-fund-rate
2737
+
2393
2738
  :param str symbol: unified symbol of the market to fetch the funding rate history for
2394
2739
  :param int [since]: timestamp in ms of the earliest funding rate to fetch
2395
2740
  :param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>` to fetch
@@ -2641,7 +2986,9 @@ class bybit(Exchange, ImplicitAPI):
2641
2986
  async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
2642
2987
  """
2643
2988
  get the list of most recent trades for a particular symbol
2644
- :see: https://bybit-exchange.github.io/docs/v5/market/recent-trade
2989
+
2990
+ https://bybit-exchange.github.io/docs/v5/market/recent-trade
2991
+
2645
2992
  :param str symbol: unified symbol of the market to fetch trades for
2646
2993
  :param int [since]: timestamp in ms of the earliest trade to fetch
2647
2994
  :param int [limit]: the maximum amount of trades to fetch
@@ -2696,7 +3043,9 @@ class bybit(Exchange, ImplicitAPI):
2696
3043
  async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
2697
3044
  """
2698
3045
  fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
2699
- :see: https://bybit-exchange.github.io/docs/v5/market/orderbook
3046
+
3047
+ https://bybit-exchange.github.io/docs/v5/market/orderbook
3048
+
2700
3049
  :param str symbol: unified symbol of the market to fetch the order book for
2701
3050
  :param int [limit]: the maximum amount of order book entries to return
2702
3051
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -2865,7 +3214,7 @@ class bybit(Exchange, ImplicitAPI):
2865
3214
  'datetime': self.iso8601(timestamp),
2866
3215
  }
2867
3216
  responseResult = self.safe_dict(response, 'result', {})
2868
- currencyList = self.safe_value_n(responseResult, ['loanAccountList', 'list', 'balance'])
3217
+ currencyList = self.safe_list_n(responseResult, ['loanAccountList', 'list', 'balance'])
2869
3218
  if currencyList is None:
2870
3219
  # usdc wallet
2871
3220
  code = 'USDC'
@@ -2887,7 +3236,16 @@ class bybit(Exchange, ImplicitAPI):
2887
3236
  if (loan is not None) and (interest is not None):
2888
3237
  account['debt'] = Precise.string_add(loan, interest)
2889
3238
  account['total'] = self.safe_string(coinEntry, 'walletBalance')
2890
- account['free'] = self.safe_string_2(coinEntry, 'availableToWithdraw', 'free')
3239
+ free = self.safe_string_2(coinEntry, 'availableToWithdraw', 'free')
3240
+ if free is not None:
3241
+ account['free'] = free
3242
+ else:
3243
+ locked = self.safe_string(coinEntry, 'locked', '0')
3244
+ totalPositionIm = self.safe_string(coinEntry, 'totalPositionIM', '0')
3245
+ totalOrderIm = self.safe_string(coinEntry, 'totalOrderIM', '0')
3246
+ totalUsed = Precise.string_add(locked, totalPositionIm)
3247
+ totalUsed = Precise.string_add(totalUsed, totalOrderIm)
3248
+ account['used'] = totalUsed
2891
3249
  # account['used'] = self.safe_string(coinEntry, 'locked')
2892
3250
  currencyId = self.safe_string(coinEntry, 'coin')
2893
3251
  code = self.safe_currency_code(currencyId)
@@ -2909,11 +3267,13 @@ class bybit(Exchange, ImplicitAPI):
2909
3267
  async def fetch_balance(self, params={}) -> Balances:
2910
3268
  """
2911
3269
  query for balance and get the amount of funds available for trading or funds locked in orders
2912
- :see: https://bybit-exchange.github.io/docs/v5/spot-margin-normal/account-info
2913
- :see: https://bybit-exchange.github.io/docs/v5/asset/all-balance
2914
- :see: https://bybit-exchange.github.io/docs/v5/account/wallet-balance
3270
+
3271
+ https://bybit-exchange.github.io/docs/v5/spot-margin-normal/account-info
3272
+ https://bybit-exchange.github.io/docs/v5/asset/all-balance
3273
+ https://bybit-exchange.github.io/docs/v5/account/wallet-balance
3274
+
2915
3275
  :param dict [params]: extra parameters specific to the exchange API endpoint
2916
- :param str [params.type]: wallet type, ['spot', 'swap', 'fund']
3276
+ :param str [params.type]: wallet type, ['spot', 'swap', 'funding']
2917
3277
  :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
2918
3278
  """
2919
3279
  await self.load_markets()
@@ -2921,14 +3281,29 @@ class bybit(Exchange, ImplicitAPI):
2921
3281
  enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
2922
3282
  isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
2923
3283
  type = None
3284
+ # don't use getBybitType here
2924
3285
  type, params = self.handle_market_type_and_params('fetchBalance', None, params)
3286
+ subType = None
3287
+ subType, params = self.handle_sub_type_and_params('fetchBalance', None, params)
3288
+ if (type == 'swap') or (type == 'future'):
3289
+ type = subType
3290
+ lowercaseRawType = type.lower() if (type is not None) else None
2925
3291
  isSpot = (type == 'spot')
2926
- isSwap = (type == 'swap')
3292
+ isLinear = (type == 'linear')
3293
+ isInverse = (type == 'inverse')
3294
+ isFunding = (lowercaseRawType == 'fund') or (lowercaseRawType == 'funding')
2927
3295
  if isUnifiedAccount:
2928
- if isSpot or isSwap:
2929
- type = 'unified'
3296
+ unifiedMarginStatus = self.safe_integer(self.options, 'unifiedMarginStatus', 6)
3297
+ if unifiedMarginStatus < 5:
3298
+ # it's not uta.20 where inverse are unified
3299
+ if isInverse:
3300
+ type = 'contract'
3301
+ else:
3302
+ type = 'unified'
3303
+ else:
3304
+ type = 'unified' # uta.20 where inverse are unified
2930
3305
  else:
2931
- if isSwap:
3306
+ if isLinear or isInverse:
2932
3307
  type = 'contract'
2933
3308
  accountTypes = self.safe_dict(self.options, 'accountsByType', {})
2934
3309
  unifiedType = self.safe_string_upper(accountTypes, type, type)
@@ -2937,10 +3312,10 @@ class bybit(Exchange, ImplicitAPI):
2937
3312
  response = None
2938
3313
  if isSpot and (marginMode is not None):
2939
3314
  response = await self.privateGetV5SpotCrossMarginTradeAccount(self.extend(request, params))
2940
- elif unifiedType == 'FUND':
3315
+ elif isFunding:
2941
3316
  # use self endpoint only we have no other choice
2942
3317
  # because it requires transfer permission
2943
- request['accountType'] = unifiedType
3318
+ request['accountType'] = 'FUND'
2944
3319
  response = await self.privateGetV5AssetTransferQueryAccountCoinsBalance(self.extend(request, params))
2945
3320
  else:
2946
3321
  request['accountType'] = unifiedType
@@ -3184,13 +3559,13 @@ class bybit(Exchange, ImplicitAPI):
3184
3559
  if code is not None:
3185
3560
  if code != '0':
3186
3561
  category = self.safe_string(order, 'category')
3187
- inferedMarketType = 'spot' if (category == 'spot') else 'contract'
3562
+ inferredMarketType = 'spot' if (category == 'spot') else 'contract'
3188
3563
  return self.safe_order({
3189
3564
  'info': order,
3190
3565
  'status': 'rejected',
3191
3566
  'id': self.safe_string(order, 'orderId'),
3192
3567
  'clientOrderId': self.safe_string(order, 'orderLinkId'),
3193
- 'symbol': self.safe_symbol(self.safe_string(order, 'symbol'), None, None, inferedMarketType),
3568
+ 'symbol': self.safe_symbol(self.safe_string(order, 'symbol'), None, None, inferredMarketType),
3194
3569
  })
3195
3570
  marketId = self.safe_string(order, 'symbol')
3196
3571
  isContract = ('tpslMode' in order)
@@ -3202,11 +3577,17 @@ class bybit(Exchange, ImplicitAPI):
3202
3577
  market = self.safe_market(marketId, market, None, marketType)
3203
3578
  symbol = market['symbol']
3204
3579
  timestamp = self.safe_integer_2(order, 'createdTime', 'createdAt')
3580
+ marketUnit = self.safe_string(order, 'marketUnit', 'baseCoin')
3205
3581
  id = self.safe_string(order, 'orderId')
3206
3582
  type = self.safe_string_lower(order, 'orderType')
3207
3583
  price = self.safe_string(order, 'price')
3208
- amount = self.safe_string(order, 'qty')
3209
- cost = self.safe_string(order, 'cumExecValue')
3584
+ amount: Str = None
3585
+ cost: Str = None
3586
+ if marketUnit == 'baseCoin':
3587
+ amount = self.safe_string(order, 'qty')
3588
+ cost = self.safe_string(order, 'cumExecValue')
3589
+ else:
3590
+ cost = self.safe_string(order, 'cumExecValue')
3210
3591
  filled = self.safe_string(order, 'cumExecQty')
3211
3592
  remaining = self.safe_string(order, 'leavesQty')
3212
3593
  lastTradeTimestamp = self.safe_integer_2(order, 'updatedTime', 'updatedAt')
@@ -3231,7 +3612,7 @@ class bybit(Exchange, ImplicitAPI):
3231
3612
  else:
3232
3613
  feeCurrencyCode = market['base'] if market['inverse'] else market['settle']
3233
3614
  fee = {
3234
- 'cost': feeCostString,
3615
+ 'cost': self.parse_number(feeCostString),
3235
3616
  'currency': feeCurrencyCode,
3236
3617
  }
3237
3618
  clientOrderId = self.safe_string(order, 'orderLinkId')
@@ -3240,29 +3621,29 @@ class bybit(Exchange, ImplicitAPI):
3240
3621
  avgPrice = self.omit_zero(self.safe_string(order, 'avgPrice'))
3241
3622
  rawTimeInForce = self.safe_string(order, 'timeInForce')
3242
3623
  timeInForce = self.parse_time_in_force(rawTimeInForce)
3243
- stopPrice = self.omit_zero(self.safe_string(order, 'triggerPrice'))
3624
+ triggerPrice = self.omit_zero(self.safe_string(order, 'triggerPrice'))
3244
3625
  reduceOnly = self.safe_bool(order, 'reduceOnly')
3245
3626
  takeProfitPrice = self.omit_zero(self.safe_string(order, 'takeProfit'))
3246
3627
  stopLossPrice = self.omit_zero(self.safe_string(order, 'stopLoss'))
3247
3628
  triggerDirection = self.safe_string(order, 'triggerDirection')
3248
3629
  isAscending = (triggerDirection == '1')
3249
- isStopOrderType2 = (stopPrice is not None) and reduceOnly
3630
+ isStopOrderType2 = (triggerPrice is not None) and reduceOnly
3250
3631
  if (stopLossPrice is None) and isStopOrderType2:
3251
3632
  # check if order is stop order type 2 - stopLossPrice
3252
3633
  if isAscending and (side == 'buy'):
3253
3634
  # stopLoss order against short position
3254
- stopLossPrice = stopPrice
3635
+ stopLossPrice = triggerPrice
3255
3636
  if not isAscending and (side == 'sell'):
3256
3637
  # stopLoss order against a long position
3257
- stopLossPrice = stopPrice
3638
+ stopLossPrice = triggerPrice
3258
3639
  if (takeProfitPrice is None) and isStopOrderType2:
3259
3640
  # check if order is stop order type 2 - takeProfitPrice
3260
3641
  if isAscending and (side == 'sell'):
3261
3642
  # takeprofit order against a long position
3262
- takeProfitPrice = stopPrice
3643
+ takeProfitPrice = triggerPrice
3263
3644
  if not isAscending and (side == 'buy'):
3264
3645
  # takeprofit order against a short position
3265
- takeProfitPrice = stopPrice
3646
+ takeProfitPrice = triggerPrice
3266
3647
  return self.safe_order({
3267
3648
  'info': order,
3268
3649
  'id': id,
@@ -3278,8 +3659,7 @@ class bybit(Exchange, ImplicitAPI):
3278
3659
  'reduceOnly': self.safe_bool(order, 'reduceOnly'),
3279
3660
  'side': side,
3280
3661
  'price': price,
3281
- 'stopPrice': stopPrice,
3282
- 'triggerPrice': stopPrice,
3662
+ 'triggerPrice': triggerPrice,
3283
3663
  'takeProfitPrice': takeProfitPrice,
3284
3664
  'stopLossPrice': stopLossPrice,
3285
3665
  'amount': amount,
@@ -3292,10 +3672,12 @@ class bybit(Exchange, ImplicitAPI):
3292
3672
  'trades': None,
3293
3673
  }, market)
3294
3674
 
3295
- async def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}):
3675
+ async def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}) -> Order:
3296
3676
  """
3297
- :see: https://bybit-exchange.github.io/docs/v5/order/create-order
3298
3677
  create a market buy order by providing the symbol and cost
3678
+
3679
+ https://bybit-exchange.github.io/docs/v5/order/create-order
3680
+
3299
3681
  :param str symbol: unified symbol of the market to create an order in
3300
3682
  :param float cost: how much you want to trade in units of the quote currency
3301
3683
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -3305,12 +3687,17 @@ class bybit(Exchange, ImplicitAPI):
3305
3687
  market = self.market(symbol)
3306
3688
  if not market['spot']:
3307
3689
  raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only')
3308
- return await self.create_order(symbol, 'market', 'buy', cost, 1, params)
3690
+ req = {
3691
+ 'cost': cost,
3692
+ }
3693
+ return await self.create_order(symbol, 'market', 'buy', -1, None, self.extend(req, params))
3309
3694
 
3310
- async def create_market_sell_order_with_cost(self, symbol: str, cost: float, params={}):
3695
+ async def create_market_sell_order_with_cost(self, symbol: str, cost: float, params={}) -> Order:
3311
3696
  """
3312
- :see: https://bybit-exchange.github.io/docs/v5/order/create-order
3313
3697
  create a market sell order by providing the symbol and cost
3698
+
3699
+ https://bybit-exchange.github.io/docs/v5/order/create-order
3700
+
3314
3701
  :param str symbol: unified symbol of the market to create an order in
3315
3702
  :param float cost: how much you want to trade in units of the quote currency
3316
3703
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -3324,27 +3711,33 @@ class bybit(Exchange, ImplicitAPI):
3324
3711
  market = self.market(symbol)
3325
3712
  if not market['spot']:
3326
3713
  raise NotSupported(self.id + ' createMarketSellOrderWithCost() supports spot orders only')
3327
- return await self.create_order(symbol, 'market', 'sell', cost, 1, params)
3714
+ req = {
3715
+ 'cost': cost,
3716
+ }
3717
+ return await self.create_order(symbol, 'market', 'sell', -1, None, self.extend(req, params))
3328
3718
 
3329
- async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
3719
+ async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
3330
3720
  """
3331
3721
  create a trade order
3332
- :see: https://bybit-exchange.github.io/docs/v5/order/create-order
3333
- :see: https://bybit-exchange.github.io/docs/v5/position/trading-stop
3722
+
3723
+ https://bybit-exchange.github.io/docs/v5/order/create-order
3724
+ https://bybit-exchange.github.io/docs/v5/position/trading-stop
3725
+
3334
3726
  :param str symbol: unified symbol of the market to create an order in
3335
3727
  :param str type: 'market' or 'limit'
3336
3728
  :param str side: 'buy' or 'sell'
3337
3729
  :param float amount: how much of currency you want to trade in units of base currency
3338
- :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
3730
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
3339
3731
  :param dict [params]: extra parameters specific to the exchange API endpoint
3340
3732
  :param str [params.timeInForce]: "GTC", "IOC", "FOK"
3341
3733
  :param bool [params.postOnly]: True or False whether the order is post-only
3342
3734
  :param bool [params.reduceOnly]: True or False whether the order is reduce-only
3343
- :param str [params.positionIdx]: *contracts only* 0 for one-way mode, 1 buy side of hedged mode, 2 sell side of hedged mode
3344
- :param boolean [params.isLeverage]: *unified spot only* False then spot trading True then margin trading
3735
+ :param str [params.positionIdx]: *contracts only* 0 for one-way mode, 1 buy side of hedged mode, 2 sell side of hedged mode
3736
+ :param bool [params.hedged]: *contracts only* True for hedged mode, False for one way mode, default is False
3737
+ :param int [params.isLeverage]: *unified spot only* False then spot trading True then margin trading
3345
3738
  :param str [params.tpslMode]: *contract only* 'full' or 'partial'
3346
3739
  :param str [params.mmp]: *option only* market maker protection
3347
- :param str [params.triggerDirection]: *contract only* the direction for trigger orders, 'above' or 'below'
3740
+ :param str [params.triggerDirection]: *contract only* the direction for trigger orders, 'ascending' or 'descending'
3348
3741
  :param float [params.triggerPrice]: The price at which a trigger order is triggered at
3349
3742
  :param float [params.stopLossPrice]: The price at which a stop loss order is triggered at
3350
3743
  :param float [params.takeProfitPrice]: The price at which a take profit order is triggered at
@@ -3358,18 +3751,24 @@ class bybit(Exchange, ImplicitAPI):
3358
3751
  """
3359
3752
  await self.load_markets()
3360
3753
  market = self.market(symbol)
3361
- enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
3362
- isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
3363
- isUsdcSettled = market['settle'] == 'USDC'
3364
- if isUsdcSettled and not isUnifiedAccount:
3365
- return await self.create_usdc_order(symbol, type, side, amount, price, params)
3754
+ parts = await self.is_unified_enabled()
3755
+ enableUnifiedAccount = parts[1]
3366
3756
  trailingAmount = self.safe_string_2(params, 'trailingAmount', 'trailingStop')
3757
+ stopLossPrice = self.safe_string(params, 'stopLossPrice')
3758
+ takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
3367
3759
  isTrailingAmountOrder = trailingAmount is not None
3760
+ isStopLoss = stopLossPrice is not None
3761
+ isTakeProfit = takeProfitPrice is not None
3368
3762
  orderRequest = self.create_order_request(symbol, type, side, amount, price, params, enableUnifiedAccount)
3369
- options = self.safe_dict(self.options, 'createOrder', {})
3370
- defaultMethod = self.safe_string(options, 'method', 'privatePostV5OrderCreate')
3763
+ defaultMethod = None
3764
+ if (isTrailingAmountOrder or isStopLoss or isTakeProfit) and not market['spot']:
3765
+ defaultMethod = 'privatePostV5PositionTradingStop'
3766
+ else:
3767
+ defaultMethod = 'privatePostV5OrderCreate'
3768
+ method = None
3769
+ method, params = self.handle_option_and_params(params, 'createOrder', 'method', defaultMethod)
3371
3770
  response = None
3372
- if isTrailingAmountOrder or (defaultMethod == 'privatePostV5PositionTradingStop'):
3771
+ if method == 'privatePostV5PositionTradingStop':
3373
3772
  response = await self.privatePostV5PositionTradingStop(orderRequest)
3374
3773
  else:
3375
3774
  response = await self.privatePostV5OrderCreate(orderRequest) # already extended inside createOrderRequest
@@ -3394,8 +3793,6 @@ class bybit(Exchange, ImplicitAPI):
3394
3793
  lowerCaseType = type.lower()
3395
3794
  if (price is None) and (lowerCaseType == 'limit'):
3396
3795
  raise ArgumentsRequired(self.id + ' createOrder requires a price argument for limit orders')
3397
- defaultMethod = None
3398
- defaultMethod, params = self.handle_option_and_params(params, 'createOrder', 'method', 'privatePostV5OrderCreate')
3399
3796
  request: dict = {
3400
3797
  'symbol': market['id'],
3401
3798
  # 'side': self.capitalize(side),
@@ -3421,6 +3818,8 @@ class bybit(Exchange, ImplicitAPI):
3421
3818
  # Valid for option only.
3422
3819
  # 'orderIv': '0', # Implied volatility; parameters are passed according to the real value; for example, for 10%, 0.1 is passed
3423
3820
  }
3821
+ hedged = self.safe_bool(params, 'hedged', False)
3822
+ reduceOnly = self.safe_bool(params, 'reduceOnly')
3424
3823
  triggerPrice = self.safe_value_2(params, 'triggerPrice', 'stopPrice')
3425
3824
  stopLossTriggerPrice = self.safe_value(params, 'stopLossPrice')
3426
3825
  takeProfitTriggerPrice = self.safe_value(params, 'takeProfitPrice')
@@ -3437,25 +3836,34 @@ class bybit(Exchange, ImplicitAPI):
3437
3836
  isMarket = lowerCaseType == 'market'
3438
3837
  isLimit = lowerCaseType == 'limit'
3439
3838
  isBuy = side == 'buy'
3440
- isAlternativeEndpoint = defaultMethod == 'privatePostV5PositionTradingStop'
3839
+ defaultMethod = None
3840
+ if (isTrailingAmountOrder or isStopLossTriggerOrder or isTakeProfitTriggerOrder) and not market['spot']:
3841
+ defaultMethod = 'privatePostV5PositionTradingStop'
3842
+ else:
3843
+ defaultMethod = 'privatePostV5OrderCreate'
3844
+ method = None
3845
+ method, params = self.handle_option_and_params(params, 'createOrder', 'method', defaultMethod)
3846
+ isAlternativeEndpoint = method == 'privatePostV5PositionTradingStop'
3847
+ amountString = self.get_amount(symbol, amount)
3848
+ priceString = self.get_price(symbol, self.number_to_string(price)) if (price is not None) else None
3441
3849
  if isTrailingAmountOrder or isAlternativeEndpoint:
3442
3850
  if isStopLoss or isTakeProfit or isTriggerOrder or market['spot']:
3443
3851
  raise InvalidOrder(self.id + ' the API endpoint used only supports contract trailingAmount, stopLossPrice and takeProfitPrice orders')
3444
3852
  if isStopLossTriggerOrder or isTakeProfitTriggerOrder:
3445
3853
  if isStopLossTriggerOrder:
3446
- request['stopLoss'] = self.price_to_precision(symbol, stopLossTriggerPrice)
3854
+ request['stopLoss'] = self.get_price(symbol, stopLossTriggerPrice)
3447
3855
  if isLimit:
3448
3856
  request['tpslMode'] = 'Partial'
3449
3857
  request['slOrderType'] = 'Limit'
3450
- request['slLimitPrice'] = self.price_to_precision(symbol, price)
3451
- request['slSize'] = self.amount_to_precision(symbol, amount)
3858
+ request['slLimitPrice'] = priceString
3859
+ request['slSize'] = amountString
3452
3860
  elif isTakeProfitTriggerOrder:
3453
- request['takeProfit'] = self.price_to_precision(symbol, takeProfitTriggerPrice)
3861
+ request['takeProfit'] = self.get_price(symbol, takeProfitTriggerPrice)
3454
3862
  if isLimit:
3455
3863
  request['tpslMode'] = 'Partial'
3456
3864
  request['tpOrderType'] = 'Limit'
3457
- request['tpLimitPrice'] = self.price_to_precision(symbol, price)
3458
- request['tpSize'] = self.amount_to_precision(symbol, amount)
3865
+ request['tpLimitPrice'] = priceString
3866
+ request['tpSize'] = amountString
3459
3867
  else:
3460
3868
  request['side'] = self.capitalize(side)
3461
3869
  request['orderType'] = self.capitalize(lowerCaseType)
@@ -3483,15 +3891,10 @@ class bybit(Exchange, ImplicitAPI):
3483
3891
  # mandatory field for options
3484
3892
  request['orderLinkId'] = self.uuid16()
3485
3893
  if isLimit:
3486
- request['price'] = self.price_to_precision(symbol, price)
3487
- if market['spot']:
3488
- request['category'] = 'spot'
3489
- elif market['linear']:
3490
- request['category'] = 'linear'
3491
- elif market['inverse']:
3492
- request['category'] = 'inverse'
3493
- elif market['option']:
3494
- request['category'] = 'option'
3894
+ request['price'] = priceString
3895
+ category = None
3896
+ category, params = self.get_bybit_type('createOrderRequest', market, params)
3897
+ request['category'] = category
3495
3898
  cost = self.safe_string(params, 'cost')
3496
3899
  params = self.omit(params, 'cost')
3497
3900
  # if the cost is inferable, let's keep the old logic and ignore marketUnit, to minimize the impact of the changes
@@ -3504,36 +3907,37 @@ class bybit(Exchange, ImplicitAPI):
3504
3907
  if cost is not None:
3505
3908
  orderCost = cost
3506
3909
  else:
3507
- amountString = self.number_to_string(amount)
3508
- priceString = self.number_to_string(price)
3509
3910
  quoteAmount = Precise.string_mul(amountString, priceString)
3510
3911
  orderCost = quoteAmount
3511
- request['qty'] = self.cost_to_precision(symbol, orderCost)
3912
+ request['qty'] = self.get_cost(symbol, orderCost)
3512
3913
  else:
3513
3914
  request['marketUnit'] = 'baseCoin'
3514
- request['qty'] = self.amount_to_precision(symbol, amount)
3915
+ request['qty'] = amountString
3515
3916
  elif market['spot'] and (type == 'market') and (side == 'buy'):
3516
3917
  # classic accounts
3517
3918
  # for market buy it requires the amount of quote currency to spend
3518
3919
  createMarketBuyOrderRequiresPrice = True
3519
- createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
3920
+ createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice')
3520
3921
  if createMarketBuyOrderRequiresPrice:
3521
3922
  if (price is None) and (cost is None):
3522
3923
  raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend in the amount argument')
3523
3924
  else:
3524
- amountString = self.number_to_string(amount)
3525
- priceString = self.number_to_string(price)
3526
- quoteAmount = Precise.string_mul(amountString, priceString)
3925
+ quoteAmount = Precise.string_mul(self.number_to_string(amount), priceString)
3527
3926
  costRequest = cost if (cost is not None) else quoteAmount
3528
- request['qty'] = self.cost_to_precision(symbol, costRequest)
3927
+ request['qty'] = self.get_cost(symbol, costRequest)
3529
3928
  else:
3530
- request['qty'] = self.cost_to_precision(symbol, amount)
3929
+ if cost is not None:
3930
+ request['qty'] = self.get_cost(symbol, self.number_to_string(cost))
3931
+ elif price is not None:
3932
+ request['qty'] = self.get_cost(symbol, Precise.string_mul(amountString, priceString))
3933
+ else:
3934
+ request['qty'] = amountString
3531
3935
  else:
3532
3936
  if not isTrailingAmountOrder and not isAlternativeEndpoint:
3533
- request['qty'] = self.amount_to_precision(symbol, amount)
3937
+ request['qty'] = amountString
3534
3938
  if isTrailingAmountOrder:
3535
3939
  if trailingTriggerPrice is not None:
3536
- request['activePrice'] = self.price_to_precision(symbol, trailingTriggerPrice)
3940
+ request['activePrice'] = self.get_price(symbol, trailingTriggerPrice)
3537
3941
  request['trailingStop'] = trailingAmount
3538
3942
  elif isTriggerOrder and not isAlternativeEndpoint:
3539
3943
  triggerDirection = self.safe_string(params, 'triggerDirection')
@@ -3543,43 +3947,51 @@ class bybit(Exchange, ImplicitAPI):
3543
3947
  raise NotSupported(self.id + ' createOrder() : trigger order does not support triggerDirection for spot markets yet')
3544
3948
  else:
3545
3949
  if triggerDirection is None:
3546
- raise ArgumentsRequired(self.id + ' stop/trigger orders require a triggerDirection parameter, either "above" or "below" to determine the direction of the trigger.')
3547
- isAsending = ((triggerDirection == 'above') or (triggerDirection == '1'))
3950
+ raise ArgumentsRequired(self.id + ' stop/trigger orders require a triggerDirection parameter, either "ascending" or "descending" to determine the direction of the trigger.')
3951
+ isAsending = ((triggerDirection == 'ascending') or (triggerDirection == 'above') or (triggerDirection == '1'))
3548
3952
  request['triggerDirection'] = 1 if isAsending else 2
3549
- request['triggerPrice'] = self.price_to_precision(symbol, triggerPrice)
3953
+ request['triggerPrice'] = self.get_price(symbol, triggerPrice)
3550
3954
  elif (isStopLossTriggerOrder or isTakeProfitTriggerOrder) and not isAlternativeEndpoint:
3551
3955
  if isBuy:
3552
3956
  request['triggerDirection'] = 1 if isStopLossTriggerOrder else 2
3553
3957
  else:
3554
3958
  request['triggerDirection'] = 2 if isStopLossTriggerOrder else 1
3555
3959
  triggerPrice = stopLossTriggerPrice if isStopLossTriggerOrder else takeProfitTriggerPrice
3556
- request['triggerPrice'] = self.price_to_precision(symbol, triggerPrice)
3960
+ request['triggerPrice'] = self.get_price(symbol, triggerPrice)
3557
3961
  request['reduceOnly'] = True
3558
3962
  if (isStopLoss or isTakeProfit) and not isAlternativeEndpoint:
3559
3963
  if isStopLoss:
3560
3964
  slTriggerPrice = self.safe_value_2(stopLoss, 'triggerPrice', 'stopPrice', stopLoss)
3561
- request['stopLoss'] = self.price_to_precision(symbol, slTriggerPrice)
3965
+ request['stopLoss'] = self.get_price(symbol, slTriggerPrice)
3562
3966
  slLimitPrice = self.safe_value(stopLoss, 'price')
3563
3967
  if slLimitPrice is not None:
3564
3968
  request['tpslMode'] = 'Partial'
3565
3969
  request['slOrderType'] = 'Limit'
3566
- request['slLimitPrice'] = self.price_to_precision(symbol, slLimitPrice)
3970
+ request['slLimitPrice'] = self.get_price(symbol, slLimitPrice)
3567
3971
  if isTakeProfit:
3568
3972
  tpTriggerPrice = self.safe_value_2(takeProfit, 'triggerPrice', 'stopPrice', takeProfit)
3569
- request['takeProfit'] = self.price_to_precision(symbol, tpTriggerPrice)
3973
+ request['takeProfit'] = self.get_price(symbol, tpTriggerPrice)
3570
3974
  tpLimitPrice = self.safe_value(takeProfit, 'price')
3571
3975
  if tpLimitPrice is not None:
3572
3976
  request['tpslMode'] = 'Partial'
3573
3977
  request['tpOrderType'] = 'Limit'
3574
- request['tpLimitPrice'] = self.price_to_precision(symbol, tpLimitPrice)
3575
- params = self.omit(params, ['stopPrice', 'timeInForce', 'stopLossPrice', 'takeProfitPrice', 'postOnly', 'clientOrderId', 'triggerPrice', 'stopLoss', 'takeProfit', 'trailingAmount', 'trailingTriggerPrice'])
3978
+ request['tpLimitPrice'] = self.get_price(symbol, tpLimitPrice)
3979
+ if not market['spot'] and hedged:
3980
+ if reduceOnly:
3981
+ params = self.omit(params, 'reduceOnly')
3982
+ side = 'sell' if (side == 'buy') else 'buy'
3983
+ request['positionIdx'] = 1 if (side == 'buy') else 2
3984
+ params = self.omit(params, ['stopPrice', 'timeInForce', 'stopLossPrice', 'takeProfitPrice', 'postOnly', 'clientOrderId', 'triggerPrice', 'stopLoss', 'takeProfit', 'trailingAmount', 'trailingTriggerPrice', 'hedged'])
3576
3985
  return self.extend(request, params)
3577
3986
 
3578
- async def create_orders(self, orders: List[OrderRequest], params={}):
3987
+ async def create_orders(self, orders: List[OrderRequest], params={}) -> List[Order]:
3579
3988
  """
3580
3989
  create a list of trade orders
3581
- :see: https://bybit-exchange.github.io/docs/v5/order/batch-place
3990
+
3991
+ https://bybit-exchange.github.io/docs/v5/order/batch-place
3992
+
3582
3993
  :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
3994
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3583
3995
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
3584
3996
  """
3585
3997
  await self.load_markets()
@@ -3595,15 +4007,17 @@ class bybit(Exchange, ImplicitAPI):
3595
4007
  side = self.safe_string(rawOrder, 'side')
3596
4008
  amount = self.safe_value(rawOrder, 'amount')
3597
4009
  price = self.safe_value(rawOrder, 'price')
3598
- orderParams = self.safe_value(rawOrder, 'params', {})
4010
+ orderParams = self.safe_dict(rawOrder, 'params', {})
3599
4011
  orderRequest = self.create_order_request(marketId, type, side, amount, price, orderParams, isUta)
4012
+ del orderRequest['category']
3600
4013
  ordersRequests.append(orderRequest)
3601
4014
  symbols = self.market_symbols(orderSymbols, None, False, True, True)
3602
4015
  market = self.market(symbols[0])
4016
+ unifiedMarginStatus = self.safe_integer(self.options, 'unifiedMarginStatus', 6)
3603
4017
  category = None
3604
4018
  category, params = self.get_bybit_type('createOrders', market, params)
3605
- if category == 'inverse':
3606
- raise NotSupported(self.id + ' createOrders does not allow inverse orders')
4019
+ if (category == 'inverse') and (unifiedMarginStatus < 5):
4020
+ raise NotSupported(self.id + ' createOrders does not allow inverse orders for non UTA2.0 account')
3607
4021
  request: dict = {
3608
4022
  'category': category,
3609
4023
  'request': ordersRequests,
@@ -3658,165 +4072,6 @@ class bybit(Exchange, ImplicitAPI):
3658
4072
  #
3659
4073
  return self.parse_orders(data)
3660
4074
 
3661
- async def create_usdc_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
3662
- await self.load_markets()
3663
- market = self.market(symbol)
3664
- if type == 'market':
3665
- raise NotSupported(self.id + ' createOrder does not allow market orders for ' + symbol + ' markets')
3666
- lowerCaseType = type.lower()
3667
- if (price is None) and (lowerCaseType == 'limit'):
3668
- raise ArgumentsRequired(self.id + ' createOrder requires a price argument for limit orders')
3669
- request: dict = {
3670
- 'symbol': market['id'],
3671
- 'side': self.capitalize(side),
3672
- 'orderType': self.capitalize(lowerCaseType), # limit or market
3673
- 'timeInForce': 'GoodTillCancel', # ImmediateOrCancel, FillOrKill, PostOnly
3674
- 'orderQty': self.amount_to_precision(symbol, amount),
3675
- # 'takeProfit': 123.45, # take profit price, only take effect upon opening the position
3676
- # 'stopLoss': 123.45, # stop loss price, only take effect upon opening the position
3677
- # 'reduceOnly': False, # reduce only, required for linear orders
3678
- # when creating a closing order, bybit recommends a True value for
3679
- # closeOnTrigger to avoid failing due to insufficient available margin
3680
- # 'closeOnTrigger': False, required for linear orders
3681
- # 'orderLinkId': 'string', # unique client order id, max 36 characters
3682
- # 'triggerPrice': 123.45, # trigger price, required for conditional orders
3683
- # 'trigger_by': 'MarkPrice', # IndexPrice, MarkPrice
3684
- # 'tptriggerby': 'MarkPrice', # IndexPrice, MarkPrice
3685
- # 'slTriggerBy': 'MarkPrice', # IndexPrice, MarkPrice
3686
- # 'orderFilter': 'Order' or 'StopOrder'
3687
- # 'mmp': False # market maker protection
3688
- }
3689
- isMarket = lowerCaseType == 'market'
3690
- isLimit = lowerCaseType == 'limit'
3691
- if isLimit is not None:
3692
- request['orderPrice'] = self.price_to_precision(symbol, price)
3693
- exchangeSpecificParam = self.safe_string(params, 'time_in_force')
3694
- timeInForce = self.safe_string_lower(params, 'timeInForce')
3695
- postOnly = self.is_post_only(isMarket, exchangeSpecificParam == 'PostOnly', params)
3696
- if postOnly:
3697
- request['time_in_force'] = 'PostOnly'
3698
- elif timeInForce == 'gtc':
3699
- request['time_in_force'] = 'GoodTillCancel'
3700
- elif timeInForce == 'fok':
3701
- request['time_in_force'] = 'FillOrKill'
3702
- elif timeInForce == 'ioc':
3703
- request['time_in_force'] = 'ImmediateOrCancel'
3704
- if market['swap']:
3705
- triggerPrice = self.safe_value_2(params, 'stopPrice', 'triggerPrice')
3706
- stopLossTriggerPrice = self.safe_value(params, 'stopLossPrice', triggerPrice)
3707
- takeProfitTriggerPrice = self.safe_value(params, 'takeProfitPrice')
3708
- stopLoss = self.safe_value(params, 'stopLoss')
3709
- takeProfit = self.safe_value(params, 'takeProfit')
3710
- isStopLossTriggerOrder = stopLossTriggerPrice is not None
3711
- isTakeProfitTriggerOrder = takeProfitTriggerPrice is not None
3712
- isStopLoss = stopLoss is not None
3713
- isTakeProfit = takeProfit is not None
3714
- isStopOrder = isStopLossTriggerOrder or isTakeProfitTriggerOrder
3715
- if isStopOrder:
3716
- request['orderFilter'] = 'StopOrder'
3717
- request['trigger_by'] = 'LastPrice'
3718
- stopPx = stopLossTriggerPrice if isStopLossTriggerOrder else takeProfitTriggerPrice
3719
- preciseStopPrice = self.price_to_precision(symbol, stopPx)
3720
- request['triggerPrice'] = preciseStopPrice
3721
- delta = self.number_to_string(market['precision']['price'])
3722
- request['basePrice'] = Precise.string_sub(preciseStopPrice, delta) if isStopLossTriggerOrder else Precise.string_add(preciseStopPrice, delta)
3723
- elif isStopLoss or isTakeProfit:
3724
- if isStopLoss:
3725
- slTriggerPrice = self.safe_value_2(stopLoss, 'triggerPrice', 'stopPrice', stopLoss)
3726
- request['stopLoss'] = self.price_to_precision(symbol, slTriggerPrice)
3727
- if isTakeProfit:
3728
- tpTriggerPrice = self.safe_value_2(takeProfit, 'triggerPrice', 'stopPrice', takeProfit)
3729
- request['takeProfit'] = self.price_to_precision(symbol, tpTriggerPrice)
3730
- else:
3731
- request['orderFilter'] = 'Order'
3732
- clientOrderId = self.safe_string(params, 'clientOrderId')
3733
- if clientOrderId is not None:
3734
- request['orderLinkId'] = clientOrderId
3735
- elif market['option']:
3736
- # mandatory field for options
3737
- request['orderLinkId'] = self.uuid16()
3738
- params = self.omit(params, ['stopPrice', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'postOnly', 'clientOrderId'])
3739
- response = None
3740
- if market['option']:
3741
- response = await self.privatePostOptionUsdcOpenapiPrivateV1PlaceOrder(self.extend(request, params))
3742
- else:
3743
- response = await self.privatePostPerpetualUsdcOpenapiPrivateV1PlaceOrder(self.extend(request, params))
3744
- #
3745
- # {
3746
- # "retCode":0,
3747
- # "retMsg":"",
3748
- # "result":{
3749
- # "orderId":"34450a59-325e-4296-8af0-63c7c524ae33",
3750
- # "orderLinkId":"",
3751
- # "mmp":false,
3752
- # "symbol":"BTCPERP",
3753
- # "orderType":"Limit",
3754
- # "side":"Buy",
3755
- # "orderQty":"0.00100000",
3756
- # "orderPrice":"20000.00",
3757
- # "iv":"0",
3758
- # "timeInForce":"GoodTillCancel",
3759
- # "orderStatus":"Created",
3760
- # "createdAt":"1652261746007873",
3761
- # "basePrice":"0.00",
3762
- # "triggerPrice":"0.00",
3763
- # "takeProfit":"0.00",
3764
- # "stopLoss":"0.00",
3765
- # "slTriggerBy":"UNKNOWN",
3766
- # "tpTriggerBy":"UNKNOWN"
3767
- # }
3768
- #
3769
- order = self.safe_dict(response, 'result', {})
3770
- return self.parse_order(order, market)
3771
-
3772
- async def edit_usdc_order(self, id, symbol, type, side, amount=None, price=None, params={}):
3773
- await self.load_markets()
3774
- market = self.market(symbol)
3775
- request: dict = {
3776
- 'symbol': market['id'],
3777
- 'orderId': id,
3778
- }
3779
- if amount is not None:
3780
- request['orderQty'] = self.amount_to_precision(symbol, amount)
3781
- if price is not None:
3782
- request['orderPrice'] = self.price_to_precision(symbol, price)
3783
- response = None
3784
- if market['option']:
3785
- response = await self.privatePostOptionUsdcOpenapiPrivateV1ReplaceOrder(self.extend(request, params))
3786
- else:
3787
- isStop = self.safe_bool_2(params, 'stop', 'trigger', False)
3788
- triggerPrice = self.safe_value_2(params, 'stopPrice', 'triggerPrice')
3789
- stopLossPrice = self.safe_value(params, 'stopLossPrice')
3790
- isStopLossOrder = stopLossPrice is not None
3791
- takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
3792
- isTakeProfitOrder = takeProfitPrice is not None
3793
- isStopOrder = isStopLossOrder or isTakeProfitOrder or isStop
3794
- if isStopOrder:
3795
- request['orderFilter'] = 'StopOrder' if isStop else 'Order'
3796
- if triggerPrice is not None:
3797
- request['triggerPrice'] = self.price_to_precision(symbol, triggerPrice)
3798
- if stopLossPrice is not None:
3799
- request['stopLoss'] = self.price_to_precision(symbol, stopLossPrice)
3800
- if takeProfitPrice is not None:
3801
- request['takeProfit'] = self.price_to_precision(symbol, takeProfitPrice)
3802
- params = self.omit(params, ['stop', 'stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice'])
3803
- response = await self.privatePostPerpetualUsdcOpenapiPrivateV1ReplaceOrder(self.extend(request, params))
3804
- #
3805
- # {
3806
- # "retCode": 0,
3807
- # "retMsg": "OK",
3808
- # "result": {
3809
- # "outRequestId": "",
3810
- # "symbol": "BTC-13MAY22-40000-C",
3811
- # "orderId": "8c65df91-91fc-461d-9b14-786379ef138c",
3812
- # "orderLinkId": "AAAAA41133"
3813
- # },
3814
- # "retExtMap": {}
3815
- # }
3816
- #
3817
- result = self.safe_dict(response, 'result', {})
3818
- return self.parse_order(result, market)
3819
-
3820
4075
  def edit_order_request(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
3821
4076
  market = self.market(symbol)
3822
4077
  request: dict = {
@@ -3832,20 +4087,13 @@ class bybit(Exchange, ImplicitAPI):
3832
4087
  # Valid for option only.
3833
4088
  # 'orderIv': '0', # Implied volatility; parameters are passed according to the real value; for example, for 10%, 0.1 is passed
3834
4089
  }
3835
- if market['spot']:
3836
- request['category'] = 'spot'
3837
- elif market['linear']:
3838
- request['category'] = 'linear'
3839
- elif market['inverse']:
3840
- request['category'] = 'inverse'
3841
- elif market['option']:
3842
- request['category'] = 'option'
4090
+ category = None
4091
+ category, params = self.get_bybit_type('editOrderRequest', market, params)
4092
+ request['category'] = category
3843
4093
  if amount is not None:
3844
- request['qty'] = self.amount_to_precision(symbol, amount)
4094
+ request['qty'] = self.get_amount(symbol, amount)
3845
4095
  if price is not None:
3846
- request['price'] = self.price_to_precision(symbol, price)
3847
- if amount is not None:
3848
- request['qty'] = self.amount_to_precision(symbol, amount)
4096
+ request['price'] = self.get_price(symbol, self.number_to_string(price))
3849
4097
  triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
3850
4098
  stopLossTriggerPrice = self.safe_string(params, 'stopLossPrice')
3851
4099
  takeProfitTriggerPrice = self.safe_string(params, 'takeProfitPrice')
@@ -3858,20 +4106,20 @@ class bybit(Exchange, ImplicitAPI):
3858
4106
  if isStopLossTriggerOrder or isTakeProfitTriggerOrder:
3859
4107
  triggerPrice = stopLossTriggerPrice if isStopLossTriggerOrder else takeProfitTriggerPrice
3860
4108
  if triggerPrice is not None:
3861
- triggerPriceRequest = triggerPrice if (triggerPrice == '0') else self.price_to_precision(symbol, triggerPrice)
4109
+ triggerPriceRequest = triggerPrice if (triggerPrice == '0') else self.get_price(symbol, triggerPrice)
3862
4110
  request['triggerPrice'] = triggerPriceRequest
3863
4111
  triggerBy = self.safe_string(params, 'triggerBy', 'LastPrice')
3864
4112
  request['triggerBy'] = triggerBy
3865
4113
  if isStopLoss or isTakeProfit:
3866
4114
  if isStopLoss:
3867
4115
  slTriggerPrice = self.safe_string_2(stopLoss, 'triggerPrice', 'stopPrice', stopLoss)
3868
- stopLossRequest = slTriggerPrice if (slTriggerPrice == '0') else self.price_to_precision(symbol, slTriggerPrice)
4116
+ stopLossRequest = slTriggerPrice if (slTriggerPrice == '0') else self.get_price(symbol, slTriggerPrice)
3869
4117
  request['stopLoss'] = stopLossRequest
3870
4118
  slTriggerBy = self.safe_string(params, 'slTriggerBy', 'LastPrice')
3871
4119
  request['slTriggerBy'] = slTriggerBy
3872
4120
  if isTakeProfit:
3873
4121
  tpTriggerPrice = self.safe_string_2(takeProfit, 'triggerPrice', 'stopPrice', takeProfit)
3874
- takeProfitRequest = tpTriggerPrice if (tpTriggerPrice == '0') else self.price_to_precision(symbol, tpTriggerPrice)
4122
+ takeProfitRequest = tpTriggerPrice if (tpTriggerPrice == '0') else self.get_price(symbol, tpTriggerPrice)
3875
4123
  request['takeProfit'] = takeProfitRequest
3876
4124
  tpTriggerBy = self.safe_string(params, 'tpTriggerBy', 'LastPrice')
3877
4125
  request['tpTriggerBy'] = tpTriggerBy
@@ -3881,18 +4129,20 @@ class bybit(Exchange, ImplicitAPI):
3881
4129
  params = self.omit(params, ['stopPrice', 'stopLossPrice', 'takeProfitPrice', 'triggerPrice', 'clientOrderId', 'stopLoss', 'takeProfit'])
3882
4130
  return request
3883
4131
 
3884
- async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
4132
+ async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}) -> Order:
3885
4133
  """
3886
4134
  edit a trade order
3887
- :see: https://bybit-exchange.github.io/docs/v5/order/amend-order
3888
- :see: https://bybit-exchange.github.io/docs/derivatives/unified/replace-order
3889
- :see: https://bybit-exchange.github.io/docs/api-explorer/derivatives/trade/contract/replace-order
4135
+
4136
+ https://bybit-exchange.github.io/docs/v5/order/amend-order
4137
+ https://bybit-exchange.github.io/docs/derivatives/unified/replace-order
4138
+ https://bybit-exchange.github.io/docs/api-explorer/derivatives/trade/contract/replace-order
4139
+
3890
4140
  :param str id: cancel order id
3891
4141
  :param str symbol: unified symbol of the market to create an order in
3892
4142
  :param str type: 'market' or 'limit'
3893
4143
  :param str side: 'buy' or 'sell'
3894
4144
  :param float amount: how much of currency you want to trade in units of base currency
3895
- :param float price: the price at which the order is to be fullfilled, in units of the base currency, ignored in market orders
4145
+ :param float price: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
3896
4146
  :param dict [params]: extra parameters specific to the exchange API endpoint
3897
4147
  :param float [params.triggerPrice]: The price that a trigger order is triggered at
3898
4148
  :param float [params.stopLossPrice]: The price that a stop loss order is triggered at
@@ -3909,12 +4159,6 @@ class bybit(Exchange, ImplicitAPI):
3909
4159
  await self.load_markets()
3910
4160
  if symbol is None:
3911
4161
  raise ArgumentsRequired(self.id + ' editOrder() requires a symbol argument')
3912
- market = self.market(symbol)
3913
- enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
3914
- isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
3915
- isUsdcSettled = market['settle'] == 'USDC'
3916
- if isUsdcSettled and not isUnifiedAccount:
3917
- return await self.edit_usdc_order(id, symbol, type, side, amount, price, params)
3918
4162
  request = self.edit_order_request(id, symbol, type, side, amount, price, params)
3919
4163
  response = await self.privatePostV5OrderAmend(self.extend(request, params))
3920
4164
  #
@@ -3935,41 +4179,90 @@ class bybit(Exchange, ImplicitAPI):
3935
4179
  'id': self.safe_string(result, 'orderId'),
3936
4180
  })
3937
4181
 
3938
- async def cancel_usdc_order(self, id, symbol: Str = None, params={}):
3939
- if symbol is None:
3940
- raise ArgumentsRequired(self.id + ' cancelUsdcOrder() requires a symbol argument')
4182
+ async def edit_orders(self, orders: List[OrderRequest], params={}) -> List[Order]:
4183
+ """
4184
+ edit a list of trade orders
4185
+
4186
+ https://bybit-exchange.github.io/docs/v5/order/batch-amend
4187
+
4188
+ :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
4189
+ :param dict [params]: extra parameters specific to the exchange API endpoint
4190
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
4191
+ """
3941
4192
  await self.load_markets()
3942
- market = self.market(symbol)
3943
- request: dict = {
3944
- 'symbol': market['id'],
3945
- # 'orderLinkId': 'string', # one of order_id, stop_order_id or order_link_id is required
3946
- # 'orderId': id,
3947
- }
3948
- isStop = self.safe_bool_2(params, 'stop', 'trigger', False)
3949
- params = self.omit(params, ['stop', 'trigger'])
3950
- if id is not None: # The user can also use argument params["order_link_id"]
3951
- request['orderId'] = id
3952
- response = None
3953
- if market['option']:
3954
- response = await self.privatePostOptionUsdcOpenapiPrivateV1CancelOrder(self.extend(request, params))
3955
- else:
3956
- request['orderFilter'] = 'StopOrder' if isStop else 'Order'
3957
- response = await self.privatePostPerpetualUsdcOpenapiPrivateV1CancelOrder(self.extend(request, params))
4193
+ ordersRequests = []
4194
+ orderSymbols = []
4195
+ for i in range(0, len(orders)):
4196
+ rawOrder = orders[i]
4197
+ symbol = self.safe_string(rawOrder, 'symbol')
4198
+ orderSymbols.append(symbol)
4199
+ id = self.safe_string(rawOrder, 'id')
4200
+ type = self.safe_string(rawOrder, 'type')
4201
+ side = self.safe_string(rawOrder, 'side')
4202
+ amount = self.safe_value(rawOrder, 'amount')
4203
+ price = self.safe_value(rawOrder, 'price')
4204
+ orderParams = self.safe_dict(rawOrder, 'params', {})
4205
+ orderRequest = self.edit_order_request(id, symbol, type, side, amount, price, orderParams)
4206
+ del orderRequest['category']
4207
+ ordersRequests.append(orderRequest)
4208
+ orderSymbols = self.market_symbols(orderSymbols, None, False, True, True)
4209
+ market = self.market(orderSymbols[0])
4210
+ unifiedMarginStatus = self.safe_integer(self.options, 'unifiedMarginStatus', 6)
4211
+ category = None
4212
+ category, params = self.get_bybit_type('editOrders', market, params)
4213
+ if (category == 'inverse') and (unifiedMarginStatus < 5):
4214
+ raise NotSupported(self.id + ' editOrders does not allow inverse orders for non UTA2.0 account')
4215
+ request: dict = {
4216
+ 'category': category,
4217
+ 'request': ordersRequests,
4218
+ }
4219
+ response = await self.privatePostV5OrderAmendBatch(self.extend(request, params))
4220
+ result = self.safe_dict(response, 'result', {})
4221
+ data = self.safe_list(result, 'list', [])
4222
+ retInfo = self.safe_dict(response, 'retExtInfo', {})
4223
+ codes = self.safe_list(retInfo, 'list', [])
4224
+ # self.extend the error with the unsuccessful orders
4225
+ for i in range(0, len(codes)):
4226
+ code = codes[i]
4227
+ retCode = self.safe_integer(code, 'code')
4228
+ if retCode != 0:
4229
+ data[i] = self.extend(data[i], code)
3958
4230
  #
3959
- # {
3960
- # "retCode": 0,
3961
- # "retMsg": "OK",
3962
- # "result": {
3963
- # "outRequestId": "",
3964
- # "symbol": "BTC-13MAY22-40000-C",
3965
- # "orderId": "8c65df91-91fc-461d-9b14-786379ef138c",
3966
- # "orderLinkId": ""
3967
- # },
3968
- # "retExtMap": {}
3969
- # }
4231
+ # {
4232
+ # "retCode": 0,
4233
+ # "retMsg": "OK",
4234
+ # "result": {
4235
+ # "list": [
4236
+ # {
4237
+ # "category": "option",
4238
+ # "symbol": "ETH-30DEC22-500-C",
4239
+ # "orderId": "b551f227-7059-4fb5-a6a6-699c04dbd2f2",
4240
+ # "orderLinkId": ""
4241
+ # },
4242
+ # {
4243
+ # "category": "option",
4244
+ # "symbol": "ETH-30DEC22-700-C",
4245
+ # "orderId": "fa6a595f-1a57-483f-b9d3-30e9c8235a52",
4246
+ # "orderLinkId": ""
4247
+ # }
4248
+ # ]
4249
+ # },
4250
+ # "retExtInfo": {
4251
+ # "list": [
4252
+ # {
4253
+ # "code": 0,
4254
+ # "msg": "OK"
4255
+ # },
4256
+ # {
4257
+ # "code": 0,
4258
+ # "msg": "OK"
4259
+ # }
4260
+ # ]
4261
+ # },
4262
+ # "time": 1672222808060
4263
+ # }
3970
4264
  #
3971
- result = self.safe_dict(response, 'result', {})
3972
- return self.parse_order(result, market)
4265
+ return self.parse_orders(data)
3973
4266
 
3974
4267
  def cancel_order_request(self, id: str, symbol: Str = None, params={}):
3975
4268
  market = self.market(symbol)
@@ -3982,29 +4275,27 @@ class bybit(Exchange, ImplicitAPI):
3982
4275
  }
3983
4276
  if market['spot']:
3984
4277
  # only works for spot market
3985
- isStop = self.safe_bool_2(params, 'stop', 'trigger', False)
4278
+ isTrigger = self.safe_bool_2(params, 'stop', 'trigger', False)
3986
4279
  params = self.omit(params, ['stop', 'trigger'])
3987
- request['orderFilter'] = 'StopOrder' if isStop else 'Order'
4280
+ request['orderFilter'] = 'StopOrder' if isTrigger else 'Order'
3988
4281
  if id is not None: # The user can also use argument params["orderLinkId"]
3989
4282
  request['orderId'] = id
3990
- if market['spot']:
3991
- request['category'] = 'spot'
3992
- elif market['linear']:
3993
- request['category'] = 'linear'
3994
- elif market['inverse']:
3995
- request['category'] = 'inverse'
3996
- elif market['option']:
3997
- request['category'] = 'option'
4283
+ category = None
4284
+ category, params = self.get_bybit_type('cancelOrderRequest', market, params)
4285
+ request['category'] = category
3998
4286
  return self.extend(request, params)
3999
4287
 
4000
- async def cancel_order(self, id: str, symbol: Str = None, params={}):
4288
+ async def cancel_order(self, id: str, symbol: Str = None, params={}) -> Order:
4001
4289
  """
4002
4290
  cancels an open order
4003
- :see: https://bybit-exchange.github.io/docs/v5/order/cancel-order
4291
+
4292
+ https://bybit-exchange.github.io/docs/v5/order/cancel-order
4293
+
4004
4294
  :param str id: order id
4005
4295
  :param str symbol: unified symbol of the market the order was made in
4006
4296
  :param dict [params]: extra parameters specific to the exchange API endpoint
4007
- :param boolean [params.stop]: *spot only* whether the order is a stop order
4297
+ :param boolean [params.trigger]: *spot only* whether the order is a trigger order
4298
+ :param boolean [params.stop]: alias for trigger
4008
4299
  :param str [params.orderFilter]: *spot only* 'Order' or 'StopOrder' or 'tpslOrder'
4009
4300
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
4010
4301
  """
@@ -4012,11 +4303,6 @@ class bybit(Exchange, ImplicitAPI):
4012
4303
  raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
4013
4304
  await self.load_markets()
4014
4305
  market = self.market(symbol)
4015
- enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
4016
- isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
4017
- isUsdcSettled = market['settle'] == 'USDC'
4018
- if isUsdcSettled and not isUnifiedAccount:
4019
- return await self.cancel_usdc_order(id, symbol, params)
4020
4306
  requestExtended = self.cancel_order_request(id, symbol, params)
4021
4307
  response = await self.privatePostV5OrderCancel(requestExtended)
4022
4308
  #
@@ -4034,10 +4320,12 @@ class bybit(Exchange, ImplicitAPI):
4034
4320
  result = self.safe_dict(response, 'result', {})
4035
4321
  return self.parse_order(result, market)
4036
4322
 
4037
- async def cancel_orders(self, ids, symbol: Str = None, params={}):
4323
+ async def cancel_orders(self, ids, symbol: Str = None, params={}) -> List[Order]:
4038
4324
  """
4039
4325
  cancel multiple orders
4040
- :see: https://bybit-exchange.github.io/docs/v5/order/batch-cancel
4326
+
4327
+ https://bybit-exchange.github.io/docs/v5/order/batch-cancel
4328
+
4041
4329
  :param str[] ids: order ids
4042
4330
  :param str symbol: unified symbol of the market the order was made in
4043
4331
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -4048,6 +4336,10 @@ class bybit(Exchange, ImplicitAPI):
4048
4336
  raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument')
4049
4337
  await self.load_markets()
4050
4338
  market = self.market(symbol)
4339
+ types = await self.is_unified_enabled()
4340
+ enableUnifiedAccount = types[1]
4341
+ if not enableUnifiedAccount:
4342
+ raise NotSupported(self.id + ' cancelOrders() supports UTA accounts only')
4051
4343
  category = None
4052
4344
  category, params = self.get_bybit_type('cancelOrders', market, params)
4053
4345
  if category == 'inverse':
@@ -4109,17 +4401,54 @@ class bybit(Exchange, ImplicitAPI):
4109
4401
  row = self.safe_list(result, 'list', [])
4110
4402
  return self.parse_orders(row, market)
4111
4403
 
4404
+ async def cancel_all_orders_after(self, timeout: Int, params={}):
4405
+ """
4406
+ dead man's switch, cancel all orders after the given timeout
4407
+
4408
+ https://bybit-exchange.github.io/docs/v5/order/dcp
4409
+
4410
+ :param number timeout: time in milliseconds
4411
+ :param dict [params]: extra parameters specific to the exchange API endpoint
4412
+ :param str [params.product]: OPTIONS, DERIVATIVES, SPOT, default is 'DERIVATIVES'
4413
+ :returns dict: the api result
4414
+ """
4415
+ await self.load_markets()
4416
+ request: dict = {
4417
+ 'timeWindow': self.parse_to_int(timeout / 1000),
4418
+ }
4419
+ type: Str = None
4420
+ type, params = self.handle_market_type_and_params('cancelAllOrdersAfter', None, params, 'swap')
4421
+ productMap = {
4422
+ 'spot': 'SPOT',
4423
+ 'swap': 'DERIVATIVES',
4424
+ 'option': 'OPTIONS',
4425
+ }
4426
+ product = self.safe_string(productMap, type, type)
4427
+ request['product'] = product
4428
+ response = await self.privatePostV5OrderDisconnectedCancelAll(self.extend(request, params))
4429
+ #
4430
+ # {
4431
+ # "retCode": 0,
4432
+ # "retMsg": "success"
4433
+ # }
4434
+ #
4435
+ return response
4436
+
4112
4437
  async def cancel_orders_for_symbols(self, orders: List[CancellationRequest], params={}):
4113
4438
  """
4114
4439
  cancel multiple orders for multiple symbols
4115
- :see: https://bybit-exchange.github.io/docs/v5/order/batch-cancel
4116
- :param str[] ids: order ids
4117
- :param str symbol: unified symbol of the market the order was made in
4440
+
4441
+ https://bybit-exchange.github.io/docs/v5/order/batch-cancel
4442
+
4443
+ :param CancellationRequest[] orders: list of order ids with symbol, example [{"id": "a", "symbol": "BTC/USDT"}, {"id": "b", "symbol": "ETH/USDT"}]
4118
4444
  :param dict [params]: extra parameters specific to the exchange API endpoint
4119
- :param str[] [params.clientOrderIds]: client order ids
4120
4445
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
4121
4446
  """
4122
4447
  await self.load_markets()
4448
+ types = await self.is_unified_enabled()
4449
+ enableUnifiedAccount = types[1]
4450
+ if not enableUnifiedAccount:
4451
+ raise NotSupported(self.id + ' cancelOrdersForSymbols() supports UTA accounts only')
4123
4452
  ordersRequests = []
4124
4453
  category = None
4125
4454
  for i in range(0, len(orders)):
@@ -4187,54 +4516,16 @@ class bybit(Exchange, ImplicitAPI):
4187
4516
  row = self.safe_list(result, 'list', [])
4188
4517
  return self.parse_orders(row, None)
4189
4518
 
4190
- async def cancel_all_usdc_orders(self, symbol: Str = None, params={}):
4191
- if symbol is None:
4192
- raise ArgumentsRequired(self.id + ' cancelAllUsdcOrders() requires a symbol argument')
4193
- await self.load_markets()
4194
- market = self.market(symbol)
4195
- request: dict = {
4196
- 'symbol': market['id'],
4197
- }
4198
- response = None
4199
- if market['option']:
4200
- response = await self.privatePostOptionUsdcOpenapiPrivateV1CancelAll(self.extend(request, params))
4201
- else:
4202
- isStop = self.safe_bool_2(params, 'stop', 'trigger', False)
4203
- if isStop:
4204
- request['orderFilter'] = 'StopOrder'
4205
- else:
4206
- request['orderFilter'] = 'Order'
4207
- params = self.omit(params, ['stop', 'trigger'])
4208
- response = await self.privatePostPerpetualUsdcOpenapiPrivateV1CancelAll(self.extend(request, params))
4209
- #
4210
- # {
4211
- # "retCode": 0,
4212
- # "retMsg": "OK",
4213
- # "retExtMap": {},
4214
- # "result": [
4215
- # {
4216
- # "outRequestId": "cancelAll-290119-1652176443114-0",
4217
- # "symbol": "BTC-13MAY22-40000-C",
4218
- # "orderId": "fa6cd740-56ed-477d-9385-90ccbfee49ca",
4219
- # "orderLinkId": "",
4220
- # "errorCode": 0,
4221
- # "errorDesc": ""
4222
- # }
4223
- # ]
4224
- # }
4225
- #
4226
- result = self.safe_value(response, 'result', [])
4227
- if not isinstance(result, list):
4228
- return response
4229
- return self.parse_orders(result, market)
4230
-
4231
4519
  async def cancel_all_orders(self, symbol: Str = None, params={}):
4232
4520
  """
4233
4521
  cancel all open orders
4234
- :see: https://bybit-exchange.github.io/docs/v5/order/cancel-all
4522
+
4523
+ https://bybit-exchange.github.io/docs/v5/order/cancel-all
4524
+
4235
4525
  :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
4236
4526
  :param dict [params]: extra parameters specific to the exchange API endpoint
4237
- :param boolean [params.stop]: True if stop order
4527
+ :param boolean [params.trigger]: True if trigger order
4528
+ :param boolean [params.stop]: alias for trigger
4238
4529
  :param str [params.type]: market type, ['swap', 'option', 'spot']
4239
4530
  :param str [params.subType]: market subType, ['linear', 'inverse']
4240
4531
  :param str [params.baseCoin]: Base coin. Supports linear, inverse & option
@@ -4248,9 +4539,6 @@ class bybit(Exchange, ImplicitAPI):
4248
4539
  request: dict = {}
4249
4540
  if symbol is not None:
4250
4541
  market = self.market(symbol)
4251
- isUsdcSettled = market['settle'] == 'USDC'
4252
- if isUsdcSettled and not isUnifiedAccount:
4253
- return await self.cancel_all_usdc_orders(symbol, params)
4254
4542
  request['symbol'] = market['id']
4255
4543
  type = None
4256
4544
  type, params = self.get_bybit_type('cancelAllOrders', market, params)
@@ -4262,9 +4550,9 @@ class bybit(Exchange, ImplicitAPI):
4262
4550
  if symbol is None and baseCoin is None:
4263
4551
  defaultSettle = self.safe_string(self.options, 'defaultSettle', 'USDT')
4264
4552
  request['settleCoin'] = self.safe_string(params, 'settleCoin', defaultSettle)
4265
- isStop = self.safe_bool_2(params, 'stop', 'trigger', False)
4553
+ isTrigger = self.safe_bool_2(params, 'stop', 'trigger', False)
4266
4554
  params = self.omit(params, ['stop', 'trigger'])
4267
- if isStop:
4555
+ if isTrigger:
4268
4556
  request['orderFilter'] = 'StopOrder'
4269
4557
  response = await self.privatePostV5OrderCancelAll(self.extend(request, params))
4270
4558
  #
@@ -4298,95 +4586,16 @@ class bybit(Exchange, ImplicitAPI):
4298
4586
  result = self.safe_dict(response, 'result', {})
4299
4587
  orders = self.safe_list(result, 'list')
4300
4588
  if not isinstance(orders, list):
4301
- return response
4589
+ return [self.safe_order({'info': response})]
4302
4590
  return self.parse_orders(orders, market)
4303
4591
 
4304
- async def fetch_usdc_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
4305
- await self.load_markets()
4306
- market = None
4307
- request: dict = {
4308
- # 'category': '', # Type. PERPETUAL, OPTION
4309
- # 'symbol': '', # Contract name
4310
- # 'baseCoin': '', # Base currency
4311
- # 'orderId': '', # Order ID
4312
- # 'orderLinkId': '', # Custom ID used for cross-platform strategy remarks; a max. of 32 characters
4313
- # 'orderStatus': '', # Order status
4314
- # 'orderFilter': '', # refer to Order Filter
4315
- # 'direction': '', # prev: prev page, next: next page.
4316
- # 'limit': 0, # Limit for data size per page, max size is 50. Default 20 pieces of data per page
4317
- # 'cursor': '', # API pass-through
4318
- }
4319
- if symbol is not None:
4320
- market = self.market(symbol)
4321
- request['symbol'] = market['id']
4322
- type = None
4323
- type, params = self.handle_market_type_and_params('fetchOrders', market, params)
4324
- if type == 'swap':
4325
- request['category'] = 'PERPETUAL'
4326
- else:
4327
- request['category'] = 'OPTION'
4328
- isStop = self.safe_bool_2(params, 'stop', 'trigger', False)
4329
- params = self.omit(params, ['stop', 'trigger'])
4330
- if isStop:
4331
- request['orderFilter'] = 'StopOrder'
4332
- if limit is not None:
4333
- request['limit'] = limit
4334
- response = await self.privatePostOptionUsdcOpenapiPrivateV1QueryOrderHistory(self.extend(request, params))
4335
- #
4336
- # {
4337
- # "result": {
4338
- # "cursor": "640034d1-97ec-4382-9983-694898c03ba3%3A1640854950675%2C640034d1-97ec-4382-9983-694898c03ba3%3A1640854950675",
4339
- # "resultTotalSize": 1,
4340
- # "dataList": [
4341
- # {
4342
- # "symbol": "ETHPERP",
4343
- # "orderType": "Limit",
4344
- # "orderLinkId": "",
4345
- # "orderId": "c04ad17d-ca85-45d1-859e-561e7236f6db",
4346
- # "cancelType": "UNKNOWN",
4347
- # "stopOrderType": "UNKNOWN",
4348
- # "orderStatus": "Filled",
4349
- # "updateTimeStamp": "1666178097006",
4350
- # "takeProfit": "0.0000",
4351
- # "cumExecValue": "12.9825",
4352
- # "createdAt": "1666178096996",
4353
- # "blockTradeId": "",
4354
- # "orderPnl": "",
4355
- # "price": "1300.0",
4356
- # "tpTriggerBy": "UNKNOWN",
4357
- # "timeInForce": "GoodTillCancel",
4358
- # "updatedAt": "1666178097006",
4359
- # "basePrice": "",
4360
- # "realisedPnl": "0.0000",
4361
- # "side": "Buy",
4362
- # "triggerPrice": "0.0",
4363
- # "cumExecFee": "0.0078",
4364
- # "leavesQty": "0.000",
4365
- # "cashFlow": "",
4366
- # "slTriggerBy": "UNKNOWN",
4367
- # "iv": "",
4368
- # "closeOnTrigger": "UNKNOWN",
4369
- # "cumExecQty": "0.010",
4370
- # "reduceOnly": 0,
4371
- # "qty": "0.010",
4372
- # "stopLoss": "0.0000",
4373
- # "triggerBy": "UNKNOWN",
4374
- # "orderIM": ""
4375
- # }
4376
- # ]
4377
- # },
4378
- # "retCode": 0,
4379
- # "retMsg": "Success."
4380
- # }
4381
- #
4382
- result = self.safe_dict(response, 'result', {})
4383
- data = self.safe_list(result, 'dataList', [])
4384
- return self.parse_orders(data, market, since, limit)
4385
-
4386
- async def fetch_order_classic(self, id: str, symbol: Str = None, params={}):
4592
+ async def fetch_order_classic(self, id: str, symbol: Str = None, params={}) -> Order:
4387
4593
  """
4388
4594
  fetches information on an order made by the user *classic accounts only*
4389
- :see: https://bybit-exchange.github.io/docs/v5/order/order-list
4595
+
4596
+ https://bybit-exchange.github.io/docs/v5/order/order-list
4597
+
4598
+ :param str id: the order id
4390
4599
  :param str symbol: unified symbol of the market the order was made in
4391
4600
  :param dict [params]: extra parameters specific to the exchange API endpoint
4392
4601
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
@@ -4404,7 +4613,7 @@ class bybit(Exchange, ImplicitAPI):
4404
4613
  length = len(result)
4405
4614
  if length == 0:
4406
4615
  isTrigger = self.safe_bool_n(params, ['trigger', 'stop'], False)
4407
- extra = '' if isTrigger else 'If you are trying to fetch SL/TP conditional order, you might try setting params["trigger"] = True'
4616
+ extra = '' if isTrigger else ' If you are trying to fetch SL/TP conditional order, you might try setting params["trigger"] = True'
4408
4617
  raise OrderNotFound('Order ' + str(id) + ' was not found.' + extra)
4409
4618
  if length > 1:
4410
4619
  raise InvalidOrder(self.id + ' returned more than one order')
@@ -4412,28 +4621,106 @@ class bybit(Exchange, ImplicitAPI):
4412
4621
 
4413
4622
  async def fetch_order(self, id: str, symbol: Str = None, params={}) -> Order:
4414
4623
  """
4415
- *classic accounts only/ spot not supported* fetches information on an order made by the user *classic accounts only*
4416
- :see: https://bybit-exchange.github.io/docs/v5/order/order-list
4624
+ classic accounts only/ spot not supported* fetches information on an order made by the user *classic accounts only*
4625
+
4626
+ https://bybit-exchange.github.io/docs/v5/order/order-list
4627
+
4628
+ :param str id: the order id
4417
4629
  :param str symbol: unified symbol of the market the order was made in
4418
4630
  :param dict [params]: extra parameters specific to the exchange API endpoint
4631
+ :param dict [params.acknowledged]: to suppress the warning, set to True
4419
4632
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
4420
4633
  """
4421
- res = await self.is_unified_enabled()
4422
- enableUnifiedAccount = self.safe_bool(res, 1)
4423
- if enableUnifiedAccount:
4424
- raise NotSupported(self.id + ' fetchOrder() is not supported after the 5/02 update for UTA accounts, please use fetchOpenOrder or fetchClosedOrder')
4425
- return await self.fetch_order_classic(id, symbol, params)
4634
+ await self.load_markets()
4635
+ enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
4636
+ isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
4637
+ if not isUnifiedAccount:
4638
+ return await self.fetch_order_classic(id, symbol, params)
4639
+ acknowledge = False
4640
+ acknowledge, params = self.handle_option_and_params(params, 'fetchOrder', 'acknowledged')
4641
+ if not acknowledge:
4642
+ raise ArgumentsRequired(self.id + ' fetchOrder() can only access an order if it is in last 500 orders(of any status) for your account. Set params["acknowledged"] = True to hide self warning. Alternatively, we suggest to use fetchOpenOrder or fetchClosedOrder')
4643
+ market = self.market(symbol)
4644
+ marketType = None
4645
+ marketType, params = self.get_bybit_type('fetchOrder', market, params)
4646
+ request: dict = {
4647
+ 'symbol': market['id'],
4648
+ 'orderId': id,
4649
+ 'category': marketType,
4650
+ }
4651
+ isTrigger = None
4652
+ isTrigger, params = self.handle_param_bool_2(params, 'trigger', 'stop', False)
4653
+ if isTrigger:
4654
+ request['orderFilter'] = 'StopOrder'
4655
+ response = await self.privateGetV5OrderRealtime(self.extend(request, params))
4656
+ #
4657
+ # {
4658
+ # "retCode": 0,
4659
+ # "retMsg": "OK",
4660
+ # "result": {
4661
+ # "nextPageCursor": "1321052653536515584%3A1672217748287%2C1321052653536515584%3A1672217748287",
4662
+ # "category": "spot",
4663
+ # "list": [
4664
+ # {
4665
+ # "symbol": "ETHUSDT",
4666
+ # "orderType": "Limit",
4667
+ # "orderLinkId": "1672217748277652",
4668
+ # "orderId": "1321052653536515584",
4669
+ # "cancelType": "UNKNOWN",
4670
+ # "avgPrice": "",
4671
+ # "stopOrderType": "tpslOrder",
4672
+ # "lastPriceOnCreated": "",
4673
+ # "orderStatus": "Cancelled",
4674
+ # "takeProfit": "",
4675
+ # "cumExecValue": "0",
4676
+ # "triggerDirection": 0,
4677
+ # "isLeverage": "0",
4678
+ # "rejectReason": "",
4679
+ # "price": "1000",
4680
+ # "orderIv": "",
4681
+ # "createdTime": "1672217748287",
4682
+ # "tpTriggerBy": "",
4683
+ # "positionIdx": 0,
4684
+ # "timeInForce": "GTC",
4685
+ # "leavesValue": "500",
4686
+ # "updatedTime": "1672217748287",
4687
+ # "side": "Buy",
4688
+ # "triggerPrice": "1500",
4689
+ # "cumExecFee": "0",
4690
+ # "leavesQty": "0",
4691
+ # "slTriggerBy": "",
4692
+ # "closeOnTrigger": False,
4693
+ # "cumExecQty": "0",
4694
+ # "reduceOnly": False,
4695
+ # "qty": "0.5",
4696
+ # "stopLoss": "",
4697
+ # "triggerBy": "1192.5"
4698
+ # }
4699
+ # ]
4700
+ # },
4701
+ # "retExtInfo": {},
4702
+ # "time": 1672219526294
4703
+ # }
4704
+ #
4705
+ result = self.safe_dict(response, 'result', {})
4706
+ innerList = self.safe_list(result, 'list', [])
4707
+ if len(innerList) == 0:
4708
+ extra = '' if isTrigger else ' If you are trying to fetch SL/TP conditional order, you might try setting params["trigger"] = True'
4709
+ raise OrderNotFound('Order ' + str(id) + ' was not found.' + extra)
4710
+ order = self.safe_dict(innerList, 0, {})
4711
+ return self.parse_order(order, market)
4426
4712
 
4427
4713
  async def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
4428
4714
  res = await self.is_unified_enabled()
4429
4715
  """
4430
4716
  *classic accounts only/ spot not supported* fetches information on multiple orders made by the user *classic accounts only/ spot not supported*
4431
- :see: https://bybit-exchange.github.io/docs/v5/order/order-list
4717
+ https://bybit-exchange.github.io/docs/v5/order/order-list
4432
4718
  :param str symbol: unified market symbol of the market orders were made in
4433
4719
  :param int [since]: the earliest time in ms to fetch orders for
4434
4720
  :param int [limit]: the maximum number of order structures to retrieve
4435
4721
  :param dict [params]: extra parameters specific to the exchange API endpoint
4436
- :param boolean [params.stop]: True if stop order
4722
+ :param boolean [params.trigger]: True if trigger order
4723
+ :param boolean [params.stop]: alias for trigger
4437
4724
  :param str [params.type]: market type, ['swap', 'option']
4438
4725
  :param str [params.subType]: market subType, ['linear', 'inverse']
4439
4726
  :param str [params.orderFilter]: 'Order' or 'StopOrder' or 'tpslOrder'
@@ -4449,12 +4736,15 @@ class bybit(Exchange, ImplicitAPI):
4449
4736
  async def fetch_orders_classic(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
4450
4737
  """
4451
4738
  fetches information on multiple orders made by the user *classic accounts only*
4452
- :see: https://bybit-exchange.github.io/docs/v5/order/order-list
4739
+
4740
+ https://bybit-exchange.github.io/docs/v5/order/order-list
4741
+
4453
4742
  :param str symbol: unified market symbol of the market orders were made in
4454
4743
  :param int [since]: the earliest time in ms to fetch orders for
4455
4744
  :param int [limit]: the maximum number of order structures to retrieve
4456
4745
  :param dict [params]: extra parameters specific to the exchange API endpoint
4457
- :param boolean [params.stop]: True if stop order
4746
+ :param boolean [params.trigger]: True if trigger order
4747
+ :param boolean [params.stop]: alias for trigger
4458
4748
  :param str [params.type]: market type, ['swap', 'option', 'spot']
4459
4749
  :param str [params.subType]: market subType, ['linear', 'inverse']
4460
4750
  :param str [params.orderFilter]: 'Order' or 'StopOrder' or 'tpslOrder'
@@ -4467,25 +4757,19 @@ class bybit(Exchange, ImplicitAPI):
4467
4757
  paginate, params = self.handle_option_and_params(params, 'fetchOrders', 'paginate')
4468
4758
  if paginate:
4469
4759
  return await self.fetch_paginated_call_cursor('fetchOrders', symbol, since, limit, params, 'nextPageCursor', 'cursor', None, 50)
4470
- enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
4471
- isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
4472
4760
  request: dict = {}
4473
4761
  market = None
4474
- isUsdcSettled = False
4475
4762
  if symbol is not None:
4476
4763
  market = self.market(symbol)
4477
- isUsdcSettled = market['settle'] == 'USDC'
4478
4764
  request['symbol'] = market['id']
4479
4765
  type = None
4480
4766
  type, params = self.get_bybit_type('fetchOrders', market, params)
4481
- if ((type == 'option') or isUsdcSettled) and not isUnifiedAccount:
4482
- return await self.fetch_usdc_orders(symbol, since, limit, params)
4483
4767
  if type == 'spot':
4484
4768
  raise NotSupported(self.id + ' fetchOrders() is not supported for spot markets')
4485
4769
  request['category'] = type
4486
- isStop = self.safe_bool_n(params, ['trigger', 'stop'], False)
4770
+ isTrigger = self.safe_bool_n(params, ['trigger', 'stop'], False)
4487
4771
  params = self.omit(params, ['trigger', 'stop'])
4488
- if isStop:
4772
+ if isTrigger:
4489
4773
  request['orderFilter'] = 'StopOrder'
4490
4774
  if limit is not None:
4491
4775
  request['limit'] = limit
@@ -4550,14 +4834,17 @@ class bybit(Exchange, ImplicitAPI):
4550
4834
  data = self.add_pagination_cursor_to_result(response)
4551
4835
  return self.parse_orders(data, market, since, limit)
4552
4836
 
4553
- async def fetch_closed_order(self, id: str, symbol: Str = None, params={}):
4837
+ async def fetch_closed_order(self, id: str, symbol: Str = None, params={}) -> Order:
4554
4838
  """
4555
4839
  fetches information on a closed order made by the user
4556
- :see: https://bybit-exchange.github.io/docs/v5/order/order-list
4840
+
4841
+ https://bybit-exchange.github.io/docs/v5/order/order-list
4842
+
4557
4843
  :param str id: order id
4558
4844
  :param str [symbol]: unified symbol of the market the order was made in
4559
4845
  :param dict [params]: extra parameters specific to the exchange API endpoint
4560
- :param boolean [params.stop]: set to True for fetching a closed stop order
4846
+ :param boolean [params.trigger]: set to True for fetching a closed trigger order
4847
+ :param boolean [params.stop]: alias for trigger
4561
4848
  :param str [params.type]: market type, ['swap', 'option', 'spot']
4562
4849
  :param str [params.subType]: market subType, ['linear', 'inverse']
4563
4850
  :param str [params.orderFilter]: 'Order' or 'StopOrder' or 'tpslOrder'
@@ -4571,20 +4858,23 @@ class bybit(Exchange, ImplicitAPI):
4571
4858
  length = len(result)
4572
4859
  if length == 0:
4573
4860
  isTrigger = self.safe_bool_n(params, ['trigger', 'stop'], False)
4574
- extra = '' if isTrigger else 'If you are trying to fetch SL/TP conditional order, you might try setting params["trigger"] = True'
4861
+ extra = '' if isTrigger else ' If you are trying to fetch SL/TP conditional order, you might try setting params["trigger"] = True'
4575
4862
  raise OrderNotFound('Order ' + str(id) + ' was not found.' + extra)
4576
4863
  if length > 1:
4577
4864
  raise InvalidOrder(self.id + ' returned more than one order')
4578
4865
  return self.safe_value(result, 0)
4579
4866
 
4580
- async def fetch_open_order(self, id: str, symbol: Str = None, params={}):
4867
+ async def fetch_open_order(self, id: str, symbol: Str = None, params={}) -> Order:
4581
4868
  """
4582
4869
  fetches information on an open order made by the user
4583
- :see: https://bybit-exchange.github.io/docs/v5/order/open-order
4870
+
4871
+ https://bybit-exchange.github.io/docs/v5/order/open-order
4872
+
4584
4873
  :param str id: order id
4585
4874
  :param str [symbol]: unified symbol of the market the order was made in
4586
4875
  :param dict [params]: extra parameters specific to the exchange API endpoint
4587
- :param boolean [params.stop]: set to True for fetching an open stop order
4876
+ :param boolean [params.trigger]: set to True for fetching an open trigger order
4877
+ :param boolean [params.stop]: alias for trigger
4588
4878
  :param str [params.type]: market type, ['swap', 'option', 'spot']
4589
4879
  :param str [params.subType]: market subType, ['linear', 'inverse']
4590
4880
  :param str [params.baseCoin]: Base coin. Supports linear, inverse & option
@@ -4600,7 +4890,7 @@ class bybit(Exchange, ImplicitAPI):
4600
4890
  length = len(result)
4601
4891
  if length == 0:
4602
4892
  isTrigger = self.safe_bool_n(params, ['trigger', 'stop'], False)
4603
- extra = '' if isTrigger else 'If you are trying to fetch SL/TP conditional order, you might try setting params["trigger"] = True'
4893
+ extra = '' if isTrigger else ' If you are trying to fetch SL/TP conditional order, you might try setting params["trigger"] = True'
4604
4894
  raise OrderNotFound('Order ' + str(id) + ' was not found.' + extra)
4605
4895
  if length > 1:
4606
4896
  raise InvalidOrder(self.id + ' returned more than one order')
@@ -4609,12 +4899,15 @@ class bybit(Exchange, ImplicitAPI):
4609
4899
  async def fetch_canceled_and_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
4610
4900
  """
4611
4901
  fetches information on multiple canceled and closed orders made by the user
4612
- :see: https://bybit-exchange.github.io/docs/v5/order/order-list
4902
+
4903
+ https://bybit-exchange.github.io/docs/v5/order/order-list
4904
+
4613
4905
  :param str [symbol]: unified market symbol of the market orders were made in
4614
4906
  :param int [since]: the earliest time in ms to fetch orders for
4615
4907
  :param int [limit]: the maximum number of order structures to retrieve
4616
4908
  :param dict [params]: extra parameters specific to the exchange API endpoint
4617
- :param boolean [params.stop]: set to True for fetching stop orders
4909
+ :param boolean [params.trigger]: set to True for fetching trigger orders
4910
+ :param boolean [params.stop]: alias for trigger
4618
4911
  :param str [params.type]: market type, ['swap', 'option', 'spot']
4619
4912
  :param str [params.subType]: market subType, ['linear', 'inverse']
4620
4913
  :param str [params.orderFilter]: 'Order' or 'StopOrder' or 'tpslOrder'
@@ -4627,23 +4920,17 @@ class bybit(Exchange, ImplicitAPI):
4627
4920
  paginate, params = self.handle_option_and_params(params, 'fetchCanceledAndClosedOrders', 'paginate')
4628
4921
  if paginate:
4629
4922
  return await self.fetch_paginated_call_cursor('fetchCanceledAndClosedOrders', symbol, since, limit, params, 'nextPageCursor', 'cursor', None, 50)
4630
- enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
4631
- isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
4632
4923
  request: dict = {}
4633
4924
  market = None
4634
- isUsdcSettled = False
4635
4925
  if symbol is not None:
4636
4926
  market = self.market(symbol)
4637
- isUsdcSettled = market['settle'] == 'USDC'
4638
4927
  request['symbol'] = market['id']
4639
4928
  type = None
4640
4929
  type, params = self.get_bybit_type('fetchCanceledAndClosedOrders', market, params)
4641
- if ((type == 'option') or isUsdcSettled) and not isUnifiedAccount:
4642
- return await self.fetch_usdc_orders(symbol, since, limit, params)
4643
4930
  request['category'] = type
4644
- isStop = self.safe_bool_n(params, ['trigger', 'stop'], False)
4931
+ isTrigger = self.safe_bool_n(params, ['trigger', 'stop'], False)
4645
4932
  params = self.omit(params, ['trigger', 'stop'])
4646
- if isStop:
4933
+ if isTrigger:
4647
4934
  request['orderFilter'] = 'StopOrder'
4648
4935
  if limit is not None:
4649
4936
  request['limit'] = limit
@@ -4711,12 +4998,15 @@ class bybit(Exchange, ImplicitAPI):
4711
4998
  async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
4712
4999
  """
4713
5000
  fetches information on multiple closed orders made by the user
4714
- :see: https://bybit-exchange.github.io/docs/v5/order/order-list
5001
+
5002
+ https://bybit-exchange.github.io/docs/v5/order/order-list
5003
+
4715
5004
  :param str [symbol]: unified market symbol of the market orders were made in
4716
5005
  :param int [since]: the earliest time in ms to fetch orders for
4717
5006
  :param int [limit]: the maximum number of order structures to retrieve
4718
5007
  :param dict [params]: extra parameters specific to the exchange API endpoint
4719
- :param boolean [params.stop]: set to True for fetching closed stop orders
5008
+ :param boolean [params.trigger]: set to True for fetching closed trigger orders
5009
+ :param boolean [params.stop]: alias for trigger
4720
5010
  :param str [params.type]: market type, ['swap', 'option', 'spot']
4721
5011
  :param str [params.subType]: market subType, ['linear', 'inverse']
4722
5012
  :param str [params.orderFilter]: 'Order' or 'StopOrder' or 'tpslOrder'
@@ -4730,15 +5020,18 @@ class bybit(Exchange, ImplicitAPI):
4730
5020
  }
4731
5021
  return await self.fetch_canceled_and_closed_orders(symbol, since, limit, self.extend(request, params))
4732
5022
 
4733
- async def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
5023
+ async def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
4734
5024
  """
4735
5025
  fetches information on multiple canceled orders made by the user
4736
- :see: https://bybit-exchange.github.io/docs/v5/order/order-list
5026
+
5027
+ https://bybit-exchange.github.io/docs/v5/order/order-list
5028
+
4737
5029
  :param str [symbol]: unified market symbol of the market orders were made in
4738
5030
  :param int [since]: timestamp in ms of the earliest order, default is None
4739
5031
  :param int [limit]: max number of orders to return, default is None
4740
5032
  :param dict [params]: extra parameters specific to the exchange API endpoint
4741
- :param boolean [params.stop]: True if stop order
5033
+ :param boolean [params.trigger]: True if trigger order
5034
+ :param boolean [params.stop]: alias for trigger
4742
5035
  :param str [params.type]: market type, ['swap', 'option', 'spot']
4743
5036
  :param str [params.subType]: market subType, ['linear', 'inverse']
4744
5037
  :param str [params.orderFilter]: 'Order' or 'StopOrder' or 'tpslOrder'
@@ -4752,72 +5045,35 @@ class bybit(Exchange, ImplicitAPI):
4752
5045
  }
4753
5046
  return await self.fetch_canceled_and_closed_orders(symbol, since, limit, self.extend(request, params))
4754
5047
 
4755
- async def fetch_usdc_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
4756
- await self.load_markets()
4757
- request: dict = {}
4758
- market = None
4759
- if symbol is not None:
4760
- market = self.market(symbol)
4761
- request['symbol'] = market['id']
4762
- type = None
4763
- type, params = self.handle_market_type_and_params('fetchUsdcOpenOrders', market, params)
4764
- request['category'] = 'perpetual' if (type == 'swap') else 'option'
4765
- response = await self.privatePostOptionUsdcOpenapiPrivateV1QueryActiveOrders(self.extend(request, params))
4766
- result = self.safe_dict(response, 'result', {})
4767
- orders = self.safe_list(result, 'dataList', [])
4768
- #
4769
- # {
4770
- # "retCode": 0,
4771
- # "retMsg": "OK",
4772
- # "result": {
4773
- # "resultTotalSize": 1,
4774
- # "cursor": "id%3D1662019818569%23df31e03b-fc00-4b4c-bd1c-b97fd72b5c5c",
4775
- # "dataList": [
4776
- # {
4777
- # "orderId": "df31e03b-fc00-4b4c-bd1c-b97fd72b5c5c",
4778
- # "orderLinkId": "",
4779
- # "symbol": "BTC-2SEP22-18000-C",
4780
- # "orderStatus": "New",
4781
- # "orderPrice": "500",
4782
- # "side": "Buy",
4783
- # "remainingQty": "0.1",
4784
- # "orderType": "Limit",
4785
- # "qty": "0.1",
4786
- # "iv": "0.0000",
4787
- # "cancelType": "",
4788
- # "updateTimestamp": "1662019818579"
4789
- # }
4790
- # ]
4791
- # }
4792
- # }
4793
- #
4794
- return self.parse_orders(orders, market, since, limit)
4795
-
4796
5048
  async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
4797
5049
  """
4798
5050
  fetch all unfilled currently open orders
4799
- :see: https://bybit-exchange.github.io/docs/v5/order/open-order
5051
+
5052
+ https://bybit-exchange.github.io/docs/v5/order/open-order
5053
+
4800
5054
  :param str symbol: unified market symbol
4801
5055
  :param int [since]: the earliest time in ms to fetch open orders for
4802
5056
  :param int [limit]: the maximum number of open orders structures to retrieve
4803
5057
  :param dict [params]: extra parameters specific to the exchange API endpoint
4804
- :param boolean [params.stop]: set to True for fetching open stop orders
5058
+ :param boolean [params.trigger]: set to True for fetching open trigger orders
5059
+ :param boolean [params.stop]: alias for trigger
4805
5060
  :param str [params.type]: market type, ['swap', 'option', 'spot']
4806
5061
  :param str [params.subType]: market subType, ['linear', 'inverse']
4807
5062
  :param str [params.baseCoin]: Base coin. Supports linear, inverse & option
4808
5063
  :param str [params.settleCoin]: Settle coin. Supports linear, inverse & option
4809
5064
  :param str [params.orderFilter]: 'Order' or 'StopOrder' or 'tpslOrder'
5065
+ :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
4810
5066
  :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
4811
5067
  """
4812
5068
  await self.load_markets()
4813
- enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
4814
- isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
5069
+ paginate = False
5070
+ paginate, params = self.handle_option_and_params(params, 'fetchOpenOrders', 'paginate')
5071
+ if paginate:
5072
+ return await self.fetch_paginated_call_cursor('fetchOpenOrders', symbol, since, limit, params, 'nextPageCursor', 'cursor', None, 50)
4815
5073
  request: dict = {}
4816
5074
  market = None
4817
- isUsdcSettled = False
4818
5075
  if symbol is not None:
4819
5076
  market = self.market(symbol)
4820
- isUsdcSettled = market['settle'] == 'USDC'
4821
5077
  request['symbol'] = market['id']
4822
5078
  type = None
4823
5079
  type, params = self.get_bybit_type('fetchOpenOrders', market, params)
@@ -4827,13 +5083,10 @@ class bybit(Exchange, ImplicitAPI):
4827
5083
  defaultSettle = self.safe_string(self.options, 'defaultSettle', 'USDT')
4828
5084
  settleCoin = self.safe_string(params, 'settleCoin', defaultSettle)
4829
5085
  request['settleCoin'] = settleCoin
4830
- isUsdcSettled = (settleCoin == 'USDC')
4831
- if ((type == 'option') or isUsdcSettled) and not isUnifiedAccount:
4832
- return await self.fetch_usdc_open_orders(symbol, since, limit, params)
4833
5086
  request['category'] = type
4834
- isStop = self.safe_bool_2(params, 'stop', 'trigger', False)
5087
+ isTrigger = self.safe_bool_2(params, 'stop', 'trigger', False)
4835
5088
  params = self.omit(params, ['stop', 'trigger'])
4836
- if isStop:
5089
+ if isTrigger:
4837
5090
  request['orderFilter'] = 'StopOrder'
4838
5091
  if limit is not None:
4839
5092
  request['limit'] = limit
@@ -4890,10 +5143,12 @@ class bybit(Exchange, ImplicitAPI):
4890
5143
  data = self.add_pagination_cursor_to_result(response)
4891
5144
  return self.parse_orders(data, market, since, limit)
4892
5145
 
4893
- async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
5146
+ async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
4894
5147
  """
4895
5148
  fetch all the trades made from a single order
4896
- :see: https://bybit-exchange.github.io/docs/v5/position/execution
5149
+
5150
+ https://bybit-exchange.github.io/docs/v5/position/execution
5151
+
4897
5152
  :param str id: order id
4898
5153
  :param str symbol: unified market symbol
4899
5154
  :param int [since]: the earliest time in ms to fetch trades for
@@ -4910,53 +5165,12 @@ class bybit(Exchange, ImplicitAPI):
4910
5165
  params = self.omit(params, ['clientOrderId', 'orderLinkId'])
4911
5166
  return await self.fetch_my_trades(symbol, since, limit, self.extend(request, params))
4912
5167
 
4913
- async def fetch_my_usdc_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
4914
- await self.load_markets()
4915
- market = None
4916
- request: dict = {}
4917
- if symbol is not None:
4918
- market = self.market(symbol)
4919
- request['symbol'] = market['id']
4920
- request['category'] = 'OPTION' if market['option'] else 'PERPETUAL'
4921
- else:
4922
- request['category'] = 'PERPETUAL'
4923
- response = await self.privatePostOptionUsdcOpenapiPrivateV1ExecutionList(self.extend(request, params))
4924
- #
4925
- # {
4926
- # "result": {
4927
- # "cursor": "29%3A1%2C28%3A1",
4928
- # "resultTotalSize": 2,
4929
- # "dataList": [
4930
- # {
4931
- # "symbol": "ETHPERP",
4932
- # "orderLinkId": "",
4933
- # "side": "Sell",
4934
- # "orderId": "d83f8b4d-2f60-4e04-a64a-a3f207989dc6",
4935
- # "execFee": "0.0210",
4936
- # "feeRate": "0.000600",
4937
- # "blockTradeId": "",
4938
- # "tradeTime": "1669196423581",
4939
- # "execPrice": "1161.45",
4940
- # "lastLiquidityInd": "TAKER",
4941
- # "execValue": "34.8435",
4942
- # "execType": "Trade",
4943
- # "execQty": "0.030",
4944
- # "tradeId": "d9aa8590-9e6a-575e-a1be-d6261e6ed2e5"
4945
- # }, ...
4946
- # ]
4947
- # },
4948
- # "retCode": 0,
4949
- # "retMsg": "Success."
4950
- # }
4951
- #
4952
- result = self.safe_dict(response, 'result', {})
4953
- dataList = self.safe_list(result, 'dataList', [])
4954
- return self.parse_trades(dataList, market, since, limit)
4955
-
4956
- async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
5168
+ async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
4957
5169
  """
4958
5170
  fetch all trades made by the user
4959
- :see: https://bybit-exchange.github.io/docs/api-explorer/v5/position/execution
5171
+
5172
+ https://bybit-exchange.github.io/docs/api-explorer/v5/position/execution
5173
+
4960
5174
  :param str symbol: unified market symbol
4961
5175
  :param int [since]: the earliest time in ms to fetch trades for
4962
5176
  :param int [limit]: the maximum number of trades structures to retrieve
@@ -4971,21 +5185,15 @@ class bybit(Exchange, ImplicitAPI):
4971
5185
  paginate, params = self.handle_option_and_params(params, 'fetchMyTrades', 'paginate')
4972
5186
  if paginate:
4973
5187
  return await self.fetch_paginated_call_cursor('fetchMyTrades', symbol, since, limit, params, 'nextPageCursor', 'cursor', None, 100)
4974
- enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
4975
- isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
4976
5188
  request: dict = {
4977
5189
  'execType': 'Trade',
4978
5190
  }
4979
5191
  market = None
4980
- isUsdcSettled = False
4981
5192
  if symbol is not None:
4982
5193
  market = self.market(symbol)
4983
- isUsdcSettled = market['settle'] == 'USDC'
4984
5194
  request['symbol'] = market['id']
4985
5195
  type = None
4986
5196
  type, params = self.get_bybit_type('fetchMyTrades', market, params)
4987
- if ((type == 'option') or isUsdcSettled) and not isUnifiedAccount:
4988
- return await self.fetch_my_usdc_trades(symbol, since, limit, params)
4989
5197
  request['category'] = type
4990
5198
  if limit is not None:
4991
5199
  request['limit'] = limit
@@ -5036,7 +5244,7 @@ class bybit(Exchange, ImplicitAPI):
5036
5244
  trades = self.add_pagination_cursor_to_result(response)
5037
5245
  return self.parse_trades(trades, market, since, limit)
5038
5246
 
5039
- def parse_deposit_address(self, depositAddress, currency: Currency = None):
5247
+ def parse_deposit_address(self, depositAddress, currency: Currency = None) -> DepositAddress:
5040
5248
  #
5041
5249
  # {
5042
5250
  # "chainType": "ERC20",
@@ -5048,20 +5256,21 @@ class bybit(Exchange, ImplicitAPI):
5048
5256
  address = self.safe_string(depositAddress, 'addressDeposit')
5049
5257
  tag = self.safe_string(depositAddress, 'tagDeposit')
5050
5258
  code = self.safe_string(currency, 'code')
5051
- chain = self.safe_string(depositAddress, 'chain')
5052
5259
  self.check_address(address)
5053
5260
  return {
5261
+ 'info': depositAddress,
5054
5262
  'currency': code,
5263
+ 'network': self.network_id_to_code(self.safe_string(depositAddress, 'chain'), code),
5055
5264
  'address': address,
5056
5265
  'tag': tag,
5057
- 'network': chain,
5058
- 'info': depositAddress,
5059
5266
  }
5060
5267
 
5061
- async def fetch_deposit_addresses_by_network(self, code: str, params={}):
5268
+ async def fetch_deposit_addresses_by_network(self, code: str, params={}) -> List[DepositAddress]:
5062
5269
  """
5063
5270
  fetch a dictionary of addresses for a currency, indexed by network
5064
- :see: https://bybit-exchange.github.io/docs/v5/asset/master-deposit-addr
5271
+
5272
+ https://bybit-exchange.github.io/docs/v5/asset/master-deposit-addr
5273
+
5065
5274
  :param str code: unified currency code of the currency for the deposit address
5066
5275
  :param dict [params]: extra parameters specific to the exchange API endpoint
5067
5276
  :returns dict: a dictionary of `address structures <https://docs.ccxt.com/#/?id=address-structure>` indexed by the network
@@ -5071,6 +5280,10 @@ class bybit(Exchange, ImplicitAPI):
5071
5280
  request: dict = {
5072
5281
  'coin': currency['id'],
5073
5282
  }
5283
+ networkCode = None
5284
+ networkCode, params = self.handle_network_code_and_params(params)
5285
+ if networkCode is not None:
5286
+ request['chainType'] = self.network_code_to_id(networkCode, code)
5074
5287
  response = await self.privateGetV5AssetDepositQueryAddress(self.extend(request, params))
5075
5288
  #
5076
5289
  # {
@@ -5100,60 +5313,35 @@ class bybit(Exchange, ImplicitAPI):
5100
5313
  })
5101
5314
  return self.index_by(parsed, 'network')
5102
5315
 
5103
- async def fetch_deposit_address(self, code: str, params={}):
5316
+ async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
5104
5317
  """
5105
5318
  fetch the deposit address for a currency associated with self account
5106
- :see: https://bybit-exchange.github.io/docs/v5/asset/master-deposit-addr
5319
+
5320
+ https://bybit-exchange.github.io/docs/v5/asset/master-deposit-addr
5321
+
5107
5322
  :param str code: unified currency code
5108
5323
  :param dict [params]: extra parameters specific to the exchange API endpoint
5109
5324
  :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
5110
5325
  """
5111
5326
  await self.load_markets()
5112
- networkCode, query = self.handle_network_code_and_params(params)
5113
- networkId = self.network_code_to_id(networkCode)
5114
5327
  currency = self.currency(code)
5115
- request: dict = {
5116
- 'coin': currency['id'],
5117
- }
5118
- if networkId is not None:
5119
- request['chainType'] = networkId
5120
- response = await self.privateGetV5AssetDepositQueryAddress(self.extend(request, query))
5121
- #
5122
- # {
5123
- # "retCode": 0,
5124
- # "retMsg": "success",
5125
- # "result": {
5126
- # "coin": "USDT",
5127
- # "chains": [
5128
- # {
5129
- # "chainType": "ERC20",
5130
- # "addressDeposit": "0xd9e1cd77afa0e50b452a62fbb68a3340602286c3",
5131
- # "tagDeposit": "",
5132
- # "chain": "ETH"
5133
- # }
5134
- # ]
5135
- # },
5136
- # "retExtInfo": {},
5137
- # "time": 1672192792860
5138
- # }
5139
- #
5140
- result = self.safe_dict(response, 'result', {})
5141
- chains = self.safe_list(result, 'chains', [])
5142
- chainsIndexedById = self.index_by(chains, 'chain')
5143
- selectedNetworkId = self.select_network_id_from_raw_networks(code, networkCode, chainsIndexedById)
5144
- addressObject = self.safe_dict(chainsIndexedById, selectedNetworkId, {})
5145
- return self.parse_deposit_address(addressObject, currency)
5328
+ networkCode, paramsOmited = self.handle_network_code_and_params(params)
5329
+ indexedAddresses = await self.fetch_deposit_addresses_by_network(code, paramsOmited)
5330
+ selectedNetworkCode = self.select_network_code_from_unified_networks(currency['code'], networkCode, indexedAddresses)
5331
+ return indexedAddresses[selectedNetworkCode]
5146
5332
 
5147
5333
  async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
5148
5334
  """
5149
5335
  fetch all deposits made to an account
5150
- :see: https://bybit-exchange.github.io/docs/v5/asset/deposit-record
5336
+
5337
+ https://bybit-exchange.github.io/docs/v5/asset/deposit-record
5338
+
5151
5339
  :param str code: unified currency code
5152
5340
  :param int [since]: the earliest time in ms to fetch deposits for, default = 30 days before the current time
5153
5341
  :param int [limit]: the maximum number of deposits structures to retrieve, default = 50, max = 50
5154
5342
  :param dict [params]: extra parameters specific to the exchange API endpoint
5155
5343
  :param int [params.until]: the latest time in ms to fetch deposits for, default = 30 days after since
5156
- * EXCHANGE SPECIFIC PARAMETERS
5344
+ EXCHANGE SPECIFIC PARAMETERS
5157
5345
  :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
5158
5346
  :param str [params.cursor]: used for pagination
5159
5347
  :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
@@ -5211,7 +5399,9 @@ class bybit(Exchange, ImplicitAPI):
5211
5399
  async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
5212
5400
  """
5213
5401
  fetch all withdrawals made from an account
5214
- :see: https://bybit-exchange.github.io/docs/v5/asset/withdraw-record
5402
+
5403
+ https://bybit-exchange.github.io/docs/v5/asset/withdraw-record
5404
+
5215
5405
  :param str code: unified currency code
5216
5406
  :param int [since]: the earliest time in ms to fetch withdrawals for
5217
5407
  :param int [limit]: the maximum number of withdrawals structures to retrieve
@@ -5381,17 +5571,26 @@ class bybit(Exchange, ImplicitAPI):
5381
5571
  'comment': None,
5382
5572
  }
5383
5573
 
5384
- async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
5574
+ async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
5385
5575
  """
5386
- fetch the history of changes, actions done by the user or operations that altered balance of the user
5387
- :see: https://bybit-exchange.github.io/docs/v5/account/transaction-log
5388
- :param str code: unified currency code, default is None
5576
+ fetch the history of changes, actions done by the user or operations that altered the balance of the user
5577
+
5578
+ https://bybit-exchange.github.io/docs/v5/account/transaction-log
5579
+ https://bybit-exchange.github.io/docs/v5/account/contract-transaction-log
5580
+
5581
+ :param str [code]: unified currency code, default is None
5389
5582
  :param int [since]: timestamp in ms of the earliest ledger entry, default is None
5390
- :param int [limit]: max number of ledger entrys to return, default is None
5583
+ :param int [limit]: max number of ledger entries to return, default is None
5391
5584
  :param dict [params]: extra parameters specific to the exchange API endpoint
5392
- :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger-structure>`
5585
+ :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
5586
+ :param str [params.subType]: if inverse will use v5/account/contract-transaction-log
5587
+ :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
5393
5588
  """
5394
5589
  await self.load_markets()
5590
+ paginate = False
5591
+ paginate, params = self.handle_option_and_params(params, 'fetchLedger', 'paginate')
5592
+ if paginate:
5593
+ return await self.fetch_paginated_call_cursor('fetchLedger', code, since, limit, params, 'nextPageCursor', 'cursor', None, 50)
5395
5594
  request: dict = {
5396
5595
  # 'coin': currency['id'],
5397
5596
  # 'currency': currency['id'], # alias
@@ -5426,11 +5625,17 @@ class bybit(Exchange, ImplicitAPI):
5426
5625
  request[currencyKey] = currency['id']
5427
5626
  if limit is not None:
5428
5627
  request['limit'] = limit
5628
+ subType = None
5629
+ subType, params = self.handle_sub_type_and_params('fetchLedger', None, params)
5429
5630
  response = None
5430
5631
  if enableUnified[1]:
5431
- response = await self.privateGetV5AccountTransactionLog(self.extend(request, params))
5632
+ unifiedMarginStatus = self.safe_integer(self.options, 'unifiedMarginStatus', 5) # 3/4 uta 1.0, 5/6 uta 2.0
5633
+ if subType == 'inverse' and (unifiedMarginStatus < 5):
5634
+ response = await self.privateGetV5AccountContractTransactionLog(self.extend(request, params))
5635
+ else:
5636
+ response = await self.privateGetV5AccountTransactionLog(self.extend(request, params))
5432
5637
  else:
5433
- response = await self.privateGetV2PrivateWalletFundRecords(self.extend(request, params))
5638
+ response = await self.privateGetV5AccountContractTransactionLog(self.extend(request, params))
5434
5639
  #
5435
5640
  # {
5436
5641
  # "ret_code": 0,
@@ -5537,7 +5742,7 @@ class bybit(Exchange, ImplicitAPI):
5537
5742
  data = self.add_pagination_cursor_to_result(response)
5538
5743
  return self.parse_ledger(data, currency, since, limit)
5539
5744
 
5540
- def parse_ledger_entry(self, item: dict, currency: Currency = None):
5745
+ def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
5541
5746
  #
5542
5747
  # {
5543
5748
  # "id": 234467,
@@ -5576,36 +5781,41 @@ class bybit(Exchange, ImplicitAPI):
5576
5781
  #
5577
5782
  currencyId = self.safe_string_2(item, 'coin', 'currency')
5578
5783
  code = self.safe_currency_code(currencyId, currency)
5579
- amount = self.safe_string_2(item, 'amount', 'change')
5580
- after = self.safe_string_2(item, 'wallet_balance', 'cashBalance')
5581
- direction = 'out' if Precise.string_lt(amount, '0') else 'in'
5784
+ currency = self.safe_currency(currencyId, currency)
5785
+ amountString = self.safe_string_2(item, 'amount', 'change')
5786
+ afterString = self.safe_string_2(item, 'wallet_balance', 'cashBalance')
5787
+ direction = 'out' if Precise.string_lt(amountString, '0') else 'in'
5582
5788
  before = None
5583
- if after is not None and amount is not None:
5584
- difference = amount if (direction == 'out') else Precise.string_neg(amount)
5585
- before = Precise.string_add(after, difference)
5789
+ after = None
5790
+ amount = None
5791
+ if afterString is not None and amountString is not None:
5792
+ difference = amountString if (direction == 'out') else Precise.string_neg(amountString)
5793
+ before = self.parse_to_numeric(Precise.string_add(afterString, difference))
5794
+ after = self.parse_to_numeric(afterString)
5795
+ amount = self.parse_to_numeric(Precise.string_abs(amountString))
5586
5796
  timestamp = self.parse8601(self.safe_string(item, 'exec_time'))
5587
5797
  if timestamp is None:
5588
5798
  timestamp = self.safe_integer(item, 'transactionTime')
5589
- type = self.parse_ledger_entry_type(self.safe_string(item, 'type'))
5590
- id = self.safe_string(item, 'id')
5591
- referenceId = self.safe_string(item, 'tx_id')
5592
- return {
5593
- 'id': id,
5594
- 'currency': code,
5799
+ return self.safe_ledger_entry({
5800
+ 'info': item,
5801
+ 'id': self.safe_string(item, 'id'),
5802
+ 'direction': direction,
5595
5803
  'account': self.safe_string(item, 'wallet_id'),
5804
+ 'referenceId': self.safe_string(item, 'tx_id'),
5596
5805
  'referenceAccount': None,
5597
- 'referenceId': referenceId,
5598
- 'status': None,
5599
- 'amount': self.parse_number(Precise.string_abs(amount)),
5600
- 'before': self.parse_number(before),
5601
- 'after': self.parse_number(after),
5602
- 'fee': self.parse_number(self.safe_string(item, 'fee')),
5603
- 'direction': direction,
5806
+ 'type': self.parse_ledger_entry_type(self.safe_string(item, 'type')),
5807
+ 'currency': code,
5808
+ 'amount': amount,
5604
5809
  'timestamp': timestamp,
5605
5810
  'datetime': self.iso8601(timestamp),
5606
- 'type': type,
5607
- 'info': item,
5608
- }
5811
+ 'before': before,
5812
+ 'after': after,
5813
+ 'status': 'ok',
5814
+ 'fee': {
5815
+ 'currency': code,
5816
+ 'cost': self.safe_number(item, 'fee'),
5817
+ },
5818
+ }, currency)
5609
5819
 
5610
5820
  def parse_ledger_entry_type(self, type):
5611
5821
  types: dict = {
@@ -5632,10 +5842,12 @@ class bybit(Exchange, ImplicitAPI):
5632
5842
  }
5633
5843
  return self.safe_string(types, type, type)
5634
5844
 
5635
- async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
5845
+ async def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction:
5636
5846
  """
5637
5847
  make a withdrawal
5638
- :see: https://bybit-exchange.github.io/docs/v5/asset/withdraw
5848
+
5849
+ https://bybit-exchange.github.io/docs/v5/asset/withdraw
5850
+
5639
5851
  :param str code: unified currency code
5640
5852
  :param float amount: the amount to withdraw
5641
5853
  :param str address: the address to withdraw to
@@ -5644,6 +5856,12 @@ class bybit(Exchange, ImplicitAPI):
5644
5856
  :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
5645
5857
  """
5646
5858
  tag, params = self.handle_withdraw_tag_and_params(tag, params)
5859
+ accountType = None
5860
+ accounts = await self.is_unified_enabled()
5861
+ isUta = accounts[1]
5862
+ accountType, params = self.handle_option_and_params(params, 'withdraw', 'accountType', 'SPOT')
5863
+ if isUta:
5864
+ accountType = 'UTA'
5647
5865
  await self.load_markets()
5648
5866
  self.check_address(address)
5649
5867
  currency = self.currency(code)
@@ -5652,6 +5870,7 @@ class bybit(Exchange, ImplicitAPI):
5652
5870
  'amount': self.number_to_string(amount),
5653
5871
  'address': address,
5654
5872
  'timestamp': self.milliseconds(),
5873
+ 'accountType': accountType,
5655
5874
  }
5656
5875
  if tag is not None:
5657
5876
  request['tag'] = tag
@@ -5674,10 +5893,12 @@ class bybit(Exchange, ImplicitAPI):
5674
5893
  result = self.safe_dict(response, 'result', {})
5675
5894
  return self.parse_transaction(result, currency)
5676
5895
 
5677
- async def fetch_position(self, symbol: str, params={}):
5896
+ async def fetch_position(self, symbol: str, params={}) -> Position:
5678
5897
  """
5679
5898
  fetch data on a single open contract trade position
5680
- :see: https://bybit-exchange.github.io/docs/v5/position
5899
+
5900
+ https://bybit-exchange.github.io/docs/v5/position
5901
+
5681
5902
  :param str symbol: unified market symbol of the market the position is held in, default is None
5682
5903
  :param dict [params]: extra parameters specific to the exchange API endpoint
5683
5904
  :returns dict: a `position structure <https://docs.ccxt.com/#/?id=position-structure>`
@@ -5689,18 +5910,11 @@ class bybit(Exchange, ImplicitAPI):
5689
5910
  request: dict = {
5690
5911
  'symbol': market['id'],
5691
5912
  }
5692
- enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
5693
- isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
5694
- isUsdcSettled = market['settle'] == 'USDC'
5695
5913
  response = None
5696
5914
  type = None
5697
5915
  type, params = self.get_bybit_type('fetchPosition', market, params)
5698
- if (type == 'option' or isUsdcSettled) and not isUnifiedAccount:
5699
- request['category'] = 'OPTION' if (type == 'option') else 'PERPETUAL'
5700
- response = await self.privatePostOptionUsdcOpenapiPrivateV1QueryPosition(self.extend(request, params))
5701
- else:
5702
- request['category'] = type
5703
- response = await self.privateGetV5PositionList(self.extend(request, params))
5916
+ request['category'] = type
5917
+ response = await self.privateGetV5PositionList(self.extend(request, params))
5704
5918
  #
5705
5919
  # {
5706
5920
  # "retCode": 0,
@@ -5750,88 +5964,26 @@ class bybit(Exchange, ImplicitAPI):
5750
5964
  position['datetime'] = self.iso8601(timestamp)
5751
5965
  return position
5752
5966
 
5753
- async def fetch_usdc_positions(self, symbols: Strings = None, params={}):
5754
- await self.load_markets()
5755
- request: dict = {}
5756
- market = None
5757
- if isinstance(symbols, list):
5758
- length = len(symbols)
5759
- if length != 1:
5760
- raise ArgumentsRequired(self.id + ' fetchUsdcPositions() takes an array with exactly one symbol')
5761
- symbol = self.safe_string(symbols, 0)
5762
- market = self.market(symbol)
5763
- request['symbol'] = market['id']
5764
- elif symbols is not None:
5765
- market = self.market(symbols)
5766
- request['symbol'] = market['id']
5767
- type = None
5768
- type, params = self.get_bybit_type('fetchUsdcPositions', market, params)
5769
- request['category'] = 'OPTION' if (type == 'option') else 'PERPETUAL'
5770
- response = await self.privatePostOptionUsdcOpenapiPrivateV1QueryPosition(self.extend(request, params))
5771
- #
5772
- # {
5773
- # "result": {
5774
- # "cursor": "BTC-31DEC21-24000-P%3A1640834421431%2CBTC-31DEC21-24000-P%3A1640834421431",
5775
- # "resultTotalSize": 1,
5776
- # "dataList": [
5777
- # {
5778
- # "symbol": "BTC-31DEC21-24000-P",
5779
- # "leverage": "",
5780
- # "occClosingFee": "",
5781
- # "liqPrice": "",
5782
- # "positionValue": "",
5783
- # "takeProfit": "",
5784
- # "riskId": "",
5785
- # "trailingStop": "",
5786
- # "unrealisedPnl": "",
5787
- # "createdAt": "1640834421431",
5788
- # "markPrice": "0.00",
5789
- # "cumRealisedPnl": "",
5790
- # "positionMM": "359.5271",
5791
- # "positionIM": "467.0633",
5792
- # "updatedAt": "1640834421431",
5793
- # "tpSLMode": "",
5794
- # "side": "Sell",
5795
- # "bustPrice": "",
5796
- # "deleverageIndicator": 0,
5797
- # "entryPrice": "1.4",
5798
- # "size": "-0.100",
5799
- # "sessionRPL": "",
5800
- # "positionStatus": "",
5801
- # "sessionUPL": "",
5802
- # "stopLoss": "",
5803
- # "orderMargin": "",
5804
- # "sessionAvgPrice": "1.5"
5805
- # }
5806
- # ]
5807
- # },
5808
- # "retCode": 0,
5809
- # "retMsg": "Success."
5810
- # }
5811
- #
5812
- result = self.safe_dict(response, 'result', {})
5813
- positions = self.safe_list(result, 'dataList', [])
5814
- results = []
5815
- for i in range(0, len(positions)):
5816
- rawPosition = positions[i]
5817
- if ('data' in rawPosition) and ('is_valid' in rawPosition):
5818
- # futures only
5819
- rawPosition = self.safe_dict(rawPosition, 'data')
5820
- results.append(self.parse_position(rawPosition, market))
5821
- return self.filter_by_array_positions(results, 'symbol', symbols, False)
5822
-
5823
- async def fetch_positions(self, symbols: Strings = None, params={}):
5967
+ async def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
5824
5968
  """
5825
5969
  fetch all open positions
5826
- :see: https://bybit-exchange.github.io/docs/v5/position
5970
+
5971
+ https://bybit-exchange.github.io/docs/v5/position
5972
+
5827
5973
  :param str[] symbols: list of unified market symbols
5828
5974
  :param dict [params]: extra parameters specific to the exchange API endpoint
5829
5975
  :param str [params.type]: market type, ['swap', 'option', 'spot']
5830
5976
  :param str [params.subType]: market subType, ['linear', 'inverse']
5831
5977
  :param str [params.baseCoin]: Base coin. Supports linear, inverse & option
5832
5978
  :param str [params.settleCoin]: Settle coin. Supports linear, inverse & option
5979
+ :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times
5833
5980
  :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
5834
5981
  """
5982
+ await self.load_markets()
5983
+ paginate = False
5984
+ paginate, params = self.handle_option_and_params(params, 'fetchPositions', 'paginate')
5985
+ if paginate:
5986
+ return await self.fetch_paginated_call_cursor('fetchPositions', symbols, None, None, params, 'nextPageCursor', 'cursor', None, 200)
5835
5987
  symbol = None
5836
5988
  if (symbols is not None) and isinstance(symbols, list):
5837
5989
  symbolsLength = len(symbols)
@@ -5843,17 +5995,12 @@ class bybit(Exchange, ImplicitAPI):
5843
5995
  elif symbols is not None:
5844
5996
  symbol = symbols
5845
5997
  symbols = [self.symbol(symbol)]
5846
- await self.load_markets()
5847
- enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
5848
- isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
5849
5998
  request: dict = {}
5850
5999
  market = None
5851
- isUsdcSettled = False
5852
6000
  if symbol is not None:
5853
6001
  market = self.market(symbol)
5854
6002
  symbol = market['symbol']
5855
6003
  request['symbol'] = market['id']
5856
- isUsdcSettled = market['settle'] == 'USDC'
5857
6004
  type = None
5858
6005
  type, params = self.get_bybit_type('fetchPositions', market, params)
5859
6006
  if type == 'linear' or type == 'inverse':
@@ -5863,13 +6010,12 @@ class bybit(Exchange, ImplicitAPI):
5863
6010
  defaultSettle = self.safe_string(self.options, 'defaultSettle', 'USDT')
5864
6011
  settleCoin = self.safe_string(params, 'settleCoin', defaultSettle)
5865
6012
  request['settleCoin'] = settleCoin
5866
- isUsdcSettled = (settleCoin == 'USDC')
5867
6013
  else:
5868
6014
  # inverse
5869
6015
  if symbol is None and baseCoin is None:
5870
6016
  request['category'] = 'inverse'
5871
- if ((type == 'option') or isUsdcSettled) and not isUnifiedAccount:
5872
- return await self.fetch_usdc_positions(symbols, params)
6017
+ if self.safe_integer(params, 'limit') is None:
6018
+ request['limit'] = 200 # max limit
5873
6019
  params = self.omit(params, ['type'])
5874
6020
  request['category'] = type
5875
6021
  response = await self.privateGetV5PositionList(self.extend(request, params))
@@ -5918,7 +6064,7 @@ class bybit(Exchange, ImplicitAPI):
5918
6064
  results.append(self.parse_position(rawPosition))
5919
6065
  return self.filter_by_array_positions(results, 'symbol', symbols, False)
5920
6066
 
5921
- def parse_position(self, position: dict, market: Market = None):
6067
+ def parse_position(self, position: dict, market: Market = None) -> Position:
5922
6068
  #
5923
6069
  # linear swap
5924
6070
  #
@@ -6083,12 +6229,14 @@ class bybit(Exchange, ImplicitAPI):
6083
6229
  marginMode = 'isolated' if (tradeMode == 1) else 'cross'
6084
6230
  collateralString = self.safe_string(position, 'positionBalance')
6085
6231
  entryPrice = self.omit_zero(self.safe_string_n(position, ['entryPrice', 'avgPrice', 'avgEntryPrice']))
6232
+ markPrice = self.safe_string(position, 'markPrice')
6086
6233
  liquidationPrice = self.omit_zero(self.safe_string(position, 'liqPrice'))
6087
6234
  leverage = self.safe_string(position, 'leverage')
6088
6235
  if liquidationPrice is not None:
6089
6236
  if market['settle'] == 'USDC':
6090
6237
  # (Entry price - Liq price) * Contracts + Maintenance Margin + (unrealised pnl) = Collateral
6091
- difference = Precise.string_abs(Precise.string_sub(entryPrice, liquidationPrice))
6238
+ price = markPrice if self.safe_bool(self.options, 'useMarkPriceForPositionCollateral', False) else entryPrice
6239
+ difference = Precise.string_abs(Precise.string_sub(price, liquidationPrice))
6092
6240
  collateralString = Precise.string_add(Precise.string_add(Precise.string_mul(difference, size), maintenanceMarginString), unrealisedPnl)
6093
6241
  else:
6094
6242
  bustPrice = self.safe_string(position, 'bustPrice')
@@ -6115,6 +6263,8 @@ class bybit(Exchange, ImplicitAPI):
6115
6263
  initialMarginString = Precise.string_div(size, Precise.string_mul(entryPrice, leverage))
6116
6264
  maintenanceMarginPercentage = Precise.string_div(maintenanceMarginString, notional)
6117
6265
  marginRatio = Precise.string_div(maintenanceMarginString, collateralString, 4)
6266
+ positionIdx = self.safe_string(position, 'positionIdx')
6267
+ hedged = (positionIdx is not None) and (positionIdx != '0')
6118
6268
  return self.safe_position({
6119
6269
  'info': position,
6120
6270
  'id': None,
@@ -6135,7 +6285,7 @@ class bybit(Exchange, ImplicitAPI):
6135
6285
  'contractSize': self.safe_number(market, 'contractSize'),
6136
6286
  'marginRatio': self.parse_number(marginRatio),
6137
6287
  'liquidationPrice': self.parse_number(liquidationPrice),
6138
- 'markPrice': self.safe_number(position, 'markPrice'),
6288
+ 'markPrice': self.parse_number(markPrice),
6139
6289
  'lastPrice': self.safe_number(position, 'avgExitPrice'),
6140
6290
  'collateral': self.parse_number(collateralString),
6141
6291
  'marginMode': marginMode,
@@ -6143,12 +6293,15 @@ class bybit(Exchange, ImplicitAPI):
6143
6293
  'percentage': None,
6144
6294
  'stopLossPrice': self.safe_number_2(position, 'stop_loss', 'stopLoss'),
6145
6295
  'takeProfitPrice': self.safe_number_2(position, 'take_profit', 'takeProfit'),
6296
+ 'hedged': hedged,
6146
6297
  })
6147
6298
 
6148
6299
  async def fetch_leverage(self, symbol: str, params={}) -> Leverage:
6149
6300
  """
6150
6301
  fetch the set leverage for a market
6151
- :see: https://bybit-exchange.github.io/docs/v5/position
6302
+
6303
+ https://bybit-exchange.github.io/docs/v5/position
6304
+
6152
6305
  :param str symbol: unified market symbol
6153
6306
  :param dict [params]: extra parameters specific to the exchange API endpoint
6154
6307
  :returns dict: a `leverage structure <https://docs.ccxt.com/#/?id=leverage-structure>`
@@ -6172,8 +6325,10 @@ class bybit(Exchange, ImplicitAPI):
6172
6325
  async def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
6173
6326
  """
6174
6327
  set margin mode(account) or trade mode(symbol)
6175
- :see: https://bybit-exchange.github.io/docs/v5/account/set-margin-mode
6176
- :see: https://bybit-exchange.github.io/docs/v5/position/cross-isolate
6328
+
6329
+ https://bybit-exchange.github.io/docs/v5/account/set-margin-mode
6330
+ https://bybit-exchange.github.io/docs/v5/position/cross-isolate
6331
+
6177
6332
  :param str marginMode: account mode must be either [isolated, cross, portfolio], trade mode must be either [isolated, cross]
6178
6333
  :param str symbol: unified market symbol of the market the position is held in, default is None
6179
6334
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -6251,10 +6406,12 @@ class bybit(Exchange, ImplicitAPI):
6251
6406
  response = await self.privatePostV5PositionSwitchIsolated(self.extend(request, params))
6252
6407
  return response
6253
6408
 
6254
- async def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
6409
+ async def set_leverage(self, leverage: int, symbol: Str = None, params={}):
6255
6410
  """
6256
6411
  set the level of leverage for a market
6257
- :see: https://bybit-exchange.github.io/docs/v5/position/leverage
6412
+
6413
+ https://bybit-exchange.github.io/docs/v5/position/leverage
6414
+
6258
6415
  :param float leverage: the rate of leverage
6259
6416
  :param str symbol: unified market symbol
6260
6417
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -6268,9 +6425,6 @@ class bybit(Exchange, ImplicitAPI):
6268
6425
  market = self.market(symbol)
6269
6426
  # WARNING: THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
6270
6427
  # AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
6271
- enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
6272
- isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
6273
- isUsdcSettled = market['settle'] == 'USDC'
6274
6428
  # engage in leverage setting
6275
6429
  # we reuse the code here instead of having two methods
6276
6430
  leverageString = self.number_to_string(leverage)
@@ -6279,26 +6433,23 @@ class bybit(Exchange, ImplicitAPI):
6279
6433
  'buyLeverage': leverageString,
6280
6434
  'sellLeverage': leverageString,
6281
6435
  }
6282
- response = None
6283
- if isUsdcSettled and not isUnifiedAccount:
6284
- request['leverage'] = leverageString
6285
- response = await self.privatePostPerpetualUsdcOpenapiPrivateV1PositionLeverageSave(self.extend(request, params))
6436
+ request['buyLeverage'] = leverageString
6437
+ request['sellLeverage'] = leverageString
6438
+ if market['linear']:
6439
+ request['category'] = 'linear'
6440
+ elif market['inverse']:
6441
+ request['category'] = 'inverse'
6286
6442
  else:
6287
- request['buyLeverage'] = leverageString
6288
- request['sellLeverage'] = leverageString
6289
- if market['linear']:
6290
- request['category'] = 'linear'
6291
- elif market['inverse']:
6292
- request['category'] = 'inverse'
6293
- else:
6294
- raise NotSupported(self.id + ' setLeverage() only support linear and inverse market')
6295
- response = await self.privatePostV5PositionSetLeverage(self.extend(request, params))
6443
+ raise NotSupported(self.id + ' setLeverage() only support linear and inverse market')
6444
+ response = await self.privatePostV5PositionSetLeverage(self.extend(request, params))
6296
6445
  return response
6297
6446
 
6298
6447
  async def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
6299
6448
  """
6300
6449
  set hedged to True or False for a market
6301
- :see: https://bybit-exchange.github.io/docs/v5/position/position-mode
6450
+
6451
+ https://bybit-exchange.github.io/docs/v5/position/position-mode
6452
+
6302
6453
  :param bool hedged:
6303
6454
  :param str symbol: used for unified account with inverse market
6304
6455
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -6389,12 +6540,14 @@ class bybit(Exchange, ImplicitAPI):
6389
6540
  data = self.add_pagination_cursor_to_result(response)
6390
6541
  id = self.safe_string(result, 'symbol')
6391
6542
  market = self.safe_market(id, market, None, 'contract')
6392
- return self.parse_open_interests(data, market, since, limit)
6543
+ return self.parse_open_interests_history(data, market, since, limit)
6393
6544
 
6394
6545
  async def fetch_open_interest(self, symbol: str, params={}):
6395
6546
  """
6396
6547
  Retrieves the open interest of a derivative trading pair
6397
- :see: https://bybit-exchange.github.io/docs/v5/market/open-interest
6548
+
6549
+ https://bybit-exchange.github.io/docs/v5/market/open-interest
6550
+
6398
6551
  :param str symbol: Unified CCXT market symbol
6399
6552
  :param dict [params]: exchange specific parameters
6400
6553
  :param str [params.interval]: 5m, 15m, 30m, 1h, 4h, 1d
@@ -6450,7 +6603,9 @@ class bybit(Exchange, ImplicitAPI):
6450
6603
  async def fetch_open_interest_history(self, symbol: str, timeframe='1h', since: Int = None, limit: Int = None, params={}):
6451
6604
  """
6452
6605
  Gets the total amount of unsettled contracts. In other words, the total number of contracts held in open positions
6453
- :see: https://bybit-exchange.github.io/docs/v5/market/open-interest
6606
+
6607
+ https://bybit-exchange.github.io/docs/v5/market/open-interest
6608
+
6454
6609
  :param str symbol: Unified market symbol
6455
6610
  :param str timeframe: "5m", 15m, 30m, 1h, 4h, 1d
6456
6611
  :param int [since]: Not used by Bybit
@@ -6460,12 +6615,13 @@ class bybit(Exchange, ImplicitAPI):
6460
6615
  :returns: An array of open interest structures
6461
6616
  """
6462
6617
  if timeframe == '1m':
6463
- raise BadRequest(self.id + 'fetchOpenInterestHistory cannot use the 1m timeframe')
6618
+ raise BadRequest(self.id + ' fetchOpenInterestHistory cannot use the 1m timeframe')
6464
6619
  await self.load_markets()
6465
6620
  paginate = self.safe_bool(params, 'paginate')
6466
6621
  if paginate:
6467
6622
  params = self.omit(params, 'paginate')
6468
- return await self.fetch_paginated_call_deterministic('fetchOpenInterestHistory', symbol, since, limit, timeframe, params, 500)
6623
+ params['timeframe'] = timeframe
6624
+ return await self.fetch_paginated_call_cursor('fetchOpenInterestHistory', symbol, since, limit, params, 'nextPageCursor', 'cursor', None, 200)
6469
6625
  market = self.market(symbol)
6470
6626
  if market['spot'] or market['option']:
6471
6627
  raise BadRequest(self.id + ' fetchOpenInterestHistory() symbol does not support market ' + symbol)
@@ -6484,10 +6640,13 @@ class bybit(Exchange, ImplicitAPI):
6484
6640
  # }
6485
6641
  #
6486
6642
  timestamp = self.safe_integer(interest, 'timestamp')
6487
- value = self.safe_number_2(interest, 'open_interest', 'openInterest')
6643
+ openInterest = self.safe_number_2(interest, 'open_interest', 'openInterest')
6644
+ # the openInterest is in the base asset for linear and quote asset for inverse
6645
+ amount = openInterest if market['linear'] else None
6646
+ value = openInterest if market['inverse'] else None
6488
6647
  return self.safe_open_interest({
6489
6648
  'symbol': market['symbol'],
6490
- 'openInterestAmount': None,
6649
+ 'openInterestAmount': amount,
6491
6650
  'openInterestValue': value,
6492
6651
  'timestamp': timestamp,
6493
6652
  'datetime': self.iso8601(timestamp),
@@ -6497,7 +6656,9 @@ class bybit(Exchange, ImplicitAPI):
6497
6656
  async def fetch_cross_borrow_rate(self, code: str, params={}) -> CrossBorrowRate:
6498
6657
  """
6499
6658
  fetch the rate of interest to borrow a currency for margin trading
6500
- :see: https://bybit-exchange.github.io/docs/zh-TW/v5/spot-margin-normal/interest-quota
6659
+
6660
+ https://bybit-exchange.github.io/docs/zh-TW/v5/spot-margin-normal/interest-quota
6661
+
6501
6662
  :param str code: unified currency code
6502
6663
  :param dict [params]: extra parameters specific to the exchange API endpoint
6503
6664
  :returns dict: a `borrow rate structure <https://docs.ccxt.com/#/?id=borrow-rate-structure>`
@@ -6537,21 +6698,33 @@ class bybit(Exchange, ImplicitAPI):
6537
6698
  # "timestamp": 1666734490778
6538
6699
  # }
6539
6700
  #
6701
+ # fetchBorrowRateHistory
6702
+ # {
6703
+ # "timestamp": 1721469600000,
6704
+ # "currency": "USDC",
6705
+ # "hourlyBorrowRate": "0.000014621596",
6706
+ # "vipLevel": "No VIP"
6707
+ # }
6708
+ #
6540
6709
  timestamp = self.safe_integer(info, 'timestamp')
6541
- currencyId = self.safe_string(info, 'coin')
6710
+ currencyId = self.safe_string_2(info, 'coin', 'currency')
6711
+ hourlyBorrowRate = self.safe_number(info, 'hourlyBorrowRate')
6712
+ period = 3600000 if (hourlyBorrowRate is not None) else 86400000 # 1h or 1d
6542
6713
  return {
6543
6714
  'currency': self.safe_currency_code(currencyId, currency),
6544
- 'rate': self.safe_number(info, 'interestRate'),
6545
- 'period': 86400000, # Daily
6715
+ 'rate': self.safe_number(info, 'interestRate', hourlyBorrowRate),
6716
+ 'period': period, # Daily
6546
6717
  'timestamp': timestamp,
6547
6718
  'datetime': self.iso8601(timestamp),
6548
6719
  'info': info,
6549
6720
  }
6550
6721
 
6551
- async def fetch_borrow_interest(self, code: Str = None, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
6722
+ async def fetch_borrow_interest(self, code: Str = None, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[BorrowInterest]:
6552
6723
  """
6553
6724
  fetch the interest owed by the user for borrowing currency for margin trading
6554
- :see: https://bybit-exchange.github.io/docs/zh-TW/v5/spot-margin-normal/account-info
6725
+
6726
+ https://bybit-exchange.github.io/docs/zh-TW/v5/spot-margin-normal/account-info
6727
+
6555
6728
  :param str code: unified currency code
6556
6729
  :param str symbol: unified market symbol when fetch interest in isolated markets
6557
6730
  :param number [since]: the earliest time in ms to fetch borrrow interest for
@@ -6592,7 +6765,56 @@ class bybit(Exchange, ImplicitAPI):
6592
6765
  interest = self.parse_borrow_interests(rows, None)
6593
6766
  return self.filter_by_currency_since_limit(interest, code, since, limit)
6594
6767
 
6595
- def parse_borrow_interest(self, info: dict, market: Market = None):
6768
+ async def fetch_borrow_rate_history(self, code: str, since: Int = None, limit: Int = None, params={}):
6769
+ """
6770
+ retrieves a history of a currencies borrow interest rate at specific time slots
6771
+
6772
+ https://bybit-exchange.github.io/docs/v5/spot-margin-uta/historical-interest
6773
+
6774
+ :param str code: unified currency code
6775
+ :param int [since]: timestamp for the earliest borrow rate
6776
+ :param int [limit]: the maximum number of `borrow rate structures <https://docs.ccxt.com/#/?id=borrow-rate-structure>` to retrieve
6777
+ :param dict [params]: extra parameters specific to the exchange API endpoint
6778
+ :param int [params.until]: the latest time in ms to fetch entries for
6779
+ :returns dict[]: an array of `borrow rate structures <https://docs.ccxt.com/#/?id=borrow-rate-structure>`
6780
+ """
6781
+ await self.load_markets()
6782
+ currency = self.currency(code)
6783
+ request: dict = {
6784
+ 'currency': currency['id'],
6785
+ }
6786
+ if since is None:
6787
+ since = self.milliseconds() - 86400000 * 30 # last 30 days
6788
+ request['startTime'] = since
6789
+ endTime = self.safe_integer_2(params, 'until', 'endTime')
6790
+ params = self.omit(params, ['until'])
6791
+ if endTime is None:
6792
+ endTime = since + 86400000 * 30 # since + 30 days
6793
+ request['endTime'] = endTime
6794
+ response = await self.privateGetV5SpotMarginTradeInterestRateHistory(self.extend(request, params))
6795
+ #
6796
+ # {
6797
+ # "retCode": 0,
6798
+ # "retMsg": "OK",
6799
+ # "result": {
6800
+ # "list": [
6801
+ # {
6802
+ # "timestamp": 1721469600000,
6803
+ # "currency": "USDC",
6804
+ # "hourlyBorrowRate": "0.000014621596",
6805
+ # "vipLevel": "No VIP"
6806
+ # }
6807
+ # ]
6808
+ # },
6809
+ # "retExtInfo": "{}",
6810
+ # "time": 1721899048991
6811
+ # }
6812
+ #
6813
+ data = self.safe_dict(response, 'result')
6814
+ rows = self.safe_list(data, 'list', [])
6815
+ return self.parse_borrow_rate_history(rows, code, since, limit)
6816
+
6817
+ def parse_borrow_interest(self, info: dict, market: Market = None) -> BorrowInterest:
6596
6818
  #
6597
6819
  # {
6598
6820
  # "tokenId": "BTC",
@@ -6604,21 +6826,23 @@ class bybit(Exchange, ImplicitAPI):
6604
6826
  # },
6605
6827
  #
6606
6828
  return {
6829
+ 'info': info,
6607
6830
  'symbol': None,
6608
- 'marginMode': 'cross',
6609
6831
  'currency': self.safe_currency_code(self.safe_string(info, 'tokenId')),
6610
6832
  'interest': self.safe_number(info, 'interest'),
6611
6833
  'interestRate': None,
6612
6834
  'amountBorrowed': self.safe_number(info, 'loan'),
6835
+ 'marginMode': 'cross',
6613
6836
  'timestamp': None,
6614
6837
  'datetime': None,
6615
- 'info': info,
6616
6838
  }
6617
6839
 
6618
6840
  async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
6619
6841
  """
6620
6842
  transfer currency internally between wallets on the same account
6621
- :see: https://bybit-exchange.github.io/docs/v5/asset/create-inter-transfer
6843
+
6844
+ https://bybit-exchange.github.io/docs/v5/asset/create-inter-transfer
6845
+
6622
6846
  :param str code: unified currency code
6623
6847
  :param float amount: amount to transfer
6624
6848
  :param str fromAccount: account to transfer from
@@ -6666,10 +6890,12 @@ class bybit(Exchange, ImplicitAPI):
6666
6890
  'status': status,
6667
6891
  })
6668
6892
 
6669
- async def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> TransferEntries:
6893
+ async def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[TransferEntry]:
6670
6894
  """
6671
6895
  fetch a history of internal transfers made on an account
6672
- :see: https://bybit-exchange.github.io/docs/v5/asset/inter-transfer-list
6896
+
6897
+ https://bybit-exchange.github.io/docs/v5/asset/inter-transfer-list
6898
+
6673
6899
  :param str code: unified currency code of the currency transferred
6674
6900
  :param int [since]: the earliest time in ms to fetch transfers for
6675
6901
  :param int [limit]: the maximum number of transfer structures to retrieve
@@ -6686,8 +6912,8 @@ class bybit(Exchange, ImplicitAPI):
6686
6912
  currency = None
6687
6913
  request: dict = {}
6688
6914
  if code is not None:
6689
- currency = self.safe_currency_code(code)
6690
- request['coin'] = currency
6915
+ currency = self.safe_currency(code)
6916
+ request['coin'] = currency['id']
6691
6917
  if since is not None:
6692
6918
  request['startTime'] = since
6693
6919
  if limit is not None:
@@ -6722,7 +6948,9 @@ class bybit(Exchange, ImplicitAPI):
6722
6948
  async def borrow_cross_margin(self, code: str, amount: float, params={}):
6723
6949
  """
6724
6950
  create a loan to borrow margin
6725
- :see: https://bybit-exchange.github.io/docs/v5/spot-margin-normal/borrow
6951
+
6952
+ https://bybit-exchange.github.io/docs/v5/spot-margin-normal/borrow
6953
+
6726
6954
  :param str code: unified currency code of the currency to borrow
6727
6955
  :param float amount: the amount to borrow
6728
6956
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -6756,7 +6984,9 @@ class bybit(Exchange, ImplicitAPI):
6756
6984
  async def repay_cross_margin(self, code: str, amount, params={}):
6757
6985
  """
6758
6986
  repay borrowed margin and interest
6759
- :see: https://bybit-exchange.github.io/docs/v5/spot-margin-normal/repay
6987
+
6988
+ https://bybit-exchange.github.io/docs/v5/spot-margin-normal/repay
6989
+
6760
6990
  :param str code: unified currency code of the currency to repay
6761
6991
  :param float amount: the amount to repay
6762
6992
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -6787,15 +7017,15 @@ class bybit(Exchange, ImplicitAPI):
6787
7017
  'amount': amount,
6788
7018
  })
6789
7019
 
6790
- def parse_margin_loan(self, info, currency: Currency = None):
7020
+ def parse_margin_loan(self, info, currency: Currency = None) -> dict:
6791
7021
  #
6792
- # borrowMargin
7022
+ # borrowCrossMargin
6793
7023
  #
6794
7024
  # {
6795
7025
  # "transactId": "14143"
6796
7026
  # }
6797
7027
  #
6798
- # repayMargin
7028
+ # repayCrossMargin
6799
7029
  #
6800
7030
  # {
6801
7031
  # "repayId": "12128"
@@ -6858,7 +7088,7 @@ class bybit(Exchange, ImplicitAPI):
6858
7088
  'status': self.parse_transfer_status(self.safe_string(transfer, 'status')),
6859
7089
  }
6860
7090
 
6861
- async def fetch_derivatives_market_leverage_tiers(self, symbol: str, params={}):
7091
+ async def fetch_derivatives_market_leverage_tiers(self, symbol: str, params={}) -> List[LeverageTier]:
6862
7092
  await self.load_markets()
6863
7093
  market = self.market(symbol)
6864
7094
  request: dict = {
@@ -6899,7 +7129,9 @@ class bybit(Exchange, ImplicitAPI):
6899
7129
  async def fetch_market_leverage_tiers(self, symbol: str, params={}) -> List[LeverageTier]:
6900
7130
  """
6901
7131
  retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes for a single market
6902
- :see: https://bybit-exchange.github.io/docs/v5/market/risk-limit
7132
+
7133
+ https://bybit-exchange.github.io/docs/v5/market/risk-limit
7134
+
6903
7135
  :param str symbol: unified market symbol
6904
7136
  :param dict [params]: extra parameters specific to the exchange API endpoint
6905
7137
  :returns dict: a `leverage tiers structure <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`
@@ -6936,7 +7168,9 @@ class bybit(Exchange, ImplicitAPI):
6936
7168
  async def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
6937
7169
  """
6938
7170
  fetch the trading fees for a market
6939
- :see: https://bybit-exchange.github.io/docs/v5/account/fee-rate
7171
+
7172
+ https://bybit-exchange.github.io/docs/v5/account/fee-rate
7173
+
6940
7174
  :param str symbol: unified market symbol
6941
7175
  :param dict [params]: extra parameters specific to the exchange API endpoint
6942
7176
  :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
@@ -6947,14 +7181,7 @@ class bybit(Exchange, ImplicitAPI):
6947
7181
  'symbol': market['id'],
6948
7182
  }
6949
7183
  category = None
6950
- if market['linear']:
6951
- category = 'linear'
6952
- elif market['inverse']:
6953
- category = 'inverse'
6954
- elif market['spot']:
6955
- category = 'spot'
6956
- else:
6957
- category = 'option'
7184
+ category, params = self.get_bybit_type('fetchTradingFee', market, params)
6958
7185
  request['category'] = category
6959
7186
  response = await self.privateGetV5AccountFeeRate(self.extend(request, params))
6960
7187
  #
@@ -6982,7 +7209,9 @@ class bybit(Exchange, ImplicitAPI):
6982
7209
  async def fetch_trading_fees(self, params={}) -> TradingFees:
6983
7210
  """
6984
7211
  fetch the trading fees for multiple markets
6985
- :see: https://bybit-exchange.github.io/docs/v5/account/fee-rate
7212
+
7213
+ https://bybit-exchange.github.io/docs/v5/account/fee-rate
7214
+
6986
7215
  :param dict [params]: extra parameters specific to the exchange API endpoint
6987
7216
  :param str [params.type]: market type, ['swap', 'option', 'spot']
6988
7217
  :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
@@ -7019,7 +7248,7 @@ class bybit(Exchange, ImplicitAPI):
7019
7248
  result[symbol] = fee
7020
7249
  return result
7021
7250
 
7022
- def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
7251
+ def parse_deposit_withdraw_fee(self, fee, currency: Currency = None) -> Any:
7023
7252
  #
7024
7253
  # {
7025
7254
  # "name": "BTC",
@@ -7072,7 +7301,9 @@ class bybit(Exchange, ImplicitAPI):
7072
7301
  async def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
7073
7302
  """
7074
7303
  fetch deposit and withdraw fees
7075
- :see: https://bybit-exchange.github.io/docs/v5/asset/coin-info
7304
+
7305
+ https://bybit-exchange.github.io/docs/v5/asset/coin-info
7306
+
7076
7307
  :param str[] codes: list of unified currency codes
7077
7308
  :param dict [params]: extra parameters specific to the exchange API endpoint
7078
7309
  :returns dict: a list of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>`
@@ -7117,7 +7348,9 @@ class bybit(Exchange, ImplicitAPI):
7117
7348
  async def fetch_settlement_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
7118
7349
  """
7119
7350
  fetches historical settlement records
7120
- :see: https://bybit-exchange.github.io/docs/v5/market/delivery-price
7351
+
7352
+ https://bybit-exchange.github.io/docs/v5/market/delivery-price
7353
+
7121
7354
  :param str symbol: unified market symbol of the settlement history
7122
7355
  :param int [since]: timestamp in ms
7123
7356
  :param int [limit]: number of records
@@ -7168,7 +7401,9 @@ class bybit(Exchange, ImplicitAPI):
7168
7401
  async def fetch_my_settlement_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
7169
7402
  """
7170
7403
  fetches historical settlement records of the user
7171
- :see: https://bybit-exchange.github.io/docs/v5/asset/delivery
7404
+
7405
+ https://bybit-exchange.github.io/docs/v5/asset/delivery
7406
+
7172
7407
  :param str symbol: unified market symbol of the settlement history
7173
7408
  :param int [since]: timestamp in ms
7174
7409
  :param int [limit]: number of records
@@ -7185,9 +7420,9 @@ class bybit(Exchange, ImplicitAPI):
7185
7420
  request['symbol'] = market['id']
7186
7421
  type = None
7187
7422
  type, params = self.get_bybit_type('fetchMySettlementHistory', market, params)
7188
- if type == 'spot' or type == 'inverse':
7423
+ if type == 'spot':
7189
7424
  raise NotSupported(self.id + ' fetchMySettlementHistory() is not supported for spot market')
7190
- request['category'] = 'linear'
7425
+ request['category'] = type
7191
7426
  if limit is not None:
7192
7427
  request['limit'] = limit
7193
7428
  response = await self.privateGetV5AssetDeliveryRecord(self.extend(request, params))
@@ -7289,7 +7524,9 @@ class bybit(Exchange, ImplicitAPI):
7289
7524
  async def fetch_volatility_history(self, code: str, params={}):
7290
7525
  """
7291
7526
  fetch the historical volatility of an option market based on an underlying asset
7292
- :see: https://bybit-exchange.github.io/docs/v5/market/iv
7527
+
7528
+ https://bybit-exchange.github.io/docs/v5/market/iv
7529
+
7293
7530
  :param str code: unified currency code
7294
7531
  :param dict [params]: extra parameters specific to the exchange API endpoint
7295
7532
  :param int [params.period]: the period in days to fetch the volatility for: 7,14,21,30,60,90,180,270
@@ -7342,7 +7579,9 @@ class bybit(Exchange, ImplicitAPI):
7342
7579
  async def fetch_greeks(self, symbol: str, params={}) -> Greeks:
7343
7580
  """
7344
7581
  fetches an option contracts greeks, financial metrics used to measure the factors that affect the price of an options contract
7345
- :see: https://bybit-exchange.github.io/docs/api-explorer/v5/market/tickers
7582
+
7583
+ https://bybit-exchange.github.io/docs/api-explorer/v5/market/tickers
7584
+
7346
7585
  :param str symbol: unified symbol of the market to fetch greeks for
7347
7586
  :param dict [params]: extra parameters specific to the exchange API endpoint
7348
7587
  :returns dict: a `greeks structure <https://docs.ccxt.com/#/?id=greeks-structure>`
@@ -7403,6 +7642,75 @@ class bybit(Exchange, ImplicitAPI):
7403
7642
  'datetime': self.iso8601(timestamp),
7404
7643
  })
7405
7644
 
7645
+ async def fetch_all_greeks(self, symbols: Strings = None, params={}) -> List[Greeks]:
7646
+ """
7647
+ fetches all option contracts greeks, financial metrics used to measure the factors that affect the price of an options contract
7648
+
7649
+ https://bybit-exchange.github.io/docs/api-explorer/v5/market/tickers
7650
+
7651
+ :param str[] [symbols]: unified symbols of the markets to fetch greeks for, all markets are returned if not assigned
7652
+ :param dict [params]: extra parameters specific to the exchange API endpoint
7653
+ :param str [params.baseCoin]: the baseCoin of the symbol, default is BTC
7654
+ :returns dict: a `greeks structure <https://docs.ccxt.com/#/?id=greeks-structure>`
7655
+ """
7656
+ await self.load_markets()
7657
+ symbols = self.market_symbols(symbols, None, True, True, True)
7658
+ baseCoin = self.safe_string(params, 'baseCoin', 'BTC')
7659
+ request: dict = {
7660
+ 'category': 'option',
7661
+ 'baseCoin': baseCoin,
7662
+ }
7663
+ market = None
7664
+ if symbols is not None:
7665
+ symbolsLength = len(symbols)
7666
+ if symbolsLength == 1:
7667
+ market = self.market(symbols[0])
7668
+ request['symbol'] = market['id']
7669
+ response = await self.publicGetV5MarketTickers(self.extend(request, params))
7670
+ #
7671
+ # {
7672
+ # "retCode": 0,
7673
+ # "retMsg": "SUCCESS",
7674
+ # "result": {
7675
+ # "category": "option",
7676
+ # "list": [
7677
+ # {
7678
+ # "symbol": "BTC-26JAN24-39000-C",
7679
+ # "bid1Price": "3205",
7680
+ # "bid1Size": "7.1",
7681
+ # "bid1Iv": "0.5478",
7682
+ # "ask1Price": "3315",
7683
+ # "ask1Size": "1.98",
7684
+ # "ask1Iv": "0.5638",
7685
+ # "lastPrice": "3230",
7686
+ # "highPrice24h": "3255",
7687
+ # "lowPrice24h": "3200",
7688
+ # "markPrice": "3273.02263032",
7689
+ # "indexPrice": "36790.96",
7690
+ # "markIv": "0.5577",
7691
+ # "underlyingPrice": "37649.67254894",
7692
+ # "openInterest": "19.67",
7693
+ # "turnover24h": "170140.33875912",
7694
+ # "volume24h": "4.56",
7695
+ # "totalVolume": "22",
7696
+ # "totalTurnover": "789305",
7697
+ # "delta": "0.49640971",
7698
+ # "gamma": "0.00004131",
7699
+ # "vega": "69.08651675",
7700
+ # "theta": "-24.9443226",
7701
+ # "predictedDeliveryPrice": "0",
7702
+ # "change24h": "0.18532111"
7703
+ # }
7704
+ # ]
7705
+ # },
7706
+ # "retExtInfo": {},
7707
+ # "time": 1699584008326
7708
+ # }
7709
+ #
7710
+ result = self.safe_dict(response, 'result', {})
7711
+ data = self.safe_list(result, 'list', [])
7712
+ return self.parse_all_greeks(data, symbols)
7713
+
7406
7714
  def parse_greeks(self, greeks: dict, market: Market = None) -> Greeks:
7407
7715
  #
7408
7716
  # {
@@ -7457,10 +7765,12 @@ class bybit(Exchange, ImplicitAPI):
7457
7765
  'info': greeks,
7458
7766
  }
7459
7767
 
7460
- async def fetch_my_liquidations(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
7768
+ async def fetch_my_liquidations(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
7461
7769
  """
7462
7770
  retrieves the users liquidated positions
7463
- :see: https://bybit-exchange.github.io/docs/api-explorer/v5/position/execution
7771
+
7772
+ https://bybit-exchange.github.io/docs/api-explorer/v5/position/execution
7773
+
7464
7774
  :param str [symbol]: unified CCXT market symbol
7465
7775
  :param int [since]: the earliest time in ms to fetch liquidations for
7466
7776
  :param int [limit]: the maximum number of liquidation structures to retrieve
@@ -7534,7 +7844,7 @@ class bybit(Exchange, ImplicitAPI):
7534
7844
  liquidations = self.add_pagination_cursor_to_result(response)
7535
7845
  return self.parse_liquidations(liquidations, market, since, limit)
7536
7846
 
7537
- def parse_liquidation(self, liquidation, market: Market = None):
7847
+ def parse_liquidation(self, liquidation, market: Market = None) -> Liquidation:
7538
7848
  #
7539
7849
  # {
7540
7850
  # "symbol": "ETHPERP",
@@ -7572,7 +7882,7 @@ class bybit(Exchange, ImplicitAPI):
7572
7882
  quoteValueString = Precise.string_mul(baseValueString, priceString)
7573
7883
  return self.safe_liquidation({
7574
7884
  'info': liquidation,
7575
- 'symbol': self.safe_symbol(marketId, market),
7885
+ 'symbol': self.safe_symbol(marketId, market, None, 'contract'),
7576
7886
  'contracts': self.parse_number(contractsString),
7577
7887
  'contractSize': self.parse_number(contractSizeString),
7578
7888
  'price': self.parse_number(priceString),
@@ -7611,8 +7921,10 @@ class bybit(Exchange, ImplicitAPI):
7611
7921
 
7612
7922
  async def fetch_leverage_tiers(self, symbols: Strings = None, params={}) -> LeverageTiers:
7613
7923
  """
7614
- :see: https://bybit-exchange.github.io/docs/v5/market/risk-limit
7615
7924
  retrieve information on the maximum leverage, for different trade sizes
7925
+
7926
+ https://bybit-exchange.github.io/docs/v5/market/risk-limit
7927
+
7616
7928
  :param str[] [symbols]: a list of unified market symbols
7617
7929
  :param dict [params]: extra parameters specific to the exchange API endpoint
7618
7930
  :param str [params.subType]: market subType, ['linear', 'inverse'], default is 'linear'
@@ -7627,11 +7939,11 @@ class bybit(Exchange, ImplicitAPI):
7627
7939
  if market['spot']:
7628
7940
  raise NotSupported(self.id + ' fetchLeverageTiers() is not supported for spot market')
7629
7941
  symbol = market['symbol']
7630
- data = await self.get_leverage_tiers_paginated(symbol, self.extend({'paginate': True, 'paginationCalls': 20}, params))
7942
+ data = await self.get_leverage_tiers_paginated(symbol, self.extend({'paginate': True, 'paginationCalls': 50}, params))
7631
7943
  symbols = self.market_symbols(symbols)
7632
7944
  return self.parse_leverage_tiers(data, symbols, 'symbol')
7633
7945
 
7634
- def parse_leverage_tiers(self, response, symbols: Strings = None, marketIdKey=None):
7946
+ def parse_leverage_tiers(self, response, symbols: Strings = None, marketIdKey=None) -> LeverageTiers:
7635
7947
  #
7636
7948
  # [
7637
7949
  # {
@@ -7685,6 +7997,7 @@ class bybit(Exchange, ImplicitAPI):
7685
7997
  minNotional = self.safe_number(info[i - 1], 'riskLimitValue')
7686
7998
  tiers.append({
7687
7999
  'tier': self.safe_integer(tier, 'id'),
8000
+ 'symbol': self.safe_symbol(marketId, market),
7688
8001
  'currency': market['settle'],
7689
8002
  'minNotional': minNotional,
7690
8003
  'maxNotional': self.safe_number(tier, 'riskLimitValue'),
@@ -7694,10 +8007,12 @@ class bybit(Exchange, ImplicitAPI):
7694
8007
  })
7695
8008
  return tiers
7696
8009
 
7697
- async def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
8010
+ async def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[FundingHistory]:
7698
8011
  """
7699
8012
  fetch the history of funding payments paid and received on self account
7700
- :see: https://bybit-exchange.github.io/docs/api-explorer/v5/position/execution
8013
+
8014
+ https://bybit-exchange.github.io/docs/api-explorer/v5/position/execution
8015
+
7701
8016
  :param str [symbol]: unified market symbol
7702
8017
  :param int [since]: the earliest time in ms to fetch funding history for
7703
8018
  :param int [limit]: the maximum number of funding history structures to retrieve
@@ -7733,7 +8048,7 @@ class bybit(Exchange, ImplicitAPI):
7733
8048
  fundings = self.add_pagination_cursor_to_result(response)
7734
8049
  return self.parse_incomes(fundings, market, since, limit)
7735
8050
 
7736
- def parse_income(self, income, market: Market = None):
8051
+ def parse_income(self, income, market: Market = None) -> object:
7737
8052
  #
7738
8053
  # {
7739
8054
  # "symbol": "XMRUSDT",
@@ -7781,14 +8096,16 @@ class bybit(Exchange, ImplicitAPI):
7781
8096
  'timestamp': timestamp,
7782
8097
  'datetime': self.iso8601(timestamp),
7783
8098
  'id': self.safe_string(income, 'execId'),
7784
- 'amount': self.safe_number(income, 'execQty'),
8099
+ 'amount': self.safe_number(income, 'execFee'),
7785
8100
  'rate': self.safe_number(income, 'feeRate'),
7786
8101
  }
7787
8102
 
7788
8103
  async def fetch_option(self, symbol: str, params={}) -> Option:
7789
8104
  """
7790
8105
  fetches option data that is commonly found in an option chain
7791
- :see: https://bybit-exchange.github.io/docs/v5/market/tickers
8106
+
8107
+ https://bybit-exchange.github.io/docs/v5/market/tickers
8108
+
7792
8109
  :param str symbol: unified market symbol
7793
8110
  :param dict [params]: extra parameters specific to the exchange API endpoint
7794
8111
  :returns dict: an `option chain structure <https://docs.ccxt.com/#/?id=option-chain-structure>`
@@ -7848,8 +8165,10 @@ class bybit(Exchange, ImplicitAPI):
7848
8165
  async def fetch_option_chain(self, code: str, params={}) -> OptionChain:
7849
8166
  """
7850
8167
  fetches data for an underlying asset that is commonly found in an option chain
7851
- :see: https://bybit-exchange.github.io/docs/v5/market/tickers
7852
- :param str currency: base currency to fetch an option chain for
8168
+
8169
+ https://bybit-exchange.github.io/docs/v5/market/tickers
8170
+
8171
+ :param str code: base currency to fetch an option chain for
7853
8172
  :param dict [params]: extra parameters specific to the exchange API endpoint
7854
8173
  :returns dict: a list of `option chain structures <https://docs.ccxt.com/#/?id=option-chain-structure>`
7855
8174
  """
@@ -7959,8 +8278,10 @@ class bybit(Exchange, ImplicitAPI):
7959
8278
  async def fetch_positions_history(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
7960
8279
  """
7961
8280
  fetches historical positions
7962
- :see: https://bybit-exchange.github.io/docs/v5/position/close-pnl
7963
- :param str [symbol]: unified market symbols, symbols must have the same subType(must all be linear or all be inverse)
8281
+
8282
+ https://bybit-exchange.github.io/docs/v5/position/close-pnl
8283
+
8284
+ :param str[] symbols: a list of unified market symbols
7964
8285
  :param int [since]: timestamp in ms of the earliest position to fetch, params["until"] - since <= 7 days
7965
8286
  :param int [limit]: the maximum amount of records to fetch, default=50, max=100
7966
8287
  :param dict params: extra parameters specific to the exchange api endpoint
@@ -8029,6 +8350,428 @@ class bybit(Exchange, ImplicitAPI):
8029
8350
  positions = self.parse_positions(rawPositions, symbols, params)
8030
8351
  return self.filter_by_since_limit(positions, since, limit)
8031
8352
 
8353
+ async def fetch_convert_currencies(self, params={}) -> Currencies:
8354
+ """
8355
+ fetches all available currencies that can be converted
8356
+
8357
+ https://bybit-exchange.github.io/docs/v5/asset/convert/convert-coin-list
8358
+
8359
+ :param dict [params]: extra parameters specific to the exchange API endpoint
8360
+ :param str [params.accountType]: eb_convert_uta, eb_convert_spot, eb_convert_funding, eb_convert_inverse, or eb_convert_contract
8361
+ :returns dict: an associative dictionary of currencies
8362
+ """
8363
+ await self.load_markets()
8364
+ accountType = None
8365
+ enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
8366
+ isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
8367
+ accountTypeDefault = 'eb_convert_uta' if isUnifiedAccount else 'eb_convert_spot'
8368
+ accountType, params = self.handle_option_and_params(params, 'fetchConvertCurrencies', 'accountType', accountTypeDefault)
8369
+ request: dict = {
8370
+ 'accountType': accountType,
8371
+ }
8372
+ response = await self.privateGetV5AssetExchangeQueryCoinList(self.extend(request, params))
8373
+ #
8374
+ # {
8375
+ # "retCode": 0,
8376
+ # "retMsg": "ok",
8377
+ # "result": {
8378
+ # "coins": [
8379
+ # {
8380
+ # "coin": "MATIC",
8381
+ # "fullName": "MATIC",
8382
+ # "icon": "https://s1.bycsi.com/app/assets/token/0552ae79c535c3095fa18f7b377dd2e9.svg",
8383
+ # "iconNight": "https://t1.bycsi.com/app/assets/token/f59301aef2d6ac2165c4c4603e672fb4.svg",
8384
+ # "accuracyLength": 8,
8385
+ # "coinType": "crypto",
8386
+ # "balance": "0",
8387
+ # "uBalance": "0",
8388
+ # "timePeriod": 0,
8389
+ # "singleFromMinLimit": "1.1",
8390
+ # "singleFromMaxLimit": "20001",
8391
+ # "singleToMinLimit": "0",
8392
+ # "singleToMaxLimit": "0",
8393
+ # "dailyFromMinLimit": "0",
8394
+ # "dailyFromMaxLimit": "0",
8395
+ # "dailyToMinLimit": "0",
8396
+ # "dailyToMaxLimit": "0",
8397
+ # "disableFrom": False,
8398
+ # "disableTo": False
8399
+ # },
8400
+ # ]
8401
+ # },
8402
+ # "retExtInfo": {},
8403
+ # "time": 1727256416250
8404
+ # }
8405
+ #
8406
+ result: dict = {}
8407
+ data = self.safe_dict(response, 'result', {})
8408
+ coins = self.safe_list(data, 'coins', [])
8409
+ for i in range(0, len(coins)):
8410
+ entry = coins[i]
8411
+ id = self.safe_string(entry, 'coin')
8412
+ disableFrom = self.safe_bool(entry, 'disableFrom')
8413
+ disableTo = self.safe_bool(entry, 'disableTo')
8414
+ inactive = (disableFrom or disableTo)
8415
+ code = self.safe_currency_code(id)
8416
+ result[code] = {
8417
+ 'info': entry,
8418
+ 'id': id,
8419
+ 'code': code,
8420
+ 'networks': None,
8421
+ 'type': self.safe_string(entry, 'coinType'),
8422
+ 'name': self.safe_string(entry, 'fullName'),
8423
+ 'active': not inactive,
8424
+ 'deposit': None,
8425
+ 'withdraw': self.safe_number(entry, 'balance'),
8426
+ 'fee': None,
8427
+ 'precision': None,
8428
+ 'limits': {
8429
+ 'amount': {
8430
+ 'min': self.safe_number(entry, 'singleFromMinLimit'),
8431
+ 'max': self.safe_number(entry, 'singleFromMaxLimit'),
8432
+ },
8433
+ 'withdraw': {
8434
+ 'min': None,
8435
+ 'max': None,
8436
+ },
8437
+ 'deposit': {
8438
+ 'min': None,
8439
+ 'max': None,
8440
+ },
8441
+ },
8442
+ 'created': None,
8443
+ }
8444
+ return result
8445
+
8446
+ async def fetch_convert_quote(self, fromCode: str, toCode: str, amount: Num = None, params={}) -> Conversion:
8447
+ """
8448
+ fetch a quote for converting from one currency to another
8449
+
8450
+ https://bybit-exchange.github.io/docs/v5/asset/convert/apply-quote
8451
+
8452
+ :param str fromCode: the currency that you want to sell and convert from
8453
+ :param str toCode: the currency that you want to buy and convert into
8454
+ :param float [amount]: how much you want to trade in units of the from currency
8455
+ :param dict [params]: extra parameters specific to the exchange API endpoint
8456
+ :param str [params.accountType]: eb_convert_uta, eb_convert_spot, eb_convert_funding, eb_convert_inverse, or eb_convert_contract
8457
+ :returns dict: a `conversion structure <https://docs.ccxt.com/#/?id=conversion-structure>`
8458
+ """
8459
+ await self.load_markets()
8460
+ accountType = None
8461
+ enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
8462
+ isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
8463
+ accountTypeDefault = 'eb_convert_uta' if isUnifiedAccount else 'eb_convert_spot'
8464
+ accountType, params = self.handle_option_and_params(params, 'fetchConvertQuote', 'accountType', accountTypeDefault)
8465
+ request: dict = {
8466
+ 'fromCoin': fromCode,
8467
+ 'toCoin': toCode,
8468
+ 'requestAmount': self.number_to_string(amount),
8469
+ 'requestCoin': fromCode,
8470
+ 'accountType': accountType,
8471
+ }
8472
+ response = await self.privatePostV5AssetExchangeQuoteApply(self.extend(request, params))
8473
+ #
8474
+ # {
8475
+ # "retCode": 0,
8476
+ # "retMsg": "ok",
8477
+ # "result": {
8478
+ # "quoteTxId": "1010020692439481682687668224",
8479
+ # "exchangeRate": "0.000015330836780000",
8480
+ # "fromCoin": "USDT",
8481
+ # "fromCoinType": "crypto",
8482
+ # "toCoin": "BTC",
8483
+ # "toCoinType": "crypto",
8484
+ # "fromAmount": "10",
8485
+ # "toAmount": "0.000153308367800000",
8486
+ # "expiredTime": "1727257413353",
8487
+ # "requestId": ""
8488
+ # },
8489
+ # "retExtInfo": {},
8490
+ # "time": 1727257398375
8491
+ # }
8492
+ #
8493
+ data = self.safe_dict(response, 'result', {})
8494
+ fromCurrencyId = self.safe_string(data, 'fromCoin', fromCode)
8495
+ fromCurrency = self.currency(fromCurrencyId)
8496
+ toCurrencyId = self.safe_string(data, 'toCoin', toCode)
8497
+ toCurrency = self.currency(toCurrencyId)
8498
+ return self.parse_conversion(data, fromCurrency, toCurrency)
8499
+
8500
+ async def create_convert_trade(self, id: str, fromCode: str, toCode: str, amount: Num = None, params={}) -> Conversion:
8501
+ """
8502
+ convert from one currency to another
8503
+
8504
+ https://bybit-exchange.github.io/docs/v5/asset/convert/confirm-quote
8505
+
8506
+ :param str id: the id of the trade that you want to make
8507
+ :param str fromCode: the currency that you want to sell and convert from
8508
+ :param str toCode: the currency that you want to buy and convert into
8509
+ :param float amount: how much you want to trade in units of the from currency
8510
+ :param dict [params]: extra parameters specific to the exchange API endpoint
8511
+ :returns dict: a `conversion structure <https://docs.ccxt.com/#/?id=conversion-structure>`
8512
+ """
8513
+ await self.load_markets()
8514
+ request: dict = {
8515
+ 'quoteTxId': id,
8516
+ }
8517
+ response = await self.privatePostV5AssetExchangeConvertExecute(self.extend(request, params))
8518
+ #
8519
+ # {
8520
+ # "retCode": 0,
8521
+ # "retMsg": "ok",
8522
+ # "result": {
8523
+ # "exchangeStatus": "processing",
8524
+ # "quoteTxId": "1010020692439483803499737088"
8525
+ # },
8526
+ # "retExtInfo": {},
8527
+ # "time": 1727257904969
8528
+ # }
8529
+ #
8530
+ data = self.safe_dict(response, 'result', {})
8531
+ return self.parse_conversion(data)
8532
+
8533
+ async def fetch_convert_trade(self, id: str, code: Str = None, params={}) -> Conversion:
8534
+ """
8535
+ fetch the data for a conversion trade
8536
+
8537
+ https://bybit-exchange.github.io/docs/v5/asset/convert/get-convert-result
8538
+
8539
+ :param str id: the id of the trade that you want to fetch
8540
+ :param str [code]: the unified currency code of the conversion trade
8541
+ :param dict [params]: extra parameters specific to the exchange API endpoint
8542
+ :param str [params.accountType]: eb_convert_uta, eb_convert_spot, eb_convert_funding, eb_convert_inverse, or eb_convert_contract
8543
+ :returns dict: a `conversion structure <https://docs.ccxt.com/#/?id=conversion-structure>`
8544
+ """
8545
+ await self.load_markets()
8546
+ accountType = None
8547
+ enableUnifiedMargin, enableUnifiedAccount = await self.is_unified_enabled()
8548
+ isUnifiedAccount = (enableUnifiedMargin or enableUnifiedAccount)
8549
+ accountTypeDefault = 'eb_convert_uta' if isUnifiedAccount else 'eb_convert_spot'
8550
+ accountType, params = self.handle_option_and_params(params, 'fetchConvertQuote', 'accountType', accountTypeDefault)
8551
+ request: dict = {
8552
+ 'quoteTxId': id,
8553
+ 'accountType': accountType,
8554
+ }
8555
+ response = await self.privateGetV5AssetExchangeConvertResultQuery(self.extend(request, params))
8556
+ #
8557
+ # {
8558
+ # "retCode": 0,
8559
+ # "retMsg": "ok",
8560
+ # "result": {
8561
+ # "result": {
8562
+ # "accountType": "eb_convert_uta",
8563
+ # "exchangeTxId": "1010020692439483803499737088",
8564
+ # "userId": "100406395",
8565
+ # "fromCoin": "USDT",
8566
+ # "fromCoinType": "crypto",
8567
+ # "fromAmount": "10",
8568
+ # "toCoin": "BTC",
8569
+ # "toCoinType": "crypto",
8570
+ # "toAmount": "0.00015344889",
8571
+ # "exchangeStatus": "success",
8572
+ # "extInfo": {},
8573
+ # "convertRate": "0.000015344889",
8574
+ # "createdAt": "1727257904726"
8575
+ # }
8576
+ # },
8577
+ # "retExtInfo": {},
8578
+ # "time": 1727258257216
8579
+ # }
8580
+ #
8581
+ data = self.safe_dict(response, 'result', {})
8582
+ result = self.safe_dict(data, 'result', {})
8583
+ fromCurrencyId = self.safe_string(result, 'fromCoin')
8584
+ toCurrencyId = self.safe_string(result, 'toCoin')
8585
+ fromCurrency = None
8586
+ toCurrency = None
8587
+ if fromCurrencyId is not None:
8588
+ fromCurrency = self.currency(fromCurrencyId)
8589
+ if toCurrencyId is not None:
8590
+ toCurrency = self.currency(toCurrencyId)
8591
+ return self.parse_conversion(result, fromCurrency, toCurrency)
8592
+
8593
+ async def fetch_convert_trade_history(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Conversion]:
8594
+ """
8595
+ fetch the users history of conversion trades
8596
+
8597
+ https://bybit-exchange.github.io/docs/v5/asset/convert/get-convert-history
8598
+
8599
+ :param str [code]: the unified currency code
8600
+ :param int [since]: the earliest time in ms to fetch conversions for
8601
+ :param int [limit]: the maximum number of conversion structures to retrieve
8602
+ :param dict [params]: extra parameters specific to the exchange API endpoint
8603
+ :param str [params.accountType]: eb_convert_uta, eb_convert_spot, eb_convert_funding, eb_convert_inverse, or eb_convert_contract
8604
+ :returns dict[]: a list of `conversion structures <https://docs.ccxt.com/#/?id=conversion-structure>`
8605
+ """
8606
+ await self.load_markets()
8607
+ request: dict = {}
8608
+ if limit is not None:
8609
+ request['limit'] = limit
8610
+ response = await self.privateGetV5AssetExchangeQueryConvertHistory(self.extend(request, params))
8611
+ #
8612
+ # {
8613
+ # "retCode": 0,
8614
+ # "retMsg": "ok",
8615
+ # "result": {
8616
+ # "list": [
8617
+ # {
8618
+ # "accountType": "eb_convert_uta",
8619
+ # "exchangeTxId": "1010020692439483803499737088",
8620
+ # "userId": "100406395",
8621
+ # "fromCoin": "USDT",
8622
+ # "fromCoinType": "crypto",
8623
+ # "fromAmount": "10",
8624
+ # "toCoin": "BTC",
8625
+ # "toCoinType": "crypto",
8626
+ # "toAmount": "0.00015344889",
8627
+ # "exchangeStatus": "success",
8628
+ # "extInfo": {},
8629
+ # "convertRate": "0.000015344889",
8630
+ # "createdAt": "1727257904726"
8631
+ # }
8632
+ # ]
8633
+ # },
8634
+ # "retExtInfo": {},
8635
+ # "time": 1727258761874
8636
+ # }
8637
+ #
8638
+ data = self.safe_dict(response, 'result', {})
8639
+ dataList = self.safe_list(data, 'list', [])
8640
+ return self.parse_conversions(dataList, code, 'fromCoin', 'toCoin', since, limit)
8641
+
8642
+ def parse_conversion(self, conversion: dict, fromCurrency: Currency = None, toCurrency: Currency = None) -> Conversion:
8643
+ #
8644
+ # fetchConvertQuote
8645
+ #
8646
+ # {
8647
+ # "quoteTxId": "1010020692439481682687668224",
8648
+ # "exchangeRate": "0.000015330836780000",
8649
+ # "fromCoin": "USDT",
8650
+ # "fromCoinType": "crypto",
8651
+ # "toCoin": "BTC",
8652
+ # "toCoinType": "crypto",
8653
+ # "fromAmount": "10",
8654
+ # "toAmount": "0.000153308367800000",
8655
+ # "expiredTime": "1727257413353",
8656
+ # "requestId": ""
8657
+ # }
8658
+ #
8659
+ # createConvertTrade
8660
+ #
8661
+ # {
8662
+ # "exchangeStatus": "processing",
8663
+ # "quoteTxId": "1010020692439483803499737088"
8664
+ # }
8665
+ #
8666
+ # fetchConvertTrade, fetchConvertTradeHistory
8667
+ #
8668
+ # {
8669
+ # "accountType": "eb_convert_uta",
8670
+ # "exchangeTxId": "1010020692439483803499737088",
8671
+ # "userId": "100406395",
8672
+ # "fromCoin": "USDT",
8673
+ # "fromCoinType": "crypto",
8674
+ # "fromAmount": "10",
8675
+ # "toCoin": "BTC",
8676
+ # "toCoinType": "crypto",
8677
+ # "toAmount": "0.00015344889",
8678
+ # "exchangeStatus": "success",
8679
+ # "extInfo": {},
8680
+ # "convertRate": "0.000015344889",
8681
+ # "createdAt": "1727257904726"
8682
+ # }
8683
+ #
8684
+ timestamp = self.safe_integer_2(conversion, 'expiredTime', 'createdAt')
8685
+ fromCoin = self.safe_string(conversion, 'fromCoin')
8686
+ fromCode = self.safe_currency_code(fromCoin, fromCurrency)
8687
+ to = self.safe_string(conversion, 'toCoin')
8688
+ toCode = self.safe_currency_code(to, toCurrency)
8689
+ return {
8690
+ 'info': conversion,
8691
+ 'timestamp': timestamp,
8692
+ 'datetime': self.iso8601(timestamp),
8693
+ 'id': self.safe_string_2(conversion, 'quoteTxId', 'exchangeTxId'),
8694
+ 'fromCurrency': fromCode,
8695
+ 'fromAmount': self.safe_number(conversion, 'fromAmount'),
8696
+ 'toCurrency': toCode,
8697
+ 'toAmount': self.safe_number(conversion, 'toAmount'),
8698
+ 'price': None,
8699
+ 'fee': None,
8700
+ }
8701
+
8702
+ async def fetch_long_short_ratio_history(self, symbol: Str = None, timeframe: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LongShortRatio]:
8703
+ """
8704
+ fetches the long short ratio history for a unified market symbol
8705
+
8706
+ https://bybit-exchange.github.io/docs/v5/market/long-short-ratio
8707
+
8708
+ :param str symbol: unified symbol of the market to fetch the long short ratio for
8709
+ :param str [timeframe]: the period for the ratio, default is 24 hours
8710
+ :param int [since]: the earliest time in ms to fetch ratios for
8711
+ :param int [limit]: the maximum number of long short ratio structures to retrieve
8712
+ :param dict [params]: extra parameters specific to the exchange API endpoint
8713
+ :returns dict[]: an array of `long short ratio structures <https://docs.ccxt.com/#/?id=long-short-ratio-structure>`
8714
+ """
8715
+ await self.load_markets()
8716
+ market = self.market(symbol)
8717
+ type = None
8718
+ type, params = self.get_bybit_type('fetchLongShortRatioHistory', market, params)
8719
+ if type == 'spot' or type == 'option':
8720
+ raise NotSupported(self.id + ' fetchLongShortRatioHistory() only support linear and inverse markets')
8721
+ if timeframe is None:
8722
+ timeframe = '1d'
8723
+ request: dict = {
8724
+ 'symbol': market['id'],
8725
+ 'period': timeframe,
8726
+ 'category': type,
8727
+ }
8728
+ if limit is not None:
8729
+ request['limit'] = limit
8730
+ response = await self.publicGetV5MarketAccountRatio(self.extend(request, params))
8731
+ #
8732
+ # {
8733
+ # "retCode": 0,
8734
+ # "retMsg": "OK",
8735
+ # "result": {
8736
+ # "list": [
8737
+ # {
8738
+ # "symbol": "BTCUSDT",
8739
+ # "buyRatio": "0.5707",
8740
+ # "sellRatio": "0.4293",
8741
+ # "timestamp": "1729123200000"
8742
+ # },
8743
+ # ]
8744
+ # },
8745
+ # "retExtInfo": {},
8746
+ # "time": 1729147842516
8747
+ # }
8748
+ #
8749
+ result = self.safe_dict(response, 'result', {})
8750
+ data = self.safe_list(result, 'list', [])
8751
+ return self.parse_long_short_ratio_history(data, market)
8752
+
8753
+ def parse_long_short_ratio(self, info: dict, market: Market = None) -> LongShortRatio:
8754
+ #
8755
+ # {
8756
+ # "symbol": "BTCUSDT",
8757
+ # "buyRatio": "0.5707",
8758
+ # "sellRatio": "0.4293",
8759
+ # "timestamp": "1729123200000"
8760
+ # }
8761
+ #
8762
+ marketId = self.safe_string(info, 'symbol')
8763
+ timestamp = self.safe_integer_omit_zero(info, 'timestamp')
8764
+ longString = self.safe_string(info, 'buyRatio')
8765
+ shortString = self.safe_string(info, 'sellRatio')
8766
+ return {
8767
+ 'info': info,
8768
+ 'symbol': self.safe_symbol(marketId, market, None, 'contract'),
8769
+ 'timestamp': timestamp,
8770
+ 'datetime': self.iso8601(timestamp),
8771
+ 'timeframe': None,
8772
+ 'longShortRatio': self.parse_to_numeric(Precise.string_div(longString, shortString)),
8773
+ }
8774
+
8032
8775
  def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
8033
8776
  url = self.implode_hostname(self.urls['api'][api]) + '/' + path
8034
8777
  if api == 'public':
@@ -8074,7 +8817,7 @@ class bybit(Exchange, ImplicitAPI):
8074
8817
  authFull = auth_base + body
8075
8818
  else:
8076
8819
  authFull = auth_base + queryEncoded
8077
- url += '?' + self.rawencode(query)
8820
+ url += '?' + queryEncoded
8078
8821
  signature = None
8079
8822
  if self.secret.find('PRIVATE KEY') > -1:
8080
8823
  signature = self.rsa(authFull, self.secret, 'sha256')
@@ -8088,7 +8831,7 @@ class bybit(Exchange, ImplicitAPI):
8088
8831
  'timestamp': timestamp,
8089
8832
  })
8090
8833
  sortedQuery = self.keysort(query)
8091
- auth = self.rawencode(sortedQuery)
8834
+ auth = self.rawencode(sortedQuery, True)
8092
8835
  signature = None
8093
8836
  if self.secret.find('PRIVATE KEY') > -1:
8094
8837
  signature = self.rsa(auth, self.secret, 'sha256')
@@ -8110,7 +8853,7 @@ class bybit(Exchange, ImplicitAPI):
8110
8853
  'Content-Type': 'application/json',
8111
8854
  }
8112
8855
  else:
8113
- url += '?' + self.rawencode(sortedQuery)
8856
+ url += '?' + self.rawencode(sortedQuery, True)
8114
8857
  url += '&sign=' + signature
8115
8858
  if method == 'POST':
8116
8859
  brokerId = self.safe_string(self.options, 'brokerId')
@@ -8153,6 +8896,8 @@ class bybit(Exchange, ImplicitAPI):
8153
8896
  feedback = self.id + ' private api uses /user/v3/private/query-api to check if you have a unified account. The API key of user id must own one of permissions: "Account Transfer", "Subaccount Transfer", "Withdrawal" ' + body
8154
8897
  else:
8155
8898
  feedback = self.id + ' ' + body
8899
+ if body.find('Withdraw address chain or destination tag are not equal') > -1:
8900
+ feedback = feedback + '; You might also need to ensure the address is whitelisted'
8156
8901
  self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
8157
8902
  self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
8158
8903
  raise ExchangeError(feedback) # unknown message