ccxt 4.2.77__py2.py3-none-any.whl → 4.4.49__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 (546) hide show
  1. ccxt/__init__.py +36 -14
  2. ccxt/abstract/alpaca.py +4 -0
  3. ccxt/abstract/bigone.py +1 -1
  4. ccxt/abstract/binance.py +112 -48
  5. ccxt/abstract/binancecoinm.py +112 -48
  6. ccxt/abstract/binanceus.py +147 -83
  7. ccxt/abstract/binanceusdm.py +112 -48
  8. ccxt/abstract/bingx.py +133 -78
  9. ccxt/abstract/bitbank.py +5 -0
  10. ccxt/abstract/bitfinex.py +136 -65
  11. ccxt/abstract/bitfinex1.py +69 -0
  12. ccxt/abstract/bitflyer.py +1 -0
  13. ccxt/abstract/bitget.py +8 -1
  14. ccxt/abstract/bitmart.py +13 -1
  15. ccxt/abstract/bitopro.py +1 -0
  16. ccxt/abstract/bitpanda.py +0 -12
  17. ccxt/abstract/bitrue.py +3 -3
  18. ccxt/abstract/bitstamp.py +26 -3
  19. ccxt/abstract/blofin.py +24 -0
  20. ccxt/abstract/btcbox.py +1 -0
  21. ccxt/abstract/bybit.py +29 -14
  22. ccxt/abstract/cex.py +28 -29
  23. ccxt/abstract/coinbase.py +6 -0
  24. ccxt/abstract/coinbaseadvanced.py +94 -0
  25. ccxt/abstract/{coinbasepro.py → coinbaseexchange.py} +1 -0
  26. ccxt/abstract/coinbaseinternational.py +1 -1
  27. ccxt/abstract/coincatch.py +94 -0
  28. ccxt/abstract/coinex.py +233 -123
  29. ccxt/abstract/coinmetro.py +1 -0
  30. ccxt/abstract/cryptocom.py +14 -0
  31. ccxt/abstract/defx.py +69 -0
  32. ccxt/abstract/deribit.py +1 -0
  33. ccxt/abstract/digifinex.py +1 -0
  34. ccxt/abstract/ellipx.py +25 -0
  35. ccxt/abstract/gate.py +20 -0
  36. ccxt/abstract/gateio.py +20 -0
  37. ccxt/abstract/gemini.py +1 -0
  38. ccxt/abstract/hashkey.py +67 -0
  39. ccxt/abstract/hyperliquid.py +1 -1
  40. ccxt/abstract/independentreserve.py +6 -0
  41. ccxt/abstract/kraken.py +4 -3
  42. ccxt/abstract/krakenfutures.py +4 -0
  43. ccxt/abstract/kucoin.py +24 -0
  44. ccxt/abstract/kucoinfutures.py +34 -0
  45. ccxt/abstract/luno.py +2 -0
  46. ccxt/abstract/mexc.py +4 -0
  47. ccxt/abstract/myokx.py +340 -0
  48. ccxt/abstract/oceanex.py +5 -0
  49. ccxt/abstract/okx.py +30 -0
  50. ccxt/abstract/onetrading.py +0 -12
  51. ccxt/abstract/oxfun.py +34 -0
  52. ccxt/abstract/paradex.py +40 -0
  53. ccxt/abstract/phemex.py +1 -0
  54. ccxt/abstract/upbit.py +4 -0
  55. ccxt/abstract/vertex.py +19 -0
  56. ccxt/abstract/whitebit.py +31 -1
  57. ccxt/abstract/woo.py +6 -2
  58. ccxt/abstract/woofipro.py +119 -0
  59. ccxt/abstract/xt.py +153 -0
  60. ccxt/abstract/zonda.py +6 -0
  61. ccxt/ace.py +164 -60
  62. ccxt/alpaca.py +727 -63
  63. ccxt/ascendex.py +395 -249
  64. ccxt/async_support/__init__.py +36 -14
  65. ccxt/async_support/ace.py +164 -60
  66. ccxt/async_support/alpaca.py +727 -63
  67. ccxt/async_support/ascendex.py +396 -249
  68. ccxt/async_support/base/exchange.py +531 -155
  69. ccxt/async_support/base/ws/aiohttp_client.py +28 -5
  70. ccxt/async_support/base/ws/cache.py +3 -2
  71. ccxt/async_support/base/ws/client.py +26 -5
  72. ccxt/async_support/base/ws/fast_client.py +4 -3
  73. ccxt/async_support/base/ws/functions.py +1 -1
  74. ccxt/async_support/base/ws/future.py +40 -31
  75. ccxt/async_support/base/ws/order_book_side.py +3 -0
  76. ccxt/async_support/bequant.py +1 -1
  77. ccxt/async_support/bigone.py +329 -202
  78. ccxt/async_support/binance.py +3030 -1087
  79. ccxt/async_support/binancecoinm.py +2 -1
  80. ccxt/async_support/binanceus.py +12 -1
  81. ccxt/async_support/binanceusdm.py +3 -1
  82. ccxt/async_support/bingx.py +3205 -937
  83. ccxt/async_support/bit2c.py +119 -38
  84. ccxt/async_support/bitbank.py +215 -76
  85. ccxt/async_support/bitbns.py +124 -53
  86. ccxt/async_support/bitfinex.py +3236 -1078
  87. ccxt/async_support/bitfinex1.py +1711 -0
  88. ccxt/async_support/bitflyer.py +238 -49
  89. ccxt/async_support/bitget.py +1525 -573
  90. ccxt/async_support/bithumb.py +199 -65
  91. ccxt/async_support/bitmart.py +1320 -435
  92. ccxt/async_support/bitmex.py +308 -111
  93. ccxt/async_support/bitopro.py +256 -96
  94. ccxt/async_support/bitrue.py +365 -233
  95. ccxt/async_support/bitso.py +201 -89
  96. ccxt/async_support/bitstamp.py +438 -269
  97. ccxt/async_support/bitteam.py +179 -73
  98. ccxt/async_support/bitvavo.py +180 -70
  99. ccxt/async_support/bl3p.py +92 -25
  100. ccxt/async_support/blockchaincom.py +193 -79
  101. ccxt/async_support/blofin.py +392 -148
  102. ccxt/async_support/btcalpha.py +161 -55
  103. ccxt/async_support/btcbox.py +250 -34
  104. ccxt/async_support/btcmarkets.py +232 -85
  105. ccxt/async_support/btcturk.py +159 -60
  106. ccxt/async_support/bybit.py +2231 -1193
  107. ccxt/async_support/cex.py +1409 -1329
  108. ccxt/async_support/coinbase.py +1454 -287
  109. ccxt/async_support/coinbaseadvanced.py +17 -0
  110. ccxt/async_support/{coinbasepro.py → coinbaseexchange.py} +233 -99
  111. ccxt/async_support/coinbaseinternational.py +428 -88
  112. ccxt/async_support/coincatch.py +5152 -0
  113. ccxt/async_support/coincheck.py +121 -38
  114. ccxt/async_support/coinex.py +4020 -3339
  115. ccxt/async_support/coinlist.py +273 -116
  116. ccxt/async_support/coinmate.py +204 -97
  117. ccxt/async_support/coinmetro.py +203 -110
  118. ccxt/async_support/coinone.py +142 -68
  119. ccxt/async_support/coinsph.py +223 -97
  120. ccxt/async_support/coinspot.py +137 -62
  121. ccxt/async_support/cryptocom.py +515 -185
  122. ccxt/async_support/currencycom.py +203 -85
  123. ccxt/async_support/defx.py +2066 -0
  124. ccxt/async_support/delta.py +404 -109
  125. ccxt/async_support/deribit.py +639 -323
  126. ccxt/async_support/digifinex.py +465 -233
  127. ccxt/async_support/ellipx.py +1887 -0
  128. ccxt/async_support/exmo.py +317 -128
  129. ccxt/async_support/gate.py +1472 -463
  130. ccxt/async_support/gemini.py +206 -84
  131. ccxt/async_support/hashkey.py +4164 -0
  132. ccxt/async_support/hitbtc.py +433 -178
  133. ccxt/async_support/hollaex.py +207 -83
  134. ccxt/async_support/htx.py +1095 -563
  135. ccxt/async_support/huobijp.py +178 -56
  136. ccxt/async_support/hyperliquid.py +1678 -292
  137. ccxt/async_support/idex.py +219 -95
  138. ccxt/async_support/independentreserve.py +300 -31
  139. ccxt/async_support/indodax.py +226 -62
  140. ccxt/async_support/kraken.py +871 -354
  141. ccxt/async_support/krakenfutures.py +324 -100
  142. ccxt/async_support/kucoin.py +917 -357
  143. ccxt/async_support/kucoinfutures.py +1004 -149
  144. ccxt/async_support/kuna.py +198 -107
  145. ccxt/async_support/latoken.py +199 -79
  146. ccxt/async_support/lbank.py +360 -113
  147. ccxt/async_support/luno.py +185 -62
  148. ccxt/async_support/lykke.py +168 -55
  149. ccxt/async_support/mercado.py +101 -29
  150. ccxt/async_support/mexc.py +995 -429
  151. ccxt/async_support/myokx.py +53 -0
  152. ccxt/async_support/ndax.py +234 -82
  153. ccxt/async_support/novadax.py +195 -75
  154. ccxt/async_support/oceanex.py +244 -59
  155. ccxt/async_support/okcoin.py +301 -165
  156. ccxt/async_support/okx.py +1776 -454
  157. ccxt/async_support/onetrading.py +198 -414
  158. ccxt/async_support/oxfun.py +2898 -0
  159. ccxt/async_support/p2b.py +142 -52
  160. ccxt/async_support/paradex.py +2085 -0
  161. ccxt/async_support/paymium.py +56 -32
  162. ccxt/async_support/phemex.py +572 -196
  163. ccxt/async_support/poloniex.py +218 -95
  164. ccxt/async_support/poloniexfutures.py +260 -92
  165. ccxt/async_support/probit.py +143 -110
  166. ccxt/async_support/timex.py +123 -70
  167. ccxt/async_support/tokocrypto.py +129 -93
  168. ccxt/async_support/tradeogre.py +39 -25
  169. ccxt/async_support/upbit.py +322 -113
  170. ccxt/async_support/vertex.py +2983 -0
  171. ccxt/async_support/wavesexchange.py +227 -173
  172. ccxt/async_support/wazirx.py +145 -65
  173. ccxt/async_support/whitebit.py +533 -138
  174. ccxt/async_support/woo.py +1137 -296
  175. ccxt/async_support/woofipro.py +2716 -0
  176. ccxt/async_support/xt.py +4628 -0
  177. ccxt/async_support/yobit.py +160 -92
  178. ccxt/async_support/zaif.py +80 -33
  179. ccxt/async_support/zonda.py +140 -69
  180. ccxt/base/errors.py +51 -20
  181. ccxt/base/exchange.py +1722 -480
  182. ccxt/base/precise.py +10 -0
  183. ccxt/base/types.py +223 -4
  184. ccxt/bequant.py +1 -1
  185. ccxt/bigone.py +329 -202
  186. ccxt/binance.py +3030 -1087
  187. ccxt/binancecoinm.py +2 -1
  188. ccxt/binanceus.py +12 -1
  189. ccxt/binanceusdm.py +3 -1
  190. ccxt/bingx.py +3205 -937
  191. ccxt/bit2c.py +119 -38
  192. ccxt/bitbank.py +215 -76
  193. ccxt/bitbns.py +124 -53
  194. ccxt/bitfinex.py +3235 -1078
  195. ccxt/bitfinex1.py +1710 -0
  196. ccxt/bitflyer.py +238 -49
  197. ccxt/bitget.py +1525 -573
  198. ccxt/bithumb.py +198 -65
  199. ccxt/bitmart.py +1320 -435
  200. ccxt/bitmex.py +308 -111
  201. ccxt/bitopro.py +256 -96
  202. ccxt/bitrue.py +365 -233
  203. ccxt/bitso.py +201 -89
  204. ccxt/bitstamp.py +438 -269
  205. ccxt/bitteam.py +179 -73
  206. ccxt/bitvavo.py +180 -70
  207. ccxt/bl3p.py +92 -25
  208. ccxt/blockchaincom.py +193 -79
  209. ccxt/blofin.py +392 -148
  210. ccxt/btcalpha.py +161 -55
  211. ccxt/btcbox.py +250 -34
  212. ccxt/btcmarkets.py +232 -85
  213. ccxt/btcturk.py +159 -60
  214. ccxt/bybit.py +2231 -1193
  215. ccxt/cex.py +1408 -1329
  216. ccxt/coinbase.py +1454 -287
  217. ccxt/coinbaseadvanced.py +17 -0
  218. ccxt/{coinbasepro.py → coinbaseexchange.py} +233 -99
  219. ccxt/coinbaseinternational.py +428 -88
  220. ccxt/coincatch.py +5152 -0
  221. ccxt/coincheck.py +121 -38
  222. ccxt/coinex.py +4020 -3339
  223. ccxt/coinlist.py +273 -116
  224. ccxt/coinmate.py +204 -97
  225. ccxt/coinmetro.py +203 -110
  226. ccxt/coinone.py +142 -68
  227. ccxt/coinsph.py +223 -97
  228. ccxt/coinspot.py +137 -62
  229. ccxt/cryptocom.py +515 -185
  230. ccxt/currencycom.py +203 -85
  231. ccxt/defx.py +2065 -0
  232. ccxt/delta.py +404 -109
  233. ccxt/deribit.py +639 -323
  234. ccxt/digifinex.py +465 -233
  235. ccxt/ellipx.py +1887 -0
  236. ccxt/exmo.py +317 -128
  237. ccxt/gate.py +1472 -463
  238. ccxt/gemini.py +206 -84
  239. ccxt/hashkey.py +4164 -0
  240. ccxt/hitbtc.py +433 -178
  241. ccxt/hollaex.py +207 -83
  242. ccxt/htx.py +1095 -563
  243. ccxt/huobijp.py +178 -56
  244. ccxt/hyperliquid.py +1677 -292
  245. ccxt/idex.py +219 -95
  246. ccxt/independentreserve.py +299 -31
  247. ccxt/indodax.py +226 -62
  248. ccxt/kraken.py +871 -354
  249. ccxt/krakenfutures.py +324 -100
  250. ccxt/kucoin.py +917 -357
  251. ccxt/kucoinfutures.py +1004 -149
  252. ccxt/kuna.py +198 -107
  253. ccxt/latoken.py +199 -79
  254. ccxt/lbank.py +360 -113
  255. ccxt/luno.py +185 -62
  256. ccxt/lykke.py +168 -55
  257. ccxt/mercado.py +101 -29
  258. ccxt/mexc.py +994 -429
  259. ccxt/myokx.py +53 -0
  260. ccxt/ndax.py +234 -82
  261. ccxt/novadax.py +195 -75
  262. ccxt/oceanex.py +244 -59
  263. ccxt/okcoin.py +301 -165
  264. ccxt/okx.py +1776 -454
  265. ccxt/onetrading.py +198 -414
  266. ccxt/oxfun.py +2897 -0
  267. ccxt/p2b.py +142 -52
  268. ccxt/paradex.py +2085 -0
  269. ccxt/paymium.py +56 -32
  270. ccxt/phemex.py +572 -196
  271. ccxt/poloniex.py +218 -95
  272. ccxt/poloniexfutures.py +260 -92
  273. ccxt/pro/__init__.py +29 -5
  274. ccxt/pro/alpaca.py +32 -17
  275. ccxt/pro/ascendex.py +62 -14
  276. ccxt/pro/bequant.py +4 -0
  277. ccxt/pro/binance.py +1596 -329
  278. ccxt/pro/binancecoinm.py +1 -0
  279. ccxt/pro/binanceus.py +2 -9
  280. ccxt/pro/binanceusdm.py +2 -0
  281. ccxt/pro/bingx.py +527 -134
  282. ccxt/pro/bitcoincom.py +4 -1
  283. ccxt/pro/bitfinex.py +731 -266
  284. ccxt/pro/bitfinex1.py +635 -0
  285. ccxt/pro/bitget.py +726 -357
  286. ccxt/pro/bithumb.py +380 -0
  287. ccxt/pro/bitmart.py +143 -39
  288. ccxt/pro/bitmex.py +199 -40
  289. ccxt/pro/bitopro.py +25 -13
  290. ccxt/pro/bitrue.py +31 -32
  291. ccxt/pro/bitstamp.py +7 -6
  292. ccxt/pro/bitvavo.py +203 -81
  293. ccxt/pro/blockchaincom.py +30 -17
  294. ccxt/pro/blofin.py +692 -0
  295. ccxt/pro/bybit.py +791 -82
  296. ccxt/pro/cex.py +99 -51
  297. ccxt/pro/coinbase.py +220 -30
  298. ccxt/{async_support/hitbtc3.py → pro/coinbaseadvanced.py} +5 -5
  299. ccxt/pro/{coinbasepro.py → coinbaseexchange.py} +19 -19
  300. ccxt/pro/coinbaseinternational.py +193 -30
  301. ccxt/pro/coincatch.py +1464 -0
  302. ccxt/pro/coincheck.py +11 -6
  303. ccxt/pro/coinex.py +965 -665
  304. ccxt/pro/coinone.py +17 -10
  305. ccxt/pro/cryptocom.py +446 -66
  306. ccxt/pro/currencycom.py +11 -10
  307. ccxt/pro/defx.py +832 -0
  308. ccxt/pro/deribit.py +167 -31
  309. ccxt/pro/exmo.py +252 -20
  310. ccxt/pro/gate.py +729 -64
  311. ccxt/pro/gemini.py +44 -26
  312. ccxt/pro/hashkey.py +802 -0
  313. ccxt/pro/hitbtc.py +208 -103
  314. ccxt/pro/hollaex.py +25 -9
  315. ccxt/pro/htx.py +83 -39
  316. ccxt/pro/huobijp.py +17 -16
  317. ccxt/pro/hyperliquid.py +502 -31
  318. ccxt/pro/idex.py +28 -13
  319. ccxt/pro/independentreserve.py +21 -16
  320. ccxt/pro/kraken.py +298 -51
  321. ccxt/pro/krakenfutures.py +166 -75
  322. ccxt/pro/kucoin.py +395 -77
  323. ccxt/pro/kucoinfutures.py +400 -99
  324. ccxt/pro/lbank.py +52 -31
  325. ccxt/pro/luno.py +12 -10
  326. ccxt/pro/mexc.py +400 -50
  327. ccxt/pro/myokx.py +28 -0
  328. ccxt/pro/ndax.py +25 -12
  329. ccxt/pro/okcoin.py +28 -9
  330. ccxt/pro/okx.py +935 -124
  331. ccxt/pro/onetrading.py +41 -24
  332. ccxt/pro/oxfun.py +1054 -0
  333. ccxt/pro/p2b.py +100 -24
  334. ccxt/pro/paradex.py +352 -0
  335. ccxt/pro/phemex.py +92 -33
  336. ccxt/pro/poloniex.py +128 -49
  337. ccxt/pro/poloniexfutures.py +53 -32
  338. ccxt/pro/probit.py +92 -85
  339. ccxt/pro/upbit.py +401 -8
  340. ccxt/pro/vertex.py +943 -0
  341. ccxt/pro/wazirx.py +46 -28
  342. ccxt/pro/whitebit.py +65 -12
  343. ccxt/pro/woo.py +437 -65
  344. ccxt/pro/woofipro.py +1271 -0
  345. ccxt/pro/xt.py +1067 -0
  346. ccxt/probit.py +143 -110
  347. ccxt/static_dependencies/__init__.py +1 -1
  348. ccxt/static_dependencies/lark/__init__.py +38 -0
  349. ccxt/static_dependencies/lark/__pyinstaller/__init__.py +6 -0
  350. ccxt/static_dependencies/lark/__pyinstaller/hook-lark.py +14 -0
  351. ccxt/static_dependencies/lark/ast_utils.py +59 -0
  352. ccxt/static_dependencies/lark/common.py +86 -0
  353. ccxt/static_dependencies/lark/exceptions.py +292 -0
  354. ccxt/static_dependencies/lark/grammar.py +130 -0
  355. ccxt/static_dependencies/lark/grammars/__init__.py +0 -0
  356. ccxt/static_dependencies/lark/indenter.py +143 -0
  357. ccxt/static_dependencies/lark/lark.py +658 -0
  358. ccxt/static_dependencies/lark/lexer.py +678 -0
  359. ccxt/static_dependencies/lark/load_grammar.py +1428 -0
  360. ccxt/static_dependencies/lark/parse_tree_builder.py +391 -0
  361. ccxt/static_dependencies/lark/parser_frontends.py +257 -0
  362. ccxt/static_dependencies/lark/parsers/__init__.py +0 -0
  363. ccxt/static_dependencies/lark/parsers/cyk.py +340 -0
  364. ccxt/static_dependencies/lark/parsers/earley.py +314 -0
  365. ccxt/static_dependencies/lark/parsers/earley_common.py +42 -0
  366. ccxt/static_dependencies/lark/parsers/earley_forest.py +801 -0
  367. ccxt/static_dependencies/lark/parsers/grammar_analysis.py +203 -0
  368. ccxt/static_dependencies/lark/parsers/lalr_analysis.py +332 -0
  369. ccxt/static_dependencies/lark/parsers/lalr_interactive_parser.py +158 -0
  370. ccxt/static_dependencies/lark/parsers/lalr_parser.py +122 -0
  371. ccxt/static_dependencies/lark/parsers/lalr_parser_state.py +110 -0
  372. ccxt/static_dependencies/lark/parsers/xearley.py +165 -0
  373. ccxt/static_dependencies/lark/py.typed +0 -0
  374. ccxt/static_dependencies/lark/reconstruct.py +107 -0
  375. ccxt/static_dependencies/lark/tools/__init__.py +70 -0
  376. ccxt/static_dependencies/lark/tools/nearley.py +202 -0
  377. ccxt/static_dependencies/lark/tools/serialize.py +32 -0
  378. ccxt/static_dependencies/lark/tools/standalone.py +196 -0
  379. ccxt/static_dependencies/lark/tree.py +267 -0
  380. ccxt/static_dependencies/lark/tree_matcher.py +186 -0
  381. ccxt/static_dependencies/lark/tree_templates.py +180 -0
  382. ccxt/static_dependencies/lark/utils.py +343 -0
  383. ccxt/static_dependencies/lark/visitors.py +596 -0
  384. ccxt/static_dependencies/marshmallow/__init__.py +81 -0
  385. ccxt/static_dependencies/marshmallow/base.py +65 -0
  386. ccxt/static_dependencies/marshmallow/class_registry.py +94 -0
  387. ccxt/static_dependencies/marshmallow/decorators.py +231 -0
  388. ccxt/static_dependencies/marshmallow/error_store.py +60 -0
  389. ccxt/static_dependencies/marshmallow/exceptions.py +71 -0
  390. ccxt/static_dependencies/marshmallow/fields.py +2114 -0
  391. ccxt/static_dependencies/marshmallow/orderedset.py +89 -0
  392. ccxt/static_dependencies/marshmallow/py.typed +0 -0
  393. ccxt/static_dependencies/marshmallow/schema.py +1228 -0
  394. ccxt/static_dependencies/marshmallow/types.py +12 -0
  395. ccxt/static_dependencies/marshmallow/utils.py +378 -0
  396. ccxt/static_dependencies/marshmallow/validate.py +678 -0
  397. ccxt/static_dependencies/marshmallow/warnings.py +2 -0
  398. ccxt/static_dependencies/marshmallow_dataclass/__init__.py +1047 -0
  399. ccxt/static_dependencies/marshmallow_dataclass/collection_field.py +51 -0
  400. ccxt/static_dependencies/marshmallow_dataclass/lazy_class_attribute.py +45 -0
  401. ccxt/static_dependencies/marshmallow_dataclass/mypy.py +71 -0
  402. ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
  403. ccxt/static_dependencies/marshmallow_dataclass/typing.py +14 -0
  404. ccxt/static_dependencies/marshmallow_dataclass/union_field.py +82 -0
  405. ccxt/static_dependencies/marshmallow_oneofschema/__init__.py +1 -0
  406. ccxt/static_dependencies/marshmallow_oneofschema/one_of_schema.py +193 -0
  407. ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
  408. ccxt/static_dependencies/starknet/__init__.py +0 -0
  409. ccxt/static_dependencies/starknet/cairo/__init__.py +0 -0
  410. ccxt/static_dependencies/starknet/cairo/data_types.py +123 -0
  411. ccxt/static_dependencies/starknet/cairo/deprecated_parse/__init__.py +0 -0
  412. ccxt/static_dependencies/starknet/cairo/deprecated_parse/cairo_types.py +77 -0
  413. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser.py +46 -0
  414. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser_transformer.py +138 -0
  415. ccxt/static_dependencies/starknet/cairo/felt.py +64 -0
  416. ccxt/static_dependencies/starknet/cairo/type_parser.py +121 -0
  417. ccxt/static_dependencies/starknet/cairo/v1/__init__.py +0 -0
  418. ccxt/static_dependencies/starknet/cairo/v1/type_parser.py +59 -0
  419. ccxt/static_dependencies/starknet/cairo/v2/__init__.py +0 -0
  420. ccxt/static_dependencies/starknet/cairo/v2/type_parser.py +77 -0
  421. ccxt/static_dependencies/starknet/ccxt_utils.py +7 -0
  422. ccxt/static_dependencies/starknet/common.py +15 -0
  423. ccxt/static_dependencies/starknet/constants.py +39 -0
  424. ccxt/static_dependencies/starknet/hash/__init__.py +0 -0
  425. ccxt/static_dependencies/starknet/hash/address.py +79 -0
  426. ccxt/static_dependencies/starknet/hash/compiled_class_hash_objects.py +111 -0
  427. ccxt/static_dependencies/starknet/hash/selector.py +16 -0
  428. ccxt/static_dependencies/starknet/hash/storage.py +12 -0
  429. ccxt/static_dependencies/starknet/hash/utils.py +78 -0
  430. ccxt/static_dependencies/starknet/models/__init__.py +0 -0
  431. ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
  432. ccxt/static_dependencies/starknet/serialization/__init__.py +24 -0
  433. ccxt/static_dependencies/starknet/serialization/_calldata_reader.py +40 -0
  434. ccxt/static_dependencies/starknet/serialization/_context.py +142 -0
  435. ccxt/static_dependencies/starknet/serialization/data_serializers/__init__.py +10 -0
  436. ccxt/static_dependencies/starknet/serialization/data_serializers/_common.py +82 -0
  437. ccxt/static_dependencies/starknet/serialization/data_serializers/array_serializer.py +43 -0
  438. ccxt/static_dependencies/starknet/serialization/data_serializers/bool_serializer.py +37 -0
  439. ccxt/static_dependencies/starknet/serialization/data_serializers/byte_array_serializer.py +66 -0
  440. ccxt/static_dependencies/starknet/serialization/data_serializers/cairo_data_serializer.py +71 -0
  441. ccxt/static_dependencies/starknet/serialization/data_serializers/enum_serializer.py +71 -0
  442. ccxt/static_dependencies/starknet/serialization/data_serializers/felt_serializer.py +50 -0
  443. ccxt/static_dependencies/starknet/serialization/data_serializers/named_tuple_serializer.py +58 -0
  444. ccxt/static_dependencies/starknet/serialization/data_serializers/option_serializer.py +43 -0
  445. ccxt/static_dependencies/starknet/serialization/data_serializers/output_serializer.py +40 -0
  446. ccxt/static_dependencies/starknet/serialization/data_serializers/payload_serializer.py +72 -0
  447. ccxt/static_dependencies/starknet/serialization/data_serializers/struct_serializer.py +36 -0
  448. ccxt/static_dependencies/starknet/serialization/data_serializers/tuple_serializer.py +36 -0
  449. ccxt/static_dependencies/starknet/serialization/data_serializers/uint256_serializer.py +76 -0
  450. ccxt/static_dependencies/starknet/serialization/data_serializers/uint_serializer.py +100 -0
  451. ccxt/static_dependencies/starknet/serialization/data_serializers/unit_serializer.py +32 -0
  452. ccxt/static_dependencies/starknet/serialization/errors.py +10 -0
  453. ccxt/static_dependencies/starknet/serialization/factory.py +229 -0
  454. ccxt/static_dependencies/starknet/serialization/function_serialization_adapter.py +110 -0
  455. ccxt/static_dependencies/starknet/serialization/tuple_dataclass.py +59 -0
  456. ccxt/static_dependencies/starknet/utils/__init__.py +0 -0
  457. ccxt/static_dependencies/starknet/utils/constructor_args_translator.py +86 -0
  458. ccxt/static_dependencies/starknet/utils/iterable.py +13 -0
  459. ccxt/static_dependencies/starknet/utils/schema.py +13 -0
  460. ccxt/static_dependencies/starknet/utils/typed_data.py +182 -0
  461. ccxt/static_dependencies/starkware/__init__.py +0 -0
  462. ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
  463. ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
  464. ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
  465. ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
  466. ccxt/static_dependencies/starkware/crypto/utils.py +63 -0
  467. ccxt/static_dependencies/sympy/__init__.py +0 -0
  468. ccxt/static_dependencies/sympy/core/__init__.py +0 -0
  469. ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
  470. ccxt/static_dependencies/sympy/external/__init__.py +0 -0
  471. ccxt/static_dependencies/sympy/external/gmpy.py +345 -0
  472. ccxt/static_dependencies/sympy/external/importtools.py +187 -0
  473. ccxt/static_dependencies/sympy/external/ntheory.py +637 -0
  474. ccxt/static_dependencies/sympy/external/pythonmpq.py +341 -0
  475. ccxt/static_dependencies/typing_inspect/__init__.py +0 -0
  476. ccxt/static_dependencies/typing_inspect/typing_inspect.py +851 -0
  477. ccxt/test/{test_async.py → tests_async.py} +456 -391
  478. ccxt/test/tests_helpers.py +285 -0
  479. ccxt/test/tests_init.py +39 -0
  480. ccxt/test/{test_sync.py → tests_sync.py} +456 -393
  481. ccxt/timex.py +123 -70
  482. ccxt/tokocrypto.py +129 -93
  483. ccxt/tradeogre.py +39 -25
  484. ccxt/upbit.py +322 -113
  485. ccxt/vertex.py +2983 -0
  486. ccxt/wavesexchange.py +227 -173
  487. ccxt/wazirx.py +145 -65
  488. ccxt/whitebit.py +533 -138
  489. ccxt/woo.py +1137 -296
  490. ccxt/woofipro.py +2716 -0
  491. ccxt/xt.py +4627 -0
  492. ccxt/yobit.py +159 -92
  493. ccxt/zaif.py +80 -33
  494. ccxt/zonda.py +140 -69
  495. ccxt-4.4.49.dist-info/LICENSE.txt +21 -0
  496. ccxt-4.4.49.dist-info/METADATA +646 -0
  497. ccxt-4.4.49.dist-info/RECORD +669 -0
  498. {ccxt-4.2.77.dist-info → ccxt-4.4.49.dist-info}/WHEEL +1 -1
  499. ccxt/abstract/bitbay.py +0 -47
  500. ccxt/abstract/bitfinex2.py +0 -139
  501. ccxt/abstract/hitbtc3.py +0 -115
  502. ccxt/async_support/bitbay.py +0 -17
  503. ccxt/async_support/bitfinex2.py +0 -3496
  504. ccxt/async_support/flowbtc.py +0 -34
  505. ccxt/bitbay.py +0 -17
  506. ccxt/bitfinex2.py +0 -3496
  507. ccxt/flowbtc.py +0 -34
  508. ccxt/hitbtc3.py +0 -16
  509. ccxt/pro/bitfinex2.py +0 -1081
  510. ccxt/test/base/__init__.py +0 -28
  511. ccxt/test/base/test_account.py +0 -26
  512. ccxt/test/base/test_balance.py +0 -56
  513. ccxt/test/base/test_borrow_interest.py +0 -35
  514. ccxt/test/base/test_borrow_rate.py +0 -32
  515. ccxt/test/base/test_calculate_fee.py +0 -51
  516. ccxt/test/base/test_crypto.py +0 -127
  517. ccxt/test/base/test_currency.py +0 -76
  518. ccxt/test/base/test_datetime.py +0 -103
  519. ccxt/test/base/test_decimal_to_precision.py +0 -392
  520. ccxt/test/base/test_deep_extend.py +0 -68
  521. ccxt/test/base/test_deposit_withdrawal.py +0 -50
  522. ccxt/test/base/test_exchange_datetime_functions.py +0 -76
  523. ccxt/test/base/test_funding_rate_history.py +0 -29
  524. ccxt/test/base/test_last_price.py +0 -32
  525. ccxt/test/base/test_ledger_entry.py +0 -45
  526. ccxt/test/base/test_ledger_item.py +0 -48
  527. ccxt/test/base/test_leverage_tier.py +0 -33
  528. ccxt/test/base/test_margin_mode.py +0 -24
  529. ccxt/test/base/test_margin_modification.py +0 -35
  530. ccxt/test/base/test_market.py +0 -190
  531. ccxt/test/base/test_number.py +0 -411
  532. ccxt/test/base/test_ohlcv.py +0 -32
  533. ccxt/test/base/test_open_interest.py +0 -32
  534. ccxt/test/base/test_order.py +0 -64
  535. ccxt/test/base/test_order_book.py +0 -63
  536. ccxt/test/base/test_position.py +0 -60
  537. ccxt/test/base/test_shared_methods.py +0 -345
  538. ccxt/test/base/test_status.py +0 -24
  539. ccxt/test/base/test_throttle.py +0 -126
  540. ccxt/test/base/test_ticker.py +0 -86
  541. ccxt/test/base/test_trade.py +0 -47
  542. ccxt/test/base/test_trading_fee.py +0 -26
  543. ccxt/test/base/test_transaction.py +0 -39
  544. ccxt-4.2.77.dist-info/METADATA +0 -626
  545. ccxt-4.2.77.dist-info/RECORD +0 -534
  546. {ccxt-4.2.77.dist-info → ccxt-4.4.49.dist-info}/top_level.txt +0 -0
