/php/bybit.php

https://github.com/kroitor/ccxt · PHP · 2162 lines · 1179 code · 40 blank · 943 comment · 126 complexity · 5198f9201294e7ee55e8d6f435e71144 MD5 · raw file

  1. <?php
  2. namespace ccxt;
  3. // PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
  4. // https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
  5. use Exception; // a common import
  6. use \ccxt\ExchangeError;
  7. use \ccxt\ArgumentsRequired;
  8. class bybit extends Exchange {
  9. public function describe() {
  10. return $this->deep_extend(parent::describe (), array(
  11. 'id' => 'bybit',
  12. 'name' => 'Bybit',
  13. 'countries' => array( 'VG' ), // British Virgin Islands
  14. 'version' => 'v2',
  15. 'userAgent' => null,
  16. 'rateLimit' => 100,
  17. 'has' => array(
  18. 'cancelOrder' => true,
  19. 'CORS' => true,
  20. 'cancelAllOrders' => true,
  21. 'createOrder' => true,
  22. 'editOrder' => true,
  23. 'fetchBalance' => true,
  24. 'fetchClosedOrders' => true,
  25. 'fetchDeposits' => true,
  26. 'fetchLedger' => true,
  27. 'fetchMarkets' => true,
  28. 'fetchMyTrades' => true,
  29. 'fetchOHLCV' => true,
  30. 'fetchOpenOrders' => true,
  31. 'fetchOrder' => true,
  32. 'fetchOrderBook' => true,
  33. 'fetchOrders' => true,
  34. 'fetchOrderTrades' => true,
  35. 'fetchTicker' => true,
  36. 'fetchTickers' => true,
  37. 'fetchTime' => true,
  38. 'fetchTrades' => true,
  39. 'fetchTransactions' => false,
  40. 'fetchWithdrawals' => true,
  41. ),
  42. 'timeframes' => array(
  43. '1m' => '1',
  44. '3m' => '3',
  45. '5m' => '5',
  46. '15m' => '15',
  47. '30m' => '30',
  48. '1h' => '60',
  49. '2h' => '120',
  50. '4h' => '240',
  51. '6h' => '360',
  52. '12h' => '720',
  53. '1d' => 'D',
  54. '1w' => 'W',
  55. '1M' => 'M',
  56. '1y' => 'Y',
  57. ),
  58. 'urls' => array(
  59. 'test' => 'https://api-testnet.bybit.com',
  60. 'logo' => 'https://user-images.githubusercontent.com/51840849/76547799-daff5b80-649e-11ea-87fb-3be9bac08954.jpg',
  61. 'api' => 'https://api.bybit.com',
  62. 'www' => 'https://www.bybit.com',
  63. 'doc' => array(
  64. 'https://bybit-exchange.github.io/docs/inverse/',
  65. 'https://bybit-exchange.github.io/docs/linear/',
  66. 'https://github.com/bybit-exchange',
  67. ),
  68. 'fees' => 'https://help.bybit.com/hc/en-us/articles/360039261154',
  69. 'referral' => 'https://www.bybit.com/app/register?ref=X7Prm',
  70. ),
  71. 'api' => array(
  72. 'public' => array(
  73. 'get' => array(
  74. 'orderBook/L2',
  75. 'kline/list',
  76. 'tickers',
  77. 'trading-records',
  78. 'symbols',
  79. 'time',
  80. 'announcement',
  81. ),
  82. ),
  83. 'private' => array(
  84. 'get' => array(
  85. 'order',
  86. 'stop-order',
  87. 'position/list',
  88. 'wallet/balance',
  89. 'execution/list',
  90. ),
  91. 'post' => array(
  92. 'order/create',
  93. 'order/cancel',
  94. 'order/cancelAll',
  95. 'stop-order/cancelAll',
  96. ),
  97. ),
  98. 'openapi' => array(
  99. 'get' => array(
  100. 'order/list',
  101. 'stop-order/list',
  102. 'wallet/risk-limit/list',
  103. 'wallet/risk-limit',
  104. 'funding/prev-funding-rate',
  105. 'funding/prev-funding',
  106. 'funding/predicted-funding',
  107. 'api-key',
  108. 'wallet/fund/records',
  109. 'wallet/withdraw/list',
  110. ),
  111. 'post' => array(
  112. 'order/replace',
  113. 'stop-order/create',
  114. 'stop-order/cancel',
  115. 'stop-order/replace',
  116. 'position/trading-stop',
  117. ),
  118. ),
  119. 'publicLinear' => array(
  120. 'get' => array(
  121. 'kline',
  122. 'recent-trading-records',
  123. 'funding/prev-funding-rate',
  124. 'mark-price-kline',
  125. ),
  126. ),
  127. 'privateLinear' => array(
  128. 'get' => array(
  129. 'order/list',
  130. 'order/search',
  131. 'stop-order/list',
  132. 'stop-order/search',
  133. 'position/list',
  134. 'trade/execution/list',
  135. 'trade/closed-pnl/list',
  136. 'risk-limit',
  137. 'funding/prev-funding',
  138. 'funding/predicted-funding',
  139. ),
  140. 'post' => array(
  141. 'order/create',
  142. 'order/cancel',
  143. 'order/cancelAll',
  144. 'order/replace',
  145. 'stop-order/create',
  146. 'stop-order/cancel',
  147. 'stop-order/cancelAll',
  148. 'stop-order/replace',
  149. 'position/switch-isolated',
  150. 'position/set-auto-add-margin',
  151. 'position/set-leverage',
  152. 'position/trading-stop',
  153. 'position/add-margin',
  154. ),
  155. ),
  156. 'position' => array(
  157. 'post' => array(
  158. 'change-position-margin',
  159. ),
  160. ),
  161. 'user' => array(
  162. 'get' => array(
  163. 'leverage',
  164. ),
  165. 'post' => array(
  166. 'leverage/save',
  167. ),
  168. ),
  169. ),
  170. 'httpExceptions' => array(
  171. '403' => '\\ccxt\\RateLimitExceeded', // Forbidden -- You request too many times
  172. ),
  173. 'exceptions' => array(
  174. 'exact' => array(
  175. '10001' => '\\ccxt\\BadRequest', // parameter error
  176. '10002' => '\\ccxt\\InvalidNonce', // request expired, check your timestamp and recv_window
  177. '10003' => '\\ccxt\\AuthenticationError', // Invalid apikey
  178. '10004' => '\\ccxt\\AuthenticationError', // invalid sign
  179. '10005' => '\\ccxt\\PermissionDenied', // permission denied for current apikey
  180. '10006' => '\\ccxt\\RateLimitExceeded', // too many requests
  181. '10007' => '\\ccxt\\AuthenticationError', // api_key not found in your request parameters
  182. '10010' => '\\ccxt\\PermissionDenied', // request ip mismatch
  183. '10017' => '\\ccxt\\BadRequest', // request path not found or request method is invalid
  184. '20001' => '\\ccxt\\OrderNotFound', // Order not exists
  185. '20003' => '\\ccxt\\InvalidOrder', // missing parameter side
  186. '20004' => '\\ccxt\\InvalidOrder', // invalid parameter side
  187. '20005' => '\\ccxt\\InvalidOrder', // missing parameter symbol
  188. '20006' => '\\ccxt\\InvalidOrder', // invalid parameter symbol
  189. '20007' => '\\ccxt\\InvalidOrder', // missing parameter order_type
  190. '20008' => '\\ccxt\\InvalidOrder', // invalid parameter order_type
  191. '20009' => '\\ccxt\\InvalidOrder', // missing parameter qty
  192. '20010' => '\\ccxt\\InvalidOrder', // qty must be greater than 0
  193. '20011' => '\\ccxt\\InvalidOrder', // qty must be an integer
  194. '20012' => '\\ccxt\\InvalidOrder', // qty must be greater than zero and less than 1 million
  195. '20013' => '\\ccxt\\InvalidOrder', // missing parameter price
  196. '20014' => '\\ccxt\\InvalidOrder', // price must be greater than 0
  197. '20015' => '\\ccxt\\InvalidOrder', // missing parameter time_in_force
  198. '20016' => '\\ccxt\\InvalidOrder', // invalid value for parameter time_in_force
  199. '20017' => '\\ccxt\\InvalidOrder', // missing parameter order_id
  200. '20018' => '\\ccxt\\InvalidOrder', // invalid date format
  201. '20019' => '\\ccxt\\InvalidOrder', // missing parameter stop_px
  202. '20020' => '\\ccxt\\InvalidOrder', // missing parameter base_price
  203. '20021' => '\\ccxt\\InvalidOrder', // missing parameter stop_order_id
  204. '20022' => '\\ccxt\\BadRequest', // missing parameter leverage
  205. '20023' => '\\ccxt\\BadRequest', // leverage must be a number
  206. '20031' => '\\ccxt\\BadRequest', // leverage must be greater than zero
  207. '20070' => '\\ccxt\\BadRequest', // missing parameter margin
  208. '20071' => '\\ccxt\\BadRequest', // margin must be greater than zero
  209. '20084' => '\\ccxt\\BadRequest', // order_id or order_link_id is required
  210. '30001' => '\\ccxt\\BadRequest', // order_link_id is repeated
  211. '30003' => '\\ccxt\\InvalidOrder', // qty must be more than the minimum allowed
  212. '30004' => '\\ccxt\\InvalidOrder', // qty must be less than the maximum allowed
  213. '30005' => '\\ccxt\\InvalidOrder', // price exceeds maximum allowed
  214. '30007' => '\\ccxt\\InvalidOrder', // price exceeds minimum allowed
  215. '30008' => '\\ccxt\\InvalidOrder', // invalid order_type
  216. '30009' => '\\ccxt\\ExchangeError', // no position found
  217. '30010' => '\\ccxt\\InsufficientFunds', // insufficient wallet balance
  218. '30011' => '\\ccxt\\PermissionDenied', // operation not allowed as position is undergoing liquidation
  219. '30012' => '\\ccxt\\PermissionDenied', // operation not allowed as position is undergoing ADL
  220. '30013' => '\\ccxt\\PermissionDenied', // position is in liq or adl status
  221. '30014' => '\\ccxt\\InvalidOrder', // invalid closing order, qty should not greater than size
  222. '30015' => '\\ccxt\\InvalidOrder', // invalid closing order, side should be opposite
  223. '30016' => '\\ccxt\\ExchangeError', // TS and SL must be cancelled first while closing position
  224. '30017' => '\\ccxt\\InvalidOrder', // estimated fill price cannot be lower than current Buy liq_price
  225. '30018' => '\\ccxt\\InvalidOrder', // estimated fill price cannot be higher than current Sell liq_price
  226. '30019' => '\\ccxt\\InvalidOrder', // cannot attach TP/SL params for non-zero position when placing non-opening position order
  227. '30020' => '\\ccxt\\InvalidOrder', // position already has TP/SL params
  228. '30021' => '\\ccxt\\InvalidOrder', // cannot afford estimated position_margin
  229. '30022' => '\\ccxt\\InvalidOrder', // estimated buy liq_price cannot be higher than current mark_price
  230. '30023' => '\\ccxt\\InvalidOrder', // estimated sell liq_price cannot be lower than current mark_price
  231. '30024' => '\\ccxt\\InvalidOrder', // cannot set TP/SL/TS for zero-position
  232. '30025' => '\\ccxt\\InvalidOrder', // trigger price should bigger than 10% of last price
  233. '30026' => '\\ccxt\\InvalidOrder', // price too high
  234. '30027' => '\\ccxt\\InvalidOrder', // price set for Take profit should be higher than Last Traded Price
  235. '30028' => '\\ccxt\\InvalidOrder', // price set for Stop loss should be between Liquidation price and Last Traded Price
  236. '30029' => '\\ccxt\\InvalidOrder', // price set for Stop loss should be between Last Traded Price and Liquidation price
  237. '30030' => '\\ccxt\\InvalidOrder', // price set for Take profit should be lower than Last Traded Price
  238. '30031' => '\\ccxt\\InsufficientFunds', // insufficient available balance for order cost
  239. '30032' => '\\ccxt\\InvalidOrder', // order has been filled or cancelled
  240. '30033' => '\\ccxt\\RateLimitExceeded', // The number of stop orders exceeds maximum limit allowed
  241. '30034' => '\\ccxt\\OrderNotFound', // no order found
  242. '30035' => '\\ccxt\\RateLimitExceeded', // too fast to cancel
  243. '30036' => '\\ccxt\\ExchangeError', // the expected position value after order execution exceeds the current risk limit
  244. '30037' => '\\ccxt\\InvalidOrder', // order already cancelled
  245. '30041' => '\\ccxt\\ExchangeError', // no position found
  246. '30042' => '\\ccxt\\InsufficientFunds', // insufficient wallet balance
  247. '30043' => '\\ccxt\\PermissionDenied', // operation not allowed as position is undergoing liquidation
  248. '30044' => '\\ccxt\\PermissionDenied', // operation not allowed as position is undergoing AD
  249. '30045' => '\\ccxt\\PermissionDenied', // operation not allowed as position is not normal status
  250. '30049' => '\\ccxt\\InsufficientFunds', // insufficient available balance
  251. '30050' => '\\ccxt\\ExchangeError', // any adjustments made will trigger immediate liquidation
  252. '30051' => '\\ccxt\\ExchangeError', // due to risk limit, cannot adjust leverage
  253. '30052' => '\\ccxt\\ExchangeError', // leverage can not less than 1
  254. '30054' => '\\ccxt\\ExchangeError', // position margin is invalid
  255. '30057' => '\\ccxt\\ExchangeError', // requested quantity of contracts exceeds risk limit
  256. '30063' => '\\ccxt\\ExchangeError', // reduce-only rule not satisfied
  257. '30067' => '\\ccxt\\InsufficientFunds', // insufficient available balance
  258. '30068' => '\\ccxt\\ExchangeError', // exit value must be positive
  259. '34026' => '\\ccxt\\ExchangeError', // the limit is no change
  260. ),
  261. 'broad' => array(
  262. 'unknown orderInfo' => '\\ccxt\\OrderNotFound', // array("ret_code":-1,"ret_msg":"unknown orderInfo","ext_code":"","ext_info":"","result":null,"time_now":"1584030414.005545","rate_limit_status":99,"rate_limit_reset_ms":1584030414003,"rate_limit":100)
  263. 'invalid api_key' => '\\ccxt\\AuthenticationError', // array("ret_code":10003,"ret_msg":"invalid api_key","ext_code":"","ext_info":"","result":null,"time_now":"1599547085.415797")
  264. ),
  265. ),
  266. 'precisionMode' => TICK_SIZE,
  267. 'options' => array(
  268. 'marketTypes' => array(
  269. 'BTC/USDT' => 'linear',
  270. ),
  271. 'code' => 'BTC',
  272. 'fetchBalance' => array(
  273. 'code' => 'BTC',
  274. ),
  275. 'cancelAllOrders' => array(
  276. 'method' => 'privatePostOrderCancelAll', // privatePostStopOrderCancelAll
  277. ),
  278. 'recvWindow' => 5 * 1000, // 5 sec default
  279. 'timeDifference' => 0, // the difference between system clock and Binance clock
  280. 'adjustForTimeDifference' => false, // controls the adjustment logic upon instantiation
  281. ),
  282. 'fees' => array(
  283. 'trading' => array(
  284. 'tierBased' => false,
  285. 'percentage' => true,
  286. 'taker' => 0.00075,
  287. 'maker' => -0.00025,
  288. ),
  289. 'funding' => array(
  290. 'tierBased' => false,
  291. 'percentage' => false,
  292. 'withdraw' => array(),
  293. 'deposit' => array(),
  294. ),
  295. ),
  296. ));
  297. }
  298. public function nonce() {
  299. return $this->milliseconds() - $this->options['timeDifference'];
  300. }
  301. public function load_time_difference($params = array ()) {
  302. $serverTime = $this->fetch_time($params);
  303. $after = $this->milliseconds();
  304. $this->options['timeDifference'] = $after - $serverTime;
  305. return $this->options['timeDifference'];
  306. }
  307. public function fetch_time($params = array ()) {
  308. $response = $this->publicGetTime ($params);
  309. //
  310. // {
  311. // ret_code => 0,
  312. // ret_msg => 'OK',
  313. // ext_code => '',
  314. // ext_info => '',
  315. // result => array(),
  316. // time_now => '1583933682.448826'
  317. // }
  318. //
  319. return $this->safe_timestamp($response, 'time_now');
  320. }
  321. public function fetch_markets($params = array ()) {
  322. if ($this->options['adjustForTimeDifference']) {
  323. $this->load_time_difference();
  324. }
  325. $response = $this->publicGetSymbols ($params);
  326. //
  327. // {
  328. // ret_code => 0,
  329. // ret_msg => 'OK',
  330. // ext_code => '',
  331. // ext_info => '',
  332. // $result => array(
  333. // array(
  334. // name => 'BTCUSD',
  335. // base_currency => 'BTC',
  336. // quote_currency => 'USD',
  337. // price_scale => 2,
  338. // taker_fee => '0.00075',
  339. // maker_fee => '-0.00025',
  340. // leverage_filter => array( min_leverage => 1, max_leverage => 100, leverage_step => '0.01' ),
  341. // price_filter => array( min_price => '0.5', max_price => '999999.5', tick_size => '0.5' ),
  342. // lot_size_filter => array( max_trading_qty => 1000000, min_trading_qty => 1, qty_step => 1 )
  343. // ),
  344. // ),
  345. // time_now => '1583930495.454196'
  346. // }
  347. //
  348. $markets = $this->safe_value($response, 'result', array());
  349. $options = $this->safe_value($this->options, 'fetchMarkets', array());
  350. $linearQuoteCurrencies = $this->safe_value($options, 'linear', array( 'USDT' => true ));
  351. $result = array();
  352. for ($i = 0; $i < count($markets); $i++) {
  353. $market = $markets[$i];
  354. $id = $this->safe_string($market, 'name');
  355. $baseId = $this->safe_string($market, 'base_currency');
  356. $quoteId = $this->safe_string($market, 'quote_currency');
  357. $base = $this->safe_currency_code($baseId);
  358. $quote = $this->safe_currency_code($quoteId);
  359. $linear = (is_array($linearQuoteCurrencies) && array_key_exists($quote, $linearQuoteCurrencies));
  360. $inverse = !$linear;
  361. $symbol = $base . '/' . $quote;
  362. $baseQuote = $base . $quote;
  363. if ($baseQuote !== $id) {
  364. $symbol = $id;
  365. }
  366. $lotSizeFilter = $this->safe_value($market, 'lot_size_filter', array());
  367. $priceFilter = $this->safe_value($market, 'price_filter', array());
  368. $precision = array(
  369. 'amount' => $this->safe_float($lotSizeFilter, 'qty_step'),
  370. 'price' => $this->safe_float($priceFilter, 'tick_size'),
  371. );
  372. $result[] = array(
  373. 'id' => $id,
  374. 'symbol' => $symbol,
  375. 'base' => $base,
  376. 'quote' => $quote,
  377. 'active' => null,
  378. 'precision' => $precision,
  379. 'taker' => $this->safe_float($market, 'taker_fee'),
  380. 'maker' => $this->safe_float($market, 'maker_fee'),
  381. 'type' => 'future',
  382. 'spot' => false,
  383. 'future' => true,
  384. 'option' => false,
  385. 'linear' => $linear,
  386. 'inverse' => $inverse,
  387. 'limits' => array(
  388. 'amount' => array(
  389. 'min' => $this->safe_float($lotSizeFilter, 'min_trading_qty'),
  390. 'max' => $this->safe_float($lotSizeFilter, 'max_trading_qty'),
  391. ),
  392. 'price' => array(
  393. 'min' => $this->safe_float($priceFilter, 'min_price'),
  394. 'max' => $this->safe_float($priceFilter, 'max_price'),
  395. ),
  396. 'cost' => array(
  397. 'min' => null,
  398. 'max' => null,
  399. ),
  400. ),
  401. 'info' => $market,
  402. );
  403. }
  404. return $result;
  405. }
  406. public function fetch_balance($params = array ()) {
  407. $this->load_markets();
  408. $defaultCode = $this->safe_value($this->options, 'code', 'BTC');
  409. $options = $this->safe_value($this->options, 'fetchBalance', array());
  410. $code = $this->safe_value($options, 'code', $defaultCode);
  411. $currency = $this->currency($code);
  412. $request = array(
  413. 'coin' => $currency['id'],
  414. );
  415. $response = $this->privateGetWalletBalance (array_merge($request, $params));
  416. //
  417. // {
  418. // ret_code => 0,
  419. // ret_msg => 'OK',
  420. // ext_code => '',
  421. // ext_info => '',
  422. // $result => {
  423. // BTC => array(
  424. // equity => 0,
  425. // available_balance => 0,
  426. // used_margin => 0,
  427. // order_margin => 0,
  428. // position_margin => 0,
  429. // occ_closing_fee => 0,
  430. // occ_funding_fee => 0,
  431. // wallet_balance => 0,
  432. // realised_pnl => 0,
  433. // unrealised_pnl => 0,
  434. // cum_realised_pnl => 0,
  435. // given_cash => 0,
  436. // service_cash => 0
  437. // }
  438. // ),
  439. // time_now => '1583937810.370020',
  440. // rate_limit_status => 119,
  441. // rate_limit_reset_ms => 1583937810367,
  442. // rate_limit => 120
  443. // }
  444. //
  445. $result = array(
  446. 'info' => $response,
  447. );
  448. $balances = $this->safe_value($response, 'result', array());
  449. $currencyIds = is_array($balances) ? array_keys($balances) : array();
  450. for ($i = 0; $i < count($currencyIds); $i++) {
  451. $currencyId = $currencyIds[$i];
  452. $balance = $balances[$currencyId];
  453. $code = $this->safe_currency_code($currencyId);
  454. $account = $this->account();
  455. $account['free'] = $this->safe_float($balance, 'available_balance');
  456. $account['used'] = $this->safe_float($balance, 'used_margin');
  457. $account['total'] = $this->safe_float($balance, 'equity');
  458. $result[$code] = $account;
  459. }
  460. return $this->parse_balance($result);
  461. }
  462. public function parse_ticker($ticker, $market = null) {
  463. //
  464. // fetchTicker
  465. //
  466. // {
  467. // $symbol => 'BTCUSD',
  468. // bid_price => '7680',
  469. // ask_price => '7680.5',
  470. // last_price => '7680.00',
  471. // last_tick_direction => 'MinusTick',
  472. // prev_price_24h => '7870.50',
  473. // price_24h_pcnt => '-0.024204',
  474. // high_price_24h => '8035.00',
  475. // low_price_24h => '7671.00',
  476. // prev_price_1h => '7780.00',
  477. // price_1h_pcnt => '-0.012853',
  478. // mark_price => '7683.27',
  479. // index_price => '7682.74',
  480. // open_interest => 188829147,
  481. // open_value => '23670.06',
  482. // total_turnover => '25744224.90',
  483. // turnover_24h => '102997.83',
  484. // total_volume => 225448878806,
  485. // volume_24h => 809919408,
  486. // funding_rate => '0.0001',
  487. // predicted_funding_rate => '0.0001',
  488. // next_funding_time => '2020-03-12T00:00:00Z',
  489. // countdown_hour => 7
  490. // }
  491. //
  492. $timestamp = null;
  493. $marketId = $this->safe_string($ticker, 'symbol');
  494. $symbol = $marketId;
  495. if (is_array($this->markets_by_id) && array_key_exists($marketId, $this->markets_by_id)) {
  496. $market = $this->markets_by_id[$marketId];
  497. }
  498. if (($symbol === null) && ($market !== null)) {
  499. $symbol = $market['symbol'];
  500. }
  501. $last = $this->safe_float($ticker, 'last_price');
  502. $open = $this->safe_float($ticker, 'prev_price_24h');
  503. $percentage = $this->safe_float($ticker, 'price_24h_pcnt');
  504. if ($percentage !== null) {
  505. $percentage *= 100;
  506. }
  507. $change = null;
  508. $average = null;
  509. if (($last !== null) && ($open !== null)) {
  510. $change = $last - $open;
  511. $average = $this->sum($open, $last) / 2;
  512. }
  513. $baseVolume = $this->safe_float($ticker, 'turnover_24h');
  514. $quoteVolume = $this->safe_float($ticker, 'volume_24h');
  515. $vwap = $this->vwap($baseVolume, $quoteVolume);
  516. return array(
  517. 'symbol' => $symbol,
  518. 'timestamp' => $timestamp,
  519. 'datetime' => $this->iso8601($timestamp),
  520. 'high' => $this->safe_float($ticker, 'high_price_24h'),
  521. 'low' => $this->safe_float($ticker, 'low_price_24h'),
  522. 'bid' => $this->safe_float($ticker, 'bid_price'),
  523. 'bidVolume' => null,
  524. 'ask' => $this->safe_float($ticker, 'ask_price'),
  525. 'askVolume' => null,
  526. 'vwap' => $vwap,
  527. 'open' => $open,
  528. 'close' => $last,
  529. 'last' => $last,
  530. 'previousClose' => null,
  531. 'change' => $change,
  532. 'percentage' => $percentage,
  533. 'average' => $average,
  534. 'baseVolume' => $baseVolume,
  535. 'quoteVolume' => $quoteVolume,
  536. 'info' => $ticker,
  537. );
  538. }
  539. public function fetch_ticker($symbol, $params = array ()) {
  540. $this->load_markets();
  541. $market = $this->market($symbol);
  542. $request = array(
  543. 'symbol' => $market['id'],
  544. );
  545. $response = $this->publicGetTickers (array_merge($request, $params));
  546. //
  547. // {
  548. // ret_code => 0,
  549. // ret_msg => 'OK',
  550. // ext_code => '',
  551. // ext_info => '',
  552. // $result => array(
  553. // {
  554. // $symbol => 'BTCUSD',
  555. // bid_price => '7680',
  556. // ask_price => '7680.5',
  557. // last_price => '7680.00',
  558. // last_tick_direction => 'MinusTick',
  559. // prev_price_24h => '7870.50',
  560. // price_24h_pcnt => '-0.024204',
  561. // high_price_24h => '8035.00',
  562. // low_price_24h => '7671.00',
  563. // prev_price_1h => '7780.00',
  564. // price_1h_pcnt => '-0.012853',
  565. // mark_price => '7683.27',
  566. // index_price => '7682.74',
  567. // open_interest => 188829147,
  568. // open_value => '23670.06',
  569. // total_turnover => '25744224.90',
  570. // turnover_24h => '102997.83',
  571. // total_volume => 225448878806,
  572. // volume_24h => 809919408,
  573. // funding_rate => '0.0001',
  574. // predicted_funding_rate => '0.0001',
  575. // next_funding_time => '2020-03-12T00:00:00Z',
  576. // countdown_hour => 7
  577. // }
  578. // ),
  579. // time_now => '1583948195.818255'
  580. // }
  581. //
  582. $result = $this->safe_value($response, 'result', array());
  583. $first = $this->safe_value($result, 0);
  584. $timestamp = $this->safe_timestamp($response, 'time_now');
  585. $ticker = $this->parse_ticker($first, $market);
  586. $ticker['timestamp'] = $timestamp;
  587. $ticker['datetime'] = $this->iso8601($timestamp);
  588. return $ticker;
  589. }
  590. public function fetch_tickers($symbols = null, $params = array ()) {
  591. $this->load_markets();
  592. $response = $this->publicGetTickers ($params);
  593. //
  594. // {
  595. // ret_code => 0,
  596. // ret_msg => 'OK',
  597. // ext_code => '',
  598. // ext_info => '',
  599. // $result => array(
  600. // {
  601. // $symbol => 'BTCUSD',
  602. // bid_price => '7680',
  603. // ask_price => '7680.5',
  604. // last_price => '7680.00',
  605. // last_tick_direction => 'MinusTick',
  606. // prev_price_24h => '7870.50',
  607. // price_24h_pcnt => '-0.024204',
  608. // high_price_24h => '8035.00',
  609. // low_price_24h => '7671.00',
  610. // prev_price_1h => '7780.00',
  611. // price_1h_pcnt => '-0.012853',
  612. // mark_price => '7683.27',
  613. // index_price => '7682.74',
  614. // open_interest => 188829147,
  615. // open_value => '23670.06',
  616. // total_turnover => '25744224.90',
  617. // turnover_24h => '102997.83',
  618. // total_volume => 225448878806,
  619. // volume_24h => 809919408,
  620. // funding_rate => '0.0001',
  621. // predicted_funding_rate => '0.0001',
  622. // next_funding_time => '2020-03-12T00:00:00Z',
  623. // countdown_hour => 7
  624. // }
  625. // ),
  626. // time_now => '1583948195.818255'
  627. // }
  628. //
  629. $result = $this->safe_value($response, 'result', array());
  630. $tickers = array();
  631. for ($i = 0; $i < count($result); $i++) {
  632. $ticker = $this->parse_ticker($result[$i]);
  633. $symbol = $ticker['symbol'];
  634. $tickers[$symbol] = $ticker;
  635. }
  636. return $this->filter_by_array($tickers, 'symbol', $symbols);
  637. }
  638. public function parse_ohlcv($ohlcv, $market = null) {
  639. //
  640. // inverse perpetual BTC/USD
  641. //
  642. // {
  643. // symbol => 'BTCUSD',
  644. // interval => '1',
  645. // open_time => 1583952540,
  646. // open => '7760.5',
  647. // high => '7764',
  648. // low => '7757',
  649. // close => '7763.5',
  650. // volume => '1259766',
  651. // turnover => '162.32773718999994'
  652. // }
  653. //
  654. // linear perpetual BTC/USDT
  655. //
  656. // {
  657. // "id":143536,
  658. // "symbol":"BTCUSDT",
  659. // "period":"15",
  660. // "start_at":1587883500,
  661. // "volume":1.035,
  662. // "open":7540.5,
  663. // "high":7541,
  664. // "low":7540.5,
  665. // "close":7541
  666. // }
  667. //
  668. return array(
  669. $this->safe_timestamp_2($ohlcv, 'open_time', 'start_at'),
  670. $this->safe_float($ohlcv, 'open'),
  671. $this->safe_float($ohlcv, 'high'),
  672. $this->safe_float($ohlcv, 'low'),
  673. $this->safe_float($ohlcv, 'close'),
  674. $this->safe_float_2($ohlcv, 'turnover', 'volume'),
  675. );
  676. }
  677. public function fetch_ohlcv($symbol, $timeframe = '1m', $since = null, $limit = null, $params = array ()) {
  678. $this->load_markets();
  679. $market = $this->market($symbol);
  680. $request = array(
  681. 'symbol' => $market['id'],
  682. 'interval' => $this->timeframes[$timeframe],
  683. );
  684. $duration = $this->parse_timeframe($timeframe);
  685. $now = $this->seconds();
  686. if ($since === null) {
  687. if ($limit === null) {
  688. throw new ArgumentsRequired($this->id . ' fetchOHLCV requires a $since argument or a $limit argument');
  689. } else {
  690. $request['from'] = $now - $limit * $duration;
  691. }
  692. } else {
  693. $request['from'] = intval($since / 1000);
  694. }
  695. if ($limit !== null) {
  696. $request['limit'] = $limit; // max 200, default 200
  697. }
  698. $marketTypes = $this->safe_value($this->options, 'marketTypes', array());
  699. $marketType = $this->safe_string($marketTypes, $symbol);
  700. $method = ($marketType === 'linear') ? 'publicLinearGetKline' : 'publicGetKlineList';
  701. $response = $this->$method (array_merge($request, $params));
  702. //
  703. // inverse perpetual BTC/USD
  704. //
  705. // {
  706. // ret_code => 0,
  707. // ret_msg => 'OK',
  708. // ext_code => '',
  709. // ext_info => '',
  710. // $result => array(
  711. // array(
  712. // $symbol => 'BTCUSD',
  713. // interval => '1',
  714. // open_time => 1583952540,
  715. // open => '7760.5',
  716. // high => '7764',
  717. // low => '7757',
  718. // close => '7763.5',
  719. // volume => '1259766',
  720. // turnover => '162.32773718999994'
  721. // ),
  722. // ),
  723. // time_now => '1583953082.397330'
  724. // }
  725. //
  726. // linear perpetual BTC/USDT
  727. //
  728. // {
  729. // "ret_code":0,
  730. // "ret_msg":"OK",
  731. // "ext_code":"",
  732. // "ext_info":"",
  733. // "$result":array(
  734. // {
  735. // "id":143536,
  736. // "$symbol":"BTCUSDT",
  737. // "period":"15",
  738. // "start_at":1587883500,
  739. // "volume":1.035,
  740. // "open":7540.5,
  741. // "high":7541,
  742. // "low":7540.5,
  743. // "close":7541
  744. // }
  745. // ),
  746. // "time_now":"1587884120.168077"
  747. // }
  748. //
  749. $result = $this->safe_value($response, 'result', array());
  750. return $this->parse_ohlcvs($result, $market, $timeframe, $since, $limit);
  751. }
  752. public function parse_trade($trade, $market = null) {
  753. //
  754. // fetchTrades (public)
  755. //
  756. // {
  757. // $id => 43785688,
  758. // $symbol => 'BTCUSD',
  759. // $price => 7786,
  760. // qty => 67,
  761. // $side => 'Sell',
  762. // time => '2020-03-11T19:18:30.123Z'
  763. // }
  764. //
  765. // fetchMyTrades, fetchOrderTrades (private)
  766. //
  767. // {
  768. // "closed_size" => 0,
  769. // "cross_seq" => 277136382,
  770. // "exec_fee" => "0.0000001",
  771. // "exec_id" => "256e5ef8-abfe-5772-971b-f944e15e0d68",
  772. // "exec_price" => "8178.5",
  773. // "exec_qty" => 1,
  774. // // the docs say the exec_time field is "abandoned" now
  775. // // the user should use "trade_time_ms"
  776. // "exec_time" => "1571676941.70682",
  777. // "exec_type" => "Trade", //Exec Type Enum
  778. // "exec_value" => "0.00012227",
  779. // "fee_rate" => "0.00075",
  780. // "last_liquidity_ind" => "RemovedLiquidity", //Liquidity Enum
  781. // "leaves_qty" => 0,
  782. // "nth_fill" => 2,
  783. // "order_id" => "7ad50cb1-9ad0-4f74-804b-d82a516e1029",
  784. // "order_link_id" => "",
  785. // "order_price" => "8178",
  786. // "order_qty" => 1,
  787. // "order_type" => "Market", //Order Type Enum
  788. // "$side" => "Buy", //Side Enum
  789. // "$symbol" => "BTCUSD", //Symbol Enum
  790. // "user_id" => 1,
  791. // "trade_time_ms" => 1577480599000
  792. // }
  793. //
  794. $id = $this->safe_string_2($trade, 'id', 'exec_id');
  795. $symbol = null;
  796. $base = null;
  797. $marketId = $this->safe_string($trade, 'symbol');
  798. $amount = $this->safe_float_2($trade, 'qty', 'exec_qty');
  799. $cost = $this->safe_float($trade, 'exec_value');
  800. $price = $this->safe_float_2($trade, 'price', 'exec_price');
  801. if (is_array($this->markets_by_id) && array_key_exists($marketId, $this->markets_by_id)) {
  802. $market = $this->markets_by_id[$marketId];
  803. $symbol = $market['symbol'];
  804. $base = $market['base'];
  805. }
  806. if ($market !== null) {
  807. if ($symbol === null) {
  808. $symbol = $market['symbol'];
  809. $base = $market['base'];
  810. }
  811. }
  812. if ($cost === null) {
  813. if ($amount !== null) {
  814. if ($price !== null) {
  815. $cost = $amount * $price;
  816. }
  817. }
  818. }
  819. $timestamp = $this->parse8601($this->safe_string($trade, 'time'));
  820. if ($timestamp === null) {
  821. $timestamp = $this->safe_integer($trade, 'trade_time_ms');
  822. }
  823. $side = $this->safe_string_lower($trade, 'side');
  824. $lastLiquidityInd = $this->safe_string($trade, 'last_liquidity_ind');
  825. $takerOrMaker = ($lastLiquidityInd === 'AddedLiquidity') ? 'maker' : 'taker';
  826. $feeCost = $this->safe_float($trade, 'exec_fee');
  827. $fee = null;
  828. if ($feeCost !== null) {
  829. $fee = array(
  830. 'cost' => $feeCost,
  831. 'currency' => $base,
  832. 'rate' => $this->safe_float($trade, 'fee_rate'),
  833. );
  834. }
  835. return array(
  836. 'id' => $id,
  837. 'info' => $trade,
  838. 'timestamp' => $timestamp,
  839. 'datetime' => $this->iso8601($timestamp),
  840. 'symbol' => $symbol,
  841. 'order' => $this->safe_string($trade, 'order_id'),
  842. 'type' => $this->safe_string_lower($trade, 'order_type'),
  843. 'side' => $side,
  844. 'takerOrMaker' => $takerOrMaker,
  845. 'price' => $price,
  846. 'amount' => $amount,
  847. 'cost' => $cost,
  848. 'fee' => $fee,
  849. );
  850. }
  851. public function fetch_trades($symbol, $since = null, $limit = null, $params = array ()) {
  852. $this->load_markets();
  853. $market = $this->market($symbol);
  854. $request = array(
  855. 'symbol' => $market['id'],
  856. // 'from' => 123, // from id
  857. );
  858. if ($limit !== null) {
  859. $request['count'] = $limit; // default 500, max 1000
  860. }
  861. $marketTypes = $this->safe_value($this->options, 'marketTypes', array());
  862. $marketType = $this->safe_string($marketTypes, $symbol);
  863. $method = ($marketType === 'linear') ? 'publicLinearGetRecentTradingRecords' : 'publicGetTradingRecords';
  864. $response = $this->$method (array_merge($request, $params));
  865. //
  866. // {
  867. // ret_code => 0,
  868. // ret_msg => 'OK',
  869. // ext_code => '',
  870. // ext_info => '',
  871. // $result => array(
  872. // array(
  873. // id => 43785688,
  874. // $symbol => 'BTCUSD',
  875. // price => 7786,
  876. // qty => 67,
  877. // side => 'Sell',
  878. // time => '2020-03-11T19:18:30.123Z'
  879. // ),
  880. // ),
  881. // time_now => '1583954313.393362'
  882. // }
  883. //
  884. $result = $this->safe_value($response, 'result', array());
  885. return $this->parse_trades($result, $market, $since, $limit);
  886. }
  887. public function parse_order_book($orderbook, $timestamp = null, $bidsKey = 'Buy', $asksKey = 'Sell', $priceKey = 'price', $amountKey = 'size') {
  888. $bids = array();
  889. $asks = array();
  890. for ($i = 0; $i < count($orderbook); $i++) {
  891. $bidask = $orderbook[$i];
  892. $side = $this->safe_string($bidask, 'side');
  893. if ($side === 'Buy') {
  894. $bids[] = $this->parse_bid_ask($bidask, $priceKey, $amountKey);
  895. } else if ($side === 'Sell') {
  896. $asks[] = $this->parse_bid_ask($bidask, $priceKey, $amountKey);
  897. } else {
  898. throw new ExchangeError($this->id . ' parseOrderBook encountered an unrecognized $bidask format => ' . $this->json($bidask));
  899. }
  900. }
  901. return array(
  902. 'bids' => $this->sort_by($bids, 0, true),
  903. 'asks' => $this->sort_by($asks, 0),
  904. 'timestamp' => $timestamp,
  905. 'datetime' => $this->iso8601($timestamp),
  906. 'nonce' => null,
  907. );
  908. }
  909. public function fetch_order_book($symbol, $limit = null, $params = array ()) {
  910. $this->load_markets();
  911. $market = $this->market($symbol);
  912. $request = array(
  913. 'symbol' => $market['id'],
  914. );
  915. $response = $this->publicGetOrderBookL2 (array_merge($request, $params));
  916. //
  917. // {
  918. // ret_code => 0,
  919. // ret_msg => 'OK',
  920. // ext_code => '',
  921. // ext_info => '',
  922. // $result => array(
  923. // array( $symbol => 'BTCUSD', price => '7767.5', size => 677956, side => 'Buy' ),
  924. // array( $symbol => 'BTCUSD', price => '7767', size => 580690, side => 'Buy' ),
  925. // array( $symbol => 'BTCUSD', price => '7766.5', size => 475252, side => 'Buy' ),
  926. // array( $symbol => 'BTCUSD', price => '7768', size => 330847, side => 'Sell' ),
  927. // array( $symbol => 'BTCUSD', price => '7768.5', size => 97159, side => 'Sell' ),
  928. // array( $symbol => 'BTCUSD', price => '7769', size => 6508, side => 'Sell' ),
  929. // ),
  930. // time_now => '1583954829.874823'
  931. // }
  932. //
  933. $result = $this->safe_value($response, 'result', array());
  934. $timestamp = $this->safe_timestamp($response, 'time_now');
  935. return $this->parse_order_book($result, $timestamp, 'Buy', 'Sell', 'price', 'size');
  936. }
  937. public function parse_order_status($status) {
  938. $statuses = array(
  939. // basic orders
  940. 'Created' => 'open',
  941. 'Rejected' => 'rejected', // order is triggered but failed upon being placed
  942. 'New' => 'open',
  943. 'PartiallyFilled' => 'open',
  944. 'Filled' => 'closed',
  945. 'Cancelled' => 'canceled',
  946. 'PendingCancel' => 'canceling', // the engine has received the cancellation but there is no guarantee that it will be successful
  947. // conditional orders
  948. 'Active' => 'open', // order is triggered and placed successfully
  949. 'Untriggered' => 'open', // order waits to be triggered
  950. 'Triggered' => 'closed', // order is triggered
  951. // 'Cancelled' => 'canceled', // order is cancelled
  952. // 'Rejected' => 'rejected', // order is triggered but fail to be placed
  953. 'Deactivated' => 'canceled', // conditional order was cancelled before triggering
  954. );
  955. return $this->safe_string($statuses, $status, $status);
  956. }
  957. public function parse_order($order, $market = null) {
  958. //
  959. // createOrder
  960. //
  961. // {
  962. // "user_id" => 1,
  963. // "order_id" => "335fd977-e5a5-4781-b6d0-c772d5bfb95b",
  964. // "$symbol" => "BTCUSD",
  965. // "$side" => "Buy",
  966. // "order_type" => "Limit",
  967. // "$price" => 8800,
  968. // "qty" => 1,
  969. // "time_in_force" => "GoodTillCancel",
  970. // "order_status" => "Created",
  971. // "last_exec_time" => 0,
  972. // "last_exec_price" => 0,
  973. // "leaves_qty" => 1,
  974. // "cum_exec_qty" => 0, // in contracts, where 1 contract = 1 quote currency unit (USD for inverse contracts)
  975. // "cum_exec_value" => 0, // in contract's underlying currency (BTC for inverse contracts)
  976. // "cum_exec_fee" => 0,
  977. // "reject_reason" => "",
  978. // "order_link_id" => "",
  979. // "created_at" => "2019-11-30T11:03:43.452Z",
  980. // "updated_at" => "2019-11-30T11:03:43.455Z"
  981. // }
  982. //
  983. // fetchOrder
  984. //
  985. // {
  986. // "user_id" : 599946,
  987. // "$symbol" : "BTCUSD",
  988. // "$side" : "Buy",
  989. // "order_type" : "Limit",
  990. // "$price" : "7948",
  991. // "qty" : 10,
  992. // "time_in_force" : "GoodTillCancel",
  993. // "order_status" : "Filled",
  994. // "ext_fields" : array(
  995. // "o_req_num" : -1600687220498,
  996. // "xreq_type" : "x_create"
  997. // ),
  998. // "last_exec_time" : "1588150113.968422",
  999. // "last_exec_price" : "7948",
  1000. // "leaves_qty" : 0,
  1001. // "leaves_value" : "0",
  1002. // "cum_exec_qty" : 10,
  1003. // "cum_exec_value" : "0.00125817",
  1004. // "cum_exec_fee" : "-0.00000031",
  1005. // "reject_reason" : "",
  1006. // "cancel_type" : "",
  1007. // "order_link_id" : "",
  1008. // "created_at" : "2020-04-29T08:45:24.399146Z",
  1009. // "updated_at" : "2020-04-29T08:48:33.968422Z",
  1010. // "order_id" : "dd2504b9-0157-406a-99e1-efa522373944"
  1011. // }
  1012. //
  1013. // conditional $order
  1014. //
  1015. // {
  1016. // "user_id":##,
  1017. // "$symbol":"BTCUSD",
  1018. // "$side":"Buy",
  1019. // "order_type":"Market",
  1020. // "$price":0,
  1021. // "qty":10,
  1022. // "time_in_force":"GoodTillCancel",
  1023. // "stop_order_type":"Stop",
  1024. // "trigger_by":"LastPrice",
  1025. // "base_price":11833,
  1026. // "order_status":"Untriggered",
  1027. // "ext_fields":array(
  1028. // "stop_order_type":"Stop",
  1029. // "trigger_by":"LastPrice",
  1030. // "base_price":11833,
  1031. // "expected_direction":"Rising",
  1032. // "trigger_price":12400,
  1033. // "close_on_trigger":true,
  1034. // "op_from":"api",
  1035. // "remark":"145.53.159.48",
  1036. // "o_req_num":0
  1037. // ),
  1038. // "leaves_qty":10,
  1039. // "leaves_value":0.00080645,
  1040. // "reject_reason":null,
  1041. // "cross_seq":-1,
  1042. // "created_at":"2020-08-21T09:18:48.000Z",
  1043. // "updated_at":"2020-08-21T09:18:48.000Z",
  1044. // "stop_px":12400,
  1045. // "stop_order_id":"3f3b54b1-3379-42c7-8510-44f4d9915be0"
  1046. // }
  1047. //
  1048. $marketId = $this->safe_string($order, 'symbol');
  1049. $symbol = null;
  1050. $base = null;
  1051. if (is_array($this->markets_by_id) && array_key_exists($marketId, $this->markets_by_id)) {
  1052. $market = $this->markets_by_id[$marketId];
  1053. }
  1054. $timestamp = $this->parse8601($this->safe_string($order, 'created_at'));
  1055. $id = $this->safe_string_2($order, 'order_id', 'stop_order_id');
  1056. $price = $this->safe_float($order, 'price');
  1057. $average = $this->safe_float($order, 'average_price');
  1058. $amount = $this->safe_float($order, 'qty');
  1059. $cost = $this->safe_float($order, 'cum_exec_value');
  1060. $filled = $this->safe_float($order, 'cum_exec_qty');
  1061. $remaining = $this->safe_float($order, 'leaves_qty');
  1062. if ($market !== null) {
  1063. $symbol = $market['symbol'];
  1064. $base = $market['base'];
  1065. }
  1066. $lastTradeTimestamp = $this->safe_timestamp($order, 'last_exec_time');
  1067. if ($lastTradeTimestamp === 0) {
  1068. $lastTradeTimestamp = null;
  1069. }
  1070. if (($filled === null) && ($amount !== null) && ($remaining !== null)) {
  1071. $filled = $amount - $remaining;
  1072. }
  1073. if ($filled !== null) {
  1074. if (($remaining === null) && ($amount !== null)) {
  1075. $remaining = $amount - $filled;
  1076. }
  1077. if ($cost === null) {
  1078. if ($price !== null) {
  1079. $cost = $price * $filled;
  1080. }
  1081. }
  1082. }
  1083. $status = $this->parse_order_status($this->safe_string_2($order, 'order_status', 'stop_order_status'));
  1084. $side = $this->safe_string_lower($order, 'side');
  1085. $feeCost = $this->safe_float($order, 'cum_exec_fee');
  1086. $fee = null;
  1087. if ($feeCost !== null) {
  1088. $feeCost = abs($feeCost);
  1089. $fee = array(
  1090. 'cost' => $feeCost,
  1091. 'currency' => $base,
  1092. );
  1093. }
  1094. $type = $this->safe_string_lower($order, 'order_type');
  1095. $clientOrderId = $this->safe_string($order, 'order_link_id');
  1096. if (($clientOrderId !== null) && (strlen($clientOrderId) < 1)) {
  1097. $clientOrderId = null;
  1098. }
  1099. return array(
  1100. 'info' => $order,
  1101. 'id' => $id,
  1102. 'clientOrderId' => $clientOrderId,
  1103. 'timestamp' => $timestamp,
  1104. 'datetime' => $this->iso8601($timestamp),
  1105. 'lastTradeTimestamp' => $lastTradeTimestamp,
  1106. 'symbol' => $symbol,
  1107. 'type' => $type,
  1108. 'side' => $side,
  1109. 'price' => $price,
  1110. 'amount' => $amount,
  1111. 'cost' => $cost,
  1112. 'average' => $average,
  1113. 'filled' => $filled,
  1114. 'remaining' => $remaining,
  1115. 'status' => $status,
  1116. 'fee' => $fee,
  1117. 'trades' => null,
  1118. );
  1119. }
  1120. public function fetch_order($id, $symbol = null, $params = array ()) {
  1121. if ($symbol === null) {
  1122. throw new ArgumentsRequired($this->id . ' fetchOrder requires a $symbol argument');
  1123. }
  1124. $this->load_markets();
  1125. $market = $this->market($symbol);
  1126. $request = array(
  1127. 'symbol' => $market['id'],
  1128. // 'order_link_id' => 'string', // one of order_id, stop_order_id or order_link_id is required
  1129. // regular orders ---------------------------------------------
  1130. // 'order_id' => $id, // one of order_id or order_link_id is required for regular orders
  1131. // conditional orders ---------------------------------------------
  1132. // 'stop_order_id' => $id, // one of stop_order_id or order_link_id is required for conditional orders
  1133. );
  1134. $marketTypes = $this->safe_value($this->options, 'marketTypes', array());
  1135. $marketType = $this->safe_string($marketTypes, $symbol);
  1136. $method = ($marketType === 'linear') ? 'privateLinearGetOrderSearch' : 'privateGetOrder';
  1137. $stopOrderId = $this->safe_string($params, 'stop_order_id');
  1138. if ($stopOrderId === null) {
  1139. $orderLinkId = $this->safe_string($params, 'order_link_id');
  1140. if ($orderLinkId === null) {
  1141. $request['order_id'] = $id;
  1142. }
  1143. } else {
  1144. $method = ($marketType === 'linear') ? 'privateLinearGetStopOrderSearch' : 'privateGetStopOrder';
  1145. }
  1146. $response = $this->$method (array_merge($request, $params));
  1147. //
  1148. // {
  1149. // "ret_code" => 0,
  1150. // "ret_msg" => "OK",
  1151. // "ext_code" => "",
  1152. // "ext_info" => "",
  1153. // "$result" => array(
  1154. // "user_id" => 1,
  1155. // "$symbol" => "BTCUSD",
  1156. // "side" => "Sell",
  1157. // "order_type" => "Limit",
  1158. // "price" => "8083",
  1159. // "qty" => 10,
  1160. // "time_in_force" => "GoodTillCancel",
  1161. // "order_status" => "New",
  1162. // "ext_fields" => array( "o_req_num" => -308787, "xreq_type" => "x_create", "xreq_offset" => 4154640 ),
  1163. // "leaves_qty" => 10,
  1164. // "leaves_value" => "0.00123716",
  1165. // "cum_exec_qty" => 0,
  1166. // "reject_reason" => "",
  1167. // "order_link_id" => "",
  1168. // "created_at" => "2019-10-21T07:28:19.396246Z",
  1169. // "updated_at" => "2019-10-21T07:28:19.396246Z",
  1170. // "order_id" => "efa44157-c355-4a98-b6d6-1d846a936b93"
  1171. // ),
  1172. // "time_now" => "1571651135.291930",
  1173. // "rate_limit_status" => 99, // The remaining number of accesses in one minute
  1174. // "rate_limit_reset_ms" => 1580885703683,
  1175. // "rate_limit" => 100
  1176. // }
  1177. //
  1178. // conditional orders
  1179. //
  1180. // {
  1181. // "ret_code" => 0,
  1182. // "ret_msg" => "OK",
  1183. // "ext_code" => "",
  1184. // "ext_info" => "",
  1185. // "$result" => array(
  1186. // "user_id" => 1,
  1187. // "$symbol" => "BTCUSD",
  1188. // "side" => "Buy",
  1189. // "order_type" => "Limit",
  1190. // "price" => "8000",
  1191. // "qty" => 1,
  1192. // "time_in_force" => "GoodTillCancel",
  1193. // "order_status" => "Untriggered",
  1194. // "ext_fields" => array(),
  1195. // "leaves_qty" => 1,
  1196. // "leaves_value" => "0.00013333",
  1197. // "cum_exec_qty" => 0,
  1198. // "cum_exec_value" => null,
  1199. // "cum_exec_fee" => null,
  1200. // "reject_reason" => "",
  1201. // "order_link_id" => "",
  1202. // "created_at" => "2019-12-27T19:56:24.052194Z",
  1203. // "updated_at" => "2019-12-27T19:56:24.052194Z",
  1204. // "order_id" => "378a1bbc-a93a-4e75-87f4-502ea754ba36"
  1205. // ),
  1206. // "time_now" => "1577476584.386958",
  1207. // "rate_limit_status" => 99,
  1208. // "rate_limit_reset_ms" => 1580885703683,
  1209. // "rate_limit" => 100
  1210. // }
  1211. //
  1212. $result = $this->safe_value($response, 'result');
  1213. return $this->parse_order($result, $market);
  1214. }
  1215. public function create_order($symbol, $type, $side, $amount, $price = null, $params = array ()) {
  1216. $this->load_markets();
  1217. $market = $this->market($symbol);
  1218. $qty = $this->amount_to_precision($symbol, $amount);
  1219. if ($market['inverse']) {
  1220. $qty = intval($qty);
  1221. } else {
  1222. $qty = floatval($qty);
  1223. }
  1224. $request = array(
  1225. // orders ---------------------------------------------------------
  1226. 'side' => $this->capitalize($side),
  1227. 'symbol' => $market['id'],
  1228. 'order_type' => $this->capitalize($type),
  1229. 'qty' => $qty, // order quantity in USD, integer only
  1230. // 'price' => floatval($this->price_to_precision($symbol, $price)), // required for limit orders
  1231. 'time_in_force' => 'GoodTillCancel', // ImmediateOrCancel, FillOrKill, PostOnly
  1232. // 'take_profit' => 123.45, // take profit $price, only take effect upon opening the position
  1233. // 'stop_loss' => 123.45, // stop loss $price, only take effect upon opening the position
  1234. // 'reduce_only' => false, // reduce only
  1235. // when creating a closing order, bybit recommends a True value for
  1236. // close_on_trigger to avoid failing due to insufficient available margin
  1237. // 'close_on_trigger' => false,
  1238. // 'order_link_id' => 'string', // unique client order id, max 36 characters
  1239. // conditional orders ---------------------------------------------
  1240. // base_price is used to compare with the value of stop_px, to decide
  1241. // whether your conditional order will be triggered by crossing trigger
  1242. // $price from upper $side or lower $side, mainly used to identify the
  1243. // expected direction of the current conditional order
  1244. // 'base_price' => 123.45, // required for conditional orders
  1245. // 'stop_px' => 123.45, // trigger $price, required for conditional orders
  1246. // 'trigger_by' => 'LastPrice', // IndexPrice, MarkPrice
  1247. );
  1248. $priceIsRequired = false;
  1249. if ($type === 'limit') {
  1250. $priceIsRequired = true;
  1251. }
  1252. if ($priceIsRequired) {
  1253. if ($price !== null) {
  1254. $request['price'] = floatval($this->price_to_precision($symbol, $price));
  1255. } else {
  1256. throw new ArgumentsRequired($this->id . ' createOrder requires a $price argument for a ' . $type . ' order');
  1257. }
  1258. }
  1259. $stopPx = $this->safe_value($params, 'stop_px');
  1260. $basePrice = $this->safe_value($params, 'base_price');
  1261. $marketTypes = $this->safe_value($this->options, 'marketTypes', array());
  1262. $marketType = $this->safe_string($marketTypes, $symbol);
  1263. $method = ($marketType === 'linear') ? 'privateLinearPostOrderCreate' : 'privatePostOrderCreate';
  1264. if ($stopPx !== null) {
  1265. if ($basePrice === null) {
  1266. throw new ArgumentsRequired($this->id . ' createOrder requires both the stop_px and base_price $params for a conditional ' . $type . ' order');
  1267. } else {
  1268. $method = ($marketType === 'linear') ? 'privateLinearPostStopOrderCreate' : 'openapiPostStopOrderCreate';
  1269. $request['stop_px'] = floatval($this->price_to_precision($symbol, $stopPx));
  1270. $request['base_price'] = floatval($this->price_to_precision($symbol, $basePrice));
  1271. $params = $this->omit($params, array( 'stop_px', 'base_price' ));
  1272. }
  1273. } else if ($basePrice !== null) {
  1274. throw new ArgumentsRequired($this->id . ' createOrder requires both the stop_px and base_price $params for a conditional ' . $type . ' order');
  1275. }
  1276. $response = $this->$method (array_merge($request, $params));
  1277. //
  1278. // {
  1279. // "ret_code" => 0,
  1280. // "ret_msg" => "OK",
  1281. // "ext_code" => "",
  1282. // "ext_info" => "",
  1283. // "$result" => array(
  1284. // "user_id" => 1,
  1285. // "order_id" => "335fd977-e5a5-4781-b6d0-c772d5bfb95b",
  1286. // "$symbol" => "BTCUSD",
  1287. // "$side" => "Buy",
  1288. // "order_type" => "Limit",
  1289. // "$price" => 8800,
  1290. // "$qty" => 1,
  1291. // "time_in_force" => "GoodTillCancel",
  1292. // "order_status" => "Created",
  1293. // "last_exec_time" => 0,
  1294. // "last_exec_price" => 0,
  1295. // "leaves_qty" => 1,
  1296. // "cum_exec_qty" => 0,
  1297. // "cum_exec_value" => 0,
  1298. // "cum_exec_fee" => 0,
  1299. // "reject_reason" => "",
  1300. // "order_link_id" => "",
  1301. // "created_at" => "2019-11-30T11:03:43.452Z",
  1302. // "updated_at" => "2019-11-30T11:03:43.455Z"
  1303. // ),
  1304. // "time_now" => "1575111823.458705",
  1305. // "rate_limit_status" => 98,
  1306. // "rate_limit_reset_ms" => 1580885703683,
  1307. // "rate_limit" => 100
  1308. // }
  1309. //
  1310. // conditional orders
  1311. //
  1312. // {
  1313. // "ret_code" => 0,
  1314. // "ret_msg" => "ok",
  1315. // "ext_code" => "",
  1316. // "$result" => array(
  1317. // "user_id" => 1,
  1318. // "$symbol" => "BTCUSD",
  1319. // "$side" => "Buy",
  1320. // "order_type" => "Limit",
  1321. // "$price" => 8000,
  1322. // "$qty" => 1,
  1323. // "time_in_force" => "GoodTillCancel",
  1324. // "stop_order_type" => "Stop",
  1325. // "trigger_by" => "LastPrice",
  1326. // "base_price" => 7000,
  1327. // "order_status" => "Untriggered",
  1328. // "ext_fields" => array(
  1329. // "stop_order_type" => "Stop",
  1330. // "trigger_by" => "LastPrice",
  1331. // "base_price" => 7000,
  1332. // "expected_direction" => "Rising",
  1333. // "trigger_price" => 7500,
  1334. // "op_from" => "api",
  1335. // "remark" => "127.0.01",
  1336. // "o_req_num" => 0
  1337. // ),
  1338. // "leaves_qty" => 1,
  1339. // "leaves_value" => 0.00013333,
  1340. // "reject_reason" => null,
  1341. // "cross_seq" => -1,
  1342. // "created_at" => "2019-12-27T12:48:24.000Z",
  1343. // "updated_at" => "2019-12-27T12:48:24.000Z",
  1344. // "stop_px" => 7500,
  1345. // "stop_order_id" => "a85cd1c0-a9a4-49d3-a1bd-bab5ebe946d5"
  1346. // ),
  1347. // "ext_info" => null,
  1348. // "time_now" => "1577450904.327654",
  1349. // "rate_limit_status" => 99,
  1350. // "rate_limit_reset_ms" => 1577450904335,
  1351. // "rate_limit" => "100"
  1352. // }
  1353. //
  1354. $result = $this->safe_value($response, 'result');
  1355. return $this->parse_order($result, $market);
  1356. }
  1357. public function edit_order($id, $symbol, $type, $side, $amount = null, $price = null, $params = array ()) {
  1358. if ($symbol === null) {
  1359. throw new ArgumentsRequired($this->id . ' editOrder requires an $symbol argument');
  1360. }
  1361. $marketTypes = $this->safe_value($this->options, 'marketTypes', array());
  1362. $marketType = $this->safe_string($marketTypes, $symbol);
  1363. $this->load_markets();
  1364. $market = $this->market($symbol);
  1365. $request = array(
  1366. // 'order_id' => $id, // only for non-conditional orders
  1367. 'symbol' => $market['id'],
  1368. // 'p_r_qty' => $this->amount_to_precision($symbol, $amount), // new order quantity, optional
  1369. // 'p_r_price' $this->priceToprecision ($symbol, $price), // new order $price, optional
  1370. // ----------------------------------------------------------------
  1371. // conditional orders
  1372. // 'stop_order_id' => $id, // only for conditional orders
  1373. // 'p_r_trigger_price' => 123.45, // new trigger $price also known as stop_px
  1374. );
  1375. $method = ($marketType === 'linear') ? 'privateLinearPostOrderReplace' : 'openapiPostOrderReplace';
  1376. $stopOrderId = $this->safe_string($params, 'stop_order_id');
  1377. if ($stopOrderId !== null) {
  1378. $method = ($marketType === 'linear') ? 'privateLinearPostStopOrderReplace' : 'openapiPostStopOrderReplace';
  1379. $request['stop_order_id'] = $stopOrderId;
  1380. $params = $this->omit($params, array( 'stop_order_id' ));
  1381. } else {
  1382. $request['order_id'] = $id;
  1383. }
  1384. if ($amount !== null) {
  1385. $request['p_r_qty'] = intval($this->amount_to_precision($symbol, $amount));
  1386. }
  1387. if ($price !== null) {
  1388. $request['p_r_price'] = floatval($this->price_to_precision($symbol, $price));
  1389. }
  1390. $response = $this->$method (array_merge($request, $params));
  1391. //
  1392. // {
  1393. // "ret_code" => 0,
  1394. // "ret_msg" => "ok",
  1395. // "ext_code" => "",
  1396. // "$result" => array( "order_id" => "efa44157-c355-4a98-b6d6-1d846a936b93" ),
  1397. // "time_now" => "1539778407.210858",
  1398. // "rate_limit_status" => 99, // remaining number of accesses in one minute
  1399. // "rate_limit_reset_ms" => 1580885703683,
  1400. // "rate_limit" => 100
  1401. // }
  1402. //
  1403. // conditional orders
  1404. //
  1405. // {
  1406. // "ret_code" => 0,
  1407. // "ret_msg" => "ok",
  1408. // "ext_code" => "",
  1409. // "$result" => array( "stop_order_id" => "378a1bbc-a93a-4e75-87f4-502ea754ba36" ),
  1410. // "ext_info" => null,
  1411. // "time_now" => "1577475760.604942",
  1412. // "rate_limit_status" => 96,
  1413. // "rate_limit_reset_ms" => 1577475760612,
  1414. // "rate_limit" => "100"
  1415. // }
  1416. //
  1417. $result = $this->safe_value($response, 'result', array());
  1418. return array(
  1419. 'info' => $response,
  1420. 'id' => $this->safe_string_2($result, 'order_id', 'stop_order_id'),
  1421. 'order_id' => $this->safe_string($result, 'order_id'),
  1422. 'stop_order_id' => $this->safe_string($result, 'stop_order_id'),
  1423. );
  1424. }
  1425. public function cancel_order($id, $symbol = null, $params = array ()) {
  1426. if ($symbol === null) {
  1427. throw new ArgumentsRequired($this->id . ' cancelOrder requires a $symbol argument');
  1428. }
  1429. $this->load_markets();
  1430. $market = $this->market($symbol);
  1431. $request = array(
  1432. 'symbol' => $market['id'],
  1433. // 'order_link_id' => 'string', // one of order_id, stop_order_id or order_link_id is required
  1434. // regular orders ---------------------------------------------
  1435. // 'order_id' => $id, // one of order_id or order_link_id is required for regular orders
  1436. // conditional orders ---------------------------------------------
  1437. // 'stop_order_id' => $id, // one of stop_order_id or order_link_id is required for conditional orders
  1438. );
  1439. $marketTypes = $this->safe_value($this->options, 'marketTypes', array());
  1440. $marketType = $this->safe_value($marketTypes, $symbol);
  1441. $method = ($marketType === 'linear') ? 'privateLinearPostOrderCancel' : 'privatePostOrderCancel';
  1442. $stopOrderId = $this->safe_string($params, 'stop_order_id');
  1443. if ($stopOrderId === null) {
  1444. $orderLinkId = $this->safe_string($params, 'order_link_id');
  1445. if ($orderLinkId === null) {
  1446. $request['order_id'] = $id;
  1447. }
  1448. } else {
  1449. $method = ($marketType === 'linear') ? 'privateLinearPostStopOrderCancel' : 'openapiPostStopOrderCancel';
  1450. }
  1451. $response = $this->$method (array_merge($request, $params));
  1452. $result = $this->safe_value($response, 'result', array());
  1453. return $this->parse_order($result, $market);
  1454. }
  1455. public function cancel_all_orders($symbol = null, $params = array ()) {
  1456. if ($symbol === null) {
  1457. throw new ArgumentsRequired($this->id . ' cancelAllOrders requires a $symbol argument');
  1458. }
  1459. $this->load_markets();
  1460. $market = $this->market($symbol);
  1461. $request = array(
  1462. 'symbol' => $market['id'],
  1463. );
  1464. $options = $this->safe_value($this->options, 'cancelAllOrders');
  1465. $marketTypes = $this->safe_value($this->options, 'marketTypes', array());
  1466. $marketType = $this->safe_string($marketTypes, $symbol);
  1467. $defaultMethod = ($marketType === 'linear') ? 'privateLinearPostOrderCancelAll' : 'privatePostOrderCancelAll';
  1468. $method = $this->safe_string($options, 'method', $defaultMethod);
  1469. $response = $this->$method (array_merge($request, $params));
  1470. $result = $this->safe_value($response, 'result', array());
  1471. return $this->parse_orders($result, $market);
  1472. }
  1473. public function fetch_orders($symbol = null, $since = null, $limit = null, $params = array ()) {
  1474. $this->load_markets();
  1475. $request = array(
  1476. // 'order_id' => 'string'
  1477. // 'order_link_id' => 'string', // unique client order id, max 36 characters
  1478. // 'symbol' => $market['id'], // default BTCUSD
  1479. // 'order' => 'desc', // asc
  1480. // 'page' => 1,
  1481. // 'limit' => 20, // max 50
  1482. // 'order_status' => 'Created,New'
  1483. // conditional orders ---------------------------------------------
  1484. // 'stop_order_id' => 'string',
  1485. // 'stop_order_status' => 'Untriggered',
  1486. );
  1487. $market = null;
  1488. if ($symbol !== null) {
  1489. $market = $this->market($symbol);
  1490. $request['symbol'] = $market['id'];
  1491. }
  1492. if ($limit !== null) {
  1493. $request['limit'] = $limit;
  1494. }
  1495. $options = $this->safe_value($this->options, 'fetchOrders', array());
  1496. $marketTypes = $this->safe_value($this->options, 'marketTypes', array());
  1497. $marketType = $this->safe_string($marketTypes, $symbol);
  1498. $defaultMethod = ($marketType === 'linear') ? 'privateLinearGetOrderList' : 'openapiGetOrderList';
  1499. $query = $params;
  1500. if ((is_array($params) && array_key_exists('stop_order_id', $params)) || (is_array($params) && array_key_exists('stop_order_status', $params))) {
  1501. $stopOrderStatus = $this->safe_value($params, 'stopOrderStatus');
  1502. if ($stopOrderStatus !== null) {
  1503. if (gettype($stopOrderStatus) === 'array' && count(array_filter(array_keys($stopOrderStatus), 'is_string')) == 0) {
  1504. $stopOrderStatus = implode(',', $stopOrderStatus);
  1505. }
  1506. $request['stop_order_status'] = $stopOrderStatus;
  1507. $query = $this->omit($params, 'stop_order_status');
  1508. }
  1509. $defaultMethod = ($marketType === 'linear') ? 'privateLinearGetStopOrderList' : 'openapiGetStopOrderList';
  1510. }
  1511. $method = $this->safe_string($options, 'method', $defaultMethod);
  1512. $response = $this->$method (array_merge($request, $query));
  1513. //
  1514. // {
  1515. // "ret_code" => 0,
  1516. // "ret_msg" => "ok",
  1517. // "ext_code" => "",
  1518. // "$result" => {
  1519. // "current_page" => 1,
  1520. // "last_page" => 6,
  1521. // "$data" => array(
  1522. // array(
  1523. // "user_id" => 1,
  1524. // "$symbol" => "BTCUSD",
  1525. // "side" => "Sell",
  1526. // "order_type" => "Market",
  1527. // "price" => 7074,
  1528. // "qty" => 2,
  1529. // "time_in_force" => "ImmediateOrCancel",
  1530. // "order_status" => "Filled",
  1531. // "ext_fields" => array(
  1532. // "close_on_trigger" => true,
  1533. // "orig_order_type" => "BLimit",
  1534. // "prior_x_req_price" => 5898.5,
  1535. // "op_from" => "pc",
  1536. // "remark" => "127.0.0.1",
  1537. // "o_req_num" => -34799032763,
  1538. // "xreq_type" => "x_create"
  1539. // ),
  1540. // "last_exec_time" => "1577448481.696421",
  1541. // "last_exec_price" => 7070.5,
  1542. // "leaves_qty" => 0,
  1543. // "leaves_value" => 0,
  1544. // "cum_exec_qty" => 2,
  1545. // "cum_exec_value" => 0.00028283,
  1546. // "cum_exec_fee" => 0.00002,
  1547. // "reject_reason" => "NoError",
  1548. // "order_link_id" => "",
  1549. // "created_at" => "2019-12-27T12:08:01.000Z",
  1550. // "updated_at" => "2019-12-27T12:08:01.000Z",
  1551. // "order_id" => "f185806b-b801-40ff-adec-52289370ed62"
  1552. // }
  1553. // )
  1554. // ),
  1555. // "ext_info" => null,
  1556. // "time_now" => "1577448922.437871",
  1557. // "rate_limit_status" => 98,
  1558. // "rate_limit_reset_ms" => 1580885703683,
  1559. // "rate_limit" => 100
  1560. // }
  1561. //
  1562. // conditional orders
  1563. //
  1564. // {
  1565. // "ret_code" => 0,
  1566. // "ret_msg" => "ok",
  1567. // "ext_code" => "",
  1568. // "$result" => array(
  1569. // "current_page" => 1,
  1570. // "last_page" => 1,
  1571. // "$data" => array(
  1572. // array(
  1573. // "user_id" => 1,
  1574. // "stop_order_status" => "Untriggered",
  1575. // "$symbol" => "BTCUSD",
  1576. // "side" => "Buy",
  1577. // "order_type" => "Limit",
  1578. // "price" => 8000,
  1579. // "qty" => 1,
  1580. // "time_in_force" => "GoodTillCancel",
  1581. // "stop_order_type" => "Stop",
  1582. // "trigger_by" => "LastPrice",
  1583. // "base_price" => 7000,
  1584. // "order_link_id" => "",
  1585. // "created_at" => "2019-12-27T12:48:24.000Z",
  1586. // "updated_at" => "2019-12-27T12:48:24.000Z",
  1587. // "stop_px" => 7500,
  1588. // "stop_order_id" => "a85cd1c0-a9a4-49d3-a1bd-bab5ebe946d5"
  1589. // ),
  1590. // )
  1591. // ),
  1592. // "ext_info" => null,
  1593. // "time_now" => "1577451658.755468",
  1594. // "rate_limit_status" => 599,
  1595. // "rate_limit_reset_ms" => 1577451658762,
  1596. // "rate_limit" => 600
  1597. // }
  1598. //
  1599. $result = $this->safe_value($response, 'result', array());
  1600. $data = $this->safe_value($result, 'data', array());
  1601. return $this->parse_orders($data, $market, $since, $limit);
  1602. }
  1603. public function fetch_closed_orders($symbol = null, $since = null, $limit = null, $params = array ()) {
  1604. $defaultStatuses = array(
  1605. 'Rejected',
  1606. 'Filled',
  1607. 'Cancelled',
  1608. // conditional orders
  1609. // 'Active',
  1610. // 'Triggered',
  1611. // 'Cancelled',
  1612. // 'Rejected',
  1613. // 'Deactivated',
  1614. );
  1615. $options = $this->safe_value($this->options, 'fetchClosedOrders', array());
  1616. $status = $this->safe_value($options, 'order_status', $defaultStatuses);
  1617. if (gettype($status) === 'array' && count(array_filter(array_keys($status), 'is_string')) == 0) {
  1618. $status = implode(',', $status);
  1619. }
  1620. $request = array();
  1621. $stopOrderStatus = $this->safe_value($params, 'stop_order_status');
  1622. if ($stopOrderStatus === null) {
  1623. $request['order_status'] = $status;
  1624. } else {
  1625. $request['stop_order_status'] = $stopOrderStatus;
  1626. }
  1627. return $this->fetch_orders($symbol, $since, $limit, array_merge($request, $params));
  1628. }
  1629. public function fetch_open_orders($symbol = null, $since = null, $limit = null, $params = array ()) {
  1630. $defaultStatuses = array(
  1631. 'Created',
  1632. 'New',
  1633. 'PartiallyFilled',
  1634. 'PendingCancel',
  1635. // conditional orders
  1636. // 'Untriggered',
  1637. );
  1638. $options = $this->safe_value($this->options, 'fetchOpenOrders', array());
  1639. $status = $this->safe_value($options, 'order_status', $defaultStatuses);
  1640. if (gettype($status) === 'array' && count(array_filter(array_keys($status), 'is_string')) == 0) {
  1641. $status = implode(',', $status);
  1642. }
  1643. $request = array();
  1644. $stopOrderStatus = $this->safe_value($params, 'stop_order_status');
  1645. if ($stopOrderStatus === null) {
  1646. $request['order_status'] = $status;
  1647. } else {
  1648. $request['stop_order_status'] = $stopOrderStatus;
  1649. }
  1650. return $this->fetch_orders($symbol, $since, $limit, array_merge($request, $params));
  1651. }
  1652. public function fetch_order_trades($id, $symbol = null, $since = null, $limit = null, $params = array ()) {
  1653. $request = array(
  1654. 'order_id' => $id,
  1655. );
  1656. return $this->fetch_my_trades($symbol, $since, $limit, array_merge($request, $params));
  1657. }
  1658. public function fetch_my_trades($symbol = null, $since = null, $limit = null, $params = array ()) {
  1659. $this->load_markets();
  1660. $request = array(
  1661. // 'order_id' => 'f185806b-b801-40ff-adec-52289370ed62', // if not provided will return user's trading records
  1662. // 'symbol' => $market['id'],
  1663. // 'start_time' => intval($since / 1000),
  1664. // 'page' => 1,
  1665. // 'limit' 20, // max 50
  1666. );
  1667. $market = null;
  1668. if ($symbol === null) {
  1669. $orderId = $this->safe_string($params, 'order_id');
  1670. if ($orderId === null) {
  1671. throw new ArgumentsRequired($this->id . ' fetchMyTrades requires a $symbol argument or an order_id param');
  1672. } else {
  1673. $request['order_id'] = $orderId;
  1674. $params = $this->omit($params, 'order_id');
  1675. }
  1676. } else {
  1677. $market = $this->market($symbol);
  1678. $request['symbol'] = $market['id'];
  1679. }
  1680. if ($since !== null) {
  1681. $request['start_time'] = $since;
  1682. }
  1683. if ($limit !== null) {
  1684. $request['limit'] = $limit; // default 20, max 50
  1685. }
  1686. $marketTypes = $this->safe_value($this->options, 'marketTypes', array());
  1687. $marketType = $this->safe_string($marketTypes, $symbol);
  1688. $method = ($marketType === 'linear') ? 'privateLinearGetTradeExecutionList' : 'privateGetExecutionList';
  1689. $response = $this->$method (array_merge($request, $params));
  1690. //
  1691. // inverse
  1692. //
  1693. // {
  1694. // "ret_code" => 0,
  1695. // "ret_msg" => "OK",
  1696. // "ext_code" => "",
  1697. // "ext_info" => "",
  1698. // "$result" => {
  1699. // "order_id" => "Abandoned!!", // Abandoned!!
  1700. // "trade_list" => array(
  1701. // array(
  1702. // "closed_size" => 0,
  1703. // "cross_seq" => 277136382,
  1704. // "exec_fee" => "0.0000001",
  1705. // "exec_id" => "256e5ef8-abfe-5772-971b-f944e15e0d68",
  1706. // "exec_price" => "8178.5",
  1707. // "exec_qty" => 1,
  1708. // "exec_time" => "1571676941.70682",
  1709. // "exec_type" => "Trade", //Exec Type Enum
  1710. // "exec_value" => "0.00012227",
  1711. // "fee_rate" => "0.00075",
  1712. // "last_liquidity_ind" => "RemovedLiquidity", //Liquidity Enum
  1713. // "leaves_qty" => 0,
  1714. // "nth_fill" => 2,
  1715. // "order_id" => "7ad50cb1-9ad0-4f74-804b-d82a516e1029",
  1716. // "order_link_id" => "",
  1717. // "order_price" => "8178",
  1718. // "order_qty" => 1,
  1719. // "order_type" => "Market", //Order Type Enum
  1720. // "side" => "Buy", //Side Enum
  1721. // "$symbol" => "BTCUSD", //Symbol Enum
  1722. // "user_id" => 1
  1723. // }
  1724. // )
  1725. // ),
  1726. // "time_now" => "1577483699.281488",
  1727. // "rate_limit_status" => 118,
  1728. // "rate_limit_reset_ms" => 1577483699244737,
  1729. // "rate_limit" => 120
  1730. // }
  1731. //
  1732. // linear
  1733. //
  1734. // {
  1735. // "ret_code":0,
  1736. // "ret_msg":"OK",
  1737. // "ext_code":"",
  1738. // "ext_info":"",
  1739. // "$result":{
  1740. // "current_page":1,
  1741. // "data":array(
  1742. // array(
  1743. // "order_id":"b59418ec-14d4-4ef9-b9f4-721d5d576974",
  1744. // "order_link_id":"",
  1745. // "side":"Sell",
  1746. // "$symbol":"BTCUSDT",
  1747. // "exec_id":"0327284d-faec-5191-bd89-acc5b4fafda9",
  1748. // "price":0.5,
  1749. // "order_price":0.5,
  1750. // "order_qty":0.01,
  1751. // "order_type":"Market",
  1752. // "fee_rate":0.00075,
  1753. // "exec_price":9709.5,
  1754. // "exec_type":"Trade",
  1755. // "exec_qty":0.01,
  1756. // "exec_fee":0.07282125,
  1757. // "exec_value":97.095,
  1758. // "leaves_qty":0,
  1759. // "closed_size":0.01,
  1760. // "last_liquidity_ind":"RemovedLiquidity",
  1761. // "trade_time":1591648052,
  1762. // "trade_time_ms":1591648052861
  1763. // }
  1764. // )
  1765. // ),
  1766. // "time_now":"1591736501.979264",
  1767. // "rate_limit_status":119,
  1768. // "rate_limit_reset_ms":1591736501974,
  1769. // "rate_limit":120
  1770. // }
  1771. //
  1772. $result = $this->safe_value($response, 'result', array());
  1773. $trades = $this->safe_value_2($result, 'trade_list', 'data', array());
  1774. return $this->parse_trades($trades, $market, $since, $limit);
  1775. }
  1776. public function fetch_deposits($code = null, $since = null, $limit = null, $params = array ()) {
  1777. if ($code === null) {
  1778. throw new ArgumentsRequired($this->id . ' fetchWithdrawals() requires a $currency $code argument');
  1779. }
  1780. $this->load_markets();
  1781. $currency = $this->currency($code);
  1782. $request = array(
  1783. 'currency' => $currency['id'],
  1784. );
  1785. if ($limit !== null) {
  1786. $request['count'] = $limit;
  1787. }
  1788. $response = $this->privateGetGetDeposits (array_merge($request, $params));
  1789. //
  1790. // {
  1791. // "jsonrpc" => "2.0",
  1792. // "id" => 5611,
  1793. // "$result" => {
  1794. // "count" => 1,
  1795. // "$data" => array(
  1796. // {
  1797. // "address" => "2N35qDKDY22zmJq9eSyiAerMD4enJ1xx6ax",
  1798. // "amount" => 5,
  1799. // "$currency" => "BTC",
  1800. // "received_timestamp" => 1549295017670,
  1801. // "state" => "completed",
  1802. // "transaction_id" => "230669110fdaf0a0dbcdc079b6b8b43d5af29cc73683835b9bc6b3406c065fda",
  1803. // "updated_timestamp" => 1549295130159
  1804. // }
  1805. // )
  1806. // }
  1807. // }
  1808. //
  1809. $result = $this->safe_value($response, 'result', array());
  1810. $data = $this->safe_value($result, 'data', array());
  1811. return $this->parse_transactions($data, $currency, $since, $limit, $params);
  1812. }
  1813. public function fetch_withdrawals($code = null, $since = null, $limit = null, $params = array ()) {
  1814. $this->load_markets();
  1815. $request = array(
  1816. // 'coin' => $currency['id'],
  1817. // 'start_date' => $this->iso8601($since),
  1818. // 'end_date' => $this->iso8601(till),
  1819. // 'status' => 'Pending', // ToBeConfirmed, UnderReview, Pending, Success, CancelByUser, Reject, Expire
  1820. // 'page' => 1,
  1821. // 'limit' => 20, // max 50
  1822. );
  1823. $currency = null;
  1824. if ($code !== null) {
  1825. $currency = $this->currency($code);
  1826. $request['coin'] = $currency['id'];
  1827. }
  1828. if ($since !== null) {
  1829. $request['start_date'] = $this->iso8601($since);
  1830. }
  1831. if ($limit !== null) {
  1832. $request['limit'] = $limit;
  1833. }
  1834. $response = $this->openapiGetWalletWithdrawList (array_merge($request, $params));
  1835. //
  1836. // {
  1837. // "ret_code" => 0,
  1838. // "ret_msg" => "ok",
  1839. // "ext_code" => "",
  1840. // "$result" => array(
  1841. // "$data" => array(
  1842. // array(
  1843. // "id" => 137,
  1844. // "user_id" => 1,
  1845. // "coin" => "XRP", // Coin Enum
  1846. // "status" => "Pending", // Withdraw Status Enum
  1847. // "amount" => "20.00000000",
  1848. // "fee" => "0.25000000",
  1849. // "address" => "rH7H595XYEVTEHU2FySYsWnmfACBnZS9zM",
  1850. // "tx_id" => "",
  1851. // "submited_at" => "2019-06-11T02:20:24.000Z",
  1852. // "updated_at" => "2019-06-11T02:20:24.000Z"
  1853. // ),
  1854. // ),
  1855. // "current_page" => 1,
  1856. // "last_page" => 1
  1857. // ),
  1858. // "ext_info" => null,
  1859. // "time_now" => "1577482295.125488",
  1860. // "rate_limit_status" => 119,
  1861. // "rate_limit_reset_ms" => 1577482295132,
  1862. // "rate_limit" => 120
  1863. // }
  1864. //
  1865. $result = $this->safe_value($response, 'result', array());
  1866. $data = $this->safe_value($result, 'data', array());
  1867. return $this->parse_transactions($data, $currency, $since, $limit, $params);
  1868. }
  1869. public function parse_transaction_status($status) {
  1870. $statuses = array(
  1871. 'ToBeConfirmed' => 'pending',
  1872. 'UnderReview' => 'pending',
  1873. 'Pending' => 'pending',
  1874. 'Success' => 'ok',
  1875. 'CancelByUser' => 'canceled',
  1876. 'Reject' => 'rejected',
  1877. 'Expire' => 'expired',
  1878. );
  1879. return $this->safe_string($statuses, $status, $status);
  1880. }
  1881. public function parse_transaction($transaction, $currency = null) {
  1882. //
  1883. // fetchWithdrawals
  1884. //
  1885. // {
  1886. // "id" => 137,
  1887. // "user_id" => 1,
  1888. // "coin" => "XRP", // Coin Enum
  1889. // "$status" => "Pending", // Withdraw Status Enum
  1890. // "amount" => "20.00000000",
  1891. // "$fee" => "0.25000000",
  1892. // "$address" => "rH7H595XYEVTEHU2FySYsWnmfACBnZS9zM",
  1893. // "tx_id" => "",
  1894. // "submited_at" => "2019-06-11T02:20:24.000Z",
  1895. // "updated_at" => "2019-06-11T02:20:24.000Z"
  1896. // }
  1897. //
  1898. $currencyId = $this->safe_string($transaction, 'coin');
  1899. $code = $this->safe_currency_code($currencyId, $currency);
  1900. $timestamp = $this->parse8601($this->safe_string($transaction, 'submited_at'));
  1901. $updated = $this->parse8601($this->safe_string($transaction, 'updated_at'));
  1902. $status = $this->parse_transaction_status($this->safe_string($transaction, 'status'));
  1903. $address = $this->safe_string($transaction, 'address');
  1904. $feeCost = $this->safe_float($transaction, 'fee');
  1905. $fee = null;
  1906. if ($feeCost !== null) {
  1907. $fee = array(
  1908. 'cost' => $feeCost,
  1909. 'currency' => $code,
  1910. );
  1911. }
  1912. return array(
  1913. 'info' => $transaction,
  1914. 'id' => $this->safe_string($transaction, 'id'),
  1915. 'txid' => $this->safe_string($transaction, 'tx_id'),
  1916. 'timestamp' => $timestamp,
  1917. 'datetime' => $this->iso8601($timestamp),
  1918. 'address' => $address,
  1919. 'addressTo' => null,
  1920. 'addressFrom' => null,
  1921. 'tag' => null,
  1922. 'tagTo' => null,
  1923. 'tagFrom' => null,
  1924. 'type' => 'withdrawal',
  1925. 'amount' => $this->safe_float($transaction, 'amount'),
  1926. 'currency' => $code,
  1927. 'status' => $status,
  1928. 'updated' => $updated,
  1929. 'fee' => $fee,
  1930. );
  1931. }
  1932. public function fetch_ledger($code = null, $since = null, $limit = null, $params = array ()) {
  1933. $this->load_markets();
  1934. $request = array(
  1935. // 'coin' => $currency['id'],
  1936. // 'currency' => $currency['id'], // alias
  1937. // 'start_date' => $this->iso8601($since),
  1938. // 'end_date' => $this->iso8601(till),
  1939. // 'wallet_fund_type' => 'Deposit', // Withdraw, RealisedPNL, Commission, Refund, Prize, ExchangeOrderWithdraw, ExchangeOrderDeposit
  1940. // 'page' => 1,
  1941. // 'limit' => 20, // max 50
  1942. );
  1943. $currency = null;
  1944. if ($code !== null) {
  1945. $currency = $this->currency($code);
  1946. $request['coin'] = $currency['id'];
  1947. }
  1948. if ($since !== null) {
  1949. $request['start_date'] = $this->iso8601($since);
  1950. }
  1951. if ($limit !== null) {
  1952. $request['limit'] = $limit;
  1953. }
  1954. $response = $this->openapiGetWalletFundRecords (array_merge($request, $params));
  1955. //
  1956. // {
  1957. // "ret_code" => 0,
  1958. // "ret_msg" => "ok",
  1959. // "ext_code" => "",
  1960. // "$result" => {
  1961. // "$data" => array(
  1962. // array(
  1963. // "id" => 234467,
  1964. // "user_id" => 1,
  1965. // "coin" => "BTC",
  1966. // "wallet_id" => 27913,
  1967. // "type" => "Realized P&L",
  1968. // "amount" => "-0.00000006",
  1969. // "tx_id" => "",
  1970. // "address" => "BTCUSD",
  1971. // "wallet_balance" => "0.03000330",
  1972. // "exec_time" => "2019-12-09T00:00:25.000Z",
  1973. // "cross_seq" => 0
  1974. // }
  1975. // )
  1976. // ),
  1977. // "ext_info" => null,
  1978. // "time_now" => "1577481867.115552",
  1979. // "rate_limit_status" => 119,
  1980. // "rate_limit_reset_ms" => 1577481867122,
  1981. // "rate_limit" => 120
  1982. // }
  1983. //
  1984. $result = $this->safe_value($response, 'result', array());
  1985. $data = $this->safe_value($result, 'data', array());
  1986. return $this->parse_ledger($data, $currency, $since, $limit);
  1987. }
  1988. public function parse_ledger_entry($item, $currency = null) {
  1989. //
  1990. // {
  1991. // "$id" => 234467,
  1992. // "user_id" => 1,
  1993. // "coin" => "BTC",
  1994. // "wallet_id" => 27913,
  1995. // "$type" => "Realized P&L",
  1996. // "$amount" => "-0.00000006",
  1997. // "tx_id" => "",
  1998. // "address" => "BTCUSD",
  1999. // "wallet_balance" => "0.03000330",
  2000. // "exec_time" => "2019-12-09T00:00:25.000Z",
  2001. // "cross_seq" => 0
  2002. // }
  2003. //
  2004. $currencyId = $this->safe_string($item, 'coin');
  2005. $code = $this->safe_currency_code($currencyId, $currency);
  2006. $amount = $this->safe_float($item, 'amount');
  2007. $after = $this->safe_float($item, 'wallet_balance');
  2008. $direction = ($amount < 0) ? 'out' : 'in';
  2009. $before = null;
  2010. if ($after !== null && $amount !== null) {
  2011. $difference = ($direction === 'out') ? $amount : -$amount;
  2012. $before = $this->sum($after, $difference);
  2013. }
  2014. $timestamp = $this->parse8601($this->safe_string($item, 'exec_time'));
  2015. $type = $this->parse_ledger_entry_type($this->safe_string($item, 'type'));
  2016. $id = $this->safe_string($item, 'id');
  2017. $referenceId = $this->safe_string($item, 'tx_id');
  2018. return array(
  2019. 'id' => $id,
  2020. 'currency' => $code,
  2021. 'account' => $this->safe_string($item, 'wallet_id'),
  2022. 'referenceAccount' => null,
  2023. 'referenceId' => $referenceId,
  2024. 'status' => null,
  2025. 'amount' => $amount,
  2026. 'before' => $before,
  2027. 'after' => $after,
  2028. 'fee' => null,
  2029. 'direction' => $direction,
  2030. 'timestamp' => $timestamp,
  2031. 'datetime' => $this->iso8601($timestamp),
  2032. 'type' => $type,
  2033. 'info' => $item,
  2034. );
  2035. }
  2036. public function parse_ledger_entry_type($type) {
  2037. $types = array(
  2038. 'Deposit' => 'transaction',
  2039. 'Withdraw' => 'transaction',
  2040. 'RealisedPNL' => 'trade',
  2041. 'Commission' => 'fee',
  2042. 'Refund' => 'cashback',
  2043. 'Prize' => 'prize', // ?
  2044. 'ExchangeOrderWithdraw' => 'transaction',
  2045. 'ExchangeOrderDeposit' => 'transaction',
  2046. );
  2047. return $this->safe_string($types, $type, $type);
  2048. }
  2049. public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
  2050. $url = $this->urls['api'];
  2051. $request = $path;
  2052. // public v2
  2053. if ($api === 'public') {
  2054. $request = '/' . $this->version . '/' . $api . '/' . $request;
  2055. if ($params) {
  2056. $request .= '?' . $this->rawencode($params);
  2057. }
  2058. } else if ($api === 'publicLinear') {
  2059. $request = '/public/linear/' . $request;
  2060. if ($params) {
  2061. $request .= '?' . $this->rawencode($params);
  2062. }
  2063. } else {
  2064. $this->check_required_credentials();
  2065. if ($api === 'openapi') {
  2066. $request = '/open-api/' . $request;
  2067. } else if ($api === 'private') {
  2068. // private v2
  2069. $request = '/' . $this->version . '/' . $api . '/' . $request;
  2070. } else if ($api === 'privateLinear') {
  2071. $request = '/private/linear/' . $request;
  2072. } else {
  2073. // position, user
  2074. $request = '/' . $api . '/' . $request;
  2075. }
  2076. $timestamp = $this->nonce();
  2077. $query = array_merge($params, array(
  2078. 'api_key' => $this->apiKey,
  2079. 'recv_window' => $this->options['recvWindow'],
  2080. 'timestamp' => $timestamp,
  2081. ));
  2082. $auth = $this->rawencode($this->keysort($query));
  2083. $signature = $this->hmac($this->encode($auth), $this->encode($this->secret));
  2084. if ($method === 'POST') {
  2085. $body = $this->json(array_merge($query, array(
  2086. 'sign' => $signature,
  2087. )));
  2088. $headers = array(
  2089. 'Content-Type' => 'application/json',
  2090. );
  2091. } else {
  2092. $request .= '?' . $auth . '&sign=' . $signature;
  2093. }
  2094. }
  2095. $url .= $request;
  2096. return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
  2097. }
  2098. public function handle_errors($httpCode, $reason, $url, $method, $headers, $body, $response, $requestHeaders, $requestBody) {
  2099. if (!$response) {
  2100. return; // fallback to default error handler
  2101. }
  2102. //
  2103. // {
  2104. // ret_code => 10001,
  2105. // ret_msg => 'ReadMapCB => expect { or n, but found \u0000, error ' +
  2106. // 'found in #0 byte of ...||..., bigger context ' +
  2107. // '...||...',
  2108. // ext_code => '',
  2109. // ext_info => '',
  2110. // result => null,
  2111. // time_now => '1583934106.590436'
  2112. // }
  2113. //
  2114. $errorCode = $this->safe_value($response, 'ret_code');
  2115. if ($errorCode !== 0) {
  2116. $feedback = $this->id . ' ' . $body;
  2117. $this->throw_exactly_matched_exception($this->exceptions['exact'], $errorCode, $feedback);
  2118. $this->throw_broadly_matched_exception($this->exceptions['broad'], $body, $feedback);
  2119. throw new ExchangeError($feedback); // unknown message
  2120. }
  2121. }
  2122. }