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