@@ -5,12 +5,15 @@
5
5
 
6
6
  from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.bitfinex import ImplicitAPI
8
+ import asyncio
8
9
  import hashlib
9
- from ccxt.base.types import Balances, Currency, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, Transaction, TransferEntry
10
+ from ccxt.base.types import Balances, Currencies, Currency, DepositAddress, Int, LedgerEntry, MarginModification, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFees, Transaction, TransferEntry
10
11
  from typing import List
11
12
  from ccxt.base.errors import ExchangeError
13
+ from ccxt.base.errors import AuthenticationError
12
14
  from ccxt.base.errors import PermissionDenied
13
15
  from ccxt.base.errors import ArgumentsRequired
16
+ from ccxt.base.errors import BadRequest
14
17
  from ccxt.base.errors import BadSymbol
15
18
  from ccxt.base.errors import InsufficientFunds
16
19
  from ccxt.base.errors import InvalidOrder
@@ -18,8 +21,8 @@ from ccxt.base.errors import OrderNotFound
18
21
  from ccxt.base.errors import NotSupported
19
22
  from ccxt.base.errors import RateLimitExceeded
20
23
  from ccxt.base.errors import ExchangeNotAvailable
24
+ from ccxt.base.errors import OnMaintenance
21
25
  from ccxt.base.errors import InvalidNonce
22
- from ccxt.base.errors import AuthenticationError
23
26
  from ccxt.base.decimal_to_precision import ROUND
24
27
  from ccxt.base.decimal_to_precision import TRUNCATE
25
28
  from ccxt.base.decimal_to_precision import DECIMAL_PLACES
@@ -34,51 +37,96 @@ class bitfinex(Exchange, ImplicitAPI):
34
37
  'id': 'bitfinex',
35
38
  'name': 'Bitfinex',
36
39
  'countries': ['VG'],
37
- 'version': 'v1',
38
- # cheapest is 90 requests a minute = 1.5 requests per second on average =>( 1000ms / 1.5) = 666.666 ms between requests on average
39
- 'rateLimit': 666.666,
40
+ 'version': 'v2',
41
+ 'certified': False,
40
42
  'pro': True,
41
43
  # new metainfo interface
42
44
  'has': {
43
45
  'CORS': None,
44
46
  'spot': True,
45
- 'margin': None, # has but unimplemented
46
- 'swap': None, # has but unimplemented
47
- 'future': None,
48
- 'option': None,
47
+ 'margin': True,
48
+ 'swap': True,
49
+ 'future': False,
50
+ 'option': False,
51
+ 'addMargin': False,
52
+ 'borrowCrossMargin': False,
53
+ 'borrowIsolatedMargin': False,
49
54
  'cancelAllOrders': True,
50
55
  'cancelOrder': True,
56
+ 'cancelOrders': True,
51
57
  'createDepositAddress': True,
58
+ 'createLimitOrder': True,
59
+ 'createMarketOrder': True,
52
60
  'createOrder': True,
61
+ 'createPostOnlyOrder': True,
62
+ 'createReduceOnlyOrder': True,
63
+ 'createStopLimitOrder': True,
64
+ 'createStopMarketOrder': True,
65
+ 'createStopOrder': True,
66
+ 'createTrailingAmountOrder': True,
67
+ 'createTrailingPercentOrder': False,
68
+ 'createTriggerOrder': True,
53
69
  'editOrder': True,
54
70
  'fetchBalance': True,
71
+ 'fetchBorrowInterest': False,
72
+ 'fetchBorrowRate': False,
73
+ 'fetchBorrowRateHistories': False,
74
+ 'fetchBorrowRateHistory': False,
75
+ 'fetchBorrowRates': False,
76
+ 'fetchBorrowRatesPerSymbol': False,
77
+ 'fetchClosedOrder': True,
55
78
  'fetchClosedOrders': True,
79
+ 'fetchCrossBorrowRate': False,
80
+ 'fetchCrossBorrowRates': False,
81
+ 'fetchCurrencies': True,
56
82
  'fetchDepositAddress': True,
57
- 'fetchDeposits': False,
83
+ 'fetchDepositAddresses': False,
84
+ 'fetchDepositAddressesByNetwork': False,
58
85
  'fetchDepositsWithdrawals': True,
59
- 'fetchDepositWithdrawFee': 'emulated',
60
- 'fetchDepositWithdrawFees': True,
86
+ 'fetchFundingHistory': False,
87
+ 'fetchFundingRate': 'emulated', # emulated in exchange
88
+ 'fetchFundingRateHistory': True,
89
+ 'fetchFundingRates': True,
61
90
  'fetchIndexOHLCV': False,
91
+ 'fetchIsolatedBorrowRate': False,
92
+ 'fetchIsolatedBorrowRates': False,
93
+ 'fetchLedger': True,
94
+ 'fetchLeverage': False,
62
95
  'fetchLeverageTiers': False,
96
+ 'fetchLiquidations': True,
63
97
  'fetchMarginMode': False,
64
- 'fetchMarkets': True,
98
+ 'fetchMarketLeverageTiers': False,
65
99
  'fetchMarkOHLCV': False,
66
100
  'fetchMyTrades': True,
67
101
  'fetchOHLCV': True,
102
+ 'fetchOpenInterest': True,
103
+ 'fetchOpenInterestHistory': True,
104
+ 'fetchOpenInterests': True,
105
+ 'fetchOpenOrder': True,
68
106
  'fetchOpenOrders': True,
69
107
  'fetchOrder': True,
70
108
  'fetchOrderBook': True,
109
+ 'fetchOrderBooks': False,
110
+ 'fetchOrderTrades': True,
111
+ 'fetchPosition': False,
71
112
  'fetchPositionMode': False,
72
113
  'fetchPositions': True,
73
114
  'fetchPremiumIndexOHLCV': False,
74
- 'fetchTicker': True,
115
+ 'fetchStatus': True,
75
116
  'fetchTickers': True,
76
117
  'fetchTime': False,
77
- 'fetchTrades': True,
78
118
  'fetchTradingFee': False,
79
119
  'fetchTradingFees': True,
80
- 'fetchTransactionFees': True,
120
+ 'fetchTransactionFees': None,
81
121
  'fetchTransactions': 'emulated',
122
+ 'reduceMargin': False,
123
+ 'repayCrossMargin': False,
124
+ 'repayIsolatedMargin': False,
125
+ 'setLeverage': False,
126
+ 'setMargin': True,
127
+ 'setMarginMode': False,
128
+ 'setPositionMode': False,
129
+ 'signIn': False,
82
130
  'transfer': True,
83
131
  'withdraw': True,
84
132
  },
@@ -97,107 +145,177 @@ class bitfinex(Exchange, ImplicitAPI):
97
145
  '2w': '14D',
98
146
  '1M': '1M',
99
147
  },
148
+ # cheapest endpoint is 240 requests per minute => ~ 4 requests per second =>( 1000ms / 4 ) = 250ms between requests on average
149
+ 'rateLimit': 250,
100
150
  'urls': {
101
- 'logo': 'https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg',
151
+ 'logo': 'https://github.com/user-attachments/assets/4a8e947f-ab46-481a-a8ae-8b20e9b03178',
102
152
  'api': {
103
- 'v2': 'https://api-pub.bitfinex.com', # https://github.com/ccxt/ccxt/issues/5109
104
- 'public': 'https://api.bitfinex.com',
153
+ 'v1': 'https://api.bitfinex.com',
154
+ 'public': 'https://api-pub.bitfinex.com',
105
155
  'private': 'https://api.bitfinex.com',
106
156
  },
107
157
  'www': 'https://www.bitfinex.com',
108
- 'referral': 'https://www.bitfinex.com/?refcode=P61eYxFL',
109
158
  'doc': [
110
- 'https://docs.bitfinex.com/v1/docs',
159
+ 'https://docs.bitfinex.com/v2/docs/',
111
160
  'https://github.com/bitfinexcom/bitfinex-api-node',
112
161
  ],
162
+ 'fees': 'https://www.bitfinex.com/fees',
113
163
  },
114
164
  'api': {
115
- # v2 symbol ids require a 't' prefix
116
- # just the public part of it(use bitfinex2 for everything else)
117
- 'v2': {
118
- 'get': {
119
- 'platform/status': 3, # 30 requests per minute
120
- 'tickers': 1, # 90 requests a minute
121
- 'ticker/{symbol}': 1,
122
- 'tickers/hist': 1,
123
- 'trades/{symbol}/hist': 1,
124
- 'book/{symbol}/{precision}': 0.375, # 240 requests per minute = 4 requests per second(1000ms / rateLimit) / 4 = 0.37500375
125
- 'book/{symbol}/P0': 0.375,
126
- 'book/{symbol}/P1': 0.375,
127
- 'book/{symbol}/P2': 0.375,
128
- 'book/{symbol}/P3': 0.375,
129
- 'book/{symbol}/R0': 0.375,
130
- 'stats1/{key}:{size}:{symbol}:{side}/{section}': 1, # 90 requests a minute
131
- 'stats1/{key}:{size}:{symbol}/{section}': 1,
132
- 'stats1/{key}:{size}:{symbol}:long/last': 1,
133
- 'stats1/{key}:{size}:{symbol}:long/hist': 1,
134
- 'stats1/{key}:{size}:{symbol}:short/last': 1,
135
- 'stats1/{key}:{size}:{symbol}:short/hist': 1,
136
- 'candles/trade:{timeframe}:{symbol}/{section}': 1, # 90 requests a minute
137
- 'candles/trade:{timeframe}:{symbol}/last': 1,
138
- 'candles/trade:{timeframe}:{symbol}/hist': 1,
139
- },
140
- },
141
165
  'public': {
142
166
  'get': {
143
- 'book/{symbol}': 1, # 90 requests a minute
144
- # 'candles/{symbol}':0,
145
- 'lendbook/{currency}': 6, # 15 requests a minute
146
- 'lends/{currency}': 3, # 30 requests a minute
147
- 'pubticker/{symbol}': 3, # 30 requests a minute = 0.5 requests per second =>(1000ms / rateLimit) / 0.5 = 3.00003
148
- 'stats/{symbol}': 6, # 15 requests a minute = 0.25 requests per second =>(1000ms / rateLimit ) /0.25 = 6.00006(endpoint returns red html... or 'unknown symbol')
149
- 'symbols': 18, # 5 requests a minute = 0.08333 requests per second =>(1000ms / rateLimit) / 0.08333 = 18.0009
150
- 'symbols_details': 18, # 5 requests a minute
151
- 'tickers': 1, # endpoint not mentioned in v1 docs... but still responds
152
- 'trades/{symbol}': 3, # 60 requests a minute = 1 request per second =>(1000ms / rateLimit) / 1 = 1.5 ... but only works if set to 3
167
+ 'conf/{config}': 2.7, # 90 requests a minute, 90/60 = 1.5, 1000 / (250 * 2.66) = 1.503, use 2.7 instead of 2.66 to ensure rateLimitExceeded is not triggered
168
+ 'conf/pub:{action}:{object}': 2.7,
169
+ 'conf/pub:{action}:{object}:{detail}': 2.7,
170
+ 'conf/pub:map:{object}': 2.7,
171
+ 'conf/pub:map:{object}:{detail}': 2.7,
172
+ 'conf/pub:map:currency:{detail}': 2.7,
173
+ 'conf/pub:map:currency:sym': 2.7, # maps symbols to their API symbols, BAB > BCH
174
+ 'conf/pub:map:currency:label': 2.7, # verbose friendly names, BNT > Bancor
175
+ 'conf/pub:map:currency:unit': 2.7, # maps symbols to unit of measure where applicable
176
+ 'conf/pub:map:currency:undl': 2.7, # maps derivatives symbols to their underlying currency
177
+ 'conf/pub:map:currency:pool': 2.7, # maps symbols to underlying network/protocol they operate on
178
+ 'conf/pub:map:currency:explorer': 2.7, # maps symbols to their recognised block explorer URLs
179
+ 'conf/pub:map:currency:tx:fee': 2.7, # maps currencies to their withdrawal fees https://github.com/ccxt/ccxt/issues/7745
180
+ 'conf/pub:map:tx:method': 2.7,
181
+ 'conf/pub:list:{object}': 2.7,
182
+ 'conf/pub:list:{object}:{detail}': 2.7,
183
+ 'conf/pub:list:currency': 2.7,
184
+ 'conf/pub:list:pair:exchange': 2.7,
185
+ 'conf/pub:list:pair:margin': 2.7,
186
+ 'conf/pub:list:pair:futures': 2.7,
187
+ 'conf/pub:list:competitions': 2.7,
188
+ 'conf/pub:info:{object}': 2.7,
189
+ 'conf/pub:info:{object}:{detail}': 2.7,
190
+ 'conf/pub:info:pair': 2.7,
191
+ 'conf/pub:info:pair:futures': 2.7,
192
+ 'conf/pub:info:tx:status': 2.7, # [deposit, withdrawal] statuses 1 = active, 0 = maintenance
193
+ 'conf/pub:fees': 2.7,
194
+ 'platform/status': 8, # 30 requests per minute = 0.5 requests per second =>( 1000ms / rateLimit ) / 0.5 = 8
195
+ 'tickers': 2.7, # 90 requests a minute = 1.5 requests per second =>( 1000 / rateLimit ) / 1.5 = 2.666666666
196
+ 'ticker/{symbol}': 2.7,
197
+ 'tickers/hist': 2.7,
198
+ 'trades/{symbol}/hist': 2.7,
199
+ 'book/{symbol}/{precision}': 1, # 240 requests a minute
200
+ 'book/{symbol}/P0': 1,
201
+ 'book/{symbol}/P1': 1,
202
+ 'book/{symbol}/P2': 1,
203
+ 'book/{symbol}/P3': 1,
204
+ 'book/{symbol}/R0': 1,
205
+ 'stats1/{key}:{size}:{symbol}:{side}/{section}': 2.7,
206
+ 'stats1/{key}:{size}:{symbol}:{side}/last': 2.7,
207
+ 'stats1/{key}:{size}:{symbol}:{side}/hist': 2.7,
208
+ 'stats1/{key}:{size}:{symbol}/{section}': 2.7,
209
+ 'stats1/{key}:{size}:{symbol}/last': 2.7,
210
+ 'stats1/{key}:{size}:{symbol}/hist': 2.7,
211
+ 'stats1/{key}:{size}:{symbol}:long/last': 2.7,
212
+ 'stats1/{key}:{size}:{symbol}:long/hist': 2.7,
213
+ 'stats1/{key}:{size}:{symbol}:short/last': 2.7,
214
+ 'stats1/{key}:{size}:{symbol}:short/hist': 2.7,
215
+ 'candles/trade:{timeframe}:{symbol}:{period}/{section}': 2.7,
216
+ 'candles/trade:{timeframe}:{symbol}/{section}': 2.7,
217
+ 'candles/trade:{timeframe}:{symbol}/last': 2.7,
218
+ 'candles/trade:{timeframe}:{symbol}/hist': 2.7,
219
+ 'status/{type}': 2.7,
220
+ 'status/deriv': 2.7,
221
+ 'status/deriv/{symbol}/hist': 2.7,
222
+ 'liquidations/hist': 80, # 3 requests a minute = 0.05 requests a second =>( 1000ms / rateLimit ) / 0.05 = 80
223
+ 'rankings/{key}:{timeframe}:{symbol}/{section}': 2.7,
224
+ 'rankings/{key}:{timeframe}:{symbol}/hist': 2.7,
225
+ 'pulse/hist': 2.7,
226
+ 'pulse/profile/{nickname}': 2.7,
227
+ 'funding/stats/{symbol}/hist': 10, # ratelimit not in docs
228
+ 'ext/vasps': 1,
229
+ },
230
+ 'post': {
231
+ 'calc/trade/avg': 2.7,
232
+ 'calc/fx': 2.7,
153
233
  },
154
234
  },
155
235
  'private': {
156
236
  'post': {
157
- 'account_fees': 18,
158
- 'account_infos': 6,
159
- 'balances': 9.036, # 10 requests a minute = 0.166 requests per second =>(1000ms / rateLimit) / 0.166 = 9.036
160
- 'basket_manage': 6,
161
- 'credits': 6,
162
- 'deposit/new': 18,
163
- 'funding/close': 6,
164
- 'history': 6, # 15 requests a minute
165
- 'history/movements': 6,
166
- 'key_info': 6,
167
- 'margin_infos': 3, # 30 requests a minute
168
- 'mytrades': 3,
169
- 'mytrades_funding': 6,
170
- 'offer/cancel': 6,
171
- 'offer/new': 6,
172
- 'offer/status': 6,
173
- 'offers': 6,
174
- 'offers/hist': 90.03, # one request per minute
175
- 'order/cancel': 0.2,
176
- 'order/cancel/all': 0.2,
177
- 'order/cancel/multi': 0.2,
178
- 'order/cancel/replace': 0.2,
179
- 'order/new': 0.2, # 450 requests a minute = 7.5 request a second =>(1000ms / rateLimit) / 7.5 = 0.2000002
180
- 'order/new/multi': 0.2,
181
- 'order/status': 0.2,
182
- 'orders': 0.2,
183
- 'orders/hist': 90.03, # one request per minute = 0.1666 =>(1000ms / rateLimit) / 0.01666 = 90.03
184
- 'position/claim': 18,
185
- 'position/close': 18,
186
- 'positions': 18,
187
- 'summary': 18,
188
- 'taken_funds': 6,
189
- 'total_taken_funds': 6,
190
- 'transfer': 18,
191
- 'unused_taken_funds': 6,
192
- 'withdraw': 18,
237
+ # 'auth/r/orders/{symbol}/new', # outdated
238
+ # 'auth/r/stats/perf:{timeframe}/hist', # outdated
239
+ 'auth/r/wallets': 2.7,
240
+ 'auth/r/wallets/hist': 2.7,
241
+ 'auth/r/orders': 2.7,
242
+ 'auth/r/orders/{symbol}': 2.7,
243
+ 'auth/w/order/submit': 2.7,
244
+ 'auth/w/order/update': 2.7,
245
+ 'auth/w/order/cancel': 2.7,
246
+ 'auth/w/order/multi': 2.7,
247
+ 'auth/w/order/cancel/multi': 2.7,
248
+ 'auth/r/orders/{symbol}/hist': 2.7,
249
+ 'auth/r/orders/hist': 2.7,
250
+ 'auth/r/order/{symbol}:{id}/trades': 2.7,
251
+ 'auth/r/trades/{symbol}/hist': 2.7,
252
+ 'auth/r/trades/hist': 2.7,
253
+ 'auth/r/ledgers/{currency}/hist': 2.7,
254
+ 'auth/r/ledgers/hist': 2.7,
255
+ 'auth/r/info/margin/{key}': 2.7,
256
+ 'auth/r/info/margin/base': 2.7,
257
+ 'auth/r/info/margin/sym_all': 2.7,
258
+ 'auth/r/positions': 2.7,
259
+ 'auth/w/position/claim': 2.7,
260
+ 'auth/w/position/increase:': 2.7,
261
+ 'auth/r/position/increase/info': 2.7,
262
+ 'auth/r/positions/hist': 2.7,
263
+ 'auth/r/positions/audit': 2.7,
264
+ 'auth/r/positions/snap': 2.7,
265
+ 'auth/w/deriv/collateral/set': 2.7,
266
+ 'auth/w/deriv/collateral/limits': 2.7,
267
+ 'auth/r/funding/offers': 2.7,
268
+ 'auth/r/funding/offers/{symbol}': 2.7,
269
+ 'auth/w/funding/offer/submit': 2.7,
270
+ 'auth/w/funding/offer/cancel': 2.7,
271
+ 'auth/w/funding/offer/cancel/all': 2.7,
272
+ 'auth/w/funding/close': 2.7,
273
+ 'auth/w/funding/auto': 2.7,
274
+ 'auth/w/funding/keep': 2.7,
275
+ 'auth/r/funding/offers/{symbol}/hist': 2.7,
276
+ 'auth/r/funding/offers/hist': 2.7,
277
+ 'auth/r/funding/loans': 2.7,
278
+ 'auth/r/funding/loans/hist': 2.7,
279
+ 'auth/r/funding/loans/{symbol}': 2.7,
280
+ 'auth/r/funding/loans/{symbol}/hist': 2.7,
281
+ 'auth/r/funding/credits': 2.7,
282
+ 'auth/r/funding/credits/hist': 2.7,
283
+ 'auth/r/funding/credits/{symbol}': 2.7,
284
+ 'auth/r/funding/credits/{symbol}/hist': 2.7,
285
+ 'auth/r/funding/trades/{symbol}/hist': 2.7,
286
+ 'auth/r/funding/trades/hist': 2.7,
287
+ 'auth/r/info/funding/{key}': 2.7,
288
+ 'auth/r/info/user': 2.7,
289
+ 'auth/r/summary': 2.7,
290
+ 'auth/r/logins/hist': 2.7,
291
+ 'auth/r/permissions': 2.7,
292
+ 'auth/w/token': 2.7,
293
+ 'auth/r/audit/hist': 2.7,
294
+ 'auth/w/transfer': 2.7, # ratelimit not in docs...
295
+ 'auth/w/deposit/address': 24, # 10 requests a minute = 0.166 requests per second =>( 1000ms / rateLimit ) / 0.166 = 24
296
+ 'auth/w/deposit/invoice': 24, # ratelimit not in docs
297
+ 'auth/w/withdraw': 24, # ratelimit not in docs
298
+ 'auth/r/movements/{currency}/hist': 2.7,
299
+ 'auth/r/movements/hist': 2.7,
300
+ 'auth/r/alerts': 5.34, # 45 requests a minute = 0.75 requests per second =>( 1000ms / rateLimit ) / 0.749 => 5.34
301
+ 'auth/w/alert/set': 2.7,
302
+ 'auth/w/alert/price:{symbol}:{price}/del': 2.7,
303
+ 'auth/w/alert/{type}:{symbol}:{price}/del': 2.7,
304
+ 'auth/calc/order/avail': 2.7,
305
+ 'auth/w/settings/set': 2.7,
306
+ 'auth/r/settings': 2.7,
307
+ 'auth/w/settings/del': 2.7,
308
+ 'auth/r/pulse/hist': 2.7,
309
+ 'auth/w/pulse/add': 16, # 15 requests a minute = 0.25 requests per second =>( 1000ms / rateLimit ) / 0.25 => 16
310
+ 'auth/w/pulse/del': 2.7,
193
311
  },
194
312
  },
195
313
  },
196
314
  'fees': {
197
315
  'trading': {
198
316
  'feeSide': 'get',
199
- 'tierBased': True,
200
317
  'percentage': True,
318
+ 'tierBased': True,
201
319
  'maker': self.parse_number('0.001'),
202
320
  'taker': self.parse_number('0.002'),
203
321
  'tiers': {
@@ -230,17 +348,174 @@ class bitfinex(Exchange, ImplicitAPI):
230
348
  },
231
349
  },
232
350
  'funding': {
233
- 'tierBased': False, # True for tier-based/progressive
234
- 'percentage': False, # fixed commission
235
- # Actually deposit fees are free for larger deposits(> $1000 USD equivalent)
236
- # these values below are deprecated, we should not hardcode fees and limits anymore
237
- # to be reimplemented with bitfinex funding fees from their API or web endpoints
238
- 'deposit': {},
239
351
  'withdraw': {},
240
352
  },
241
353
  },
