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