242
- # todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
354
+ 'precisionMode': SIGNIFICANT_DIGITS,
355
+ 'options': {
356
+ 'precision': 'R0', # P0, P1, P2, P3, P4, R0
357
+ # convert 'EXCHANGE MARKET' to lowercase 'market'
358
+ # convert 'EXCHANGE LIMIT' to lowercase 'limit'
359
+ # everything else remains uppercase
360
+ 'exchangeTypes': {
361
+ 'MARKET': 'market',
362
+ 'EXCHANGE MARKET': 'market',
363
+ 'LIMIT': 'limit',
364
+ 'EXCHANGE LIMIT': 'limit',
365
+ # 'STOP': None,
366
+ 'EXCHANGE STOP': 'market',
367
+ # 'TRAILING STOP': None,
368
+ # 'EXCHANGE TRAILING STOP': None,
369
+ # 'FOK': None,
370
+ 'EXCHANGE FOK': 'limit',
371
+ # 'STOP LIMIT': None,
372
+ 'EXCHANGE STOP LIMIT': 'limit',
373
+ # 'IOC': None,
374
+ 'EXCHANGE IOC': 'limit',
375
+ },
376
+ # convert 'market' to 'EXCHANGE MARKET'
377
+ # convert 'limit' 'EXCHANGE LIMIT'
378
+ # everything else remains
379
+ 'orderTypes': {
380
+ 'market': 'EXCHANGE MARKET',
381
+ 'limit': 'EXCHANGE LIMIT',
382
+ },
383
+ 'fiat': {
384
+ 'USD': 'USD',
385
+ 'EUR': 'EUR',
386
+ 'JPY': 'JPY',
387
+ 'GBP': 'GBP',
388
+ 'CHN': 'CHN',
389
+ },
390
+ # actually the correct names unlike the v1
391
+ # we don't want to self.extend self with accountsByType in v1
392
+ 'v2AccountsByType': {
393
+ 'spot': 'exchange',
394
+ 'exchange': 'exchange',
395
+ 'funding': 'funding',
396
+ 'margin': 'margin',
397
+ 'derivatives': 'margin',
398
+ 'future': 'margin',
399
+ 'swap': 'margin',
400
+ },
401
+ 'withdraw': {
402
+ 'includeFee': False,
403
+ },
404
+ 'networks': {
405
+ 'BTC': 'BITCOIN',
406
+ 'LTC': 'LITECOIN',
407
+ 'ERC20': 'ETHEREUM',
408
+ 'OMNI': 'TETHERUSO',
409
+ 'LIQUID': 'TETHERUSL',
410
+ 'TRC20': 'TETHERUSX',
411
+ 'EOS': 'TETHERUSS',
412
+ 'AVAX': 'TETHERUSDTAVAX',
413
+ 'SOL': 'TETHERUSDTSOL',
414
+ 'ALGO': 'TETHERUSDTALG',
415
+ 'BCH': 'TETHERUSDTBCH',
416
+ 'KSM': 'TETHERUSDTKSM',
417
+ 'DVF': 'TETHERUSDTDVF',
418
+ 'OMG': 'TETHERUSDTOMG',
419
+ },
420
+ 'networksById': {
421
+ 'TETHERUSE': 'ERC20',
422
+ },
423
+ },
424
+ 'features': {
425
+ 'default': {
426
+ 'sandbox': False,
427
+ 'createOrder': {
428
+ 'marginMode': True,
429
+ 'triggerPrice': True,
430
+ 'triggerPriceType': None,
431
+ 'triggerDirection': False,
432
+ 'stopLossPrice': True,
433
+ 'takeProfitPrice': True,
434
+ 'attachedStopLossTakeProfit': None,
435
+ 'timeInForce': {
436
+ 'IOC': True,
437
+ 'FOK': True,
438
+ 'PO': True,
439
+ 'GTD': False,
440
+ },
441
+ 'hedged': False,
442
+ 'trailing': True, # todo: implement
443
+ 'leverage': True, # todo: implement
444
+ 'marketBuyRequiresPrice': False,
445
+ 'marketBuyByCost': True,
446
+ 'selfTradePrevention': False,
447
+ 'iceberg': False,
448
+ },
449
+ 'createOrders': {
450
+ 'max': 75,
451
+ },
452
+ 'fetchMyTrades': {
453
+ 'marginMode': False,
454
+ 'limit': 2500,
455
+ 'daysBack': None,
456
+ 'untilDays': 100000, # todo: implement
457
+ },
458
+ 'fetchOrder': {
459
+ 'marginMode': False,
460
+ 'trigger': False,
461
+ 'trailing': False,
462
+ },
463
+ 'fetchOpenOrders': {
464
+ 'marginMode': False,
465
+ 'limit': None,
466
+ 'trigger': False,
467
+ 'trailing': False,
468
+ },
469
+ 'fetchOrders': None,
470
+ 'fetchClosedOrders': {
471
+ 'marginMode': False,
472
+ 'limit': None,
473
+ 'daysBack': None,
474
+ 'daysBackCanceled': None,
475
+ 'untilDays': 100000,
476
+ 'trigger': False,
477
+ 'trailing': False,
478
+ },
479
+ 'fetchOHLCV': {
480
+ 'limit': 10000,
481
+ },
482
+ },
483
+ 'spot': {
484
+ 'extends': 'default',
485
+ },
486
+ 'swap': {
487
+ 'linear': {
488
+ 'extends': 'default',
489
+ },
490
+ 'inverse': None,
491
+ },
492
+ 'future': {
493
+ 'linear': None,
494
+ 'inverse': None,
495
+ },
496
+ },
497
+ 'exceptions': {
498
+ 'exact': {
499
+ '11010': RateLimitExceeded,
500
+ '10001': PermissionDenied, # api_key: permission invalid(#10001)
501
+ '10020': BadRequest,
502
+ '10100': AuthenticationError,
503
+ '10114': InvalidNonce,
504
+ '20060': OnMaintenance,
505
+ # {"code":503,"error":"temporarily_unavailable","error_description":"Sorry, the service is temporarily unavailable. See https://www.bitfinex.com/ for more info."}
506
+ 'temporarily_unavailable': ExchangeNotAvailable,
507
+ },
508
+ 'broad': {
509
+ 'available balance is only': InsufficientFunds,
510
+ 'not enough exchange balance': InsufficientFunds,
511
+ 'Order not found': OrderNotFound,
512
+ 'symbol: invalid': BadSymbol,
513
+ },
514
+ },
243
515
  'commonCurrencies': {
516
+ 'UST': 'USDT',
517
+ 'EUTF0': 'EURT',
518
+ 'USTF0': 'USDT',
244
519
  'ALG': 'ALGO', # https://github.com/ccxt/ccxt/issues/6034
245
520
  'AMP': 'AMPL',
246
521
  'ATO': 'ATOM', # https://github.com/ccxt/ccxt/issues/5118
@@ -249,12 +524,10 @@ class bitfinex(Exchange, ImplicitAPI):
249
524
  'DAT': 'DATA',
250
525
  'DOG': 'MDOGE',
251
526
  'DSH': 'DASH',
252
- # https://github.com/ccxt/ccxt/issues/7399
253
- # https://coinmarketcap.com/currencies/pnetwork/
254
- # https://en.cryptonomist.ch/blog/eidoo/the-edo-to-pnt-upgrade-what-you-need-to-know-updated/
255
527
  'EDO': 'PNT',
256
528
  'EUS': 'EURS',
257
529
  'EUT': 'EURT',
530
+ 'HTX': 'HT',
258
531
  'IDX': 'ID',
259
532
  'IOT': 'IOTA',
260
533
  'IQX': 'IQ',
@@ -273,324 +546,109 @@ class bitfinex(Exchange, ImplicitAPI):
273
546
  'YGG': 'YEED', # conflict with Yield Guild Games
274
547
  'YYW': 'YOYOW',
275
548
  'UDC': 'USDC',
276
- 'UST': 'USDT',
277
549
  'VSY': 'VSYS',
278
550
  'WAX': 'WAXP',
279
551
  'XCH': 'XCHF',
280
552
  'ZBT': 'ZB',
281
553
  },
282
- 'exceptions': {
283
- 'exact': {
284
- 'temporarily_unavailable': ExchangeNotAvailable, # Sorry, the service is temporarily unavailable. See https://www.bitfinex.com/ for more info.
285
- 'Order could not be cancelled.': OrderNotFound, # non-existent order
286
- 'No such order found.': OrderNotFound, # ?
287
- 'Order price must be positive.': InvalidOrder, # on price <= 0
288
- 'Could not find a key matching the given X-BFX-APIKEY.': AuthenticationError,
289
- 'Key price should be a decimal number, e.g. "123.456"': InvalidOrder, # on isNaN(price)
290
- 'Key amount should be a decimal number, e.g. "123.456"': InvalidOrder, # on isNaN(amount)
291
- 'ERR_RATE_LIMIT': RateLimitExceeded,
292
- 'Ratelimit': RateLimitExceeded,
293
- 'Nonce is too small.': InvalidNonce,
294
- 'No summary found.': ExchangeError, # fetchTradingFees(summary) endpoint can give self vague error message
295
- 'Cannot evaluate your available balance, please try again': ExchangeNotAvailable,
296
- 'Unknown symbol': BadSymbol,
297
- 'Cannot complete transfer. Exchange balance insufficient.': InsufficientFunds,
298
- 'Momentary balance check. Please wait few seconds and try the transfer again.': ExchangeError,
299
- },
300
- 'broad': {
301
- 'Invalid X-BFX-SIGNATURE': AuthenticationError,
302
- 'This API key does not have permission': PermissionDenied, # authenticated but not authorized
303
- 'not enough exchange balance for ': InsufficientFunds, # when buying cost is greater than the available quote currency
304
- 'minimum size for ': InvalidOrder, # when amount below limits.amount.min
305
- 'Invalid order': InvalidOrder, # ?
306
- 'The available balance is only': InsufficientFunds, # {"status":"error","message":"Cannot withdraw 1.0027 ETH from your exchange wallet. The available balance is only 0.0 ETH. If you have limit orders, open positions, unused or active margin funding, self will decrease your available balance. To increase it, you can cancel limit orders or reduce/close your positions.","withdrawal_id":0,"fees":"0.0027"}
307
- },
308
- },
309
- 'precisionMode': SIGNIFICANT_DIGITS,
310
- 'options': {
311
- 'currencyNames': {
312
- 'AGI': 'agi',
313
- 'AID': 'aid',
314
- 'AIO': 'aio',
315
- 'ANT': 'ant',
316
- 'AVT': 'aventus', # #1811
317
- 'BAT': 'bat',
318
- # https://github.com/ccxt/ccxt/issues/5833
319
- 'BCH': 'bab', # undocumented
320
- # 'BCH': 'bcash', # undocumented
321
- 'BCI': 'bci',
322
- 'BFT': 'bft',
323
- 'BSV': 'bsv',
324
- 'BTC': 'bitcoin',
325
- 'BTG': 'bgold',
326
- 'CFI': 'cfi',
327
- 'COMP': 'comp',
328
- 'DAI': 'dai',
329
- 'DADI': 'dad',
330
- 'DASH': 'dash',
331
- 'DATA': 'datacoin',
332
- 'DTH': 'dth',
333
- 'EDO': 'eidoo', # #1811
334
- 'ELF': 'elf',
335
- 'EOS': 'eos',
336
- 'ETC': 'ethereumc',
337
- 'ETH': 'ethereum',
338
- 'ETP': 'metaverse',
339
- 'FUN': 'fun',
340
- 'GNT': 'golem',
341
- 'IOST': 'ios',
342
- 'IOTA': 'iota',
343
- # https://github.com/ccxt/ccxt/issues/5833
344
- 'LEO': 'let', # ETH chain
345
- # 'LEO': 'les', # EOS chain
346
- 'LINK': 'link',
347
- 'LRC': 'lrc',
348
- 'LTC': 'litecoin',
349
- 'LYM': 'lym',
350
- 'MANA': 'mna',
351
- 'MIT': 'mit',
352
- 'MKR': 'mkr',
353
- 'MTN': 'mtn',
354
- 'NEO': 'neo',
355
- 'ODE': 'ode',
356
- 'OMG': 'omisego',
357
- 'OMNI': 'mastercoin',
358
- 'QASH': 'qash',
359
- 'QTUM': 'qtum', # #1811
360
- 'RCN': 'rcn',
361
- 'RDN': 'rdn',
362
- 'REP': 'rep',
363
- 'REQ': 'req',
364
- 'RLC': 'rlc',
365
- 'SAN': 'santiment',
366
- 'SNGLS': 'sng',
367
- 'SNT': 'status',
368
- 'SPANK': 'spk',
369
- 'STORJ': 'stj',
370
- 'TNB': 'tnb',
371
- 'TRX': 'trx',
372
- 'TUSD': 'tsd',
373
- 'USD': 'wire',
374
- 'USDC': 'udc', # https://github.com/ccxt/ccxt/issues/5833
375
- 'UTK': 'utk',
376
- 'USDT': 'tetheruso', # Tether on Omni
377
- # 'USDT': 'tetheruse', # Tether on ERC20
378
- # 'USDT': 'tetherusl', # Tether on Liquid
379
- # 'USDT': 'tetherusx', # Tether on Tron
380
- # 'USDT': 'tetheruss', # Tether on EOS
381
- 'VEE': 'vee',
382
- 'WAX': 'wax',
383
- 'XLM': 'xlm',
384
- 'XMR': 'monero',
385
- 'XRP': 'ripple',
386
- 'XVG': 'xvg',
387
- 'YOYOW': 'yoyow',
388
- 'ZEC': 'zcash',
389
- 'ZRX': 'zrx',
390
- 'XTZ': 'xtz',
391
- },
392
- 'orderTypes': {
393
- 'limit': 'exchange limit',
394
- 'market': 'exchange market',
395
- },
396
- 'fiat': {
397
- 'USD': 'USD',
398
- 'EUR': 'EUR',
399
- 'JPY': 'JPY',
400
- 'GBP': 'GBP',
401
- 'CNH': 'CNH',
402
- },
403
- 'accountsByType': {
404
- 'spot': 'exchange',
405
- 'margin': 'trading',
406
- 'funding': 'deposit',
407
- 'swap': 'trading',
408
- },
409
- },
410
554
  })
411
555
 
412
- async def fetch_transaction_fees(self, codes: List[str] = None, params={}):
413
- """
414
- * @deprecated
415
- please use fetchDepositWithdrawFees instead
416
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-fees
417
- :param str[]|None codes: list of unified currency codes
418
- :param dict [params]: extra parameters specific to the exchange API endpoint
419
- :returns dict[]: a list of `fees structures <https://docs.ccxt.com/#/?id=fee-structure>`
420
- """
421
- await self.load_markets()
422
- result = {}
423
- response = await self.privatePostAccountFees(params)
424
- #
425
- # {
426
- # "withdraw": {
427
- # "BTC": "0.0004",
428
- # }
429
- # }
430
- #
431
- fees = self.safe_value(response, 'withdraw')
432
- ids = list(fees.keys())
433
- for i in range(0, len(ids)):
434
- id = ids[i]
435
- code = self.safe_currency_code(id)
436
- if (codes is not None) and not self.in_array(code, codes):
437
- continue
438
- result[code] = {
439
- 'withdraw': self.safe_number(fees, id),
440
- 'deposit': {},
441
- 'info': self.safe_number(fees, id),
442
- }
443
- return result
556
+ def is_fiat(self, code):
557
+ return(code in self.options['fiat'])
558
+
559
+ def get_currency_id(self, code):
560
+ return 'f' + code
561
+
562
+ def get_currency_name(self, code):
563
+ # temporary fix for transpiler recognition, even though self is in parent class
564
+ if code in self.options['currencyNames']:
565
+ return self.options['currencyNames'][code]
566
+ raise NotSupported(self.id + ' ' + code + ' not supported for withdrawal')
567
+
568
+ def amount_to_precision(self, symbol, amount):
569
+ # https://docs.bitfinex.com/docs/introduction#amount-precision
570
+ # The amount field allows up to 8 decimals.
571
+ # Anything exceeding self will be rounded to the 8th decimal.
572
+ symbol = self.safe_symbol(symbol)
573
+ return self.decimal_to_precision(amount, TRUNCATE, self.markets[symbol]['precision']['amount'], DECIMAL_PLACES)
444
574
 
445
- async def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
575
+ def price_to_precision(self, symbol, price):
576
+ symbol = self.safe_symbol(symbol)
577
+ price = self.decimal_to_precision(price, ROUND, self.markets[symbol]['precision']['price'], self.precisionMode)
578
+ # https://docs.bitfinex.com/docs/introduction#price-precision
579
+ # The precision level of all trading prices is based on significant figures.
580
+ # All pairs on Bitfinex use up to 5 significant digits and up to 8 decimals(e.g. 1.2345, 123.45, 1234.5, 0.00012345).
581
+ # Prices submit with a precision larger than 5 will be cut by the API.
582
+ return self.decimal_to_precision(price, TRUNCATE, 8, DECIMAL_PLACES)
583
+
584
+ async def fetch_status(self, params={}):
446
585
  """
447
- fetch deposit and withdraw fees
448
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-fees
449
- :param str[]|None codes: list of unified currency codes
586
+ the latest known information on the availability of the exchange API
587
+
588
+ https://docs.bitfinex.com/reference/rest-public-platform-status
589
+
450
590
  :param dict [params]: extra parameters specific to the exchange API endpoint
451
- :returns dict[]: a list of `fees structures <https://docs.ccxt.com/#/?id=fee-structure>`
591
+ :returns dict: a `status structure <https://docs.ccxt.com/#/?id=exchange-status-structure>`
452
592
  """
453
- await self.load_markets()
454
- response = await self.privatePostAccountFees(params)
455
- #
456
- # {
457
- # "withdraw": {
458
- # "BTC": "0.0004",
459
- # ...
460
- # }
461
- # }
462
593
  #
463
- withdraw = self.safe_value(response, 'withdraw')
464
- return self.parse_deposit_withdraw_fees(withdraw, codes)
465
-
466
- def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
467
- #
468
- # '0.0004'
594
+ # [1] # operative
595
+ # [0] # maintenance
469
596
  #
597
+ response = await self.publicGetPlatformStatus(params)
598
+ statusRaw = self.safe_string(response, 0)
470
599
  return {
471
- 'withdraw': {
472
- 'fee': self.parse_number(fee),
473
- 'percentage': None,
474
- },
475
- 'deposit': {
476
- 'fee': None,
477
- 'percentage': None,
478
- },
479
- 'networks': {},
480
- 'info': fee,
600
+ 'status': self.safe_string({'0': 'maintenance', '1': 'ok'}, statusRaw, statusRaw),
601
+ 'updated': None,
602
+ 'eta': None,
603
+ 'url': None,
604
+ 'info': response,
481
605
  }
482
606
 
483
- async def fetch_trading_fees(self, params={}):
484
- """
485
- fetch the trading fees for multiple markets
486
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-summary
487
- :param dict [params]: extra parameters specific to the exchange API endpoint
488
- :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
489
- """
490
- await self.load_markets()
491
- response = await self.privatePostSummary(params)
492
- #
493
- # {
494
- # "time": "2022-02-23T16:05:47.659000Z",
495
- # "status": {resid_hint: null, login_last: "2022-02-23T16:05:48Z"},
496
- # "is_locked": False,
497
- # "leo_lev": "0",
498
- # "leo_amount_avg": "0.0",
499
- # "trade_vol_30d": [
500
- # {
501
- # "curr": "Total(USD)",
502
- # "vol": "0.0",
503
- # "vol_safe": "0.0",
504
- # "vol_maker": "0.0",
505
- # "vol_BFX": "0.0",
506
- # "vol_BFX_safe": "0.0",
507
- # "vol_BFX_maker": "0.0"
508
- # }
509
- # ],
510
- # "fees_funding_30d": {},
511
- # "fees_funding_total_30d": "0",
512
- # "fees_trading_30d": {},
513
- # "fees_trading_total_30d": "0",
514
- # "rebates_trading_30d": {},
515
- # "rebates_trading_total_30d": "0",
516
- # "maker_fee": "0.001",
517
- # "taker_fee": "0.002",
518
- # "maker_fee_2crypto": "0.001",
519
- # "maker_fee_2stablecoin": "0.001",
520
- # "maker_fee_2fiat": "0.001",
521
- # "maker_fee_2deriv": "0.0002",
522
- # "taker_fee_2crypto": "0.002",
523
- # "taker_fee_2stablecoin": "0.002",
524
- # "taker_fee_2fiat": "0.002",
525
- # "taker_fee_2deriv": "0.00065",
526
- # "deriv_maker_rebate": "0.0002",
527
- # "deriv_taker_fee": "0.00065",
528
- # "trade_last": null
529
- # }
530
- #
531
- result = {}
532
- fiat = self.safe_value(self.options, 'fiat', {})
533
- makerFee = self.safe_number(response, 'maker_fee')
534
- takerFee = self.safe_number(response, 'taker_fee')
535
- makerFee2Fiat = self.safe_number(response, 'maker_fee_2fiat')
536
- takerFee2Fiat = self.safe_number(response, 'taker_fee_2fiat')
537
- makerFee2Deriv = self.safe_number(response, 'maker_fee_2deriv')
538
- takerFee2Deriv = self.safe_number(response, 'taker_fee_2deriv')
539
- for i in range(0, len(self.symbols)):
540
- symbol = self.symbols[i]
541
- market = self.market(symbol)
542
- fee = {
543
- 'info': response,
544
- 'symbol': symbol,
545
- 'percentage': True,
546
- 'tierBased': True,
547
- }
548
- if market['quote'] in fiat:
549
- fee['maker'] = makerFee2Fiat
550
- fee['taker'] = takerFee2Fiat
551
- elif market['contract']:
552
- fee['maker'] = makerFee2Deriv
553
- fee['taker'] = takerFee2Deriv
554
- else:
555
- fee['maker'] = makerFee
556
- fee['taker'] = takerFee
557
- result[symbol] = fee
558
- return result
559
-
560
- async def fetch_markets(self, params={}):
607
+ async def fetch_markets(self, params={}) -> List[Market]:
561
608
  """
562
609
  retrieves data on all markets for bitfinex
563
- :see: https://docs.bitfinex.com/v1/reference/rest-public-symbols
564
- :see: https://docs.bitfinex.com/v1/reference/rest-public-symbol-details
610
+
611
+ https://docs.bitfinex.com/reference/rest-public-conf
612
+
565
613
  :param dict [params]: extra parameters specific to the exchange API endpoint
566
614
  :returns dict[]: an array of objects representing market data
567
615
  """
568
- ids = await self.publicGetSymbols()
569
- #
570
- # ["btcusd", "ltcusd", "ltcbtc"]
616
+ spotMarketsInfoPromise = self.publicGetConfPubInfoPair(params)
617
+ futuresMarketsInfoPromise = self.publicGetConfPubInfoPairFutures(params)
618
+ marginIdsPromise = self.publicGetConfPubListPairMargin(params)
619
+ spotMarketsInfo, futuresMarketsInfo, marginIds = await asyncio.gather(*[spotMarketsInfoPromise, futuresMarketsInfoPromise, marginIdsPromise])
620
+ spotMarketsInfo = self.safe_list(spotMarketsInfo, 0, [])
621
+ futuresMarketsInfo = self.safe_list(futuresMarketsInfo, 0, [])
622
+ markets = self.array_concat(spotMarketsInfo, futuresMarketsInfo)
623
+ marginIds = self.safe_value(marginIds, 0, [])
571
624
  #
572
- details = await self.publicGetSymbolsDetails()
573
- #
574
- # [
575
- # {
576
- # "pair":"btcusd",
577
- # "price_precision":5,
578
- # "initial_margin":"10.0",
579
- # "minimum_margin":"5.0",
580
- # "maximum_order_size":"2000.0",
581
- # "minimum_order_size":"0.0002",
582
- # "expiration":"NA",
583
- # "margin":true
584
- # },
585
- # ]
625
+ # [
626
+ # "1INCH:USD",
627
+ # [
628
+ # null,
629
+ # null,
630
+ # null,
631
+ # "2.0",
632
+ # "100000.0",
633
+ # null,
634
+ # null,
635
+ # null,
636
+ # null,
637
+ # null,
638
+ # null,
639
+ # null
640
+ # ]
641
+ # ]
586
642
  #
587
643
  result = []
588
- for i in range(0, len(details)):
589
- market = details[i]
590
- id = self.safe_string(market, 'pair')
591
- if not self.in_array(id, ids):
592
- continue
593
- id = id.upper()
644
+ for i in range(0, len(markets)):
645
+ pair = markets[i]
646
+ id = self.safe_string_upper(pair, 0)
647
+ market = self.safe_value(pair, 1, {})
648
+ spot = True
649
+ if id.find('F0') >= 0:
650
+ spot = False
651
+ swap = not spot
594
652
  baseId = None
595
653
  quoteId = None
596
654
  if id.find(':') >= 0:
@@ -602,40 +660,51 @@ class bitfinex(Exchange, ImplicitAPI):
602
660
  quoteId = id[3:6]
603
661
  base = self.safe_currency_code(baseId)
604
662
  quote = self.safe_currency_code(quoteId)
663
+ splitBase = base.split('F0')
664
+ splitQuote = quote.split('F0')
665
+ base = self.safe_string(splitBase, 0)
666
+ quote = self.safe_string(splitQuote, 0)
605
667
  symbol = base + '/' + quote
606
- type = 'spot'
607
- if id.find('F0') > -1:
608
- type = 'swap'
668
+ baseId = self.get_currency_id(baseId)
669
+ quoteId = self.get_currency_id(quoteId)
670
+ settle = None
671
+ settleId = None
672
+ if swap:
673
+ settle = quote
674
+ settleId = quote
675
+ symbol = symbol + ':' + settle
676
+ minOrderSizeString = self.safe_string(market, 3)
677
+ maxOrderSizeString = self.safe_string(market, 4)
678
+ margin = False
679
+ if spot and self.in_array(id, marginIds):
680
+ margin = True
609
681
  result.append({
610
- 'id': id,
682
+ 'id': 't' + id,
611
683
  'symbol': symbol,
612
684
  'base': base,
613
685
  'quote': quote,
614
- 'settle': None,
686
+ 'settle': settle,
615
687
  'baseId': baseId,
616
688
  'quoteId': quoteId,
617
- 'settleId': None,
618
- 'type': type,
619
- 'spot': (type == 'spot'),
620
- 'margin': self.safe_value(market, 'margin'),
621
- 'swap': (type == 'swap'),
689
+ 'settleId': settleId,
690
+ 'type': 'spot' if spot else 'swap',
691
+ 'spot': spot,
692
+ 'margin': margin,
693
+ 'swap': swap,
622
694
  'future': False,
623
695
  'option': False,
624
696
  'active': True,
625
- 'contract': (type == 'swap'),
626
- 'linear': None,
627
- 'inverse': None,
628
- 'contractSize': None,
697
+ 'contract': swap,
698
+ 'linear': True if swap else None,
699
+ 'inverse': False if swap else None,
700
+ 'contractSize': self.parse_number('1') if swap else None,
629
701
  'expiry': None,
630
702
  'expiryDatetime': None,
631
703
  'strike': None,
632
704
  'optionType': None,
633
705
  'precision': {
634
- # https://docs.bitfinex.com/docs/introduction#amount-precision
635
- # The amount field allows up to 8 decimals.
636
- # Anything exceeding self will be rounded to the 8th decimal.
637
- 'amount': int('8'),
638
- 'price': self.safe_integer(market, 'price_precision'),
706
+ 'amount': int('8'), # https://github.com/ccxt/ccxt/issues/7310
707
+ 'price': int('5'),
639
708
  },
640
709
  'limits': {
641
710
  'leverage': {
@@ -643,8 +712,8 @@ class bitfinex(Exchange, ImplicitAPI):
643
712
  'max': None,
644
713
  },
645
714
  'amount': {
646
- 'min': self.safe_number(market, 'minimum_order_size'),
647
- 'max': self.safe_number(market, 'maximum_order_size'),
715
+ 'min': self.parse_number(minOrderSizeString),
716
+ 'max': self.parse_number(maxOrderSizeString),
648
717
  },
649
718
  'price': {
650
719
  'min': self.parse_number('1e-8'),
@@ -655,83 +724,247 @@ class bitfinex(Exchange, ImplicitAPI):
655
724
  'max': None,
656
725
  },
657
726
  },
658
- 'created': None,
727
+ 'created': None, # todo: the api needs revision for extra params & endpoints for possibility of returning a timestamp for self
659
728
  'info': market,
660
729
  })
661
730
  return result
662
731
 
663
- def amount_to_precision(self, symbol, amount):
664
- # https://docs.bitfinex.com/docs/introduction#amount-precision
665
- # The amount field allows up to 8 decimals.
666
- # Anything exceeding self will be rounded to the 8th decimal.
667
- symbol = self.safe_symbol(symbol)
668
- return self.decimal_to_precision(amount, TRUNCATE, self.markets[symbol]['precision']['amount'], DECIMAL_PLACES)
732
+ async def fetch_currencies(self, params={}) -> Currencies:
733
+ """
734
+ fetches all available currencies on an exchange
669
735
 
670
- def price_to_precision(self, symbol, price):
671
- symbol = self.safe_symbol(symbol)
672
- price = self.decimal_to_precision(price, ROUND, self.markets[symbol]['precision']['price'], self.precisionMode)
673
- # https://docs.bitfinex.com/docs/introduction#price-precision
674
- # The precision level of all trading prices is based on significant figures.
675
- # All pairs on Bitfinex use up to 5 significant digits and up to 8 decimals(e.g. 1.2345, 123.45, 1234.5, 0.00012345).
676
- # Prices submit with a precision larger than 5 will be cut by the API.
677
- return self.decimal_to_precision(price, TRUNCATE, 8, DECIMAL_PLACES)
736
+ https://docs.bitfinex.com/reference/rest-public-conf
737
+
738
+ :param dict [params]: extra parameters specific to the exchange API endpoint
739
+ :returns dict: an associative dictionary of currencies
740
+ """
741
+ labels = [
742
+ 'pub:list:currency',
743
+ 'pub:map:currency:sym', # maps symbols to their API symbols, BAB > BCH
744
+ 'pub:map:currency:label', # verbose friendly names, BNT > Bancor
745
+ 'pub:map:currency:unit', # maps symbols to unit of measure where applicable
746
+ 'pub:map:currency:undl', # maps derivatives symbols to their underlying currency
747
+ 'pub:map:currency:pool', # maps symbols to underlying network/protocol they operate on
748
+ 'pub:map:currency:explorer', # maps symbols to their recognised block explorer URLs
749
+ 'pub:map:currency:tx:fee', # maps currencies to their withdrawal fees https://github.com/ccxt/ccxt/issues/7745,
750
+ 'pub:map:tx:method', # maps withdrawal/deposit methods to their API symbols
751
+ ]
752
+ config = ','.join(labels)
753
+ request: dict = {
754
+ 'config': config,
755
+ }
756
+ response = await self.publicGetConfConfig(self.extend(request, params))
757
+ #
758
+ # [
759
+ #
760
+ # a list of symbols
761
+ # ["AAA","ABS","ADA"],
762
+ #
763
+ # # sym
764
+ # # maps symbols to their API symbols, BAB > BCH
765
+ # [
766
+ # ["BAB", "BCH"],
767
+ # ["CNHT", "CNHt"],
768
+ # ["DSH", "DASH"],
769
+ # ["IOT", "IOTA"],
770
+ # ["LES", "LEO-EOS"],
771
+ # ["LET", "LEO-ERC20"],
772
+ # ["STJ", "STORJ"],
773
+ # ["TSD", "TUSD"],
774
+ # ["UDC", "USDC"],
775
+ # ["USK", "USDK"],
776
+ # ["UST", "USDt"],
777
+ # ["USTF0", "USDt0"],
778
+ # ["XCH", "XCHF"],
779
+ # ["YYW", "YOYOW"],
780
+ # # ...
781
+ # ],
782
+ # # label
783
+ # # verbose friendly names, BNT > Bancor
784
+ # [
785
+ # ["BAB", "Bitcoin Cash"],
786
+ # ["BCH", "Bitcoin Cash"],
787
+ # ["LEO", "Unus Sed LEO"],
788
+ # ["LES", "Unus Sed LEO(EOS)"],
789
+ # ["LET", "Unus Sed LEO(ERC20)"],
790
+ # # ...
791
+ # ],
792
+ # # unit
793
+ # # maps symbols to unit of measure where applicable
794
+ # [
795
+ # ["IOT", "Mi|MegaIOTA"],
796
+ # ],
797
+ # # undl
798
+ # # maps derivatives symbols to their underlying currency
799
+ # [
800
+ # ["USTF0", "UST"],
801
+ # ["BTCF0", "BTC"],
802
+ # ["ETHF0", "ETH"],
803
+ # ],
804
+ # # pool
805
+ # # maps symbols to underlying network/protocol they operate on
806
+ # [
807
+ # ['SAN', 'ETH'], ['OMG', 'ETH'], ['AVT', 'ETH'], ["EDO", "ETH"],
808
+ # ['ESS', 'ETH'], ['ATD', 'EOS'], ['ADD', 'EOS'], ["MTO", "EOS"],
809
+ # ['PNK', 'ETH'], ['BAB', 'BCH'], ['WLO', 'XLM'], ["VLD", "ETH"],
810
+ # ['BTT', 'TRX'], ['IMP', 'ETH'], ['SCR', 'ETH'], ["GNO", "ETH"],
811
+ # # ...
812
+ # ],
813
+ # # explorer
814
+ # # maps symbols to their recognised block explorer URLs
815
+ # [
816
+ # [
817
+ # "AIO",
818
+ # [
819
+ # "https://mainnet.aion.network",
820
+ # "https://mainnet.aion.network/#/account/VAL",
821
+ # "https://mainnet.aion.network/#/transaction/VAL"
822
+ # ]
823
+ # ],
824
+ # # ...
825
+ # ],
826
+ # # fee
827
+ # # maps currencies to their withdrawal fees
828
+ # [
829
+ # ["AAA",[0,0]],
830
+ # ["ABS",[0,131.3]],
831
+ # ["ADA",[0,0.3]],
832
+ # ],
833
+ # ]
834
+ #
835
+ indexed: dict = {
836
+ 'sym': self.index_by(self.safe_value(response, 1, []), 0),
837
+ 'label': self.index_by(self.safe_value(response, 2, []), 0),
838
+ 'unit': self.index_by(self.safe_value(response, 3, []), 0),
839
+ 'undl': self.index_by(self.safe_value(response, 4, []), 0),
840
+ 'pool': self.index_by(self.safe_value(response, 5, []), 0),
841
+ 'explorer': self.index_by(self.safe_value(response, 6, []), 0),
842
+ 'fees': self.index_by(self.safe_value(response, 7, []), 0),
843
+ }
844
+ ids = self.safe_value(response, 0, [])
845
+ result: dict = {}
846
+ for i in range(0, len(ids)):
847
+ id = ids[i]
848
+ if id.find('F0') >= 0:
849
+ # we get a lot of F0 currencies, skip those
850
+ continue
851
+ code = self.safe_currency_code(id)
852
+ label = self.safe_value(indexed['label'], id, [])
853
+ name = self.safe_string(label, 1)
854
+ pool = self.safe_value(indexed['pool'], id, [])
855
+ rawType = self.safe_string(pool, 1)
856
+ isCryptoCoin = (rawType is not None) or (id in indexed['explorer']) # "hacky" solution
857
+ type = None
858
+ if isCryptoCoin:
859
+ type = 'crypto'
860
+ feeValues = self.safe_value(indexed['fees'], id, [])
861
+ fees = self.safe_value(feeValues, 1, [])
862
+ fee = self.safe_number(fees, 1)
863
+ undl = self.safe_value(indexed['undl'], id, [])
864
+ precision = '8' # default precision, todo: fix "magic constants"
865
+ fid = 'f' + id
866
+ result[code] = {
867
+ 'id': fid,
868
+ 'uppercaseId': id,
869
+ 'code': code,
870
+ 'info': [id, label, pool, feeValues, undl],
871
+ 'type': type,
872
+ 'name': name,
873
+ 'active': True,
874
+ 'deposit': None,
875
+ 'withdraw': None,
876
+ 'fee': fee,
877
+ 'precision': int(precision),
878
+ 'limits': {
879
+ 'amount': {
880
+ 'min': self.parse_number(self.parse_precision(precision)),
881
+ 'max': None,
882
+ },
883
+ 'withdraw': {
884
+ 'min': fee,
885
+ 'max': None,
886
+ },
887
+ },
888
+ 'networks': {},
889
+ }
890
+ networks: dict = {}
891
+ currencyNetworks = self.safe_value(response, 8, [])
892
+ cleanId = id.replace('F0', '')
893
+ for j in range(0, len(currencyNetworks)):
894
+ pair = currencyNetworks[j]
895
+ networkId = self.safe_string(pair, 0)
896
+ currencyId = self.safe_string(self.safe_value(pair, 1, []), 0)
897
+ if currencyId == cleanId:
898
+ network = self.network_id_to_code(networkId)
899
+ networks[network] = {
900
+ 'info': networkId,
901
+ 'id': networkId.lower(),
902
+ 'network': networkId,
903
+ 'active': None,
904
+ 'deposit': None,
905
+ 'withdraw': None,
906
+ 'fee': None,
907
+ 'precision': None,
908
+ 'limits': {
909
+ 'withdraw': {
910
+ 'min': None,
911
+ 'max': None,
912
+ },
913
+ },
914
+ }
915
+ keysNetworks = list(networks.keys())
916
+ networksLength = len(keysNetworks)
917
+ if networksLength > 0:
918
+ result[code]['networks'] = networks
919
+ return result
678
920
 
679
921
  async def fetch_balance(self, params={}) -> Balances:
680
922
  """
681
923
  query for balance and get the amount of funds available for trading or funds locked in orders
682
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-wallet-balances
924
+
925
+ https://docs.bitfinex.com/reference/rest-auth-wallets
926
+
683
927
  :param dict [params]: extra parameters specific to the exchange API endpoint
684
928
  :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
685
929
  """
930
+ # self api call does not return the 'used' amount - use the v1 version instead(which also returns zero balances)
931
+ # there is a difference between self and the v1 api, namely trading wallet is called margin in v2
686
932
  await self.load_markets()
687
- accountsByType = self.safe_value(self.options, 'accountsByType', {})
933
+ accountsByType = self.safe_value(self.options, 'v2AccountsByType', {})
688
934
  requestedType = self.safe_string(params, 'type', 'exchange')
689
935
  accountType = self.safe_string(accountsByType, requestedType, requestedType)
690
936
  if accountType is None:
691
937
  keys = list(accountsByType.keys())
692
938
  raise ExchangeError(self.id + ' fetchBalance() type parameter must be one of ' + ', '.join(keys))
693
- query = self.omit(params, 'type')
694
- response = await self.privatePostBalances(query)
695
- # [{type: "deposit",
696
- # "currency": "btc",
697
- # "amount": "0.00116721",
698
- # "available": "0.00116721"},
699
- # {type: "exchange",
700
- # "currency": "ust",
701
- # "amount": "0.0000002",
702
- # "available": "0.0000002"},
703
- # {type: "trading",
704
- # "currency": "btc",
705
- # "amount": "0.0005",
706
- # "available": "0.0005"}],
707
- result = {'info': response}
708
939
  isDerivative = requestedType == 'derivatives'
940
+ query = self.omit(params, 'type')
941
+ response = await self.privatePostAuthRWallets(query)
942
+ result: dict = {'info': response}
709
943
  for i in range(0, len(response)):
710
944
  balance = response[i]
711
- type = self.safe_string(balance, 'type')
712
- currencyId = self.safe_string_lower(balance, 'currency', '')
945
+ account = self.account()
946
+ interest = self.safe_string(balance, 3)
947
+ if interest != '0':
948
+ account['debt'] = interest
949
+ type = self.safe_string(balance, 0)
950
+ currencyId = self.safe_string_lower(balance, 1, '')
713
951
  start = len(currencyId) - 2
714
952
  isDerivativeCode = currencyId[start:] == 'f0'
715
953
  # self will only filter the derivative codes if the requestedType is 'derivatives'
716
954
  derivativeCondition = (not isDerivative or isDerivativeCode)
717
955
  if (accountType == type) and derivativeCondition:
718
956
  code = self.safe_currency_code(currencyId)
719
- # bitfinex had BCH previously, now it's BAB, but the old
720
- # BCH symbol is kept for backward-compatibility
721
- # we need a workaround here so that the old BCH balance
722
- # would not override the new BAB balance(BAB is unified to BCH)
723
- # https://github.com/ccxt/ccxt/issues/4989
724
- if not (code in result):
725
- account = self.account()
726
- account['free'] = self.safe_string(balance, 'available')
727
- account['total'] = self.safe_string(balance, 'amount')
728
- result[code] = account
957
+ account['total'] = self.safe_string(balance, 2)
958
+ account['free'] = self.safe_string(balance, 4)
959
+ result[code] = account
729
960
  return self.safe_balance(result)
730
961
 
731
962
  async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
732
963
  """
733
964
  transfer currency internally between wallets on the same account
734
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-transfer-between-wallets
965
+
966
+ https://docs.bitfinex.com/reference/rest-auth-transfer
967
+
735
968
  :param str code: unified currency code
736
969
  :param float amount: amount to transfer
737
970
  :param str fromAccount: account to transfer from
@@ -742,707 +975,1445 @@ class bitfinex(Exchange, ImplicitAPI):
742
975
  # transferring between derivatives wallet and regular wallet is not documented in their API
743
976
  # however we support it in CCXT(from just looking at web inspector)
744
977
  await self.load_markets()
745
- accountsByType = self.safe_value(self.options, 'accountsByType', {})
746
- fromId = self.safe_string(accountsByType, fromAccount, fromAccount)
747
- toId = self.safe_string(accountsByType, toAccount, toAccount)
978
+ accountsByType = self.safe_value(self.options, 'v2AccountsByType', {})
979
+ fromId = self.safe_string(accountsByType, fromAccount)
980
+ if fromId is None:
981
+ keys = list(accountsByType.keys())
982
+ raise ArgumentsRequired(self.id + ' transfer() fromAccount must be one of ' + ', '.join(keys))
983
+ toId = self.safe_string(accountsByType, toAccount)
984
+ if toId is None:
985
+ keys = list(accountsByType.keys())
986
+ raise ArgumentsRequired(self.id + ' transfer() toAccount must be one of ' + ', '.join(keys))
748
987
  currency = self.currency(code)
749
- fromCurrencyId = self.convert_derivatives_id(currency['id'], fromAccount)
750
- toCurrencyId = self.convert_derivatives_id(currency['id'], toAccount)
988
+ fromCurrencyId = self.convert_derivatives_id(currency, fromAccount)
989
+ toCurrencyId = self.convert_derivatives_id(currency, toAccount)
751
990
  requestedAmount = self.currency_to_precision(code, amount)
752
- request = {
991
+ # self request is slightly different from v1 fromAccount -> from
992
+ request: dict = {
753
993
  'amount': requestedAmount,
754
994
  'currency': fromCurrencyId,
755
995
  'currency_to': toCurrencyId,
756
- 'walletfrom': fromId,
757
- 'walletto': toId,
996
+ 'from': fromId,
997
+ 'to': toId,
758
998
  }
759
- response = await self.privatePostTransfer(self.extend(request, params))
999
+ response = await self.privatePostAuthWTransfer(self.extend(request, params))
760
1000
  #
761
1001
  # [
762
- # {
763
- # "status": "success",
764
- # "message": "0.0001 Bitcoin transfered from Margin to Exchange"
765
- # }
1002
+ # 1616451183763,
1003
+ # "acc_tf",
1004
+ # null,
1005
+ # null,
1006
+ # [
1007
+ # 1616451183763,
1008
+ # "exchange",
1009
+ # "margin",
1010
+ # null,
1011
+ # "UST",
1012
+ # "UST",
1013
+ # null,
1014
+ # 1
1015
+ # ],
1016
+ # null,
1017
+ # "SUCCESS",
1018
+ # "1.0 Tether USDt transfered from Exchange to Margin"
766
1019
  # ]
767
1020
  #
768
- result = self.safe_value(response, 0)
769
- message = self.safe_string(result, 'message')
770
- if message is None:
771
- raise ExchangeError(self.id + ' transfer failed')
772
- return self.extend(self.parse_transfer(result, currency), {
773
- 'fromAccount': fromAccount,
774
- 'toAccount': toAccount,
775
- 'amount': self.parse_number(requestedAmount),
776
- })
1021
+ error = self.safe_string(response, 0)
1022
+ if error == 'error':
1023
+ message = self.safe_string(response, 2, '')
1024
+ # same message v1
1025
+ self.throw_exactly_matched_exception(self.exceptions['exact'], message, self.id + ' ' + message)
1026
+ raise ExchangeError(self.id + ' ' + message)
1027
+ return self.parse_transfer({'result': response}, currency)
777
1028
 
778
- def parse_transfer(self, transfer, currency: Currency = None):
1029
+ def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
779
1030
  #
780
- # {
781
- # "status": "success",
782
- # "message": "0.0001 Bitcoin transfered from Margin to Exchange"
783
- # }
1031
+ # transfer
1032
+ #
1033
+ # [
1034
+ # 1616451183763,
1035
+ # "acc_tf",
1036
+ # null,
1037
+ # null,
1038
+ # [
1039
+ # 1616451183763,
1040
+ # "exchange",
1041
+ # "margin",
1042
+ # null,
1043
+ # "UST",
1044
+ # "UST",
1045
+ # null,
1046
+ # 1
1047
+ # ],
1048
+ # null,
1049
+ # "SUCCESS",
1050
+ # "1.0 Tether USDt transfered from Exchange to Margin"
1051
+ # ]
784
1052
  #
1053
+ result = self.safe_list(transfer, 'result')
1054
+ timestamp = self.safe_integer(result, 0)
1055
+ info = self.safe_value(result, 4)
1056
+ fromAccount = self.safe_string(info, 1)
1057
+ toAccount = self.safe_string(info, 2)
1058
+ currencyId = self.safe_string(info, 5)
1059
+ status = self.safe_string(result, 6)
785
1060
  return {
786
- 'info': transfer,
787
1061
  'id': None,
788
- 'timestamp': None,
789
- 'datetime': None,
790
- 'currency': self.safe_currency_code(None, currency),
791
- 'amount': None,
792
- 'fromAccount': None,
793
- 'toAccount': None,
794
- 'status': self.parse_transfer_status(self.safe_string(transfer, 'status')),
1062
+ 'timestamp': timestamp,
1063
+ 'datetime': self.iso8601(timestamp),
1064
+ 'status': self.parse_transfer_status(status),
1065
+ 'amount': self.safe_number(info, 7),
1066
+ 'currency': self.safe_currency_code(currencyId, currency),
1067
+ 'fromAccount': fromAccount,
1068
+ 'toAccount': toAccount,
1069
+ 'info': result,
795
1070
  }
796
1071
 
797
- def parse_transfer_status(self, status):
798
- statuses = {
1072
+ def parse_transfer_status(self, status: Str) -> Str:
1073
+ statuses: dict = {
799
1074
  'SUCCESS': 'ok',
1075
+ 'ERROR': 'failed',
1076
+ 'FAILURE': 'failed',
800
1077
  }
801
1078
  return self.safe_string(statuses, status, status)
802
1079
 
803
- def convert_derivatives_id(self, currencyId, type):
804
- start = len(currencyId) - 2
805
- isDerivativeCode = currencyId[start:] == 'F0'
806
- if (type != 'derivatives' and type != 'trading' and type != 'margin') and isDerivativeCode:
807
- currencyId = currencyId[0:start]
808
- elif type == 'derivatives' and not isDerivativeCode:
809
- currencyId = currencyId + 'F0'
1080
+ def convert_derivatives_id(self, currency, type):
1081
+ # there is a difference between self and the v1 api, namely trading wallet is called margin in v2
1082
+ # {
1083
+ # "id": "fUSTF0",
1084
+ # "code": "USTF0",
1085
+ # "info": ['USTF0', [], [], [], ["USTF0", "UST"]],
1086
+ info = self.safe_value(currency, 'info')
1087
+ transferId = self.safe_string(info, 0)
1088
+ underlying = self.safe_value(info, 4, [])
1089
+ currencyId = None
1090
+ if type == 'derivatives':
1091
+ currencyId = self.safe_string(underlying, 0, transferId)
1092
+ start = len(currencyId) - 2
1093
+ isDerivativeCode = currencyId[start:] == 'F0'
1094
+ if not isDerivativeCode:
1095
+ currencyId = currencyId + 'F0'
1096
+ elif type != 'margin':
1097
+ currencyId = self.safe_string(underlying, 1, transferId)
1098
+ else:
1099
+ currencyId = transferId
810
1100
  return currencyId
811
1101
 
812
1102
  async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
813
1103
  """
814
1104
  fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
815
- :see: https://docs.bitfinex.com/v1/reference/rest-public-orderbook
1105
+
1106
+ https://docs.bitfinex.com/reference/rest-public-book
1107
+
816
1108
  :param str symbol: unified symbol of the market to fetch the order book for
817
- :param int [limit]: the maximum amount of order book entries to return
1109
+ :param int [limit]: the maximum amount of order book entries to return, bitfinex only allows 1, 25, or 100
818
1110
  :param dict [params]: extra parameters specific to the exchange API endpoint
819
1111
  :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
820
1112
  """
821
1113
  await self.load_markets()
1114
+ precision = self.safe_value(self.options, 'precision', 'R0')
822
1115
  market = self.market(symbol)
823
- request = {
1116
+ request: dict = {
824
1117
  'symbol': market['id'],
1118
+ 'precision': precision,
825
1119
  }
826
1120
  if limit is not None:
827
- request['limit_bids'] = limit
828
- request['limit_asks'] = limit
829
- response = await self.publicGetBookSymbol(self.extend(request, params))
830
- return self.parse_order_book(response, market['symbol'], None, 'bids', 'asks', 'price', 'amount')
1121
+ request['len'] = limit
1122
+ fullRequest = self.extend(request, params)
1123
+ orderbook = await self.publicGetBookSymbolPrecision(fullRequest)
1124
+ timestamp = self.milliseconds()
1125
+ result: dict = {
1126
+ 'symbol': market['symbol'],
1127
+ 'bids': [],
1128
+ 'asks': [],
1129
+ 'timestamp': timestamp,
1130
+ 'datetime': self.iso8601(timestamp),
1131
+ 'nonce': None,
1132
+ }
1133
+ priceIndex = 1 if (fullRequest['precision'] == 'R0') else 0
1134
+ for i in range(0, len(orderbook)):
1135
+ order = orderbook[i]
1136
+ price = self.safe_number(order, priceIndex)
1137
+ signedAmount = self.safe_string(order, 2)
1138
+ amount = Precise.string_abs(signedAmount)
1139
+ side = 'bids' if Precise.string_gt(signedAmount, '0') else 'asks'
1140
+ result[side].append([price, self.parse_number(amount)])
1141
+ result['bids'] = self.sort_by(result['bids'], 0, True)
1142
+ result['asks'] = self.sort_by(result['asks'], 0)
1143
+ return result
1144
+
1145
+ def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
1146
+ #
1147
+ # on trading pairs(ex. tBTCUSD)
1148
+ #
1149
+ # {
1150
+ # 'result': [
1151
+ # SYMBOL,
1152
+ # BID,
1153
+ # BID_SIZE,
1154
+ # ASK,
1155
+ # ASK_SIZE,
1156
+ # DAILY_CHANGE,
1157
+ # DAILY_CHANGE_RELATIVE,
1158
+ # LAST_PRICE,
1159
+ # VOLUME,
1160
+ # HIGH,
1161
+ # LOW
1162
+ # ]
1163
+ # }
1164
+ #
1165
+ #
1166
+ # on funding currencies(ex. fUSD)
1167
+ #
1168
+ # {
1169
+ # 'result': [
1170
+ # SYMBOL,
1171
+ # FRR,
1172
+ # BID,
1173
+ # BID_PERIOD,
1174
+ # BID_SIZE,
1175
+ # ASK,
1176
+ # ASK_PERIOD,
1177
+ # ASK_SIZE,
1178
+ # DAILY_CHANGE,
1179
+ # DAILY_CHANGE_RELATIVE,
1180
+ # LAST_PRICE,
1181
+ # VOLUME,
1182
+ # HIGH,
1183
+ # LOW,
1184
+ # _PLACEHOLDER,
1185
+ # _PLACEHOLDER,
1186
+ # FRR_AMOUNT_AVAILABLE
1187
+ # ]
1188
+ # }
1189
+ #
1190
+ result = self.safe_list(ticker, 'result')
1191
+ symbol = self.safe_symbol(None, market)
1192
+ length = len(result)
1193
+ last = self.safe_string(result, length - 4)
1194
+ percentage = self.safe_string(result, length - 5)
1195
+ return self.safe_ticker({
1196
+ 'symbol': symbol,
1197
+ 'timestamp': None,
1198
+ 'datetime': None,
1199
+ 'high': self.safe_string(result, length - 2),
1200
+ 'low': self.safe_string(result, length - 1),
1201
+ 'bid': self.safe_string(result, length - 10),
1202
+ 'bidVolume': self.safe_string(result, length - 9),
1203
+ 'ask': self.safe_string(result, length - 8),
1204
+ 'askVolume': self.safe_string(result, length - 7),
1205
+ 'vwap': None,
1206
+ 'open': None,
1207
+ 'close': last,
1208
+ 'last': last,
1209
+ 'previousClose': None,
1210
+ 'change': self.safe_string(result, length - 6),
1211
+ 'percentage': Precise.string_mul(percentage, '100'),
1212
+ 'average': None,
1213
+ 'baseVolume': self.safe_string(result, length - 3),
1214
+ 'quoteVolume': None,
1215
+ 'info': result,
1216
+ }, market)
831
1217
 
832
1218
  async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
833
1219
  """
834
1220
  fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
1221
+
1222
+ https://docs.bitfinex.com/reference/rest-public-tickers
1223
+
835
1224
  :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
836
1225
  :param dict [params]: extra parameters specific to the exchange API endpoint
837
1226
  :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
838
1227
  """
839
1228
  await self.load_markets()
840
1229
  symbols = self.market_symbols(symbols)
841
- response = await self.publicGetTickers(params)
842
- result = {}
843
- for i in range(0, len(response)):
844
- ticker = self.parse_ticker(response[i])
845
- symbol = ticker['symbol']
846
- result[symbol] = ticker
847
- return self.filter_by_array_tickers(result, 'symbol', symbols)
1230
+ request: dict = {}
1231
+ if symbols is not None:
1232
+ ids = self.market_ids(symbols)
1233
+ request['symbols'] = ','.join(ids)
1234
+ else:
1235
+ request['symbols'] = 'ALL'
1236
+ tickers = await self.publicGetTickers(self.extend(request, params))
1237
+ #
1238
+ # [
1239
+ # # on trading pairs(ex. tBTCUSD)
1240
+ # [
1241
+ # SYMBOL,
1242
+ # BID,
1243
+ # BID_SIZE,
1244
+ # ASK,
1245
+ # ASK_SIZE,
1246
+ # DAILY_CHANGE,
1247
+ # DAILY_CHANGE_RELATIVE,
1248
+ # LAST_PRICE,
1249
+ # VOLUME,
1250
+ # HIGH,
1251
+ # LOW
1252
+ # ],
1253
+ # # on funding currencies(ex. fUSD)
1254
+ # [
1255
+ # SYMBOL,
1256
+ # FRR,
1257
+ # BID,
1258
+ # BID_PERIOD,
1259
+ # BID_SIZE,
1260
+ # ASK,
1261
+ # ASK_PERIOD,
1262
+ # ASK_SIZE,
1263
+ # DAILY_CHANGE,
1264
+ # DAILY_CHANGE_RELATIVE,
1265
+ # LAST_PRICE,
1266
+ # VOLUME,
1267
+ # HIGH,
1268
+ # LOW,
1269
+ # _PLACEHOLDER,
1270
+ # _PLACEHOLDER,
1271
+ # FRR_AMOUNT_AVAILABLE
1272
+ # ],
1273
+ # ...
1274
+ # ]
1275
+ #
1276
+ result: dict = {}
1277
+ for i in range(0, len(tickers)):
1278
+ ticker = tickers[i]
1279
+ marketId = self.safe_string(ticker, 0)
1280
+ market = self.safe_market(marketId)
1281
+ symbol = market['symbol']
1282
+ result[symbol] = self.parse_ticker({'result': ticker}, market)
1283
+ return self.filter_by_array_tickers(result, 'symbol', symbols)
848
1284
 
849
1285
  async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
850
1286
  """
851
1287
  fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
852
- :see: https://docs.bitfinex.com/v1/reference/rest-public-ticker
1288
+
1289
+ https://docs.bitfinex.com/reference/rest-public-ticker
1290
+
853
1291
  :param str symbol: unified symbol of the market to fetch the ticker for
854
1292
  :param dict [params]: extra parameters specific to the exchange API endpoint
855
1293
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
856
1294
  """
857
1295
  await self.load_markets()
858
1296
  market = self.market(symbol)
859
- request = {
1297
+ request: dict = {
860
1298
  'symbol': market['id'],
861
1299
  }
862
- ticker = await self.publicGetPubtickerSymbol(self.extend(request, params))
863
- return self.parse_ticker(ticker, market)
864
-
865
- def parse_ticker(self, ticker, market: Market = None) -> Ticker:
866
- timestamp = self.safe_timestamp(ticker, 'timestamp')
867
- marketId = self.safe_string(ticker, 'pair')
868
- market = self.safe_market(marketId, market)
869
- symbol = market['symbol']
870
- last = self.safe_string(ticker, 'last_price')
871
- return self.safe_ticker({
872
- 'symbol': symbol,
873
- 'timestamp': timestamp,
874
- 'datetime': self.iso8601(timestamp),
875
- 'high': self.safe_string(ticker, 'high'),
876
- 'low': self.safe_string(ticker, 'low'),
877
- 'bid': self.safe_string(ticker, 'bid'),
878
- 'bidVolume': None,
879
- 'ask': self.safe_string(ticker, 'ask'),
880
- 'askVolume': None,
881
- 'vwap': None,
882
- 'open': None,
883
- 'close': last,
884
- 'last': last,
885
- 'previousClose': None,
886
- 'change': None,
887
- 'percentage': None,
888
- 'average': self.safe_string(ticker, 'mid'),
889
- 'baseVolume': self.safe_string(ticker, 'volume'),
890
- 'quoteVolume': None,
891
- 'info': ticker,
892
- }, market)
1300
+ ticker = await self.publicGetTickerSymbol(self.extend(request, params))
1301
+ result: dict = {'result': ticker}
1302
+ return self.parse_ticker(result, market)
893
1303
 
894
- def parse_trade(self, trade, market: Market = None) -> Trade:
895
- #
896
- # fetchTrades(public) v1
897
- #
898
- # {
899
- # "timestamp":1637258380,
900
- # "tid":894452833,
901
- # "price":"0.99941",
902
- # "amount":"261.38",
903
- # "exchange":"bitfinex",
904
- # "type":"sell"
905
- # }
906
- #
907
- # fetchMyTrades(private) v1
908
- #
909
- # {
910
- # "price":"0.99941",
911
- # "amount":"261.38",
912
- # "timestamp":"1637258380.0",
913
- # "type":"Sell",
914
- # "fee_currency":"UST",
915
- # "fee_amount":"-0.52245157",
916
- # "tid":894452833,
917
- # "order_id":78819731373
918
- # }
919
- #
920
- # {
921
- # "price":"0.99958",
922
- # "amount":"261.90514",
923
- # "timestamp":"1637258238.0",
924
- # "type":"Buy",
925
- # "fee_currency":"UDC",
926
- # "fee_amount":"-0.52381028",
927
- # "tid":894452800,
928
- # "order_id":78819504838
929
- # }
930
- #
931
- id = self.safe_string(trade, 'tid')
932
- timestamp = self.safe_timestamp(trade, 'timestamp')
1304
+ def parse_trade(self, trade: dict, market: Market = None) -> Trade:
1305
+ #
1306
+ # fetchTrades(public)
1307
+ #
1308
+ # [
1309
+ # ID,
1310
+ # MTS, # timestamp
1311
+ # AMOUNT,
1312
+ # PRICE
1313
+ # ]
1314
+ #
1315
+ # fetchMyTrades(private)
1316
+ #
1317
+ # [
1318
+ # ID,
1319
+ # PAIR,
1320
+ # MTS_CREATE,
1321
+ # ORDER_ID,
1322
+ # EXEC_AMOUNT,
1323
+ # EXEC_PRICE,
1324
+ # ORDER_TYPE,
1325
+ # ORDER_PRICE,
1326
+ # MAKER,
1327
+ # FEE,
1328
+ # FEE_CURRENCY,
1329
+ # ...
1330
+ # ]
1331
+ #
1332
+ tradeList = self.safe_list(trade, 'result', [])
1333
+ tradeLength = len(tradeList)
1334
+ isPrivate = (tradeLength > 5)
1335
+ id = self.safe_string(tradeList, 0)
1336
+ amountIndex = 4 if isPrivate else 2
1337
+ side = None
1338
+ amountString = self.safe_string(tradeList, amountIndex)
1339
+ priceIndex = 5 if isPrivate else 3
1340
+ priceString = self.safe_string(tradeList, priceIndex)
1341
+ if amountString[0] == '-':
1342
+ side = 'sell'
1343
+ amountString = Precise.string_abs(amountString)
1344
+ else:
1345
+ side = 'buy'
1346
+ orderId = None
1347
+ takerOrMaker = None
933
1348
  type = None
934
- side = self.safe_string_lower(trade, 'type')
935
- orderId = self.safe_string(trade, 'order_id')
936
- priceString = self.safe_string(trade, 'price')
937
- amountString = self.safe_string(trade, 'amount')
938
1349
  fee = None
939
- if 'fee_amount' in trade:
940
- feeCostString = Precise.string_neg(self.safe_string(trade, 'fee_amount'))
941
- feeCurrencyId = self.safe_string(trade, 'fee_currency')
942
- feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
1350
+ symbol = self.safe_symbol(None, market)
1351
+ timestampIndex = 2 if isPrivate else 1
1352
+ timestamp = self.safe_integer(tradeList, timestampIndex)
1353
+ if isPrivate:
1354
+ marketId = tradeList[1]
1355
+ symbol = self.safe_symbol(marketId)
1356
+ orderId = self.safe_string(tradeList, 3)
1357
+ maker = self.safe_integer(tradeList, 8)
1358
+ takerOrMaker = 'maker' if (maker == 1) else 'taker'
1359
+ feeCostString = self.safe_string(tradeList, 9)
1360
+ feeCostString = Precise.string_neg(feeCostString)
1361
+ feeCurrencyId = self.safe_string(tradeList, 10)
1362
+ feeCurrency = self.safe_currency_code(feeCurrencyId)
943
1363
  fee = {
944
1364
  'cost': feeCostString,
945
- 'currency': feeCurrencyCode,
1365
+ 'currency': feeCurrency,
946
1366
  }
1367
+ orderType = tradeList[6]
1368
+ type = self.safe_string(self.options['exchangeTypes'], orderType)
947
1369
  return self.safe_trade({
948
1370
  'id': id,
949
- 'info': trade,
950
1371
  'timestamp': timestamp,
951
1372
  'datetime': self.iso8601(timestamp),
952
- 'symbol': market['symbol'],
953
- 'type': type,
1373
+ 'symbol': symbol,
954
1374
  'order': orderId,
955
1375
  'side': side,
956
- 'takerOrMaker': None,
1376
+ 'type': type,
1377
+ 'takerOrMaker': takerOrMaker,
957
1378
  'price': priceString,
958
1379
  'amount': amountString,
959
1380
  'cost': None,
960
1381
  'fee': fee,
1382
+ 'info': tradeList,
961
1383
  }, market)
962
1384
 
963
- async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = 50, params={}) -> List[Trade]:
1385
+ async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
964
1386
  """
965
1387
  get the list of most recent trades for a particular symbol
966
- :see: https://docs.bitfinex.com/v1/reference/rest-public-trades
1388
+
1389
+ https://docs.bitfinex.com/reference/rest-public-trades
1390
+
967
1391
  :param str symbol: unified symbol of the market to fetch trades for
968
1392
  :param int [since]: timestamp in ms of the earliest trade to fetch
969
- :param int [limit]: the maximum amount of trades to fetch
1393
+ :param int [limit]: the maximum amount of trades to fetch, default 120, max 10000
970
1394
  :param dict [params]: extra parameters specific to the exchange API endpoint
1395
+ :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)
1396
+ :param int [params.until]: the latest time in ms to fetch entries for
971
1397
  :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
972
1398
  """
973
1399
  await self.load_markets()
1400
+ paginate = False
1401
+ paginate, params = self.handle_option_and_params(params, 'fetchTrades', 'paginate')
1402
+ if paginate:
1403
+ return await self.fetch_paginated_call_dynamic('fetchTrades', symbol, since, limit, params, 10000)
974
1404
  market = self.market(symbol)
975
- request = {
1405
+ sort = '-1'
1406
+ request: dict = {
976
1407
  'symbol': market['id'],
977
- 'limit_trades': limit,
978
1408
  }
979
1409
  if since is not None:
980
- request['timestamp'] = self.parse_to_int(since / 1000)
981
- response = await self.publicGetTradesSymbol(self.extend(request, params))
1410
+ request['start'] = since
1411
+ sort = '1'
1412
+ if limit is not None:
1413
+ request['limit'] = min(limit, 10000) # default 120, max 10000
1414
+ request['sort'] = sort
1415
+ request, params = self.handle_until_option('end', request, params)
1416
+ response = await self.publicGetTradesSymbolHist(self.extend(request, params))
982
1417
  #
983
- # [
984
- # {
985
- # "timestamp": "1694284565",
986
- # "tid": "1415415034",
987
- # "price": "25862.0",
988
- # "amount": "0.00020685",
989
- # "exchange": "bitfinex",
990
- # "type": "buy"
991
- # },
992
- # ]
1418
+ # [
1419
+ # [
1420
+ # ID,
1421
+ # MTS, # timestamp
1422
+ # AMOUNT,
1423
+ # PRICE
1424
+ # ]
1425
+ # ]
993
1426
  #
994
- return self.parse_trades(response, market, since, limit)
1427
+ trades = self.sort_by(response, 1)
1428
+ tradesList = []
1429
+ for i in range(0, len(trades)):
1430
+ tradesList.append({'result': trades[i]}) # convert to array of dicts to match parseOrder signature
1431
+ return self.parse_trades(tradesList, market, None, limit)
995
1432
 
996
- async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1433
+ async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = 100, params={}) -> List[list]:
997
1434
  """
998
- fetch all trades made by the user
999
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-past-trades
1000
- :param str symbol: unified market symbol
1001
- :param int [since]: the earliest time in ms to fetch trades for
1002
- :param int [limit]: the maximum number of trades structures to retrieve
1435
+ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1436
+
1437
+ https://docs.bitfinex.com/reference/rest-public-candles
1438
+
1439
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
1440
+ :param str timeframe: the length of time each candle represents
1441
+ :param int [since]: timestamp in ms of the earliest candle to fetch
1442
+ :param int [limit]: the maximum amount of candles to fetch, default 100 max 10000
1003
1443
  :param dict [params]: extra parameters specific to the exchange API endpoint
1004
- :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1444
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
1445
+ :param int [params.until]: timestamp in ms of the latest candle to fetch
1446
+ :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)
1005
1447
  """
1006
- if symbol is None:
1007
- raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
1008
1448
  await self.load_markets()
1449
+ paginate = False
1450
+ paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate')
1451
+ if paginate:
1452
+ return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 10000)
1009
1453
  market = self.market(symbol)
1010
- request = {
1454
+ if limit is None:
1455
+ limit = 10000
1456
+ else:
1457
+ limit = min(limit, 10000)
1458
+ request: dict = {
1011
1459
  'symbol': market['id'],
1460
+ 'timeframe': self.safe_string(self.timeframes, timeframe, timeframe),
1461
+ 'sort': 1,
1462
+ 'limit': limit,
1012
1463
  }
1013
- if limit is not None:
1014
- request['limit_trades'] = limit
1015
1464
  if since is not None:
1016
- request['timestamp'] = self.parse_to_int(since / 1000)
1017
- response = await self.privatePostMytrades(self.extend(request, params))
1018
- return self.parse_trades(response, market, since, limit)
1465
+ request['start'] = since
1466
+ request, params = self.handle_until_option('end', request, params)
1467
+ response = await self.publicGetCandlesTradeTimeframeSymbolHist(self.extend(request, params))
1468
+ #
1469
+ # [
1470
+ # [1591503840000,0.025069,0.025068,0.025069,0.025068,1.97828998],
1471
+ # [1591504500000,0.025065,0.025065,0.025065,0.025065,1.0164],
1472
+ # [1591504620000,0.025062,0.025062,0.025062,0.025062,0.5],
1473
+ # ]
1474
+ #
1475
+ return self.parse_ohlcvs(response, market, timeframe, since, limit)
1019
1476
 
1020
- async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1021
- """
1022
- create a trade order
1023
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-new-order
1024
- :param str symbol: unified symbol of the market to create an order in
1025
- :param str type: 'market' or 'limit'
1026
- :param str side: 'buy' or 'sell'
1027
- :param float amount: how much of currency you want to trade in units of base currency
1028
- :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1029
- :param dict [params]: extra parameters specific to the exchange API endpoint
1030
- :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1031
- """
1032
- await self.load_markets()
1033
- market = self.market(symbol)
1034
- postOnly = self.safe_bool(params, 'postOnly', False)
1035
- type = type.lower()
1036
- params = self.omit(params, ['postOnly'])
1037
- if market['spot']:
1038
- # although they claim that type needs to be 'exchange limit' or 'exchange market'
1039
- # in fact that's not the case for swap markets
1040
- type = self.safe_string_lower(self.options['orderTypes'], type, type)
1041
- request = {
1042
- 'symbol': market['id'],
1043
- 'side': side,
1044
- 'amount': self.amount_to_precision(symbol, amount),
1045
- 'type': type,
1046
- 'ocoorder': False,
1047
- 'buy_price_oco': 0,
1048
- 'sell_price_oco': 0,
1477
+ def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
1478
+ #
1479
+ # [
1480
+ # 1457539800000,
1481
+ # 0.02594,
1482
+ # 0.02594,
1483
+ # 0.02594,
1484
+ # 0.02594,
1485
+ # 0.1
1486
+ # ]
1487
+ #
1488
+ return [
1489
+ self.safe_integer(ohlcv, 0),
1490
+ self.safe_number(ohlcv, 1),
1491
+ self.safe_number(ohlcv, 3),
1492
+ self.safe_number(ohlcv, 4),
1493
+ self.safe_number(ohlcv, 2),
1494
+ self.safe_number(ohlcv, 5),
1495
+ ]
1496
+
1497
+ def parse_order_status(self, status: Str):
1498
+ if status is None:
1499
+ return status
1500
+ parts = status.split(' ')
1501
+ state = self.safe_string(parts, 0)
1502
+ statuses: dict = {
1503
+ 'ACTIVE': 'open',
1504
+ 'PARTIALLY': 'open',
1505
+ 'EXECUTED': 'closed',
1506
+ 'CANCELED': 'canceled',
1507
+ 'INSUFFICIENT': 'canceled',
1508
+ 'POSTONLY CANCELED': 'canceled',
1509
+ 'RSN_DUST': 'rejected',
1510
+ 'RSN_PAUSE': 'rejected',
1511
+ 'IOC CANCELED': 'canceled',
1512
+ 'FILLORKILL CANCELED': 'canceled',
1049
1513
  }
1050
- if type.find('market') > -1:
1051
- request['price'] = str(self.nonce())
1052
- else:
1053
- request['price'] = self.price_to_precision(symbol, price)
1054
- if postOnly:
1055
- request['is_postonly'] = True
1056
- response = await self.privatePostOrderNew(self.extend(request, params))
1057
- return self.parse_order(response, market)
1514
+ return self.safe_string(statuses, state, status)
1058
1515
 
1059
- async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
1060
- await self.load_markets()
1061
- order = {
1062
- 'order_id': int(id),
1516
+ def parse_order_flags(self, flags):
1517
+ # flags can be added to each other...
1518
+ flagValues: dict = {
1519
+ '1024': ['reduceOnly'],
1520
+ '4096': ['postOnly'],
1521
+ '5120': ['reduceOnly', 'postOnly'],
1522
+ # '64': 'hidden', # The hidden order option ensures an order does not appear in the order book
1523
+ # '512': 'close', # Close position if position present.
1524
+ # '16384': 'OCO', # The one cancels other order option allows you to place a pair of orders stipulating that if one order is executed fully or partially, then the other is automatically canceled.
1525
+ # '524288': 'No Var Rates' # Excludes variable rate funding offers from matching against self order, if on margin
1063
1526
  }
1064
- if price is not None:
1065
- order['price'] = self.price_to_precision(symbol, price)
1066
- if amount is not None:
1067
- order['amount'] = self.number_to_string(amount)
1068
- if symbol is not None:
1069
- order['symbol'] = self.market_id(symbol)
1070
- if side is not None:
1071
- order['side'] = side
1072
- if type is not None:
1073
- order['type'] = self.safe_string(self.options['orderTypes'], type, type)
1074
- response = await self.privatePostOrderCancelReplace(self.extend(order, params))
1075
- return self.parse_order(response)
1527
+ return self.safe_value(flagValues, flags, None)
1076
1528
 
1077
- async def cancel_order(self, id: str, symbol: Str = None, params={}):
1078
- """
1079
- cancels an open order
1080
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-cancel-order
1081
- :param str id: order id
1082
- :param str symbol: not used by bitfinex cancelOrder()
1083
- :param dict [params]: extra parameters specific to the exchange API endpoint
1084
- :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1085
- """
1086
- await self.load_markets()
1087
- request = {
1088
- 'order_id': int(id),
1529
+ def parse_time_in_force(self, orderType):
1530
+ orderTypes: dict = {
1531
+ 'EXCHANGE IOC': 'IOC',
1532
+ 'EXCHANGE FOK': 'FOK',
1533
+ 'IOC': 'IOC', # Margin
1534
+ 'FOK': 'FOK', # Margin
1089
1535
  }
1090
- return await self.privatePostOrderCancel(self.extend(request, params))
1536
+ return self.safe_string(orderTypes, orderType, 'GTC')
1091
1537
 
1092
- async def cancel_all_orders(self, symbol: Str = None, params={}):
1093
- """
1094
- cancel all open orders
1095
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-cancel-all-orders
1096
- :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
1097
- :param dict [params]: extra parameters specific to the exchange API endpoint
1098
- :returns dict: response from exchange
1099
- """
1100
- return await self.privatePostOrderCancelAll(params)
1101
-
1102
- def parse_order(self, order, market: Market = None) -> Order:
1103
- #
1104
- # {
1105
- # "id": 57334010955,
1106
- # "cid": 1611584840966,
1107
- # "cid_date": null,
1108
- # "gid": null,
1109
- # "symbol": "ltcbtc",
1110
- # "exchange": null,
1111
- # "price": "0.0042125",
1112
- # "avg_execution_price": "0.0042097",
1113
- # "side": "sell",
1114
- # "type": "exchange market",
1115
- # "timestamp": "1611584841.0",
1116
- # "is_live": False,
1117
- # "is_cancelled": False,
1118
- # "is_hidden": 0,
1119
- # "oco_order": 0,
1120
- # "was_forced": False,
1121
- # "original_amount": "0.205176",
1122
- # "remaining_amount": "0.0",
1123
- # "executed_amount": "0.205176",
1124
- # "src": "web"
1125
- # }
1126
- #
1127
- side = self.safe_string(order, 'side')
1128
- open = self.safe_value(order, 'is_live')
1129
- canceled = self.safe_value(order, 'is_cancelled')
1538
+ def parse_order(self, order: dict, market: Market = None) -> Order:
1539
+ orderList = self.safe_list(order, 'result')
1540
+ id = self.safe_string(orderList, 0)
1541
+ marketId = self.safe_string(orderList, 3)
1542
+ symbol = self.safe_symbol(marketId)
1543
+ # https://github.com/ccxt/ccxt/issues/6686
1544
+ # timestamp = self.safe_timestamp(orderObject, 5)
1545
+ timestamp = self.safe_integer(orderList, 5)
1546
+ remaining = Precise.string_abs(self.safe_string(orderList, 6))
1547
+ signedAmount = self.safe_string(orderList, 7)
1548
+ amount = Precise.string_abs(signedAmount)
1549
+ side = 'sell' if Precise.string_lt(signedAmount, '0') else 'buy'
1550
+ orderType = self.safe_string(orderList, 8)
1551
+ type = self.safe_string(self.safe_value(self.options, 'exchangeTypes'), orderType)
1552
+ timeInForce = self.parse_time_in_force(orderType)
1553
+ rawFlags = self.safe_string(orderList, 12)
1554
+ flags = self.parse_order_flags(rawFlags)
1555
+ postOnly = False
1556
+ if flags is not None:
1557
+ for i in range(0, len(flags)):
1558
+ if flags[i] == 'postOnly':
1559
+ postOnly = True
1560
+ price = self.safe_string(orderList, 16)
1561
+ triggerPrice = None
1562
+ if (orderType == 'EXCHANGE STOP') or (orderType == 'EXCHANGE STOP LIMIT'):
1563
+ price = None
1564
+ triggerPrice = self.safe_string(orderList, 16)
1565
+ if orderType == 'EXCHANGE STOP LIMIT':
1566
+ price = self.safe_string(orderList, 19)
1130
1567
  status = None
1131
- if open:
1132
- status = 'open'
1133
- elif canceled:
1134
- status = 'canceled'
1135
- else:
1136
- status = 'closed'
1137
- marketId = self.safe_string_upper(order, 'symbol')
1138
- symbol = self.safe_symbol(marketId, market)
1139
- orderType = self.safe_string(order, 'type', '')
1140
- exchange = orderType.find('exchange ') >= 0
1141
- if exchange:
1142
- parts = order['type'].split(' ')
1143
- orderType = parts[1]
1144
- timestamp = self.safe_timestamp(order, 'timestamp')
1145
- id = self.safe_string(order, 'id')
1568
+ statusString = self.safe_string(orderList, 13)
1569
+ if statusString is not None:
1570
+ parts = statusString.split(' @ ')
1571
+ status = self.parse_order_status(self.safe_string(parts, 0))
1572
+ average = self.safe_string(orderList, 17)
1573
+ clientOrderId = self.safe_string(orderList, 2)
1146
1574
  return self.safe_order({
1147
- 'info': order,
1575
+ 'info': orderList,
1148
1576
  'id': id,
1149
- 'clientOrderId': None,
1577
+ 'clientOrderId': clientOrderId,
1150
1578
  'timestamp': timestamp,
1151
1579
  'datetime': self.iso8601(timestamp),
1152
1580
  'lastTradeTimestamp': None,
1153
1581
  'symbol': symbol,
1154
- 'type': orderType,
1155
- 'timeInForce': None,
1156
- 'postOnly': None,
1582
+ 'type': type,
1583
+ 'timeInForce': timeInForce,
1584
+ 'postOnly': postOnly,
1157
1585
  'side': side,
1158
- 'price': self.safe_string(order, 'price'),
1159
- 'stopPrice': None,
1160
- 'triggerPrice': None,
1161
- 'average': self.safe_string(order, 'avg_execution_price'),
1162
- 'amount': self.safe_string(order, 'original_amount'),
1163
- 'remaining': self.safe_string(order, 'remaining_amount'),
1164
- 'filled': self.safe_string(order, 'executed_amount'),
1586
+ 'price': price,
1587
+ 'triggerPrice': triggerPrice,
1588
+ 'amount': amount,
1589
+ 'cost': None,
1590
+ 'average': average,
1591
+ 'filled': None,
1592
+ 'remaining': remaining,
1165
1593
  'status': status,
1166
1594
  'fee': None,
1167
- 'cost': None,
1168
1595
  'trades': None,
1169
1596
  }, market)
1170
1597
 
1171
- async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1598
+ def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1172
1599
  """
1173
- fetch all unfilled currently open orders
1174
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-active-orders
1175
- :param str symbol: unified market symbol
1176
- :param int [since]: the earliest time in ms to fetch open orders for
1177
- :param int [limit]: the maximum number of open orders structures to retrieve
1600
+ @ignore
1601
+ helper function to build an order request
1602
+ :param str symbol: unified symbol of the market to create an order in
1603
+ :param str type: 'market' or 'limit'
1604
+ :param str side: 'buy' or 'sell'
1605
+ :param float amount: how much you want to trade in units of the base currency
1606
+ :param float [price]: the price of the order, in units of the quote currency, ignored in market orders
1178
1607
  :param dict [params]: extra parameters specific to the exchange API endpoint
1179
- :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1608
+ :param float [params.triggerPrice]: The price at which a trigger order is triggered at
1609
+ :param str [params.timeInForce]: "GTC", "IOC", "FOK", or "PO"
1610
+ :param bool [params.postOnly]:
1611
+ :param bool [params.reduceOnly]: Ensures that the executed order does not flip the opened position.
1612
+ :param int [params.flags]: additional order parameters: 4096(Post Only), 1024(Reduce Only), 16384(OCO), 64(Hidden), 512(Close), 524288(No Var Rates)
1613
+ :param int [params.lev]: leverage for a derivative order, supported by derivative symbol orders only. The value should be between 1 and 100 inclusive.
1614
+ :param str [params.price_traling]: The trailing price for a trailing stop order
1615
+ :param str [params.price_aux_limit]: Order price for stop limit orders
1616
+ :param str [params.price_oco_stop]: OCO stop price
1617
+ :returns dict: an `order structure <https://github.com/ccxt/ccxt/wiki/Manual#order-structure>`
1180
1618
  """
1181
- await self.load_markets()
1182
- if symbol is not None:
1183
- if not (symbol in self.markets):
1184
- raise ExchangeError(self.id + ' has no symbol ' + symbol)
1185
- response = await self.privatePostOrders(params)
1186
- orders = self.parse_orders(response, None, since, limit)
1187
- if symbol is not None:
1188
- orders = self.filter_by(orders, 'symbol', symbol)
1189
- return orders
1619
+ market = self.market(symbol)
1620
+ amountString = self.amount_to_precision(symbol, amount)
1621
+ amountString = amountString if (side == 'buy') else Precise.string_neg(amountString)
1622
+ request: dict = {
1623
+ 'symbol': market['id'],
1624
+ 'amount': amountString,
1625
+ }
1626
+ triggerPrice = self.safe_string_2(params, 'stopPrice', 'triggerPrice')
1627
+ trailingAmount = self.safe_string(params, 'trailingAmount')
1628
+ timeInForce = self.safe_string(params, 'timeInForce')
1629
+ postOnlyParam = self.safe_bool(params, 'postOnly', False)
1630
+ reduceOnly = self.safe_bool(params, 'reduceOnly', False)
1631
+ clientOrderId = self.safe_value_2(params, 'cid', 'clientOrderId')
1632
+ orderType = type.upper()
1633
+ if trailingAmount is not None:
1634
+ orderType = 'TRAILING STOP'
1635
+ request['price_trailing'] = trailingAmount
1636
+ elif triggerPrice is not None:
1637
+ # request['price'] is taken for stop orders
1638
+ request['price'] = self.price_to_precision(symbol, triggerPrice)
1639
+ if type == 'limit':
1640
+ orderType = 'STOP LIMIT'
1641
+ request['price_aux_limit'] = self.price_to_precision(symbol, price)
1642
+ else:
1643
+ orderType = 'STOP'
1644
+ ioc = (timeInForce == 'IOC')
1645
+ fok = (timeInForce == 'FOK')
1646
+ postOnly = (postOnlyParam or (timeInForce == 'PO'))
1647
+ if (ioc or fok) and (price is None):
1648
+ raise InvalidOrder(self.id + ' createOrder() requires a price argument with IOC and FOK orders')
1649
+ if (ioc or fok) and (type == 'market'):
1650
+ raise InvalidOrder(self.id + ' createOrder() does not allow market IOC and FOK orders')
1651
+ if (type != 'market') and (triggerPrice is None):
1652
+ request['price'] = self.price_to_precision(symbol, price)
1653
+ if ioc:
1654
+ orderType = 'IOC'
1655
+ elif fok:
1656
+ orderType = 'FOK'
1657
+ marginMode = None
1658
+ marginMode, params = self.handle_margin_mode_and_params('createOrder', params)
1659
+ if market['spot'] and (marginMode is None):
1660
+ # The EXCHANGE prefix is only required for non margin spot markets
1661
+ orderType = 'EXCHANGE ' + orderType
1662
+ request['type'] = orderType
1663
+ # flag values may be summed to combine flags
1664
+ flags = 0
1665
+ if postOnly:
1666
+ flags = self.sum(flags, 4096)
1667
+ if reduceOnly:
1668
+ flags = self.sum(flags, 1024)
1669
+ if flags != 0:
1670
+ request['flags'] = flags
1671
+ if clientOrderId is not None:
1672
+ request['cid'] = clientOrderId
1673
+ params = self.omit(params, ['triggerPrice', 'stopPrice', 'timeInForce', 'postOnly', 'reduceOnly', 'trailingAmount', 'clientOrderId'])
1674
+ return self.extend(request, params)
1190
1675
 
1191
- async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1192
- """
1193
- fetches information on multiple closed orders made by the user
1194
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-orders-history
1195
- :param str symbol: unified market symbol of the market orders were made in
1196
- :param int [since]: the earliest time in ms to fetch orders for
1197
- :param int [limit]: the maximum number of order structures to retrieve
1198
- :param dict [params]: extra parameters specific to the exchange API endpoint
1199
- :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1676
+ async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1200
1677
  """
1201
- await self.load_markets()
1202
- symbol = self.symbol(symbol)
1203
- request = {}
1204
- if limit is not None:
1205
- request['limit'] = limit
1206
- response = await self.privatePostOrdersHist(self.extend(request, params))
1207
- orders = self.parse_orders(response, None, since, limit)
1208
- if symbol is not None:
1209
- orders = self.filter_by(orders, 'symbol', symbol)
1210
- orders = self.filter_by_array(orders, 'status', ['closed', 'canceled'], False)
1211
- return orders
1678
+ create an order on the exchange
1212
1679
 
1213
- async def fetch_order(self, id: str, symbol: Str = None, params={}):
1214
- """
1215
- fetches information on an order made by the user
1216
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-order-status
1217
- :param str symbol: not used by bitfinex fetchOrder
1680
+ https://docs.bitfinex.com/reference/rest-auth-submit-order
1681
+
1682
+ :param str symbol: unified CCXT market symbol
1683
+ :param str type: 'limit' or 'market'
1684
+ :param str side: 'buy' or 'sell'
1685
+ :param float amount: the amount of currency to trade
1686
+ :param float [price]: price of the order
1218
1687
  :param dict [params]: extra parameters specific to the exchange API endpoint
1219
- :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1688
+ :param float [params.triggerPrice]: the price that triggers a trigger order
1689
+ :param str [params.timeInForce]: "GTC", "IOC", "FOK", or "PO"
1690
+ :param boolean [params.postOnly]: set to True if you want to make a post only order
1691
+ :param boolean [params.reduceOnly]: indicates that the order is to reduce the size of a position
1692
+ :param int [params.flags]: additional order parameters: 4096(Post Only), 1024(Reduce Only), 16384(OCO), 64(Hidden), 512(Close), 524288(No Var Rates)
1693
+ :param int [params.lev]: leverage for a derivative order, supported by derivative symbol orders only. The value should be between 1 and 100 inclusive.
1694
+ :param str [params.price_aux_limit]: order price for stop limit orders
1695
+ :param str [params.price_oco_stop]: OCO stop price
1696
+ :param str [params.trailingAmount]: *swap only* the quote amount to trail away from the current market price
1697
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1220
1698
  """
1221
1699
  await self.load_markets()
1222
- request = {
1223
- 'order_id': int(id),
1224
- }
1225
- response = await self.privatePostOrderStatus(self.extend(request, params))
1226
- return self.parse_order(response)
1227
-
1228
- def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
1700
+ market = self.market(symbol)
1701
+ request = self.create_order_request(symbol, type, side, amount, price, params)
1702
+ response = await self.privatePostAuthWOrderSubmit(request)
1229
1703
  #
1230
- # [
1231
- # 1457539800000,
1232
- # 0.02594,
1233
- # 0.02594,
1234
- # 0.02594,
1235
- # 0.02594,
1236
- # 0.1
1237
- # ]
1704
+ # [
1705
+ # 1653325121, # Timestamp in milliseconds
1706
+ # "on-req", # Purpose of notification('on-req', 'oc-req', "uca", 'fon-req', "foc-req")
1707
+ # null, # unique ID of the message
1708
+ # null,
1709
+ # [
1710
+ # [
1711
+ # 95412102131, # Order ID
1712
+ # null, # Group ID
1713
+ # 1653325121798, # Client Order ID
1714
+ # "tDOGE:UST", # Market ID
1715
+ # 1653325121798, # Millisecond timestamp of creation
1716
+ # 1653325121798, # Millisecond timestamp of update
1717
+ # -10, # Amount(Positive means buy, negative means sell)
1718
+ # -10, # Original amount
1719
+ # "EXCHANGE LIMIT", # Type of the order: LIMIT, EXCHANGE LIMIT, MARKET, EXCHANGE MARKET, STOP, EXCHANGE STOP, STOP LIMIT, EXCHANGE STOP LIMIT, TRAILING STOP, EXCHANGE TRAILING STOP, FOK, EXCHANGE FOK, IOC, EXCHANGE IOC.
1720
+ # null, # Previous order type(stop-limit orders are converted to limit orders so for them previous type is always STOP)
1721
+ # null, # Millisecond timestamp of Time-In-Force: automatic order cancellation
1722
+ # null, # _PLACEHOLDER
1723
+ # 4096, # Flags, see parseOrderFlags()
1724
+ # "ACTIVE", # Order Status, see parseOrderStatus()
1725
+ # null, # _PLACEHOLDER
1726
+ # null, # _PLACEHOLDER
1727
+ # 0.071, # Price(Stop Price for stop-limit orders, Limit Price for limit orders)
1728
+ # 0, # Average Price
1729
+ # 0, # Trailing Price
1730
+ # 0, # Auxiliary Limit price(for STOP LIMIT)
1731
+ # null, # _PLACEHOLDER
1732
+ # null, # _PLACEHOLDER
1733
+ # null, # _PLACEHOLDER
1734
+ # 0, # Hidden(0 if False, 1 if True)
1735
+ # 0, # Placed ID(If another order caused self order to be placed(OCO) self will be that other order's ID)
1736
+ # null, # _PLACEHOLDER
1737
+ # null, # _PLACEHOLDER
1738
+ # null, # _PLACEHOLDER
1739
+ # "API>BFX", # Routing, indicates origin of action: BFX, ETHFX, API>BFX, API>ETHFX
1740
+ # null, # _PLACEHOLDER
1741
+ # null, # _PLACEHOLDER
1742
+ # {"$F7":1} # additional meta information about the order( $F7 = IS_POST_ONLY(0 if False, 1 if True), $F33 = Leverage(int))
1743
+ # ]
1744
+ # ],
1745
+ # null, # CODE(work in progress)
1746
+ # "SUCCESS", # Status of the request
1747
+ # "Submitting 1 orders." # Message
1748
+ # ]
1238
1749
  #
1239
- return [
1240
- self.safe_integer(ohlcv, 0),
1241
- self.safe_number(ohlcv, 1),
1242
- self.safe_number(ohlcv, 3),
1243
- self.safe_number(ohlcv, 4),
1244
- self.safe_number(ohlcv, 2),
1245
- self.safe_number(ohlcv, 5),
1246
- ]
1750
+ status = self.safe_string(response, 6)
1751
+ if status != 'SUCCESS':
1752
+ errorCode = response[5]
1753
+ errorText = response[7]
1754
+ raise ExchangeError(self.id + ' ' + response[6] + ': ' + errorText + '(#' + errorCode + ')')
1755
+ orders = self.safe_list(response, 4, [])
1756
+ order = self.safe_list(orders, 0)
1757
+ newOrder = {'result': order}
1758
+ return self.parse_order(newOrder, market)
1247
1759
 
1248
- async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
1760
+ async def create_orders(self, orders: List[OrderRequest], params={}):
1249
1761
  """
1250
- fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1251
- :see: https://docs.bitfinex.com/reference/rest-public-candles#aggregate-funding-currency-candles
1252
- :param str symbol: unified symbol of the market to fetch OHLCV data for
1253
- :param str timeframe: the length of time each candle represents
1254
- :param int [since]: timestamp in ms of the earliest candle to fetch
1255
- :param int [limit]: the maximum amount of candles to fetch
1762
+ create a list of trade orders
1763
+
1764
+ https://docs.bitfinex.com/reference/rest-auth-order-multi
1765
+
1766
+ :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
1256
1767
  :param dict [params]: extra parameters specific to the exchange API endpoint
1257
- :returns int[][]: A list of candles ordered, open, high, low, close, volume
1768
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1258
1769
  """
1259
1770
  await self.load_markets()
1260
- if limit is None:
1261
- limit = 100
1262
- market = self.market(symbol)
1263
- v2id = 't' + market['id']
1264
- request = {
1265
- 'symbol': v2id,
1266
- 'timeframe': self.safe_string(self.timeframes, timeframe, timeframe),
1267
- 'sort': 1,
1268
- 'limit': limit,
1771
+ ordersRequests = []
1772
+ for i in range(0, len(orders)):
1773
+ rawOrder = orders[i]
1774
+ symbol = self.safe_string(rawOrder, 'symbol')
1775
+ type = self.safe_string(rawOrder, 'type')
1776
+ side = self.safe_string(rawOrder, 'side')
1777
+ amount = self.safe_number(rawOrder, 'amount')
1778
+ price = self.safe_number(rawOrder, 'price')
1779
+ orderParams = self.safe_dict(rawOrder, 'params', {})
1780
+ orderRequest = self.create_order_request(symbol, type, side, amount, price, orderParams)
1781
+ ordersRequests.append(['on', orderRequest])
1782
+ request: dict = {
1783
+ 'ops': ordersRequests,
1269
1784
  }
1270
- if since is not None:
1271
- request['start'] = since
1272
- response = await self.v2GetCandlesTradeTimeframeSymbolHist(self.extend(request, params))
1785
+ response = await self.privatePostAuthWOrderMulti(request)
1273
1786
  #
1274
1787
  # [
1275
- # [1457539800000,0.02594,0.02594,0.02594,0.02594,0.1],
1276
- # [1457547300000,0.02577,0.02577,0.02577,0.02577,0.01],
1277
- # [1457550240000,0.0255,0.0253,0.0255,0.0252,3.2640000000000002],
1788
+ # 1706762515553,
1789
+ # "ox_multi-req",
1790
+ # null,
1791
+ # null,
1792
+ # [
1793
+ # [
1794
+ # 1706762515,
1795
+ # "on-req",
1796
+ # null,
1797
+ # null,
1798
+ # [
1799
+ # [139567428547,null,1706762515551,"tBTCUST",1706762515551,1706762515551,0.0001,0.0001,"EXCHANGE LIMIT",null,null,null,0,"ACTIVE",null,null,35000,0,0,0,null,null,null,0,0,null,null,null,"API>BFX",null,null,{}]
1800
+ # ],
1801
+ # null,
1802
+ # "SUCCESS",
1803
+ # "Submitting 1 orders."
1804
+ # ],
1805
+ # ],
1806
+ # null,
1807
+ # "SUCCESS",
1808
+ # "Submitting 2 order operations."
1278
1809
  # ]
1279
1810
  #
1280
- return self.parse_ohlcvs(response, market, timeframe, since, limit)
1281
-
1282
- def get_currency_name(self, code):
1283
- # todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
1284
- if code in self.options['currencyNames']:
1285
- return self.options['currencyNames'][code]
1286
- raise NotSupported(self.id + ' ' + code + ' not supported for withdrawal')
1811
+ results = []
1812
+ data = self.safe_list(response, 4, [])
1813
+ for i in range(0, len(data)):
1814
+ entry = data[i]
1815
+ individualOrder = entry[4]
1816
+ results.append({'result': individualOrder[0]})
1817
+ return self.parse_orders(results)
1287
1818
 
1288
- async def create_deposit_address(self, code: str, params={}):
1819
+ async def cancel_all_orders(self, symbol: Str = None, params={}):
1289
1820
  """
1290
- create a currency deposit address
1291
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-deposit
1292
- :param str code: unified currency code of the currency for the deposit address
1821
+ cancel all open orders
1822
+
1823
+ https://docs.bitfinex.com/reference/rest-auth-cancel-orders-multiple
1824
+
1825
+ :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
1293
1826
  :param dict [params]: extra parameters specific to the exchange API endpoint
1294
- :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
1827
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1295
1828
  """
1296
1829
  await self.load_markets()
1297
- request = {
1298
- 'renew': 1,
1830
+ request: dict = {
1831
+ 'all': 1,
1299
1832
  }
1300
- return await self.fetch_deposit_address(code, self.extend(request, params))
1833
+ response = await self.privatePostAuthWOrderCancelMulti(self.extend(request, params))
1834
+ orders = self.safe_list(response, 4, [])
1835
+ ordersList = []
1836
+ for i in range(0, len(orders)):
1837
+ ordersList.append({'result': orders[i]})
1838
+ return self.parse_orders(ordersList)
1301
1839
 
1302
- async def fetch_deposit_address(self, code: str, params={}):
1840
+ async def cancel_order(self, id: str, symbol: Str = None, params={}):
1303
1841
  """
1304
- fetch the deposit address for a currency associated with self account
1305
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-deposit
1306
- :param str code: unified currency code
1842
+ cancels an open order
1843
+
1844
+ https://docs.bitfinex.com/reference/rest-auth-cancel-order
1845
+
1846
+ :param str id: order id
1847
+ :param str symbol: Not used by bitfinex cancelOrder()
1307
1848
  :param dict [params]: extra parameters specific to the exchange API endpoint
1308
- :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
1849
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1309
1850
  """
1310
1851
  await self.load_markets()
1311
- # todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
1312
- name = self.get_currency_name(code)
1313
- request = {
1314
- 'method': name,
1315
- 'wallet_name': 'exchange',
1316
- 'renew': 0, # a value of 1 will generate a new address
1317
- }
1318
- response = await self.privatePostDepositNew(self.extend(request, params))
1319
- address = self.safe_value(response, 'address')
1320
- tag = None
1321
- if 'address_pool' in response:
1322
- tag = address
1323
- address = response['address_pool']
1324
- self.check_address(address)
1325
- return {
1326
- 'currency': code,
1327
- 'address': address,
1328
- 'tag': tag,
1329
- 'network': None,
1330
- 'info': response,
1331
- }
1852
+ cid = self.safe_value_2(params, 'cid', 'clientOrderId') # client order id
1853
+ request = None
1854
+ market = None
1855
+ if symbol is not None:
1856
+ market = self.market(symbol)
1857
+ if cid is not None:
1858
+ cidDate = self.safe_value(params, 'cidDate') # client order id date
1859
+ if cidDate is None:
1860
+ raise InvalidOrder(self.id + " canceling an order by clientOrderId('cid') requires both 'cid' and 'cid_date'('YYYY-MM-DD')")
1861
+ request = {
1862
+ 'cid': cid,
1863
+ 'cid_date': cidDate,
1864
+ }
1865
+ params = self.omit(params, ['cid', 'clientOrderId'])
1866
+ else:
1867
+ request = {
1868
+ 'id': int(id),
1869
+ }
1870
+ response = await self.privatePostAuthWOrderCancel(self.extend(request, params))
1871
+ order = self.safe_value(response, 4)
1872
+ newOrder = {'result': order}
1873
+ return self.parse_order(newOrder, market)
1332
1874
 
1333
- async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1875
+ async def cancel_orders(self, ids, symbol: Str = None, params={}):
1334
1876
  """
1335
- fetch history of deposits and withdrawals
1336
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-deposit-withdrawal-history
1337
- :param str code: unified currency code for the currency of the deposit/withdrawals
1338
- :param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
1339
- :param int [limit]: max number of deposit/withdrawals to return, default is None
1877
+ cancel multiple orders at the same time
1878
+
1879
+ https://docs.bitfinex.com/reference/rest-auth-cancel-orders-multiple
1880
+
1881
+ :param str[] ids: order ids
1882
+ :param str symbol: unified market symbol, default is None
1340
1883
  :param dict [params]: extra parameters specific to the exchange API endpoint
1341
- :returns dict: a list of `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1884
+ :returns dict: an array of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1342
1885
  """
1343
1886
  await self.load_markets()
1344
- currencyId = self.safe_string(params, 'currency')
1345
- query = self.omit(params, 'currency')
1346
- currency = None
1347
- if currencyId is None:
1348
- if code is None:
1349
- raise ArgumentsRequired(self.id + ' fetchDepositsWithdrawals() requires a currency `code` argument or a `currency` parameter')
1350
- else:
1351
- currency = self.currency(code)
1352
- currencyId = currency['id']
1353
- query['currency'] = currencyId
1354
- if since is not None:
1355
- query['since'] = self.parse_to_int(since / 1000)
1356
- response = await self.privatePostHistoryMovements(self.extend(query, params))
1887
+ for i in range(0, len(ids)):
1888
+ ids[i] = self.parse_to_numeric(ids[i])
1889
+ request: dict = {
1890
+ 'id': ids,
1891
+ }
1892
+ market = None
1893
+ if symbol is not None:
1894
+ market = self.market(symbol)
1895
+ response = await self.privatePostAuthWOrderCancelMulti(self.extend(request, params))
1357
1896
  #
1358
1897
  # [
1359
- # {
1360
- # "id": 581183,
1361
- # "txid": 123456,
1362
- # "currency": "BTC",
1363
- # "method": "BITCOIN",
1364
- # "type": "WITHDRAWAL",
1365
- # "amount": ".01",
1366
- # "description": "3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ, offchain transfer ",
1367
- # "address": "3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ",
1368
- # "status": "COMPLETED",
1369
- # "timestamp": "1443833327.0",
1370
- # "timestamp_created": "1443833327.1",
1371
- # "fee": 0.1,
1372
- # }
1898
+ # 1706740198811,
1899
+ # "oc_multi-req",
1900
+ # null,
1901
+ # null,
1902
+ # [
1903
+ # [
1904
+ # 139530205057,
1905
+ # null,
1906
+ # 1706740132275,
1907
+ # "tBTCF0:USTF0",
1908
+ # 1706740132276,
1909
+ # 1706740132276,
1910
+ # 0.0001,
1911
+ # 0.0001,
1912
+ # "LIMIT",
1913
+ # null,
1914
+ # null,
1915
+ # null,
1916
+ # 0,
1917
+ # "ACTIVE",
1918
+ # null,
1919
+ # null,
1920
+ # 39000,
1921
+ # 0,
1922
+ # 0,
1923
+ # 0,
1924
+ # null,
1925
+ # null,
1926
+ # null,
1927
+ # 0,
1928
+ # 0,
1929
+ # null,
1930
+ # null,
1931
+ # null,
1932
+ # "API>BFX",
1933
+ # null,
1934
+ # null,
1935
+ # {
1936
+ # "lev": 10,
1937
+ # "$F33": 10
1938
+ # }
1939
+ # ],
1940
+ # ],
1941
+ # null,
1942
+ # "SUCCESS",
1943
+ # "Submitting 2 order cancellations."
1373
1944
  # ]
1374
1945
  #
1375
- return self.parse_transactions(response, currency, since, limit)
1946
+ orders = self.safe_list(response, 4, [])
1947
+ ordersList = []
1948
+ for i in range(0, len(orders)):
1949
+ ordersList.append({'result': orders[i]})
1950
+ return self.parse_orders(ordersList, market)
1951
+
1952
+ async def fetch_open_order(self, id: str, symbol: Str = None, params={}):
1953
+ """
1954
+ fetch an open order by it's id
1955
+
1956
+ https://docs.bitfinex.com/reference/rest-auth-retrieve-orders
1957
+ https://docs.bitfinex.com/reference/rest-auth-retrieve-orders-by-symbol
1958
+
1959
+ :param str id: order id
1960
+ :param str symbol: unified market symbol, default is None
1961
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1962
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1963
+ """
1964
+ request: dict = {
1965
+ 'id': [int(id)],
1966
+ }
1967
+ orders = await self.fetch_open_orders(symbol, None, None, self.extend(request, params))
1968
+ order = self.safe_value(orders, 0)
1969
+ if order is None:
1970
+ raise OrderNotFound(self.id + ' order ' + id + ' not found')
1971
+ return order
1972
+
1973
+ async def fetch_closed_order(self, id: str, symbol: Str = None, params={}):
1974
+ """
1975
+ fetch an open order by it's id
1976
+
1977
+ https://docs.bitfinex.com/reference/rest-auth-retrieve-orders
1978
+ https://docs.bitfinex.com/reference/rest-auth-retrieve-orders-by-symbol
1979
+
1980
+ :param str id: order id
1981
+ :param str symbol: unified market symbol, default is None
1982
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1983
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1984
+ """
1985
+ request: dict = {
1986
+ 'id': [int(id)],
1987
+ }
1988
+ orders = await self.fetch_closed_orders(symbol, None, None, self.extend(request, params))
1989
+ order = self.safe_value(orders, 0)
1990
+ if order is None:
1991
+ raise OrderNotFound(self.id + ' order ' + id + ' not found')
1992
+ return order
1993
+
1994
+ async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1995
+ """
1996
+ fetch all unfilled currently open orders
1997
+
1998
+ https://docs.bitfinex.com/reference/rest-auth-retrieve-orders
1999
+ https://docs.bitfinex.com/reference/rest-auth-retrieve-orders-by-symbol
2000
+
2001
+ :param str symbol: unified market symbol
2002
+ :param int [since]: the earliest time in ms to fetch open orders for
2003
+ :param int [limit]: the maximum number of open orders structures to retrieve
2004
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2005
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2006
+ """
2007
+ await self.load_markets()
2008
+ request: dict = {}
2009
+ market = None
2010
+ response = None
2011
+ if symbol is None:
2012
+ response = await self.privatePostAuthROrders(self.extend(request, params))
2013
+ else:
2014
+ market = self.market(symbol)
2015
+ request['symbol'] = market['id']
2016
+ response = await self.privatePostAuthROrdersSymbol(self.extend(request, params))
2017
+ #
2018
+ # [
2019
+ # [
2020
+ # 95408916206, # Order ID
2021
+ # null, # Group Order ID
2022
+ # 1653322349926, # Client Order ID
2023
+ # "tDOGE:UST", # Market ID
2024
+ # 1653322349926, # Created Timestamp in milliseconds
2025
+ # 1653322349927, # Updated Timestamp in milliseconds
2026
+ # -10, # Amount remaining(Positive means buy, negative means sell)
2027
+ # -10, # Original amount
2028
+ # "EXCHANGE LIMIT", # Order type
2029
+ # null, # Previous Order Type
2030
+ # null, # _PLACEHOLDER
2031
+ # null, # _PLACEHOLDER
2032
+ # 0, # Flags, see parseOrderFlags()
2033
+ # "ACTIVE", # Order Status, see parseOrderStatus()
2034
+ # null, # _PLACEHOLDER
2035
+ # null, # _PLACEHOLDER
2036
+ # 0.11, # Price
2037
+ # 0, # Average Price
2038
+ # 0, # Trailing Price
2039
+ # 0, # Auxiliary Limit price(for STOP LIMIT)
2040
+ # null, # _PLACEHOLDER
2041
+ # null, # _PLACEHOLDER
2042
+ # null, # _PLACEHOLDER
2043
+ # 0, # Hidden(0 if False, 1 if True)
2044
+ # 0, # Placed ID(If another order caused self order to be placed(OCO) self will be that other order's ID)
2045
+ # null, # _PLACEHOLDER
2046
+ # null, # _PLACEHOLDER
2047
+ # null, # _PLACEHOLDER
2048
+ # "API>BFX", # Routing, indicates origin of action: BFX, ETHFX, API>BFX, API>ETHFX
2049
+ # null, # _PLACEHOLDER
2050
+ # null, # _PLACEHOLDER
2051
+ # {"$F7":1} # additional meta information about the order( $F7 = IS_POST_ONLY(0 if False, 1 if True), $F33 = Leverage(int))
2052
+ # ],
2053
+ # ]
2054
+ #
2055
+ ordersList = []
2056
+ for i in range(0, len(response)):
2057
+ ordersList.append({'result': response[i]})
2058
+ return self.parse_orders(ordersList, market, since, limit)
2059
+
2060
+ async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2061
+ """
2062
+ fetches information on multiple closed orders made by the user
2063
+
2064
+ https://docs.bitfinex.com/reference/rest-auth-retrieve-orders
2065
+ https://docs.bitfinex.com/reference/rest-auth-retrieve-orders-by-symbol
2066
+
2067
+ :param str symbol: unified market symbol of the market orders were made in
2068
+ :param int [since]: the earliest time in ms to fetch orders for
2069
+ :param int [limit]: the maximum number of order structures to retrieve
2070
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2071
+ :param int [params.until]: the latest time in ms to fetch entries for
2072
+ :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)
2073
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2074
+ """
2075
+ # returns the most recent closed or canceled orders up to circa two weeks ago
2076
+ await self.load_markets()
2077
+ paginate = False
2078
+ paginate, params = self.handle_option_and_params(params, 'fetchClosedOrders', 'paginate')
2079
+ if paginate:
2080
+ return await self.fetch_paginated_call_dynamic('fetchClosedOrders', symbol, since, limit, params)
2081
+ request: dict = {}
2082
+ if since is not None:
2083
+ request['start'] = since
2084
+ if limit is not None:
2085
+ request['limit'] = limit # default 25, max 2500
2086
+ request, params = self.handle_until_option('end', request, params)
2087
+ market = None
2088
+ response = None
2089
+ if symbol is None:
2090
+ response = await self.privatePostAuthROrdersHist(self.extend(request, params))
2091
+ else:
2092
+ market = self.market(symbol)
2093
+ request['symbol'] = market['id']
2094
+ response = await self.privatePostAuthROrdersSymbolHist(self.extend(request, params))
2095
+ #
2096
+ # [
2097
+ # [
2098
+ # 95412102131, # Order ID
2099
+ # null, # Group Order ID
2100
+ # 1653325121798, # Client Order ID
2101
+ # "tDOGE:UST", # Market ID
2102
+ # 1653325122000, # Created Timestamp in milliseconds
2103
+ # 1653325122000, # Updated Timestamp in milliseconds
2104
+ # -10, # Amount remaining(Positive means buy, negative means sell)
2105
+ # -10, # Original amount
2106
+ # "EXCHANGE LIMIT", # Order type
2107
+ # null, # Previous Order Type
2108
+ # null, # Millisecond timestamp of Time-In-Force: automatic order cancellation
2109
+ # null, # _PLACEHOLDER
2110
+ # "4096", # Flags, see parseOrderFlags()
2111
+ # "POSTONLY CANCELED", # Order Status, see parseOrderStatus()
2112
+ # null, # _PLACEHOLDER
2113
+ # null, # _PLACEHOLDER
2114
+ # 0.071, # Price
2115
+ # 0, # Average Price
2116
+ # 0, # Trailing Price
2117
+ # 0, # Auxiliary Limit price(for STOP LIMIT)
2118
+ # null, # _PLACEHOLDER
2119
+ # null, # _PLACEHOLDER
2120
+ # null, # _PLACEHOLDER
2121
+ # 0, # Notify(0 if False, 1 if True)
2122
+ # 0, # Hidden(0 if False, 1 if True)
2123
+ # null, # Placed ID(If another order caused self order to be placed(OCO) self will be that other order's ID)
2124
+ # null, # _PLACEHOLDER
2125
+ # null, # _PLACEHOLDER
2126
+ # "API>BFX", # Routing, indicates origin of action: BFX, ETHFX, API>BFX, API>ETHFX
2127
+ # null, # _PLACEHOLDER
2128
+ # null, # _PLACEHOLDER
2129
+ # {"_$F7":1} # additional meta information about the order( _$F7 = IS_POST_ONLY(0 if False, 1 if True), _$F33 = Leverage(int))
2130
+ # ]
2131
+ # ]
2132
+ #
2133
+ ordersList = []
2134
+ for i in range(0, len(response)):
2135
+ ordersList.append({'result': response[i]})
2136
+ return self.parse_orders(ordersList, market, since, limit)
2137
+
2138
+ async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
2139
+ """
2140
+ fetch all the trades made from a single order
2141
+
2142
+ https://docs.bitfinex.com/reference/rest-auth-order-trades
2143
+
2144
+ :param str id: order id
2145
+ :param str symbol: unified market symbol
2146
+ :param int [since]: the earliest time in ms to fetch trades for
2147
+ :param int [limit]: the maximum number of trades to retrieve
2148
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2149
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
2150
+ """
2151
+ if symbol is None:
2152
+ raise ArgumentsRequired(self.id + ' fetchOrderTrades() requires a symbol argument')
2153
+ await self.load_markets()
2154
+ market = self.market(symbol)
2155
+ orderId = int(id)
2156
+ request: dict = {
2157
+ 'id': orderId,
2158
+ 'symbol': market['id'],
2159
+ }
2160
+ # valid for trades upto 10 days old
2161
+ response = await self.privatePostAuthROrderSymbolIdTrades(self.extend(request, params))
2162
+ tradesList = []
2163
+ for i in range(0, len(response)):
2164
+ tradesList.append({'result': response[i]}) # convert to array of dicts to match parseOrder signature
2165
+ return self.parse_trades(tradesList, market, since, limit)
2166
+
2167
+ async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
2168
+ """
2169
+ fetch all trades made by the user
2170
+
2171
+ https://docs.bitfinex.com/reference/rest-auth-trades
2172
+ https://docs.bitfinex.com/reference/rest-auth-trades-by-symbol
2173
+
2174
+ :param str symbol: unified market symbol
2175
+ :param int [since]: the earliest time in ms to fetch trades for
2176
+ :param int [limit]: the maximum number of trades structures to retrieve
2177
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2178
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
2179
+ """
2180
+ await self.load_markets()
2181
+ market = None
2182
+ request: dict = {
2183
+ 'end': self.milliseconds(),
2184
+ }
2185
+ if since is not None:
2186
+ request['start'] = since
2187
+ if limit is not None:
2188
+ request['limit'] = limit # default 25, max 1000
2189
+ response = None
2190
+ if symbol is not None:
2191
+ market = self.market(symbol)
2192
+ request['symbol'] = market['id']
2193
+ response = await self.privatePostAuthRTradesSymbolHist(self.extend(request, params))
2194
+ else:
2195
+ response = await self.privatePostAuthRTradesHist(self.extend(request, params))
2196
+ tradesList = []
2197
+ for i in range(0, len(response)):
2198
+ tradesList.append({'result': response[i]}) # convert to array of dicts to match parseOrder signature
2199
+ return self.parse_trades(tradesList, market, since, limit)
2200
+
2201
+ async def create_deposit_address(self, code: str, params={}):
2202
+ """
2203
+ create a currency deposit address
2204
+
2205
+ https://docs.bitfinex.com/reference/rest-auth-deposit-address
2206
+
2207
+ :param str code: unified currency code of the currency for the deposit address
2208
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2209
+ :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
2210
+ """
2211
+ await self.load_markets()
2212
+ request: dict = {
2213
+ 'op_renew': 1,
2214
+ }
2215
+ return await self.fetch_deposit_address(code, self.extend(request, params))
2216
+
2217
+ async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
2218
+ """
2219
+ fetch the deposit address for a currency associated with self account
2220
+
2221
+ https://docs.bitfinex.com/reference/rest-auth-deposit-address
2222
+
2223
+ :param str code: unified currency code
2224
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2225
+ :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
2226
+ """
2227
+ await self.load_markets()
2228
+ currency = self.currency(code)
2229
+ # if not provided explicitly we will try to match using the currency name
2230
+ network = self.safe_string(params, 'network', code)
2231
+ currencyNetworks = self.safe_value(currency, 'networks', {})
2232
+ currencyNetwork = self.safe_value(currencyNetworks, network)
2233
+ networkId = self.safe_string(currencyNetwork, 'id')
2234
+ if networkId is None:
2235
+ raise ArgumentsRequired(self.id + " fetchDepositAddress() could not find a network for '" + code + "'. You can specify it by providing the 'network' value inside params")
2236
+ wallet = self.safe_string(params, 'wallet', 'exchange') # 'exchange', 'margin', 'funding' and also old labels 'exchange', 'trading', 'deposit', respectively
2237
+ params = self.omit(params, 'network', 'wallet')
2238
+ request: dict = {
2239
+ 'method': networkId,
2240
+ 'wallet': wallet,
2241
+ 'op_renew': 0, # a value of 1 will generate a new address
2242
+ }
2243
+ response = await self.privatePostAuthWDepositAddress(self.extend(request, params))
2244
+ #
2245
+ # [
2246
+ # 1582269616687, # MTS Millisecond Time Stamp of the update
2247
+ # "acc_dep", # TYPE Purpose of notification "acc_dep" for account deposit
2248
+ # null, # MESSAGE_ID unique ID of the message
2249
+ # null, # not documented
2250
+ # [
2251
+ # null, # PLACEHOLDER
2252
+ # "BITCOIN", # METHOD Method of deposit
2253
+ # "BTC", # CURRENCY_CODE Currency code of new address
2254
+ # null, # PLACEHOLDER
2255
+ # "1BC9PZqpUmjyEB54uggn8TFKj49zSDYzqG", # ADDRESS
2256
+ # null, # POOL_ADDRESS
2257
+ # ],
2258
+ # null, # CODE null or integer work in progress
2259
+ # "SUCCESS", # STATUS Status of the notification, SUCCESS, ERROR, FAILURE
2260
+ # "success", # TEXT Text of the notification
2261
+ # ]
2262
+ #
2263
+ result = self.safe_value(response, 4, [])
2264
+ poolAddress = self.safe_string(result, 5)
2265
+ address = self.safe_string(result, 4) if (poolAddress is None) else poolAddress
2266
+ tag = None if (poolAddress is None) else self.safe_string(result, 4)
2267
+ self.check_address(address)
2268
+ return {
2269
+ 'currency': code,
2270
+ 'address': address,
2271
+ 'tag': tag,
2272
+ 'network': None,
2273
+ 'info': response,
2274
+ }
2275
+
2276
+ def parse_transaction_status(self, status: Str):
2277
+ statuses: dict = {
2278
+ 'SUCCESS': 'ok',
2279
+ 'COMPLETED': 'ok',
2280
+ 'ERROR': 'failed',
2281
+ 'FAILURE': 'failed',
2282
+ 'CANCELED': 'canceled',
2283
+ 'PENDING APPROVAL': 'pending',
2284
+ 'PENDING': 'pending',
2285
+ 'PENDING REVIEW': 'pending',
2286
+ 'PENDING CANCELLATION': 'pending',
2287
+ 'SENDING': 'pending',
2288
+ 'USER APPROVED': 'pending',
2289
+ }
2290
+ return self.safe_string(statuses, status, status)
1376
2291
 
1377
- def parse_transaction(self, transaction, currency: Currency = None) -> Transaction:
1378
- #
1379
- # crypto
1380
- #
1381
- # {
1382
- # "id": 12042490,
1383
- # "fee": "-0.02",
1384
- # "txid": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D",
1385
- # "type": "DEPOSIT",
1386
- # "amount": "2099.849999",
1387
- # "method": "RIPPLE",
1388
- # "status": "COMPLETED",
1389
- # "address": "2505189261",
1390
- # "currency": "XRP",
1391
- # "timestamp": "1551730524.0",
1392
- # "description": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D",
1393
- # "timestamp_created": "1551730523.0"
1394
- # }
1395
- #
1396
- # fiat
1397
- #
1398
- # {
1399
- # "id": 12725095,
1400
- # "fee": "-60.0",
1401
- # "txid": null,
1402
- # "type": "WITHDRAWAL",
1403
- # "amount": "9943.0",
1404
- # "method": "WIRE",
1405
- # "status": "SENDING",
1406
- # "address": null,
1407
- # "currency": "EUR",
1408
- # "timestamp": "1561802484.0",
1409
- # "description": "Name: bob, AccountAddress: some address, Account: someaccountno, Bank: bank address, SWIFT: foo, Country: UK, Details of Payment: withdrawal name, Intermediary Bank Name: , Intermediary Bank Address: , Intermediary Bank City: , Intermediary Bank Country: , Intermediary Bank Account: , Intermediary Bank SWIFT: , Fee: -60.0",
1410
- # "timestamp_created": "1561716066.0"
1411
- # }
2292
+ def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
1412
2293
  #
1413
2294
  # withdraw
1414
2295
  #
1415
- # {
1416
- # "status": "success",
1417
- # "message": "Your withdrawal request has been successfully submitted.",
1418
- # "withdrawal_id": 586829
1419
- # }
2296
+ # [
2297
+ # 1582271520931, # MTS Millisecond Time Stamp of the update
2298
+ # "acc_wd-req", # TYPE Purpose of notification "acc_wd-req" account withdrawal request
2299
+ # null, # MESSAGE_ID unique ID of the message
2300
+ # null, # not documented
2301
+ # [
2302
+ # 0, # WITHDRAWAL_ID Unique Withdrawal ID
2303
+ # null, # PLACEHOLDER
2304
+ # "bitcoin", # METHOD Method of withdrawal
2305
+ # null, # PAYMENT_ID Payment ID if relevant
2306
+ # "exchange", # WALLET Sending wallet
2307
+ # 1, # AMOUNT Amount of Withdrawal less fee
2308
+ # null, # PLACEHOLDER
2309
+ # null, # PLACEHOLDER
2310
+ # 0.0004, # WITHDRAWAL_FEE Fee on withdrawal
2311
+ # ],
2312
+ # null, # CODE null or integer Work in progress
2313
+ # "SUCCESS", # STATUS Status of the notification, it may vary over time SUCCESS, ERROR, FAILURE
2314
+ # "Invalid bitcoin address(abcdef)", # TEXT Text of the notification
2315
+ # ]
1420
2316
  #
1421
- timestamp = self.safe_timestamp(transaction, 'timestamp_created')
1422
- currencyId = self.safe_string(transaction, 'currency')
1423
- code = self.safe_currency_code(currencyId, currency)
1424
- feeCost = self.safe_string(transaction, 'fee')
1425
- if feeCost is not None:
1426
- feeCost = Precise.string_abs(feeCost)
2317
+ # fetchDepositsWithdrawals
2318
+ #
2319
+ # [
2320
+ # 13293039, # ID
2321
+ # "ETH", # CURRENCY
2322
+ # "ETHEREUM", # CURRENCY_NAME
2323
+ # null,
2324
+ # null,
2325
+ # 1574175052000, # MTS_STARTED
2326
+ # 1574181326000, # MTS_UPDATED
2327
+ # null,
2328
+ # null,
2329
+ # "CANCELED", # STATUS
2330
+ # null,
2331
+ # null,
2332
+ # -0.24, # AMOUNT, negative for withdrawals
2333
+ # -0.00135, # FEES
2334
+ # null,
2335
+ # null,
2336
+ # "0x38110e0Fc932CB2BE...........", # DESTINATION_ADDRESS
2337
+ # null,
2338
+ # null,
2339
+ # null,
2340
+ # "0x523ec8945500.....................................", # TRANSACTION_ID
2341
+ # "Purchase of 100 pizzas", # WITHDRAW_TRANSACTION_NOTE, might also be: null
2342
+ # ]
2343
+ #
2344
+ transactionLength = len(transaction)
2345
+ timestamp = None
2346
+ updated = None
2347
+ code = None
2348
+ amount = None
2349
+ id = None
2350
+ status = None
2351
+ tag = None
2352
+ type = None
2353
+ feeCost = None
2354
+ txid = None
2355
+ addressTo = None
2356
+ network = None
2357
+ comment = None
2358
+ if transactionLength == 8:
2359
+ data = self.safe_value(transaction, 4, [])
2360
+ timestamp = self.safe_integer(transaction, 0)
2361
+ if currency is not None:
2362
+ code = currency['code']
2363
+ feeCost = self.safe_string(data, 8)
2364
+ if feeCost is not None:
2365
+ feeCost = Precise.string_abs(feeCost)
2366
+ amount = self.safe_number(data, 5)
2367
+ id = self.safe_integer(data, 0)
2368
+ status = 'ok'
2369
+ if id == 0:
2370
+ id = None
2371
+ status = 'failed'
2372
+ tag = self.safe_string(data, 3)
2373
+ type = 'withdrawal'
2374
+ networkId = self.safe_string(data, 2)
2375
+ network = self.network_id_to_code(networkId.upper()) # withdraw returns in lowercase
2376
+ elif transactionLength == 22:
2377
+ id = self.safe_string(transaction, 0)
2378
+ currencyId = self.safe_string(transaction, 1)
2379
+ code = self.safe_currency_code(currencyId, currency)
2380
+ networkId = self.safe_string(transaction, 2)
2381
+ network = self.network_id_to_code(networkId)
2382
+ timestamp = self.safe_integer(transaction, 5)
2383
+ updated = self.safe_integer(transaction, 6)
2384
+ status = self.parse_transaction_status(self.safe_string(transaction, 9))
2385
+ signedAmount = self.safe_string(transaction, 12)
2386
+ amount = Precise.string_abs(signedAmount)
2387
+ if signedAmount is not None:
2388
+ if Precise.string_lt(signedAmount, '0'):
2389
+ type = 'withdrawal'
2390
+ else:
2391
+ type = 'deposit'
2392
+ feeCost = self.safe_string(transaction, 13)
2393
+ if feeCost is not None:
2394
+ feeCost = Precise.string_abs(feeCost)
2395
+ addressTo = self.safe_string(transaction, 16)
2396
+ txid = self.safe_string(transaction, 20)
2397
+ comment = self.safe_string(transaction, 21)
1427
2398
  return {
1428
2399
  'info': transaction,
1429
- 'id': self.safe_string_2(transaction, 'id', 'withdrawal_id'),
1430
- 'txid': self.safe_string(transaction, 'txid'),
1431
- 'type': self.safe_string_lower(transaction, 'type'), # DEPOSIT or WITHDRAWAL,
2400
+ 'id': id,
2401
+ 'txid': txid,
2402
+ 'type': type,
1432
2403
  'currency': code,
1433
- 'network': None,
1434
- 'amount': self.safe_number(transaction, 'amount'),
1435
- 'status': self.parse_transaction_status(self.safe_string(transaction, 'status')),
2404
+ 'network': network,
2405
+ 'amount': self.parse_number(amount),
2406
+ 'status': status,
1436
2407
  'timestamp': timestamp,
1437
2408
  'datetime': self.iso8601(timestamp),
1438
- 'address': self.safe_string(transaction, 'address'), # todo: self is actually the tag for XRP transfers(the address is missing)
2409
+ 'address': addressTo, # self is actually the tag for XRP transfers(the address is missing)
1439
2410
  'addressFrom': None,
1440
- 'addressTo': None,
1441
- 'tag': self.safe_string(transaction, 'description'),
2411
+ 'addressTo': addressTo,
2412
+ 'tag': tag, # refix it properly for the tag from description
1442
2413
  'tagFrom': None,
1443
- 'tagTo': None,
1444
- 'updated': self.safe_timestamp(transaction, 'timestamp'),
1445
- 'comment': None,
2414
+ 'tagTo': tag,
2415
+ 'updated': updated,
2416
+ 'comment': comment,
1446
2417
  'internal': None,
1447
2418
  'fee': {
1448
2419
  'currency': code,
@@ -1451,19 +2422,179 @@ class bitfinex(Exchange, ImplicitAPI):
1451
2422
  },
1452
2423
  }
1453
2424
 
1454
- def parse_transaction_status(self, status):
1455
- statuses = {
1456
- 'SENDING': 'pending',
1457
- 'CANCELED': 'canceled',
1458
- 'ZEROCONFIRMED': 'failed', # ZEROCONFIRMED happens e.g. in a double spend attempt(I had one in my movementsnot )
1459
- 'COMPLETED': 'ok',
1460
- }
1461
- return self.safe_string(statuses, status, status)
2425
+ async def fetch_trading_fees(self, params={}) -> TradingFees:
2426
+ """
2427
+ fetch the trading fees for multiple markets
2428
+
2429
+ https://docs.bitfinex.com/reference/rest-auth-summary
2430
+
2431
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2432
+ :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
2433
+ """
2434
+ await self.load_markets()
2435
+ response = await self.privatePostAuthRSummary(params)
2436
+ #
2437
+ # Response Spec:
2438
+ # [
2439
+ # PLACEHOLDER,
2440
+ # PLACEHOLDER,
2441
+ # PLACEHOLDER,
2442
+ # PLACEHOLDER,
2443
+ # [
2444
+ # [
2445
+ # MAKER_FEE,
2446
+ # MAKER_FEE,
2447
+ # MAKER_FEE,
2448
+ # PLACEHOLDER,
2449
+ # PLACEHOLDER,
2450
+ # DERIV_REBATE
2451
+ # ],
2452
+ # [
2453
+ # TAKER_FEE_TO_CRYPTO,
2454
+ # TAKER_FEE_TO_STABLE,
2455
+ # TAKER_FEE_TO_FIAT,
2456
+ # PLACEHOLDER,
2457
+ # PLACEHOLDER,
2458
+ # DERIV_TAKER_FEE
2459
+ # ]
2460
+ # ],
2461
+ # PLACEHOLDER,
2462
+ # PLACEHOLDER,
2463
+ # PLACEHOLDER,
2464
+ # PLACEHOLDER,
2465
+ # {
2466
+ # LEO_LEV,
2467
+ # LEO_AMOUNT_AVG
2468
+ # }
2469
+ # ]
2470
+ #
2471
+ # Example response:
2472
+ #
2473
+ # [
2474
+ # null,
2475
+ # null,
2476
+ # null,
2477
+ # null,
2478
+ # [
2479
+ # [0.001, 0.001, 0.001, null, null, 0.0002],
2480
+ # [0.002, 0.002, 0.002, null, null, 0.00065]
2481
+ # ],
2482
+ # [
2483
+ # [
2484
+ # {
2485
+ # "curr": "Total(USD)",
2486
+ # "vol": "0",
2487
+ # "vol_safe": "0",
2488
+ # "vol_maker": "0",
2489
+ # "vol_BFX": "0",
2490
+ # "vol_BFX_safe": "0",
2491
+ # "vol_BFX_maker": "0"
2492
+ # }
2493
+ # ],
2494
+ # {},
2495
+ # 0
2496
+ # ],
2497
+ # [null, {}, 0],
2498
+ # null,
2499
+ # null,
2500
+ # {leo_lev: "0", leo_amount_avg: "0"}
2501
+ # ]
2502
+ #
2503
+ result: dict = {}
2504
+ fiat = self.safe_value(self.options, 'fiat', {})
2505
+ feeData = self.safe_value(response, 4, [])
2506
+ makerData = self.safe_value(feeData, 0, [])
2507
+ takerData = self.safe_value(feeData, 1, [])
2508
+ makerFee = self.safe_number(makerData, 0)
2509
+ makerFeeFiat = self.safe_number(makerData, 2)
2510
+ makerFeeDeriv = self.safe_number(makerData, 5)
2511
+ takerFee = self.safe_number(takerData, 0)
2512
+ takerFeeFiat = self.safe_number(takerData, 2)
2513
+ takerFeeDeriv = self.safe_number(takerData, 5)
2514
+ for i in range(0, len(self.symbols)):
2515
+ symbol = self.symbols[i]
2516
+ market = self.market(symbol)
2517
+ fee = {
2518
+ 'info': response,
2519
+ 'symbol': symbol,
2520
+ 'percentage': True,
2521
+ 'tierBased': True,
2522
+ }
2523
+ if market['quote'] in fiat:
2524
+ fee['maker'] = makerFeeFiat
2525
+ fee['taker'] = takerFeeFiat
2526
+ elif market['contract']:
2527
+ fee['maker'] = makerFeeDeriv
2528
+ fee['taker'] = takerFeeDeriv
2529
+ else: # TODO check if stable coin
2530
+ fee['maker'] = makerFee
2531
+ fee['taker'] = takerFee
2532
+ result[symbol] = fee
2533
+ return result
2534
+
2535
+ async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
2536
+ """
2537
+ fetch history of deposits and withdrawals
2538
+
2539
+ https://docs.bitfinex.com/reference/movement-info
2540
+ https://docs.bitfinex.com/reference/rest-auth-movements
2541
+
2542
+ :param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
2543
+ :param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
2544
+ :param int [limit]: max number of deposit/withdrawals to return, default is None
2545
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2546
+ :returns dict: a list of `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
2547
+ """
2548
+ await self.load_markets()
2549
+ currency = None
2550
+ request: dict = {}
2551
+ if since is not None:
2552
+ request['start'] = since
2553
+ if limit is not None:
2554
+ request['limit'] = limit # max 1000
2555
+ response = None
2556
+ if code is not None:
2557
+ currency = self.currency(code)
2558
+ request['currency'] = currency['uppercaseId']
2559
+ response = await self.privatePostAuthRMovementsCurrencyHist(self.extend(request, params))
2560
+ else:
2561
+ response = await self.privatePostAuthRMovementsHist(self.extend(request, params))
2562
+ #
2563
+ # [
2564
+ # [
2565
+ # 13293039, # ID
2566
+ # "ETH", # CURRENCY
2567
+ # "ETHEREUM", # CURRENCY_NAME
2568
+ # null,
2569
+ # null,
2570
+ # 1574175052000, # MTS_STARTED
2571
+ # 1574181326000, # MTS_UPDATED
2572
+ # null,
2573
+ # null,
2574
+ # "CANCELED", # STATUS
2575
+ # null,
2576
+ # null,
2577
+ # -0.24, # AMOUNT, negative for withdrawals
2578
+ # -0.00135, # FEES
2579
+ # null,
2580
+ # null,
2581
+ # "0x38110e0Fc932CB2BE...........", # DESTINATION_ADDRESS
2582
+ # null,
2583
+ # null,
2584
+ # null,
2585
+ # "0x523ec8945500.....................................", # TRANSACTION_ID
2586
+ # "Purchase of 100 pizzas", # WITHDRAW_TRANSACTION_NOTE, might also be: null
2587
+ # ]
2588
+ # ]
2589
+ #
2590
+ return self.parse_transactions(response, currency, since, limit)
1462
2591
 
1463
- async def withdraw(self, code: str, amount: float, address, tag=None, params={}):
2592
+ async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}) -> Transaction:
1464
2593
  """
1465
2594
  make a withdrawal
1466
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-withdrawal
2595
+
2596
+ https://docs.bitfinex.com/reference/rest-auth-withdraw
2597
+
1467
2598
  :param str code: unified currency code
1468
2599
  :param float amount: the amount to withdraw
1469
2600
  :param str address: the address to withdraw to
@@ -1471,121 +2602,1148 @@ class bitfinex(Exchange, ImplicitAPI):
1471
2602
  :param dict [params]: extra parameters specific to the exchange API endpoint
1472
2603
  :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1473
2604
  """
1474
- tag, params = self.handle_withdraw_tag_and_params(tag, params)
1475
2605
  self.check_address(address)
1476
2606
  await self.load_markets()
1477
- # todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
1478
- name = self.get_currency_name(code)
1479
2607
  currency = self.currency(code)
1480
- request = {
1481
- 'withdraw_type': name,
1482
- 'walletselected': 'exchange',
2608
+ # if not provided explicitly we will try to match using the currency name
2609
+ network = self.safe_string(params, 'network', code)
2610
+ params = self.omit(params, 'network')
2611
+ currencyNetworks = self.safe_value(currency, 'networks', {})
2612
+ currencyNetwork = self.safe_value(currencyNetworks, network)
2613
+ networkId = self.safe_string(currencyNetwork, 'id')
2614
+ if networkId is None:
2615
+ raise ArgumentsRequired(self.id + " withdraw() could not find a network for '" + code + "'. You can specify it by providing the 'network' value inside params")
2616
+ wallet = self.safe_string(params, 'wallet', 'exchange') # 'exchange', 'margin', 'funding' and also old labels 'exchange', 'trading', 'deposit', respectively
2617
+ params = self.omit(params, 'network', 'wallet')
2618
+ request: dict = {
2619
+ 'method': networkId,
2620
+ 'wallet': wallet,
1483
2621
  'amount': self.number_to_string(amount),
1484
2622
  'address': address,
1485
2623
  }
1486
2624
  if tag is not None:
1487
2625
  request['payment_id'] = tag
1488
- responses = await self.privatePostWithdraw(self.extend(request, params))
2626
+ withdrawOptions = self.safe_value(self.options, 'withdraw', {})
2627
+ includeFee = self.safe_bool(withdrawOptions, 'includeFee', False)
2628
+ if includeFee:
2629
+ request['fee_deduct'] = 1
2630
+ response = await self.privatePostAuthWWithdraw(self.extend(request, params))
1489
2631
  #
1490
2632
  # [
1491
- # {
1492
- # "status":"success",
1493
- # "message":"Your withdrawal request has been successfully submitted.",
1494
- # "withdrawal_id":586829
1495
- # }
2633
+ # 1582271520931, # MTS Millisecond Time Stamp of the update
2634
+ # "acc_wd-req", # TYPE Purpose of notification "acc_wd-req" account withdrawal request
2635
+ # null, # MESSAGE_ID unique ID of the message
2636
+ # null, # not documented
2637
+ # [
2638
+ # 0, # WITHDRAWAL_ID Unique Withdrawal ID
2639
+ # null, # PLACEHOLDER
2640
+ # "bitcoin", # METHOD Method of withdrawal
2641
+ # null, # PAYMENT_ID Payment ID if relevant
2642
+ # "exchange", # WALLET Sending wallet
2643
+ # 1, # AMOUNT Amount of Withdrawal less fee
2644
+ # null, # PLACEHOLDER
2645
+ # null, # PLACEHOLDER
2646
+ # 0.0004, # WITHDRAWAL_FEE Fee on withdrawal
2647
+ # ],
2648
+ # null, # CODE null or integer Work in progress
2649
+ # "SUCCESS", # STATUS Status of the notification, it may vary over time SUCCESS, ERROR, FAILURE
2650
+ # "Invalid bitcoin address(abcdef)", # TEXT Text of the notification
2651
+ # ]
2652
+ #
2653
+ # in case of failure:
2654
+ #
2655
+ # [
2656
+ # "error",
2657
+ # 10001,
2658
+ # "Momentary balance check. Please wait few seconds and try the transfer again."
1496
2659
  # ]
1497
2660
  #
1498
- response = self.safe_value(responses, 0, {})
1499
- id = self.safe_number(response, 'withdrawal_id')
1500
- message = self.safe_string(response, 'message')
1501
- errorMessage = self.find_broadly_matched_key(self.exceptions['broad'], message)
1502
- if id == 0:
1503
- if errorMessage is not None:
1504
- ExceptionClass = self.exceptions['broad'][errorMessage]
1505
- raise ExceptionClass(self.id + ' ' + message)
1506
- raise ExchangeError(self.id + ' withdraw returned an id of zero: ' + self.json(response))
2661
+ statusMessage = self.safe_string(response, 0)
2662
+ if statusMessage == 'error':
2663
+ feedback = self.id + ' ' + response
2664
+ message = self.safe_string(response, 2, '')
2665
+ # same message v1
2666
+ self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
2667
+ self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
2668
+ raise ExchangeError(feedback) # unknown message
2669
+ text = self.safe_string(response, 7)
2670
+ if text != 'success':
2671
+ self.throw_broadly_matched_exception(self.exceptions['broad'], text, text)
1507
2672
  return self.parse_transaction(response, currency)
1508
2673
 
1509
2674
  async def fetch_positions(self, symbols: Strings = None, params={}):
1510
2675
  """
1511
2676
  fetch all open positions
1512
- :see: https://docs.bitfinex.com/v1/reference/rest-auth-active-positions
2677
+
2678
+ https://docs.bitfinex.com/reference/rest-auth-positions
2679
+
1513
2680
  :param str[]|None symbols: list of unified market symbols
1514
2681
  :param dict [params]: extra parameters specific to the exchange API endpoint
1515
2682
  :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
1516
2683
  """
1517
2684
  await self.load_markets()
1518
- response = await self.privatePostPositions(params)
2685
+ symbols = self.market_symbols(symbols)
2686
+ response = await self.privatePostAuthRPositions(params)
1519
2687
  #
1520
2688
  # [
1521
- # {
1522
- # "id":943715,
1523
- # "symbol":"btcusd",
1524
- # "status":"ACTIVE",
1525
- # "base":"246.94",
1526
- # "amount":"1.0",
1527
- # "timestamp":"1444141857.0",
1528
- # "swap":"0.0",
1529
- # "pl":"-2.22042"
1530
- # }
2689
+ # [
2690
+ # "tBTCUSD", # SYMBOL
2691
+ # "ACTIVE", # STATUS
2692
+ # 0.0195, # AMOUNT
2693
+ # 8565.0267019, # BASE_PRICE
2694
+ # 0, # MARGIN_FUNDING
2695
+ # 0, # MARGIN_FUNDING_TYPE
2696
+ # -0.33455568705000516, # PL
2697
+ # -0.0003117550117425625, # PL_PERC
2698
+ # 7045.876419249083, # PRICE_LIQ
2699
+ # 3.0673001895895604, # LEVERAGE
2700
+ # null, # _PLACEHOLDER
2701
+ # 142355652, # POSITION_ID
2702
+ # 1574002216000, # MTS_CREATE
2703
+ # 1574002216000, # MTS_UPDATE
2704
+ # null, # _PLACEHOLDER
2705
+ # 0, # TYPE
2706
+ # null, # _PLACEHOLDER
2707
+ # 0, # COLLATERAL
2708
+ # 0, # COLLATERAL_MIN
2709
+ # # META
2710
+ # {
2711
+ # "reason":"TRADE",
2712
+ # "order_id":34271018124,
2713
+ # "liq_stage":null,
2714
+ # "trade_price":"8565.0267019",
2715
+ # "trade_amount":"0.0195",
2716
+ # "order_id_oppo":34277498022
2717
+ # }
2718
+ # ]
1531
2719
  # ]
1532
2720
  #
1533
- # todo unify parsePosition/parsePositions
1534
- return response
2721
+ positionsList = []
2722
+ for i in range(0, len(response)):
2723
+ positionsList.append({'result': response[i]})
2724
+ return self.parse_positions(positionsList, symbols)
2725
+
2726
+ def parse_position(self, position: dict, market: Market = None):
2727
+ #
2728
+ # [
2729
+ # "tBTCUSD", # SYMBOL
2730
+ # "ACTIVE", # STATUS
2731
+ # 0.0195, # AMOUNT
2732
+ # 8565.0267019, # BASE_PRICE
2733
+ # 0, # MARGIN_FUNDING
2734
+ # 0, # MARGIN_FUNDING_TYPE
2735
+ # -0.33455568705000516, # PL
2736
+ # -0.0003117550117425625, # PL_PERC
2737
+ # 7045.876419249083, # PRICE_LIQ
2738
+ # 3.0673001895895604, # LEVERAGE
2739
+ # null, # _PLACEHOLDER
2740
+ # 142355652, # POSITION_ID
2741
+ # 1574002216000, # MTS_CREATE
2742
+ # 1574002216000, # MTS_UPDATE
2743
+ # null, # _PLACEHOLDER
2744
+ # 0, # TYPE
2745
+ # null, # _PLACEHOLDER
2746
+ # 0, # COLLATERAL
2747
+ # 0, # COLLATERAL_MIN
2748
+ # # META
2749
+ # {
2750
+ # "reason": "TRADE",
2751
+ # "order_id": 34271018124,
2752
+ # "liq_stage": null,
2753
+ # "trade_price": "8565.0267019",
2754
+ # "trade_amount": "0.0195",
2755
+ # "order_id_oppo": 34277498022
2756
+ # }
2757
+ # ]
2758
+ #
2759
+ positionList = self.safe_list(position, 'result')
2760
+ marketId = self.safe_string(positionList, 0)
2761
+ amount = self.safe_string(positionList, 2)
2762
+ timestamp = self.safe_integer(positionList, 12)
2763
+ meta = self.safe_string(positionList, 19)
2764
+ tradePrice = self.safe_string(meta, 'trade_price')
2765
+ tradeAmount = self.safe_string(meta, 'trade_amount')
2766
+ return self.safe_position({
2767
+ 'info': positionList,
2768
+ 'id': self.safe_string(positionList, 11),
2769
+ 'symbol': self.safe_symbol(marketId, market),
2770
+ 'notional': self.parse_number(amount),
2771
+ 'marginMode': 'isolated', # derivatives use isolated, margin uses cross, https://support.bitfinex.com/hc/en-us/articles/360035475374-Derivatives-Trading-on-Bitfinex
2772
+ 'liquidationPrice': self.safe_number(positionList, 8),
2773
+ 'entryPrice': self.safe_number(positionList, 3),
2774
+ 'unrealizedPnl': self.safe_number(positionList, 6),
2775
+ 'percentage': self.safe_number(positionList, 7),
2776
+ 'contracts': None,
2777
+ 'contractSize': None,
2778
+ 'markPrice': None,
2779
+ 'lastPrice': None,
2780
+ 'side': 'long' if Precise.string_gt(amount, '0') else 'short',
2781
+ 'hedged': None,
2782
+ 'timestamp': timestamp,
2783
+ 'datetime': self.iso8601(timestamp),
2784
+ 'lastUpdateTimestamp': self.safe_integer(positionList, 13),
2785
+ 'maintenanceMargin': self.safe_number(positionList, 18),
2786
+ 'maintenanceMarginPercentage': None,
2787
+ 'collateral': self.safe_number(positionList, 17),
2788
+ 'initialMargin': self.parse_number(Precise.string_mul(tradeAmount, tradePrice)),
2789
+ 'initialMarginPercentage': None,
2790
+ 'leverage': self.safe_number(positionList, 9),
2791
+ 'marginRatio': None,
2792
+ 'stopLossPrice': None,
2793
+ 'takeProfitPrice': None,
2794
+ })
1535
2795
 
1536
2796
  def nonce(self):
1537
2797
  return self.milliseconds()
1538
2798
 
1539
2799
  def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
1540
2800
  request = '/' + self.implode_params(path, params)
1541
- if api == 'v2':
1542
- request = '/' + api + request
1543
- else:
1544
- request = '/' + self.version + request
1545
2801
  query = self.omit(params, self.extract_params(path))
1546
- url = self.urls['api'][api] + request
1547
- if (api == 'public') or (path.find('/hist') >= 0):
2802
+ if api == 'v1':
2803
+ request = api + request
2804
+ else:
2805
+ request = self.version + request
2806
+ url = self.urls['api'][api] + '/' + request
2807
+ if api == 'public':
1548
2808
  if query:
1549
- suffix = '?' + self.urlencode(query)
1550
- url += suffix
1551
- request += suffix
2809
+ url += '?' + self.urlencode(query)
1552
2810
  if api == 'private':
1553
2811
  self.check_required_credentials()
1554
- nonce = self.nonce()
1555
- query = self.extend({
1556
- 'nonce': str(nonce),
1557
- 'request': request,
1558
- }, query)
2812
+ nonce = str(self.nonce())
1559
2813
  body = self.json(query)
1560
- payload = self.string_to_base64(body)
1561
- secret = self.encode(self.secret)
1562
- signature = self.hmac(self.encode(payload), secret, hashlib.sha384)
2814
+ auth = '/api/' + request + nonce + body
2815
+ signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha384)
1563
2816
  headers = {
1564
- 'X-BFX-APIKEY': self.apiKey,
1565
- 'X-BFX-PAYLOAD': payload,
1566
- 'X-BFX-SIGNATURE': signature,
2817
+ 'bfx-nonce': nonce,
2818
+ 'bfx-apikey': self.apiKey,
2819
+ 'bfx-signature': signature,
1567
2820
  'Content-Type': 'application/json',
1568
2821
  }
1569
2822
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
1570
2823
 
1571
- def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
1572
- if response is None:
2824
+ def handle_errors(self, statusCode, statusText, url, method, headers, body, response, requestHeaders, requestBody):
2825
+ # ["error", 11010, "ratelimit: error"]
2826
+ if response is not None:
2827
+ if not isinstance(response, list):
2828
+ message = self.safe_string_2(response, 'message', 'error')
2829
+ feedback = self.id + ' ' + body
2830
+ self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
2831
+ self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
2832
+ raise ExchangeError(self.id + ' ' + body)
2833
+ elif response == '':
2834
+ raise ExchangeError(self.id + ' returned empty response')
2835
+ if statusCode == 429:
2836
+ raise RateLimitExceeded(self.id + ' ' + body)
2837
+ if statusCode == 500:
2838
+ # See https://docs.bitfinex.com/docs/abbreviations-glossary#section-errorinfo-codes
2839
+ errorCode = self.safe_string(response, 1, '')
2840
+ errorText = self.safe_string(response, 2, '')
2841
+ feedback = self.id + ' ' + errorText
2842
+ self.throw_broadly_matched_exception(self.exceptions['broad'], errorText, feedback)
2843
+ self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
2844
+ self.throw_exactly_matched_exception(self.exceptions['exact'], errorText, feedback)
2845
+ raise ExchangeError(self.id + ' ' + errorText + '(#' + errorCode + ')')
2846
+ return response
2847
+
2848
+ def parse_ledger_entry_type(self, type: Str):
2849
+ if type is None:
1573
2850
  return None
1574
- throwError = False
1575
- if code >= 400:
1576
- if body[0] == '{':
1577
- throwError = True
2851
+ elif type.find('fee') >= 0 or type.find('charged') >= 0:
2852
+ return 'fee'
2853
+ elif type.find('rebate') >= 0:
2854
+ return 'rebate'
2855
+ elif type.find('deposit') >= 0 or type.find('withdrawal') >= 0:
2856
+ return 'transaction'
2857
+ elif type.find('transfer') >= 0:
2858
+ return 'transfer'
2859
+ elif type.find('payment') >= 0:
2860
+ return 'payout'
2861
+ elif type.find('exchange') >= 0 or type.find('position') >= 0:
2862
+ return 'trade'
1578
2863
  else:
1579
- # json response with error, i.e:
1580
- # [{"status":"error","message":"Momentary balance check. Please wait few seconds and try the transfer again."}]
1581
- responseObject = self.safe_value(response, 0, {})
1582
- status = self.safe_string(responseObject, 'status', '')
1583
- if status == 'error':
1584
- throwError = True
1585
- if throwError:
1586
- feedback = self.id + ' ' + body
1587
- message = self.safe_string_2(response, 'message', 'error')
1588
- self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
1589
- self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
1590
- raise ExchangeError(feedback) # unknown message
1591
- return None
2864
+ return type
2865
+
2866
+ def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
2867
+ #
2868
+ # [
2869
+ # [
2870
+ # 2531822314, # ID: Ledger identifier
2871
+ # "USD", # CURRENCY: The symbol of the currency(ex. "BTC")
2872
+ # null, # PLACEHOLDER
2873
+ # 1573521810000, # MTS: Timestamp in milliseconds
2874
+ # null, # PLACEHOLDER
2875
+ # 0.01644445, # AMOUNT: Amount of funds moved
2876
+ # 0, # BALANCE: New balance
2877
+ # null, # PLACEHOLDER
2878
+ # "Settlement @ 185.79 on wallet margin" # DESCRIPTION: Description of ledger transaction
2879
+ # ]
2880
+ # ]
2881
+ #
2882
+ itemList = self.safe_list(item, 'result', [])
2883
+ type = None
2884
+ id = self.safe_string(itemList, 0)
2885
+ currencyId = self.safe_string(itemList, 1)
2886
+ code = self.safe_currency_code(currencyId, currency)
2887
+ currency = self.safe_currency(currencyId, currency)
2888
+ timestamp = self.safe_integer(itemList, 3)
2889
+ amount = self.safe_number(itemList, 5)
2890
+ after = self.safe_number(itemList, 6)
2891
+ description = self.safe_string(itemList, 8)
2892
+ if description is not None:
2893
+ parts = description.split(' @ ')
2894
+ first = self.safe_string_lower(parts, 0)
2895
+ type = self.parse_ledger_entry_type(first)
2896
+ return self.safe_ledger_entry({
2897
+ 'info': item,
2898
+ 'id': id,
2899
+ 'direction': None,
2900
+ 'account': None,
2901
+ 'referenceId': id,
2902
+ 'referenceAccount': None,
2903
+ 'type': type,
2904
+ 'currency': code,
2905
+ 'amount': amount,
2906
+ 'timestamp': timestamp,
2907
+ 'datetime': self.iso8601(timestamp),
2908
+ 'before': None,
2909
+ 'after': after,
2910
+ 'status': None,
2911
+ 'fee': None,
2912
+ }, currency)
2913
+
2914
+ async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
2915
+ """
2916
+ fetch the history of changes, actions done by the user or operations that altered the balance of the user
2917
+
2918
+ https://docs.bitfinex.com/reference/rest-auth-ledgers
2919
+
2920
+ :param str [code]: unified currency code, default is None
2921
+ :param int [since]: timestamp in ms of the earliest ledger entry, default is None
2922
+ :param int [limit]: max number of ledger entries to return, default is None, max is 2500
2923
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2924
+ :param int [params.until]: timestamp in ms of the latest ledger entry
2925
+ :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)
2926
+ :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
2927
+ """
2928
+ await self.load_markets()
2929
+ paginate = False
2930
+ paginate, params = self.handle_option_and_params(params, 'fetchLedger', 'paginate')
2931
+ if paginate:
2932
+ return await self.fetch_paginated_call_dynamic('fetchLedger', code, since, limit, params, 2500)
2933
+ currency = None
2934
+ request: dict = {}
2935
+ if since is not None:
2936
+ request['start'] = since
2937
+ if limit is not None:
2938
+ request['limit'] = limit
2939
+ request, params = self.handle_until_option('end', request, params)
2940
+ response = None
2941
+ if code is not None:
2942
+ currency = self.currency(code)
2943
+ request['currency'] = currency['uppercaseId']
2944
+ response = await self.privatePostAuthRLedgersCurrencyHist(self.extend(request, params))
2945
+ else:
2946
+ response = await self.privatePostAuthRLedgersHist(self.extend(request, params))
2947
+ #
2948
+ # [
2949
+ # [
2950
+ # 2531822314, # ID: Ledger identifier
2951
+ # "USD", # CURRENCY: The symbol of the currency(ex. "BTC")
2952
+ # null, # PLACEHOLDER
2953
+ # 1573521810000, # MTS: Timestamp in milliseconds
2954
+ # null, # PLACEHOLDER
2955
+ # 0.01644445, # AMOUNT: Amount of funds moved
2956
+ # 0, # BALANCE: New balance
2957
+ # null, # PLACEHOLDER
2958
+ # "Settlement @ 185.79 on wallet margin" # DESCRIPTION: Description of ledger transaction
2959
+ # ]
2960
+ # ]
2961
+ #
2962
+ ledgerObjects = []
2963
+ for i in range(0, len(response)):
2964
+ item = response[i]
2965
+ ledgerObjects.append({'result': item})
2966
+ return self.parse_ledger(ledgerObjects, currency, since, limit)
2967
+
2968
+ async def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
2969
+ """
2970
+ fetch the current funding rate for multiple symbols
2971
+
2972
+ https://docs.bitfinex.com/reference/rest-public-derivatives-status
2973
+
2974
+ :param str[] symbols: list of unified market symbols
2975
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2976
+ :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-structure>`
2977
+ """
2978
+ if symbols is None:
2979
+ raise ArgumentsRequired(self.id + ' fetchFundingRates() requires a symbols argument')
2980
+ await self.load_markets()
2981
+ marketIds = self.market_ids(symbols)
2982
+ request: dict = {
2983
+ 'keys': ','.join(marketIds),
2984
+ }
2985
+ response = await self.publicGetStatusDeriv(self.extend(request, params))
2986
+ #
2987
+ # [
2988
+ # [
2989
+ # "tBTCF0:USTF0",
2990
+ # 1691165059000,
2991
+ # null,
2992
+ # 29297.851276225,
2993
+ # 29277.5,
2994
+ # null,
2995
+ # 36950860.76010306,
2996
+ # null,
2997
+ # 1691193600000,
2998
+ # 0.00000527,
2999
+ # 82,
3000
+ # null,
3001
+ # 0.00014548,
3002
+ # null,
3003
+ # null,
3004
+ # 29278.8925,
3005
+ # null,
3006
+ # null,
3007
+ # 9636.07644994,
3008
+ # null,
3009
+ # null,
3010
+ # null,
3011
+ # 0.0005,
3012
+ # 0.0025
3013
+ # ]
3014
+ # ]
3015
+ #
3016
+ return self.parse_funding_rates(response, symbols)
3017
+
3018
+ async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
3019
+ """
3020
+ fetches historical funding rate prices
3021
+
3022
+ https://docs.bitfinex.com/reference/rest-public-derivatives-status-history
3023
+
3024
+ :param str symbol: unified market symbol
3025
+ :param int [since]: timestamp in ms of the earliest funding rate entry
3026
+ :param int [limit]: max number of funding rate entrys to return
3027
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3028
+ :param int [params.until]: timestamp in ms of the latest funding rate
3029
+ :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)
3030
+ :returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
3031
+ """
3032
+ if symbol is None:
3033
+ raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
3034
+ await self.load_markets()
3035
+ paginate = False
3036
+ paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate')
3037
+ if paginate:
3038
+ return await self.fetch_paginated_call_deterministic('fetchFundingRateHistory', symbol, since, limit, '8h', params, 5000)
3039
+ market = self.market(symbol)
3040
+ request: dict = {
3041
+ 'symbol': market['id'],
3042
+ }
3043
+ if since is not None:
3044
+ request['start'] = since
3045
+ request, params = self.handle_until_option('end', request, params)
3046
+ response = await self.publicGetStatusDerivSymbolHist(self.extend(request, params))
3047
+ #
3048
+ # [
3049
+ # [
3050
+ # "tBTCF0:USTF0",
3051
+ # 1691165059000,
3052
+ # null,
3053
+ # 29297.851276225,
3054
+ # 29277.5,
3055
+ # null,
3056
+ # 36950860.76010306,
3057
+ # null,
3058
+ # 1691193600000,
3059
+ # 0.00000527,
3060
+ # 82,
3061
+ # null,
3062
+ # 0.00014548,
3063
+ # null,
3064
+ # null,
3065
+ # 29278.8925,
3066
+ # null,
3067
+ # null,
3068
+ # 9636.07644994,
3069
+ # null,
3070
+ # null,
3071
+ # null,
3072
+ # 0.0005,
3073
+ # 0.0025
3074
+ # ]
3075
+ # ]
3076
+ #
3077
+ rates = []
3078
+ for i in range(0, len(response)):
3079
+ fr = response[i]
3080
+ rate = self.parse_funding_rate_history(fr, market)
3081
+ rates.append(rate)
3082
+ reversedArray = []
3083
+ rawRates = self.filter_by_symbol_since_limit(rates, symbol, since, limit)
3084
+ ratesLength = len(rawRates)
3085
+ for i in range(0, ratesLength):
3086
+ index = ratesLength - i - 1
3087
+ valueAtIndex = rawRates[index]
3088
+ reversedArray.append(valueAtIndex)
3089
+ return reversedArray
3090
+
3091
+ def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
3092
+ #
3093
+ # [
3094
+ # "tBTCF0:USTF0",
3095
+ # 1691165059000,
3096
+ # null,
3097
+ # 29297.851276225,
3098
+ # 29277.5,
3099
+ # null,
3100
+ # 36950860.76010306,
3101
+ # null,
3102
+ # 1691193600000,
3103
+ # 0.00000527,
3104
+ # 82,
3105
+ # null,
3106
+ # 0.00014548,
3107
+ # null,
3108
+ # null,
3109
+ # 29278.8925,
3110
+ # null,
3111
+ # null,
3112
+ # 9636.07644994,
3113
+ # null,
3114
+ # null,
3115
+ # null,
3116
+ # 0.0005,
3117
+ # 0.0025
3118
+ # ]
3119
+ #
3120
+ marketId = self.safe_string(contract, 0)
3121
+ timestamp = self.safe_integer(contract, 1)
3122
+ nextFundingTimestamp = self.safe_integer(contract, 8)
3123
+ return {
3124
+ 'info': contract,
3125
+ 'symbol': self.safe_symbol(marketId, market),
3126
+ 'markPrice': self.safe_number(contract, 15),
3127
+ 'indexPrice': self.safe_number(contract, 3),
3128
+ 'interestRate': None,
3129
+ 'estimatedSettlePrice': None,
3130
+ 'timestamp': timestamp,
3131
+ 'datetime': self.iso8601(timestamp),
3132
+ 'fundingRate': self.safe_number(contract, 12),
3133
+ 'fundingTimestamp': None,
3134
+ 'fundingDatetime': None,
3135
+ 'nextFundingRate': self.safe_number(contract, 9),
3136
+ 'nextFundingTimestamp': nextFundingTimestamp,
3137
+ 'nextFundingDatetime': self.iso8601(nextFundingTimestamp),
3138
+ 'previousFundingRate': None,
3139
+ 'previousFundingTimestamp': None,
3140
+ 'previousFundingDatetime': None,
3141
+ 'interval': None,
3142
+ }
3143
+
3144
+ def parse_funding_rate_history(self, contract, market: Market = None):
3145
+ #
3146
+ # [
3147
+ # 1691165494000,
3148
+ # null,
3149
+ # 29278.95838065,
3150
+ # 29260.5,
3151
+ # null,
3152
+ # 36950860.76010305,
3153
+ # null,
3154
+ # 1691193600000,
3155
+ # 0.00001449,
3156
+ # 222,
3157
+ # null,
3158
+ # 0.00014548,
3159
+ # null,
3160
+ # null,
3161
+ # 29260.005,
3162
+ # null,
3163
+ # null,
3164
+ # 9635.86484562,
3165
+ # null,
3166
+ # null,
3167
+ # null,
3168
+ # 0.0005,
3169
+ # 0.0025
3170
+ # ]
3171
+ #
3172
+ timestamp = self.safe_integer(contract, 0)
3173
+ nextFundingTimestamp = self.safe_integer(contract, 7)
3174
+ return {
3175
+ 'info': contract,
3176
+ 'symbol': self.safe_symbol(None, market),
3177
+ 'markPrice': self.safe_number(contract, 14),
3178
+ 'indexPrice': self.safe_number(contract, 2),
3179
+ 'interestRate': None,
3180
+ 'estimatedSettlePrice': None,
3181
+ 'timestamp': timestamp,
3182
+ 'datetime': self.iso8601(timestamp),
3183
+ 'fundingRate': self.safe_number(contract, 11),
3184
+ 'fundingTimestamp': None,
3185
+ 'fundingDatetime': None,
3186
+ 'nextFundingRate': self.safe_number(contract, 8),
3187
+ 'nextFundingTimestamp': nextFundingTimestamp,
3188
+ 'nextFundingDatetime': self.iso8601(nextFundingTimestamp),
3189
+ 'previousFundingRate': None,
3190
+ 'previousFundingTimestamp': None,
3191
+ 'previousFundingDatetime': None,
3192
+ }
3193
+
3194
+ async def fetch_open_interests(self, symbols: Strings = None, params={}):
3195
+ """
3196
+ Retrieves the open interest for a list of symbols
3197
+
3198
+ https://docs.bitfinex.com/reference/rest-public-derivatives-status
3199
+
3200
+ :param str[] [symbols]: a list of unified CCXT market symbols
3201
+ :param dict [params]: exchange specific parameters
3202
+ :returns dict[]: a list of `open interest structures <https://docs.ccxt.com/#/?id=open-interest-structure>`
3203
+ """
3204
+ await self.load_markets()
3205
+ symbols = self.market_symbols(symbols)
3206
+ marketIds = ['ALL']
3207
+ if symbols is not None:
3208
+ marketIds = self.market_ids(symbols)
3209
+ request: dict = {
3210
+ 'keys': ','.join(marketIds),
3211
+ }
3212
+ response = await self.publicGetStatusDeriv(self.extend(request, params))
3213
+ #
3214
+ # [
3215
+ # [
3216
+ # "tXRPF0:USTF0", # market id
3217
+ # 1706256986000, # millisecond timestamp
3218
+ # null,
3219
+ # 0.512705, # derivative mid price
3220
+ # 0.512395, # underlying spot mid price
3221
+ # null,
3222
+ # 37671483.04, # insurance fund balance
3223
+ # null,
3224
+ # 1706284800000, # timestamp of next funding
3225
+ # 0.00002353, # accrued funding for next period
3226
+ # 317, # next funding step
3227
+ # null,
3228
+ # 0, # current funding
3229
+ # null,
3230
+ # null,
3231
+ # 0.5123016, # mark price
3232
+ # null,
3233
+ # null,
3234
+ # 2233562.03115, # open interest in contracts
3235
+ # null,
3236
+ # null,
3237
+ # null,
3238
+ # 0.0005, # average spread without funding payment
3239
+ # 0.0025 # funding payment cap
3240
+ # ]
3241
+ # ]
3242
+ #
3243
+ return self.parse_open_interests(response, symbols)
3244
+
3245
+ async def fetch_open_interest(self, symbol: str, params={}):
3246
+ """
3247
+ retrieves the open interest of a contract trading pair
3248
+
3249
+ https://docs.bitfinex.com/reference/rest-public-derivatives-status
3250
+
3251
+ :param str symbol: unified CCXT market symbol
3252
+ :param dict [params]: exchange specific parameters
3253
+ :returns dict: an `open interest structure <https://docs.ccxt.com/#/?id=open-interest-structure>`
3254
+ """
3255
+ await self.load_markets()
3256
+ market = self.market(symbol)
3257
+ request: dict = {
3258
+ 'keys': market['id'],
3259
+ }
3260
+ response = await self.publicGetStatusDeriv(self.extend(request, params))
3261
+ #
3262
+ # [
3263
+ # [
3264
+ # "tXRPF0:USTF0", # market id
3265
+ # 1706256986000, # millisecond timestamp
3266
+ # null,
3267
+ # 0.512705, # derivative mid price
3268
+ # 0.512395, # underlying spot mid price
3269
+ # null,
3270
+ # 37671483.04, # insurance fund balance
3271
+ # null,
3272
+ # 1706284800000, # timestamp of next funding
3273
+ # 0.00002353, # accrued funding for next period
3274
+ # 317, # next funding step
3275
+ # null,
3276
+ # 0, # current funding
3277
+ # null,
3278
+ # null,
3279
+ # 0.5123016, # mark price
3280
+ # null,
3281
+ # null,
3282
+ # 2233562.03115, # open interest in contracts
3283
+ # null,
3284
+ # null,
3285
+ # null,
3286
+ # 0.0005, # average spread without funding payment
3287
+ # 0.0025 # funding payment cap
3288
+ # ]
3289
+ # ]
3290
+ #
3291
+ oi = self.safe_list(response, 0)
3292
+ return self.parse_open_interest(oi, market)
3293
+
3294
+ async def fetch_open_interest_history(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
3295
+ """
3296
+ retrieves the open interest history of a currency
3297
+
3298
+ https://docs.bitfinex.com/reference/rest-public-derivatives-status-history
3299
+
3300
+ :param str symbol: unified CCXT market symbol
3301
+ :param str timeframe: the time period of each row of data, not used by bitfinex
3302
+ :param int [since]: the time in ms of the earliest record to retrieve unix timestamp
3303
+ :param int [limit]: the number of records in the response
3304
+ :param dict [params]: exchange specific parameters
3305
+ :param int [params.until]: the time in ms of the latest record to retrieve unix timestamp
3306
+ :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)
3307
+ :returns: An array of `open interest structures <https://docs.ccxt.com/#/?id=open-interest-structure>`
3308
+ """
3309
+ await self.load_markets()
3310
+ paginate = False
3311
+ paginate, params = self.handle_option_and_params(params, 'fetchOpenInterestHistory', 'paginate')
3312
+ if paginate:
3313
+ return await self.fetch_paginated_call_deterministic('fetchOpenInterestHistory', symbol, since, limit, '8h', params, 5000)
3314
+ market = self.market(symbol)
3315
+ request: dict = {
3316
+ 'symbol': market['id'],
3317
+ }
3318
+ if since is not None:
3319
+ request['start'] = since
3320
+ if limit is not None:
3321
+ request['limit'] = limit
3322
+ request, params = self.handle_until_option('end', request, params)
3323
+ response = await self.publicGetStatusDerivSymbolHist(self.extend(request, params))
3324
+ #
3325
+ # [
3326
+ # [
3327
+ # 1706295191000, # timestamp
3328
+ # null,
3329
+ # 42152.425382, # derivative mid price
3330
+ # 42133, # spot mid price
3331
+ # null,
3332
+ # 37671589.7853521, # insurance fund balance
3333
+ # null,
3334
+ # 1706313600000, # timestamp of next funding
3335
+ # 0.00018734, # accrued funding for next period
3336
+ # 3343, # next funding step
3337
+ # null,
3338
+ # 0.00007587, # current funding
3339
+ # null,
3340
+ # null,
3341
+ # 42134.1, # mark price
3342
+ # null,
3343
+ # null,
3344
+ # 5775.20348804, # open interest number of contracts
3345
+ # null,
3346
+ # null,
3347
+ # null,
3348
+ # 0.0005, # average spread without funding payment
3349
+ # 0.0025 # funding payment cap
3350
+ # ],
3351
+ # ]
3352
+ #
3353
+ return self.parse_open_interests_history(response, market, since, limit)
3354
+
3355
+ def parse_open_interest(self, interest, market: Market = None):
3356
+ #
3357
+ # fetchOpenInterest:
3358
+ #
3359
+ # [
3360
+ # "tXRPF0:USTF0", # market id
3361
+ # 1706256986000, # millisecond timestamp
3362
+ # null,
3363
+ # 0.512705, # derivative mid price
3364
+ # 0.512395, # underlying spot mid price
3365
+ # null,
3366
+ # 37671483.04, # insurance fund balance
3367
+ # null,
3368
+ # 1706284800000, # timestamp of next funding
3369
+ # 0.00002353, # accrued funding for next period
3370
+ # 317, # next funding step
3371
+ # null,
3372
+ # 0, # current funding
3373
+ # null,
3374
+ # null,
3375
+ # 0.5123016, # mark price
3376
+ # null,
3377
+ # null,
3378
+ # 2233562.03115, # open interest in contracts
3379
+ # null,
3380
+ # null,
3381
+ # null,
3382
+ # 0.0005, # average spread without funding payment
3383
+ # 0.0025 # funding payment cap
3384
+ # ]
3385
+ #
3386
+ # fetchOpenInterestHistory:
3387
+ #
3388
+ # [
3389
+ # 1706295191000, # timestamp
3390
+ # null,
3391
+ # 42152.425382, # derivative mid price
3392
+ # 42133, # spot mid price
3393
+ # null,
3394
+ # 37671589.7853521, # insurance fund balance
3395
+ # null,
3396
+ # 1706313600000, # timestamp of next funding
3397
+ # 0.00018734, # accrued funding for next period
3398
+ # 3343, # next funding step
3399
+ # null,
3400
+ # 0.00007587, # current funding
3401
+ # null,
3402
+ # null,
3403
+ # 42134.1, # mark price
3404
+ # null,
3405
+ # null,
3406
+ # 5775.20348804, # open interest number of contracts
3407
+ # null,
3408
+ # null,
3409
+ # null,
3410
+ # 0.0005, # average spread without funding payment
3411
+ # 0.0025 # funding payment cap
3412
+ # ]
3413
+ #
3414
+ interestLength = len(interest)
3415
+ openInterestIndex = 17 if (interestLength == 23) else 18
3416
+ timestamp = self.safe_integer(interest, 1)
3417
+ marketId = self.safe_string(interest, 0)
3418
+ return self.safe_open_interest({
3419
+ 'symbol': self.safe_symbol(marketId, market, None, 'swap'),
3420
+ 'openInterestAmount': self.safe_number(interest, openInterestIndex),
3421
+ 'openInterestValue': None,
3422
+ 'timestamp': timestamp,
3423
+ 'datetime': self.iso8601(timestamp),
3424
+ 'info': interest,
3425
+ }, market)
3426
+
3427
+ async def fetch_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}):
3428
+ """
3429
+ retrieves the public liquidations of a trading pair
3430
+
3431
+ https://docs.bitfinex.com/reference/rest-public-liquidations
3432
+
3433
+ :param str symbol: unified CCXT market symbol
3434
+ :param int [since]: the earliest time in ms to fetch liquidations for
3435
+ :param int [limit]: the maximum number of liquidation structures to retrieve
3436
+ :param dict [params]: exchange specific parameters
3437
+ :param int [params.until]: timestamp in ms of the latest liquidation
3438
+ :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)
3439
+ :returns dict: an array of `liquidation structures <https://docs.ccxt.com/#/?id=liquidation-structure>`
3440
+ """
3441
+ await self.load_markets()
3442
+ paginate = False
3443
+ paginate, params = self.handle_option_and_params(params, 'fetchLiquidations', 'paginate')
3444
+ if paginate:
3445
+ return await self.fetch_paginated_call_deterministic('fetchLiquidations', symbol, since, limit, '8h', params, 500)
3446
+ market = self.market(symbol)
3447
+ request: dict = {}
3448
+ if since is not None:
3449
+ request['start'] = since
3450
+ if limit is not None:
3451
+ request['limit'] = limit
3452
+ request, params = self.handle_until_option('end', request, params)
3453
+ response = await self.publicGetLiquidationsHist(self.extend(request, params))
3454
+ #
3455
+ # [
3456
+ # [
3457
+ # [
3458
+ # "pos",
3459
+ # 171085137,
3460
+ # 1706395919788,
3461
+ # null,
3462
+ # "tAVAXF0:USTF0",
3463
+ # -8,
3464
+ # 32.868,
3465
+ # null,
3466
+ # 1,
3467
+ # 1,
3468
+ # null,
3469
+ # 33.255
3470
+ # ]
3471
+ # ],
3472
+ # ]
3473
+ #
3474
+ return self.parse_liquidations(response, market, since, limit)
3475
+
3476
+ def parse_liquidation(self, liquidation, market: Market = None):
3477
+ #
3478
+ # [
3479
+ # [
3480
+ # "pos",
3481
+ # 171085137, # position id
3482
+ # 1706395919788, # timestamp
3483
+ # null,
3484
+ # "tAVAXF0:USTF0", # market id
3485
+ # -8, # amount in contracts
3486
+ # 32.868, # base price
3487
+ # null,
3488
+ # 1,
3489
+ # 1,
3490
+ # null,
3491
+ # 33.255 # acquired price
3492
+ # ]
3493
+ # ]
3494
+ #
3495
+ entry = liquidation[0]
3496
+ timestamp = self.safe_integer(entry, 2)
3497
+ marketId = self.safe_string(entry, 4)
3498
+ contracts = Precise.string_abs(self.safe_string(entry, 5))
3499
+ contractSize = self.safe_string(market, 'contractSize')
3500
+ baseValue = Precise.string_mul(contracts, contractSize)
3501
+ price = self.safe_string(entry, 11)
3502
+ return self.safe_liquidation({
3503
+ 'info': entry,
3504
+ 'symbol': self.safe_symbol(marketId, market, None, 'contract'),
3505
+ 'contracts': self.parse_number(contracts),
3506
+ 'contractSize': self.parse_number(contractSize),
3507
+ 'price': self.parse_number(price),
3508
+ 'baseValue': self.parse_number(baseValue),
3509
+ 'quoteValue': self.parse_number(Precise.string_mul(baseValue, price)),
3510
+ 'timestamp': timestamp,
3511
+ 'datetime': self.iso8601(timestamp),
3512
+ })
3513
+
3514
+ async def set_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
3515
+ """
3516
+ either adds or reduces margin in a swap position in order to set the margin to a specific value
3517
+
3518
+ https://docs.bitfinex.com/reference/rest-auth-deriv-pos-collateral-set
3519
+
3520
+ :param str symbol: unified market symbol of the market to set margin in
3521
+ :param float amount: the amount to set the margin to
3522
+ :param dict [params]: parameters specific to the exchange API endpoint
3523
+ :returns dict: A `margin structure <https://github.com/ccxt/ccxt/wiki/Manual#add-margin-structure>`
3524
+ """
3525
+ await self.load_markets()
3526
+ market = self.market(symbol)
3527
+ if not market['swap']:
3528
+ raise NotSupported(self.id + ' setMargin() only support swap markets')
3529
+ request: dict = {
3530
+ 'symbol': market['id'],
3531
+ 'collateral': self.parse_to_numeric(amount),
3532
+ }
3533
+ response = await self.privatePostAuthWDerivCollateralSet(self.extend(request, params))
3534
+ #
3535
+ # [
3536
+ # [
3537
+ # 1
3538
+ # ]
3539
+ # ]
3540
+ #
3541
+ data = self.safe_value(response, 0)
3542
+ return self.parse_margin_modification(data, market)
3543
+
3544
+ def parse_margin_modification(self, data, market=None) -> MarginModification:
3545
+ #
3546
+ # setMargin
3547
+ #
3548
+ # [
3549
+ # [
3550
+ # 1
3551
+ # ]
3552
+ # ]
3553
+ #
3554
+ marginStatusRaw = data[0]
3555
+ marginStatus = 'ok' if (marginStatusRaw == 1) else 'failed'
3556
+ return {
3557
+ 'info': data,
3558
+ 'symbol': market['symbol'],
3559
+ 'type': None,
3560
+ 'marginMode': 'isolated',
3561
+ 'amount': None,
3562
+ 'total': None,
3563
+ 'code': None,
3564
+ 'status': marginStatus,
3565
+ 'timestamp': None,
3566
+ 'datetime': None,
3567
+ }
3568
+
3569
+ async def fetch_order(self, id: str, symbol: Str = None, params={}):
3570
+ """
3571
+ fetches information on an order made by the user
3572
+
3573
+ https://docs.bitfinex.com/reference/rest-auth-retrieve-orders
3574
+ https://docs.bitfinex.com/reference/rest-auth-retrieve-orders-by-symbol
3575
+
3576
+ :param str id: the order id
3577
+ :param str [symbol]: unified symbol of the market the order was made in
3578
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3579
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
3580
+ """
3581
+ await self.load_markets()
3582
+ request: dict = {
3583
+ 'id': [self.parse_to_numeric(id)],
3584
+ }
3585
+ market = None
3586
+ response = None
3587
+ if symbol is None:
3588
+ response = await self.privatePostAuthROrders(self.extend(request, params))
3589
+ else:
3590
+ market = self.market(symbol)
3591
+ request['symbol'] = market['id']
3592
+ response = await self.privatePostAuthROrdersSymbol(self.extend(request, params))
3593
+ #
3594
+ # [
3595
+ # [
3596
+ # 139658969116,
3597
+ # null,
3598
+ # 1706843908637,
3599
+ # "tBTCUST",
3600
+ # 1706843908637,
3601
+ # 1706843908638,
3602
+ # 0.0001,
3603
+ # 0.0001,
3604
+ # "EXCHANGE LIMIT",
3605
+ # null,
3606
+ # null,
3607
+ # null,
3608
+ # 0,
3609
+ # "ACTIVE",
3610
+ # null,
3611
+ # null,
3612
+ # 35000,
3613
+ # 0,
3614
+ # 0,
3615
+ # 0,
3616
+ # null,
3617
+ # null,
3618
+ # null,
3619
+ # 0,
3620
+ # 0,
3621
+ # null,
3622
+ # null,
3623
+ # null,
3624
+ # "API>BFX",
3625
+ # null,
3626
+ # null,
3627
+ # {}
3628
+ # ]
3629
+ # ]
3630
+ #
3631
+ order = self.safe_list(response, 0)
3632
+ newOrder = {'result': order}
3633
+ return self.parse_order(newOrder, market)
3634
+
3635
+ async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
3636
+ """
3637
+ edit a trade order
3638
+
3639
+ https://docs.bitfinex.com/reference/rest-auth-update-order
3640
+
3641
+ :param str id: edit order id
3642
+ :param str symbol: unified symbol of the market to edit an order in
3643
+ :param str type: 'market' or 'limit'
3644
+ :param str side: 'buy' or 'sell'
3645
+ :param float amount: how much you want to trade in units of the base currency
3646
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
3647
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3648
+ :param float [params.triggerPrice]: the price that triggers a trigger order
3649
+ :param boolean [params.postOnly]: set to True if you want to make a post only order
3650
+ :param boolean [params.reduceOnly]: indicates that the order is to reduce the size of a position
3651
+ :param int [params.flags]: additional order parameters: 4096(Post Only), 1024(Reduce Only), 16384(OCO), 64(Hidden), 512(Close), 524288(No Var Rates)
3652
+ :param int [params.leverage]: leverage for a derivative order, supported by derivative symbol orders only, the value should be between 1 and 100 inclusive
3653
+ :param int [params.clientOrderId]: a unique client order id for the order
3654
+ :param float [params.trailingAmount]: *swap only* the quote amount to trail away from the current market price
3655
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
3656
+ """
3657
+ await self.load_markets()
3658
+ market = self.market(symbol)
3659
+ request: dict = {
3660
+ 'id': self.parse_to_numeric(id),
3661
+ }
3662
+ if amount is not None:
3663
+ amountString = self.amount_to_precision(symbol, amount)
3664
+ amountString = amountString if (side == 'buy') else Precise.string_neg(amountString)
3665
+ request['amount'] = amountString
3666
+ triggerPrice = self.safe_string_2(params, 'stopPrice', 'triggerPrice')
3667
+ trailingAmount = self.safe_string(params, 'trailingAmount')
3668
+ timeInForce = self.safe_string(params, 'timeInForce')
3669
+ postOnlyParam = self.safe_bool(params, 'postOnly', False)
3670
+ reduceOnly = self.safe_bool(params, 'reduceOnly', False)
3671
+ clientOrderId = self.safe_integer_2(params, 'cid', 'clientOrderId')
3672
+ if trailingAmount is not None:
3673
+ request['price_trailing'] = trailingAmount
3674
+ elif triggerPrice is not None:
3675
+ # request['price'] is taken for stop orders
3676
+ request['price'] = self.price_to_precision(symbol, triggerPrice)
3677
+ if type == 'limit':
3678
+ request['price_aux_limit'] = self.price_to_precision(symbol, price)
3679
+ postOnly = (postOnlyParam or (timeInForce == 'PO'))
3680
+ if (type != 'market') and (triggerPrice is None):
3681
+ request['price'] = self.price_to_precision(symbol, price)
3682
+ # flag values may be summed to combine flags
3683
+ flags = 0
3684
+ if postOnly:
3685
+ flags = self.sum(flags, 4096)
3686
+ if reduceOnly:
3687
+ flags = self.sum(flags, 1024)
3688
+ if flags != 0:
3689
+ request['flags'] = flags
3690
+ if clientOrderId is not None:
3691
+ request['cid'] = clientOrderId
3692
+ leverage = self.safe_integer_2(params, 'leverage', 'lev')
3693
+ if leverage is not None:
3694
+ request['lev'] = leverage
3695
+ params = self.omit(params, ['triggerPrice', 'stopPrice', 'timeInForce', 'postOnly', 'reduceOnly', 'trailingAmount', 'clientOrderId', 'leverage'])
3696
+ response = await self.privatePostAuthWOrderUpdate(self.extend(request, params))
3697
+ #
3698
+ # [
3699
+ # 1706845376402,
3700
+ # "ou-req",
3701
+ # null,
3702
+ # null,
3703
+ # [
3704
+ # 139658969116,
3705
+ # null,
3706
+ # 1706843908637,
3707
+ # "tBTCUST",
3708
+ # 1706843908637,
3709
+ # 1706843908638,
3710
+ # 0.0002,
3711
+ # 0.0002,
3712
+ # "EXCHANGE LIMIT",
3713
+ # null,
3714
+ # null,
3715
+ # null,
3716
+ # 0,
3717
+ # "ACTIVE",
3718
+ # null,
3719
+ # null,
3720
+ # 35000,
3721
+ # 0,
3722
+ # 0,
3723
+ # 0,
3724
+ # null,
3725
+ # null,
3726
+ # null,
3727
+ # 0,
3728
+ # 0,
3729
+ # null,
3730
+ # null,
3731
+ # null,
3732
+ # "API>BFX",
3733
+ # null,
3734
+ # null,
3735
+ # {}
3736
+ # ],
3737
+ # null,
3738
+ # "SUCCESS",
3739
+ # "Submitting update to exchange limit buy order for 0.0002 BTC."
3740
+ # ]
3741
+ #
3742
+ status = self.safe_string(response, 6)
3743
+ if status != 'SUCCESS':
3744
+ errorCode = response[5]
3745
+ errorText = response[7]
3746
+ raise ExchangeError(self.id + ' ' + response[6] + ': ' + errorText + '(#' + errorCode + ')')
3747
+ order = self.safe_list(response, 4, [])
3748
+ newOrder = {'result': order}
3749
+ return self.parse_order(newOrder, market)