From 3904db9aee3d44ec12e8191439fa4ed967332df8 Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Sun, 7 Dec 2025 20:37:30 +0300 Subject: [PATCH 01/15] feat(futuresOrder): algo orders support added --- php-binance-api.php | 54 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/php-binance-api.php b/php-binance-api.php index 8b2215b..2232737 100755 --- a/php-binance-api.php +++ b/php-binance-api.php @@ -4721,7 +4721,7 @@ protected function createFuturesOrderRequest(string $side, string $symbol, $quan echo "warning: price expected string got " . gettype($price) . PHP_EOL; } - if ($type === "LIMIT" || $type === "STOP_LOSS_LIMIT" || $type === "TAKE_PROFIT_LIMIT") { + if ($type === "LIMIT" || $type === "STOP_LOSS_LIMIT" || $type === "TAKE_PROFIT_LIMIT" || $type === "STOP" || $type === "TAKE_PROFIT") { $request["price"] = $price; if (!isset($params['timeInForce'])) { $request['timeInForce'] = 'GTC'; @@ -4804,8 +4804,56 @@ protected function createFuturesOrderRequest(string $side, string $symbol, $quan public function futuresOrder(string $side, string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = [], $test = false) { $request = $this->createFuturesOrderRequest($side, $symbol, $quantity, $price, $type, $params); - $qstring = ($test === false) ? 'v1/order' : 'v1/order/test'; - return $this->fapiRequest($qstring, 'POST', $request, true); + $isAlgoOrder = $this->isAlgoFuturesOrderType($request['type']); + if ($isAlgoOrder) { + $algoRequest = $this->createAlgoFuturesOrderRequest($request); + return $this->fapiRequest('v1/algoOrder', 'POST', $algoRequest, true); + } else { + // regular order + $qstring = ($test === false) ? 'v1/order' : 'v1/order/test'; + return $this->fapiRequest($qstring, 'POST', $request, true); + } + } + + /** + * isAlgoFuturesOrderType helper to determine if an order type is an algo order + * @param string $type order type + * @return bool true if it is an algo order type + */ + protected function isAlgoFuturesOrderType(string $type): bool + { + $algoOrderTypes = [ + 'STOP', + 'STOP_MARKET', + 'TAKE_PROFIT', + 'TAKE_PROFIT_MARKET', + 'TRAILING_STOP_MARKET', + ]; + if (in_array($type, $algoOrderTypes)) { + return true; + } + return false; + } + + /** + * createAlgoFuturesOrderRequest + * helper for creating the request for algo futures order + * @param array $request regular order request + * @return array containing the request + */ + protected function createAlgoFuturesOrderRequest(array $request): array + { + $request['algoType'] = 'CONDITIONAL'; + if (!isset($request['clientAlgoId'])) { + $newClientOrderId = $request['newClientOrderId']; + $request['clientAlgoId'] = $newClientOrderId; + unset($request['newClientOrderId']); + } + if (!isset($request['triggerPrice']) && isset($request['stopPrice'])) { + $request['triggerPrice'] = $request['stopPrice']; + unset($request['stopPrice']); + } + return $request; } /** From 5c5992f9e8c8b332db859e425772b566c394801d Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Sun, 7 Dec 2025 20:39:20 +0300 Subject: [PATCH 02/15] test: testFuturesAlgoOrder added --- tests/BinanceStaticTests.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/BinanceStaticTests.php b/tests/BinanceStaticTests.php index c30395d..2e98f93 100644 --- a/tests/BinanceStaticTests.php +++ b/tests/BinanceStaticTests.php @@ -1885,6 +1885,30 @@ public function testFuturesOrder() $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); } + public function testFuturesAlgoOrder() + { + try { + $this->binance->futuresOrder('BUY', 'BTCUSDT', 1, 1000, 'STOP', [ + 'stopPrice' => 900, + ]); + } + catch(\Throwable $e) { + print_r($e->getMessage()); + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/algoOrder", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals("BTCUSDT", $params['symbol']); + $this->assertEquals("BUY", $params['side']); + $this->assertEquals("STOP", $params['type']); + $this->assertEquals(1, $params['quantity']); + $this->assertEquals(1000, $params['price']); + $this->assertEquals(900, $params['triggerPrice']); + $this->assertEquals("CONDITIONAL", $params['algoType']); + $this->assertTrue(str_starts_with($params['clientAlgoId'], $this->CONTRACT_ORDER_PREFIX)); + } + public function testFuturesBuy() { try { From 63677cfd7d9c71c6ae6cf5f3cc2c75897f5db55a Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:07:40 +0300 Subject: [PATCH 03/15] feat: futuresAlgoOrderStatus added --- php-binance-api.php | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/php-binance-api.php b/php-binance-api.php index 2232737..c86093e 100755 --- a/php-binance-api.php +++ b/php-binance-api.php @@ -5288,7 +5288,7 @@ public function futuresCountdownCancelAllOrders(string $symbol, int $countdownTi * @return array with error message or the order details * @throws \Exception */ - public function futuresOrderStatus(string $symbol, $orderId = null, $origClientOrderId = null, array $params = []) + public function futuresOrderStatus(string $symbol, $orderId = null, $origClientOrderId = null, array $params = [], ) { $request = [ 'symbol' => $symbol, @@ -5304,6 +5304,35 @@ public function futuresOrderStatus(string $symbol, $orderId = null, $origClientO return $this->fapiRequest("v1/order", 'GET', array_merge($request, $params), true); } + /** + * futuresAlgoOrderStatus gets the details of a futures algo order + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Algo-Order + * + * $order = $api->futuresAlgoOrderStatus("123456789"); + * + * @param string $algoId (optional) order id to get the response for (mandatory if clientAlgoId is not set) + * @param string $clientAlgoId (optional) original client order id to get the response for (mandatory if algoId is not set) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the order details + * @throws \Exception + */ + public function futuresAlgoOrderStatus($algoId = null, $clientAlgoId = null, array $params = []) + { + $request = []; + if ($algoId) { + $request['algoId'] = $algoId; + } else if ($clientAlgoId) { + $request['clientAlgoId'] = $clientAlgoId; + } else { + throw new \Exception('futuresAlgoOrderStatus(): either algoId or clientAlgoId must be set'); + } + + return $this->fapiRequest("v1/algoOrder", 'GET', array_merge($request, $params), true); + } + /** * futuresAllOrders gets all orders for a symbol * query time period must be less then 7 days (default as the recent 7 days) From 7645bc96d2dcb1476c20352cb8db7ae5ccbb3aae Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:08:30 +0300 Subject: [PATCH 04/15] test: futuresAlgoOrderStatus testing added --- tests/BinanceStaticTests.php | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/BinanceStaticTests.php b/tests/BinanceStaticTests.php index 2e98f93..2d35798 100644 --- a/tests/BinanceStaticTests.php +++ b/tests/BinanceStaticTests.php @@ -2226,6 +2226,25 @@ public function testFuturesOrderStatusByOrderId() } + public function testFuturesAlgoOrderStatusByOrderId() + { + try { + $this->binance->futuresAlgoOrderStatus($this->orderId, null, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/algoOrder?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->orderId, $params['algoId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + public function testFuturesOrderStatusByClientOrderId() { try { @@ -2246,6 +2265,25 @@ public function testFuturesOrderStatusByClientOrderId() } + public function testFuturesAlgoOrderStatusByClientOrderId() + { + try { + $this->binance->futuresAlgoOrderStatus(null, $this->origClientOrderId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/algoOrder?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->origClientOrderId, $params['clientAlgoId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + public function testFuturesAllOrders() { try { From 708aacbfdffe2759093bfe5ee0e7bc47a955223a Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:54:07 +0300 Subject: [PATCH 05/15] feat: methods for canceling algo orders added --- php-binance-api.php | 62 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/php-binance-api.php b/php-binance-api.php index c86093e..beeee9b 100755 --- a/php-binance-api.php +++ b/php-binance-api.php @@ -516,7 +516,7 @@ public function cancel(string $symbol, ?string $orderid = null, $params = []) ]; if (!is_null($orderid)) { $request["orderId"] = $orderid; - } else if (!is_set($params['origClientOrderId'])) { + } else if (!isset($params['origClientOrderId'])) { throw new Exception("Either orderId or origClientOrderId must be provided"); } return $this->apiRequest("v3/order", "DELETE", array_merge($request, $params), true); @@ -545,7 +545,7 @@ public function orderStatus(string $symbol, ?string $orderid = null, array $para ]; if (!is_null($orderid)) { $request["orderId"] = $orderid; - } else if (!is_set($params['origClientOrderId'])) { + } else if (!isset($params['origClientOrderId'])) { throw new Exception("Either orderId or origClientOrderId must be provided"); } return $this->apiRequest("v3/order", "GET", array_merge($request, $params), true); @@ -5191,6 +5191,32 @@ public function futuresCancel(string $symbol, $orderid, $params = []) return $this->fapiRequest("v1/order", 'DELETE', array_merge($request, $params), true); } + /** + * futuresAlgoCancel cancels a futures order + * + * $algoid = "123456789"; + * $order = $api->futuresCancel($algoid); + * + * @param string $algoid (optional) the algoid to cancel (mandatory if $params['clientalgoid'] is not set) + * @param array $params (optional) additional options + * - @param string $params['clientalgoid'] original client order id to cancel + * - @param int $params['recvWindow'] the time in milliseconds to wait for a response + * + * @return array with error message or the order details + * @throws \Exception + */ + public function futuresAlgoCancel($algoid, $params = []) + { + $request = []; + if ($algoid) { + $request['algoId'] = $algoid; + } else if (!isset($params['clientalgoid'])) { + throw new \Exception('futuresAlgoCancel(): either algoid or clientalgoid must be set'); + } + // todo: check camel-case or lower-case!!!! + return $this->fapiRequest("v1/algoOrder", 'DELETE', array_merge($request, $params), true); + } + /** * futuresCancelBatchOrders canceles multiple futures orders * @@ -5229,11 +5255,13 @@ public function futuresCancelBatchOrders(string $symbol, $orderIdList = null, $o * futuresCancelOpenOrders cancels all open futures orders for a symbol * * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Open-Orders - * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Algo-Open-Orders * $orders = $api->futuresCancelOpenOrders("BNBBTC"); * * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param int $recvWindow the time in milliseconds to wait for a response + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param bool $params['isAlgo'] true for canceling algo orders + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response * * @return array with error message or the orders details * @throws \Exception @@ -5243,8 +5271,32 @@ public function futuresCancelOpenOrders(string $symbol, array $params = []) $request = [ 'symbol' => $symbol, ]; + if (isset($params['isAlgo']) && ($params['isAlgo'] === true)) { + unset($params['isAlgo']); + return $this->fapiRequest("v1/algoOpenOrders", 'DELETE', array_merge($request, $params), true); + } else { + return $this->fapiRequest("v1/allOpenOrders", 'DELETE', array_merge($request, $params), true); + } + } - return $this->fapiRequest("v1/allOpenOrders", 'DELETE', array_merge($request, $params), true); + /** + * futuresCancelOpenAlgoOrders cancels all open futures algo orders for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Algo-Open-Orders + * + * $orders = $api->futuresCancelOpenAlgoOrders("BNBBTC"); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the orders details + * @throws \Exception + */ + public function futuresCancelOpenAlgoOrders (string $symbol, array $params = []) + { + $params['isAlgo'] = true; + return $this->futuresCancelOpenOrders($symbol, $params); } /** From d6dc0f5158c9176f81a1d3b947ca6911933d5e90 Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:55:41 +0300 Subject: [PATCH 06/15] test: testFuturesCancelOpenAlgoOrders added --- tests/BinanceStaticTests.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/BinanceStaticTests.php b/tests/BinanceStaticTests.php index 2d35798..5ba633a 100644 --- a/tests/BinanceStaticTests.php +++ b/tests/BinanceStaticTests.php @@ -2189,6 +2189,24 @@ public function testFuturesCancelOpenOrders() } + public function futuresCancelOpenAlgoOrders() + { + try { + $this->binance->futuresCancelOpenAlgoOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/algoOpenOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + public function testFuturesCountdownCancelAllOrders() { try { From 1e5c9c6e4048749ef5215e307e660746a371eabb Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Sun, 7 Dec 2025 22:13:58 +0300 Subject: [PATCH 07/15] feat: methods for fetching algo orders added --- php-binance-api.php | 9404 ++++++++++++++----------------------------- 1 file changed, 3015 insertions(+), 6389 deletions(-) diff --git a/php-binance-api.php b/php-binance-api.php index beeee9b..2ee6d10 100755 --- a/php-binance-api.php +++ b/php-binance-api.php @@ -1,6592 +1,3218 @@ 0, - ]; // /< Additional connection options - protected $proxyConf = null; // /< Used for story the proxy configuration - protected $caOverride = false; // /< set this if you donnot wish to use CA bundle auto download feature - protected $transfered = 0; // /< This stores the amount of bytes transfered - protected $requestCount = 0; // /< This stores the amount of API requests - protected $httpDebug = false; // /< If you enable this, curl will output debugging information - protected $subscriptions = []; // /< View all websocket subscriptions - protected $recvWindow = null; // /< The amount of time in milliseconds to wait for a response from the server for endpoints that accept a recvWindow parameter - - // /< value of available onOrder assets - - protected $exchangeInfo = null; - protected $futuresExchangeInfo = null; - protected $lastRequest = []; - - protected $xMbxUsedWeight = 0; - protected $xMbxUsedWeight1m = 0; - - public $headers = []; + public static $capturedUrl = null; + public static $capturedBody = null; + private MockBinanceAPI $binance; private $SPOT_ORDER_PREFIX = "x-B3AUXNYV"; private $CONTRACT_ORDER_PREFIX = "x-ftGmvgAN"; - /** - * Constructor for the class, - * send as many argument as you want. - * - * No arguments - use file setup - * 1 argument - file to load config from - * 2 arguments - api key and api secret - * 3 arguments - api key, api secret and use testnet flag - * - * @return null - */ - public function __construct() - { - $param = func_get_args(); - switch (count($param)) { - case 0: - $this->setupApiConfigFromFile(); - $this->setupProxyConfigFromFile(); - $this->setupCurlOptsFromFile(); - break; - case 1: - $this->setupApiConfigFromFile($param[0]); - $this->setupProxyConfigFromFile($param[0]); - $this->setupCurlOptsFromFile($param[0]); - break; - case 2: - $this->api_key = $param[0]; - $this->api_secret = $param[1]; - break; - case 3: - $this->api_key = $param[0]; - $this->api_secret = $param[1]; - $this->useTestnet = (bool)$param[2]; - break; - default: - echo 'Please see valid constructors here: https://github.com/jaggedsoft/php-binance-api/blob/master/examples/constructor.php'; - } - } - - /** - * magic get for protected and protected members - * - * @param $file string the name of the property to return - * @return null - */ - public function __get(string $member) - { - if (property_exists($this, $member)) { - return $this->$member; - } - return null; - } - - /** - * magic set for protected and protected members - * - * @param $member string the name of the member property - * @param $value the value of the member property - */ - public function __set(string $member, $value) - { - $this->$member = $value; - } - - /** - * If no paramaters are supplied in the constructor, this function will attempt - * to load the api_key and api_secret from the users home directory in the file - * ~/jaggedsoft/php-binance-api.json - * - * @param $file string file location - * @return null - */ - protected function setupApiConfigFromFile(?string $file = null) - { - $file = is_null($file) ? getenv("HOME") . "/.config/jaggedsoft/php-binance-api.json" : $file; - - if (empty($this->api_key) === false || empty($this->api_secret) === false) { - return; - } - if (file_exists($file) === false) { - echo "Unable to load config from: " . $file . PHP_EOL; - echo "Detected no API KEY or SECRET, all signed requests will fail" . PHP_EOL; - return; - } - $contents = json_decode(file_get_contents($file), true); - $this->api_key = isset($contents['api-key']) ? $contents['api-key'] : ""; - $this->api_secret = isset($contents['api-secret']) ? $contents['api-secret'] : ""; - $this->useTestnet = isset($contents['use-testnet']) ? (bool)$contents['use-testnet'] : false; - } - - /** - * If no paramaters are supplied in the constructor, this function will attempt - * to load the acurlopts from the users home directory in the file - * ~/jaggedsoft/php-binance-api.json - * - * @param $file string file location - * @return null - */ - protected function setupCurlOptsFromFile(?string $file = null) - { - $file = is_null($file) ? getenv("HOME") . "/.config/jaggedsoft/php-binance-api.json" : $file; - - if (count($this->curlOpts) > 0) { - return; - } - if (file_exists($file) === false) { - echo "Unable to load config from: " . $file . PHP_EOL; - echo "No curl options will be set" . PHP_EOL; - return; - } - $contents = json_decode(file_get_contents($file), true); - $this->curlOpts = isset($contents['curlOpts']) && is_array($contents['curlOpts']) ? $contents['curlOpts'] : []; - } - - /** - * If no paramaters are supplied in the constructor for the proxy confguration, - * this function will attempt to load the proxy info from the users home directory - * ~/jaggedsoft/php-binance-api.json - * - * @return null - */ - protected function setupProxyConfigFromFile(?string $file = null) - { - $file = is_null($file) ? getenv("HOME") . "/.config/jaggedsoft/php-binance-api.json" : $file; - - if (is_null($this->proxyConf) === false) { - return; - } - if (file_exists($file) === false) { - echo "Unable to load config from: " . $file . PHP_EOL; - echo "No proxies will be used " . PHP_EOL; - return; - } - $contents = json_decode(file_get_contents($file), true); - if (isset($contents['proto']) === false) { - return; - } - if (isset($contents['address']) === false) { - return; - } - if (isset($contents['port']) === false) { - return; - } - $this->proxyConf['proto'] = $contents['proto']; - $this->proxyConf['address'] = $contents['address']; - $this->proxyConf['port'] = $contents['port']; - if (isset($contents['user'])) { - $this->proxyConf['user'] = isset($contents['user']) ? $contents['user'] : ""; - } - if (isset($contents['pass'])) { - $this->proxyConf['pass'] = isset($contents['pass']) ? $contents['pass'] : ""; - } - } - - public static function uuid22($length = 22) { - return bin2hex(random_bytes(intval($length / 2))); - } - - protected function generateSpotClientOrderId() - { - return $this->SPOT_ORDER_PREFIX . self::uuid22(); - } - - protected function generateFuturesClientOrderId() - { - return $this->CONTRACT_ORDER_PREFIX . self::uuid22();; - } - - /** - * buy attempts to create a currency order - * each currency supports a number of order types, such as - * -LIMIT - * -MARKET - * -STOP_LOSS - * -STOP_LOSS_LIMIT - * -TAKE_PROFIT - * -TAKE_PROFIT_LIMIT - * -LIMIT_MAKER - * - * You should check the @see exchangeInfo for each currency to determine - * what types of orders can be placed against specific pairs - * - * $quantity = 1; - * $price = 0.0005; - * $order = $api->buy("BNBBTC", $quantity, $price); - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity required - * @param $price string price per unit you want to spend - * @param $type string type of order - * @param $params array addtional options for order type - * @return array with error message or the order details - */ - public function buy(string $symbol, $quantity, $price, string $type = "LIMIT", array $params = []) - { - return $this->order("BUY", $symbol, $quantity, $price, $type, $params); - } - - /** - * buyTest attempts to create a TEST currency order - * - * @see buy() - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity required - * @param $price string price per unit you want to spend - * @param $type string config - * @param $params array config - * @return array with error message or empty or the order details - */ - public function buyTest(string $symbol, $quantity, $price, string $type = "LIMIT", array $params = []) - { - return $this->order("BUY", $symbol, $quantity, $price, $type, $params, true); - } - - /** - * sell attempts to create a currency order - * each currency supports a number of order types, such as - * -LIMIT - * -MARKET - * -STOP_LOSS - * -STOP_LOSS_LIMIT - * -TAKE_PROFIT - * -TAKE_PROFIT_LIMIT - * -LIMIT_MAKER - * - * You should check the @see exchangeInfo for each currency to determine - * what types of orders can be placed against specific pairs - * - * $quantity = 1; - * $price = 0.0005; - * $order = $api->sell("BNBBTC", $quantity, $price); - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity required - * @param $price string price per unit you want to spend - * @param $type string type of order - * @param $params array addtional options for order type - * @return array with error message or the order details - */ - public function sell(string $symbol, $quantity, $price, string $type = "LIMIT", array $params = []) - { - return $this->order("SELL", $symbol, $quantity, $price, $type, $params); - } - - /** - * sellTest attempts to create a TEST currency order - * - * @see sell() - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity required - * @param $price string price per unit you want to spend - * @param $type array config - * @param $params array config - * @return array with error message or empty or the order details - */ - public function sellTest(string $symbol, $quantity, $price, string $type = "LIMIT", array $params = []) - { - return $this->order("SELL", $symbol, $quantity, $price, $type, $params, true); - } - - /** - * marketQuoteBuy attempts to create a currency order at given market price - * - * $quantity = 1; - * $order = $api->marketQuoteBuy("BNBBTC", $quantity); - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity of the quote to use - * @param $params array additional options for order type - * @return array with error message or the order details - */ - public function marketQuoteBuy(string $symbol, $quantity, array $params = []) - { - $params['isQuoteOrder'] = true; - - return $this->order("BUY", $symbol, $quantity, 0, "MARKET", $params); - } - - /** - * marketQuoteBuyTest attempts to create a TEST currency order at given market price - * - * @see marketBuy() - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity of the quote to use - * @param $params array additional options for order type - * @return array with error message or the order details - */ - public function marketQuoteBuyTest(string $symbol, $quantity, array $params = []) - { - $params['isQuoteOrder'] = true; - - return $this->order("BUY", $symbol, $quantity, 0, "MARKET", $params, true); - } - - /** - * marketBuy attempts to create a currency order at given market price - * - * $quantity = 1; - * $order = $api->marketBuy("BNBBTC", $quantity); - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity required - * @param $params array addtional options for order type - * @return array with error message or the order details - */ - public function marketBuy(string $symbol, $quantity, array $params = []) - { - return $this->order("BUY", $symbol, $quantity, 0, "MARKET", $params); - } - - /** - * marketBuyTest attempts to create a TEST currency order at given market price - * - * @see marketBuy() - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity required - * @param $params array addtional options for order type - * @return array with error message or the order details - */ - public function marketBuyTest(string $symbol, $quantity, array $params = []) - { - return $this->order("BUY", $symbol, $quantity, 0, "MARKET", $params, true); - } - - - /** - * numberOfDecimals() returns the signifcant digits level based on the minimum order amount. - * - * $dec = numberOfDecimals(0.00001); // Returns 5 - * - * @param $val float the minimum order amount for the pair - * @return integer (signifcant digits) based on the minimum order amount - */ - public function numberOfDecimals($val = 0.00000001) - { - $val = sprintf("%.14f", $val); - $parts = explode('.', $val); - $parts[1] = rtrim($parts[1], "0"); - return strlen($parts[1]); - } - - /** - * marketQuoteSell attempts to create a currency order at given market price - * - * $quantity = 1; - * $order = $api->marketQuoteSell("BNBBTC", $quantity); - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity of the quote you want to obtain - * @param $params array additional options for order type - * @return array with error message or the order details - */ - public function marketQuoteSell(string $symbol, $quantity, array $params = []) - { - $params['isQuoteOrder'] = true; - $c = $this->numberOfDecimals($this->exchangeInfo()['symbols'][$symbol]['filters'][1]['minQty']); - $quantity = $this->floorDecimal($quantity, $c); - - return $this->order("SELL", $symbol, $quantity, 0, "MARKET", $params); - } - - /** - * marketQuoteSellTest attempts to create a TEST currency order at given market price - * - * @see marketSellTest() - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity of the quote you want to obtain - * @param $params array additional options for order type - * @return array with error message or the order details - */ - public function marketQuoteSellTest(string $symbol, $quantity, array $params = []) - { - $params['isQuoteOrder'] = true; - - return $this->order("SELL", $symbol, $quantity, 0, "MARKET", $params, true); - } - - /** - * marketSell attempts to create a currency order at given market price - * - * $quantity = 1; - * $order = $api->marketSell("BNBBTC", $quantity); - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity required - * @param $params array addtional options for order type - * @return array with error message or the order details - */ - public function marketSell(string $symbol, $quantity, array $params = []) - { - $c = $this->numberOfDecimals($this->exchangeInfo()['symbols'][$symbol]['filters'][1]['minQty']); - $quantity = $this->floorDecimal($quantity, $c); - - return $this->order("SELL", $symbol, $quantity, 0, "MARKET", $params); - } - - /** - * marketSellTest attempts to create a TEST currency order at given market price - * - * @see marketSellTest() - * - * @param $symbol string the currency symbol - * @param $quantity string the quantity required - * @param $params array addtional options for order type - * @return array with error message or the order details - */ - public function marketSellTest(string $symbol, $quantity, array $params = []) - { - return $this->order("SELL", $symbol, $quantity, 0, "MARKET", $params, true); - } - - /** - * cancel attempts to cancel a currency order - * either orderid or origClientOrderId is mandatory - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#cancel-order-trade - * - * $orderid = "123456789"; - * $order = $api->cancel("BNBBTC", $orderid); - * - * @param string $symbol (mandatory) the currency symbol - * @param string $orderid (optional) the orderid to fetch (mandatory if $params['origClientOrderId'] is not provided) - * @param array $params an array of additional parameters that the API endpoint allows - * - @param string $params['origClientOrderId'] optional the original client order id (mandatory if $orderid is not provided) - * - @param string $params['cancelRestrictions'] optional ONLY_NEW (cancel will succeed if the order status is NEW) or ONLY_PARTIALLY_FILLED (cancel will succeed if the order status is PARTIALLY_FILLED) - * @return array with error message or the order details - * @throws \Exception - */ - public function cancel(string $symbol, ?string $orderid = null, $params = []) - { - $request = [ - "symbol" => $symbol, - ]; - if (!is_null($orderid)) { - $request["orderId"] = $orderid; - } else if (!isset($params['origClientOrderId'])) { - throw new Exception("Either orderId or origClientOrderId must be provided"); - } - return $this->apiRequest("v3/order", "DELETE", array_merge($request, $params), true); - } - - /** - * orderStatus attempts to get orders status - * either orderid or origClientOrderId is mandatory - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#query-order-user_data - * - * $orderid = "123456789"; - * $order = $api->orderStatus("BNBBTC", $orderid); - * - * @param string $symbol (mandatory) the currency symbol - * @param string $orderid (optional) the orderid to fetch (mandatory if $params['origClientOrderId'] is not provided) - * @param array $params an array of additional parameters that the API endpoint allows - * - @param string $params['origClientOrderId'] optional the original client order id (mandatory if $orderid is not provided) - * @return array with error message or the order details - * @throws \Exception - */ - public function orderStatus(string $symbol, ?string $orderid = null, array $params = []) - { - $request = [ - "symbol" => $symbol, - ]; - if (!is_null($orderid)) { - $request["orderId"] = $orderid; - } else if (!isset($params['origClientOrderId'])) { - throw new Exception("Either orderId or origClientOrderId must be provided"); - } - return $this->apiRequest("v3/order", "GET", array_merge($request, $params), true); - } - - /** - * openOrders attempts to get open orders for all currencies or a specific currency - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#current-open-orders-user_data - * - * $allOpenOrders = $api->openOrders(); - * $allBNBOrders = $api->openOrders( "BNBBTC" ); - * - * @param string $symbol (optional) the market symbol - * @return array with error message or the order details - * @throws \Exception - */ - public function openOrders(?string $symbol = null, array $params = []) - { - $request = []; - if (!is_null($symbol)) { - $request = [ - "symbol" => $symbol, - ]; - } - return $this->apiRequest("v3/openOrders", "GET", array_merge($request, $params), true); - } - - /** - * cancelOpenOrders - Cancel all open orders method - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#cancel-all-open-orders-on-a-symbol-trade - * - * $api->cancelOpenOrders( "BNBBTC" ); - * @param string $symbol (mandatory) the merket symbol - * @param array $params (optional) an array of additional parameters that the API endpoint allows - * @return array with error message or the order details - * @throws \Exception - */ - public function cancelOpenOrders(string $symbol, array $params = []) - { - $request = [ - "symbol" => $symbol, - ]; - return $this->apiRequest("v3/openOrders", "DELETE", array_merge($request, $params), true); - } - - /** - * orders attempts to get the orders for all or a specific currency - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#all-orders-user_data - * - * $allBNBOrders = $api->orders( "BNBBTC" ); - * - * @param string $symbol (mandatory) the currency symbol - * @param int $limit (optional) the amount of orders returned (default 500, max 1000) - * @param string $fromOrderId (optional) return the orders from this order onwards - * @param array $params (optional) an array of additional parameters that the API endpoint allows - * @return array with error message or array of orderDetails array - * @throws \Exception - */ - public function orders(string $symbol, ?int $limit = null, int $fromOrderId = null, array $params = []) - { - $request = [ - "symbol" => $symbol, - ]; - if (!is_null($limit)) { - $request["limit"] = $limit; - } - if (!is_null($fromOrderId)) { - $request["orderId"] = $fromOrderId; - } - return $this->apiRequest("v3/allOrders", "GET", array_merge($request, $params), true); - } - - /** - * history Get the complete account trade history for all or a specific currency - * @deprecated - * use myTrades() instead - * - * $BNBHistory = $api->history("BNBBTC"); - * $limitBNBHistory = $api->history("BNBBTC",5); - * $limitBNBHistoryFromId = $api->history("BNBBTC",5,3); - * - * @param $symbol string the currency symbol - * @param $limit int the amount of orders returned - * @param $fromTradeId int (optional) return the orders from this order onwards. negative for all - * @param $startTime int (optional) return the orders from this time onwards. null to ignore - * @param $endTime int (optional) return the orders from this time backwards. null to ignore - * @return array with error message or array of orderDetails array - * @throws \Exception - */ - public function history(string $symbol, int $limit = 500, int $fromTradeId = -1, ?int $startTime = null, ?int $endTime = null, array $params = []) - { - $request = [ - "symbol" => $symbol, - "limit" => $limit, - ]; - if ($fromTradeId > 0) { - $request["fromId"] = $fromTradeId; - } - if (isset($startTime)) { - $request["startTime"] = $startTime; - } - if (isset($endTime)) { - $request["endTime"] = $endTime; - } - - return $this->apiRequest("v3/myTrades", "GET", array_merge($request, $params), true); - } - - /** - * myTrades - * another name for history - * @see history() - * - * @return array with error message or array of orderDetails array - * @throws \Exception - */ - public function myTrades(string $symbol, int $limit = 500, int $fromTradeId = -1, ?int $startTime = null, ?int $endTime = null, array $params = []) - { - return $this->history($symbol, $limit, $fromTradeId, $startTime, $endTime, $params); - } - - /** - * useServerTime adds the 'useServerTime'=>true to the API request to avoid time errors - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints - * - * $api->useServerTime(); - * - * @return null - * @throws \Exception - */ - public function useServerTime(array $params = []) - { - $request = $this->apiRequest("v3/time", "GET", $params); - if (isset($request['serverTime'])) { - $this->info['timeOffset'] = $request['serverTime'] - (microtime(true) * 1000); - } - } - - /** - * time Gets the server time - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints - * - * $time = $api->time(); - * - * @return array with error message or array with server time key - * @throws \Exception - */ - public function time(array $params = []) - { - return $this->apiRequest("v3/time", "GET", $params); - } - - /** - * exchangeInfo - Gets the complete exchange info, including limits, currency options etc. - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints - * - * $info = $api->exchangeInfo(); - * $info = $api->exchangeInfo('BTCUSDT'); - * - * $arr = array('ATABUSD','BTCUSDT'); - * $info = $api->exchangeInfo($arr); - * - * @property int $weight 10 - * - * @param string|array $symbols (optional) A symbol or an array of symbols, default is empty - * - * @return array containing the response - * @throws \Exception - */ - public function exchangeInfo($symbols = null, array $params = []) - { - if (!$this->exchangeInfo) { - $arr = array(); - if ($symbols) { - if (gettype($symbols) == "string") { - $request["symbol"] = $symbols; - $arr = $this->apiRequest("v3/exchangeInfo", "GET", array_merge($request, $params)); - } - if (gettype($symbols) == "array") { - $arr = $this->apiRequest("v3/exchangeInfo?symbols=" . '["' . implode('","', $symbols) . '"]', "GET", $params); - } - } else { - $arr = $this->apiRequest("v3/exchangeInfo", "GET", $params); - } - if ((is_array($arr) === false) || empty($arr)) { - echo "Error: unable to fetch spot exchange info" . PHP_EOL; - $arr = array(); - $arr['symbols'] = array(); - } - $this->exchangeInfo = $arr; - $this->exchangeInfo['symbols'] = null; - - foreach ($arr['symbols'] as $key => $value) { - $this->exchangeInfo['symbols'][$value['symbol']] = $value; - } - } - - return $this->exchangeInfo; - } - - /** - * assetDetail - Fetch details of assets supported on Binance - * - * @link https://binance-docs.github.io/apidocs/spot/en/#asset-detail-user_data - * - * @property int $weight 1 - * - * @param string $asset (optional) Should be an asset, e.g. BNB or empty to get the full list - * - * @return array containing the response - */ - public function assetDetail($asset = '', array $params = []) - { - $request = array(); - if ($asset != '' && gettype($asset) == 'string') - $request['asset'] = $asset; - $arr = $this->sapiRequest("v1/asset/assetDetail", 'GET', array_merge($request, $params), true); - // if asset was set, no backward compatibility needed as this was implemented later - if (isset($params['asset'])) - return $arr; - - // wrap into another array for backward compatibility with the old wapi one - if (!empty($arr['BTC']['withdrawFee'])) { - return array( - 'success' => 1, - 'assetDetail' => $arr, - ); - } else { - return array( - 'success' => 0, - 'assetDetail' => array(), - ); - - } - } - - /** - * userAssetDribbletLog - Log of the conversion of the dust assets to BNB - * @deprecated - */ - public function userAssetDribbletLog() - { - $params["wapi"] = true; - trigger_error('Deprecated - function will disappear on 2021-08-01 from Binance. Please switch to $api->dustLog().', E_USER_DEPRECATED); - return $this->httpRequest("v3/userAssetDribbletLog.html", 'GET', $params, true); - } - - /** - * dustLog - Log of the conversion of the dust assets to BNB - * - * @link https://binance-docs.github.io/apidocs/spot/en/#dustlog-user_data - * - * @property int $weight 1 - * - * @param long $startTime (optional) Start time, e.g. 1617580799000 - * @param long $endTime (optional) End time, e.g. 1617580799000. Endtime is mandatory if startTime is set. - * - * @return array containing the response - * @throws \Exception - */ - public function dustLog($startTime = NULL, $endTime = NULL, array $params = []) - { - $request = array(); - if (!empty($startTime) && !empty($endTime)) { - $request['startTime'] = $startTime; - $request['endTime'] = $endTime; - } - - return $this->sapiRequest("v1/asset/dribblet", 'GET', array_merge($request, $params), true); - } - - /** - * dustTransfer - Convert dust assets ( < 0.001 BTC) to BNB - * - * @link https://binance-docs.github.io/apidocs/spot/en/#dust-transfer-user_data - * - * @property int $weight 1 - * - * @param string|array $assets (mandatory) Asset(s), e.g. IOST or array like ['IOST','AAVE','CHZ'] - * - * @return array containing the response - * @throws \Exception - */ - public function dustTransfer($assets, array $params = []) - { - $request = [ - 'assets' => $assets, - ]; + // Default values for the tests + private $symbol = 'ETHUSDT'; + private $quantity = '1.234'; + private $price = '1000'; + private $stopprice = '1100'; + private $stoplimitprice = '900'; + private $type = 'LIMIT'; + private $orderid = '000'; + private $orderId = '1234567890'; + private $limit = 2; + private $fromOrderId = 1; + private $fromTradeId = 2; + private $startTime = 1; + private $endTime = 6; + private $symbols = ['ETHUSDT','BTCUSDT']; + private $asset = 'USDT'; + private $assets = ['IOST','AAVE','CHZ']; + private $address = '0x1234567890abcdef1234567890abcdef12345678'; + private $amount = 10.1; + private $addressTag = '123456'; + private $addressName = 'MyAddress'; + private $transactionFeeFlag = true; + private $network = 'TESTNetwork'; + private $fromSymbol = 'USDT'; + private $toSymbol = 'BNB'; + private $recvWindow = 1000; + private $current = 2; + private $tradeId = 3; + private $side = 'BUY'; + private $test = false; + private $interval = '15m'; + private $nbrDays = 6; + private $baseAsset = 'ETH'; + private $quoteAsset = 'USDT'; + private $quoteQty = 10.3; + private $fromId = 1; + private $contractType = 'CURRENT_QUARTER'; + private $period = '15m'; + private $origClientOrderId = 'test client order id'; + private $orderIdList = ['123456', '654321']; + private $origClientOrderIdList = ['test client order id 1', 'test client order id 2']; + private $countdownTime = 100; + private $autoCloseType = 'LIQUIDATION'; + private $marginType = 'CROSSED'; + private $dualSidePosition = true; + private $leverage = 10; + private $multiAssetsMarginMode = true; + private $positionSide = 'SHORT'; + private $incomeType = 'COMMISSION_REBATE'; + private $page = 3; + private $downloadId = 'testDownloadId'; + private $fromAsset = 'USDT'; + private $toAsset = 'BNB'; + private $fromAmount = '100'; + private $validTime = '10s'; + private $quoteId = 'testQuoteId'; - return $this->sapiRequest("v1/asset/dust", 'POST', array_merge($request, $params), true); + public function setUp(): void { + $this->binance = new MockBinanceAPI('api_key', 'api_secret'); } - /** - * Fetch current(daily) trade fee of symbol, values in percentage. - * for more info visit binance official api document - * - * $symbol = "BNBBTC"; or any other symbol or even a set of symbols in an array - * @param string $symbol - * @return mixed - */ - public function tradeFee(string $symbol, array $params = []) + public function testPricesSpot() { - $request = [ - "symbol" => $symbol, - ]; - return $this->sapiRequest("v1/asset/tradeFee", 'GET', array_merge($request, $params), true); - } - - /** - * commissionFee - Fetch commission trade fee - * - * @link https://binance-docs.github.io/apidocs/spot/en/#trade-fee-user_data - * - * @property int $weight 1 - * - * @param string $symbol (optional) Should be a symbol, e.g. BNBUSDT or empty to get the full list - * - * @return array containing the response - * @throws \Exception - */ - public function commissionFee($symbol = '', array $params = []) - { - $request = array(); - if ($symbol != '' && gettype($symbol) == 'string') - $request['symbol'] = $symbol; - - return $this->sapiRequest("v1/asset/tradeFee", 'GET', array_merge($request, $params), true); - } - - /** - * withdraw - Submit a withdraw request to move an asset to another wallet - * - * @link https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi - * - * @example https://github.com/jaggedsoft/php-binance-api#withdraw Standard withdraw - * @example https://github.com/jaggedsoft/php-binance-api#withdraw-with-addresstag Withdraw with addressTag for e.g. XRP - * - * @property int $weight 1 - * - * @param string $asset (mandatory) An asset, e.g. BTC - * @param string $address (mandatory) The address where to send, e.g. 1C5gqLRs96Xq4V2ZZAR1347yUCpHie7sa or 44tLjmXrQNrWJ5NBsEj2R77ZBEgDa3fEe9GLpSf2FRmhexPvfYDUAB7EXX1Hdb3aMQ9FLqdJ56yaAhiXoRsceGJCRS3Jxkn - * @param string $amount (mandatory) The amount, e.g. 0.2 - * @param string $addressTag (optional) Mandatory secondary address for some assets (XRP,XMR,etc), e.g. 0e5e38a01058dbf64e53a4333a5acf98e0d5feb8e523d32e3186c664a9c762c1 - * @param string $addressName (optional) Description of the address - * @param string $transactionFeeFlag (optional) When making internal transfer, true for returning the fee to the destination account; false for returning the fee back to the departure account. - * @param string $network (optional) - * @param string $orderId (optional) Client id for withdraw - * - * @return array containing the response - * @throws \Exception - */ - public function withdraw(string $asset, string $address, $amount, $addressTag = null, $addressName = "", bool $transactionFeeFlag = false, $network = null, $orderId = null, array $params = []) - { - $request = [ - "coin" => $asset, - "address" => $address, - "amount" => $amount, - "sapi" => true, - ]; + try { + $this->binance->prices(); + } catch(\Throwable $e) { - if (is_null($addressName) === false && empty($addressName) === false) { - $request['name'] = str_replace(' ', '%20', $addressName); - } - if (is_null($addressTag) === false && empty($addressTag) === false) { - $request['addressTag'] = $addressTag; } - if ($transactionFeeFlag) $request['transactionFeeFlag'] = true; + $this->assertEquals("https://api.binance.com/api/v3/ticker/price", self::$capturedUrl); + } + + + public function testSpotOrder() + { + try { + $this->binance->order('BUY', 'BTCUSDT', 1, 1000); + } catch(\Throwable $e) { - if (is_null($network) === false && empty($network) === false) { - $request['network'] = $network; - } - if (is_null($orderId) === false && empty($orderId) === false) { - $request['withdrawOrderId'] = $orderId; } - return $this->sapiRequest("v1/capital/withdraw/apply", "POST", array_merge($request, $params), true); + $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals("BTCUSDT", $params['symbol']); + $this->assertEquals("BUY", $params['side']); + $this->assertEquals("LIMIT", $params['type']); + $this->assertEquals(1, $params['quantity']); + $this->assertEquals(1000, $params['price']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); } - /** - * depositAddress - Get the deposit address for an asset - * - * @link https://binance-docs.github.io/apidocs/spot/en/#deposit-address-supporting-network-user_data - * - * @property int $weight 1 - * - * @param string $asset (mandatory) An asset, e.g. BTC - * @param string $network (optional) You can get network in networkList from /sapi/v1/capital/config/getall - * - * @return array containing the response - * @throws \Exception - */ - public function depositAddress(string $asset, $network = null, array $params = []) + public function testSpotSorOrder() { - $request = [ - "coin" => $asset, - ]; - if (is_null($network) === false && empty($network) === false) { - $request['network'] = $network; - } - - $return = $this->sapiRequest("v1/capital/deposit/address", "GET", array_merge($request, $params), true); - - // Adding for backwards compatibility with wapi - if (is_array($return) && !empty($return)) { - $return['asset'] = $return['coin']; - $return['addressTag'] = $return['tag']; - if (!empty($return['address'])) { - $return['success'] = 1; - } else { - $return['success'] = 0; - } - } else { - echo "Error: no deposit address found" . PHP_EOL; - } - - return $return; - } - - /** - * depositHistory - Get the deposit history for one or all assets - * - * @link https://binance-docs.github.io/apidocs/spot/en/#deposit-history-supporting-network-user_data - * - * @property int $weight 1 - * - * @param string $asset (optional) An asset, e.g. BTC - or leave empty for all - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - * @return array containing the response - * @throws \Exception - */ - public function depositHistory(?string $asset = null, array $params = []) - { - $request = array(); - if (is_null($asset) === false) { - $request['coin'] = $asset; - } - $return = $this->sapiRequest("v1/capital/deposit/hisrec", "GET", array_merge($request, $params), true); - - // Adding for backwards compatibility with wapi - if (is_array($return) && !empty($return)) { - foreach ($return as $key=>$item) { - $return[$key]['asset'] = $item['coin']; - } - } else { - echo "Error: no deposit history found" . PHP_EOL; - } - - return $return; - - } - - /** - * withdrawHistory - Get the withdraw history for one or all assets - * - * @link https://binance-docs.github.io/apidocs/spot/en/#withdraw-history-supporting-network-user_data - * - * @property int $weight 1 - * - * @param string $asset (optional) An asset, e.g. BTC - or leave empty for all - * @param array $params (optional) An array of additional parameters that the API endpoint allows: status, offset, limit, startTime, endTime - * - * @return array containing the response - * @throws \Exception - */ - public function withdrawHistory(?string $asset = null, array $params = []) - { - $request = array(); - if (is_null($asset) === false) { - $request['coin'] = $asset; - } - // Wrapping in array for backwards compatibility with wapi - $return = array( - 'withdrawList' => $this->sapiRequest("v1/capital/withdraw/history", "GET", array_merge($request, $params), true) - ); - - // Adding for backwards compatibility with wapi - $return['success'] = 1; - - return $return; - } - - /** - * withdrawFee - Get the withdrawal fee for an asset - * - * @property int $weight 1 - * - * @param string $asset (mandatory) An asset, e.g. BTC - * - * @return array containing the response - * @throws \Exception - */ - public function withdrawFee(string $asset, array $params = []) - { - $return = $this->assetDetail('', $params); - - if (isset($return['success'], $return['assetDetail'], $return['assetDetail'][$asset]) && $return['success']) { - return $return['assetDetail'][$asset]; - } else { - return array(); - } - } - - /** - * transfer - Transfer asset between accounts - * possible types of transfer are: - * - MAIN_UMFUTURE - Spot account transfer to USDⓈ-M Futures account - * - MAIN_CMFUTURE - Spot account transfer to COIN-M Futures account - * - MAIN_MARGIN - Spot account transfer to Margin(cross)account - * - UMFUTURE_MAIN - USDⓈ-M Futures account transfer to Spot account - * - UMFUTURE_MARGIN - USDⓈ-M Futures account transfer to Margin(cross)account - * - CMFUTURE_MAIN - COIN-M Futures account transfer to Spot account - * - CMFUTURE_MARGIN - COIN-M Futures account transfer to Margin(cross) account - * - MARGIN_MAIN - Margin(cross)account transfer to Spot account - * - MARGIN_UMFUTURE - Margin(cross)account transfer to USDⓈ-M Futures - * - MARGIN_CMFUTURE - Margin(cross)account transfer to COIN-M Futures - * - ISOLATEDMARGIN_MARGIN - Isolated margin account transfer to Margin(cross) account - * - MARGIN_ISOLATEDMARGIN - Margin(cross) account transfer to Isolated margin account - * - ISOLATEDMARGIN_ISOLATEDMARGIN - Isolated margin account transfer to Isolated margin account - * - MAIN_FUNDING - Spot account transfer to Funding account - * - FUNDING_MAIN - Funding account transfer to Spot account - * - FUNDING_UMFUTURE - Funding account transfer to UMFUTURE account - * - UMFUTURE_FUNDING - UMFUTURE account transfer to Funding account - * - MARGIN_FUNDING - MARGIN account transfer to Funding account - * - FUNDING_MARGIN - Funding account transfer to Margin account - * - FUNDING_CMFUTURE - Funding account transfer to CMFUTURE account - * - CMFUTURE_FUNDING - CMFUTURE account transfer to Funding account - * - MAIN_OPTION - Spot account transfer to Options account - * - OPTION_MAIN - Options account transfer to Spot account - * - UMFUTURE_OPTION - USDⓈ-M Futures account transfer to Options account - * - OPTION_UMFUTURE - Options account transfer to USDⓈ-M Futures account - * - MARGIN_OPTION - Margin(cross)account transfer to Options account - * - OPTION_MARGIN - Options account transfer to Margin(cross)account - * - FUNDING_OPTION - Funding account transfer to Options account - * - OPTION_FUNDING - Options account transfer to Funding account - * - MAIN_PORTFOLIO_MARGIN - Spot account transfer to Portfolio Margin account - * - PORTFOLIO_MARGIN_MAIN - Portfolio Margin account transfer to Spot account - * - * @link https://developers.binance.com/docs/wallet/asset/user-universal-transfer - * - * @property int $weight 900 - * - * @param string $type (mandatory) type of transfer, e.g. MAIN_MARGIN - * @param string $asset (mandatory) an asset, e.g. BTC - * @param string $amount (mandatory) the amount to transfer - * @param string $fromSymbol (optional) must be sent when type are ISOLATEDMARGIN_MARGIN and ISOLATEDMARGIN_ISOLATEDMARGIN - * @param string $toSymbol (optional) must be sent when type are MARGIN_ISOLATEDMARGIN and ISOLATEDMARGIN_ISOLATEDMARGIN - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function transfer(string $type, string $asset, string $amount, $fromSymbol = null, $toSymbol = null, array $params = []) - { - $request = [ - 'type' => $type, - 'asset' => $asset, - 'amount' => $amount, - ]; - // todo: check this method with real account - if ($fromSymbol) { - $request['fromSymbol'] = $fromSymbol; - } - if ($toSymbol) { - $request['toSymbol'] = $toSymbol; - } - - - return $this->sapiRequest("v1/asset/transfer", 'POST', array_merge($request, $params), true); - } - - /** - * transfersHistory - get the transfer history between accounts - * - * @link https://developers.binance.com/docs/wallet/asset/query-user-universal-transfer - * - * @property int $weight 1 - * - * @param string $type (mandatory) type of transfer, e.g. MAIN_MARGIN (@see transfer()) - * @param string $startTime (optional) start time in milliseconds - * @param string $endTime (optional) end time in milliseconds - * @param int $limit (optional) the number of records to return (default 10, max 100) - * @param int $current (optional) default 1 - * @param string $fromSymbol (optional) must be sent when type are ISOLATEDMARGIN_MARGIN and ISOLATEDMARGIN_ISOLATEDMARGIN - * @param string $toSymbol (optional) must be sent when type are MARGIN_ISOLATEDMARGIN and ISOLATEDMARGIN_ISOLATEDMARGIN - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function transfersHistory(string $type, $startTime = null, $endTime = null, $limit = null, $current = null, $fromSymbol = null, $toSymbol = null, array $params = []) - { - $request = [ - 'type' => $type, - ]; - // todo: check this method with real account - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - if ($limit) { - $request['size'] = $limit; - } - if ($current) { - $request['current'] = $current; - } - if ($fromSymbol) { - $request['fromSymbol'] = $fromSymbol; - } - if ($toSymbol) { - $request['toSymbol'] = $toSymbol; + try { + $this->binance->sorOrder('BUY', 'BTCUSDT', 1, 1000); + } catch(\Throwable $e) { + } + $this->assertEquals("https://api.binance.com/api/v3/sor/order", self::$capturedUrl); + parse_str(self::$capturedBody, $params); - return $this->sapiRequest("v1/asset/transfer", 'GET', array_merge($request, $params), true); + $this->assertEquals("BTCUSDT", $params['symbol']); + $this->assertEquals("BUY", $params['side']); + $this->assertEquals("LIMIT", $params['type']); + $this->assertEquals(1, $params['quantity']); + $this->assertEquals(1000, $params['price']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); } - /** - * prices get all the current prices - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#symbol-price-ticker - * - * $ticker = $api->prices(); - * - * @return array with error message or array of all the currencies prices - * @throws \Exception - */ - public function prices(array $params = []) + public function testSpotReplaceOrder() { - return $this->priceData($this->apiRequest("v3/ticker/price", "GET", $params)); + try { + $this->binance->replaceOrder('BUY', 'BTCUSDT', 1, 1000, 'LIMIT', '123456789'); + } catch(\Throwable $e) { + + } + $this->assertEquals("https://api.binance.com/api/v3/order/cancelReplace", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals("BTCUSDT", $params['symbol']); + $this->assertEquals("BUY", $params['side']); + $this->assertEquals("LIMIT", $params['type']); + $this->assertEquals(1, $params['quantity']); + $this->assertEquals(1000, $params['price']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertEquals('123456789', $params['cancelOrderId']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); } - /** - * price get the latest price of a symbol - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#symbol-price-ticker - * - * $price = $api->price( "ETHBTC" ); - * - * @return array with error message or array with symbol price - * @throws \Exception - */ - public function price(string $symbol, array $params = []) + public function testSpotEditOrder() { - $request = [ - "symbol" => $symbol, - ]; - $ticker = $this->apiRequest("v3/ticker/price", "GET", array_merge($request, $params)); - if (!isset($ticker['price'])) { - echo "Error: unable to fetch price for $symbol" . PHP_EOL; - return null; - } - return $ticker['price']; - } - - /** - * bookPrices get all bid/asks prices - * - * $ticker = $api->bookPrices(); - * - * @return array with error message or array of all the book prices - * @throws \Exception - */ - public function bookPrices(array $params = []) - { - return $this->bookPriceData($this->apiRequest("v3/ticker/bookTicker", "GET", $params)); - } - - /** - * account get all information about the api account - * - * $account = $api->account(); - * - * @return array with error message or array of all the account information - * @throws \Exception - */ - public function account(array $params = []) - { - return $this->apiRequest("v3/account", "GET", $params, true); - } - - /** - * prevDay get 24hr ticker price change statistics for symbols - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints - * - * $prevDay = $api->prevDay("BNBBTC"); - * - * @param string $symbol (optional) symbol to get the previous day change for - * @param array $params (optional) an array of additional parameters that the API endpoint allows - * - @param array $params['symbols'] (optional) the symbols to get the previous day change for (e.g. ["BNBBTC","ETHBTC"]) - * @return array with error message or array of prevDay change - * @throws \Exception - */ - public function prevDay(?string $symbol = null, array $params = []) - { - $request = []; - if (is_null($symbol) === false) { - $request = [ - 'symbol' => $symbol, - ]; - } - $symbols = []; - if (isset($params['symbols'])) { - $symbols = $params['symbols']; - unset($params['symbols']); - } - if (!empty($symbols)) { - $request['symbols'] = json_encode($symbols); - } - return $this->apiRequest("v3/ticker/24hr", "GET", array_merge($request, $params)); - } - - /** - * tradingDay get 24hr ticker price change statistics for symbols - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints - * - * $tradingDay = $api->tradingDay("BNBBTC"); - * $tradingDay = $api->tradingDay(null, ["BNBBTC","ETHBTC"]); - * - * @param string $symbol (optional) symbol to get the info for - mandatory if symbols is not set - * @param array $symbols (optional) the symbols to get the info for (e.g. ["BNBBTC","ETHBTC"]) - mandatory if symbol is not set - * @param array $params (optional) an array of additional parameters that the API endpoint allows - * - @param string $params['timeZone'] (optional) default is 0 (UTC) - time zone to use for the response - * - @param string $params['type'] (optional) FULL or MINI - the format of the response (default is FULL) - * @return array with error message or array of prevDay change - * @throws \Exception - */ - public function tradingDay(?string $symbol = null, ?array $symbols = null, array $params = []) - { - $request = []; - if (!is_null($symbol) && is_string($symbol)) { - $request['symbol'] = $symbol; - } elseif (is_array($symbols) && !empty($symbols)) { - $request['symbols'] = json_encode($symbols); - } else { - throw new \Exception("tradingDay(): Either symbol or symbols must be set"); - } - return $this->apiRequest("v3/ticker/tradingDay", "GET", array_merge($request, $params)); - } - - /** - * rollingWindowPriceChange get ticker price change statistics for symbols for a rolling window - * - 1m, 2m... 59m for minutes - * - 1h, 2h... 23h - for hours - * - 1d.. 7d - for days - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints - * - * $priceChange = $api->rollingWindowPriceChange("BNBBTC"); - * $priceChange = $api->rollingWindowPriceChange(null, ["BNBBTC","ETHBTC"], '2d'); - * - * @param string $symbol (optional) symbol to get the info for - mandatory if symbols is not set - * @param array $symbols (optional) the symbols to get the info for (e.g. ["BNBBTC","ETHBTC"]) - mandatory if symbol is not set - * @param string $windowSize (optional) the size of the rolling window (e.g. 1m, 2m, 1h, 2h, 1d, 2d) - default is 1d - * @param array $params (optional) an array of additional parameters that the API endpoint allows - * - @param string $params['type'] (optional) FULL or MINI - the format of the response (default is FULL) - * @return array with error message or array of prevDay change - * @throws \Exception - */ - public function rollingWindowPriceChange(?string $symbol = null, array $symbols = null, string $windowSize = null, array $params = []) - { - $request = []; - if (!is_null($symbol) && is_string($symbol)) { - $request['symbol'] = $symbol; - } elseif (is_array($symbols) && !empty($symbols)) { - $request['symbols'] = json_encode($symbols); - } else { - throw new \Exception("rollingWindowPriceChange(): Either symbol or symbols must be set"); - } - if (!is_null($windowSize)) { - $request['windowSize'] = $windowSize; - } - return $this->apiRequest("v3/ticker", "GET", array_merge($request, $params)); - } - - /** - * aggTrades get Market History / Aggregate Trades - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints - * - * $trades = $api->aggTrades("BNBBTC"); - * - * @param $symbol string the symbol to get the trade information for - * @return array with error message or array of market history - * @throws \Exception - */ - public function aggTrades(string $symbol, array $params = []) - { - $request = [ - "symbol" => $symbol, - ]; - return $this->tradesData($this->apiRequest("v3/aggTrades", "GET", array_merge($request, $params))); - } - - /** - * historicalTrades - Get historical trades for a specific currency - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints - * - * @property int $weight 5 - * Standard weight is 5 but if no tradeId is given, weight is 1 - * - * @param string $symbol (mandatory) to query, e.g. BNBBTC - * @param int $limit (optional) limit the amount of trades, default 500, max 1000 - * @param int $tradeId (optional) return the orders from this orderId onwards, negative to get recent ones - * - * @return array containing the response - * @throws \Exception - */ - public function historicalTrades(string $symbol, int $limit = 500, int $tradeId = -1, array $params = []) - { - $request = [ - "symbol" => $symbol, - "limit" => $limit, - ]; - if ($tradeId > 0) { - $request["fromId"] = $tradeId; - } else { - // if there is no tradeId given, we can use v3/trades, weight is 1 and not 5 - return $this->apiRequest("v3/trades", "GET", array_merge($request, $params)); - } - - // The endpoint cannot handle extra parameters like 'timestamp' or 'signature', - // but it needs the http header with the key so we need to construct it here - $query = http_build_query(array_merge($request, $params), '', '&'); - return $this->apiRequest("v3/historicalTrades?$query"); - } - - /** - * depth get Market depth - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints - * - * $depth = $api->depth("ETHBTC"); - * - * @param $symbol string the symbol to get the depth information for - * @param $limit int set limition for number of market depth data - * @return array with error message or array of market depth - * @throws \Exception - */ - public function depth(string $symbol, int $limit = 100, array $params = []) - { - if (is_int($limit) === false) { - $limit = 100; - } - - if (isset($symbol) === false || is_string($symbol) === false) { - // WPCS: XSS OK. - echo "asset: expected bool false, " . gettype($symbol) . " given" . PHP_EOL; - } - $request = [ - "symbol" => $symbol, - "limit" => $limit, - ]; - $json = $this->apiRequest("v3/depth", "GET", array_merge($request, $params)); - if (is_array($json) === false) { - echo "Error: unable to fetch depth" . PHP_EOL; - $json = []; - } - if (empty($json)) { - echo "Error: depth were empty" . PHP_EOL; - return $json; - } - if (isset($this->info[$symbol]) === false) { - $this->info[$symbol] = []; - } - $this->info[$symbol]['firstUpdate'] = $json['lastUpdateId']; - return $this->depthData($symbol, $json); - } - - /** - * balances get balances for the account assets - * - * $balances = $api->balances(); - * - * @param string $market_type (optional) market type - "spot" or "futures" (default is "spot") - * @param array $params (optional) an array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the transfer to complete (not for spot) - * @param string $api_version (optional) not for spot - the api version to use (default is v2) - * - * @return array with error message or array of balances - * @throws \Exception - */ - public function balances(string $market_type = 'spot', array $params = [], string $api_version = 'v2') - { - $is_spot = $market_type === 'spot'; - $request = []; - if ($is_spot) { - $url = "v3/account"; - } else { - $request['fapi'] = true; - if ($api_version === 'v2') { - $url = "v2/balance"; - } else if ($api_version === 'v3') { - $url = "v3/balance"; - } else { - throw new \Exception("balances(): Invalid API version specified. Use 'v2' or 'v3'."); - } - } - $response = $this->httpRequest($url, "GET", array_merge($request, $params), true); - if (is_array($response) === false) { - echo "Error: unable to fetch your account details" . PHP_EOL; - } - if (empty($response) || ($is_spot && (isset($response['balances']) === false || empty($response['balances'])))) { - echo "Error: your balances were empty or unset" . PHP_EOL; - return []; - } - return $this->balanceData($response, $market_type); - } - - /** - * coins get list coins - * - * $coins = $api->coins(); - * @return array with error message or array containing coins - * @throws \Exception - */ - public function coins(array $params = []) - { - return $this->sapiRequest("v1/capital/config/getall", 'GET', $params, true); - } - - /** - * getProxyUriString get Uniform Resource Identifier string assocaited with proxy config - * - * $balances = $api->getProxyUriString(); - * - * @return string uri - */ - public function getProxyUriString() - { - $uri = isset($this->proxyConf['proto']) ? $this->proxyConf['proto'] : "http"; - // https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html - $supportedProtocols = array( - 'http', - 'https', - 'socks4', - 'socks4a', - 'socks5', - 'socks5h', - ); - - if (in_array($uri, $supportedProtocols) === false) { - // WPCS: XSS OK. - echo "Unknown proxy protocol '" . $this->proxyConf['proto'] . "', supported protocols are " . implode(", ", $supportedProtocols) . PHP_EOL; - } - - $uri .= "://"; - $uri .= isset($this->proxyConf['address']) ? $this->proxyConf['address'] : "localhost"; - - if (isset($this->proxyConf['address']) === false) { - // WPCS: XSS OK. - echo "warning: proxy address not set defaulting to localhost" . PHP_EOL; - } - - $uri .= ":"; - $uri .= isset($this->proxyConf['port']) ? $this->proxyConf['port'] : "1080"; + try { + $this->binance->editOrder('ETHUSDT', 0.009, '123456789'); + } catch(\Throwable $e) { - if (isset($this->proxyConf['address']) === false) { - // WPCS: XSS OK. - echo "warning: proxy port not set defaulting to 1080" . PHP_EOL; } + $endpoint = "https://api.binance.com/api/v3/order/amend/keepPriority?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - return $uri; - } + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); - /** - * setProxy set proxy config by passing in an array of the proxy configuration - * - * $proxyConf = [ - * 'proto' => 'tcp', - * 'address' => '192.168.1.1', - * 'port' => '8080', - * 'user' => 'dude', - * 'pass' => 'd00d' - * ]; - * - * $api->setProxy( $proxyconf ); - * - * @return null - */ - public function setProxy(array $proxyconf) - { - $this->proxyConf = $proxyconf; + $this->assertEquals("ETHUSDT", $params['symbol']); + $this->assertEquals(0.009, $params['newQty']); + $this->assertEquals('123456789', $params['orderId']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); } - - protected function curl_exec($curl) + public function testSpotBuy() { - return curl_exec($curl); - } + try { + $this->binance->buy($this->symbol, $this->quantity, $this->price, $this->type); - protected function curl_set_url($curl, $endpoint) { - curl_setopt($curl, CURLOPT_URL, $endpoint); - } + } catch (\Throwable $e) { - protected function curl_set_body($curl, $option, $query) { - curl_setopt($curl, $option, $query); + } + $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertEquals($this->price, $params['price']); + $this->assertEquals($this->type, $params['type']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); } - public function apiRequest($url, $method = "GET", $params = [], $signed = false) + public function testSpotBuyTest() { - return $this->httpRequest($url, $method, $params, $signed); + try { + $this->binance->buyTest($this->symbol, $this->quantity, $this->price, $this->type); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertEquals($this->price, $params['price']); + $this->assertEquals($this->type, $params['type']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); } - public function sapiRequest($url, $method = "GET", $params = [], $signed = false) + public function testSpotSell() { - $params['sapi'] = true; - return $this->httpRequest($url, $method, $params, $signed); + try { + $this->binance->sell($this->symbol, $this->quantity, $this->price, $this->type); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertEquals($this->price, $params['price']); + $this->assertEquals($this->type, $params['type']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); } - public function fapiRequest($url, $method = "GET", $params = [], $signed = false) + public function testSpotSellTest() { - $params['fapi'] = true; - return $this->httpRequest($url, $method, $params, $signed); + try { + $this->binance->sellTest($this->symbol, $this->quantity, $this->price, $this->type); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertEquals($this->price, $params['price']); + $this->assertEquals($this->type, $params['type']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); } - public function fapiDataRequest($url, $method = "GET", $params = [], $signed = false) + public function testSpotMarketQuoteBuy() { - $params['fapiData'] = true; - return $this->httpRequest($url, $method, $params, $signed); + try { + $this->binance->marketQuoteBuy($this->symbol, $this->quantity); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quoteOrderQty']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); } - /** - * httpRequest curl wrapper for all http api requests. - * You can't call this function directly, use the helper functions - * - * @see buy() - * @see sell() - * @see marketBuy() - * @see marketSell() $this->httpRequest( "https://api.binance.com/api/v1/ticker/24hr"); - * - * @param $url string the endpoint to query, typically includes query string - * @param $method string this should be typically GET, POST or DELETE - * @param $params array addtional options for the request - * @param $signed bool true or false sign the request with api secret - * @return array containing the response - * @throws \Exception - */ - protected function httpRequest(string $url, string $method = "GET", array $params = [], bool $signed = false) + public function testSpotMarketQuoteBuyTest() { - if (function_exists('curl_init') === false) { - throw new \Exception("Sorry cURL is not installed!"); - } + try { + $this->binance->marketQuoteBuyTest($this->symbol, $this->quantity); - if ($this->caOverride === false) { - if (file_exists(getcwd() . '/ca.pem') === false) { - $this->downloadCurlCaBundle(); - } - } - if ((!isset ($params['recvWindow'])) && (!is_null($this->recvWindow))) { - $params['recvWindow'] = $this->recvWindow; - } + } catch (\Throwable $e) { - $base = $this->base; - if ($this->useTestnet) { - $base = $this->baseTestnet; } + $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); - if (isset($params['wapi'])) { - if ($this->useTestnet) { - throw new \Exception("wapi endpoints are not available in testnet"); - } - unset($params['wapi']); - $base = $this->wapi; - } + parse_str(self::$capturedBody, $params); - if (isset($params['sapi'])) { - if ($this->useTestnet) { - throw new \Exception("sapi endpoints are not available in testnet"); - } - unset($params['sapi']); - $base = $this->sapi; - } + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quoteOrderQty']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + } - if (isset($params['fapi'])) { - unset($params['fapi']); - $base = $this->useTestnet ? $this->fapiTestnet : $this->fapi; - } + public function testSpotMarketBuy() + { + try { + $this->binance->marketBuy($this->symbol, $this->quantity); - if (isset($params['fapiData'])) { - if ($this->useTestnet) { - throw new \Exception("fapiData endpoints are not available in testnet"); - } - unset($params['fapiData']); - $base = $this->fapiData; - } + } catch (\Throwable $e) { - if (isset($params['dapi'])) { - unset($params['dapi']); - $base = $this->useTestnet ? $this->dapiTestnet : $this->dapi; } + $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); - if (isset($params['dapiData'])) { - if ($this->useTestnet) { - throw new \Exception("dapiData endpoints are not available in testnet"); - } - unset($params['dapiData']); - $base = $this->dapiData; - } + parse_str(self::$capturedBody, $params); - if (isset($params['papi'])) { - if ($this->useTestnet) { - throw new \Exception("papi endpoints are not available in testnet"); - } - unset($params['papi']); - $base = $this->papi; - } + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + } + + public function testSpotMarketBuyTest() + { + try { + $this->binance->marketBuyTest($this->symbol, $this->quantity); + + } catch (\Throwable $e) { - if (isset($params['bapi'])) { - if ($this->useTestnet) { - throw new \Exception("bapi endpoints are not available in testnet"); - } - unset($params['bapi']); - $base = $this->bapi; } + $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); - $curl = curl_init(); - curl_setopt($curl, CURLOPT_VERBOSE, $this->httpDebug); - - //set custom headers if any - curl_setopt_array($curl, [ - CURLOPT_HTTPHEADER => $this->headers, - // Optional: other cURL options - ]); - - $query = $this->binance_build_query($params); - - // signed with params - if ($signed === true) { - if (empty($this->api_key)) { - throw new \Exception("signedRequest error: API Key not set!"); - } - - if (empty($this->api_secret)) { - throw new \Exception("signedRequest error: API Secret not set!"); - } + parse_str(self::$capturedBody, $params); - $ts = (microtime(true) * 1000) + $this->info['timeOffset']; - $params['timestamp'] = number_format($ts, 0, '.', ''); - $query = $this->binance_build_query($params); - $query = str_replace([ '%40' ], [ '@' ], $query);//if send data type "e-mail" then binance return: [Signature for this request is not valid.] - $signature = hash_hmac('sha256', $query, $this->api_secret); - if ($method === "POST") { - $endpoint = $base . $url; - $params['signature'] = $signature; // signature needs to be inside BODY - $query = $this->binance_build_query($params); // rebuilding query - } else { - $endpoint = $base . $url . '?' . $query . '&signature=' . $signature; - } + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + } - $this->curl_set_url($curl, $endpoint); - curl_setopt($curl, CURLOPT_HTTPHEADER, array( - 'X-MBX-APIKEY: ' . $this->api_key, - )); - } - // params so buildquery string and append to url - elseif (count($params) > 0) { - $this->curl_set_url($curl, $base . $url . '?' . $query); - } - // no params so just the base url - else { - $this->curl_set_url($curl, $base . $url); - curl_setopt($curl, CURLOPT_HTTPHEADER, array( - 'X-MBX-APIKEY: ' . $this->api_key, - )); - } - curl_setopt($curl, CURLOPT_USERAGENT, "User-Agent: Mozilla/4.0 (compatible; PHP Binance API)"); - // Post and postfields - if ($method === "POST") { - curl_setopt($curl, CURLOPT_POST, true); - $this->curl_set_body($curl, CURLOPT_POSTFIELDS, $query); - } - // Delete Method - if ($method === "DELETE") { - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); - } - - // PUT Method - if ($method === "PUT") { - curl_setopt($curl, CURLOPT_PUT, true); - } - - // proxy settings - if (is_array($this->proxyConf)) { - curl_setopt($curl, CURLOPT_PROXY, $this->getProxyUriString()); - if (isset($this->proxyConf['user']) && isset($this->proxyConf['pass'])) { - curl_setopt($curl, CURLOPT_PROXYUSERPWD, $this->proxyConf['user'] . ':' . $this->proxyConf['pass']); - } - } - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($curl, CURLOPT_HEADER, true); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_TIMEOUT, 60); - - // set user defined curl opts last for overriding - foreach ($this->curlOpts as $key => $value) { - curl_setopt($curl, constant($key), $value); - } - - if ($this->caOverride === false) { - if (file_exists(getcwd() . '/ca.pem') === false) { - $this->downloadCurlCaBundle(); - } - } - - $output = $this->curl_exec($curl); - // Check if any error occurred - // if (curl_errno($curl) > 0) { - // // should always output error, not only on httpdebug - // // not outputing errors, hides it from users and ends up with tickets on github - // throw new \Exception('Curl error: ' . curl_error($curl)); - // } - - $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE); - $header = $this->get_headers_from_curl_response($output); - $output = substr($output, $header_size); - - curl_close($curl); - - $json = json_decode($output, true); - - $this->lastRequest = [ - 'url' => $url, - 'method' => $method, - 'params' => $params, - 'header' => $header, - 'json' => $json - ]; + public function testSpotMarketQuoteSell() + { + try { + $this->binance->marketQuoteSell($this->symbol, '1'); - if (isset($header['x-mbx-used-weight'])) { - $this->setXMbxUsedWeight($header['x-mbx-used-weight']); - } - - if (isset($header['x-mbx-used-weight-1m'])) { - $this->setXMbxUsedWeight1m($header['x-mbx-used-weight-1m']); - } - if (isset($json['msg']) && !empty($json['msg'])) { - if ($json['msg'] !== 'success' && $url != 'v1/system/status' && $url != 'v3/systemStatus.html' && $url != 'v3/accountStatus.html' && $url != 'v1/allOpenOrders') { - // should always output error, not only on httpdebug - // not outputing errors, hides it from users and ends up with tickets on github - throw new \Exception('signedRequest error: '.print_r($output, true)); - } - } - $this->transfered += strlen($output); - $this->requestCount++; - return $json; - } - - /** - * binance_build_query - Wrapper for http_build_query to allow arrays as parameters - * - * sapi v1/asset/dust can have an array, so it needs a conversion - * - * @param array $params (mandatory) Parameters to convert to http query - * - * @return array containing the response - * @throws \Exception - */ - protected function binance_build_query($params = []) - { - $new_arr = array(); - $query_add = ''; - foreach ($params as $label=>$item) { - if ( gettype($item) == 'array' ) { - foreach ($item as $arritem) { - $query_add = $label . '=' . $arritem . '&' . $query_add; - } - } else { - $new_arr[$label] = $item; - } - } - $query = http_build_query($new_arr, '', '&'); - $query = $query_add . $query; - - return $query; - } - - /** - * Converts the output of the CURL header to an array - * - * @param $header string containing the response - * @return array headers converted to an array - */ - public function get_headers_from_curl_response(string $header) - { - $headers = array(); - $header_text = substr($header, 0, strpos($header, "\r\n\r\n")); - - foreach (explode("\r\n", $header_text) as $i => $line) - if ($i === 0) - $headers['http_code'] = $line; - else { - list ($key, $value) = explode(': ', $line); - $headers[$key] = $value; - } - - return $headers; - } - - /** - * order formats the orders before sending them to the curl wrapper function - * You can call this function directly or use the helper functions - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#sor - * - * @see buy() - * @see sell() - * @see marketBuy() - * @see marketSell() $this->httpRequest( "https://api.binance.com/api/v1/ticker/24hr"); - * - * @param string $side (mandatory) typically "BUY" or "SELL" - * @param string $symbol (mandatory) to buy or sell - * @param string $quantity (mandatory) in the order - * @param string $price (optional) for the order - * @param string $type (optional) is determined by the symbol bu typicall LIMIT, STOP_LOSS_LIMIT etc. (default is LIMIT) - * @param array $params (optional) additional transaction options - * - @param string $params['timeInForce'] - GTC, IOC, FOK - * - @param bool $params['isQuoteOrder'] - if true, quantity is in the quote asset (not for sor orders) - * - @param string $params['newClientOrderId'] - custom client order id - * - @param string $params['strategyId'] - * - @param int $params['strategyType'] - * - @param string $params['stopPrice'] - stop price for STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders (not for SOR orders) - * - @param string $params['trailingDelta'] - for STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders (not for SOR orders) - * - @param string $params['icebergQty'] - used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order - * - @param string $params['newOrderRespType'] - set the response type of the order (ACK, RESULT, FULL) - * - @param string $params['selfTradePreventionMode'] - NONE, EXPIRE_MAKER, EXPIRE_TAKER, EXPIRE_BOTH or DECREMENT (default is NONE) - * - @param bool $params['sor'] - if true, order will be sent to SOR endpoint - * - @param bool $params - * @param $test bool whether to test or not, test only validates the query - * @return array containing the response - * @throws \Exception - */ - public function order(string $side, string $symbol, $quantity, $price, string $type = "LIMIT", array $params = [], bool $test = false) - { - $request = $this->createSpotOrderRequest($side, $symbol, $quantity, $price, $type, $params); - $sor = false; - if (isset($request['sor'])) { - $sor = $request['sor']; - unset($request['sor']); - } - $url = $sor ? "v3/sor/order" : "v3/order"; - if ($test) { - $url = $url . "/test"; - } - return $this->apiRequest($url, "POST", $request, true); // sending only $request cuz $params are already added to $request inside createSpotOrderRequest - } - - /** - * createSpotOrderRequest - * helper function to create a request for the spot order - */ - protected function createSpotOrderRequest(string $side, string $symbol, $quantity, $price, string $type = "LIMIT", array $params = []) - { - $request = [ - "symbol" => $symbol, - "side" => $side, - "type" => $type, - "quantity" => $quantity, - ]; + } catch (\Throwable $e) { - // someone has preformated there 8 decimal point double already - // dont do anything, leave them do whatever they want - if (gettype($price) !== "string") { - // for every other type, lets format it appropriately - $price = number_format($price, 8, '.', ''); } + // warns here cuz method needs information fetched by exchangeInfo + $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); - if (is_numeric($quantity) === false) { - // WPCS: XSS OK. - echo "warning: quantity expected numeric got " . gettype($quantity) . PHP_EOL; - } + parse_str(self::$capturedBody, $params); - if (is_string($price) === false) { - // WPCS: XSS OK. - echo "warning: price expected string got " . gettype($price) . PHP_EOL; - } + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals('SELL', $params['side']); + $this->assertEquals('MARKET', $params['type']); + $this->assertEquals(1, $params['quoteOrderQty']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + } - if ($type === "LIMIT" || $type === "STOP_LOSS_LIMIT" || $type === "TAKE_PROFIT_LIMIT") { - $request["price"] = $price; - $request["timeInForce"] = "GTC"; - } + public function testSpotMarketQuoteSellTest() + { + try { + $this->binance->marketQuoteSellTest($this->symbol, 1); - if ($type === "MARKET" && isset($params['isQuoteOrder']) && $params['isQuoteOrder']) { - unset($request['quantity']); - unset($params['quantity']); - unset($params['isQuoteOrder']); - $request['quoteOrderQty'] = $quantity; - } + } catch (\Throwable $e) { - if (isset($params['stopPrice'])) { - $request['stopPrice'] = $params['stopPrice']; } + $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); - if (isset($params['icebergQty'])) { - $request['icebergQty'] = $params['icebergQty']; - } + parse_str(self::$capturedBody, $params); - if (isset($params['newOrderRespType'])) { - $request['newOrderRespType'] = $params['newOrderRespType']; - } + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals(1, $params['quoteOrderQty']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + } + + public function testSpotMarketSell() + { + try { + $this->binance->marketSell($this->symbol, 1); + + } catch (\Throwable $e) { - if (isset($params['newClientOrderId'])) { - $request['newClientOrderId'] = $params['newClientOrderId']; - } else { - $request['newClientOrderId'] = $this->generateSpotClientOrderId(); } - return array_merge($request, $params); + // warns here cuz method needs information fetched by exchangeInfo + $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals(1, $params['quantity']); + $this->assertEquals('SELL', $params['side']); + $this->assertEquals('MARKET', $params['type']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); } - /** - * editOrder - edits a quantity of an existing order - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#order-amend-keep-priority-trade - * @link https://developers.binance.com/docs/binance-spot-api-docs/faqs/order_amend_keep_priority - * - * @param string $symbol (mandatory) market symbol - * @param string $quantity (mandatory) new quantity (must be greater than 0 and less than the original order quantity) - * @param string $orderId (optional) order id to be amended (mandatory if origClientOrderId is not set) - * @param string $origClientOrderId (optional) original client order id to be amended (mandatory if orderId is not set) - * @param array $params (optional) additional transaction options - * - @param string $params['newClientOrderId'] - custom client order id - * - * @return array containing the response - * @throws \Exception - */ - public function editOrder(string $symbol, $quantity, ?string $orderId = null, ?string $origClientOrderId = null, array $params = []) + public function testSpotMarketSellTest() { - $request = [ - "symbol" => $symbol, - "newQty" => $quantity, - ]; + try { + $this->binance->marketSellTest($this->symbol, $this->quantity); - if (!is_null($orderId)) { - $request['orderId'] = $orderId; - } else if (is_null($origClientOrderId)) { - throw new \Exception('editOrder(): Either orderId or origClientOrderId must be set'); - } else { - $request['origClientOrderId'] = $origClientOrderId; - } - if (isset($params['newClientOrderId'])) { - $request['newClientOrderId'] = $params['newClientOrderId']; - } else { - $request['newClientOrderId'] = $this->generateSpotClientOrderId(); - } - return $this->apiRequest("v3/order/amend/keepPriority", "PUT", array_merge($request, $params), true); - } - - /** - * sorOrder creates an order using the SOR endpoint - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#sor - * @link https://developers.binance.com/docs/binance-spot-api-docs/faqs/sor_faq - * - * @param string $side (mandatory) typically "BUY" or "SELL" - * @param string $symbol (mandatory) to buy or sell - * @param string $quantity (mandatory) in the order - * @param string $price (optional) for the order - * @param string $type (optional) is determined by the symbol bu typicall LIMIT, STOP_LOSS_LIMIT etc. (default is LIMIT) - * @param array $params (optional) additional transaction options - * - @param string $params['timeInForce'] - GTC, IOC, FOK - * - @param string $params['newClientOrderId'] - custom client order id - * - @param string $params['strategyId'] - * - @param int $params['strategyType'] - * - @param string $params['icebergQty'] - used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order - * - @param string $params['newOrderRespType'] - set the response type of the order (ACK, RESULT, FULL) - * - @param string $params['selfTradePreventionMode'] - NONE, EXPIRE_MAKER, EXPIRE_TAKER, EXPIRE_BOTH or DECREMENT (default is NONE) - * - @param bool $params['sor'] - if true, order will be sent to SOR endpoint - * - @param bool $params - * @param $test bool whether to test or not, test only validates the query - * - * @return array containing the response - * @throws \Exception - */ - public function sorOrder(string $side, string $symbol, $quantity, $price, string $type = "LIMIT", array $params = [], bool $test = false) - { - $params['sor'] = true; - return $this->order($side, $symbol, $quantity, $price, $type, $params, $test); - } - - /** - * replaceOrder - cancels an existing order and places a new order on the same symbol - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#cancel-an-existing-order-and-send-a-new-order-trade - * - * @param string $side (mandatory) typically "BUY" or "SELL" - * @param string $symbol (mandatory) to buy or sell - * @param string $quantity (mandatory) in the order - * @param string $price (optional) for the order - * @param string $type (optional) is determined by the symbol bu typicall LIMIT, STOP_LOSS_LIMIT etc. (default is LIMIT) - * @param string $cancelOrderId (optional) the orderId of the order to be replaced (mandatory if cancelOrigClientOrderId is not set) - * @param string $cancelOrigClientOrderId (optional) the origClientOrderId of the order to be replaced (mandatory if cancelOrderId is not set) - * @param bool $allowFailure (optional) if true new order placement will be attempted even if cancel request fails (default is false) - * @param string $cancelRestrictions (optional) ONLY_NEW (cancel will succeed if the order status is NEW) or ONLY_PARTIALLY_FILLED (cancel will succeed if order status is PARTIALLY_FILLED) - * @param string $orderRateLimitExceededMode (optional) DO_NOTHING (default - will only attempt to cancel the order if account has not exceeded the unfilled order rate limit) or CANCEL_ONLY (will always cancel the order) - * @param array $params (optional) additional transaction options (same as for order()) - * - * returns array containing the response - * @throws \Exception - */ - public function replaceOrder(string $side, string $symbol, $quantity, $price, string $type = "LIMIT", ?string $cancelOrderId = null, string $cancelOrigClientOrderId = null, bool $allowFailure = null, string $cancelRestrictions = null, string $orderRateLimitExceededMode = null, array $params = []) - { - $request = $this->createSpotOrderRequest($side, $symbol, $quantity, $price, $type, $params); - if (!is_null($cancelOrderId)) { - $request['cancelOrderId'] = $cancelOrderId; - } else if (is_null($cancelOrigClientOrderId)) { - throw new \Exception("replaceOrder(): Either cancelOrderId or cancelOrigClientOrderId must be set"); - } - if (!is_null($cancelOrigClientOrderId)) { - $request['cancelOrigClientOrderId'] = $cancelOrigClientOrderId; - } - $request['cancelReplaceMode'] = $allowFailure ? 'ALLOW_FAILURE' : 'STOP_ON_FAILURE'; - if (!is_null($cancelRestrictions)) { - $request['cancelRestrictions'] = $cancelRestrictions; - } - if (!is_null($orderRateLimitExceededMode)) { - $request['orderRateLimitExceededMode'] = $orderRateLimitExceededMode; - } - return $this->apiRequest("v3/order/cancelReplace", "POST", $request, true); // only request is needed, params are already added to request inside createSpotOrderRequest - } - /** - * candlesticks get the candles for the given intervals - * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints - * - * $candles = $api->candlesticks("BNBBTC", "5m"); - * - * @param $symbol market symbol to get the response for, e.g. ETHUSDT - * @param $interval string to request - * @param $limit int limit the amount of candles - * @param $startTime string request candle information starting from here - * @param $endTime string request candle information ending here - * @return array containing the response - * @throws \Exception - */ - public function candlesticks(string $symbol, string $interval = "5m", ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - if (!isset($this->charts[$symbol])) { - $this->charts[$symbol] = []; - } - - $request = [ - "symbol" => $symbol, - "interval" => $interval, - ]; + } catch (\Throwable $e) { - if ($limit) { - $request["limit"] = $limit; } + $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); - if ($startTime) { - $request["startTime"] = $startTime; - } + parse_str(self::$capturedBody, $params); - if ($endTime) { - $request["endTime"] = $endTime; - } + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + } + + public function testSpotCancel() + { + try { + $this->binance->cancel($this->symbol, $this->orderid); - $response = $this->apiRequest("v3/klines", "GET", array_merge($request, $params)); + } catch (\Throwable $e) { - if (is_array($response) === false) { - return []; } + $endpoint = "https://api.binance.com/api/v3/order?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - if (count($response) === 0) { - echo "warning: v3/klines returned empty array, usually a blip in the connection or server" . PHP_EOL; - return []; - } + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->orderid, $params['orderId']); - $ticks = $this->chartData($symbol, $interval, $response); - $this->charts[$symbol][$interval] = $ticks; - return $ticks; } - /** - * uiCandlesticks return modified kline data, optimized for presentation of candlestick charts - * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints - * - * $candles = $api->uiCandlesticks("BNBBTC", "5m"); - * - * @param $symbol market symbol to get the response for, e.g. ETHUSDT - * @param $interval string to request - * @param $limit int limit the amount of candles - * @param $startTime string request candle information starting from here - * @param $endTime string request candle information ending here - * @param $params array additional parameters - * - @param string $params['timeZone'] (optional) default is 0 (UTC) - time zone to use for the response - * @return array containing the response - * @throws \Exception - */ - public function uiCandlesticks(string $symbol, string $interval = "5m", ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + public function testSpotOrderStatus() { - $request = [ - "symbol" => $symbol, - "interval" => $interval, - ]; + try { + $this->binance->orderStatus($this->symbol, $this->orderid); - if ($limit) { - $request["limit"] = $limit; - } - - if ($startTime) { - $request["startTime"] = $startTime; - } - - if ($endTime) { - $request["endTime"] = $endTime; - } - - $response = $this->apiRequest("v3/uiKlines", "GET", array_merge($request, $params)); - - return $response; - } - - /** - * balanceData Converts all your balances into a nice array - * - * @param $priceData array of prices - * @return array containing the response - */ - protected function balanceData(array $array, string $marketType = 'spot') - { - $balances = []; - $is_spot = $marketType === 'spot'; - if (empty($array) || ($is_spot && empty($array['balances']))) { - // WPCS: XSS OK. - echo "balanceData error: Please make sure your system time is synchronized: call \$api->useServerTime() before this function" . PHP_EOL; - echo "ERROR: Invalid request. Please double check your API keys and permissions." . PHP_EOL; - return []; - } - $rawBalances = $is_spot ? $array['balances'] : $array; - foreach ($rawBalances as $obj) { - $asset = $obj['asset']; - $avaliable = 0.00000000; - $onOrder = 0.00000000; - if ($is_spot) { - $avaliable = $obj['free']; - $onOrder = $obj['locked']; - $total = $avaliable + $onOrder; - } else { - $avaliable = $obj['availableBalance']; - $total = $obj['balance']; - $onOrder = $total - $avaliable; - } - $balances[$asset] = [ - "available" => $avaliable, - "onOrder" => $onOrder, - "total" => $total, - "info" => $obj, - ]; - } - return $balances; - } - - /** - * balanceHandler Convert balance WebSocket data into array - * - * $data = $this->balanceHandler( $json ); - * - * @param $json array data to convert - * @return array - */ - protected function balanceHandler(array $json) - { - $balances = []; - foreach ($json as $item) { - $asset = $item->a; - $available = $item->f; - $onOrder = $item->l; - $balances[$asset] = [ - "available" => $available, - "onOrder" => $onOrder, - ]; - } - return $balances; - } - - /** - * tickerStreamHandler Convert WebSocket ticker data into array - * - * $data = $this->tickerStreamHandler( $json ); - * - * @param $json object data to convert - * @return array - */ - protected function tickerStreamHandler(\stdClass $json) - { - return [ - "eventType" => $json->e, - "eventTime" => $json->E, - "symbol" => $json->s, - "priceChange" => $json->p, - "percentChange" => $json->P, - "averagePrice" => $json->w, - "prevClose" => $json->x, - "close" => $json->c, - "closeQty" => $json->Q, - "bestBid" => $json->b, - "bestBidQty" => $json->B, - "bestAsk" => $json->a, - "bestAskQty" => $json->A, - "open" => $json->o, - "high" => $json->h, - "low" => $json->l, - "volume" => $json->v, - "quoteVolume" => $json->q, - "openTime" => $json->O, - "closeTime" => $json->C, - "firstTradeId" => $json->F, - "lastTradeId" => $json->L, - "numTrades" => $json->n, - ]; - } + } catch (\Throwable $e) { - /** - * executionHandler Convert WebSocket trade execution into array - * - * $data = $this->executionHandler( $json ); - * - * @param \stdClass $json object data to convert - * @return array - */ - protected function executionHandler(\stdClass $json) - { - return [ - "symbol" => $json->s, - "side" => $json->S, - "orderType" => $json->o, - "quantity" => $json->q, - "price" => $json->p, - "executionType" => $json->x, - "orderStatus" => $json->X, - "rejectReason" => $json->r, - "orderId" => $json->i, - "clientOrderId" => $json->c, - "orderTime" => $json->T, - "eventTime" => $json->E, - ]; - } + } + $endpoint = "https://api.binance.com/api/v3/order?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - /** - * chartData Convert kline data into object - * - * $object = $this->chartData($symbol, $interval, $ticks); - * - * @param $symbol string of your currency - * @param $interval string the time interval - * @param $ticks array of the canbles array - * @return array object of the chartdata - */ - protected function chartData(string $symbol, string $interval, array $ticks, string $market_type = "spot", string $kline_type = 'klines') - { - $output = []; - foreach ($ticks as $tick) { - list($openTime, $open, $high, $low, $close, $assetVolume, $closeTime, $baseVolume, $trades, $assetBuyVolume, $takerBuyVolume, $ignored) = $tick; - $output[$openTime] = [ - "open" => $open, - "high" => $high, - "low" => $low, - "close" => $close, - "volume" => $baseVolume, - "openTime" => $openTime, - "closeTime" => $closeTime, - "assetVolume" => $assetVolume, - "baseVolume" => $baseVolume, - "trades" => $trades, - "assetBuyVolume" => $assetBuyVolume, - "takerBuyVolume" => $takerBuyVolume, - "ignored" => $ignored, - ]; - } - - if ($market_type !== "spot") { - if (!isset($this->info[$market_type])) { - $this->info[$market_type] = []; - } - if (!isset($this->info[$market_type][$symbol])) { - $this->info[$market_type][$symbol] = []; - } - if (!isset($this->info[$market_type][$symbol][$kline_type])) { - $this->info[$market_type][$symbol][$kline_type] = []; - } - if (!isset($this->info[$market_type][$symbol][$kline_type][$interval])) { - $this->info[$market_type][$symbol][$kline_type][$interval] = []; - } - if (isset($openTime)) { - $this->info[$market_type][$symbol][$kline_type][$interval]['firstOpen'] = $openTime; - } - } else { - if (!isset($this->info[$symbol])) { - $this->info[$symbol] = []; - } - if (!isset($this->info[$symbol][$interval])) { - $this->info[$symbol][$interval] = []; - } - if (isset($openTime)) { - $this->info[$symbol][$interval]['firstOpen'] = $openTime; - } - } - - return $output; - } - - /** - * tradesData Convert aggTrades data into easier format - * - * $tradesData = $this->tradesData($trades); - * - * @param $trades array of trade information - * @return array easier format for trade information - */ - protected function tradesData(array $trades) - { - $output = []; - foreach ($trades as $trade) { - $price = $trade['p']; - $quantity = $trade['q']; - $timestamp = $trade['T']; - $maker = $trade['m'] ? 'true' : 'false'; - $output[] = [ - "price" => $price, - "quantity" => $quantity, - "timestamp" => $timestamp, - "maker" => $maker, - ]; - } - return $output; - } - - /** - * bookPriceData Consolidates Book Prices into an easy to use object - * - * $bookPriceData = $this->bookPriceData($array); - * - * @param $array array book prices - * @return array easier format for book prices information - */ - protected function bookPriceData(array $array) - { - $bookprices = []; - foreach ($array as $obj) { - $bookprices[$obj['symbol']] = [ - "bid" => $obj['bidPrice'], - "bids" => $obj['bidQty'], - "ask" => $obj['askPrice'], - "asks" => $obj['askQty'], - ]; - } - return $bookprices; - } - - /** - * priceData Converts Price Data into an easy key/value array - * - * $array = $this->priceData($array); - * - * @param $array array of prices - * @return array of key/value pairs - */ - protected function priceData(array $array) - { - $prices = []; - foreach ($array as $obj) { - $prices[$obj['symbol']] = $obj['price']; - } - return $prices; - } - - /** - * cumulative Converts depth cache into a cumulative array - * - * $cumulative = $api->cumulative($depth); - * - * @param $depth array cache array - * @return array cumulative depth cache - */ - public function cumulative(array $depth) - { - $bids = []; - $asks = []; - $cumulative = 0; - foreach ($depth['bids'] as $price => $quantity) { - $cumulative += $quantity; - $bids[] = [ - $price, - $cumulative, - ]; - } - $cumulative = 0; - foreach ($depth['asks'] as $price => $quantity) { - $cumulative += $quantity; - $asks[] = [ - $price, - $cumulative, - ]; - } - return [ - "bids" => $bids, - "asks" => array_reverse($asks), - ]; - } + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); - /** - * highstock Converts Chart Data into array for highstock & kline charts - * - * $highstock = $api->highstock($chart, $include_volume); - * - * @param $chart array - * @param $include_volume bool for inclusion of volume - * @return array highchart data - */ - public function highstock(array $chart, bool $include_volume = false) - { - $array = []; - foreach ($chart as $timestamp => $obj) { - $line = [ - $timestamp, - floatval($obj['open']), - floatval($obj['high']), - floatval($obj['low']), - floatval($obj['close']), - ]; - if ($include_volume) { - $line[] = floatval($obj['volume']); - } - - $array[] = $line; - } - return $array; - } - - /** - * first Gets first key of an array - * - * $first = $api->first($array); - * - * @param $array array - * @return string key or null - */ - public function first(array $array) - { - if (count($array) > 0) { - return array_keys($array)[0]; - } - return null; - } - - /** - * last Gets last key of an array - * - * $last = $api->last($array); - * - * @param $array array - * @return string key or null - */ - public function last(array $array) - { - if (count($array) > 0) { - return array_keys(array_slice($array, -1))[0]; - } - return null; - } - - /** - * displayDepth Formats nicely for console output - * - * $outputString = $api->displayDepth($array); - * - * @param $array array - * @return string of the depth information - */ - public function displayDepth(array $array) - { - $output = ''; - foreach ([ - 'asks', - 'bids', - ] as $type) { - $entries = $array[$type]; - if ($type === 'asks') { - $entries = array_reverse($entries); - } - - $output .= "{$type}:" . PHP_EOL; - foreach ($entries as $price => $quantity) { - $total = number_format($price * $quantity, 8, '.', ''); - $quantity = str_pad(str_pad(number_format(rtrim($quantity, '.0')), 10, ' ', STR_PAD_LEFT), 15); - $output .= "{$price} {$quantity} {$total}" . PHP_EOL; - } - // echo str_repeat('-', 32).PHP_EOL; - } - return $output; - } - - /** - * depthData Formats depth data for nice display - * - * $array = $this->depthData($symbol, $json); - * - * @param $symbol string to display - * @param $json array of the depth infomration - * @return array of the depth information - */ - protected function depthData(string $symbol, array $json, ?string $product_type = null) - { - $bids = $asks = []; - foreach ($json['bids'] as $obj) { - $bids[$obj[0]] = $obj[1]; - } - foreach ($json['asks'] as $obj) { - $asks[$obj[0]] = $obj[1]; - } - $result = [ - "bids" => $bids, - "asks" => $asks, - ]; - if (isset($product_type)) { - $this->depthCache[$symbol][$product_type] = $result; - } else { - $this->depthCache[$symbol] = $result; - } - return $result; - } - - /** - * roundStep rounds quantity with stepSize - * @param $qty quantity - * @param $stepSize parameter from exchangeInfo - * @return rounded value. example: roundStep(1.2345, 0.1) = 1.2 - * - */ - public function roundStep($qty, $stepSize = 0.1) - { - $precision = strlen(substr(strrchr(rtrim($stepSize, '0'), '.'), 1)); - return round((($qty / $stepSize) | 0) * $stepSize, $precision); - } - - /** - * roundTicks rounds price with tickSize - * @param $value price - * @param $tickSize parameter from exchangeInfo - * @return rounded value. example: roundStep(1.2345, 0.1) = 1.2 - * - */ - public function roundTicks($price, $tickSize) - { - $precision = strlen(rtrim(substr($tickSize, strpos($tickSize, '.', 1) + 1), '0')); - return number_format($price, $precision, '.', ''); - } - - /** - * getTransfered gets the total transfered in b,Kb,Mb,Gb - * - * $transfered = $api->getTransfered(); - * - * @return string showing the total transfered - */ - public function getTransfered() - { - $base = log($this->transfered, 1024); - $suffixes = array( - '', - 'K', - 'M', - 'G', - 'T', - ); - return round(pow(1024, $base - floor($base)), 2) . ' ' . $suffixes[floor($base)]; - } - - /** - * getRequestCount gets the total number of API calls - * - * $apiCount = $api->getRequestCount(); - * - * @return int get the total number of api calls - */ - public function getRequestCount() - { - return $this->requestCount; - } - - /** - * addToTransfered add interger bytes to the total transfered - * also incrementes the api counter - * - * $apiCount = $api->addToTransfered( $int ); - * - * @return null - */ - public function addToTransfered(int $int) - { - $this->transfered += $int; - $this->requestCount++; - } - - /* - * WebSockets - */ - - /** - * depthHandler For WebSocket Depth Cache - * - * $this->depthHandler($json); - * - * @param $json array of depth bids and asks - * @return null - */ - protected function depthHandler(array $json) - { - $symbol = $json['s']; - if ($json['u'] <= $this->info[$symbol]['firstUpdate']) { - return; - } - - foreach ($json['b'] as $bid) { - $this->depthCache[$symbol]['bids'][$bid[0]] = $bid[1]; - if ($bid[1] == "0.00000000") { - unset($this->depthCache[$symbol]['bids'][$bid[0]]); - } - } - foreach ($json['a'] as $ask) { - $this->depthCache[$symbol]['asks'][$ask[0]] = $ask[1]; - if ($ask[1] == "0.00000000") { - unset($this->depthCache[$symbol]['asks'][$ask[0]]); - } - } - } - - /** - * chartHandler For WebSocket Chart Cache - * - * $this->chartHandler($symbol, $interval, $json); - * - * @param $symbol string to sort - * @param $interval string time - * @param \stdClass $json object time - * @return null - */ - protected function chartHandler(string $symbol, string $interval, \stdClass $json) - { - if (!$this->info[$symbol][$interval]['firstOpen']) { // Wait for /kline to finish loading - $this->chartQueue[$symbol][$interval][] = $json; - return; - } - $chart = $json->k; - $symbol = $json->s; - $interval = $chart->i; - $tick = $chart->t; - if ($tick < $this->info[$symbol][$interval]['firstOpen']) { - return; - } - // Filter out of sync data - $open = $chart->o; - $high = $chart->h; - $low = $chart->l; - $close = $chart->c; - $volume = $chart->q; // +trades buyVolume assetVolume makerVolume - $this->charts[$symbol][$interval][$tick] = [ - "open" => $open, - "high" => $high, - "low" => $low, - "close" => $close, - "volume" => $volume, - ]; - } + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->orderid, $params['orderId']); - /** - * sortDepth Sorts depth data for display & getting highest bid and lowest ask - * - * $sorted = $api->sortDepth($symbol, $limit); - * - * @param $symbol string to sort - * @param $limit int depth - * @return null - */ - public function sortDepth(string $symbol, int $limit = 11) - { - $bids = $this->depthCache[$symbol]['bids']; - $asks = $this->depthCache[$symbol]['asks']; - krsort($bids); - ksort($asks); - return [ - "asks" => array_slice($asks, 0, $limit, true), - "bids" => array_slice($bids, 0, $limit, true), - ]; } - /** - * depthCache Pulls /depth data and subscribes to @depth WebSocket endpoint - * Maintains a local Depth Cache in sync via lastUpdateId. - * See depth() and depthHandler() - * - * $api->depthCache(["BNBBTC"], function($api, $symbol, $depth) { - * echo "{$symbol} depth cache update".PHP_EOL; - * //print_r($depth); // Print all depth data - * $limit = 11; // Show only the closest asks/bids - * $sorted = $api->sortDepth($symbol, $limit); - * $bid = $api->first($sorted['bids']); - * $ask = $api->first($sorted['asks']); - * echo $api->displayDepth($sorted); - * echo "ask: {$ask}".PHP_EOL; - * echo "bid: {$bid}".PHP_EOL; - * }); - * - * @param $symbol string optional array of symbols - * @param $callback callable closure - * @return null - */ - public function depthCache($symbols, callable $callback) - { - if (!is_array($symbols)) { - $symbols = [ - $symbols, - ]; - } - - $loop = \React\EventLoop\Factory::create(); - $react = new \React\Socket\Connector($loop); - $connector = new \Ratchet\Client\Connector($loop, $react); - foreach ($symbols as $symbol) { - if (!isset($this->info[$symbol])) { - $this->info[$symbol] = []; - } - - if (!isset($this->depthQueue[$symbol])) { - $this->depthQueue[$symbol] = []; - } - - if (!isset($this->depthCache[$symbol])) { - $this->depthCache[$symbol] = [ - "bids" => [], - "asks" => [], - ]; - } - - $this->info[$symbol]['firstUpdate'] = 0; - $endpoint = strtolower($symbol) . '@depthCache'; - $this->subscriptions[$endpoint] = true; - - $connector($this->getWsEndpoint() . strtolower($symbol) . '@depth')->then(function ($ws) use ($callback, $symbol, $loop, $endpoint) { - $ws->on('message', function ($data) use ($ws, $callback, $loop, $endpoint) { - if ($this->subscriptions[$endpoint] === false) { - //$this->subscriptions[$endpoint] = null; - $loop->stop(); - return; //return $ws->close(); - } - $json = json_decode($data, true); - $symbol = $json['s']; - if (intval($this->info[$symbol]['firstUpdate']) === 0) { - $this->depthQueue[$symbol][] = $json; - return; - } - $this->depthHandler($json); - call_user_func($callback, $this, $symbol, $this->depthCache[$symbol]); - }); - $ws->on('close', function ($code = null, $reason = null) use ($symbol, $loop) { - // WPCS: XSS OK. - echo "depthCache({$symbol}) WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; - $loop->stop(); - }); - }, function ($e) use ($loop, $symbol) { - // WPCS: XSS OK. - echo "depthCache({$symbol})) Could not connect: {$e->getMessage()}" . PHP_EOL; - $loop->stop(); - }); - $this->depth($symbol, 100); - foreach ($this->depthQueue[$symbol] as $data) { - //TODO:: WTF ??? where is json and what should be in it ?? - $this->depthHandler($json); - } - $this->depthQueue[$symbol] = []; - call_user_func($callback, $this, $symbol, $this->depthCache[$symbol]); - } - $loop->run(); - } - - /** - * trades Trades WebSocket Endpoint - * - * $api->trades(["BNBBTC"], function($api, $symbol, $trades) { - * echo "{$symbol} trades update".PHP_EOL; - * print_r($trades); - * }); - * - * @param $symbols - * @param $callback callable closure - * @return null - */ - public function trades($symbols, callable $callback) - { - if (!is_array($symbols)) { - $symbols = [ - $symbols, - ]; - } - - $loop = \React\EventLoop\Factory::create(); - $react = new \React\Socket\Connector($loop); - $connector = new \Ratchet\Client\Connector($loop, $react); - foreach ($symbols as $symbol) { - if (!isset($this->info[$symbol])) { - $this->info[$symbol] = []; - } - - // $this->info[$symbol]['tradesCallback'] = $callback; - - $endpoint = strtolower($symbol) . '@trades'; - $this->subscriptions[$endpoint] = true; - - $connector($this->getWsEndpoint() . strtolower($symbol) . '@aggTrade')->then(function ($ws) use ($callback, $symbol, $loop, $endpoint) { - $ws->on('message', function ($data) use ($ws, $callback, $loop, $endpoint) { - if ($this->subscriptions[$endpoint] === false) { - //$this->subscriptions[$endpoint] = null; - $loop->stop(); - return; //return $ws->close(); - } - $json = json_decode($data, true); - $symbol = $json['s']; - $price = $json['p']; - $quantity = $json['q']; - $timestamp = $json['T']; - $maker = $json['m'] ? 'true' : 'false'; - $trades = [ - "price" => $price, - "quantity" => $quantity, - "timestamp" => $timestamp, - "maker" => $maker, - ]; - // $this->info[$symbol]['tradesCallback']($this, $symbol, $trades); - call_user_func($callback, $this, $symbol, $trades); - }); - $ws->on('close', function ($code = null, $reason = null) use ($symbol, $loop) { - // WPCS: XSS OK. - echo "trades({$symbol}) WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; - $loop->stop(); - }); - }, function ($e) use ($loop, $symbol) { - // WPCS: XSS OK. - echo "trades({$symbol}) Could not connect: {$e->getMessage()}" . PHP_EOL; - $loop->stop(); - }); - } - $loop->run(); - } - - /** - * ticker pulls 24h price change statistics via WebSocket - * - * $api->ticker(false, function($api, $symbol, $ticker) { - * print_r($ticker); - * }); - * - * @param $symbol string optional symbol or false - * @param $callback callable closure - * @return null - */ - public function ticker($symbol, callable $callback) - { - $endpoint = $symbol ? strtolower($symbol) . '@ticker' : '!ticker@arr'; - $this->subscriptions[$endpoint] = true; - - // @codeCoverageIgnoreStart - // phpunit can't cover async function - \Ratchet\Client\connect($this->getWsEndpoint() . $endpoint)->then(function ($ws) use ($callback, $symbol, $endpoint) { - $ws->on('message', function ($data) use ($ws, $callback, $symbol, $endpoint) { - if ($this->subscriptions[$endpoint] === false) { - //$this->subscriptions[$endpoint] = null; - $ws->close(); - return; //return $ws->close(); - } - $json = json_decode($data); - if ($symbol) { - call_user_func($callback, $this, $symbol, $this->tickerStreamHandler($json)); - } else { - foreach ($json as $obj) { - $return = $this->tickerStreamHandler($obj); - $symbol = $return['symbol']; - call_user_func($callback, $this, $symbol, $return); - } - } - }); - $ws->on('close', function ($code = null, $reason = null) { - // WPCS: XSS OK. - echo "ticker: WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; - }); - }, function ($e) { - // WPCS: XSS OK. - echo "ticker: Could not connect: {$e->getMessage()}" . PHP_EOL; - }); - // @codeCoverageIgnoreEnd - } - - /** - * chart Pulls /kline data and subscribes to @klines WebSocket endpoint - * - * $api->chart(["BNBBTC"], "15m", function($api, $symbol, $chart) { - * echo "{$symbol} chart update\n"; - * print_r($chart); - * }); - * - * @param $symbols string required symbols - * @param $interval string time inteval - * @param $callback callable closure - * @param $limit int default 500, maximum 1000 - * @return null - * @throws \Exception - */ - public function chart($symbols, string $interval = "30m", ?callable $callback = null, $limit = 500) - { - if (is_null($callback)) { - throw new Exception("You must provide a valid callback"); - } - if (!is_array($symbols)) { - $symbols = [ - $symbols, - ]; - } - - $loop = \React\EventLoop\Factory::create(); - $react = new \React\Socket\Connector($loop); - $connector = new \Ratchet\Client\Connector($loop, $react); - foreach ($symbols as $symbol) { - if (!isset($this->charts[$symbol])) { - $this->charts[$symbol] = []; - } - - $this->charts[$symbol][$interval] = []; - if (!isset($this->info[$symbol])) { - $this->info[$symbol] = []; - } - - if (!isset($this->info[$symbol][$interval])) { - $this->info[$symbol][$interval] = []; - } - - if (!isset($this->chartQueue[$symbol])) { - $this->chartQueue[$symbol] = []; - } - - $this->chartQueue[$symbol][$interval] = []; - $this->info[$symbol][$interval]['firstOpen'] = 0; - $endpoint = strtolower($symbol) . '@kline_' . $interval; - $this->subscriptions[$endpoint] = true; - $connector($this->getWsEndpoint() . $endpoint)->then(function ($ws) use ($callback, $symbol, $loop, $endpoint, $interval) { - $ws->on('message', function ($data) use ($ws, $loop, $callback, $endpoint) { - if ($this->subscriptions[$endpoint] === false) { - //$this->subscriptions[$endpoint] = null; - $loop->stop(); - return; //return $ws->close(); - } - $json = json_decode($data); - $chart = $json->k; - $symbol = $json->s; - $interval = $chart->i; - $this->chartHandler($symbol, $interval, $json); - call_user_func($callback, $this, $symbol, $this->charts[$symbol][$interval]); - }); - $ws->on('close', function ($code = null, $reason = null) use ($symbol, $loop, $interval) { - // WPCS: XSS OK. - echo "chart({$symbol},{$interval}) WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; - $loop->stop(); - }); - }, function ($e) use ($loop, $symbol, $interval) { - // WPCS: XSS OK. - echo "chart({$symbol},{$interval})) Could not connect: {$e->getMessage()}" . PHP_EOL; - $loop->stop(); - }); - $this->candlesticks($symbol, $interval, $limit); - foreach ($this->chartQueue[$symbol][$interval] as $json) { - $this->chartHandler($symbol, $interval, $json); - } - $this->chartQueue[$symbol][$interval] = []; - call_user_func($callback, $this, $symbol, $this->charts[$symbol][$interval]); - } - $loop->run(); - } - - /** - * kline Subscribes to @klines WebSocket endpoint for latest chart data only - * - * $api->kline(["BNBBTC"], "15m", function($api, $symbol, $chart) { - * echo "{$symbol} chart update\n"; - * print_r($chart); - * }); - * - * @param $symbols string required symbols - * @param $interval string time inteval - * @param $callback callable closure - * @return null - * @throws \Exception - */ - public function kline($symbols, string $interval = "30m", ?callable $callback = null) - { - if (is_null($callback)) { - throw new Exception("You must provide a valid callback"); - } - if (!is_array($symbols)) { - $symbols = [ - $symbols, - ]; - } - - $loop = \React\EventLoop\Factory::create(); - $react = new \React\Socket\Connector($loop); - $connector = new \Ratchet\Client\Connector($loop, $react); - foreach ($symbols as $symbol) { - $endpoint = strtolower($symbol) . '@kline_' . $interval; - $this->subscriptions[$endpoint] = true; - $connector($this->getWsEndpoint() . $endpoint)->then(function ($ws) use ($callback, $symbol, $loop, $endpoint, $interval) { - $ws->on('message', function ($data) use ($ws, $loop, $callback, $endpoint) { - if ($this->subscriptions[$endpoint] === false) { - $loop->stop(); - return; - } - $json = json_decode($data); - $chart = $json->k; - $symbol = $json->s; - $interval = $chart->i; - call_user_func($callback, $this, $symbol, $chart); - }); - $ws->on('close', function ($code = null, $reason = null) use ($symbol, $loop, $interval) { - // WPCS: XSS OK. - echo "kline({$symbol},{$interval}) WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; - $loop->stop(); - }); - }, function ($e) use ($loop, $symbol, $interval) { - // WPCS: XSS OK. - echo "kline({$symbol},{$interval})) Could not connect: {$e->getMessage()}" . PHP_EOL; - $loop->stop(); - }); - } - $loop->run(); - } - - /** - * terminate Terminates websocket endpoints. View endpoints first: print_r($api->subscriptions) - * - * $api->terminate('ethbtc_kline@5m'); - * - * @return null - */ - public function terminate($endpoint) - { - // check if $this->subscriptions[$endpoint] is true otherwise error - $this->subscriptions[$endpoint] = false; - } - - /** - * keepAlive Keep-alive function for userDataStream - * - * $api->keepAlive(); - * - * @return null - */ - public function keepAlive() - { - $loop = \React\EventLoop\Factory::create(); - $loop->addPeriodicTimer(30, function () { - $listenKey = $this->listenKey; - $this->httpRequest("v1/userDataStream?listenKey={$listenKey}", "PUT", []); - }); - $loop->run(); - } - - /** - * userData Issues userDataStream token and keepalive, subscribes to userData WebSocket - * - * $balance_update = function($api, $balances) { - * print_r($balances); - * echo "Balance update".PHP_EOL; - * }; - * - * $order_update = function($api, $report) { - * echo "Order update".PHP_EOL; - * print_r($report); - * $price = $report['price']; - * $quantity = $report['quantity']; - * $symbol = $report['symbol']; - * $side = $report['side']; - * $orderType = $report['orderType']; - * $orderId = $report['orderId']; - * $orderStatus = $report['orderStatus']; - * $executionType = $report['orderStatus']; - * if( $executionType == "NEW" ) { - * if( $executionType == "REJECTED" ) { - * echo "Order Failed! Reason: {$report['rejectReason']}".PHP_EOL; - * } - * echo "{$symbol} {$side} {$orderType} ORDER #{$orderId} ({$orderStatus})".PHP_EOL; - * echo "..price: {$price}, quantity: {$quantity}".PHP_EOL; - * return; - * } - * - * //NEW, CANCELED, REPLACED, REJECTED, TRADE, EXPIRED - * echo "{$symbol} {$side} {$executionType} {$orderType} ORDER #{$orderId}".PHP_EOL; - * }; - * $api->userData($balance_update, $order_update); - * - * @param $balance_callback callable function - * @param bool $execution_callback callable function - * @return null - * @throws \Exception - */ - public function userData(&$balance_callback, &$execution_callback = false) - { - $response = $this->httpRequest("v1/userDataStream", "POST", []); - $this->listenKey = $response['listenKey']; - $this->info['balanceCallback'] = $balance_callback; - $this->info['executionCallback'] = $execution_callback; - - $this->subscriptions['@userdata'] = true; - - $loop = \React\EventLoop\Factory::create(); - $loop->addPeriodicTimer(30*60, function () { - $listenKey = $this->listenKey; - $this->httpRequest("v1/userDataStream?listenKey={$listenKey}", "PUT", []); - }); - $connector = new \Ratchet\Client\Connector($loop); - - // @codeCoverageIgnoreStart - // phpunit can't cover async function - $connector($this->getWsEndpoint() . $this->listenKey)->then(function ($ws) use ($loop) { - $ws->on('message', function ($data) use ($ws) { - if ($this->subscriptions['@userdata'] === false) { - //$this->subscriptions[$endpoint] = null; - $ws->close(); - return; //return $ws->close(); - } - $json = json_decode($data); - $type = $json->e; - if ($type === "outboundAccountPosition") { - $balances = $this->balanceHandler($json->B); - $this->info['balanceCallback']($this, $balances); - } elseif ($type === "executionReport") { - $report = $this->executionHandler($json); - if ($this->info['executionCallback']) { - $this->info['executionCallback']($this, $report); - } - } - }); - $ws->on('close', function ($code = null, $reason = null) use ($loop) { - // WPCS: XSS OK. - echo "userData: WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; - $loop->stop(); - }); - }, function ($e) use ($loop) { - // WPCS: XSS OK. - echo "userData: Could not connect: {$e->getMessage()}" . PHP_EOL; - $loop->stop(); - }); - - $loop->run(); - } - - /** - * miniTicker Get miniTicker for all symbols - * - * $api->miniTicker(function($api, $ticker) { - * print_r($ticker); - * }); - * - * @param $callback callable function closer that takes 2 arguments, $pai and $ticker data - * @return null - */ - public function miniTicker(callable $callback) - { - $endpoint = '@miniticker'; - $this->subscriptions[$endpoint] = true; - - // @codeCoverageIgnoreStart - // phpunit can't cover async function - \Ratchet\Client\connect($this->getWsEndpoint() . '!miniTicker@arr')->then(function ($ws) use ($callback, $endpoint) { - $ws->on('message', function ($data) use ($ws, $callback, $endpoint) { - if ($this->subscriptions[$endpoint] === false) { - //$this->subscriptions[$endpoint] = null; - $ws->close(); - return; //return $ws->close(); - } - $json = json_decode($data, true); - $markets = []; - foreach ($json as $obj) { - $markets[] = [ - "symbol" => $obj['s'], - "close" => $obj['c'], - "open" => $obj['o'], - "high" => $obj['h'], - "low" => $obj['l'], - "volume" => $obj['v'], - "quoteVolume" => $obj['q'], - "eventTime" => $obj['E'], - ]; - } - call_user_func($callback, $this, $markets); - }); - $ws->on('close', function ($code = null, $reason = null) { - // WPCS: XSS OK. - echo "miniticker: WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; - }); - }, function ($e) { - // WPCS: XSS OK. - echo "miniticker: Could not connect: {$e->getMessage()}" . PHP_EOL; - }); - // @codeCoverageIgnoreEnd - } - - /** - * bookTicker Get bookTicker for all symbols - * - * $api->bookTicker(function($api, $ticker) { - * print_r($ticker); - * }); - * - * @param $callback callable function closer that takes 2 arguments, $api and $ticker data - * @return null - */ - public function bookTicker(callable $callback) - { - $endpoint = '!bookticker'; - $this->subscriptions[$endpoint] = true; - - // @codeCoverageIgnoreStart - // phpunit can't cover async function - \Ratchet\Client\connect($this->getWsEndpoint() . '!bookTicker')->then(function ($ws) use ($callback, $endpoint) { - $ws->on('message', function ($data) use ($ws, $callback, $endpoint) { - if ($this->subscriptions[$endpoint] === false) { - //$this->subscriptions[$endpoint] = null; - $ws->close(); - return; //return $ws->close(); - } - $json = json_decode($data, true); - - $markets = [ - "updateId" => $json['u'], - "symbol" => $json['s'], - "bid_price" => $json['b'], - "bid_qty" => $json['B'], - "ask_price" => $json['a'], - "ask_qty" => $json['A'], - ]; - call_user_func($callback, $this, $markets); - }); - $ws->on('close', function ($code = null, $reason = null) { - // WPCS: XSS OK. - echo "miniticker: WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; - }); - }, function ($e) { - // WPCS: XSS OK. - echo "miniticker: Could not connect: {$e->getMessage()}" . PHP_EOL; - }); - // @codeCoverageIgnoreEnd - } - - /** - * Due to ongoing issues with out of date wamp CA bundles - * This function downloads ca bundle for curl website - * and uses it as part of the curl options - */ - protected function downloadCurlCaBundle() - { - $output_filename = getcwd() . "/ca.pem"; - - if (is_writable(getcwd()) === false) { - die(getcwd() . ' folder is not writeable, please check your permissions to download CA Certificates, or use $api->caOverride = true;'); - } + public function testSpotOpenOrders() + { + try { + $this->binance->openOrders($this->symbol); - $host = "https://curl.se/ca/cacert.pem"; - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $host); - curl_setopt($curl, CURLOPT_VERBOSE, 0); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($curl, CURLOPT_HEADER, 0); - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); + } catch (\Throwable $e) { - // proxy settings - if (is_array($this->proxyConf)) { - curl_setopt($curl, CURLOPT_PROXY, $this->getProxyUriString()); - if (isset($this->proxyConf['user']) && isset($this->proxyConf['pass'])) { - curl_setopt($curl, CURLOPT_PROXYUSERPWD, $this->proxyConf['user'] . ':' . $this->proxyConf['pass']); - } } + $endpoint = "https://api.binance.com/api/v3/openOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - $result = curl_exec($curl); - curl_close($curl); + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); - if ($result === false) { - echo "Unable to to download the CA bundle $host" . PHP_EOL; - return; - } + $this->assertEquals($this->symbol, $params['symbol']); + + } + + public function testSpotCancelOpenOrders() + { + try { + $this->binance->cancelOpenOrders($this->symbol); - $fp = fopen($output_filename, 'w'); + } catch (\Throwable $e) { - if ($fp === false) { - echo "Unable to write $output_filename, please check permissions on folder" . PHP_EOL; - return; } + $endpoint = "https://api.binance.com/api/v3/openOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); - fwrite($fp, $result); - fclose($fp); } - protected function floorDecimal($n, $decimals=2) + public function testSpotOrders() { - return floor($n * pow(10, $decimals)) / pow(10, $decimals); - } + try { + $this->binance->orders($this->symbol, $this->limit, $this->fromOrderId); + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/allOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->fromOrderId, $params['orderId']); - protected function setXMbxUsedWeight(int $usedWeight) : void - { - $this->xMbxUsedWeight = $usedWeight; } - protected function setXMbxUsedWeight1m(int $usedWeight1m) : void + public function testSpotHistory() { - $this->xMbxUsedWeight1m = $usedWeight1m; + try { + $this->binance->history($this->symbol, $this->limit, $this->fromTradeId, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/myTrades?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->fromTradeId, $params['fromId']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + } - public function getXMbxUsedWeight() : int + public function testSpotMyTrades() { - return $this->xMbxUsedWeight; + try { + $this->binance->myTrades($this->symbol, $this->limit, $this->fromTradeId, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/myTrades?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->fromTradeId, $params['fromId']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + } - public function getXMbxUsedWeight1m() : int + public function testUseServerTime() { - return $this->xMbxUsedWeight1m; + try { + $this->binance->useServerTime(); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://api.binance.com/api/v3/time", self::$capturedUrl); } - private function getWsEndpoint() : string + public function testTime() { - return $this->useTestnet ? $this->streamTestnet : $this->stream; + try { + $this->binance->time(); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://api.binance.com/api/v3/time", self::$capturedUrl); } - public function isOnTestnet() : bool + public function testSpotExchangeInfo() { - return $this->useTestnet; + try { + $this->binance->exchangeInfo($this->symbols); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/exchangeInfo?"; + $this->assertTrue(str_starts_with (self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + $this->assertTrue(!empty($params['symbols'])); + $symbols = $params['symbols']; + $this->assertTrue(str_contains($symbols, $this->symbols[0])); + $this->assertTrue(str_contains($symbols, $this->symbols[1])); } - /** - * systemStatus - Status indicator for api sapi - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints - * @link https://binance-docs.github.io/apidocs/spot/en/#system-status-system - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api - * - * @property int $weight 2 - * - * @return array containing the response - * @throws \Exception - */ - public function systemStatus(array $params = []) + public function testAssetDetail() { - $arr = array(); - $api_status = $this->apiRequest("v3/ping", 'GET', $params); - if ( empty($api_status) ) { - $arr['api']['status'] = 'ping ok'; - } else { - $arr['api']['status'] = $api_status; - } + try { + $this->binance->assetDetail($this->asset); + + } catch (\Throwable $e) { - $fapi_status = $this->fapiRequest("v1/ping", 'GET', $params); - if ( empty($fapi_status) ) { - $arr['fapi']['status'] = 'ping ok'; - } else { - $arr['fapi']['status'] = $fapi_status; } + $endpoint = "https://api.binance.com/sapi/v1/asset/assetDetail?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->asset, $params['asset']); - $arr['sapi'] = $this->sapiRequest("v1/system/status", 'GET', $params); - return $arr; } - /** - * accountSnapshot - Daily Account Snapshot at 00:00:00 UTC - * - * @link https://binance-docs.github.io/apidocs/spot/en/#daily-account-snapshot-user_data - * - * @property int $weight 1 - * - * @param string $type (mandatory) Should be SPOT, MARGIN or FUTURES - * @param int $nbrDays (optional) Number of days. Default 5, min 5, max 30 - * @param long $startTime (optional) Start time, e.g. 1617580799000 - * @param long $endTime (optional) End time, e.g. 1617667199000 - * - * @return array containing the response - * @throws \Exception - */ - public function accountSnapshot($type, $nbrDays = 5, $startTime = 0, $endTime = 0, array $params = []) + public function testSpotDustLog() { - if ($nbrDays < 5 || $nbrDays > 30) - $nbrDays = 5; + try { + $this->binance->dustLog($this->startTime, $this->endTime); - $request = [ - 'type' => $type, - ]; + } catch (\Throwable $e) { - if ($startTime > 0) - $request['startTime'] = $startTime; - if ($endTime > 0) - $request['endTime'] = $endTime; - if ($nbrDays != 5) - $request['limit'] = $nbrDays; + } + $endpoint = "https://api.binance.com/sapi/v1/asset/dribblet?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - return $this->sapiRequest("v1/accountSnapshot", 'GET', array_merge($request, $params), true); - } - - /** - * accountStatus - Fetch account status detail. - * - * @link https://binance-docs.github.io/apidocs/spot/en/#account-status-user_data - * - * @property int $weight 1 - * - * @return array containing the response - * @throws \Exception - */ - public function accountStatus(array $params = []) - { - $arr = array(); - $arr['sapi'] = $this->sapiRequest("v1/account/status", 'GET', $params, true); - return $arr; - } - - /** - * apiRestriction - Fetch a set of API restrictions - * - * @link https://binance-docs.github.io/apidocs/spot/en/#get-api-key-permission-user_data - * - * @property int $weight 1 - * - * @return array containing the response - * @throws \Exception - */ - public function apiRestrictions(array $params = []) - { - return $this->sapiRequest("v1/account/apiRestrictions", 'GET', $params, true); - } - - /** - * apiTradingStatus - Fetch account API trading status detail. - * - * @link https://binance-docs.github.io/apidocs/spot/en/#account-api-trading-status-user_data - * - * @property int $weight 1 - * - * @return array containing the response - * @throws \Exception - */ - public function apiTradingStatus(array $params = []) - { - $arr = array(); - $arr['sapi'] = $this->sapiRequest("v1/account/apiTradingStatus", 'GET', $params, true); - return $arr; - } - - /** - * orderRateLimit gets the user rate limit - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#query-unfilled-order-count-user_data - * - * $rateLimit = $api->orderRateLimit(); - * - * @property int $weight 1 - * - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the rate limit details - * @throws \Exception - */ - public function orderRateLimit(array $params = []) - { - return $this->apiRequest("v3/rateLimit/order", 'GET', $params, true); - } - - /** - * preventedMatches - Get the list of orders that were expired due to STP - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#query-prevented-matches-user_data - * - * @property int $weight 2 - * - 2 if preventedMatchId is provided - * - 20 if orderId is provided - * - * @param string $symbol (mandatory) The symbol, e.g. BTCUSDT - * @param string $preventedMatchId (optional) The ID of the prevented match (mandatory if orderId is not provided) - * @param string $orderId (optional) The ID of the order (mandatory if preventedMatchId is not provided) - * @param string $fromPreventedMatchId (optional) The ID of the prevented match to start from - * @param int $limit (optional) The number of results to return (default is 500, max is 1000) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - * @return array with error message or the rate limit details - * @throws \Exception - */ - public function preventedMatches(string $symbol, ?string $preventedMatchId = null, ?string $orderId = null, ?string $fromPreventedMatchId = null, ?int $limit = null, array $params = []) { - $request = [ - 'symbol' => $symbol, - ]; + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); - if (is_null($preventedMatchId) && is_null($orderId)) { - throw new \Exception("Either preventedMatchId or orderId must be provided"); - } else if ($preventedMatchId) { - $request['preventedMatchId'] = $preventedMatchId; - } else { - $request['orderId'] = $orderId; - } - - if ($fromPreventedMatchId) { - $request['fromPreventedMatchId'] = $fromPreventedMatchId; - } - - if (!is_null($limit)) { - $request['limit'] = $limit; - } - - return $this->apiRequest("v3/myPreventedMatches", 'GET', array_merge($request, $params), true); - } - - /** - * allocations - Get the allocations resulting from SOR order placement - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#query-allocations-user_data - * - * @property int $weight 20 - * - * @param string $symbol (mandatory) The symbol, e.g. BTCUSDT - * @param int $startTime (optional) The start time of the query - * @param int $endTime (optional) The end time of the query - * @param string $fromAllocationId (optional) The ID of the allocation to start from - * @param int $limit (optional) The number of results to return (default is 500, max is 1000) - * @param string $orderId (optional) The ID of the order - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - * @return array with error message or the rate limit details - * @throws \Exception - */ - public function allocations(string $symbol, ?int $startTime = null, ?int $endTime = null, ?string $fromAllocationId = null, ?int $limit = null, ?string $orderId = null, array $params = []) { - $request = [ - 'symbol' => $symbol, - ]; + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); - if ($startTime) { - $request['startTime'] = $startTime; - } + } - if ($endTime) { - $request['endTime'] = $endTime; - } + public function testSpotTradeFee() + { + try { + $this->binance->tradeFee($this->symbol); - if ($fromAllocationId) { - $request['fromAllocationId'] = $fromAllocationId; - } + } catch (\Throwable $e) { - if (!is_null($limit)) { - $request['limit'] = $limit; } + $endpoint = "https://api.binance.com/sapi/v1/asset/tradeFee?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - if ($orderId) { - $request['orderId'] = $orderId; - } + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); - return $this->apiRequest("v3/myAllocations", 'GET', array_merge($request, $params), true); - } + $this->assertEquals($this->symbol, $params['symbol']); - /** - * commissionRate - Get the commission rate for a symbol - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#query-commission-rates-user_data - * - * @property int $weight 20 - * - * @param string $symbol (mandatory) The symbol, e.g. BTCUSDT - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - * @return array with error message or the rate limit details - * @throws \Exception - */ - public function commissionRate(string $symbol, array $params = []) { - $request = [ - 'symbol' => $symbol, - ]; + } - return $this->apiRequest("v3/account/commission", 'GET', array_merge($request, $params), true); - } - - /** - * orderAmendments - Get all amendments of a single order - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#query-order-amendments-user_data - * - * @property int $weight 4 - * - * @param string $symbol (mandatory) The symbol, e.g. BTCUSDT - * @param string $orderId (mandatory) The ID of the order - * @param string $fromExecutionId (optional) The ID of the execution to start from - * @param int $limit (optional) The number of results to return (default is 500, max is 1000) - * - * @return array with error message or the rate limit details - * @throws \Exception - */ - public function orderAmendments(string $symbol, string $orderId, ?string $fromExecutionId = null, ?int $limit = null, array $params = []) { - $request = [ - 'symbol' => $symbol, - 'orderId' => $orderId, - ]; + public function testSpotCommissionFee() + { + try { + $this->binance->commissionFee($this->symbol); - if ($fromExecutionId) { - $request['fromExecutionId'] = $fromExecutionId; - } - - if (!is_null($limit)) { - $request['limit'] = $limit; - } - - return $this->apiRequest("v3/order/amendments", 'GET', array_merge($request, $params), true); - } - - /** - * ocoOrder - Create a new OCO order - * - * @link https://binance-docs.github.io/apidocs/spot/en/#new-oco-trade - * - * @property int $weight 1 - * - * @param string $side (mandatory) Should be SELL or BUY - * @param string $symbol (mandatory) The symbol, e.g. BTCBUSD - * @param float $quantity (mandatory) Quantity to buy/sell - * @param int $price (mandatory) Price - * @param int $stopprice (mandatory) Stop Price - * @param int $stoplimitprice (optional) Stop Limit Price - * @param int $stoplimittimeinforce (optional) GTC, FOK or IOC - * @param array $params (optional) Extra flags/parameters - * - * @return array containing the response - * @throws \Exception - */ - public function ocoOrder(string $side, string $symbol, $quantity, $price, $stopprice, $stoplimitprice = null, $stoplimittimeinforce = 'GTC', array $params = []) - { - $request = [ - "symbol" => $symbol, - "side" => $side, - ]; + } catch (\Throwable $e) { - if (is_numeric($quantity) === false) { - $error = "Parameter quantity expected numeric for ' $side . ' ' . $symbol .', got " . gettype($quantity); - trigger_error($error, E_USER_ERROR); - } else { - $request['quantity'] = $quantity; } + $endpoint = "https://api.binance.com/sapi/v1/asset/tradeFee?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - if (is_numeric($price) === false) { - $error = "Parameter price expected numeric for ' $side . ' ' . $symbol .', got " . gettype($price); - trigger_error($error, E_USER_ERROR); - } else { - $request['price'] = $price; - } + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); - if (is_numeric($stopprice) === false) { - $error = "Parameter stopprice expected numeric for ' $side . ' ' . $symbol .', got " . gettype($stopprice); - trigger_error($error, E_USER_ERROR); - } else { - $request['stopPrice'] = $stopprice; - } + $this->assertEquals($this->symbol, $params['symbol']); - if (is_null($stoplimitprice) === false && empty($stoplimitprice) === false) { - $request['stopLimitPrice'] = $stoplimitprice; - if ( ($stoplimittimeinforce == 'FOK') || ($stoplimittimeinforce == 'IOC') ) { - $request['stopLimitTimeInForce'] = $stoplimittimeinforce; - } else { - $request['stopLimitTimeInForce'] = 'GTC'; // `Good 'till cancel`. Needed if flag `stopLimitPrice` used. - } - } + } + + public function testWithdraw() + { + try { + $this->binance->withdraw($this->asset, $this->address, $this->amount, $this->addressTag, $this->addressName, $this->transactionFeeFlag, $this->network, $this->orderId); + + } catch (\Throwable $e) { - // Check other flags - foreach (array('icebergQty','stopIcebergQty','listClientOrderId','limitClientOrderId','stopClientOrderId','newOrderRespType') as $flag) { - if ( isset($params[$flag]) && !empty($params[$flag]) ) - $request[$flag] = $params[$flag]; } + $this->assertEquals("https://api.binance.com/sapi/v1/capital/withdraw/apply", self::$capturedUrl); - return $this->apiRequest("v3/order/oco", "POST", $request, true); + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->asset, $params['coin']); + $this->assertEquals($this->address, $params['address']); + $this->assertEquals($this->amount, $params['amount']); + $this->assertEquals($this->addressTag, $params['addressTag']); + $this->assertEquals($this->addressName, $params['name']); + $this->assertEquals($this->transactionFeeFlag, $params['transactionFeeFlag']); + $this->assertEquals($this->network, $params['network']); + $this->assertEquals($this->orderId, $params['withdrawOrderId']); } - /** - * avgPrice - get the average price of a symbol based on the last 5 minutes - * - * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints - * - * $avgPrice = $api->avgPrice( "ETHBTC" ); - * - * @property int $weight 1 - * - * @param string $symbol (mandatory) a symbol, e.g. ETHBTC - * - * @return string with symbol price - * @throws \Exception - */ - public function avgPrice(string $symbol, array $params = []) + public function testDepositAddress() { - $request = [ - 'symbol' => $symbol, - ]; - $ticker = $this->apiRequest("v3/avgPrice", "GET", array_merge($request, $params)); - if (is_array($ticker) === false) { - echo "Error: unable to fetch avg price" . PHP_EOL; - $ticker = []; - } - if (empty($ticker)) { - echo "Error: avg price was empty" . PHP_EOL; - return null; - } - return $ticker['price']; - } - - - /********************************************* - * - * Binance Liquid Swap (bswap) functions - * - * https://binance-docs.github.io/apidocs/spot/en/#bswap-endpoints - * - *********************************************/ - - /** - * bswapQuote - Request a quote for swap of quote asset (selling) or base asset (buying), essentially price/exchange rates. - * - * @property int $weight 2 - * - * @param string $baseAsset (mandatory) e.g. ETH - * @param string $quoteAsset (mandatory) e.g. BTC - * @param string $quoteQty (mandatory) - * - * @return array containing the response - * @throws \Exception - */ - public function bswapQuote($baseAsset, $quoteAsset, $quoteQty, array $params = []) { - $request = [ - 'quoteAsset' => $quoteAsset, - 'baseAsset' => $baseAsset, - 'quoteQty' => $quoteQty, - ]; + try { + $this->binance->depositAddress($this->asset, $this->network); - return $this->sapiRequest("v1/bswap/quote", 'GET', array_merge($request, $params), true); - } - - /********************************************* - * - * Binance futures (fapi) functions - * - * https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info - * - *********************************************/ - - /** - * futuresTime Gets the server time - * - * $time = $api->futuresTime(); - * - * @return array with error message or array with server time key - * @throws \Exception - */ - public function futuresTime(array $params = []) - { - return $this->fapiRequest("v1/time", "GET", $params); - } - - /** - * futuresExchangeInfo - Gets the complete exchange info, including limits, currency options etc. for futures - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Exchange-Information - * - * $info = $api->futuresExchangeInfo(); - * - * @property int $weight 1 - * - * @return array containing the response - * @throws \Exception - */ - public function futuresExchangeInfo(array $params = []) - { - if (!$this->futuresExchangeInfo) { - $arr = $this->fapiRequest("v1/exchangeInfo", "GET", $params); - if ((is_array($arr) === false) || empty($arr)) { - echo "Error: unable to fetch futures exchange info" . PHP_EOL; - $arr = array(); - $arr['symbols'] = array(); - } - $this->futuresExchangeInfo = $arr; - $this->futuresExchangeInfo['symbols'] = null; - - foreach ($arr['symbols'] as $key => $value) { - $this->futuresExchangeInfo['symbols'][$value['symbol']] = $value; - } - } - - return $this->futuresExchangeInfo; - } - - /** - * futuresDepth get Market depth for futures - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Order-Book - * - * $depth = $api->futuresDepth("ETHBTC"); - * - * @property int $weight 10 - * for limit 5, 10, 20, 50 - weight 2 - * for limit 100 - weight 5 - * for limit 500 (default) - weight 10 - * for limit 1000 - weight 20 - * - * @param string $symbol (mandatory) the symbol to get the depth information for, e.g. ETHUSDT - * @param int $limit (optional) $limit set limition for number of market depth data, default 500, max 1000 (possible values are 5, 10, 20, 50, 100, 500, 1000) - * @return array with error message or array of market depth - * @throws \Exception - */ - public function futuresDepth(string $symbol, ?int $limit = null, array $params = []) - { - if (isset($symbol) === false || is_string($symbol) === false) { - // WPCS: XSS OK. - echo "asset: expected bool false, " . gettype($symbol) . " given" . PHP_EOL; - } - - $request = [ - 'symbol' => $symbol, - ]; - if ($limit) { - $request['limit'] = $limit; - } - $json = $this->fapiRequest("v1/depth", "GET", array_merge($request, $params)); - if (is_array($json) === false) { - echo "Error: unable to fetch futures depth" . PHP_EOL; - $json = []; - } - if (empty($json)) { - echo "Error: futures depth were empty" . PHP_EOL; - return $json; - } - if (isset($this->info[$symbol]) === false) { - $this->info[$symbol] = []; - $this->info[$symbol]['futures'] = []; - } - $this->info[$symbol]['futures']['firstUpdate'] = $json['lastUpdateId']; - return $this->depthData($symbol, $json, 'futures'); - } - - /** - * futuresRecentTrades - Get recent trades for a specific currency - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Recent-Trades-List - * - * $trades = $api->futuresRecentTrades("ETHBTC"); - * - * @property int $weight 5 - * - * @param string $symbol (mandatory) the symbol to query, e.g. ETHUSDT - * @param int $limit (optional) limit the amount of trades, default 500, max 1000 - * - * @return array containing the response - * @throws \Exception - */ - public function futuresRecentTrades(string $symbol, ?int $limit = null, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - if ($limit) { - $request['limit'] = $limit; - } - return $this->fapiRequest("v1/trades", "GET", array_merge($request, $params)); - } - - /** - * futuresHistoricalTrades - Get historical trades for a specific currency - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Old-Trades-Lookup - * - * $trades = $api->futuresHistoricalTrades("ETHBTC"); - * - * @property int $weight 20 - * - * @param string $symbol (mandatory) the symbol to query, e.g. ETHUSDT - * @param int $limit (optional) limit the amount of trades, default 100, max 500 - * @param int $tradeId (optional) return the orders from this orderId onwards, negative to get recent ones - * - * @return array containing the response - * @throws \Exception - */ - public function futuresHistoricalTrades(string $symbol, $limit = null, $tradeId = null, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - if ($limit) { - $request['limit'] = $limit; - } - if ($tradeId) { - $request['fromId'] = $tradeId; - } - return $this->fapiRequest("v1/historicalTrades", "GET", array_merge($request, $params), true); - } - - /** - * futuresAggTrades get Market History / Aggregate Trades - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Compressed-Aggregate-Trades-List - * - * $trades = $api->futuresAggTrades("BNBBTC"); - * - * @property int $weight 20 - * - * @param string $symbol (mandatory) the symbol to get the trade information for, e.g. ETHUSDT - * @param int $fromId (optional) ID to get aggregate trades from INCLUSIVE - * @param int $startTime (optional) timestamp in ms to get aggregate trades from INCLUSIVE - * @param int $endTime (optional) timestamp in ms to get aggregate trades until INCLUSIVE - * @param int $limit (optional) the amount of trades, default 500, max 1000 - * - * @return array with error message or array of market history - * @throws \Exception - */ - public function futuresAggTrades(string $symbol, ?int $fromId = null, ?int $startTime = null, ?int $endTime = null, ?int $limit = null, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - if ($fromId) { - $request['fromId'] = $fromId; - } - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - if ($limit) { - $request['limit'] = $limit; - } - return $this->tradesData($this->fapiRequest("v1/aggTrades", "GET", array_merge($request, $params))); - } - - /** - * futuresCandlesticks get the candles for the given intervals - * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Kline-Candlestick-Data - * - * $candles = $api->futuresCandlesticks("BNBBTC", "5m"); - * - * @property int $weight 5 - * for limit < 100 - weight 1 - * for limit < 500 - weight 2 - * for limit <= 1000 - weight 5 - * for limit > 1000 - weight 10 - * - * @param string $symbol (mandatory) the symbol to query, e.g. ETHUSDT - * @param string $interval (optional) string to request - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M (default 5m) - * @param int $limit (optional) int limit the amount of candles (default 500, max 1000) - * @param int $startTime (optional) string request candle information starting from here - * @param int $endTime (optional) string request candle information ending here - * - * @return array containing the response - * @throws \Exception - */ - public function futuresCandlesticks(string $symbol, string $interval = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - return $this->futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, 'klines', null, $params); - } - - /** - * futuresContinuousCandlesticks get the candles for the given intervals - * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Continuous-Contract-Kline-Candlestick-Data - * - * $candles = $api->futuresContinuousCandlesticks("BNBBTC", "5m"); - * - * @property int $weight 5 - * for limit < 100 - weight 1 - * for limit < 500 - weight 2 - * for limit <= 1000 - weight 5 - * for limit > 1000 - weight 10 - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $interval (optional) string to request - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M (default 5m) - * @param int $limit (optional) int limit the amount of candles (default 500, max 1000) - * @param int $startTime (optional) string request candle information starting from here - * @param int $endTime (optional) string request candle information ending here - * @param string $contractType (optional) string to request - PERPETUAL, CURRENT_QUARTER, NEXT_QUARTER (default PERPETUAL) - * - * @return array containing the response - * @throws \Exception - */ - public function futuresContinuousCandlesticks(string $symbol, string $interval = '5m', ?int $limit = null, $startTime = null, $endTime = null, $contractType = 'PERPETUAL', array $params = []) - { - return $this->futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, 'continuousKlines', $contractType, $params); - } - - /** - * futuresIndexPriceCandlesticks get the candles for the given intervals - * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Index-Price-Kline-Candlestick-Data - * - * $candles = $api->futuresIndexPriceCandlesticks("BNBBTC", "5m"); - * - * @property int $weight 5 - * for limit < 100 - weight 1 - * for limit < 500 - weight 2 - * for limit <= 1000 - weight 5 - * for limit > 1000 - weight 10 - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $interval (optional) string to request - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M (default 5m) - * @param int $limit (optional) int limit the amount of candles (default 500, max 1000) - * @param int $startTime (optional) string request candle information starting from here - * @param int $endTime (optional) string request candle information ending here - * - * @return array containing the response - * @throws \Exception - */ - public function futuresIndexPriceCandlesticks(string $symbol, string $interval = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - return $this->futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, 'indexPriceKlines', null, $params); - } - - /** - * futuresMarkPriceCandlesticks get the candles for the given intervals - * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Mark-Price-Kline-Candlestick-Data - * - * $candles = $api->futuresMarkPriceCandlesticks("BNBBTC", "5m"); - * - * @property int $weight 5 - * for limit < 100 - weight 1 - * for limit < 500 - weight 2 - * for limit <= 1000 - weight 5 - * for limit > 1000 - weight 10 - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $interval (optional) string to request - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M (default 5m) - * @param int $limit (optional) int limit the amount of candles (default 500, max 1000) - * @param int $startTime (optional) string request candle information starting from here - * @param int $endTime (optional) string request candle information ending here - * - * @return array containing the response - * @throws \Exception - */ - public function futuresMarkPriceCandlesticks(string $symbol, string $interval = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - return $this->futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, 'markPriceKlines', null, $params); - } - - /** - * futuresPremiumIndexCandlesticks get the candles for the given intervals - * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Premium-Index-Kline-Data - * - * $candles = $api->futuresPremiumIndexCandlesticks("ETHBTC", "5m"); - * - * @property int $weight 5 - * for limit < 100 - weight 1 - * for limit < 500 - weight 2 - * for limit <= 1000 - weight 5 - * for limit > 1000 - weight 10 - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $interval (optional) string to request - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M (default 5m) - * @param int $limit (optional) int limit the amount of candles (default 500, max 1000) - * @param int $startTime (optional) string request candle information starting from here - * @param int $endTime (optional) string request candle information ending here - * - * @return array containing the response - * @throws \Exception - */ - public function futuresPremiumIndexCandlesticks(string $symbol, string $interval = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - return $this->futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, 'premiumIndexKlines', null, $params); - } - - /** - * futuresCandlesticksHelper - * helper for routing the futuresCandlesticks, futuresContinuousCandlesticks, futuresIndexPriceCandlesticks, futuresMarkPriceCandlesticks and futuresPremiumIndexKlines - */ - private function futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, $klineType, $contractType = null, $params = []) - { - if (!isset($this->charts['futures'])) { - $this->charts['futures'] = []; - } - if (!isset($this->charts['futures'][$symbol])) { - $this->charts['futures'][$symbol] = []; - } - if (!isset($this->charts['futures'][$symbol][$contractType])) { - $this->charts['futures'][$symbol][$contractType] = []; - } - if (!isset($this->charts['futures'][$symbol][$contractType][$interval])) { - $this->charts['futures'][$symbol][$contractType][$interval] = []; - } - $request = [ - 'interval' => $interval, - ]; - if ($klineType === 'continuousKlines' || $klineType === 'indexPriceKlines') { - $request['pair'] = $symbol; - } else { - $request['symbol'] = $symbol; - } - if ($limit) { - $request['limit'] = $limit; - } - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - if ($contractType) { - $request['contractType'] = $contractType; - } - - $response = $this->fapiRequest("v1/{$klineType}", 'GET', array_merge($request, $params)); - - if (is_array($response) === false) { - return []; - } - if (count($response) === 0) { - echo "warning: fapi/v1/{$klineType} returned empty array, usually a blip in the connection or server" . PHP_EOL; - return []; - } - - $candlesticks = $this->chartData($symbol, $interval, $response, 'futures', $klineType); - $this->charts['futures'][$symbol][$klineType][$interval] = $candlesticks; - return $candlesticks; - } - - /** - * futuresMarkPrice get the mark price for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Mark-Price - * - * $markPrice = $api->futuresMarkPrice(); - * $markPrice = $api->futuresMarkPrice("ETHBTC"); - * - * @property int $weight 1 - * - * @param string $symbol (optional) market symbol to get the response for, e.g. ETHUSDT - * - * @return array containing the response - * @throws \Exception - */ - public function futuresMarkPrice(?string $symbol = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - return $this->fapiRequest("v1/premiumIndex", "GET", array_merge($request, $params)); - } - - /** - * futuresFundingRateHistory get the funding rate history for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-History - * - * $fundingRate = $api->futuresFundingRateHistory(); - * $fundingRate = $api->futuresFundingRateHistory("ETHBTC"); - * - * @param string $symbol (optional) market symbol to get the response for, e.g. ETHUSDT - * @param int $limit (optional) int limit the amount of funding rate history (default 100, max 1000) - * @param int $startTime (optional) string request funding rate history starting from here - * @param int $endTime (optional) string request funding rate history ending here - * - * @return array containing the response - * @throws \Exception - */ - public function futuresFundingRateHistory(?string $symbol = null, ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - if ($limit) { - $request['limit'] = $limit; - } - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - return $this->fapiRequest("v1/fundingRate", "GET", array_merge($request, $params)); - } - - /** - * futuresFundingInfo get the funding rate history for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-Info - * - * $fundingInfo = $api->futuresFundingInfo(); - * - * @property int $weight 1 - * - * @return array containing the response - * @throws \Exception - */ - public function futuresFundingInfo(array $params = []) - { - return $this->fapiRequest("v1/fundingInfo", "GET", $params); - } - - /** - * futuresPrevDay get 24 hour price change statistics for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/24hr-Ticker-Price-Change-Statistics - * - * $ticker = $api->futuresPrevDay(); - * $ticker = $api->futuresPrevDay("ETHBTC"); - * - * @property int $weight 1 - * if the symbol parameter is omitted weight is 40 - * - * @param string $symbol (optional) market symbol to get the response for, e.g. ETHUSDT - * - * @return array containing the response - * @throws \Exception - */ - public function futuresPrevDay(?string $symbol = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - return $this->fapiRequest("v1/ticker/24hr", "GET", array_merge($request, $params)); - } - - /** - * futuresPrice get the latest price for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Price-Ticker - * - * $price = $api->futuresPrice('ETHUSDT'); - * - * @property int $weight 1 - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * - * @return array containing the response - * @throws \Exception - */ - public function futuresPrice(string $symbol, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - $ticker = $this->fapiRequest("v1/ticker/price", "GET", array_merge($request, $params)); - if (!isset($ticker['price'])) { - echo "Error: unable to fetch futures price for $symbol" . PHP_EOL; - return null; - } - return $ticker['price']; - } - - /** - * futuresPrices get the latest price for all symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Price-Ticker - * - * $price = $api->futuresPrices(); - * - * @property int $weight 2 - * - * @return array containing the response - * @throws \Exception - */ - public function futuresPrices(array $params = []) - { - return $this->priceData($this->fapiRequest("v1/ticker/price", "GET", $params)); - } - - /** - * futuresPriceV2 get the latest price for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Price-Ticker-v2 - * - * $price = $api->futuresPriceV2('ETHBTC'); - * - * @property int $weight 1 - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * - * @return array containing the response - * @throws \Exception - */ - public function futuresPriceV2(string $symbol, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - $ticker = $this->fapiRequest("v2/ticker/price", "GET", $request); - if (!isset($ticker['price'])) { - echo "Error: unable to fetch futures price for $symbol" . PHP_EOL; - return null; - } - return $ticker['price']; - } - - /** - * futuresPricesV2 get the latest price for all symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Price-Ticker-v2 - * - * $price = $api->futuresPricesV2(); - * - * @property int $weight 2 - * - * @return array containing the response - * @throws \Exception - */ - public function futuresPricesV2(array $params = []) - { - return $this->priceData($this->fapiRequest("v2/ticker/price", "GET", $params)); - } - - /** - * futuresSymbolOrderBookTicker get the best price/qty on the order book for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Order-Book-Ticker - * - * $ticker = $api->futuresSymbolOrderBookTicker(); - * $ticker = $api->futuresSymbolOrderBookTicker("ETHBTC"); - * - * @property int $weight 2 - * 5 when the symbol parameter is omitted - * - * @param string $symbol (optional) market symbol to get the response for, e.g. ETHUSDT - * - * @return array containing the response - * @throws \Exception - */ - public function futuresSymbolOrderBookTicker(?string $symbol = null, array $params = []): array - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - return $this->fapiRequest("v1/ticker/bookTicker", "GET", array_merge($request, $params)); - } - - /** - * futuresDeliveryPrice get the latest delivery price for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Delivery-Price - * - * $price = $api->futuresDeliveryPrice("ETHBTC"); - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * - * @return array containing the response - * @throws \Exception - */ - public function futuresDeliveryPrice(string $symbol, array $params = []): array - { - $request = [ - 'pair' => $symbol, - ]; + } catch (\Throwable $e) { - return $this->fapiDataRequest("delivery-price", "GET", array_merge($request, $params)); - } - - /** - * futuresOpenInterest get the open interest for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest - * - * $openInterest = $api->futuresOpenInterest("ETHBTC"); - * - * @property int $weight 1 - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * - * @return array containing the response - * @throws \Exception - */ - public function futuresOpenInterest(string $symbol, array $params = []): array - { - $request = [ - 'symbol'=> $symbol, - ]; - return $this->fapiRequest("v1/openInterest", 'GET', array_merge($request, $params)); + } + $endpoint = "https://api.binance.com/sapi/v1/capital/deposit/address?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->asset, $params['coin']); + $this->assertEquals($this->network, $params['network']); } - /** - * sapieriodLimitStartEndFuturesDataRequest - * helper for routing GET methods that require symbol, period, limit, startTime and endTime - */ - private function sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, $url, array $params = []) + public function testDepositHistory() { - $request = [ - 'symbol' => $symbol, - 'period' => $period, - ]; - if ($limit) { - $request['limit'] = $limit; - } - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - return $this->fapiDataRequest($url, 'GET', array_merge($request, $params)); - } - - /** - * futuresOpenInterestHistory get the open interest history for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest-Statistics - * - * $openInterest = $api->futuresOpenInterestHistory("ETHBTC", 5m); - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) - * @param int $limit (optional) int limit the amount of open interest history (default 100, max 1000) - * @param int $startTime (optional) string request open interest history starting from here - * @param int $endTime (optional) string request open interest history ending here - * - * @return array containing the response - * @throws \Exception - */ - public function futuresOpenInterestHistory(string $symbol, string $period = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - return $this->sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, 'openInterestHist', $params); - } - - /** - * futuresTopLongShortPositionRatio get the proportion of net long and net short positions to total open positions of the top 20% users with the highest margin balance - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Top-Trader-Long-Short-Ratio - * - * $ratio = $api->futuresTopLongShortPositionRatio("ETHBTC", 5m); - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) - * @param int $limit (optional) int limit the amount of open interest history (default 30, max 500) - * @param int $startTime (optional) string request open interest history starting from here - * @param int $endTime (optional) string request open interest history ending here - * - * @return array containing the response - * @throws \Exception - */ - public function futuresTopLongShortPositionRatio(string $symbol, string $period = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - return $this->sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, 'topLongShortPositionRatio', $params); - } - - /** - * futuresTopLongShortAccountRatio get the proportion of net long and net short accounts to total accounts of the top 20% users with the highest margin balance - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Top-Long-Short-Account-Ratio - * - * $ratio = $api->futuresTopLongShortAccountRatio("ETHBTC", 5m); - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) - * @param int $limit (optional) int limit the amount of open interest history (default 30, max 500) - * @param int $startTime (optional) string request open interest history starting from here - * @param int $endTime (optional) string request open interest history ending here - * - * @return array containing the response - * @throws \Exception - */ - public function futuresTopLongShortAccountRatio(string $symbol, string $period = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - return $this->sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, 'topLongShortAccountRatio', $params); - } - - /** - * futuresGlobalLongShortAccountRatio get the Long/Short Ratio for symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Long-Short-Ratio - * - * $ratio = $api->futuresGlobalLongShortAccountRatio("ETHBTC", 5m); - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) - * @param int $limit (optional) int limit the amount of open interest history (default 30, max 500) - * @param int $startTime (optional) string request open interest history starting from here - * @param int $endTime (optional) string request open interest history ending here - * - * @return array containing the response - * @throws \Exception - */ - public function futuresGlobalLongShortAccountRatio(string $symbol, string $period = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - return $this->sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, 'globalLongShortAccountRatio', $params); - } - - /** - * futuresTakerLongShortRatio get the taker Long/Short Ratio for symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Taker-BuySell-Volume - * - * $ratio = $api->futuresTakerLongShortRatio("ETHBTC", 5m); - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) - * @param int $limit (optional) int limit the amount of open interest history (default 30, max 500) - * @param int $startTime (optional) string request open interest history starting from here - * @param int $endTime (optional) string request open interest history ending here - * - * @return array containing the response - * @throws \Exception - */ - public function futuresTakerLongShortRatio(string $symbol, string $period = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) - { - return $this->sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, 'takerlongshortRatio', $params); - } - - - /** - * futuresBasis get future basis for symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Basis - * - * $basis = $api->futuresBasis("ETHBTC", 5m); - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) - * @param int $limit (optional) int limit the amount of open interest history (default 30, max 500) - * @param int $startTime (optional) string request open interest history starting from here - * @param int $endTime (optional) string request open interest history ending here - * @param string $contractType (optional) string of period to query - PERPETUAL, CURRENT_QUARTER, NEXT_QUARTER (default PERPETUAL) - * - * @return array containing the response - * @throws \Exception - */ - public function futuresBasis(string $symbol, string $period = '5m', int $limit = 30, $startTime = null, $endTime = null, $contractType = 'PERPETUAL', array $params = []) - { - $request = [ - 'pair' => $symbol, - 'period' => $period, - 'contractType' => $contractType, - ]; - if ($limit) { - $request['limit'] = $limit; - } - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - return $this->fapiDataRequest("basis", 'GET', array_merge($request, $params)); - } - - /** - * futuresIndexInfo get composite index symbol information - * only for composite index symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Composite-Index-Symbol-Information - * - * $indexInfo = $api->futuresIndexInfo("DEFIUSDT"); - * - * @property int $weight 1 - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. DEFIUSDT - * - * @return array containing the response - * @throws \Exception - */ - public function futuresIndexInfo(string $symbol, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - return $this->fapiRequest("v1/indexInfo", 'GET', array_merge($request, $params)); - } - - /** - * asset assetIndex get the asset index for a symbol or all symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Multi-Assets-Mode-Asset-Index - * - * $assetIndex = $api->futuresAssetIndex(); - * $assetIndex = $api->futuresAssetIndex("USDCUSD"); - * - * @property int $weight 1 - * with symbol parameter omitted weight is 10 - * - * @param string $symbol (optional) market symbol to get the response for, e.g. USDCUSD - * - * @return array containing the response - * @throws \Exception - */ - public function futuresAssetIndex(?string $symbol = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - return $this->fapiRequest("v1/assetIndex", 'GET', array_merge($request, $params)); - } - - /** - * futuresConstituents get the index price constituents - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Index-Constituents - * - * $constituents = $api->futuresConstituents("BTCUSDT"); - * - * @property int $weight 2 - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * - * @return array containing the response - * @throws \Exception - */ - public function futuresConstituents(string $symbol, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - return $this->fapiRequest("v1/constituents", 'GET', array_merge($request, $params)); - } - - /** - * createFuturesOrderRequest - * helper for creating the request for futures order - * @return array containing the request - * @throws \Exception - */ - protected function createFuturesOrderRequest(string $side, string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = []) { - $request = [ - 'symbol' => $symbol, - 'side' => $side, - 'type' => $type, - ]; + try { + $this->binance->depositHistory($this->asset); + + } catch (\Throwable $e) { - // someone has preformated there 8 decimal point double already - // dont do anything, leave them do whatever they want - if ($price && gettype($price) !== 'string') { - // for every other type, lets format it appropriately - $price = number_format($price, 8, '.', ''); - } - - if ($quantity) { - if (is_numeric($quantity) === false) { - // WPCS: XSS OK. - echo "warning: quantity expected numeric got " . gettype($quantity) . PHP_EOL; - } - if (isset($params['closePosition']) && $params['closePosition'] === true) { - // WPCS: XSS OK. - echo "warning: closePosition is set to true, quantity will be ignored" . PHP_EOL; - } else { - $request['quantity'] = $quantity; - } - } - - if ($price && is_string($price) === false) { - // WPCS: XSS OK. - echo "warning: price expected string got " . gettype($price) . PHP_EOL; - } - - if ($type === "LIMIT" || $type === "STOP_LOSS_LIMIT" || $type === "TAKE_PROFIT_LIMIT" || $type === "STOP" || $type === "TAKE_PROFIT") { - $request["price"] = $price; - if (!isset($params['timeInForce'])) { - $request['timeInForce'] = 'GTC'; - } - } - - if (isset($params['reduceOnly'])) { - $reduceOnly = $params['reduceOnly']; - if ($reduceOnly === true) { - $request['reduceOnly'] = 'true'; - } else { - $request['reduceOnly'] = 'false'; - } - unset($params['reduceOnly']); - } - - if (!isset($params['newClientOrderId'])) { - $request['newClientOrderId'] = $this->generateFuturesClientOrderId(); - } - - if (isset($params['closePosition'])) { - $closePosition = $params['closePosition']; - if ($closePosition === true) { - $request['closePosition'] = 'true'; - } else { - $request['closePosition'] = 'false'; - } - unset($params['closePosition']); - } - - if (isset($params['priceProtect'])) { - $priceProtect = $params['priceProtect']; - if ($priceProtect === true) { - $request['priceProtect'] = 'TRUE'; - } else { - $request['priceProtect'] = 'FALSE'; - } - unset($params['priceProtect']); - } - - return array_merge($request, $params); - } - - /** - * futuresOrder formats the orders before sending them to the curl wrapper function - * You can call this function directly or use the helper functions - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api - * - * @see futuresBuy() - * @see sell() - * @see marketBuy() - * @see marketSell() - * - * @param string $side (mandatory) typically "BUY" or "SELL" - * @param string $symbol (mandatory) market symbol - * @param string $quantity (optional) of the order (Cannot be sent with closePosition=true (Close-All)) - * @param string $price (optional) price per unit - * @param string $type (mandatory) is determined by the symbol bu typicall LIMIT, STOP_LOSS_LIMIT etc. - * @param array $params (optional) additional transaction options - * - @param string $params['positionSide'] position side, "BOTH" for One-way Mode; "LONG" or "SHORT" for Hedge Mode (mandatory for Hedge Mode) - * - @param string $params['timeInForce'] - * - @param bool $params['reduceOnly'] default false (Cannot be sent in Hedge Mode; cannot be sent with closePosition=true) - * - @param string $params['newClientOrderId'] new client order id - * - @param string $params['stopPrice'] stop price (Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders) - * - @param bool $params['closePosition'] Close-All (used with STOP_MARKET or TAKE_PROFIT_MARKET orders) - * - @param string $params['activationPrice'] Used with TRAILING_STOP_MARKET orders, default as the latest price (supporting different workingType) - * - @param string $params['callbackRate'] Used with TRAILING_STOP_MARKET orders, min 0.1, max 5 where 1 for 1% - * - @param string $params['workingType'] stopPrice triggered by: "MARK_PRICE", "CONTRACT_PRICE". Default "CONTRACT_PRICE" - * - @param bool $params['priceProtect'] Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders (default false) - * - @param string $params['newOrderRespType'] response type, default "RESULT", other option is "ACK" - * - @param string $params['priceMatch'] only avaliable for LIMIT/STOP/TAKE_PROFIT order; can be set to OPPONENT/ OPPONENT_5/ OPPONENT_10/ OPPONENT_20: /QUEUE/ QUEUE_5/ QUEUE_10/ QUEUE_20; Can't be passed together with price - * - @param string $params['selfTradePreventionMode'] EXPIRE_TAKER:expire taker order when STP triggers/ EXPIRE_MAKER:expire taker order when STP triggers/ EXPIRE_BOTH:expire both orders when STP triggers; default NONE - * - @param string $params['goodTillDate'] order cancel time for timeInForce GTD, mandatory when timeInforce set to GTD; order the timestamp only retains second-level precision, ms part will be ignored; The goodTillDate timestamp must be greater than the current time plus 600 seconds and smaller than 253402300799000 - * - @param int $params['recvWindow'] - * @param $test bool whether to test or not, test only validates the query - * @return array containing the response - * @throws \Exception - */ - public function futuresOrder(string $side, string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = [], $test = false) - { - $request = $this->createFuturesOrderRequest($side, $symbol, $quantity, $price, $type, $params); - $isAlgoOrder = $this->isAlgoFuturesOrderType($request['type']); - if ($isAlgoOrder) { - $algoRequest = $this->createAlgoFuturesOrderRequest($request); - return $this->fapiRequest('v1/algoOrder', 'POST', $algoRequest, true); - } else { - // regular order - $qstring = ($test === false) ? 'v1/order' : 'v1/order/test'; - return $this->fapiRequest($qstring, 'POST', $request, true); - } - } - - /** - * isAlgoFuturesOrderType helper to determine if an order type is an algo order - * @param string $type order type - * @return bool true if it is an algo order type - */ - protected function isAlgoFuturesOrderType(string $type): bool - { - $algoOrderTypes = [ - 'STOP', - 'STOP_MARKET', - 'TAKE_PROFIT', - 'TAKE_PROFIT_MARKET', - 'TRAILING_STOP_MARKET', - ]; - if (in_array($type, $algoOrderTypes)) { - return true; - } - return false; - } - - /** - * createAlgoFuturesOrderRequest - * helper for creating the request for algo futures order - * @param array $request regular order request - * @return array containing the request - */ - protected function createAlgoFuturesOrderRequest(array $request): array - { - $request['algoType'] = 'CONDITIONAL'; - if (!isset($request['clientAlgoId'])) { - $newClientOrderId = $request['newClientOrderId']; - $request['clientAlgoId'] = $newClientOrderId; - unset($request['newClientOrderId']); - } - if (!isset($request['triggerPrice']) && isset($request['stopPrice'])) { - $request['triggerPrice'] = $request['stopPrice']; - unset($request['stopPrice']); - } - return $request; - } - - /** - * futuresBuy attempts to create a buy order - * each market supports a number of order types, such as - * -LIMIT - * -MARKET - * -STOP_LOSS - * -STOP_LOSS_LIMIT - * -TAKE_PROFIT - * -TAKE_PROFIT_LIMIT - * -LIMIT_MAKER - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api - * - * You should check the @see exchangeInfo for each currency to determine - * what types of orders can be placed against specific pairs - * - * $quantity = 1; - * $price = 0.0005; - * $order = $api->futuresBuy("BNBBTC", $quantity, $price); - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param string $quantity (optional) the quantity required - * @param string $price (optional) price per unit - * @param string $type (mandatory) type of order - * @param array $params (optional) addtional options for order type - * - @param string $params['positionSide'] position side, "BOTH" for One-way Mode; "LONG" or "SHORT" for Hedge Mode (mandatory for Hedge Mode) - * - @param string $params['timeInForce'] - * - @param bool $params['reduceOnly'] default false (Cannot be sent in Hedge Mode; cannot be sent with closePosition=true) - * - @param string $params['newClientOrderId'] new client order id - * - @param string $params['stopPrice'] stop price (Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders) - * - @param bool $params['closePosition'] Close-All (used with STOP_MARKET or TAKE_PROFIT_MARKET orders) - * - @param string $params['activationPrice'] Used with TRAILING_STOP_MARKET orders, default as the latest price (supporting different workingType) - * - @param string $params['callbackRate'] Used with TRAILING_STOP_MARKET orders, min 0.1, max 5 where 1 for 1% - * - @param string $params['workingType'] stopPrice triggered by: "MARK_PRICE", "CONTRACT_PRICE". Default "CONTRACT_PRICE" - * - @param bool $params['priceProtect'] Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders (default false) - * - @param string $params['newOrderRespType'] response type, default "RESULT", other option is "ACK" - * - @param string $params['priceMatch'] only avaliable for LIMIT/STOP/TAKE_PROFIT order; can be set to OPPONENT/ OPPONENT_5/ OPPONENT_10/ OPPONENT_20: /QUEUE/ QUEUE_5/ QUEUE_10/ QUEUE_20; Can't be passed together with price - * - @param string $params['selfTradePreventionMode'] EXPIRE_TAKER:expire taker order when STP triggers/ EXPIRE_MAKER:expire taker order when STP triggers/ EXPIRE_BOTH:expire both orders when STP triggers; default NONE - * - @param string $params['goodTillDate'] order cancel time for timeInForce GTD, mandatory when timeInforce set to GTD; order the timestamp only retains second-level precision, ms part will be ignored; The goodTillDate timestamp must be greater than the current time plus 600 seconds and smaller than 253402300799000 - * - @param int $params['recvWindow'] - * @return array with error message or the order details - */ - public function futuresBuy(string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = []) - { - return $this->futuresOrder('BUY', $symbol, $quantity, $price, $type, $params); - } - - /** - * futuresBuyTest attempts to create a TEST futures buy order - * - * @see futuresBuy() - * - * params and return value are the same as @see futuresBuy() - */ - public function futuresBuyTest(string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = []) - { - return $this->futuresOrder('BUY', $symbol, $quantity, $price, $type, $params, true); - } - - /** - * futuresSell creates a futures sell order - * each market supports a number of order types, such as - * -LIMIT - * -MARKET - * -STOP_LOSS - * -STOP_LOSS_LIMIT - * -TAKE_PROFIT - * -TAKE_PROFIT_LIMIT - * -LIMIT_MAKER - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api - * - * You should check the @see exchangeInfo for each currency to determine - * what types of orders can be placed against specific pairs - * - * $quantity = 1; - * $price = 0.0005; - * $order = $api->futuresSell("BNBBTC", $quantity, $price); - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param string $quantity (optional) the quantity required - * @param string $price (optional) price per unit - * @param string $type (mandatory) type of order - * @param array $params (optional) addtional options for order type - * - @param string $params['positionSide'] position side, "BOTH" for One-way Mode; "LONG" or "SHORT" for Hedge Mode (mandatory for Hedge Mode) - * - @param string $params['timeInForce'] - * - @param bool $params['reduceOnly'] default false (Cannot be sent in Hedge Mode; cannot be sent with closePosition=true) - * - @param string $params['newClientOrderId'] new client order id - * - @param string $params['stopPrice'] stop price (Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders) - * - @param bool $params['closePosition'] Close-All (used with STOP_MARKET or TAKE_PROFIT_MARKET orders) - * - @param string $params['activationPrice'] Used with TRAILING_STOP_MARKET orders, default as the latest price (supporting different workingType) - * - @param string $params['callbackRate'] Used with TRAILING_STOP_MARKET orders, min 0.1, max 5 where 1 for 1% - * - @param string $params['workingType'] stopPrice triggered by: "MARK_PRICE", "CONTRACT_PRICE". Default "CONTRACT_PRICE" - * - @param bool $params['priceProtect'] Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders (default false) - * - @param string $params['newOrderRespType'] response type, default "RESULT", other option is "ACK" - * - @param string $params['priceMatch'] only avaliable for LIMIT/STOP/TAKE_PROFIT order; can be set to OPPONENT/ OPPONENT_5/ OPPONENT_10/ OPPONENT_20: /QUEUE/ QUEUE_5/ QUEUE_10/ QUEUE_20; Can't be passed together with price - * - @param string $params['selfTradePreventionMode'] EXPIRE_TAKER:expire taker order when STP triggers/ EXPIRE_MAKER:expire taker order when STP triggers/ EXPIRE_BOTH:expire both orders when STP triggers; default NONE - * - @param string $params['goodTillDate'] order cancel time for timeInForce GTD, mandatory when timeInforce set to GTD; order the timestamp only retains second-level precision, ms part will be ignored; The goodTillDate timestamp must be greater than the current time plus 600 seconds and smaller than 253402300799000 - * - @param int $params['recvWindow'] - * @return array with error message or the order details - */ - public function futuresSell(string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = []) - { - return $this->futuresOrder('SELL', $symbol, $quantity, $price, $type, $params); - } - - /** - * futuresSellTest attempts to create a TEST futures sell order - * - * @see futuresSell() - * - * params and return value are the same as @see futuresSell() - */ - public function futuresSellTest(string $symbol, $quantity = null, $price = null, $type = null, array $params = []) - { - if ($type === null) { - $type = 'LIMIT'; - } - return $this->futuresOrder('SELL', $symbol, $quantity, $price, $type, $params, true); - } - - /** - * createBatchOrdersRequest - * helper for creating the request for multiple futures orders - * @param array $orders (mandatory) array of orders to be placed - * objects in the array should contain literally the same keys as the @see futuresOrder but without the $params[recvWindow] - * - * @return array containing the request - * @throws \Exception - */ - protected function createBatchOrdersRequest(array $orders, bool $edit = false) - { - $formatedOrders = []; - for ($index = 0; $index < count($orders); $index++) { - $order = $orders[$index]; - if (!isset($order['quantity'])) { - $order['quantity'] = null; - } - if (!isset($order['price'])) { - $order['price'] = null; - } - if (!isset($order['params'])) { - $order['params'] = []; - } - if (!isset($order['type'])) { - $order['type'] = 'LIMIT'; - } - $formatedOrder = $this->createFuturesOrderRequest( - $order['side'], - $order['symbol'], - $order['quantity'], - $order['price'], - $order['type'], - $order['params'] - ); - if (isset($formatedOrder['recvWindow'])) { - // remove recvWindow from the order - unset($formatedOrder['recvWindow']); - } - if ($edit) { - if (isset($order['orderId'])) { - $formatedOrder['orderId'] = $order['orderId']; - } - if (isset($order['origClientOrderId'])) { - $formatedOrder['origClientOrderId'] = $order['origClientOrderId']; - } - unset($formatedOrder['type']); - unset($formatedOrder['newClientOrderId']); - } - $formatedOrders[$index] = $formatedOrder; - } - return $formatedOrders; - } - - /** - * futuresBatchOrders creates multiple orders in a single request - * max 5 orders - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Place-Multiple-Orders - * - * @param array $orders (mandatory) array of orders to be placed - * objects in the array should contain literally the same keys as the @see futuresOrder but without the $params['recvWindow'] - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response or error message - * @throws \Exception - */ - public function futuresBatchOrders(array $orders, array $params = []) - { - $formatedOrders = $this->createBatchOrdersRequest($orders); - if (count($formatedOrders) > 5) { - throw new \Exception('futuresBatchOrders(): max 5 orders allowed'); - } - if (count($formatedOrders) < 1) { - throw new \Exception('futuresBatchOrders(): at least 1 order required'); - } - $request = []; - - // current endpoint accepts orders list as a json string in the query string - $encodedOrders = json_encode($formatedOrders); - $url = 'v1/batchOrders?batchOrders=' . $encodedOrders; - return $this->fapiRequest($url, 'POST', array_merge($request, $params), true); - } - - /** - * futuresEditOrder edits the limit order - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Order - * - * @param string $symbol (mandatory) market symbol - * @param string $side (mandatory) "BUY" or "SELL" - * @param string $orderId (optional) order id to be modified (mandatory if $params['origClientOrderId'] is not set) - * @param string $quantity (optional) of the order (Cannot be sent for orders with closePosition=true (Close-All)) - * @param string $price (mandatory) price per unit - * @param array $params (optional) additional options - * - @param string $params['priceMatch'] only avaliable for LIMIT/STOP/TAKE_PROFIT order; can be set to OPPONENT/ OPPONENT_5/ OPPONENT_10/ OPPONENT_20: /QUEUE/ QUEUE_5/ QUEUE_10/ QUEUE_20; Can't be passed together with price - * - @param int $params['recvWindow'] - * - @param string $params['origClientOrderId'] client order id to be modified (mandatory if $orderId is not set) - * @return array containing the response - * @throws \Exception - */ - public function futuresEditOrder(string $symbol, string $side, string $quantity, string $price, $orderId = null, array $params = []) - { - $request = $this->createFuturesOrderRequest($side, $symbol, $quantity, $price, 'LIMIT', $params); - $origClientOrderId = null; - if (isset($params['origClientOrderId'])) { - $origClientOrderId = $params['origClientOrderId']; - $request['origClientOrderId'] = $origClientOrderId; - } - if (!$origClientOrderId && !$orderId) { - throw new \Exception('futuresEditOrder(): either orderId or origClientOrderId must be set'); - } - if ($orderId) { - $request['orderId'] = $orderId; - } - unset($request['type']); - unset($request['newClientOrderId']); - return $this->fapiRequest("v1/order", 'PUT', $request, true); - } - - /** - * futuresEditOrders edits the multiple limit orders - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Multiple-Orders - * - * @param array $orders (mandatory) array of orders to be modified - * objects in the array should contain literally the same keys as the @see futuresEditOrder but without the $params['recvWindow'] - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response or error message - * @throws \Exception - */ - public function futuresEditOrders(array $orders, array $params = []) - { - $formatedOrders = $this->createBatchOrdersRequest($orders, true); - $request = []; - - // current endpoint accepts orders list as a json string in the query string - $request['batchOrders'] = json_encode($formatedOrders); - return $this->fapiRequest("v1/batchOrders", 'PUT', array_merge($request, $params), true); - } - - /** - * futuresOrderAmendment get order modification history - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Get-Order-Modify-History - * - * $amendment = $api->futuresOrderAmendment("ETHUSDT"); - * - * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT - * @param string $orderId (optional) order id to get the response for - * @param string $origClientOrderId (optional) original client order id to get the response for - * @param int $startTime (optional) timestamp in ms to get modification history from INCLUSIVE - * @param int $endTime (optional) timestamp in ms to get modification history until INCLUSIVE - * @param int $limit (optional) limit the amount of open interest history (default 50, max 100) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresOrderAmendment(string $symbol, $orderId = null, $origClientOrderId = null, $startTime = null, $endTime = null, $limit = null, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - if ($orderId) { - $request['orderId'] = $orderId; - } - if ($origClientOrderId) { - $request['origClientOrderId'] = $origClientOrderId; - } - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - if ($limit) { - $request['limit'] = $limit; } + $endpoint = "https://api.binance.com/sapi/v1/capital/deposit/hisrec?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); - return $this->fapiRequest("v1/orderAmendment", 'GET', array_merge($request, $params), true); + $this->assertEquals($this->asset, $params['coin']); } - /** - * futuresCancel cancels a futures order - * - * $orderid = "123456789"; - * $order = $api->futuresCancel("BNBBTC", $orderid); - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param string $orderid (optional) the orderid to cancel (mandatory if $params['origClientOrderId'] is not set) - * @param array $params (optional) additional options - * - @param string $params['origClientOrderId'] original client order id to cancel - * - @param int $params['recvWindow'] the time in milliseconds to wait for a response - * - * @return array with error message or the order details - * @throws \Exception - */ - public function futuresCancel(string $symbol, $orderid, $params = []) + public function testWithdrawHistory() { - $request = [ - 'symbol' => $symbol, - ]; - if ($orderid) { - $request['orderId'] = $orderid; - } else if (!isset($params['origClientOrderId'])) { - throw new \Exception('futuresCancel(): either orderId or origClientOrderId must be set'); - } - return $this->fapiRequest("v1/order", 'DELETE', array_merge($request, $params), true); - } - - /** - * futuresAlgoCancel cancels a futures order - * - * $algoid = "123456789"; - * $order = $api->futuresCancel($algoid); - * - * @param string $algoid (optional) the algoid to cancel (mandatory if $params['clientalgoid'] is not set) - * @param array $params (optional) additional options - * - @param string $params['clientalgoid'] original client order id to cancel - * - @param int $params['recvWindow'] the time in milliseconds to wait for a response - * - * @return array with error message or the order details - * @throws \Exception - */ - public function futuresAlgoCancel($algoid, $params = []) - { - $request = []; - if ($algoid) { - $request['algoId'] = $algoid; - } else if (!isset($params['clientalgoid'])) { - throw new \Exception('futuresAlgoCancel(): either algoid or clientalgoid must be set'); - } - // todo: check camel-case or lower-case!!!! - return $this->fapiRequest("v1/algoOrder", 'DELETE', array_merge($request, $params), true); - } - - /** - * futuresCancelBatchOrders canceles multiple futures orders - * - * $orderIds = ["123456789", "987654321"]; - * $order = $api->futuresCancelBatchOrders("BNBBTC", $orderIds); - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param array $orderIdList (optional) list of ids to cancel (mandatory if origClientOrderIdList is not set) - * @param array $origClientOrderIdList (optional) list of client order ids to cancel (mandatory if orderIdList is not set) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the orders details - * @throws \Exception - */ - public function futuresCancelBatchOrders(string $symbol, $orderIdList = null, $origClientOrderIdList = null, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - if ($orderIdList) { - $idsString = json_encode($orderIdList); - // remove quotes and spaces - $request['orderIdList'] = str_replace(' ', '', str_replace('"', '', str_replace("'", '', $idsString))); - } else if ($origClientOrderIdList) { - // remove spaces between the ids - $request['origClientOrderIdList'] = str_replace(', ', ',', json_encode($origClientOrderIdList)); - } else { - throw new \Exception('futuresCancelBatchOrders(): either orderIdList or origClientOrderIdList must be set'); - } - - return $this->fapiRequest("v1/batchOrders", 'DELETE', array_merge($request, $params), true); - } - - /** - * futuresCancelOpenOrders cancels all open futures orders for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Open-Orders - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Algo-Open-Orders - * $orders = $api->futuresCancelOpenOrders("BNBBTC"); - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param bool $params['isAlgo'] true for canceling algo orders - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the orders details - * @throws \Exception - */ - public function futuresCancelOpenOrders(string $symbol, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - if (isset($params['isAlgo']) && ($params['isAlgo'] === true)) { - unset($params['isAlgo']); - return $this->fapiRequest("v1/algoOpenOrders", 'DELETE', array_merge($request, $params), true); - } else { - return $this->fapiRequest("v1/allOpenOrders", 'DELETE', array_merge($request, $params), true); - } - } - - /** - * futuresCancelOpenAlgoOrders cancels all open futures algo orders for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Algo-Open-Orders - * - * $orders = $api->futuresCancelOpenAlgoOrders("BNBBTC"); - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the orders details - * @throws \Exception - */ - public function futuresCancelOpenAlgoOrders (string $symbol, array $params = []) - { - $params['isAlgo'] = true; - return $this->futuresCancelOpenOrders($symbol, $params); - } - - /** - * futuresCountdownCancelAllOrders cancels all open futures orders for a symbol at the end of the specified countdown - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Auto-Cancel-All-Open-Orders - * - * $orders = $api->futuresCountdownCancelAllOrders("BNBBTC", 10); - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param int $countdownTime (mandatory) countdown in milliseconds (0 to stop the timer) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the orders details - * @throws \Exception - */ - public function futuresCountdownCancelAllOrders(string $symbol, int $countdownTime, array $params = []) - { - $request = [ - 'symbol' => $symbol, - 'countdownTime' => $countdownTime, - ]; + try { + $this->binance->withdrawHistory($this->asset); + + } catch (\Throwable $e) { - return $this->fapiRequest("v1/countdownCancelAll", 'POST', array_merge($request, $params), true); - } - - /** - * futuresOrderStatus gets the details of a futures order - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Order - * - * $order = $api->futuresOrderStatus("BNBBTC", "123456789"); - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param string $orderId (optional) order id to get the response for (mandatory if origClientOrderId is not set) - * @param string $origClientOrderId (optional) original client order id to get the response for (mandatory if orderId is not set) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the order details - * @throws \Exception - */ - public function futuresOrderStatus(string $symbol, $orderId = null, $origClientOrderId = null, array $params = [], ) - { - $request = [ - 'symbol' => $symbol, - ]; - if ($orderId) { - $request['orderId'] = $orderId; - } else if ($origClientOrderId) { - $request['origClientOrderId'] = $origClientOrderId; - } else { - throw new \Exception('futuresOrderStatus(): either orderId or origClientOrderId must be set'); - } - - return $this->fapiRequest("v1/order", 'GET', array_merge($request, $params), true); - } - - /** - * futuresAlgoOrderStatus gets the details of a futures algo order - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Algo-Order - * - * $order = $api->futuresAlgoOrderStatus("123456789"); - * - * @param string $algoId (optional) order id to get the response for (mandatory if clientAlgoId is not set) - * @param string $clientAlgoId (optional) original client order id to get the response for (mandatory if algoId is not set) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the order details - * @throws \Exception - */ - public function futuresAlgoOrderStatus($algoId = null, $clientAlgoId = null, array $params = []) - { - $request = []; - if ($algoId) { - $request['algoId'] = $algoId; - } else if ($clientAlgoId) { - $request['clientAlgoId'] = $clientAlgoId; - } else { - throw new \Exception('futuresAlgoOrderStatus(): either algoId or clientAlgoId must be set'); - } - - return $this->fapiRequest("v1/algoOrder", 'GET', array_merge($request, $params), true); - } - - /** - * futuresAllOrders gets all orders for a symbol - * query time period must be less then 7 days (default as the recent 7 days) - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/All-Orders - * - * $orders = $api->futuresAllOrders("BNBBTC"); - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param int $startTime (optional) timestamp in ms to get orders from INCLUSIVE - * @param int $endTime (optional) timestamp in ms to get orders until INCLUSIVE - * @param int $limit (optional) limit the amount of orders (default 500, max 1000) - * @param string $orderId (optional) order id to get the response from (if is set it will get orders >= that orderId) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the orders details - * @throws \Exception - */ - public function futuresAllOrders(string $symbol, $startTime = null, $endTime = null, $limit = null, $orderId = null, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - if ($limit) { - $request['limit'] = $limit; - } - if ($orderId) { - $request['orderId'] = $orderId; - } - - return $this->fapiRequest("v1/allOrders", 'GET', array_merge($request, $params), true); - } - - /** - * futuresOpenOrders gets all open orders for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Current-All-Open-Orders - * - * $orders = $api->futuresOpenOrders(); - * $orders = $api->futuresOpenOrders("BNBBTC"); - * - * @param string $symbol (optional) market symbol (e.g. ETHUSDT) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the orders details - * @throws \Exception - */ - public function futuresOpenOrders($symbol = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - - return $this->fapiRequest("v1/openOrders", 'GET', array_merge($request, $params), true); - } - - /** - * futuresOpenOrder gets an open futures order - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Current-Open-Order - * - * $order = $api->futuresOpenOrder("BNBBTC", "123456789"); - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param string $orderId (optional) order id to get the response for (mandatory if origClientOrderId is not set) - * @param string $origClientOrderId (optional) original client order id to get the response for (mandatory if orderId is not set) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the order details - * @throws \Exception - */ - public function futuresOpenOrder(string $symbol, $orderId = null, $origClientOrderId = null, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - if ($orderId) { - $request['orderId'] = $orderId; - } else if ($origClientOrderId) { - $request['origClientOrderId'] = $origClientOrderId; - } else { - throw new \Exception('futuresOpenOrder(): either orderId or origClientOrderId must be set'); - } - - return $this->fapiRequest("v1/openOrder", 'GET', array_merge($request, $params), true); - } - /** - * futuresForceOrders gets all futures force orders - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Users-Force-Orders - * - * $orders = $api->futuresForceOrders("BNBBTC"); - * - * @property int $weight 50 - * 20 if symbol is not passed - * - * @param string $symbol (optional) market symbol (e.g. ETHUSDT) - * @param int $startTime (optional) timestamp in ms to get orders from INCLUSIVE - * @param int $endTime (optional) timestamp in ms to get orders until INCLUSIVE - * @param int $limit (optional) limit the amount of orders (default 500, max 1000) - * @param string $autoCloseType (optional) "LIQUIDATION" for liquidation orders, "ADL" for ADL orders - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the orders details - * @throws \Exception - */ - public function futuresForceOrders($symbol = null, $startTime = null, $endTime = null, $limit = null, $autoCloseType = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - if ($limit) { - $request['limit'] = $limit; - } - if ($autoCloseType) { - $request['autoCloseType'] = $autoCloseType; - } - - return $this->fapiRequest("v1/forceOrders", 'GET', array_merge($request, $params), true); - } - - /** - * futuresMyTrades gets all futures trades for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Account-Trade-List - * - * $trades = $api->futuresMyTrades("BNBBTC"); - * - * @property int $weight 5 - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param int $startTime (optional) timestamp in ms to get trades from INCLUSIVE - * @param int $endTime (optional) timestamp in ms to get trades until INCLUSIVE - * @param int $limit (optional) limit the amount of trades (default 500, max 1000) - * @param string $orderId (optional) order id to get the trades for - * @param string $fromId (optional) trade id to get the trades from (if is set it will get trades >= that Id) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the trades details - * @throws \Exception - */ - public function futuresMyTrades(string $symbol, $startTime = null, $endTime = null, $limit = null, $orderId = null, $fromId = null, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - if ($limit) { - $request['limit'] = $limit; - } - if ($orderId) { - $request['orderId'] = $orderId; - } - if ($fromId) { - $request['fromId'] = $fromId; } + $endpoint = "https://api.binance.com/sapi/v1/capital/withdraw/history?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->asset, $params['coin']); - return $this->fapiRequest("v1/userTrades", 'GET', array_merge($request, $params), true); } - /** - * futuresHistory - * another name for futuresMyTrades (for naming compatibility with spot) - * @deprecated - */ - public function futuresHistory(string $symbol, $startTime = null, $endTime = null, $limit = null, $orderId = null, $fromId = null, array $params = []) + public function testWithdrawFee() { - return $this->futuresMyTrades($symbol, $startTime, $endTime, $limit, $orderId, $fromId, $params); + try { + $this->binance->withdrawFee($this->asset); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/sapi/v1/asset/assetDetail"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); } - /** - * futuresSetMarginMode sets the margin mode for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Change-Margin-Type - * - * $api->futuresSetMarginMode("BNBBTC", "ISOLATED"); - * - * @property int $weight 1 - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param string $marginType (mandatory) margin type, "CROSSED" or "ISOLATED" - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresSetMarginMode(string $symbol, string $marginType, array $params = []) + public function testTransfer() { - $request = [ - 'symbol' => $symbol, - 'marginType' => $marginType, - ]; + try { + $this->binance->transfer($this->type, $this->asset, $this->amount, $this->fromSymbol, $this->toSymbol, [ 'recvWindow' => $this->recvWindow ]); - return $this->fapiRequest("v1/marginType", 'POST', array_merge($request, $params), true); - } - - /** - * futuresPositionMode gets the position mode for ALL symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Current-Position-Mode - * - * $response = $api->futuresPositionMode(); - * - * @property int $weight 30 - * - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresPositionMode(array $params = []) - { - return $this->fapiRequest("v1/positionSide/dual", 'GET', $params, true); - } - - /** - * futuresSetPositionMode sets the position mode for ALL symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Change-Position-Mode - * - * $api->futuresSetPositionMode(true); - * - * @property int $weight 1 - * - * @param bool $dualSidePosition (mandatory) true for Hedge Mode, false for One-way Mode - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresSetPositionMode(bool $dualSidePosition, array $params = []) - { - $request = [ - 'dualSidePosition' => $dualSidePosition ? 'true' : 'false', - ]; + } catch (\Throwable $e) { - return $this->fapiRequest("v1/positionSide/dual", 'POST', array_merge($request, $params), true); - } - - /** - * futuresSetLeverage sets the leverage for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Change-Initial-Leverage - * - * $leverage = $api->futuresSetLeverage(10, "BTCUSDT"); - * - * @property int $weight 1 - * - * - * @param int $leverage (mandatory) leverage to be set (min 1, max 125) - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresSetLeverage(int $leverage, string $symbol, array $params = []) - { - $request = [ - 'symbol' => $symbol, - 'leverage' => $leverage, - ]; + } + $this->assertEquals("https://api.binance.com/sapi/v1/asset/transfer", self::$capturedUrl); - return $this->fapiRequest("v1/leverage", 'POST', array_merge($request, $params), true); - } - - /** - * futuresMultiAssetsMarginMode gets the multi-assets margin mode for ALL symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Current-Multi-Assets-Mode - * - * $response = $api->futuresMultiAssetsMarginMode(); - * - * @property int $weight 30 - * - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresMultiAssetsMarginMode(array $params = []) - { - return $this->fapiRequest("v1/multiAssetsMargin", 'GET', $params, true); - } - - /** - * futuresSetMultiAssetsMarginMode sets the multi-assets margin mode for ALL symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Change-Multi-Assets-Mode - * - * $response = $api->futuresSetMultiAssetsMarginMode(true); - * - * @property int $weight 1 - * - * @param bool $multiAssetsMarginMode (mandatory) true for multi-assets mode, false for single-asset mode - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresSetMultiAssetsMarginMode(bool $multiAssetsMarginMode, array $params = []) - { - $request = [ - 'multiAssetsMarginMode' => $multiAssetsMarginMode ? 'true' : 'false', - ]; + parse_str(self::$capturedBody, $params); - return $this->fapiRequest("v1/multiAssetsMarginMode", 'POST', array_merge($request, $params), true); + $this->assertEquals($this->type, $params['type']); + $this->assertEquals($this->asset, $params['asset']); + $this->assertEquals($this->amount, $params['amount']); + $this->assertEquals($this->fromSymbol, $params['fromSymbol']); + $this->assertEquals($this->toSymbol, $params['toSymbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); } - /** - * modifyMarginHelper helper for adding or removing margin - * - * @see futuresAddMargin() and futuresReduceMargin() - * - * @return array containing the response - * @throws \Exception - */ - protected function modifyMarginHelper(string $symbol, string $amount, $addOrReduce, $positionSide = null, array $params = []) + public function testTransfersHistory() { - $request = [ - 'symbol' => $symbol, - 'amount' => $amount, - 'type' => $addOrReduce, - ]; - if ($positionSide) { - $request['positionSide'] = $positionSide; - } - - return $this->fapiRequest("v1/positionMargin", 'POST', array_merge($request, $params), true); - } - - /** - * futuresAddMargin adds margin to a position - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Isolated-Position-Margin - * - * $response = $api->futuresAddMargin("BNBBTC", 10); - * - * @property int $weight 1 - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param string $amount (mandatory) amount to be added - * @param string $positionSide (optional) position side - "BOTH" for non-hedged and "LONG" or "SHORT" for hedged (mandatory for hedged positions) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresAddMargin(string $symbol, string $amount, $positionSide = null, array $params = []) - { - return $this->modifyMarginHelper($symbol, $amount, 1, $positionSide, $params); - } - - /** - * futuresReduceMargin removes margin from a position - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Isolated-Position-Margin - * - * $response = $api->futuresReduceMargin("BNBBTC", 10); - * - * @property int $weight 1 - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param string $amount (mandatory) amount to be removed - * @param string $positionSide (optional) position side - "BOTH" for non-hedged and "LONG" or "SHORT" for hedged (mandatory for hedged positions) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresReduceMargin(string $symbol, string $amount, $positionSide = null, array $params = []) - { - return $this->modifyMarginHelper($symbol, $amount, 2, $positionSide, $params); - } - - /** - * futuresPositions gets the position information for a symbol or all symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V2 - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V3 - * - * $position = $api->futuresPositions("BNBBTC"); - * - * @property int $weight 5 - * - * @param string $symbol (optional) market symbol (e.g. ETHUSDT) - * @param array $params (optional) an array of additional parameters that the API endpoint afllows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * @param string $api_version (optional) API version, "v2" or "v3" (default is v3) - * - * @return array with error message or the position details - * @throws \Exception - */ - public function futuresPositions($symbol = null, array $params = [], string $api_version = 'v3') - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - - if ($api_version !== 'v2' && $api_version !== 'v3') { - throw new \Exception('futuresPositions(): api_version must be either v2 or v3'); - } - return $this->fapiRequest($api_version . "/positionRisk", 'GET', array_merge($request, $params), true); - } - - /** futuresPositionsV2 - * @see futuresPositions - */ - public function futuresPositionsV2($symbol = null, array $params = []) - { - return $this->futuresPositions($symbol, $params, 'v2'); - } - - /** - * futuresPositionsV3 - * @see futuresPositions - */ - public function futuresPositionsV3($symbol = null, array $params = []) - { - return $this->futuresPositions($symbol, $params, 'v3'); - } - - /** - * futuresPosition gets the position information for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V2 - * - * $position = $api->futuresPosition("BNBBTC"); - * - * @property int $weight 5 - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param array $params (optional) an array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * @param string $api_version (optional) API version, "v2" or "v3" (default is v3) - * - * @return array with error message or the position details - * @throws \Exception - */ - public function futuresPosition(string $symbol, array $params = [], string $api_version = 'v3') - { - return $this->futuresPositions($symbol, $params, $api_version); - } - - /** - * futuresPositionV2 - * @see futuresPosition - */ - public function futuresPositionV2(string $symbol, array $params = []) - { - return $this->futuresPositionsV2($symbol, $params, 'v2'); - } - - /** - * futuresPositionV3 - * @see futuresPosition - */ - public function futuresPositionV3(string $symbol, array $params = []) - { - return $this->futuresPositionsV3($symbol, $params, 'v3'); - } - - /** - * futuresAdlQuantile gets the ADL quantile estimation for a symbol or all symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-ADL-Quantile-Estimation - * - * $response = $api->futuresAdlQuantile("BNBBTC"); - * - * @property int $weight 5 - * - * @param string $symbol (optional) market symbol (e.g. ETHUSDT) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the ADL quantile details - * @throws \Exception - */ - public function futuresAdlQuantile($symbol = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - - return $this->fapiRequest("v1/adlQuantile", 'GET', array_merge($request, $params), true); - } - - /** - * futuresPositionMarginChangeHistory gets the position margin change history for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Get-Position-Margin-Change-History - * - * $history = $api->futuresPositionMarginChangeHistory("BNBBTC"); - * - * @property int $weight 1 - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param int $startTime (optional) timestamp in ms to get history from INCLUSIVE - * @param int $endTime (optional) timestamp in ms to get history until INCLUSIVE - * @param int $limit (optional) limit the amount of history (default 500) - * @param string $addOrReduce (optional) "ADD" or "REDUCE" to filter the history - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - */ - public function futuresPositionMarginChangeHistory(string $symbol, $startTime = null, $endTime = null, $limit = null, $addOrReduce = null, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - if ($limit) { - $request['limit'] = $limit; - } - if ($addOrReduce) { - if (is_numeric($addOrReduce)) { - $request['addOrReduce'] = $addOrReduce; - } else if (is_string($addOrReduce)) { - $addOrReduce = strtoupper($addOrReduce); - if ($addOrReduce === 'ADD' || $addOrReduce === '1') { - $request['addOrReduce'] = 1; - } else if ($addOrReduce === 'REDUCE' || $addOrReduce === '2') { - $request['addOrReduce'] = 2; - } else { - throw new \Exception('futuresPositionMarginChangeHistory(): addOrReduce must be "ADD" or "REDUCE" or 1 or 2'); - } - } else { - throw new \Exception('futuresPositionMarginChangeHistory(): addOrReduce must be "ADD" or "REDUCE" or 1 or 2'); - } - } - - return $this->fapiRequest("v1/positionMargin/history", 'GET', array_merge($request, $params), true); - } - - /** - * futuresBalances gets the balance information futures account - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Futures-Account-Balance-V2 - * - * $balances = $api->futuresBalances(); - * - * @property int $weight 5 - * - * @param array $params (optional) an array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * @param string $api_version (optional) API version, "v2" or "v3" (default is v3) - * - * @return array with error message or the balance details - * @throws \Exception - */ - public function futuresBalances(array $params = [], string $api_version = 'v3') - { - if ($api_version !== 'v2' && $api_version !== 'v3') { - throw new \Exception('futuresBalances(): api_version must be either v2 or v3'); - } - return $this->balances('futures', $params, 'v3'); - } - - /** - * futuresBalancesV2 - * see futuresBalances - */ - public function futuresBalancesV2(array $params = []) - { - return $this->futuresBalances($params, 'v2'); - } - - /** - * futuresBalancesV3 - * see futuresBalances - */ - public function futuresBalancesV3(array $params = []) - { - return $this->futuresBalances($params, 'v3'); - } - - /** - * futuresAccount get all information about the api account - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Account-Information-V2 - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Account-Information-V3 - * - * $account = $api->futuresAccount(); - * - * @property int $weight 5 - * - * @param array $params (optional) an array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * @param string $api_version (optional) API version, "v2" or "v3" (default is v3) - * - * @return array with error message or array of all the account information - * @throws \Exception - */ - public function futuresAccount(array $params = [], string $api_version = 'v3') - { - if ($api_version !== 'v2' && $api_version !== 'v3') { - throw new \Exception('futuresAccount(): api_version must be either v2 or v3'); - } - - return $this->fapiRequest($api_version . "/account", "GET", $params, true); - } - - /** - * futuresAccountV2 - * see futuresAccount - */ - public function futuresAccountV2(array $params = []) - { - return $this->futuresAccount($params, 'v2'); - } - - /** - * futuresAccountV3 - * see futuresAccount - */ - public function futuresAccountV3(array $params = []) - { - return $this->futuresAccount($params, 'v3'); - } - - /** - * futuresTradeFee gets the trade fee information for a symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/User-Commission-Rate - * - * $tradeFee = $api->futuresTradeFee("BNBBTC"); - * - * @property int $weight 20 - * - * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the trade fee details - * @throws \Exception - */ - public function futuresTradeFee(string $symbol, array $params = []) - { - $request = [ - 'symbol' => $symbol, - ]; + try { + $this->binance->transfersHistory($this->type, $this->startTime, $this->endTime, $this->limit, $this->current, $this->fromSymbol, $this->toSymbol, [ 'recvWindow' => $this->recvWindow ]); - return $this->fapiRequest("v1/commissionRate", 'GET', array_merge($request, $params), true); - } - - /** - * futuresAccountConfig gets the account configuration information - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Account-Config - * - * $accountConfig = $api->futuresAccountConfig(); - * - * @property int $weight 5 - * - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the account configuration details - * @throws \Exception - */ - public function futuresAccountConfig(array $params = []) - { - return $this->fapiRequest("v1/accountConfig", 'GET', $params, true); - } - - /** - * futuresMarginModes gets the margin mode for all symbols or specific symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Symbol-Config - * - * $marginMode = $api->futuresMarginModes(); - * $marginModes = $api->futuresMarginModes("BNBBTC"); - * - * @property int $weight 5 - * - * @param string $symbol (optional) market symbol (e.g. ETHUSDT) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the margin mode details - * @throws \Exception - */ - public function futuresMarginModes($symbol = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - - return $this->fapiRequest("v1/symbolConfig", 'GET', array_merge($request, $params), true); - } - - /** - * futuresOrderRateLimit gets the user rate limit - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Query-Rate-Limit - * - * $rateLimit = $api->futuresOrderRateLimit(); - * - * @property int $weight 1 - * - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the rate limit details - * @throws \Exception - */ - public function futuresOrderRateLimit(array $params = []) - { - return $this->fapiRequest("v1/rateLimit/order", 'GET', $params, true); - } - - /** - * futuresLeverages gets the leverage information for a symbol or all symbols - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Notional-and-Leverage-Brackets - * - * $leverage = $api->futuresLeverages("BNBBTC"); - * $leverages = $api->futuresLeverages(); - * - * @property int $weight 1 - * - * @param string $symbol (optional) market symbol (e.g. ETHUSDT) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the leverage details - * @throws \Exception - */ - public function futuresLeverages($symbol = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - - return $this->fapiRequest("v1/leverageBracket", 'GET', array_merge($request, $params), true); - } - - /** - * futuresLedger fetch the history of changes, actions done by the user or operations that altered the balance of the user - * possible values for incomeType: - * - TRANSFER - * - TRANSFER - * - WELCOME_BONUS - * - REALIZED_PNL - * - FUNDING_FEE - * - COMMISSION - * - INSURANCE_CLEAR - * - REFERRAL_KICKBACK - * - COMMISSION_REBATE - * - API_REBATE - * - CONTEST_REWARD - * - CROSS_COLLATERAL_TRANSFER - * - OPTIONS_PREMIUM_FEE - * - OPTIONS_SETTLE_PROFIT - * - INTERNAL_TRANSFER - * - AUTO_EXCHANGE - * - DELIVERED_SETTELMENT - * - COIN_SWAP_DEPOSIT - * - COIN_SWAP_WITHDRAW - * - POSITION_LIMIT_INCREASE_FEE - * - STRATEGY_UMFUTURES_TRANSFER - * - FEE_RETURN - * - BFUSD_REWARD - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Income-History - * - * $income = $api->futuresLedger("BNBBTC"); - * $income = $api->futuresLedger("BNBBTC", "FUNDING_FEE"); - * - * @property int $weight 30 - * - * @param string $symbol (optional) market symbol (e.g. ETHUSDT) - * @param string $incomeType (optional) income type to filter the response - * @param int $startTime (optional) timestamp in ms to get income from INCLUSIVE - * @param int $endTime (optional) timestamp in ms to get income until INCLUSIVE - * @param int $limit (optional) limit the amount of income (default 100, max 1000) - * @param int $page (optional) number of page to get - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the income details - * @throws \Exception - */ - public function futuresLedger($symbol = null, $incomeType = null, $startTime = null, $endTime = null, $limit = null, $page = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - if ($incomeType) { - $request['incomeType'] = $incomeType; - } - if ($startTime) { - $request['startTime'] = $startTime; - } - if ($endTime) { - $request['endTime'] = $endTime; - } - if ($limit) { - $request['limit'] = $limit; - } - if ($page) { - $request['page'] = $page; - } - - return $this->fapiRequest("v1/income", 'GET', array_merge($request, $params), true); - } - - /** - * futuresTradingStatus get the futures trading quantitative rules indicators - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Futures-Trading-Quantitative-Rules-Indicators - * - * $tradingStatus = $api->futuresTradingStatus(); - * - * @property int $weight 10 - * weigth is 1 if symbol is provided - * - * @param string $symbol (optional) market symbol (e.g. ETHUSDT) - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the trading status details - * @throws \Exception - */ - public function futuresTradingStatus($symbol = null, array $params = []) - { - $request = []; - if ($symbol) { - $request['symbol'] = $symbol; - } - - return $this->fapiRequest("v1/apiTradingStatus", 'GET', array_merge($request, $params), true); - } - - /** - * futuresDownloadId - * helper for other metods for getting download id - */ - protected function futuresDownloadId($startTime, $endTime, ?array $params = null, string $url = '') - { - $request = [ - 'startTime' => $startTime, - 'endTime' => $endTime, - ]; + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/sapi/v1/asset/transfer?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->type, $params['type']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['size']); + $this->assertEquals($this->current, $params['current']); + $this->assertEquals($this->fromSymbol, $params['fromSymbol']); + $this->assertEquals($this->toSymbol, $params['toSymbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); - return $this->fapiRequest($url, 'GET', array_merge($request, $params), true); } - /** - * futuresDownloadLinkByDownloadId - * helper for other metods for getting download link by download id - */ - protected function futuresDownloadLinkByDownloadId(string $downloadId, ?array $params = null, string $url = '') + public function testSpotPrices() { - $request = [ - 'downloadId' => $downloadId, - ]; + try { + $this->binance->prices(); - return $this->fapiRequest($url, 'GET', array_merge($request, $params), true); - } - - /** - * futuresDownloadIdForTransactions gets the download id for transactions - * request limitation is 5 times per month, shared by front end download page and rest api - * the time between startTime and endTime can not be longer than 1 year - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Download-Id-For-Futures-Transaction-History - * - * $downloadId = $api->futuresDownloadIdForTransactions(1744105700000, 1744105722122); - * - * @property int $weight 1000 - * - * @param int $startTime (optional) timestamp in ms to get transactions from INCLUSIVE - * @param int $endTime (optional) timestamp in ms to get transactions until INCLUSIVE - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the response - * @throws \Exception - */ - public function futuresDownloadIdForTransactions(int $startTime, int $endTime, array $params = []) - { - return $this->futuresDownloadId($startTime, $endTime, $params, "v1/income/asyn"); - } - - /** - * futuresDownloadTransactionsByDownloadId get futures transaction history download link by Id - * download link expiration: 24h - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Futures-Transaction-History-Download-Link-by-Id - * - * $downloadLink = $api->futuresDownloadTransactionsByDownloadId("downloadId"); - * - * @property int $weight 10 - * - * @param string $downloadId (mandatory) download id - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the download link - * @throws \Exception - */ - public function futuresDownloadTransactionsByDownloadId(string $downloadId, array $params = []) - { - return $this->futuresDownloadLinkByDownloadId($downloadId, $params, "v1/income/asyn/id"); - } - - /** - * futuresDownloadIdForOrders gets the download id for orders - * request limitation is 10 times per month, shared by front end download page and rest api - * the time between startTime and endTime can not be longer than 1 year - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Download-Id-For-Futures-Order-History - * - * $downloadId = $api->futuresDownloadIdForOrders(1744105700000, 1744105722122); - * - * @property int $weight 1000 - * - * @param int $startTime (optional) timestamp in ms to get orders from INCLUSIVE - * @param int $endTime (optional) timestamp in ms to get orders until INCLUSIVE - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the response - * @throws \Exception - */ - public function futuresDownloadIdForOrders(int $startTime, int $endTime, array $params = []) - { - return $this->futuresDownloadId($startTime, $endTime, $params, "v1/order/asyn"); - } - - /** - * futuresDownloadOrdersByDownloadId get futures orders history download link by Id - * download link expiration: 24h - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Futures-Order-History-Download-Link-by-Id - * - * $downloadLink = $api->futuresDownloadOrdersByDownloadId("downloadId"); - * - * @property int $weight 10 - * - * @param string $downloadId (mandatory) download id - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the download link - * @throws \Exception - */ - public function futuresDownloadOrdersByDownloadId(string $downloadId, array $params = []) - { - return $this->futuresDownloadLinkByDownloadId($downloadId, $params, "v1/order/asyn/id"); - } - - /** - * futuresDownloadIdForTrades gets the download id for trades - * request limitation is 5 times per month, shared by front end download page and rest api - * the time between startTime and endTime can not be longer than 1 year - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Download-Id-For-Futures-Trade-History - * - * $downloadId = $api->futuresDownloadIdForTrades(1744105700000, 1744105722122); - * - * @property int $weight 1000 - * - * @param int $startTime (optional) timestamp in ms to get trades from INCLUSIVE - * @param int $endTime (optional) timestamp in ms to get trades until INCLUSIVE - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the response - * @throws \Exception - */ - public function futuresDownloadIdForTrades(int $startTime, int $endTime, array $params = []) - { - return $this->futuresDownloadId($startTime, $endTime, $params, "v1/trade/asyn"); - } - - /** - * futuresDownloadTradesByDownloadId get futures trades history download link by Id - * download link expiration: 24h - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Futures-Trade-Download-Link-by-Id - * - * $downloadLink = $api->futuresDownloadTradesByDownloadId("downloadId"); - * - * @property int $weight 10 - * - * @param string $downloadId (mandatory) download id - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array with error message or the download link - * @throws \Exception - */ - public function futuresDownloadTradesByDownloadId(string $downloadId, array $params = []) - { - return $this->futuresDownloadLinkByDownloadId($downloadId, $params, "v1/trade/asyn/id"); - } - - /** - * futuresFeeBurn change user's BNB Fee Discount (Fee Discount On or Fee Discount Off ) on EVERY symbol - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Toggle-BNB-Burn-On-Futures-Trade - * - * $response = $api->futuresFeeBurn(true); - * - * @property int $weight 1 - * - * @param bool $flag (mandatory) true for BNB Fee Discount On, false for BNB Fee Discount Off - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresFeeBurn(bool $flag, array $params = []) - { - $request = [ - 'feeBurn' => $flag ? 'true' : 'false', - ]; + } catch (\Throwable $e) { - return $this->fapiRequest("v1/feeBurn", 'POST', array_merge($request, $params), true); - } - - /** - * futuresFeeBurnStatus gets the BNB Fee Discount status - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-BNB-Burn-Status - * - * $response = $api->futuresFeeBurnStatus(); - * - * @property int $weight 30 - * - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function futuresFeeBurnStatus(array $params = []) - { - return $this->fapiRequest("v1/feeBurn", 'GET', $params, true); - } - - /** - * convertExchangeInfo get all convertible token pairs and the tokens’ respective upper/lower limits - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/convert - * - * $converInfo = $api->convertExchangeInfo(); - * $converInfo = $api->convertExchangeInfo("ETH", "DOGE"); - * - * @property int $weight 20 - * - * @param string $fromAsset (optional) the asset to convert from - * @param string $toAsset (optional) the asset to convert to - * - * @return array containing the response - * @throws \Exception - */ - public function convertExchangeInfo($fromAsset = null, $toAsset = null, array $params = []) - { - $request = []; - if ($fromAsset) { - $request['fromAsset'] = $fromAsset; - } - if ($toAsset) { - $request['toAsset'] = $toAsset; - } - return $this->fapiRequest("v1/convert/exchangeInfo", 'GET', array_merge($request, $params)); - } - - /** - * convertSend send a convert request - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/convert/Send-quote-request - * - * $convertRequest = $api->convertSend("ETH", "DOGE", 0.1); - * - * @property int $weight 50 - * - * @param string $fromAsset (mandatory) the asset to convert from - * @param string $toAsset (mandatory) the asset to convert to - * @param string $fromAmount (optional) mandatory if $toAmount is not set - * @param string $toAmount (optional) mandatory if $fromAmount is not set - * @param string $validTime (optional) deafault "10s" - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function convertSend(string $fromAsset, string $toAsset, $fromAmount = null, $toAmount = null, $validTime = null, array $params = []) - { - $request = [ - 'fromAsset' => $fromAsset, - 'toAsset' => $toAsset, - ]; - if ($fromAmount) { - $request['fromAmount'] = $fromAmount; - } else if ($toAmount) { - $request['toAmount'] = $toAmount; - } else { - throw new \Exception('convertSendRequest(): fromAmount or toAmount must be set'); - } - if ($validTime) { - $request['validTime'] = $validTime; - } - - return $this->fapiRequest("v1/convert/getQuote", 'POST', array_merge($request, $params), true); - } - - /** - * convertAccept accept the offered quote by quote ID - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/convert/Accept-Quote - * - * $convertAccept = $api->convertAccept("quoteId"); - * - * @property int $weight 200 - * - * @param string $quoteId (mandatory) the quote ID to accept - * @param array $params (optional) An array of additional parameters that the API endpoint allows - * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response - * - * @return array containing the response - * @throws \Exception - */ - public function convertAccept(string $quoteId, array $params = []) - { - $request = [ - 'quoteId' => $quoteId, - ]; - return $this->fapiRequest("v1/convert/acceptQuote", 'POST', array_merge($request, $params), true); - } - - /** - * convertStatus get the status of a convert request by orderId or quoteId - * - * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/convert/Order-Status - * - * $status = $api->convertStatus("orderId"); - * - * @property int $weight 50 - * - * @param string $orderId (optional) the order ID to get the status of (mandatory if $quoteId is not set) - * @param string $quoteId (optional) the quote ID to get the status of (mandatory if $orderId is not set) - * - * @return array containing the response - * @throws \Exception - */ - public function convertStatus($orderId = null, $quoteId = null, array $params = []) - { - $request = []; - if ($orderId) { - $request['orderId'] = $orderId; - } else if ($quoteId) { - $request['quoteId'] = $quoteId; - } else { - throw new \Exception('convertStatus(): orderId or quoteId must be set'); - } - return $this->fapiRequest("v1/convert/orderStatus", 'GET', array_merge($request, $params), true); + } + $this->assertEquals("https://api.binance.com/api/v3/ticker/price", self::$capturedUrl); + } + + public function testSpotPrice() + { + try { + $this->binance->price($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/ticker/price?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + } + + public function testSpotBookPrices() + { + try { + $this->binance->bookPrices(); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://api.binance.com/api/v3/ticker/bookTicker", self::$capturedUrl); + } + + public function testSpotAccount() + { + try { + $this->binance->account(); + + } catch (\Throwable $e) { + + } + $this->assertTrue(str_starts_with(self::$capturedUrl, "https://api.binance.com/api/v3/account")); + } + + public function testSpotPrevDay() + { + try { + $this->binance->prevDay($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/ticker/24hr?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + } + + public function testSpotTradingDay() + { + try { + $this->binance->tradingDay(null, $this->symbols); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/ticker/tradingDay?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals(json_encode($this->symbols), $params['symbols']); + } + + public function testSpotRollingWindowPriceChange() + { + try { + $this->binance->rollingWindowPriceChange($this->symbol, null, '2d'); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/ticker?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals('2d', $params['windowSize']); + } + + public function testSpotAggTrades() + { + try { + $this->binance->aggTrades($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/aggTrades?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + + } + + public function testSpotHistoricalTrades() + { + try { + $this->binance->historicalTrades($this->symbol, $this->limit); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/trades?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + + } + + public function testSpotHistoricalTradesWithTradeId() + { + try { + $this->binance->historicalTrades($this->symbol, $this->limit, $this->tradeId); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/historicalTrades?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->tradeId, $params['fromId']); + + } + + public function testSpotDepth() + { + try { + $this->binance->depth($this->symbol, $this->limit); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/depth?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + } + + public function testSpotBalances() + { + try { + $this->binance->balances(); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/account"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + parse_str(self::$capturedBody, $params); + } + + public function testFuturesBalances() + { + try { + $this->binance->balances('futures', [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v2/balance?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesBalancesV3() + { + try { + $this->binance->balances('futures', [ 'recvWindow' => $this->recvWindow ], 'v3'); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v3/balance?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testCoins() + { + try { + $this->binance->coins(); + + } catch (\Throwable $e) { + + } + $this->assertTrue(str_starts_with(self::$capturedUrl, "https://api.binance.com/sapi/v1/capital/config/getall")); + } + + public function testSpotCandlesticks() + { + try { + $this->binance->candlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/klines?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->interval, $params['interval']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + } + + public function testSpotUiCandlesticks() + { + try { + $this->binance->uiCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/uiKlines?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->interval, $params['interval']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + } + + public function testSpotAccountSnapshot() + { + try { + $this->binance->accountSnapshot('SPOT', $this->nbrDays, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/sapi/v1/accountSnapshot?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals('SPOT', $params['type']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->nbrDays, $params['limit']); + + } + + public function testMarginAccountSnapshot() + { + try { + $this->binance->accountSnapshot('MARGIN', $this->nbrDays, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/sapi/v1/accountSnapshot?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals('MARGIN', $params['type']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->nbrDays, $params['limit']); + + } + + public function testPreventedMatches() + { + try { + $this->binance->preventedMatches($this->symbol, null, $this->orderId); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/myPreventedMatches?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->orderId, $params['orderId']); + + } + + public function testAllocations() + { + try { + $this->binance->allocations($this->symbol, $this->startTime, $this->endTime, $this->fromId, $this->limit, $this->orderId); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/myAllocations?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->fromId, $params['fromAllocationId']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->orderId, $params['orderId']); + } + + public function testCommissionRate() + { + try { + $this->binance->commissionRate($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/account/commission?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + } + + public function testOrderAmendments() + { + try { + $this->binance->orderAmendments($this->symbol, $this->orderId); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/order/amendments?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->orderId, $params['orderId']); + } + + public function testFuturesAccountSnapshot() + { + try { + $this->binance->accountSnapshot('FUTURES', $this->nbrDays, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/sapi/v1/accountSnapshot?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals('FUTURES', $params['type']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->nbrDays, $params['limit']); + + } + + public function testAccountStatus() + { + try { + $this->binance->accountStatus(); + + } catch (\Throwable $e) { + + } + $this->assertTrue(str_starts_with(self::$capturedUrl, "https://api.binance.com/sapi/v1/account/status")); + } + + public function testApiRestrictions() + { + try { + $this->binance->apiRestrictions(); + + } catch (\Throwable $e) { + + } + $this->assertTrue(str_starts_with(self::$capturedUrl, "https://api.binance.com/sapi/v1/account/apiRestrictions")); + } + + public function testApiTradingStatus() + { + try { + $this->binance->apiTradingStatus(); + + } catch (\Throwable $e) { + + } + $this->assertTrue(str_starts_with(self::$capturedUrl, "https://api.binance.com/sapi/v1/account/apiTradingStatus")); + } + + public function testOcoOrder() + { + try { + $this->binance->ocoOrder($this->side, $this->symbol, $this->quantity, $this->price, $this->stopprice, $this->stoplimitprice); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://api.binance.com/api/v3/order/oco", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->side, $params['side']); + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertEquals($this->price, $params['price']); + $this->assertEquals($this->stopprice, $params['stopPrice']); + $this->assertEquals($this->stoplimitprice, $params['stopLimitPrice']); + $this->assertEquals('GTC', $params['stopLimitTimeInForce']); + } + + public function testSpotAvgPrice() + { + try { + $this->binance->avgPrice($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/avgPrice?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + } + + public function testBswapQuote() + { + try { + $this->binance->bswapQuote($this->baseAsset, $this->quoteAsset, $this->quoteQty); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/sapi/v1/bswap/quote?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->quoteAsset, $params['quoteAsset']); + $this->assertEquals($this->baseAsset, $params['baseAsset']); + $this->assertEquals($this->quoteQty, $params['quoteQty']); + + } + + public function testFuturesTime() + { + try { + $this->binance->futuresTime(); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/time", self::$capturedUrl); + } + + public function testFuturesExchangeInfo() + { + try { + $this->binance->futuresExchangeInfo(); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/exchangeInfo", self::$capturedUrl); + } + + public function testFuturesDepth() + { + try { + $this->binance->futuresDepth($this->symbol, $this->limit); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/depth?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + } + + public function testFuturesRecentTrades() + { + try { + $this->binance->futuresRecentTrades($this->symbol, $this->limit); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/trades?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + + } + + public function testFuturesHistoricalTrades() + { + try { + $this->binance->futuresHistoricalTrades($this->symbol, $this->limit, $this->tradeId); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/historicalTrades?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->tradeId, $params['fromId']); + + } + + public function testFuturesAggTrades() + { + try { + $this->binance->futuresAggTrades($this->symbol, $this->fromId, $this->startTime, $this->endTime, $this->limit); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/aggTrades?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->fromId, $params['fromId']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['limit']); + + } + + public function testFuturesCandlesticks() + { + try { + $this->binance->futuresCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/klines?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->interval, $params['interval']); + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + + } + + public function testFuturesContinuousCandlesticks() + { + try { + $this->binance->futuresContinuousCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime, $this->contractType); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/continuousKlines?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->interval, $params['interval']); + $this->assertEquals($this->symbol, $params['pair']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->contractType, $params['contractType']); + + } + + public function testFuturesIndexPriceCandlesticks() + { + try { + $this->binance->futuresIndexPriceCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/indexPriceKlines?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->interval, $params['interval']); + $this->assertEquals($this->symbol, $params['pair']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + + } + + public function testFuturesMarkPriceCandlesticks() + { + try { + $this->binance->futuresMarkPriceCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/markPriceKlines?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->interval, $params['interval']); + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + + } + + public function testFuturesPremiumIndexCandlesticks() + { + try { + $this->binance->futuresPremiumIndexCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/premiumIndexKlines?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->interval, $params['interval']); + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + } + + public function testFuturesMarkPrice() + { + try { + $this->binance->futuresMarkPrice($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/premiumIndex?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + + } + + public function testFuturesFundingRateHistory() + { + try { + $this->binance->futuresFundingRateHistory($this->symbol, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/fundingRate?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + + } + + public function testFuturesFundingInfo() + { + try { + $this->binance->futuresFundingInfo(); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/fundingInfo", self::$capturedUrl); + } + + public function testFuturesPrevDay() + { + try { + $this->binance->futuresPrevDay($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/ticker/24hr?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + + } + + public function testFuturesPrice() + { + try { + $this->binance->futuresPrice($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/ticker/price?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + + } + + public function testFuturesPrices() + { + try { + $this->binance->futuresPrices(); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/ticker/price", self::$capturedUrl); + } + + public function testFuturesPriceV2() + { + try { + $this->binance->futuresPriceV2($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v2/ticker/price?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + } + + public function testFuturesSymbolOrderBookTicker() + { + try { + $this->binance->futuresSymbolOrderBookTicker($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/ticker/bookTicker?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + + } + + public function testFuturesDeliveryPrice() + { + try { + $this->binance->futuresDeliveryPrice($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/futures/data/delivery-price?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['pair']); + + } + + public function testFuturesOpenInterest() + { + try { + $this->binance->futuresOpenInterest($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/openInterest?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + + } + + public function testFuturesOpenInterestHistory() + { + try { + $this->binance->futuresOpenInterestHistory($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/futures/data/openInterestHist?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->period, $params['period']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + + } + + public function testFuturesTopLongShortPositionRatio() + { + try { + $this->binance->futuresTopLongShortPositionRatio($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/futures/data/topLongShortPositionRatio?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->period, $params['period']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + + } + + public function testFuturesTopLongShortAccountRatio() + { + try { + $this->binance->futuresTopLongShortAccountRatio($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/futures/data/topLongShortAccountRatio?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->period, $params['period']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + + } + + public function testFuturesGlobalLongShortAccountRatio() + { + try { + $this->binance->futuresGlobalLongShortAccountRatio($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/futures/data/globalLongShortAccountRatio?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->period, $params['period']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + + } + + public function testFuturesTakerLongShortRatio() + { + try { + $this->binance->futuresTakerLongShortRatio($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/futures/data/takerlongshortRatio?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->period, $params['period']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + + } + + public function testFuturesBasis() + { + try { + $this->binance->futuresBasis($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime, $this->contractType); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/futures/data/basis?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['pair']); + $this->assertEquals($this->period, $params['period']); + $this->assertEquals($this->contractType, $params['contractType']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + + } + + public function testFuturesIndexInfo() + { + try { + $this->binance->futuresIndexInfo($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/indexInfo?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + + } + + public function testFuturesAssetIndex() + { + try { + $this->binance->futuresAssetIndex($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/assetIndex?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + + } + + public function testFuturesConstituents() + { + try { + $this->binance->futuresConstituents($this->symbol); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/constituents?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + + } + + public function testFuturesOrder() + { + try { + $this->binance->futuresOrder('BUY', 'BTCUSDT', 1, 1000); + } catch(\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/order", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals("BTCUSDT", $params['symbol']); + $this->assertEquals("BUY", $params['side']); + $this->assertEquals("LIMIT", $params['type']); + $this->assertEquals(1, $params['quantity']); + $this->assertEquals(1000, $params['price']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); + } + + public function testFuturesAlgoOrder() + { + try { + $this->binance->futuresOrder('BUY', 'BTCUSDT', 1, 1000, 'STOP', [ + 'stopPrice' => 900, + ]); + } + catch(\Throwable $e) { + print_r($e->getMessage()); + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/algoOrder", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals("BTCUSDT", $params['symbol']); + $this->assertEquals("BUY", $params['side']); + $this->assertEquals("STOP", $params['type']); + $this->assertEquals(1, $params['quantity']); + $this->assertEquals(1000, $params['price']); + $this->assertEquals(900, $params['triggerPrice']); + $this->assertEquals("CONDITIONAL", $params['algoType']); + $this->assertTrue(str_starts_with($params['clientAlgoId'], $this->CONTRACT_ORDER_PREFIX)); + } + + public function testFuturesBuy() + { + try { + $this->binance->futuresBuy($this->symbol, $this->quantity, $this->price, $this->type); + + } catch (\Throwable $e) { + + } + + $endpoint = "https://fapi.binance.com/fapi/v1/order"; + $this->assertEquals($endpoint, self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertEquals($this->price, $params['price']); + $this->assertEquals($this->type, $params['type']); + $this->assertEquals("BUY", $params['side']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); + } + + public function testFuturesBuyTest() + { + try { + $this->binance->futuresBuyTest($this->symbol, $this->quantity, $this->price, $this->type); + + } catch (\Throwable $e) { + + } + + $endpoint = "https://fapi.binance.com/fapi/v1/order/test"; + $this->assertEquals($endpoint, self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertEquals($this->price, $params['price']); + $this->assertEquals($this->type, $params['type']); + $this->assertEquals("BUY", $params['side']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); + } + + public function testFuturesSell() + { + try { + $this->binance->futuresSell($this->symbol, $this->quantity, $this->price, $this->type); + + } catch (\Throwable $e) { + + } + + $endpoint = "https://fapi.binance.com/fapi/v1/order"; + $this->assertEquals($endpoint, self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertEquals($this->price, $params['price']); + $this->assertEquals($this->type, $params['type']); + $this->assertEquals("SELL", $params['side']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); + } + + public function testFuturesSellTest() + { + try { + $this->binance->futuresSellTest($this->symbol, $this->quantity, $this->price, $this->type); + + } catch (\Throwable $e) { + + } + + $endpoint = "https://fapi.binance.com/fapi/v1/order/test"; + $this->assertEquals($endpoint, self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertEquals($this->price, $params['price']); + $this->assertEquals($this->type, $params['type']); + $this->assertEquals("SELL", $params['side']); + $this->assertEquals("GTC", $params['timeInForce']); + $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); + } + + public function testFuturesBatchOrders() + { + $order = [ + 'symbol' => $this->symbol, + 'side' => $this->side, + 'quantity' => $this->quantity, + 'price' => $this->price, + 'type' => $this->type, + ]; + + try { + $this->binance->futuresBatchOrders([ $order ], [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/batchOrders?batchOrders="; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $ordersString = substr(self::$capturedUrl, strlen($endpoint)); + + $orders = json_decode(urldecode($ordersString), true); + $this->assertEquals($this->symbol, $orders[0]['symbol']); + $this->assertEquals($this->side, $orders[0]['side']); + $this->assertEquals($this->quantity, $orders[0]['quantity']); + $this->assertEquals($this->price, $orders[0]['price']); + $this->assertEquals($this->type, $orders[0]['type']); + $this->assertEquals("GTC", $orders[0]['timeInForce']); + $this->assertTrue(str_starts_with($orders[0]['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesEditOrder() + { + try { + $this->binance->futuresEditOrder($this->symbol, $this->side, $this->quantity, $this->price, $this->orderId); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/order?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $orderString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($orderString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->side, $params['side']); + $this->assertEquals($this->quantity, $params['quantity']); + $this->assertEquals($this->price, $params['price']); + $this->assertEquals($this->orderId, $params['orderId']); + $this->assertEquals('GTC', $params['timeInForce']); + } + + public function testFuturesEditOrders() + { + $order = [ + 'symbol' => $this->symbol, + 'side' => $this->side, + 'quantity' => $this->quantity, + 'price' => $this->price, + 'orderId' => $this->orderId, + ]; + + try { + $this->binance->futuresEditOrders([ $order ], [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/batchOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + $orders = json_decode($params['batchOrders'], true); + $this->assertEquals($this->symbol, $orders[0]['symbol']); + $this->assertEquals($this->side, $orders[0]['side']); + $this->assertEquals($this->quantity, $orders[0]['quantity']); + $this->assertEquals($this->price, $orders[0]['price']); + $this->assertEquals("GTC", $orders[0]['timeInForce']); + } + + public function testFuturesOrderAmendment() + { + try { + $this->binance->futuresOrderAmendment($this->symbol, $this->orderId, $this->origClientOrderId, $this->startTime, $this->endTime, $this->limit, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/orderAmendment?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->orderId, $params['orderId']); + $this->assertEquals($this->origClientOrderId, $params['origClientOrderId']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesCancel() + { + try { + $this->binance->futuresCancel($this->symbol, $this->orderid); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/order?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->orderid, $params['orderId']); + + } + + public function testFuturesCancelBatchOrdersByOrderIds() + { + try { + $this->binance->futuresCancelBatchOrders($this->symbol, $this->orderIdList, null, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/batchOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals('[123456,654321]', $params['orderIdList']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesCancelBatchOrdersByClientOrderIds() + { + try { + $this->binance->futuresCancelBatchOrders($this->symbol, null, $this->origClientOrderIdList, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/batchOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals('["test client order id 1","test client order id 2"]', $params['origClientOrderIdList']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesCancelOpenOrders() + { + try { + $this->binance->futuresCancelOpenOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/allOpenOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesCancelOpenAlgoOrders() + { + try { + $this->binance->futuresCancelOpenAlgoOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/algoOpenOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesCountdownCancelAllOrders() + { + try { + $this->binance->futuresCountdownCancelAllOrders($this->symbol, $this->countdownTime, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/countdownCancelAll", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->countdownTime, $params['countdownTime']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesOrderStatusByOrderId() + { + try { + $this->binance->futuresOrderStatus($this->symbol, $this->orderId, null, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/order?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->orderId, $params['orderId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesAlgoOrderStatusByOrderId() + { + try { + $this->binance->futuresAlgoOrderStatus($this->orderId, null, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/algoOrder?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->orderId, $params['algoId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesOrderStatusByClientOrderId() + { + try { + $this->binance->futuresOrderStatus($this->symbol, null, $this->origClientOrderId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/order?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->origClientOrderId, $params['origClientOrderId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesAlgoOrderStatusByClientOrderId() + { + try { + $this->binance->futuresAlgoOrderStatus(null, $this->origClientOrderId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/algoOrder?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->origClientOrderId, $params['clientAlgoId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesAllOrders() + { + try { + $this->binance->futuresAllOrders($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->orderId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/allOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->orderId, $params['orderId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesAllAlgoOrders() + { + try { + $this->binance->futuresAllAlgoOrders($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->orderId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/allAlgoOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->orderId, $params['algoId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesOpenOrders() + { + try { + $this->binance->futuresOpenOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/openOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesOpenAlgoOrders() + { + try { + $this->binance->futuresOpenAlgoOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/openAlgoOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesOpenOrderByOrderId() + { + try { + $this->binance->futuresOpenOrder($this->symbol, $this->orderId, null, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/openOrder?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->orderId, $params['orderId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesOpenOrderByClientOrderId() + { + try { + $this->binance->futuresOpenOrder($this->symbol, null, $this->origClientOrderId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/openOrder?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->origClientOrderId, $params['origClientOrderId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesForceOrders() + { + try { + $this->binance->futuresForceOrders($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->autoCloseType, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + + $endpoint = "https://fapi.binance.com/fapi/v1/forceOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->autoCloseType, $params['autoCloseType']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesMyTrades() + { + try { + $this->binance->futuresMyTrades($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->orderId, $this->fromId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/userTrades?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->orderId, $params['orderId']); + $this->assertEquals($this->fromId, $params['fromId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesHistory() + { + try { + $this->binance->futuresHistory($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->orderId, $this->fromId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/userTrades?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->orderId, $params['orderId']); + $this->assertEquals($this->fromId, $params['fromId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesSetMarginMode() + { + try { + $this->binance->futuresSetMarginMode($this->symbol, $this->marginType, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/marginType", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->marginType, $params['marginType']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesPositionMode() + { + try { + $this->binance->futuresPositionMode([ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/positionSide/dual?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesSetPositionMode() + { + try { + $this->binance->futuresSetPositionMode($this->dualSidePosition, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/positionSide/dual", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->dualSidePosition, $params['dualSidePosition']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesSetLeverage() + { + try { + $this->binance->futuresSetLeverage($this->leverage, $this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/leverage", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->leverage, $params['leverage']); + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesMultiAssetsMarginMode() + { + try { + $this->binance->futuresMultiAssetsMarginMode([ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/multiAssetsMargin?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + + public function testFuturesSetMultiAssetsMarginMode() + { + try { + $this->binance->futuresSetMultiAssetsMarginMode($this->multiAssetsMarginMode, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/multiAssetsMarginMode", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->multiAssetsMarginMode, $params['multiAssetsMarginMode']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesAddMargin() + { + try { + $this->binance->futuresAddMargin($this->symbol, $this->amount, $this->positionSide, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/positionMargin"; + $this->assertEquals($endpoint, self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->amount, $params['amount']); + $this->assertEquals($this->positionSide, $params['positionSide']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + $this->assertEquals(1, $params['type']); + } + + public function testFuturesReduceMargin() + { + try { + $this->binance->futuresReduceMargin($this->symbol, $this->amount, $this->positionSide, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/positionMargin"; + $this->assertEquals($endpoint, self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->amount, $params['amount']); + $this->assertEquals($this->positionSide, $params['positionSide']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + $this->assertEquals(2, $params['type']); + } + + public function testFuturesPositions() + { + try { + $this->binance->futuresPositions($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v3/positionRisk?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesPositionsV2() + { + try { + $this->binance->futuresPositionsV2($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v2/positionRisk?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesPositionsV3() + { + try { + $this->binance->futuresPositionsV3($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v3/positionRisk?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesPosition() + { + try { + $this->binance->futuresPosition($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v3/positionRisk?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesPositionV2() + { + try { + $this->binance->futuresPositionV2($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v2/positionRisk?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesPositionV3() + { + try { + $this->binance->futuresPositionV3($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v3/positionRisk?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesAdlQuantile() + { + try { + $this->binance->futuresAdlQuantile($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/adlQuantile?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesPositionMarginChangeHistory() + { + try { + $this->binance->futuresPositionMarginChangeHistory($this->symbol, $this->startTime, $this->endTime, $this->limit, 'ADD', [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/positionMargin/history?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals(1, $params['addOrReduce']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesAccount() + { + try { + $this->binance->futuresAccount([ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v3/account?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesAccountV2() + { + try { + $this->binance->futuresAccountV2([ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v2/account?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesAccountV3() + { + try { + $this->binance->futuresAccountV3([ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v3/account?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesTradeFee() + { + try { + $this->binance->futuresTradeFee($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/commissionRate?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesAccountConfig() + { + try { + $this->binance->futuresAccountConfig([ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/accountConfig?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesMarginModes() + { + try { + $this->binance->futuresMarginModes($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/symbolConfig?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesOrderRateLimit() + { + try { + $this->binance->futuresOrderRateLimit([ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/rateLimit/order?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testOrderRateLimit() + { + try { + $this->binance->orderRateLimit([ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://api.binance.com/api/v3/rateLimit/order?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesLeverages() + { + try { + $this->binance->futuresLeverages($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/leverageBracket?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesLedger() + { + try { + $this->binance->futuresLedger($this->symbol, $this->incomeType, $this->startTime, $this->endTime, $this->limit, $this->page, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/income?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->incomeType, $params['incomeType']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->page, $params['page']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesTradingStatus() + { + try { + $this->binance->futuresTradingStatus($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/apiTradingStatus?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesDownloadIdForTransactions() + { + try { + $this->binance->futuresDownloadIdForTransactions($this->startTime, $this->endTime, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/income/asyn?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesDownloadTransactionsByDownloadId() + { + try { + $this->binance->futuresDownloadTransactionsByDownloadId($this->downloadId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/income/asyn/id?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->downloadId, $params['downloadId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesDownloadIdForOrders() + { + try { + $this->binance->futuresDownloadIdForOrders($this->startTime, $this->endTime, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/order/asyn?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesDownloadOrdersByDownloadId() + { + try { + $this->binance->futuresDownloadOrdersByDownloadId($this->downloadId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/order/asyn/id?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->downloadId, $params['downloadId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesDownloadIdForTrades() + { + try { + $this->binance->futuresDownloadIdForTrades($this->startTime, $this->endTime, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/trade/asyn?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesDownloadTradesByDownloadId() + { + try { + $this->binance->futuresDownloadTradesByDownloadId($this->downloadId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/trade/asyn/id?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->downloadId, $params['downloadId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesFeeBurn() + { + try { + $this->binance->futuresFeeBurn(true, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/feeBurn", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals('true', $params['feeBurn']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testFuturesFeeBurnStatus() + { + try { + $this->binance->futuresFeeBurnStatus([ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/feeBurn?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testConvertExchangeInfo() + { + try { + $this->binance->convertExchangeInfo($this->fromAsset, $this->toAsset); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/convert/exchangeInfo?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->fromAsset, $params['fromAsset']); + $this->assertEquals($this->toAsset, $params['toAsset']); + } + + public function testConvertSend() + { + try { + $this->binance->convertSend($this->fromAsset, $this->toAsset, $this->fromAmount, null, $this->validTime, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/convert/getQuote", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->fromAsset, $params['fromAsset']); + $this->assertEquals($this->toAsset, $params['toAsset']); + $this->assertEquals($this->fromAmount, $params['fromAmount']); + $this->assertEquals($this->validTime, $params['validTime']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testConvertAccept() + { + try { + $this->binance->convertAccept($this->quoteId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $this->assertEquals("https://fapi.binance.com/fapi/v1/convert/acceptQuote", self::$capturedUrl); + + parse_str(self::$capturedBody, $params); + + $this->assertEquals($this->quoteId, $params['quoteId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + } + + public function testConvertStatusByOrderId() + { + try { + $this->binance->convertStatus($this->orderId, null); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/convert/orderStatus?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->orderId, $params['orderId']); + } + + public function testConvertStatusByQuoteId() + { + try { + $this->binance->convertStatus(null, $this->quoteId); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/convert/orderStatus?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->quoteId, $params['quoteId']); } } From 08b214699c618c1e4dc2ddfae3c83e44891b568d Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Sun, 7 Dec 2025 22:15:30 +0300 Subject: [PATCH 08/15] fix: correct file updated --- php-binance-api.php | 9461 +++++++++++++++++++++++++++++-------------- 1 file changed, 6446 insertions(+), 3015 deletions(-) diff --git a/php-binance-api.php b/php-binance-api.php index 2ee6d10..f186e93 100755 --- a/php-binance-api.php +++ b/php-binance-api.php @@ -1,3218 +1,6649 @@ 0, + ]; // /< Additional connection options + protected $proxyConf = null; // /< Used for story the proxy configuration + protected $caOverride = false; // /< set this if you donnot wish to use CA bundle auto download feature + protected $transfered = 0; // /< This stores the amount of bytes transfered + protected $requestCount = 0; // /< This stores the amount of API requests + protected $httpDebug = false; // /< If you enable this, curl will output debugging information + protected $subscriptions = []; // /< View all websocket subscriptions + protected $recvWindow = null; // /< The amount of time in milliseconds to wait for a response from the server for endpoints that accept a recvWindow parameter + + // /< value of available onOrder assets + + protected $exchangeInfo = null; + protected $futuresExchangeInfo = null; + protected $lastRequest = []; + + protected $xMbxUsedWeight = 0; + protected $xMbxUsedWeight1m = 0; + + public $headers = []; private $SPOT_ORDER_PREFIX = "x-B3AUXNYV"; private $CONTRACT_ORDER_PREFIX = "x-ftGmvgAN"; - // Default values for the tests - private $symbol = 'ETHUSDT'; - private $quantity = '1.234'; - private $price = '1000'; - private $stopprice = '1100'; - private $stoplimitprice = '900'; - private $type = 'LIMIT'; - private $orderid = '000'; - private $orderId = '1234567890'; - private $limit = 2; - private $fromOrderId = 1; - private $fromTradeId = 2; - private $startTime = 1; - private $endTime = 6; - private $symbols = ['ETHUSDT','BTCUSDT']; - private $asset = 'USDT'; - private $assets = ['IOST','AAVE','CHZ']; - private $address = '0x1234567890abcdef1234567890abcdef12345678'; - private $amount = 10.1; - private $addressTag = '123456'; - private $addressName = 'MyAddress'; - private $transactionFeeFlag = true; - private $network = 'TESTNetwork'; - private $fromSymbol = 'USDT'; - private $toSymbol = 'BNB'; - private $recvWindow = 1000; - private $current = 2; - private $tradeId = 3; - private $side = 'BUY'; - private $test = false; - private $interval = '15m'; - private $nbrDays = 6; - private $baseAsset = 'ETH'; - private $quoteAsset = 'USDT'; - private $quoteQty = 10.3; - private $fromId = 1; - private $contractType = 'CURRENT_QUARTER'; - private $period = '15m'; - private $origClientOrderId = 'test client order id'; - private $orderIdList = ['123456', '654321']; - private $origClientOrderIdList = ['test client order id 1', 'test client order id 2']; - private $countdownTime = 100; - private $autoCloseType = 'LIQUIDATION'; - private $marginType = 'CROSSED'; - private $dualSidePosition = true; - private $leverage = 10; - private $multiAssetsMarginMode = true; - private $positionSide = 'SHORT'; - private $incomeType = 'COMMISSION_REBATE'; - private $page = 3; - private $downloadId = 'testDownloadId'; - private $fromAsset = 'USDT'; - private $toAsset = 'BNB'; - private $fromAmount = '100'; - private $validTime = '10s'; - private $quoteId = 'testQuoteId'; - - public function setUp(): void { - $this->binance = new MockBinanceAPI('api_key', 'api_secret'); - } - - public function testPricesSpot() + /** + * Constructor for the class, + * send as many argument as you want. + * + * No arguments - use file setup + * 1 argument - file to load config from + * 2 arguments - api key and api secret + * 3 arguments - api key, api secret and use testnet flag + * + * @return null + */ + public function __construct() + { + $param = func_get_args(); + switch (count($param)) { + case 0: + $this->setupApiConfigFromFile(); + $this->setupProxyConfigFromFile(); + $this->setupCurlOptsFromFile(); + break; + case 1: + $this->setupApiConfigFromFile($param[0]); + $this->setupProxyConfigFromFile($param[0]); + $this->setupCurlOptsFromFile($param[0]); + break; + case 2: + $this->api_key = $param[0]; + $this->api_secret = $param[1]; + break; + case 3: + $this->api_key = $param[0]; + $this->api_secret = $param[1]; + $this->useTestnet = (bool)$param[2]; + break; + default: + echo 'Please see valid constructors here: https://github.com/jaggedsoft/php-binance-api/blob/master/examples/constructor.php'; + } + } + + /** + * magic get for protected and protected members + * + * @param $file string the name of the property to return + * @return null + */ + public function __get(string $member) + { + if (property_exists($this, $member)) { + return $this->$member; + } + return null; + } + + /** + * magic set for protected and protected members + * + * @param $member string the name of the member property + * @param $value the value of the member property + */ + public function __set(string $member, $value) + { + $this->$member = $value; + } + + /** + * If no paramaters are supplied in the constructor, this function will attempt + * to load the api_key and api_secret from the users home directory in the file + * ~/jaggedsoft/php-binance-api.json + * + * @param $file string file location + * @return null + */ + protected function setupApiConfigFromFile(?string $file = null) { - try { - $this->binance->prices(); - } catch(\Throwable $e) { + $file = is_null($file) ? getenv("HOME") . "/.config/jaggedsoft/php-binance-api.json" : $file; + + if (empty($this->api_key) === false || empty($this->api_secret) === false) { + return; + } + if (file_exists($file) === false) { + echo "Unable to load config from: " . $file . PHP_EOL; + echo "Detected no API KEY or SECRET, all signed requests will fail" . PHP_EOL; + return; + } + $contents = json_decode(file_get_contents($file), true); + $this->api_key = isset($contents['api-key']) ? $contents['api-key'] : ""; + $this->api_secret = isset($contents['api-secret']) ? $contents['api-secret'] : ""; + $this->useTestnet = isset($contents['use-testnet']) ? (bool)$contents['use-testnet'] : false; + } + + /** + * If no paramaters are supplied in the constructor, this function will attempt + * to load the acurlopts from the users home directory in the file + * ~/jaggedsoft/php-binance-api.json + * + * @param $file string file location + * @return null + */ + protected function setupCurlOptsFromFile(?string $file = null) + { + $file = is_null($file) ? getenv("HOME") . "/.config/jaggedsoft/php-binance-api.json" : $file; + + if (count($this->curlOpts) > 0) { + return; + } + if (file_exists($file) === false) { + echo "Unable to load config from: " . $file . PHP_EOL; + echo "No curl options will be set" . PHP_EOL; + return; + } + $contents = json_decode(file_get_contents($file), true); + $this->curlOpts = isset($contents['curlOpts']) && is_array($contents['curlOpts']) ? $contents['curlOpts'] : []; + } + + /** + * If no paramaters are supplied in the constructor for the proxy confguration, + * this function will attempt to load the proxy info from the users home directory + * ~/jaggedsoft/php-binance-api.json + * + * @return null + */ + protected function setupProxyConfigFromFile(?string $file = null) + { + $file = is_null($file) ? getenv("HOME") . "/.config/jaggedsoft/php-binance-api.json" : $file; + + if (is_null($this->proxyConf) === false) { + return; + } + if (file_exists($file) === false) { + echo "Unable to load config from: " . $file . PHP_EOL; + echo "No proxies will be used " . PHP_EOL; + return; + } + $contents = json_decode(file_get_contents($file), true); + if (isset($contents['proto']) === false) { + return; + } + if (isset($contents['address']) === false) { + return; + } + if (isset($contents['port']) === false) { + return; + } + $this->proxyConf['proto'] = $contents['proto']; + $this->proxyConf['address'] = $contents['address']; + $this->proxyConf['port'] = $contents['port']; + if (isset($contents['user'])) { + $this->proxyConf['user'] = isset($contents['user']) ? $contents['user'] : ""; + } + if (isset($contents['pass'])) { + $this->proxyConf['pass'] = isset($contents['pass']) ? $contents['pass'] : ""; + } + } + + public static function uuid22($length = 22) { + return bin2hex(random_bytes(intval($length / 2))); + } + + protected function generateSpotClientOrderId() + { + return $this->SPOT_ORDER_PREFIX . self::uuid22(); + } + + protected function generateFuturesClientOrderId() + { + return $this->CONTRACT_ORDER_PREFIX . self::uuid22();; + } + + /** + * buy attempts to create a currency order + * each currency supports a number of order types, such as + * -LIMIT + * -MARKET + * -STOP_LOSS + * -STOP_LOSS_LIMIT + * -TAKE_PROFIT + * -TAKE_PROFIT_LIMIT + * -LIMIT_MAKER + * + * You should check the @see exchangeInfo for each currency to determine + * what types of orders can be placed against specific pairs + * + * $quantity = 1; + * $price = 0.0005; + * $order = $api->buy("BNBBTC", $quantity, $price); + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity required + * @param $price string price per unit you want to spend + * @param $type string type of order + * @param $params array addtional options for order type + * @return array with error message or the order details + */ + public function buy(string $symbol, $quantity, $price, string $type = "LIMIT", array $params = []) + { + return $this->order("BUY", $symbol, $quantity, $price, $type, $params); + } + + /** + * buyTest attempts to create a TEST currency order + * + * @see buy() + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity required + * @param $price string price per unit you want to spend + * @param $type string config + * @param $params array config + * @return array with error message or empty or the order details + */ + public function buyTest(string $symbol, $quantity, $price, string $type = "LIMIT", array $params = []) + { + return $this->order("BUY", $symbol, $quantity, $price, $type, $params, true); + } + + /** + * sell attempts to create a currency order + * each currency supports a number of order types, such as + * -LIMIT + * -MARKET + * -STOP_LOSS + * -STOP_LOSS_LIMIT + * -TAKE_PROFIT + * -TAKE_PROFIT_LIMIT + * -LIMIT_MAKER + * + * You should check the @see exchangeInfo for each currency to determine + * what types of orders can be placed against specific pairs + * + * $quantity = 1; + * $price = 0.0005; + * $order = $api->sell("BNBBTC", $quantity, $price); + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity required + * @param $price string price per unit you want to spend + * @param $type string type of order + * @param $params array addtional options for order type + * @return array with error message or the order details + */ + public function sell(string $symbol, $quantity, $price, string $type = "LIMIT", array $params = []) + { + return $this->order("SELL", $symbol, $quantity, $price, $type, $params); + } + + /** + * sellTest attempts to create a TEST currency order + * + * @see sell() + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity required + * @param $price string price per unit you want to spend + * @param $type array config + * @param $params array config + * @return array with error message or empty or the order details + */ + public function sellTest(string $symbol, $quantity, $price, string $type = "LIMIT", array $params = []) + { + return $this->order("SELL", $symbol, $quantity, $price, $type, $params, true); + } + + /** + * marketQuoteBuy attempts to create a currency order at given market price + * + * $quantity = 1; + * $order = $api->marketQuoteBuy("BNBBTC", $quantity); + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity of the quote to use + * @param $params array additional options for order type + * @return array with error message or the order details + */ + public function marketQuoteBuy(string $symbol, $quantity, array $params = []) + { + $params['isQuoteOrder'] = true; + + return $this->order("BUY", $symbol, $quantity, 0, "MARKET", $params); + } + + /** + * marketQuoteBuyTest attempts to create a TEST currency order at given market price + * + * @see marketBuy() + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity of the quote to use + * @param $params array additional options for order type + * @return array with error message or the order details + */ + public function marketQuoteBuyTest(string $symbol, $quantity, array $params = []) + { + $params['isQuoteOrder'] = true; + + return $this->order("BUY", $symbol, $quantity, 0, "MARKET", $params, true); + } + + /** + * marketBuy attempts to create a currency order at given market price + * + * $quantity = 1; + * $order = $api->marketBuy("BNBBTC", $quantity); + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity required + * @param $params array addtional options for order type + * @return array with error message or the order details + */ + public function marketBuy(string $symbol, $quantity, array $params = []) + { + return $this->order("BUY", $symbol, $quantity, 0, "MARKET", $params); + } + + /** + * marketBuyTest attempts to create a TEST currency order at given market price + * + * @see marketBuy() + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity required + * @param $params array addtional options for order type + * @return array with error message or the order details + */ + public function marketBuyTest(string $symbol, $quantity, array $params = []) + { + return $this->order("BUY", $symbol, $quantity, 0, "MARKET", $params, true); + } + + + /** + * numberOfDecimals() returns the signifcant digits level based on the minimum order amount. + * + * $dec = numberOfDecimals(0.00001); // Returns 5 + * + * @param $val float the minimum order amount for the pair + * @return integer (signifcant digits) based on the minimum order amount + */ + public function numberOfDecimals($val = 0.00000001) + { + $val = sprintf("%.14f", $val); + $parts = explode('.', $val); + $parts[1] = rtrim($parts[1], "0"); + return strlen($parts[1]); + } + + /** + * marketQuoteSell attempts to create a currency order at given market price + * + * $quantity = 1; + * $order = $api->marketQuoteSell("BNBBTC", $quantity); + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity of the quote you want to obtain + * @param $params array additional options for order type + * @return array with error message or the order details + */ + public function marketQuoteSell(string $symbol, $quantity, array $params = []) + { + $params['isQuoteOrder'] = true; + $c = $this->numberOfDecimals($this->exchangeInfo()['symbols'][$symbol]['filters'][1]['minQty']); + $quantity = $this->floorDecimal($quantity, $c); + + return $this->order("SELL", $symbol, $quantity, 0, "MARKET", $params); + } + + /** + * marketQuoteSellTest attempts to create a TEST currency order at given market price + * + * @see marketSellTest() + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity of the quote you want to obtain + * @param $params array additional options for order type + * @return array with error message or the order details + */ + public function marketQuoteSellTest(string $symbol, $quantity, array $params = []) + { + $params['isQuoteOrder'] = true; + + return $this->order("SELL", $symbol, $quantity, 0, "MARKET", $params, true); + } + + /** + * marketSell attempts to create a currency order at given market price + * + * $quantity = 1; + * $order = $api->marketSell("BNBBTC", $quantity); + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity required + * @param $params array addtional options for order type + * @return array with error message or the order details + */ + public function marketSell(string $symbol, $quantity, array $params = []) + { + $c = $this->numberOfDecimals($this->exchangeInfo()['symbols'][$symbol]['filters'][1]['minQty']); + $quantity = $this->floorDecimal($quantity, $c); + + return $this->order("SELL", $symbol, $quantity, 0, "MARKET", $params); + } + + /** + * marketSellTest attempts to create a TEST currency order at given market price + * + * @see marketSellTest() + * + * @param $symbol string the currency symbol + * @param $quantity string the quantity required + * @param $params array addtional options for order type + * @return array with error message or the order details + */ + public function marketSellTest(string $symbol, $quantity, array $params = []) + { + return $this->order("SELL", $symbol, $quantity, 0, "MARKET", $params, true); + } + + /** + * cancel attempts to cancel a currency order + * either orderid or origClientOrderId is mandatory + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#cancel-order-trade + * + * $orderid = "123456789"; + * $order = $api->cancel("BNBBTC", $orderid); + * + * @param string $symbol (mandatory) the currency symbol + * @param string $orderid (optional) the orderid to fetch (mandatory if $params['origClientOrderId'] is not provided) + * @param array $params an array of additional parameters that the API endpoint allows + * - @param string $params['origClientOrderId'] optional the original client order id (mandatory if $orderid is not provided) + * - @param string $params['cancelRestrictions'] optional ONLY_NEW (cancel will succeed if the order status is NEW) or ONLY_PARTIALLY_FILLED (cancel will succeed if the order status is PARTIALLY_FILLED) + * @return array with error message or the order details + * @throws \Exception + */ + public function cancel(string $symbol, ?string $orderid = null, $params = []) + { + $request = [ + "symbol" => $symbol, + ]; + if (!is_null($orderid)) { + $request["orderId"] = $orderid; + } else if (!isset($params['origClientOrderId'])) { + throw new Exception("Either orderId or origClientOrderId must be provided"); + } + return $this->apiRequest("v3/order", "DELETE", array_merge($request, $params), true); + } + + /** + * orderStatus attempts to get orders status + * either orderid or origClientOrderId is mandatory + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#query-order-user_data + * + * $orderid = "123456789"; + * $order = $api->orderStatus("BNBBTC", $orderid); + * + * @param string $symbol (mandatory) the currency symbol + * @param string $orderid (optional) the orderid to fetch (mandatory if $params['origClientOrderId'] is not provided) + * @param array $params an array of additional parameters that the API endpoint allows + * - @param string $params['origClientOrderId'] optional the original client order id (mandatory if $orderid is not provided) + * @return array with error message or the order details + * @throws \Exception + */ + public function orderStatus(string $symbol, ?string $orderid = null, array $params = []) + { + $request = [ + "symbol" => $symbol, + ]; + if (!is_null($orderid)) { + $request["orderId"] = $orderid; + } else if (!isset($params['origClientOrderId'])) { + throw new Exception("Either orderId or origClientOrderId must be provided"); + } + return $this->apiRequest("v3/order", "GET", array_merge($request, $params), true); + } + + /** + * openOrders attempts to get open orders for all currencies or a specific currency + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#current-open-orders-user_data + * + * $allOpenOrders = $api->openOrders(); + * $allBNBOrders = $api->openOrders( "BNBBTC" ); + * + * @param string $symbol (optional) the market symbol + * @return array with error message or the order details + * @throws \Exception + */ + public function openOrders(?string $symbol = null, array $params = []) + { + $request = []; + if (!is_null($symbol)) { + $request = [ + "symbol" => $symbol, + ]; + } + return $this->apiRequest("v3/openOrders", "GET", array_merge($request, $params), true); + } + + /** + * cancelOpenOrders - Cancel all open orders method + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#cancel-all-open-orders-on-a-symbol-trade + * + * $api->cancelOpenOrders( "BNBBTC" ); + * @param string $symbol (mandatory) the merket symbol + * @param array $params (optional) an array of additional parameters that the API endpoint allows + * @return array with error message or the order details + * @throws \Exception + */ + public function cancelOpenOrders(string $symbol, array $params = []) + { + $request = [ + "symbol" => $symbol, + ]; + return $this->apiRequest("v3/openOrders", "DELETE", array_merge($request, $params), true); + } + + /** + * orders attempts to get the orders for all or a specific currency + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#all-orders-user_data + * + * $allBNBOrders = $api->orders( "BNBBTC" ); + * + * @param string $symbol (mandatory) the currency symbol + * @param int $limit (optional) the amount of orders returned (default 500, max 1000) + * @param string $fromOrderId (optional) return the orders from this order onwards + * @param array $params (optional) an array of additional parameters that the API endpoint allows + * @return array with error message or array of orderDetails array + * @throws \Exception + */ + public function orders(string $symbol, ?int $limit = null, int $fromOrderId = null, array $params = []) + { + $request = [ + "symbol" => $symbol, + ]; + if (!is_null($limit)) { + $request["limit"] = $limit; + } + if (!is_null($fromOrderId)) { + $request["orderId"] = $fromOrderId; + } + return $this->apiRequest("v3/allOrders", "GET", array_merge($request, $params), true); + } + + /** + * history Get the complete account trade history for all or a specific currency + * @deprecated + * use myTrades() instead + * + * $BNBHistory = $api->history("BNBBTC"); + * $limitBNBHistory = $api->history("BNBBTC",5); + * $limitBNBHistoryFromId = $api->history("BNBBTC",5,3); + * + * @param $symbol string the currency symbol + * @param $limit int the amount of orders returned + * @param $fromTradeId int (optional) return the orders from this order onwards. negative for all + * @param $startTime int (optional) return the orders from this time onwards. null to ignore + * @param $endTime int (optional) return the orders from this time backwards. null to ignore + * @return array with error message or array of orderDetails array + * @throws \Exception + */ + public function history(string $symbol, int $limit = 500, int $fromTradeId = -1, ?int $startTime = null, ?int $endTime = null, array $params = []) + { + $request = [ + "symbol" => $symbol, + "limit" => $limit, + ]; + if ($fromTradeId > 0) { + $request["fromId"] = $fromTradeId; + } + if (isset($startTime)) { + $request["startTime"] = $startTime; + } + if (isset($endTime)) { + $request["endTime"] = $endTime; + } + + return $this->apiRequest("v3/myTrades", "GET", array_merge($request, $params), true); + } + + /** + * myTrades + * another name for history + * @see history() + * + * @return array with error message or array of orderDetails array + * @throws \Exception + */ + public function myTrades(string $symbol, int $limit = 500, int $fromTradeId = -1, ?int $startTime = null, ?int $endTime = null, array $params = []) + { + return $this->history($symbol, $limit, $fromTradeId, $startTime, $endTime, $params); + } + + /** + * useServerTime adds the 'useServerTime'=>true to the API request to avoid time errors + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints + * + * $api->useServerTime(); + * + * @return null + * @throws \Exception + */ + public function useServerTime(array $params = []) + { + $request = $this->apiRequest("v3/time", "GET", $params); + if (isset($request['serverTime'])) { + $this->info['timeOffset'] = $request['serverTime'] - (microtime(true) * 1000); + } + } + + /** + * time Gets the server time + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints + * + * $time = $api->time(); + * + * @return array with error message or array with server time key + * @throws \Exception + */ + public function time(array $params = []) + { + return $this->apiRequest("v3/time", "GET", $params); + } + + /** + * exchangeInfo - Gets the complete exchange info, including limits, currency options etc. + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints + * + * $info = $api->exchangeInfo(); + * $info = $api->exchangeInfo('BTCUSDT'); + * + * $arr = array('ATABUSD','BTCUSDT'); + * $info = $api->exchangeInfo($arr); + * + * @property int $weight 10 + * + * @param string|array $symbols (optional) A symbol or an array of symbols, default is empty + * + * @return array containing the response + * @throws \Exception + */ + public function exchangeInfo($symbols = null, array $params = []) + { + if (!$this->exchangeInfo) { + $arr = array(); + if ($symbols) { + if (gettype($symbols) == "string") { + $request["symbol"] = $symbols; + $arr = $this->apiRequest("v3/exchangeInfo", "GET", array_merge($request, $params)); + } + if (gettype($symbols) == "array") { + $arr = $this->apiRequest("v3/exchangeInfo?symbols=" . '["' . implode('","', $symbols) . '"]', "GET", $params); + } + } else { + $arr = $this->apiRequest("v3/exchangeInfo", "GET", $params); + } + if ((is_array($arr) === false) || empty($arr)) { + echo "Error: unable to fetch spot exchange info" . PHP_EOL; + $arr = array(); + $arr['symbols'] = array(); + } + $this->exchangeInfo = $arr; + $this->exchangeInfo['symbols'] = null; + + foreach ($arr['symbols'] as $key => $value) { + $this->exchangeInfo['symbols'][$value['symbol']] = $value; + } + } + + return $this->exchangeInfo; + } + + /** + * assetDetail - Fetch details of assets supported on Binance + * + * @link https://binance-docs.github.io/apidocs/spot/en/#asset-detail-user_data + * + * @property int $weight 1 + * + * @param string $asset (optional) Should be an asset, e.g. BNB or empty to get the full list + * + * @return array containing the response + */ + public function assetDetail($asset = '', array $params = []) + { + $request = array(); + if ($asset != '' && gettype($asset) == 'string') + $request['asset'] = $asset; + $arr = $this->sapiRequest("v1/asset/assetDetail", 'GET', array_merge($request, $params), true); + // if asset was set, no backward compatibility needed as this was implemented later + if (isset($params['asset'])) + return $arr; + + // wrap into another array for backward compatibility with the old wapi one + if (!empty($arr['BTC']['withdrawFee'])) { + return array( + 'success' => 1, + 'assetDetail' => $arr, + ); + } else { + return array( + 'success' => 0, + 'assetDetail' => array(), + ); + + } + } + + /** + * userAssetDribbletLog - Log of the conversion of the dust assets to BNB + * @deprecated + */ + public function userAssetDribbletLog() + { + $params["wapi"] = true; + trigger_error('Deprecated - function will disappear on 2021-08-01 from Binance. Please switch to $api->dustLog().', E_USER_DEPRECATED); + return $this->httpRequest("v3/userAssetDribbletLog.html", 'GET', $params, true); + } + + /** + * dustLog - Log of the conversion of the dust assets to BNB + * + * @link https://binance-docs.github.io/apidocs/spot/en/#dustlog-user_data + * + * @property int $weight 1 + * + * @param long $startTime (optional) Start time, e.g. 1617580799000 + * @param long $endTime (optional) End time, e.g. 1617580799000. Endtime is mandatory if startTime is set. + * + * @return array containing the response + * @throws \Exception + */ + public function dustLog($startTime = NULL, $endTime = NULL, array $params = []) + { + $request = array(); + if (!empty($startTime) && !empty($endTime)) { + $request['startTime'] = $startTime; + $request['endTime'] = $endTime; + } + + return $this->sapiRequest("v1/asset/dribblet", 'GET', array_merge($request, $params), true); + } + + /** + * dustTransfer - Convert dust assets ( < 0.001 BTC) to BNB + * + * @link https://binance-docs.github.io/apidocs/spot/en/#dust-transfer-user_data + * + * @property int $weight 1 + * + * @param string|array $assets (mandatory) Asset(s), e.g. IOST or array like ['IOST','AAVE','CHZ'] + * + * @return array containing the response + * @throws \Exception + */ + public function dustTransfer($assets, array $params = []) + { + $request = [ + 'assets' => $assets, + ]; - } - $this->assertEquals("https://api.binance.com/api/v3/ticker/price", self::$capturedUrl); + return $this->sapiRequest("v1/asset/dust", 'POST', array_merge($request, $params), true); } - - public function testSpotOrder() + /** + * Fetch current(daily) trade fee of symbol, values in percentage. + * for more info visit binance official api document + * + * $symbol = "BNBBTC"; or any other symbol or even a set of symbols in an array + * @param string $symbol + * @return mixed + */ + public function tradeFee(string $symbol, array $params = []) { - try { - $this->binance->order('BUY', 'BTCUSDT', 1, 1000); - } catch(\Throwable $e) { + $request = [ + "symbol" => $symbol, + ]; + return $this->sapiRequest("v1/asset/tradeFee", 'GET', array_merge($request, $params), true); + } + + /** + * commissionFee - Fetch commission trade fee + * + * @link https://binance-docs.github.io/apidocs/spot/en/#trade-fee-user_data + * + * @property int $weight 1 + * + * @param string $symbol (optional) Should be a symbol, e.g. BNBUSDT or empty to get the full list + * + * @return array containing the response + * @throws \Exception + */ + public function commissionFee($symbol = '', array $params = []) + { + $request = array(); + if ($symbol != '' && gettype($symbol) == 'string') + $request['symbol'] = $symbol; + + return $this->sapiRequest("v1/asset/tradeFee", 'GET', array_merge($request, $params), true); + } + + /** + * withdraw - Submit a withdraw request to move an asset to another wallet + * + * @link https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi + * + * @example https://github.com/jaggedsoft/php-binance-api#withdraw Standard withdraw + * @example https://github.com/jaggedsoft/php-binance-api#withdraw-with-addresstag Withdraw with addressTag for e.g. XRP + * + * @property int $weight 1 + * + * @param string $asset (mandatory) An asset, e.g. BTC + * @param string $address (mandatory) The address where to send, e.g. 1C5gqLRs96Xq4V2ZZAR1347yUCpHie7sa or 44tLjmXrQNrWJ5NBsEj2R77ZBEgDa3fEe9GLpSf2FRmhexPvfYDUAB7EXX1Hdb3aMQ9FLqdJ56yaAhiXoRsceGJCRS3Jxkn + * @param string $amount (mandatory) The amount, e.g. 0.2 + * @param string $addressTag (optional) Mandatory secondary address for some assets (XRP,XMR,etc), e.g. 0e5e38a01058dbf64e53a4333a5acf98e0d5feb8e523d32e3186c664a9c762c1 + * @param string $addressName (optional) Description of the address + * @param string $transactionFeeFlag (optional) When making internal transfer, true for returning the fee to the destination account; false for returning the fee back to the departure account. + * @param string $network (optional) + * @param string $orderId (optional) Client id for withdraw + * + * @return array containing the response + * @throws \Exception + */ + public function withdraw(string $asset, string $address, $amount, $addressTag = null, $addressName = "", bool $transactionFeeFlag = false, $network = null, $orderId = null, array $params = []) + { + $request = [ + "coin" => $asset, + "address" => $address, + "amount" => $amount, + "sapi" => true, + ]; + if (is_null($addressName) === false && empty($addressName) === false) { + $request['name'] = str_replace(' ', '%20', $addressName); } - $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); + if (is_null($addressTag) === false && empty($addressTag) === false) { + $request['addressTag'] = $addressTag; + } + if ($transactionFeeFlag) $request['transactionFeeFlag'] = true; - $this->assertEquals("BTCUSDT", $params['symbol']); - $this->assertEquals("BUY", $params['side']); - $this->assertEquals("LIMIT", $params['type']); - $this->assertEquals(1, $params['quantity']); - $this->assertEquals(1000, $params['price']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + if (is_null($network) === false && empty($network) === false) { + $request['network'] = $network; + } + if (is_null($orderId) === false && empty($orderId) === false) { + $request['withdrawOrderId'] = $orderId; + } + return $this->sapiRequest("v1/capital/withdraw/apply", "POST", array_merge($request, $params), true); } - public function testSpotSorOrder() + /** + * depositAddress - Get the deposit address for an asset + * + * @link https://binance-docs.github.io/apidocs/spot/en/#deposit-address-supporting-network-user_data + * + * @property int $weight 1 + * + * @param string $asset (mandatory) An asset, e.g. BTC + * @param string $network (optional) You can get network in networkList from /sapi/v1/capital/config/getall + * + * @return array containing the response + * @throws \Exception + */ + public function depositAddress(string $asset, $network = null, array $params = []) { - try { - $this->binance->sorOrder('BUY', 'BTCUSDT', 1, 1000); - } catch(\Throwable $e) { - + $request = [ + "coin" => $asset, + ]; + if (is_null($network) === false && empty($network) === false) { + $request['network'] = $network; + } + + $return = $this->sapiRequest("v1/capital/deposit/address", "GET", array_merge($request, $params), true); + + // Adding for backwards compatibility with wapi + if (is_array($return) && !empty($return)) { + $return['asset'] = $return['coin']; + $return['addressTag'] = $return['tag']; + if (!empty($return['address'])) { + $return['success'] = 1; + } else { + $return['success'] = 0; + } + } else { + echo "Error: no deposit address found" . PHP_EOL; + } + + return $return; + } + + /** + * depositHistory - Get the deposit history for one or all assets + * + * @link https://binance-docs.github.io/apidocs/spot/en/#deposit-history-supporting-network-user_data + * + * @property int $weight 1 + * + * @param string $asset (optional) An asset, e.g. BTC - or leave empty for all + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * + * @return array containing the response + * @throws \Exception + */ + public function depositHistory(?string $asset = null, array $params = []) + { + $request = array(); + if (is_null($asset) === false) { + $request['coin'] = $asset; + } + $return = $this->sapiRequest("v1/capital/deposit/hisrec", "GET", array_merge($request, $params), true); + + // Adding for backwards compatibility with wapi + if (is_array($return) && !empty($return)) { + foreach ($return as $key=>$item) { + $return[$key]['asset'] = $item['coin']; + } + } else { + echo "Error: no deposit history found" . PHP_EOL; + } + + return $return; + + } + + /** + * withdrawHistory - Get the withdraw history for one or all assets + * + * @link https://binance-docs.github.io/apidocs/spot/en/#withdraw-history-supporting-network-user_data + * + * @property int $weight 1 + * + * @param string $asset (optional) An asset, e.g. BTC - or leave empty for all + * @param array $params (optional) An array of additional parameters that the API endpoint allows: status, offset, limit, startTime, endTime + * + * @return array containing the response + * @throws \Exception + */ + public function withdrawHistory(?string $asset = null, array $params = []) + { + $request = array(); + if (is_null($asset) === false) { + $request['coin'] = $asset; + } + // Wrapping in array for backwards compatibility with wapi + $return = array( + 'withdrawList' => $this->sapiRequest("v1/capital/withdraw/history", "GET", array_merge($request, $params), true) + ); + + // Adding for backwards compatibility with wapi + $return['success'] = 1; + + return $return; + } + + /** + * withdrawFee - Get the withdrawal fee for an asset + * + * @property int $weight 1 + * + * @param string $asset (mandatory) An asset, e.g. BTC + * + * @return array containing the response + * @throws \Exception + */ + public function withdrawFee(string $asset, array $params = []) + { + $return = $this->assetDetail('', $params); + + if (isset($return['success'], $return['assetDetail'], $return['assetDetail'][$asset]) && $return['success']) { + return $return['assetDetail'][$asset]; + } else { + return array(); + } + } + + /** + * transfer - Transfer asset between accounts + * possible types of transfer are: + * - MAIN_UMFUTURE - Spot account transfer to USDⓈ-M Futures account + * - MAIN_CMFUTURE - Spot account transfer to COIN-M Futures account + * - MAIN_MARGIN - Spot account transfer to Margin(cross)account + * - UMFUTURE_MAIN - USDⓈ-M Futures account transfer to Spot account + * - UMFUTURE_MARGIN - USDⓈ-M Futures account transfer to Margin(cross)account + * - CMFUTURE_MAIN - COIN-M Futures account transfer to Spot account + * - CMFUTURE_MARGIN - COIN-M Futures account transfer to Margin(cross) account + * - MARGIN_MAIN - Margin(cross)account transfer to Spot account + * - MARGIN_UMFUTURE - Margin(cross)account transfer to USDⓈ-M Futures + * - MARGIN_CMFUTURE - Margin(cross)account transfer to COIN-M Futures + * - ISOLATEDMARGIN_MARGIN - Isolated margin account transfer to Margin(cross) account + * - MARGIN_ISOLATEDMARGIN - Margin(cross) account transfer to Isolated margin account + * - ISOLATEDMARGIN_ISOLATEDMARGIN - Isolated margin account transfer to Isolated margin account + * - MAIN_FUNDING - Spot account transfer to Funding account + * - FUNDING_MAIN - Funding account transfer to Spot account + * - FUNDING_UMFUTURE - Funding account transfer to UMFUTURE account + * - UMFUTURE_FUNDING - UMFUTURE account transfer to Funding account + * - MARGIN_FUNDING - MARGIN account transfer to Funding account + * - FUNDING_MARGIN - Funding account transfer to Margin account + * - FUNDING_CMFUTURE - Funding account transfer to CMFUTURE account + * - CMFUTURE_FUNDING - CMFUTURE account transfer to Funding account + * - MAIN_OPTION - Spot account transfer to Options account + * - OPTION_MAIN - Options account transfer to Spot account + * - UMFUTURE_OPTION - USDⓈ-M Futures account transfer to Options account + * - OPTION_UMFUTURE - Options account transfer to USDⓈ-M Futures account + * - MARGIN_OPTION - Margin(cross)account transfer to Options account + * - OPTION_MARGIN - Options account transfer to Margin(cross)account + * - FUNDING_OPTION - Funding account transfer to Options account + * - OPTION_FUNDING - Options account transfer to Funding account + * - MAIN_PORTFOLIO_MARGIN - Spot account transfer to Portfolio Margin account + * - PORTFOLIO_MARGIN_MAIN - Portfolio Margin account transfer to Spot account + * + * @link https://developers.binance.com/docs/wallet/asset/user-universal-transfer + * + * @property int $weight 900 + * + * @param string $type (mandatory) type of transfer, e.g. MAIN_MARGIN + * @param string $asset (mandatory) an asset, e.g. BTC + * @param string $amount (mandatory) the amount to transfer + * @param string $fromSymbol (optional) must be sent when type are ISOLATEDMARGIN_MARGIN and ISOLATEDMARGIN_ISOLATEDMARGIN + * @param string $toSymbol (optional) must be sent when type are MARGIN_ISOLATEDMARGIN and ISOLATEDMARGIN_ISOLATEDMARGIN + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function transfer(string $type, string $asset, string $amount, $fromSymbol = null, $toSymbol = null, array $params = []) + { + $request = [ + 'type' => $type, + 'asset' => $asset, + 'amount' => $amount, + ]; + // todo: check this method with real account + if ($fromSymbol) { + $request['fromSymbol'] = $fromSymbol; + } + if ($toSymbol) { + $request['toSymbol'] = $toSymbol; + } + + + return $this->sapiRequest("v1/asset/transfer", 'POST', array_merge($request, $params), true); + } + + /** + * transfersHistory - get the transfer history between accounts + * + * @link https://developers.binance.com/docs/wallet/asset/query-user-universal-transfer + * + * @property int $weight 1 + * + * @param string $type (mandatory) type of transfer, e.g. MAIN_MARGIN (@see transfer()) + * @param string $startTime (optional) start time in milliseconds + * @param string $endTime (optional) end time in milliseconds + * @param int $limit (optional) the number of records to return (default 10, max 100) + * @param int $current (optional) default 1 + * @param string $fromSymbol (optional) must be sent when type are ISOLATEDMARGIN_MARGIN and ISOLATEDMARGIN_ISOLATEDMARGIN + * @param string $toSymbol (optional) must be sent when type are MARGIN_ISOLATEDMARGIN and ISOLATEDMARGIN_ISOLATEDMARGIN + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function transfersHistory(string $type, $startTime = null, $endTime = null, $limit = null, $current = null, $fromSymbol = null, $toSymbol = null, array $params = []) + { + $request = [ + 'type' => $type, + ]; + // todo: check this method with real account + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + if ($limit) { + $request['size'] = $limit; + } + if ($current) { + $request['current'] = $current; + } + if ($fromSymbol) { + $request['fromSymbol'] = $fromSymbol; + } + if ($toSymbol) { + $request['toSymbol'] = $toSymbol; } - $this->assertEquals("https://api.binance.com/api/v3/sor/order", self::$capturedUrl); - parse_str(self::$capturedBody, $params); - $this->assertEquals("BTCUSDT", $params['symbol']); - $this->assertEquals("BUY", $params['side']); - $this->assertEquals("LIMIT", $params['type']); - $this->assertEquals(1, $params['quantity']); - $this->assertEquals(1000, $params['price']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + return $this->sapiRequest("v1/asset/transfer", 'GET', array_merge($request, $params), true); } - public function testSpotReplaceOrder() + /** + * prices get all the current prices + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#symbol-price-ticker + * + * $ticker = $api->prices(); + * + * @return array with error message or array of all the currencies prices + * @throws \Exception + */ + public function prices(array $params = []) { - try { - $this->binance->replaceOrder('BUY', 'BTCUSDT', 1, 1000, 'LIMIT', '123456789'); - } catch(\Throwable $e) { - - } - $this->assertEquals("https://api.binance.com/api/v3/order/cancelReplace", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals("BTCUSDT", $params['symbol']); - $this->assertEquals("BUY", $params['side']); - $this->assertEquals("LIMIT", $params['type']); - $this->assertEquals(1, $params['quantity']); - $this->assertEquals(1000, $params['price']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertEquals('123456789', $params['cancelOrderId']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + return $this->priceData($this->apiRequest("v3/ticker/price", "GET", $params)); } - public function testSpotEditOrder() + /** + * price get the latest price of a symbol + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#symbol-price-ticker + * + * $price = $api->price( "ETHBTC" ); + * + * @return array with error message or array with symbol price + * @throws \Exception + */ + public function price(string $symbol, array $params = []) { - try { - $this->binance->editOrder('ETHUSDT', 0.009, '123456789'); - } catch(\Throwable $e) { + $request = [ + "symbol" => $symbol, + ]; + $ticker = $this->apiRequest("v3/ticker/price", "GET", array_merge($request, $params)); + if (!isset($ticker['price'])) { + echo "Error: unable to fetch price for $symbol" . PHP_EOL; + return null; + } + return $ticker['price']; + } + + /** + * bookPrices get all bid/asks prices + * + * $ticker = $api->bookPrices(); + * + * @return array with error message or array of all the book prices + * @throws \Exception + */ + public function bookPrices(array $params = []) + { + return $this->bookPriceData($this->apiRequest("v3/ticker/bookTicker", "GET", $params)); + } + + /** + * account get all information about the api account + * + * $account = $api->account(); + * + * @return array with error message or array of all the account information + * @throws \Exception + */ + public function account(array $params = []) + { + return $this->apiRequest("v3/account", "GET", $params, true); + } + + /** + * prevDay get 24hr ticker price change statistics for symbols + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints + * + * $prevDay = $api->prevDay("BNBBTC"); + * + * @param string $symbol (optional) symbol to get the previous day change for + * @param array $params (optional) an array of additional parameters that the API endpoint allows + * - @param array $params['symbols'] (optional) the symbols to get the previous day change for (e.g. ["BNBBTC","ETHBTC"]) + * @return array with error message or array of prevDay change + * @throws \Exception + */ + public function prevDay(?string $symbol = null, array $params = []) + { + $request = []; + if (is_null($symbol) === false) { + $request = [ + 'symbol' => $symbol, + ]; + } + $symbols = []; + if (isset($params['symbols'])) { + $symbols = $params['symbols']; + unset($params['symbols']); + } + if (!empty($symbols)) { + $request['symbols'] = json_encode($symbols); + } + return $this->apiRequest("v3/ticker/24hr", "GET", array_merge($request, $params)); + } + + /** + * tradingDay get 24hr ticker price change statistics for symbols + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints + * + * $tradingDay = $api->tradingDay("BNBBTC"); + * $tradingDay = $api->tradingDay(null, ["BNBBTC","ETHBTC"]); + * + * @param string $symbol (optional) symbol to get the info for - mandatory if symbols is not set + * @param array $symbols (optional) the symbols to get the info for (e.g. ["BNBBTC","ETHBTC"]) - mandatory if symbol is not set + * @param array $params (optional) an array of additional parameters that the API endpoint allows + * - @param string $params['timeZone'] (optional) default is 0 (UTC) - time zone to use for the response + * - @param string $params['type'] (optional) FULL or MINI - the format of the response (default is FULL) + * @return array with error message or array of prevDay change + * @throws \Exception + */ + public function tradingDay(?string $symbol = null, ?array $symbols = null, array $params = []) + { + $request = []; + if (!is_null($symbol) && is_string($symbol)) { + $request['symbol'] = $symbol; + } elseif (is_array($symbols) && !empty($symbols)) { + $request['symbols'] = json_encode($symbols); + } else { + throw new \Exception("tradingDay(): Either symbol or symbols must be set"); + } + return $this->apiRequest("v3/ticker/tradingDay", "GET", array_merge($request, $params)); + } + + /** + * rollingWindowPriceChange get ticker price change statistics for symbols for a rolling window + * - 1m, 2m... 59m for minutes + * - 1h, 2h... 23h - for hours + * - 1d.. 7d - for days + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints + * + * $priceChange = $api->rollingWindowPriceChange("BNBBTC"); + * $priceChange = $api->rollingWindowPriceChange(null, ["BNBBTC","ETHBTC"], '2d'); + * + * @param string $symbol (optional) symbol to get the info for - mandatory if symbols is not set + * @param array $symbols (optional) the symbols to get the info for (e.g. ["BNBBTC","ETHBTC"]) - mandatory if symbol is not set + * @param string $windowSize (optional) the size of the rolling window (e.g. 1m, 2m, 1h, 2h, 1d, 2d) - default is 1d + * @param array $params (optional) an array of additional parameters that the API endpoint allows + * - @param string $params['type'] (optional) FULL or MINI - the format of the response (default is FULL) + * @return array with error message or array of prevDay change + * @throws \Exception + */ + public function rollingWindowPriceChange(?string $symbol = null, array $symbols = null, string $windowSize = null, array $params = []) + { + $request = []; + if (!is_null($symbol) && is_string($symbol)) { + $request['symbol'] = $symbol; + } elseif (is_array($symbols) && !empty($symbols)) { + $request['symbols'] = json_encode($symbols); + } else { + throw new \Exception("rollingWindowPriceChange(): Either symbol or symbols must be set"); + } + if (!is_null($windowSize)) { + $request['windowSize'] = $windowSize; + } + return $this->apiRequest("v3/ticker", "GET", array_merge($request, $params)); + } + + /** + * aggTrades get Market History / Aggregate Trades + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints + * + * $trades = $api->aggTrades("BNBBTC"); + * + * @param $symbol string the symbol to get the trade information for + * @return array with error message or array of market history + * @throws \Exception + */ + public function aggTrades(string $symbol, array $params = []) + { + $request = [ + "symbol" => $symbol, + ]; + return $this->tradesData($this->apiRequest("v3/aggTrades", "GET", array_merge($request, $params))); + } + + /** + * historicalTrades - Get historical trades for a specific currency + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints + * + * @property int $weight 5 + * Standard weight is 5 but if no tradeId is given, weight is 1 + * + * @param string $symbol (mandatory) to query, e.g. BNBBTC + * @param int $limit (optional) limit the amount of trades, default 500, max 1000 + * @param int $tradeId (optional) return the orders from this orderId onwards, negative to get recent ones + * + * @return array containing the response + * @throws \Exception + */ + public function historicalTrades(string $symbol, int $limit = 500, int $tradeId = -1, array $params = []) + { + $request = [ + "symbol" => $symbol, + "limit" => $limit, + ]; + if ($tradeId > 0) { + $request["fromId"] = $tradeId; + } else { + // if there is no tradeId given, we can use v3/trades, weight is 1 and not 5 + return $this->apiRequest("v3/trades", "GET", array_merge($request, $params)); + } + + // The endpoint cannot handle extra parameters like 'timestamp' or 'signature', + // but it needs the http header with the key so we need to construct it here + $query = http_build_query(array_merge($request, $params), '', '&'); + return $this->apiRequest("v3/historicalTrades?$query"); + } + + /** + * depth get Market depth + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints + * + * $depth = $api->depth("ETHBTC"); + * + * @param $symbol string the symbol to get the depth information for + * @param $limit int set limition for number of market depth data + * @return array with error message or array of market depth + * @throws \Exception + */ + public function depth(string $symbol, int $limit = 100, array $params = []) + { + if (is_int($limit) === false) { + $limit = 100; + } + + if (isset($symbol) === false || is_string($symbol) === false) { + // WPCS: XSS OK. + echo "asset: expected bool false, " . gettype($symbol) . " given" . PHP_EOL; + } + $request = [ + "symbol" => $symbol, + "limit" => $limit, + ]; + $json = $this->apiRequest("v3/depth", "GET", array_merge($request, $params)); + if (is_array($json) === false) { + echo "Error: unable to fetch depth" . PHP_EOL; + $json = []; + } + if (empty($json)) { + echo "Error: depth were empty" . PHP_EOL; + return $json; + } + if (isset($this->info[$symbol]) === false) { + $this->info[$symbol] = []; + } + $this->info[$symbol]['firstUpdate'] = $json['lastUpdateId']; + return $this->depthData($symbol, $json); + } + + /** + * balances get balances for the account assets + * + * $balances = $api->balances(); + * + * @param string $market_type (optional) market type - "spot" or "futures" (default is "spot") + * @param array $params (optional) an array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the transfer to complete (not for spot) + * @param string $api_version (optional) not for spot - the api version to use (default is v2) + * + * @return array with error message or array of balances + * @throws \Exception + */ + public function balances(string $market_type = 'spot', array $params = [], string $api_version = 'v2') + { + $is_spot = $market_type === 'spot'; + $request = []; + if ($is_spot) { + $url = "v3/account"; + } else { + $request['fapi'] = true; + if ($api_version === 'v2') { + $url = "v2/balance"; + } else if ($api_version === 'v3') { + $url = "v3/balance"; + } else { + throw new \Exception("balances(): Invalid API version specified. Use 'v2' or 'v3'."); + } + } + $response = $this->httpRequest($url, "GET", array_merge($request, $params), true); + if (is_array($response) === false) { + echo "Error: unable to fetch your account details" . PHP_EOL; + } + if (empty($response) || ($is_spot && (isset($response['balances']) === false || empty($response['balances'])))) { + echo "Error: your balances were empty or unset" . PHP_EOL; + return []; + } + return $this->balanceData($response, $market_type); + } + + /** + * coins get list coins + * + * $coins = $api->coins(); + * @return array with error message or array containing coins + * @throws \Exception + */ + public function coins(array $params = []) + { + return $this->sapiRequest("v1/capital/config/getall", 'GET', $params, true); + } + + /** + * getProxyUriString get Uniform Resource Identifier string assocaited with proxy config + * + * $balances = $api->getProxyUriString(); + * + * @return string uri + */ + public function getProxyUriString() + { + $uri = isset($this->proxyConf['proto']) ? $this->proxyConf['proto'] : "http"; + // https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html + $supportedProtocols = array( + 'http', + 'https', + 'socks4', + 'socks4a', + 'socks5', + 'socks5h', + ); + + if (in_array($uri, $supportedProtocols) === false) { + // WPCS: XSS OK. + echo "Unknown proxy protocol '" . $this->proxyConf['proto'] . "', supported protocols are " . implode(", ", $supportedProtocols) . PHP_EOL; + } + + $uri .= "://"; + $uri .= isset($this->proxyConf['address']) ? $this->proxyConf['address'] : "localhost"; + + if (isset($this->proxyConf['address']) === false) { + // WPCS: XSS OK. + echo "warning: proxy address not set defaulting to localhost" . PHP_EOL; + } + + $uri .= ":"; + $uri .= isset($this->proxyConf['port']) ? $this->proxyConf['port'] : "1080"; + if (isset($this->proxyConf['address']) === false) { + // WPCS: XSS OK. + echo "warning: proxy port not set defaulting to 1080" . PHP_EOL; } - $endpoint = "https://api.binance.com/api/v3/order/amend/keepPriority?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals("ETHUSDT", $params['symbol']); - $this->assertEquals(0.009, $params['newQty']); - $this->assertEquals('123456789', $params['orderId']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); - } + return $uri; + } - public function testSpotBuy() + /** + * setProxy set proxy config by passing in an array of the proxy configuration + * + * $proxyConf = [ + * 'proto' => 'tcp', + * 'address' => '192.168.1.1', + * 'port' => '8080', + * 'user' => 'dude', + * 'pass' => 'd00d' + * ]; + * + * $api->setProxy( $proxyconf ); + * + * @return null + */ + public function setProxy(array $proxyconf) { - try { - $this->binance->buy($this->symbol, $this->quantity, $this->price, $this->type); + $this->proxyConf = $proxyconf; + } - } catch (\Throwable $e) { - } - $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); + protected function curl_exec($curl) + { + return curl_exec($curl); + } - parse_str(self::$capturedBody, $params); + protected function curl_set_url($curl, $endpoint) { + curl_setopt($curl, CURLOPT_URL, $endpoint); + } - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertEquals($this->price, $params['price']); - $this->assertEquals($this->type, $params['type']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + protected function curl_set_body($curl, $option, $query) { + curl_setopt($curl, $option, $query); } - public function testSpotBuyTest() + public function apiRequest($url, $method = "GET", $params = [], $signed = false) { - try { - $this->binance->buyTest($this->symbol, $this->quantity, $this->price, $this->type); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertEquals($this->price, $params['price']); - $this->assertEquals($this->type, $params['type']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + return $this->httpRequest($url, $method, $params, $signed); } - public function testSpotSell() + public function sapiRequest($url, $method = "GET", $params = [], $signed = false) { - try { - $this->binance->sell($this->symbol, $this->quantity, $this->price, $this->type); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertEquals($this->price, $params['price']); - $this->assertEquals($this->type, $params['type']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + $params['sapi'] = true; + return $this->httpRequest($url, $method, $params, $signed); } - public function testSpotSellTest() + public function fapiRequest($url, $method = "GET", $params = [], $signed = false) { - try { - $this->binance->sellTest($this->symbol, $this->quantity, $this->price, $this->type); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertEquals($this->price, $params['price']); - $this->assertEquals($this->type, $params['type']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + $params['fapi'] = true; + return $this->httpRequest($url, $method, $params, $signed); } - public function testSpotMarketQuoteBuy() + public function fapiDataRequest($url, $method = "GET", $params = [], $signed = false) { - try { - $this->binance->marketQuoteBuy($this->symbol, $this->quantity); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quoteOrderQty']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + $params['fapiData'] = true; + return $this->httpRequest($url, $method, $params, $signed); } - public function testSpotMarketQuoteBuyTest() + /** + * httpRequest curl wrapper for all http api requests. + * You can't call this function directly, use the helper functions + * + * @see buy() + * @see sell() + * @see marketBuy() + * @see marketSell() $this->httpRequest( "https://api.binance.com/api/v1/ticker/24hr"); + * + * @param $url string the endpoint to query, typically includes query string + * @param $method string this should be typically GET, POST or DELETE + * @param $params array addtional options for the request + * @param $signed bool true or false sign the request with api secret + * @return array containing the response + * @throws \Exception + */ + protected function httpRequest(string $url, string $method = "GET", array $params = [], bool $signed = false) { - try { - $this->binance->marketQuoteBuyTest($this->symbol, $this->quantity); - - } catch (\Throwable $e) { - + if (function_exists('curl_init') === false) { + throw new \Exception("Sorry cURL is not installed!"); } - $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); - parse_str(self::$capturedBody, $params); + if ($this->caOverride === false) { + if (file_exists(getcwd() . '/ca.pem') === false) { + $this->downloadCurlCaBundle(); + } + } + if ((!isset ($params['recvWindow'])) && (!is_null($this->recvWindow))) { + $params['recvWindow'] = $this->recvWindow; + } - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quoteOrderQty']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); - } + $base = $this->base; + if ($this->useTestnet) { + $base = $this->baseTestnet; + } - public function testSpotMarketBuy() - { - try { - $this->binance->marketBuy($this->symbol, $this->quantity); + if (isset($params['wapi'])) { + if ($this->useTestnet) { + throw new \Exception("wapi endpoints are not available in testnet"); + } + unset($params['wapi']); + $base = $this->wapi; + } - } catch (\Throwable $e) { + if (isset($params['sapi'])) { + if ($this->useTestnet) { + throw new \Exception("sapi endpoints are not available in testnet"); + } + unset($params['sapi']); + $base = $this->sapi; + } + if (isset($params['fapi'])) { + unset($params['fapi']); + $base = $this->useTestnet ? $this->fapiTestnet : $this->fapi; } - $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); - parse_str(self::$capturedBody, $params); + if (isset($params['fapiData'])) { + if ($this->useTestnet) { + throw new \Exception("fapiData endpoints are not available in testnet"); + } + unset($params['fapiData']); + $base = $this->fapiData; + } - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); - } + if (isset($params['dapi'])) { + unset($params['dapi']); + $base = $this->useTestnet ? $this->dapiTestnet : $this->dapi; + } - public function testSpotMarketBuyTest() - { - try { - $this->binance->marketBuyTest($this->symbol, $this->quantity); + if (isset($params['dapiData'])) { + if ($this->useTestnet) { + throw new \Exception("dapiData endpoints are not available in testnet"); + } + unset($params['dapiData']); + $base = $this->dapiData; + } - } catch (\Throwable $e) { + if (isset($params['papi'])) { + if ($this->useTestnet) { + throw new \Exception("papi endpoints are not available in testnet"); + } + unset($params['papi']); + $base = $this->papi; + } + if (isset($params['bapi'])) { + if ($this->useTestnet) { + throw new \Exception("bapi endpoints are not available in testnet"); + } + unset($params['bapi']); + $base = $this->bapi; } - $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); - parse_str(self::$capturedBody, $params); + $curl = curl_init(); + curl_setopt($curl, CURLOPT_VERBOSE, $this->httpDebug); + + //set custom headers if any + curl_setopt_array($curl, [ + CURLOPT_HTTPHEADER => $this->headers, + // Optional: other cURL options + ]); + + $query = $this->binance_build_query($params); + + // signed with params + if ($signed === true) { + if (empty($this->api_key)) { + throw new \Exception("signedRequest error: API Key not set!"); + } + + if (empty($this->api_secret)) { + throw new \Exception("signedRequest error: API Secret not set!"); + } - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); - } + $ts = (microtime(true) * 1000) + $this->info['timeOffset']; + $params['timestamp'] = number_format($ts, 0, '.', ''); + $query = $this->binance_build_query($params); + $query = str_replace([ '%40' ], [ '@' ], $query);//if send data type "e-mail" then binance return: [Signature for this request is not valid.] + $signature = hash_hmac('sha256', $query, $this->api_secret); + if ($method === "POST") { + $endpoint = $base . $url; + $params['signature'] = $signature; // signature needs to be inside BODY + $query = $this->binance_build_query($params); // rebuilding query + } else { + $endpoint = $base . $url . '?' . $query . '&signature=' . $signature; + } - public function testSpotMarketQuoteSell() - { - try { - $this->binance->marketQuoteSell($this->symbol, '1'); + $this->curl_set_url($curl, $endpoint); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'X-MBX-APIKEY: ' . $this->api_key, + )); + } + // params so buildquery string and append to url + elseif (count($params) > 0) { + $this->curl_set_url($curl, $base . $url . '?' . $query); + } + // no params so just the base url + else { + $this->curl_set_url($curl, $base . $url); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'X-MBX-APIKEY: ' . $this->api_key, + )); + } + curl_setopt($curl, CURLOPT_USERAGENT, "User-Agent: Mozilla/4.0 (compatible; PHP Binance API)"); + // Post and postfields + if ($method === "POST") { + curl_setopt($curl, CURLOPT_POST, true); + $this->curl_set_body($curl, CURLOPT_POSTFIELDS, $query); + } + // Delete Method + if ($method === "DELETE") { + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); + } + + // PUT Method + if ($method === "PUT") { + curl_setopt($curl, CURLOPT_PUT, true); + } + + // proxy settings + if (is_array($this->proxyConf)) { + curl_setopt($curl, CURLOPT_PROXY, $this->getProxyUriString()); + if (isset($this->proxyConf['user']) && isset($this->proxyConf['pass'])) { + curl_setopt($curl, CURLOPT_PROXYUSERPWD, $this->proxyConf['user'] . ':' . $this->proxyConf['pass']); + } + } + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_HEADER, true); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_TIMEOUT, 60); + + // set user defined curl opts last for overriding + foreach ($this->curlOpts as $key => $value) { + curl_setopt($curl, constant($key), $value); + } + + if ($this->caOverride === false) { + if (file_exists(getcwd() . '/ca.pem') === false) { + $this->downloadCurlCaBundle(); + } + } + + $output = $this->curl_exec($curl); + // Check if any error occurred + // if (curl_errno($curl) > 0) { + // // should always output error, not only on httpdebug + // // not outputing errors, hides it from users and ends up with tickets on github + // throw new \Exception('Curl error: ' . curl_error($curl)); + // } + + $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE); + $header = $this->get_headers_from_curl_response($output); + $output = substr($output, $header_size); + + curl_close($curl); + + $json = json_decode($output, true); + + $this->lastRequest = [ + 'url' => $url, + 'method' => $method, + 'params' => $params, + 'header' => $header, + 'json' => $json + ]; - } catch (\Throwable $e) { + if (isset($header['x-mbx-used-weight'])) { + $this->setXMbxUsedWeight($header['x-mbx-used-weight']); + } + + if (isset($header['x-mbx-used-weight-1m'])) { + $this->setXMbxUsedWeight1m($header['x-mbx-used-weight-1m']); + } + if (isset($json['msg']) && !empty($json['msg'])) { + if ($json['msg'] !== 'success' && $url != 'v1/system/status' && $url != 'v3/systemStatus.html' && $url != 'v3/accountStatus.html' && $url != 'v1/allOpenOrders') { + // should always output error, not only on httpdebug + // not outputing errors, hides it from users and ends up with tickets on github + throw new \Exception('signedRequest error: '.print_r($output, true)); + } + } + $this->transfered += strlen($output); + $this->requestCount++; + return $json; + } + + /** + * binance_build_query - Wrapper for http_build_query to allow arrays as parameters + * + * sapi v1/asset/dust can have an array, so it needs a conversion + * + * @param array $params (mandatory) Parameters to convert to http query + * + * @return array containing the response + * @throws \Exception + */ + protected function binance_build_query($params = []) + { + $new_arr = array(); + $query_add = ''; + foreach ($params as $label=>$item) { + if ( gettype($item) == 'array' ) { + foreach ($item as $arritem) { + $query_add = $label . '=' . $arritem . '&' . $query_add; + } + } else { + $new_arr[$label] = $item; + } + } + $query = http_build_query($new_arr, '', '&'); + $query = $query_add . $query; + + return $query; + } + + /** + * Converts the output of the CURL header to an array + * + * @param $header string containing the response + * @return array headers converted to an array + */ + public function get_headers_from_curl_response(string $header) + { + $headers = array(); + $header_text = substr($header, 0, strpos($header, "\r\n\r\n")); + + foreach (explode("\r\n", $header_text) as $i => $line) + if ($i === 0) + $headers['http_code'] = $line; + else { + list ($key, $value) = explode(': ', $line); + $headers[$key] = $value; + } + + return $headers; + } + + /** + * order formats the orders before sending them to the curl wrapper function + * You can call this function directly or use the helper functions + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#sor + * + * @see buy() + * @see sell() + * @see marketBuy() + * @see marketSell() $this->httpRequest( "https://api.binance.com/api/v1/ticker/24hr"); + * + * @param string $side (mandatory) typically "BUY" or "SELL" + * @param string $symbol (mandatory) to buy or sell + * @param string $quantity (mandatory) in the order + * @param string $price (optional) for the order + * @param string $type (optional) is determined by the symbol bu typicall LIMIT, STOP_LOSS_LIMIT etc. (default is LIMIT) + * @param array $params (optional) additional transaction options + * - @param string $params['timeInForce'] - GTC, IOC, FOK + * - @param bool $params['isQuoteOrder'] - if true, quantity is in the quote asset (not for sor orders) + * - @param string $params['newClientOrderId'] - custom client order id + * - @param string $params['strategyId'] + * - @param int $params['strategyType'] + * - @param string $params['stopPrice'] - stop price for STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders (not for SOR orders) + * - @param string $params['trailingDelta'] - for STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders (not for SOR orders) + * - @param string $params['icebergQty'] - used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order + * - @param string $params['newOrderRespType'] - set the response type of the order (ACK, RESULT, FULL) + * - @param string $params['selfTradePreventionMode'] - NONE, EXPIRE_MAKER, EXPIRE_TAKER, EXPIRE_BOTH or DECREMENT (default is NONE) + * - @param bool $params['sor'] - if true, order will be sent to SOR endpoint + * - @param bool $params + * @param $test bool whether to test or not, test only validates the query + * @return array containing the response + * @throws \Exception + */ + public function order(string $side, string $symbol, $quantity, $price, string $type = "LIMIT", array $params = [], bool $test = false) + { + $request = $this->createSpotOrderRequest($side, $symbol, $quantity, $price, $type, $params); + $sor = false; + if (isset($request['sor'])) { + $sor = $request['sor']; + unset($request['sor']); + } + $url = $sor ? "v3/sor/order" : "v3/order"; + if ($test) { + $url = $url . "/test"; + } + return $this->apiRequest($url, "POST", $request, true); // sending only $request cuz $params are already added to $request inside createSpotOrderRequest + } + + /** + * createSpotOrderRequest + * helper function to create a request for the spot order + */ + protected function createSpotOrderRequest(string $side, string $symbol, $quantity, $price, string $type = "LIMIT", array $params = []) + { + $request = [ + "symbol" => $symbol, + "side" => $side, + "type" => $type, + "quantity" => $quantity, + ]; + // someone has preformated there 8 decimal point double already + // dont do anything, leave them do whatever they want + if (gettype($price) !== "string") { + // for every other type, lets format it appropriately + $price = number_format($price, 8, '.', ''); } - // warns here cuz method needs information fetched by exchangeInfo - $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals('SELL', $params['side']); - $this->assertEquals('MARKET', $params['type']); - $this->assertEquals(1, $params['quoteOrderQty']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); - } - - public function testSpotMarketQuoteSellTest() - { - try { - $this->binance->marketQuoteSellTest($this->symbol, 1); - - } catch (\Throwable $e) { + if (is_numeric($quantity) === false) { + // WPCS: XSS OK. + echo "warning: quantity expected numeric got " . gettype($quantity) . PHP_EOL; } - $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); - parse_str(self::$capturedBody, $params); + if (is_string($price) === false) { + // WPCS: XSS OK. + echo "warning: price expected string got " . gettype($price) . PHP_EOL; + } - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals(1, $params['quoteOrderQty']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); - } + if ($type === "LIMIT" || $type === "STOP_LOSS_LIMIT" || $type === "TAKE_PROFIT_LIMIT") { + $request["price"] = $price; + $request["timeInForce"] = "GTC"; + } - public function testSpotMarketSell() - { - try { - $this->binance->marketSell($this->symbol, 1); + if ($type === "MARKET" && isset($params['isQuoteOrder']) && $params['isQuoteOrder']) { + unset($request['quantity']); + unset($params['quantity']); + unset($params['isQuoteOrder']); + $request['quoteOrderQty'] = $quantity; + } - } catch (\Throwable $e) { + if (isset($params['stopPrice'])) { + $request['stopPrice'] = $params['stopPrice']; + } + if (isset($params['icebergQty'])) { + $request['icebergQty'] = $params['icebergQty']; } - // warns here cuz method needs information fetched by exchangeInfo - $this->assertEquals("https://api.binance.com/api/v3/order", self::$capturedUrl); - parse_str(self::$capturedBody, $params); + if (isset($params['newOrderRespType'])) { + $request['newOrderRespType'] = $params['newOrderRespType']; + } - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals(1, $params['quantity']); - $this->assertEquals('SELL', $params['side']); - $this->assertEquals('MARKET', $params['type']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); + if (isset($params['newClientOrderId'])) { + $request['newClientOrderId'] = $params['newClientOrderId']; + } else { + $request['newClientOrderId'] = $this->generateSpotClientOrderId(); + } + return array_merge($request, $params); } - public function testSpotMarketSellTest() + /** + * editOrder - edits a quantity of an existing order + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#order-amend-keep-priority-trade + * @link https://developers.binance.com/docs/binance-spot-api-docs/faqs/order_amend_keep_priority + * + * @param string $symbol (mandatory) market symbol + * @param string $quantity (mandatory) new quantity (must be greater than 0 and less than the original order quantity) + * @param string $orderId (optional) order id to be amended (mandatory if origClientOrderId is not set) + * @param string $origClientOrderId (optional) original client order id to be amended (mandatory if orderId is not set) + * @param array $params (optional) additional transaction options + * - @param string $params['newClientOrderId'] - custom client order id + * + * @return array containing the response + * @throws \Exception + */ + public function editOrder(string $symbol, $quantity, ?string $orderId = null, ?string $origClientOrderId = null, array $params = []) { - try { - $this->binance->marketSellTest($this->symbol, $this->quantity); + $request = [ + "symbol" => $symbol, + "newQty" => $quantity, + ]; - } catch (\Throwable $e) { + if (!is_null($orderId)) { + $request['orderId'] = $orderId; + } else if (is_null($origClientOrderId)) { + throw new \Exception('editOrder(): Either orderId or origClientOrderId must be set'); + } else { + $request['origClientOrderId'] = $origClientOrderId; + } + if (isset($params['newClientOrderId'])) { + $request['newClientOrderId'] = $params['newClientOrderId']; + } else { + $request['newClientOrderId'] = $this->generateSpotClientOrderId(); + } + return $this->apiRequest("v3/order/amend/keepPriority", "PUT", array_merge($request, $params), true); + } + + /** + * sorOrder creates an order using the SOR endpoint + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#sor + * @link https://developers.binance.com/docs/binance-spot-api-docs/faqs/sor_faq + * + * @param string $side (mandatory) typically "BUY" or "SELL" + * @param string $symbol (mandatory) to buy or sell + * @param string $quantity (mandatory) in the order + * @param string $price (optional) for the order + * @param string $type (optional) is determined by the symbol bu typicall LIMIT, STOP_LOSS_LIMIT etc. (default is LIMIT) + * @param array $params (optional) additional transaction options + * - @param string $params['timeInForce'] - GTC, IOC, FOK + * - @param string $params['newClientOrderId'] - custom client order id + * - @param string $params['strategyId'] + * - @param int $params['strategyType'] + * - @param string $params['icebergQty'] - used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order + * - @param string $params['newOrderRespType'] - set the response type of the order (ACK, RESULT, FULL) + * - @param string $params['selfTradePreventionMode'] - NONE, EXPIRE_MAKER, EXPIRE_TAKER, EXPIRE_BOTH or DECREMENT (default is NONE) + * - @param bool $params['sor'] - if true, order will be sent to SOR endpoint + * - @param bool $params + * @param $test bool whether to test or not, test only validates the query + * + * @return array containing the response + * @throws \Exception + */ + public function sorOrder(string $side, string $symbol, $quantity, $price, string $type = "LIMIT", array $params = [], bool $test = false) + { + $params['sor'] = true; + return $this->order($side, $symbol, $quantity, $price, $type, $params, $test); + } + + /** + * replaceOrder - cancels an existing order and places a new order on the same symbol + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#cancel-an-existing-order-and-send-a-new-order-trade + * + * @param string $side (mandatory) typically "BUY" or "SELL" + * @param string $symbol (mandatory) to buy or sell + * @param string $quantity (mandatory) in the order + * @param string $price (optional) for the order + * @param string $type (optional) is determined by the symbol bu typicall LIMIT, STOP_LOSS_LIMIT etc. (default is LIMIT) + * @param string $cancelOrderId (optional) the orderId of the order to be replaced (mandatory if cancelOrigClientOrderId is not set) + * @param string $cancelOrigClientOrderId (optional) the origClientOrderId of the order to be replaced (mandatory if cancelOrderId is not set) + * @param bool $allowFailure (optional) if true new order placement will be attempted even if cancel request fails (default is false) + * @param string $cancelRestrictions (optional) ONLY_NEW (cancel will succeed if the order status is NEW) or ONLY_PARTIALLY_FILLED (cancel will succeed if order status is PARTIALLY_FILLED) + * @param string $orderRateLimitExceededMode (optional) DO_NOTHING (default - will only attempt to cancel the order if account has not exceeded the unfilled order rate limit) or CANCEL_ONLY (will always cancel the order) + * @param array $params (optional) additional transaction options (same as for order()) + * + * returns array containing the response + * @throws \Exception + */ + public function replaceOrder(string $side, string $symbol, $quantity, $price, string $type = "LIMIT", ?string $cancelOrderId = null, string $cancelOrigClientOrderId = null, bool $allowFailure = null, string $cancelRestrictions = null, string $orderRateLimitExceededMode = null, array $params = []) + { + $request = $this->createSpotOrderRequest($side, $symbol, $quantity, $price, $type, $params); + if (!is_null($cancelOrderId)) { + $request['cancelOrderId'] = $cancelOrderId; + } else if (is_null($cancelOrigClientOrderId)) { + throw new \Exception("replaceOrder(): Either cancelOrderId or cancelOrigClientOrderId must be set"); + } + if (!is_null($cancelOrigClientOrderId)) { + $request['cancelOrigClientOrderId'] = $cancelOrigClientOrderId; + } + $request['cancelReplaceMode'] = $allowFailure ? 'ALLOW_FAILURE' : 'STOP_ON_FAILURE'; + if (!is_null($cancelRestrictions)) { + $request['cancelRestrictions'] = $cancelRestrictions; + } + if (!is_null($orderRateLimitExceededMode)) { + $request['orderRateLimitExceededMode'] = $orderRateLimitExceededMode; + } + return $this->apiRequest("v3/order/cancelReplace", "POST", $request, true); // only request is needed, params are already added to request inside createSpotOrderRequest + } + /** + * candlesticks get the candles for the given intervals + * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints + * + * $candles = $api->candlesticks("BNBBTC", "5m"); + * + * @param $symbol market symbol to get the response for, e.g. ETHUSDT + * @param $interval string to request + * @param $limit int limit the amount of candles + * @param $startTime string request candle information starting from here + * @param $endTime string request candle information ending here + * @return array containing the response + * @throws \Exception + */ + public function candlesticks(string $symbol, string $interval = "5m", ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + if (!isset($this->charts[$symbol])) { + $this->charts[$symbol] = []; + } + + $request = [ + "symbol" => $symbol, + "interval" => $interval, + ]; + if ($limit) { + $request["limit"] = $limit; } - $this->assertEquals("https://api.binance.com/api/v3/order/test", self::$capturedUrl); - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->SPOT_ORDER_PREFIX)); - } + if ($startTime) { + $request["startTime"] = $startTime; + } - public function testSpotCancel() - { - try { - $this->binance->cancel($this->symbol, $this->orderid); + if ($endTime) { + $request["endTime"] = $endTime; + } - } catch (\Throwable $e) { + $response = $this->apiRequest("v3/klines", "GET", array_merge($request, $params)); + if (is_array($response) === false) { + return []; } - $endpoint = "https://api.binance.com/api/v3/order?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->orderid, $params['orderId']); + if (count($response) === 0) { + echo "warning: v3/klines returned empty array, usually a blip in the connection or server" . PHP_EOL; + return []; + } + $ticks = $this->chartData($symbol, $interval, $response); + $this->charts[$symbol][$interval] = $ticks; + return $ticks; } - public function testSpotOrderStatus() + /** + * uiCandlesticks return modified kline data, optimized for presentation of candlestick charts + * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints + * + * $candles = $api->uiCandlesticks("BNBBTC", "5m"); + * + * @param $symbol market symbol to get the response for, e.g. ETHUSDT + * @param $interval string to request + * @param $limit int limit the amount of candles + * @param $startTime string request candle information starting from here + * @param $endTime string request candle information ending here + * @param $params array additional parameters + * - @param string $params['timeZone'] (optional) default is 0 (UTC) - time zone to use for the response + * @return array containing the response + * @throws \Exception + */ + public function uiCandlesticks(string $symbol, string $interval = "5m", ?int $limit = null, $startTime = null, $endTime = null, array $params = []) { - try { - $this->binance->orderStatus($this->symbol, $this->orderid); + $request = [ + "symbol" => $symbol, + "interval" => $interval, + ]; - } catch (\Throwable $e) { + if ($limit) { + $request["limit"] = $limit; + } + + if ($startTime) { + $request["startTime"] = $startTime; + } + + if ($endTime) { + $request["endTime"] = $endTime; + } + + $response = $this->apiRequest("v3/uiKlines", "GET", array_merge($request, $params)); + + return $response; + } + + /** + * balanceData Converts all your balances into a nice array + * + * @param $priceData array of prices + * @return array containing the response + */ + protected function balanceData(array $array, string $marketType = 'spot') + { + $balances = []; + $is_spot = $marketType === 'spot'; + if (empty($array) || ($is_spot && empty($array['balances']))) { + // WPCS: XSS OK. + echo "balanceData error: Please make sure your system time is synchronized: call \$api->useServerTime() before this function" . PHP_EOL; + echo "ERROR: Invalid request. Please double check your API keys and permissions." . PHP_EOL; + return []; + } + $rawBalances = $is_spot ? $array['balances'] : $array; + foreach ($rawBalances as $obj) { + $asset = $obj['asset']; + $avaliable = 0.00000000; + $onOrder = 0.00000000; + if ($is_spot) { + $avaliable = $obj['free']; + $onOrder = $obj['locked']; + $total = $avaliable + $onOrder; + } else { + $avaliable = $obj['availableBalance']; + $total = $obj['balance']; + $onOrder = $total - $avaliable; + } + $balances[$asset] = [ + "available" => $avaliable, + "onOrder" => $onOrder, + "total" => $total, + "info" => $obj, + ]; + } + return $balances; + } + + /** + * balanceHandler Convert balance WebSocket data into array + * + * $data = $this->balanceHandler( $json ); + * + * @param $json array data to convert + * @return array + */ + protected function balanceHandler(array $json) + { + $balances = []; + foreach ($json as $item) { + $asset = $item->a; + $available = $item->f; + $onOrder = $item->l; + $balances[$asset] = [ + "available" => $available, + "onOrder" => $onOrder, + ]; + } + return $balances; + } + + /** + * tickerStreamHandler Convert WebSocket ticker data into array + * + * $data = $this->tickerStreamHandler( $json ); + * + * @param $json object data to convert + * @return array + */ + protected function tickerStreamHandler(\stdClass $json) + { + return [ + "eventType" => $json->e, + "eventTime" => $json->E, + "symbol" => $json->s, + "priceChange" => $json->p, + "percentChange" => $json->P, + "averagePrice" => $json->w, + "prevClose" => $json->x, + "close" => $json->c, + "closeQty" => $json->Q, + "bestBid" => $json->b, + "bestBidQty" => $json->B, + "bestAsk" => $json->a, + "bestAskQty" => $json->A, + "open" => $json->o, + "high" => $json->h, + "low" => $json->l, + "volume" => $json->v, + "quoteVolume" => $json->q, + "openTime" => $json->O, + "closeTime" => $json->C, + "firstTradeId" => $json->F, + "lastTradeId" => $json->L, + "numTrades" => $json->n, + ]; + } - } - $endpoint = "https://api.binance.com/api/v3/order?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + /** + * executionHandler Convert WebSocket trade execution into array + * + * $data = $this->executionHandler( $json ); + * + * @param \stdClass $json object data to convert + * @return array + */ + protected function executionHandler(\stdClass $json) + { + return [ + "symbol" => $json->s, + "side" => $json->S, + "orderType" => $json->o, + "quantity" => $json->q, + "price" => $json->p, + "executionType" => $json->x, + "orderStatus" => $json->X, + "rejectReason" => $json->r, + "orderId" => $json->i, + "clientOrderId" => $json->c, + "orderTime" => $json->T, + "eventTime" => $json->E, + ]; + } - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); + /** + * chartData Convert kline data into object + * + * $object = $this->chartData($symbol, $interval, $ticks); + * + * @param $symbol string of your currency + * @param $interval string the time interval + * @param $ticks array of the canbles array + * @return array object of the chartdata + */ + protected function chartData(string $symbol, string $interval, array $ticks, string $market_type = "spot", string $kline_type = 'klines') + { + $output = []; + foreach ($ticks as $tick) { + list($openTime, $open, $high, $low, $close, $assetVolume, $closeTime, $baseVolume, $trades, $assetBuyVolume, $takerBuyVolume, $ignored) = $tick; + $output[$openTime] = [ + "open" => $open, + "high" => $high, + "low" => $low, + "close" => $close, + "volume" => $baseVolume, + "openTime" => $openTime, + "closeTime" => $closeTime, + "assetVolume" => $assetVolume, + "baseVolume" => $baseVolume, + "trades" => $trades, + "assetBuyVolume" => $assetBuyVolume, + "takerBuyVolume" => $takerBuyVolume, + "ignored" => $ignored, + ]; + } + + if ($market_type !== "spot") { + if (!isset($this->info[$market_type])) { + $this->info[$market_type] = []; + } + if (!isset($this->info[$market_type][$symbol])) { + $this->info[$market_type][$symbol] = []; + } + if (!isset($this->info[$market_type][$symbol][$kline_type])) { + $this->info[$market_type][$symbol][$kline_type] = []; + } + if (!isset($this->info[$market_type][$symbol][$kline_type][$interval])) { + $this->info[$market_type][$symbol][$kline_type][$interval] = []; + } + if (isset($openTime)) { + $this->info[$market_type][$symbol][$kline_type][$interval]['firstOpen'] = $openTime; + } + } else { + if (!isset($this->info[$symbol])) { + $this->info[$symbol] = []; + } + if (!isset($this->info[$symbol][$interval])) { + $this->info[$symbol][$interval] = []; + } + if (isset($openTime)) { + $this->info[$symbol][$interval]['firstOpen'] = $openTime; + } + } + + return $output; + } + + /** + * tradesData Convert aggTrades data into easier format + * + * $tradesData = $this->tradesData($trades); + * + * @param $trades array of trade information + * @return array easier format for trade information + */ + protected function tradesData(array $trades) + { + $output = []; + foreach ($trades as $trade) { + $price = $trade['p']; + $quantity = $trade['q']; + $timestamp = $trade['T']; + $maker = $trade['m'] ? 'true' : 'false'; + $output[] = [ + "price" => $price, + "quantity" => $quantity, + "timestamp" => $timestamp, + "maker" => $maker, + ]; + } + return $output; + } + + /** + * bookPriceData Consolidates Book Prices into an easy to use object + * + * $bookPriceData = $this->bookPriceData($array); + * + * @param $array array book prices + * @return array easier format for book prices information + */ + protected function bookPriceData(array $array) + { + $bookprices = []; + foreach ($array as $obj) { + $bookprices[$obj['symbol']] = [ + "bid" => $obj['bidPrice'], + "bids" => $obj['bidQty'], + "ask" => $obj['askPrice'], + "asks" => $obj['askQty'], + ]; + } + return $bookprices; + } + + /** + * priceData Converts Price Data into an easy key/value array + * + * $array = $this->priceData($array); + * + * @param $array array of prices + * @return array of key/value pairs + */ + protected function priceData(array $array) + { + $prices = []; + foreach ($array as $obj) { + $prices[$obj['symbol']] = $obj['price']; + } + return $prices; + } + + /** + * cumulative Converts depth cache into a cumulative array + * + * $cumulative = $api->cumulative($depth); + * + * @param $depth array cache array + * @return array cumulative depth cache + */ + public function cumulative(array $depth) + { + $bids = []; + $asks = []; + $cumulative = 0; + foreach ($depth['bids'] as $price => $quantity) { + $cumulative += $quantity; + $bids[] = [ + $price, + $cumulative, + ]; + } + $cumulative = 0; + foreach ($depth['asks'] as $price => $quantity) { + $cumulative += $quantity; + $asks[] = [ + $price, + $cumulative, + ]; + } + return [ + "bids" => $bids, + "asks" => array_reverse($asks), + ]; + } - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->orderid, $params['orderId']); + /** + * highstock Converts Chart Data into array for highstock & kline charts + * + * $highstock = $api->highstock($chart, $include_volume); + * + * @param $chart array + * @param $include_volume bool for inclusion of volume + * @return array highchart data + */ + public function highstock(array $chart, bool $include_volume = false) + { + $array = []; + foreach ($chart as $timestamp => $obj) { + $line = [ + $timestamp, + floatval($obj['open']), + floatval($obj['high']), + floatval($obj['low']), + floatval($obj['close']), + ]; + if ($include_volume) { + $line[] = floatval($obj['volume']); + } + + $array[] = $line; + } + return $array; + } + + /** + * first Gets first key of an array + * + * $first = $api->first($array); + * + * @param $array array + * @return string key or null + */ + public function first(array $array) + { + if (count($array) > 0) { + return array_keys($array)[0]; + } + return null; + } + + /** + * last Gets last key of an array + * + * $last = $api->last($array); + * + * @param $array array + * @return string key or null + */ + public function last(array $array) + { + if (count($array) > 0) { + return array_keys(array_slice($array, -1))[0]; + } + return null; + } + + /** + * displayDepth Formats nicely for console output + * + * $outputString = $api->displayDepth($array); + * + * @param $array array + * @return string of the depth information + */ + public function displayDepth(array $array) + { + $output = ''; + foreach ([ + 'asks', + 'bids', + ] as $type) { + $entries = $array[$type]; + if ($type === 'asks') { + $entries = array_reverse($entries); + } + + $output .= "{$type}:" . PHP_EOL; + foreach ($entries as $price => $quantity) { + $total = number_format($price * $quantity, 8, '.', ''); + $quantity = str_pad(str_pad(number_format(rtrim($quantity, '.0')), 10, ' ', STR_PAD_LEFT), 15); + $output .= "{$price} {$quantity} {$total}" . PHP_EOL; + } + // echo str_repeat('-', 32).PHP_EOL; + } + return $output; + } + + /** + * depthData Formats depth data for nice display + * + * $array = $this->depthData($symbol, $json); + * + * @param $symbol string to display + * @param $json array of the depth infomration + * @return array of the depth information + */ + protected function depthData(string $symbol, array $json, ?string $product_type = null) + { + $bids = $asks = []; + foreach ($json['bids'] as $obj) { + $bids[$obj[0]] = $obj[1]; + } + foreach ($json['asks'] as $obj) { + $asks[$obj[0]] = $obj[1]; + } + $result = [ + "bids" => $bids, + "asks" => $asks, + ]; + if (isset($product_type)) { + $this->depthCache[$symbol][$product_type] = $result; + } else { + $this->depthCache[$symbol] = $result; + } + return $result; + } + + /** + * roundStep rounds quantity with stepSize + * @param $qty quantity + * @param $stepSize parameter from exchangeInfo + * @return rounded value. example: roundStep(1.2345, 0.1) = 1.2 + * + */ + public function roundStep($qty, $stepSize = 0.1) + { + $precision = strlen(substr(strrchr(rtrim($stepSize, '0'), '.'), 1)); + return round((($qty / $stepSize) | 0) * $stepSize, $precision); + } + + /** + * roundTicks rounds price with tickSize + * @param $value price + * @param $tickSize parameter from exchangeInfo + * @return rounded value. example: roundStep(1.2345, 0.1) = 1.2 + * + */ + public function roundTicks($price, $tickSize) + { + $precision = strlen(rtrim(substr($tickSize, strpos($tickSize, '.', 1) + 1), '0')); + return number_format($price, $precision, '.', ''); + } + + /** + * getTransfered gets the total transfered in b,Kb,Mb,Gb + * + * $transfered = $api->getTransfered(); + * + * @return string showing the total transfered + */ + public function getTransfered() + { + $base = log($this->transfered, 1024); + $suffixes = array( + '', + 'K', + 'M', + 'G', + 'T', + ); + return round(pow(1024, $base - floor($base)), 2) . ' ' . $suffixes[floor($base)]; + } + + /** + * getRequestCount gets the total number of API calls + * + * $apiCount = $api->getRequestCount(); + * + * @return int get the total number of api calls + */ + public function getRequestCount() + { + return $this->requestCount; + } + + /** + * addToTransfered add interger bytes to the total transfered + * also incrementes the api counter + * + * $apiCount = $api->addToTransfered( $int ); + * + * @return null + */ + public function addToTransfered(int $int) + { + $this->transfered += $int; + $this->requestCount++; + } + + /* + * WebSockets + */ + + /** + * depthHandler For WebSocket Depth Cache + * + * $this->depthHandler($json); + * + * @param $json array of depth bids and asks + * @return null + */ + protected function depthHandler(array $json) + { + $symbol = $json['s']; + if ($json['u'] <= $this->info[$symbol]['firstUpdate']) { + return; + } + + foreach ($json['b'] as $bid) { + $this->depthCache[$symbol]['bids'][$bid[0]] = $bid[1]; + if ($bid[1] == "0.00000000") { + unset($this->depthCache[$symbol]['bids'][$bid[0]]); + } + } + foreach ($json['a'] as $ask) { + $this->depthCache[$symbol]['asks'][$ask[0]] = $ask[1]; + if ($ask[1] == "0.00000000") { + unset($this->depthCache[$symbol]['asks'][$ask[0]]); + } + } + } + + /** + * chartHandler For WebSocket Chart Cache + * + * $this->chartHandler($symbol, $interval, $json); + * + * @param $symbol string to sort + * @param $interval string time + * @param \stdClass $json object time + * @return null + */ + protected function chartHandler(string $symbol, string $interval, \stdClass $json) + { + if (!$this->info[$symbol][$interval]['firstOpen']) { // Wait for /kline to finish loading + $this->chartQueue[$symbol][$interval][] = $json; + return; + } + $chart = $json->k; + $symbol = $json->s; + $interval = $chart->i; + $tick = $chart->t; + if ($tick < $this->info[$symbol][$interval]['firstOpen']) { + return; + } + // Filter out of sync data + $open = $chart->o; + $high = $chart->h; + $low = $chart->l; + $close = $chart->c; + $volume = $chart->q; // +trades buyVolume assetVolume makerVolume + $this->charts[$symbol][$interval][$tick] = [ + "open" => $open, + "high" => $high, + "low" => $low, + "close" => $close, + "volume" => $volume, + ]; + } + /** + * sortDepth Sorts depth data for display & getting highest bid and lowest ask + * + * $sorted = $api->sortDepth($symbol, $limit); + * + * @param $symbol string to sort + * @param $limit int depth + * @return null + */ + public function sortDepth(string $symbol, int $limit = 11) + { + $bids = $this->depthCache[$symbol]['bids']; + $asks = $this->depthCache[$symbol]['asks']; + krsort($bids); + ksort($asks); + return [ + "asks" => array_slice($asks, 0, $limit, true), + "bids" => array_slice($bids, 0, $limit, true), + ]; } - public function testSpotOpenOrders() - { - try { - $this->binance->openOrders($this->symbol); + /** + * depthCache Pulls /depth data and subscribes to @depth WebSocket endpoint + * Maintains a local Depth Cache in sync via lastUpdateId. + * See depth() and depthHandler() + * + * $api->depthCache(["BNBBTC"], function($api, $symbol, $depth) { + * echo "{$symbol} depth cache update".PHP_EOL; + * //print_r($depth); // Print all depth data + * $limit = 11; // Show only the closest asks/bids + * $sorted = $api->sortDepth($symbol, $limit); + * $bid = $api->first($sorted['bids']); + * $ask = $api->first($sorted['asks']); + * echo $api->displayDepth($sorted); + * echo "ask: {$ask}".PHP_EOL; + * echo "bid: {$bid}".PHP_EOL; + * }); + * + * @param $symbol string optional array of symbols + * @param $callback callable closure + * @return null + */ + public function depthCache($symbols, callable $callback) + { + if (!is_array($symbols)) { + $symbols = [ + $symbols, + ]; + } + + $loop = \React\EventLoop\Factory::create(); + $react = new \React\Socket\Connector($loop); + $connector = new \Ratchet\Client\Connector($loop, $react); + foreach ($symbols as $symbol) { + if (!isset($this->info[$symbol])) { + $this->info[$symbol] = []; + } + + if (!isset($this->depthQueue[$symbol])) { + $this->depthQueue[$symbol] = []; + } + + if (!isset($this->depthCache[$symbol])) { + $this->depthCache[$symbol] = [ + "bids" => [], + "asks" => [], + ]; + } + + $this->info[$symbol]['firstUpdate'] = 0; + $endpoint = strtolower($symbol) . '@depthCache'; + $this->subscriptions[$endpoint] = true; + + $connector($this->getWsEndpoint() . strtolower($symbol) . '@depth')->then(function ($ws) use ($callback, $symbol, $loop, $endpoint) { + $ws->on('message', function ($data) use ($ws, $callback, $loop, $endpoint) { + if ($this->subscriptions[$endpoint] === false) { + //$this->subscriptions[$endpoint] = null; + $loop->stop(); + return; //return $ws->close(); + } + $json = json_decode($data, true); + $symbol = $json['s']; + if (intval($this->info[$symbol]['firstUpdate']) === 0) { + $this->depthQueue[$symbol][] = $json; + return; + } + $this->depthHandler($json); + call_user_func($callback, $this, $symbol, $this->depthCache[$symbol]); + }); + $ws->on('close', function ($code = null, $reason = null) use ($symbol, $loop) { + // WPCS: XSS OK. + echo "depthCache({$symbol}) WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; + $loop->stop(); + }); + }, function ($e) use ($loop, $symbol) { + // WPCS: XSS OK. + echo "depthCache({$symbol})) Could not connect: {$e->getMessage()}" . PHP_EOL; + $loop->stop(); + }); + $this->depth($symbol, 100); + foreach ($this->depthQueue[$symbol] as $data) { + //TODO:: WTF ??? where is json and what should be in it ?? + $this->depthHandler($json); + } + $this->depthQueue[$symbol] = []; + call_user_func($callback, $this, $symbol, $this->depthCache[$symbol]); + } + $loop->run(); + } + + /** + * trades Trades WebSocket Endpoint + * + * $api->trades(["BNBBTC"], function($api, $symbol, $trades) { + * echo "{$symbol} trades update".PHP_EOL; + * print_r($trades); + * }); + * + * @param $symbols + * @param $callback callable closure + * @return null + */ + public function trades($symbols, callable $callback) + { + if (!is_array($symbols)) { + $symbols = [ + $symbols, + ]; + } + + $loop = \React\EventLoop\Factory::create(); + $react = new \React\Socket\Connector($loop); + $connector = new \Ratchet\Client\Connector($loop, $react); + foreach ($symbols as $symbol) { + if (!isset($this->info[$symbol])) { + $this->info[$symbol] = []; + } + + // $this->info[$symbol]['tradesCallback'] = $callback; + + $endpoint = strtolower($symbol) . '@trades'; + $this->subscriptions[$endpoint] = true; + + $connector($this->getWsEndpoint() . strtolower($symbol) . '@aggTrade')->then(function ($ws) use ($callback, $symbol, $loop, $endpoint) { + $ws->on('message', function ($data) use ($ws, $callback, $loop, $endpoint) { + if ($this->subscriptions[$endpoint] === false) { + //$this->subscriptions[$endpoint] = null; + $loop->stop(); + return; //return $ws->close(); + } + $json = json_decode($data, true); + $symbol = $json['s']; + $price = $json['p']; + $quantity = $json['q']; + $timestamp = $json['T']; + $maker = $json['m'] ? 'true' : 'false'; + $trades = [ + "price" => $price, + "quantity" => $quantity, + "timestamp" => $timestamp, + "maker" => $maker, + ]; + // $this->info[$symbol]['tradesCallback']($this, $symbol, $trades); + call_user_func($callback, $this, $symbol, $trades); + }); + $ws->on('close', function ($code = null, $reason = null) use ($symbol, $loop) { + // WPCS: XSS OK. + echo "trades({$symbol}) WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; + $loop->stop(); + }); + }, function ($e) use ($loop, $symbol) { + // WPCS: XSS OK. + echo "trades({$symbol}) Could not connect: {$e->getMessage()}" . PHP_EOL; + $loop->stop(); + }); + } + $loop->run(); + } + + /** + * ticker pulls 24h price change statistics via WebSocket + * + * $api->ticker(false, function($api, $symbol, $ticker) { + * print_r($ticker); + * }); + * + * @param $symbol string optional symbol or false + * @param $callback callable closure + * @return null + */ + public function ticker($symbol, callable $callback) + { + $endpoint = $symbol ? strtolower($symbol) . '@ticker' : '!ticker@arr'; + $this->subscriptions[$endpoint] = true; + + // @codeCoverageIgnoreStart + // phpunit can't cover async function + \Ratchet\Client\connect($this->getWsEndpoint() . $endpoint)->then(function ($ws) use ($callback, $symbol, $endpoint) { + $ws->on('message', function ($data) use ($ws, $callback, $symbol, $endpoint) { + if ($this->subscriptions[$endpoint] === false) { + //$this->subscriptions[$endpoint] = null; + $ws->close(); + return; //return $ws->close(); + } + $json = json_decode($data); + if ($symbol) { + call_user_func($callback, $this, $symbol, $this->tickerStreamHandler($json)); + } else { + foreach ($json as $obj) { + $return = $this->tickerStreamHandler($obj); + $symbol = $return['symbol']; + call_user_func($callback, $this, $symbol, $return); + } + } + }); + $ws->on('close', function ($code = null, $reason = null) { + // WPCS: XSS OK. + echo "ticker: WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; + }); + }, function ($e) { + // WPCS: XSS OK. + echo "ticker: Could not connect: {$e->getMessage()}" . PHP_EOL; + }); + // @codeCoverageIgnoreEnd + } + + /** + * chart Pulls /kline data and subscribes to @klines WebSocket endpoint + * + * $api->chart(["BNBBTC"], "15m", function($api, $symbol, $chart) { + * echo "{$symbol} chart update\n"; + * print_r($chart); + * }); + * + * @param $symbols string required symbols + * @param $interval string time inteval + * @param $callback callable closure + * @param $limit int default 500, maximum 1000 + * @return null + * @throws \Exception + */ + public function chart($symbols, string $interval = "30m", ?callable $callback = null, $limit = 500) + { + if (is_null($callback)) { + throw new Exception("You must provide a valid callback"); + } + if (!is_array($symbols)) { + $symbols = [ + $symbols, + ]; + } + + $loop = \React\EventLoop\Factory::create(); + $react = new \React\Socket\Connector($loop); + $connector = new \Ratchet\Client\Connector($loop, $react); + foreach ($symbols as $symbol) { + if (!isset($this->charts[$symbol])) { + $this->charts[$symbol] = []; + } + + $this->charts[$symbol][$interval] = []; + if (!isset($this->info[$symbol])) { + $this->info[$symbol] = []; + } + + if (!isset($this->info[$symbol][$interval])) { + $this->info[$symbol][$interval] = []; + } + + if (!isset($this->chartQueue[$symbol])) { + $this->chartQueue[$symbol] = []; + } + + $this->chartQueue[$symbol][$interval] = []; + $this->info[$symbol][$interval]['firstOpen'] = 0; + $endpoint = strtolower($symbol) . '@kline_' . $interval; + $this->subscriptions[$endpoint] = true; + $connector($this->getWsEndpoint() . $endpoint)->then(function ($ws) use ($callback, $symbol, $loop, $endpoint, $interval) { + $ws->on('message', function ($data) use ($ws, $loop, $callback, $endpoint) { + if ($this->subscriptions[$endpoint] === false) { + //$this->subscriptions[$endpoint] = null; + $loop->stop(); + return; //return $ws->close(); + } + $json = json_decode($data); + $chart = $json->k; + $symbol = $json->s; + $interval = $chart->i; + $this->chartHandler($symbol, $interval, $json); + call_user_func($callback, $this, $symbol, $this->charts[$symbol][$interval]); + }); + $ws->on('close', function ($code = null, $reason = null) use ($symbol, $loop, $interval) { + // WPCS: XSS OK. + echo "chart({$symbol},{$interval}) WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; + $loop->stop(); + }); + }, function ($e) use ($loop, $symbol, $interval) { + // WPCS: XSS OK. + echo "chart({$symbol},{$interval})) Could not connect: {$e->getMessage()}" . PHP_EOL; + $loop->stop(); + }); + $this->candlesticks($symbol, $interval, $limit); + foreach ($this->chartQueue[$symbol][$interval] as $json) { + $this->chartHandler($symbol, $interval, $json); + } + $this->chartQueue[$symbol][$interval] = []; + call_user_func($callback, $this, $symbol, $this->charts[$symbol][$interval]); + } + $loop->run(); + } + + /** + * kline Subscribes to @klines WebSocket endpoint for latest chart data only + * + * $api->kline(["BNBBTC"], "15m", function($api, $symbol, $chart) { + * echo "{$symbol} chart update\n"; + * print_r($chart); + * }); + * + * @param $symbols string required symbols + * @param $interval string time inteval + * @param $callback callable closure + * @return null + * @throws \Exception + */ + public function kline($symbols, string $interval = "30m", ?callable $callback = null) + { + if (is_null($callback)) { + throw new Exception("You must provide a valid callback"); + } + if (!is_array($symbols)) { + $symbols = [ + $symbols, + ]; + } + + $loop = \React\EventLoop\Factory::create(); + $react = new \React\Socket\Connector($loop); + $connector = new \Ratchet\Client\Connector($loop, $react); + foreach ($symbols as $symbol) { + $endpoint = strtolower($symbol) . '@kline_' . $interval; + $this->subscriptions[$endpoint] = true; + $connector($this->getWsEndpoint() . $endpoint)->then(function ($ws) use ($callback, $symbol, $loop, $endpoint, $interval) { + $ws->on('message', function ($data) use ($ws, $loop, $callback, $endpoint) { + if ($this->subscriptions[$endpoint] === false) { + $loop->stop(); + return; + } + $json = json_decode($data); + $chart = $json->k; + $symbol = $json->s; + $interval = $chart->i; + call_user_func($callback, $this, $symbol, $chart); + }); + $ws->on('close', function ($code = null, $reason = null) use ($symbol, $loop, $interval) { + // WPCS: XSS OK. + echo "kline({$symbol},{$interval}) WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; + $loop->stop(); + }); + }, function ($e) use ($loop, $symbol, $interval) { + // WPCS: XSS OK. + echo "kline({$symbol},{$interval})) Could not connect: {$e->getMessage()}" . PHP_EOL; + $loop->stop(); + }); + } + $loop->run(); + } + + /** + * terminate Terminates websocket endpoints. View endpoints first: print_r($api->subscriptions) + * + * $api->terminate('ethbtc_kline@5m'); + * + * @return null + */ + public function terminate($endpoint) + { + // check if $this->subscriptions[$endpoint] is true otherwise error + $this->subscriptions[$endpoint] = false; + } + + /** + * keepAlive Keep-alive function for userDataStream + * + * $api->keepAlive(); + * + * @return null + */ + public function keepAlive() + { + $loop = \React\EventLoop\Factory::create(); + $loop->addPeriodicTimer(30, function () { + $listenKey = $this->listenKey; + $this->httpRequest("v1/userDataStream?listenKey={$listenKey}", "PUT", []); + }); + $loop->run(); + } + + /** + * userData Issues userDataStream token and keepalive, subscribes to userData WebSocket + * + * $balance_update = function($api, $balances) { + * print_r($balances); + * echo "Balance update".PHP_EOL; + * }; + * + * $order_update = function($api, $report) { + * echo "Order update".PHP_EOL; + * print_r($report); + * $price = $report['price']; + * $quantity = $report['quantity']; + * $symbol = $report['symbol']; + * $side = $report['side']; + * $orderType = $report['orderType']; + * $orderId = $report['orderId']; + * $orderStatus = $report['orderStatus']; + * $executionType = $report['orderStatus']; + * if( $executionType == "NEW" ) { + * if( $executionType == "REJECTED" ) { + * echo "Order Failed! Reason: {$report['rejectReason']}".PHP_EOL; + * } + * echo "{$symbol} {$side} {$orderType} ORDER #{$orderId} ({$orderStatus})".PHP_EOL; + * echo "..price: {$price}, quantity: {$quantity}".PHP_EOL; + * return; + * } + * + * //NEW, CANCELED, REPLACED, REJECTED, TRADE, EXPIRED + * echo "{$symbol} {$side} {$executionType} {$orderType} ORDER #{$orderId}".PHP_EOL; + * }; + * $api->userData($balance_update, $order_update); + * + * @param $balance_callback callable function + * @param bool $execution_callback callable function + * @return null + * @throws \Exception + */ + public function userData(&$balance_callback, &$execution_callback = false) + { + $response = $this->httpRequest("v1/userDataStream", "POST", []); + $this->listenKey = $response['listenKey']; + $this->info['balanceCallback'] = $balance_callback; + $this->info['executionCallback'] = $execution_callback; + + $this->subscriptions['@userdata'] = true; + + $loop = \React\EventLoop\Factory::create(); + $loop->addPeriodicTimer(30*60, function () { + $listenKey = $this->listenKey; + $this->httpRequest("v1/userDataStream?listenKey={$listenKey}", "PUT", []); + }); + $connector = new \Ratchet\Client\Connector($loop); + + // @codeCoverageIgnoreStart + // phpunit can't cover async function + $connector($this->getWsEndpoint() . $this->listenKey)->then(function ($ws) use ($loop) { + $ws->on('message', function ($data) use ($ws) { + if ($this->subscriptions['@userdata'] === false) { + //$this->subscriptions[$endpoint] = null; + $ws->close(); + return; //return $ws->close(); + } + $json = json_decode($data); + $type = $json->e; + if ($type === "outboundAccountPosition") { + $balances = $this->balanceHandler($json->B); + $this->info['balanceCallback']($this, $balances); + } elseif ($type === "executionReport") { + $report = $this->executionHandler($json); + if ($this->info['executionCallback']) { + $this->info['executionCallback']($this, $report); + } + } + }); + $ws->on('close', function ($code = null, $reason = null) use ($loop) { + // WPCS: XSS OK. + echo "userData: WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; + $loop->stop(); + }); + }, function ($e) use ($loop) { + // WPCS: XSS OK. + echo "userData: Could not connect: {$e->getMessage()}" . PHP_EOL; + $loop->stop(); + }); + + $loop->run(); + } + + /** + * miniTicker Get miniTicker for all symbols + * + * $api->miniTicker(function($api, $ticker) { + * print_r($ticker); + * }); + * + * @param $callback callable function closer that takes 2 arguments, $pai and $ticker data + * @return null + */ + public function miniTicker(callable $callback) + { + $endpoint = '@miniticker'; + $this->subscriptions[$endpoint] = true; + + // @codeCoverageIgnoreStart + // phpunit can't cover async function + \Ratchet\Client\connect($this->getWsEndpoint() . '!miniTicker@arr')->then(function ($ws) use ($callback, $endpoint) { + $ws->on('message', function ($data) use ($ws, $callback, $endpoint) { + if ($this->subscriptions[$endpoint] === false) { + //$this->subscriptions[$endpoint] = null; + $ws->close(); + return; //return $ws->close(); + } + $json = json_decode($data, true); + $markets = []; + foreach ($json as $obj) { + $markets[] = [ + "symbol" => $obj['s'], + "close" => $obj['c'], + "open" => $obj['o'], + "high" => $obj['h'], + "low" => $obj['l'], + "volume" => $obj['v'], + "quoteVolume" => $obj['q'], + "eventTime" => $obj['E'], + ]; + } + call_user_func($callback, $this, $markets); + }); + $ws->on('close', function ($code = null, $reason = null) { + // WPCS: XSS OK. + echo "miniticker: WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; + }); + }, function ($e) { + // WPCS: XSS OK. + echo "miniticker: Could not connect: {$e->getMessage()}" . PHP_EOL; + }); + // @codeCoverageIgnoreEnd + } + + /** + * bookTicker Get bookTicker for all symbols + * + * $api->bookTicker(function($api, $ticker) { + * print_r($ticker); + * }); + * + * @param $callback callable function closer that takes 2 arguments, $api and $ticker data + * @return null + */ + public function bookTicker(callable $callback) + { + $endpoint = '!bookticker'; + $this->subscriptions[$endpoint] = true; + + // @codeCoverageIgnoreStart + // phpunit can't cover async function + \Ratchet\Client\connect($this->getWsEndpoint() . '!bookTicker')->then(function ($ws) use ($callback, $endpoint) { + $ws->on('message', function ($data) use ($ws, $callback, $endpoint) { + if ($this->subscriptions[$endpoint] === false) { + //$this->subscriptions[$endpoint] = null; + $ws->close(); + return; //return $ws->close(); + } + $json = json_decode($data, true); + + $markets = [ + "updateId" => $json['u'], + "symbol" => $json['s'], + "bid_price" => $json['b'], + "bid_qty" => $json['B'], + "ask_price" => $json['a'], + "ask_qty" => $json['A'], + ]; + call_user_func($callback, $this, $markets); + }); + $ws->on('close', function ($code = null, $reason = null) { + // WPCS: XSS OK. + echo "miniticker: WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; + }); + }, function ($e) { + // WPCS: XSS OK. + echo "miniticker: Could not connect: {$e->getMessage()}" . PHP_EOL; + }); + // @codeCoverageIgnoreEnd + } + + /** + * Due to ongoing issues with out of date wamp CA bundles + * This function downloads ca bundle for curl website + * and uses it as part of the curl options + */ + protected function downloadCurlCaBundle() + { + $output_filename = getcwd() . "/ca.pem"; + + if (is_writable(getcwd()) === false) { + die(getcwd() . ' folder is not writeable, please check your permissions to download CA Certificates, or use $api->caOverride = true;'); + } - } catch (\Throwable $e) { + $host = "https://curl.se/ca/cacert.pem"; + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $host); + curl_setopt($curl, CURLOPT_VERBOSE, 0); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_HEADER, 0); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); + // proxy settings + if (is_array($this->proxyConf)) { + curl_setopt($curl, CURLOPT_PROXY, $this->getProxyUriString()); + if (isset($this->proxyConf['user']) && isset($this->proxyConf['pass'])) { + curl_setopt($curl, CURLOPT_PROXYUSERPWD, $this->proxyConf['user'] . ':' . $this->proxyConf['pass']); + } } - $endpoint = "https://api.binance.com/api/v3/openOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - - } - - public function testSpotCancelOpenOrders() - { - try { - $this->binance->cancelOpenOrders($this->symbol); - } catch (\Throwable $e) { + $result = curl_exec($curl); + curl_close($curl); + if ($result === false) { + echo "Unable to to download the CA bundle $host" . PHP_EOL; + return; } - $endpoint = "https://api.binance.com/api/v3/openOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); + $fp = fopen($output_filename, 'w'); - $this->assertEquals($this->symbol, $params['symbol']); + if ($fp === false) { + echo "Unable to write $output_filename, please check permissions on folder" . PHP_EOL; + return; + } + fwrite($fp, $result); + fclose($fp); } - public function testSpotOrders() + protected function floorDecimal($n, $decimals=2) { - try { - $this->binance->orders($this->symbol, $this->limit, $this->fromOrderId); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/allOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); + return floor($n * pow(10, $decimals)) / pow(10, $decimals); + } - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->fromOrderId, $params['orderId']); + protected function setXMbxUsedWeight(int $usedWeight) : void + { + $this->xMbxUsedWeight = $usedWeight; } - public function testSpotHistory() + protected function setXMbxUsedWeight1m(int $usedWeight1m) : void { - try { - $this->binance->history($this->symbol, $this->limit, $this->fromTradeId, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/myTrades?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->fromTradeId, $params['fromId']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - + $this->xMbxUsedWeight1m = $usedWeight1m; } - public function testSpotMyTrades() + public function getXMbxUsedWeight() : int { - try { - $this->binance->myTrades($this->symbol, $this->limit, $this->fromTradeId, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/myTrades?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->fromTradeId, $params['fromId']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - + return $this->xMbxUsedWeight; } - public function testUseServerTime() + public function getXMbxUsedWeight1m() : int { - try { - $this->binance->useServerTime(); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://api.binance.com/api/v3/time", self::$capturedUrl); + return $this->xMbxUsedWeight1m; } - public function testTime() + private function getWsEndpoint() : string { - try { - $this->binance->time(); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://api.binance.com/api/v3/time", self::$capturedUrl); + return $this->useTestnet ? $this->streamTestnet : $this->stream; } - public function testSpotExchangeInfo() + public function isOnTestnet() : bool { - try { - $this->binance->exchangeInfo($this->symbols); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/exchangeInfo?"; - $this->assertTrue(str_starts_with (self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - $this->assertTrue(!empty($params['symbols'])); - $symbols = $params['symbols']; - $this->assertTrue(str_contains($symbols, $this->symbols[0])); - $this->assertTrue(str_contains($symbols, $this->symbols[1])); + return $this->useTestnet; } - public function testAssetDetail() + /** + * systemStatus - Status indicator for api sapi + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints + * @link https://binance-docs.github.io/apidocs/spot/en/#system-status-system + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api + * + * @property int $weight 2 + * + * @return array containing the response + * @throws \Exception + */ + public function systemStatus(array $params = []) { - try { - $this->binance->assetDetail($this->asset); - - } catch (\Throwable $e) { - + $arr = array(); + $api_status = $this->apiRequest("v3/ping", 'GET', $params); + if ( empty($api_status) ) { + $arr['api']['status'] = 'ping ok'; + } else { + $arr['api']['status'] = $api_status; } - $endpoint = "https://api.binance.com/sapi/v1/asset/assetDetail?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->asset, $params['asset']); + $fapi_status = $this->fapiRequest("v1/ping", 'GET', $params); + if ( empty($fapi_status) ) { + $arr['fapi']['status'] = 'ping ok'; + } else { + $arr['fapi']['status'] = $fapi_status; + } + $arr['sapi'] = $this->sapiRequest("v1/system/status", 'GET', $params); + return $arr; } - public function testSpotDustLog() + /** + * accountSnapshot - Daily Account Snapshot at 00:00:00 UTC + * + * @link https://binance-docs.github.io/apidocs/spot/en/#daily-account-snapshot-user_data + * + * @property int $weight 1 + * + * @param string $type (mandatory) Should be SPOT, MARGIN or FUTURES + * @param int $nbrDays (optional) Number of days. Default 5, min 5, max 30 + * @param long $startTime (optional) Start time, e.g. 1617580799000 + * @param long $endTime (optional) End time, e.g. 1617667199000 + * + * @return array containing the response + * @throws \Exception + */ + public function accountSnapshot($type, $nbrDays = 5, $startTime = 0, $endTime = 0, array $params = []) { - try { - $this->binance->dustLog($this->startTime, $this->endTime); + if ($nbrDays < 5 || $nbrDays > 30) + $nbrDays = 5; - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/sapi/v1/asset/dribblet?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + $request = [ + 'type' => $type, + ]; - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); + if ($startTime > 0) + $request['startTime'] = $startTime; + if ($endTime > 0) + $request['endTime'] = $endTime; + if ($nbrDays != 5) + $request['limit'] = $nbrDays; - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); + return $this->sapiRequest("v1/accountSnapshot", 'GET', array_merge($request, $params), true); + } + + /** + * accountStatus - Fetch account status detail. + * + * @link https://binance-docs.github.io/apidocs/spot/en/#account-status-user_data + * + * @property int $weight 1 + * + * @return array containing the response + * @throws \Exception + */ + public function accountStatus(array $params = []) + { + $arr = array(); + $arr['sapi'] = $this->sapiRequest("v1/account/status", 'GET', $params, true); + return $arr; + } + + /** + * apiRestriction - Fetch a set of API restrictions + * + * @link https://binance-docs.github.io/apidocs/spot/en/#get-api-key-permission-user_data + * + * @property int $weight 1 + * + * @return array containing the response + * @throws \Exception + */ + public function apiRestrictions(array $params = []) + { + return $this->sapiRequest("v1/account/apiRestrictions", 'GET', $params, true); + } + + /** + * apiTradingStatus - Fetch account API trading status detail. + * + * @link https://binance-docs.github.io/apidocs/spot/en/#account-api-trading-status-user_data + * + * @property int $weight 1 + * + * @return array containing the response + * @throws \Exception + */ + public function apiTradingStatus(array $params = []) + { + $arr = array(); + $arr['sapi'] = $this->sapiRequest("v1/account/apiTradingStatus", 'GET', $params, true); + return $arr; + } + + /** + * orderRateLimit gets the user rate limit + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#query-unfilled-order-count-user_data + * + * $rateLimit = $api->orderRateLimit(); + * + * @property int $weight 1 + * + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the rate limit details + * @throws \Exception + */ + public function orderRateLimit(array $params = []) + { + return $this->apiRequest("v3/rateLimit/order", 'GET', $params, true); + } + + /** + * preventedMatches - Get the list of orders that were expired due to STP + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#query-prevented-matches-user_data + * + * @property int $weight 2 + * - 2 if preventedMatchId is provided + * - 20 if orderId is provided + * + * @param string $symbol (mandatory) The symbol, e.g. BTCUSDT + * @param string $preventedMatchId (optional) The ID of the prevented match (mandatory if orderId is not provided) + * @param string $orderId (optional) The ID of the order (mandatory if preventedMatchId is not provided) + * @param string $fromPreventedMatchId (optional) The ID of the prevented match to start from + * @param int $limit (optional) The number of results to return (default is 500, max is 1000) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * + * @return array with error message or the rate limit details + * @throws \Exception + */ + public function preventedMatches(string $symbol, ?string $preventedMatchId = null, ?string $orderId = null, ?string $fromPreventedMatchId = null, ?int $limit = null, array $params = []) { + $request = [ + 'symbol' => $symbol, + ]; - } + if (is_null($preventedMatchId) && is_null($orderId)) { + throw new \Exception("Either preventedMatchId or orderId must be provided"); + } else if ($preventedMatchId) { + $request['preventedMatchId'] = $preventedMatchId; + } else { + $request['orderId'] = $orderId; + } + + if ($fromPreventedMatchId) { + $request['fromPreventedMatchId'] = $fromPreventedMatchId; + } + + if (!is_null($limit)) { + $request['limit'] = $limit; + } + + return $this->apiRequest("v3/myPreventedMatches", 'GET', array_merge($request, $params), true); + } + + /** + * allocations - Get the allocations resulting from SOR order placement + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#query-allocations-user_data + * + * @property int $weight 20 + * + * @param string $symbol (mandatory) The symbol, e.g. BTCUSDT + * @param int $startTime (optional) The start time of the query + * @param int $endTime (optional) The end time of the query + * @param string $fromAllocationId (optional) The ID of the allocation to start from + * @param int $limit (optional) The number of results to return (default is 500, max is 1000) + * @param string $orderId (optional) The ID of the order + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * + * @return array with error message or the rate limit details + * @throws \Exception + */ + public function allocations(string $symbol, ?int $startTime = null, ?int $endTime = null, ?string $fromAllocationId = null, ?int $limit = null, ?string $orderId = null, array $params = []) { + $request = [ + 'symbol' => $symbol, + ]; - public function testSpotTradeFee() - { - try { - $this->binance->tradeFee($this->symbol); + if ($startTime) { + $request['startTime'] = $startTime; + } - } catch (\Throwable $e) { + if ($endTime) { + $request['endTime'] = $endTime; + } + if ($fromAllocationId) { + $request['fromAllocationId'] = $fromAllocationId; } - $endpoint = "https://api.binance.com/sapi/v1/asset/tradeFee?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); + if (!is_null($limit)) { + $request['limit'] = $limit; + } - $this->assertEquals($this->symbol, $params['symbol']); + if ($orderId) { + $request['orderId'] = $orderId; + } + return $this->apiRequest("v3/myAllocations", 'GET', array_merge($request, $params), true); } - public function testSpotCommissionFee() - { - try { - $this->binance->commissionFee($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/sapi/v1/asset/tradeFee?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + /** + * commissionRate - Get the commission rate for a symbol + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#query-commission-rates-user_data + * + * @property int $weight 20 + * + * @param string $symbol (mandatory) The symbol, e.g. BTCUSDT + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * + * @return array with error message or the rate limit details + * @throws \Exception + */ + public function commissionRate(string $symbol, array $params = []) { + $request = [ + 'symbol' => $symbol, + ]; - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); + return $this->apiRequest("v3/account/commission", 'GET', array_merge($request, $params), true); + } + + /** + * orderAmendments - Get all amendments of a single order + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#query-order-amendments-user_data + * + * @property int $weight 4 + * + * @param string $symbol (mandatory) The symbol, e.g. BTCUSDT + * @param string $orderId (mandatory) The ID of the order + * @param string $fromExecutionId (optional) The ID of the execution to start from + * @param int $limit (optional) The number of results to return (default is 500, max is 1000) + * + * @return array with error message or the rate limit details + * @throws \Exception + */ + public function orderAmendments(string $symbol, string $orderId, ?string $fromExecutionId = null, ?int $limit = null, array $params = []) { + $request = [ + 'symbol' => $symbol, + 'orderId' => $orderId, + ]; - $this->assertEquals($this->symbol, $params['symbol']); + if ($fromExecutionId) { + $request['fromExecutionId'] = $fromExecutionId; + } + + if (!is_null($limit)) { + $request['limit'] = $limit; + } + + return $this->apiRequest("v3/order/amendments", 'GET', array_merge($request, $params), true); + } + + /** + * ocoOrder - Create a new OCO order + * + * @link https://binance-docs.github.io/apidocs/spot/en/#new-oco-trade + * + * @property int $weight 1 + * + * @param string $side (mandatory) Should be SELL or BUY + * @param string $symbol (mandatory) The symbol, e.g. BTCBUSD + * @param float $quantity (mandatory) Quantity to buy/sell + * @param int $price (mandatory) Price + * @param int $stopprice (mandatory) Stop Price + * @param int $stoplimitprice (optional) Stop Limit Price + * @param int $stoplimittimeinforce (optional) GTC, FOK or IOC + * @param array $params (optional) Extra flags/parameters + * + * @return array containing the response + * @throws \Exception + */ + public function ocoOrder(string $side, string $symbol, $quantity, $price, $stopprice, $stoplimitprice = null, $stoplimittimeinforce = 'GTC', array $params = []) + { + $request = [ + "symbol" => $symbol, + "side" => $side, + ]; - } + if (is_numeric($quantity) === false) { + $error = "Parameter quantity expected numeric for ' $side . ' ' . $symbol .', got " . gettype($quantity); + trigger_error($error, E_USER_ERROR); + } else { + $request['quantity'] = $quantity; + } - public function testWithdraw() - { - try { - $this->binance->withdraw($this->asset, $this->address, $this->amount, $this->addressTag, $this->addressName, $this->transactionFeeFlag, $this->network, $this->orderId); + if (is_numeric($price) === false) { + $error = "Parameter price expected numeric for ' $side . ' ' . $symbol .', got " . gettype($price); + trigger_error($error, E_USER_ERROR); + } else { + $request['price'] = $price; + } - } catch (\Throwable $e) { + if (is_numeric($stopprice) === false) { + $error = "Parameter stopprice expected numeric for ' $side . ' ' . $symbol .', got " . gettype($stopprice); + trigger_error($error, E_USER_ERROR); + } else { + $request['stopPrice'] = $stopprice; + } + if (is_null($stoplimitprice) === false && empty($stoplimitprice) === false) { + $request['stopLimitPrice'] = $stoplimitprice; + if ( ($stoplimittimeinforce == 'FOK') || ($stoplimittimeinforce == 'IOC') ) { + $request['stopLimitTimeInForce'] = $stoplimittimeinforce; + } else { + $request['stopLimitTimeInForce'] = 'GTC'; // `Good 'till cancel`. Needed if flag `stopLimitPrice` used. + } } - $this->assertEquals("https://api.binance.com/sapi/v1/capital/withdraw/apply", self::$capturedUrl); - parse_str(self::$capturedBody, $params); + // Check other flags + foreach (array('icebergQty','stopIcebergQty','listClientOrderId','limitClientOrderId','stopClientOrderId','newOrderRespType') as $flag) { + if ( isset($params[$flag]) && !empty($params[$flag]) ) + $request[$flag] = $params[$flag]; + } - $this->assertEquals($this->asset, $params['coin']); - $this->assertEquals($this->address, $params['address']); - $this->assertEquals($this->amount, $params['amount']); - $this->assertEquals($this->addressTag, $params['addressTag']); - $this->assertEquals($this->addressName, $params['name']); - $this->assertEquals($this->transactionFeeFlag, $params['transactionFeeFlag']); - $this->assertEquals($this->network, $params['network']); - $this->assertEquals($this->orderId, $params['withdrawOrderId']); + return $this->apiRequest("v3/order/oco", "POST", $request, true); } - public function testDepositAddress() + /** + * avgPrice - get the average price of a symbol based on the last 5 minutes + * + * @link https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints + * + * $avgPrice = $api->avgPrice( "ETHBTC" ); + * + * @property int $weight 1 + * + * @param string $symbol (mandatory) a symbol, e.g. ETHBTC + * + * @return string with symbol price + * @throws \Exception + */ + public function avgPrice(string $symbol, array $params = []) { - try { - $this->binance->depositAddress($this->asset, $this->network); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/sapi/v1/capital/deposit/address?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + $request = [ + 'symbol' => $symbol, + ]; + $ticker = $this->apiRequest("v3/avgPrice", "GET", array_merge($request, $params)); + if (is_array($ticker) === false) { + echo "Error: unable to fetch avg price" . PHP_EOL; + $ticker = []; + } + if (empty($ticker)) { + echo "Error: avg price was empty" . PHP_EOL; + return null; + } + return $ticker['price']; + } + + + /********************************************* + * + * Binance Liquid Swap (bswap) functions + * + * https://binance-docs.github.io/apidocs/spot/en/#bswap-endpoints + * + *********************************************/ + + /** + * bswapQuote - Request a quote for swap of quote asset (selling) or base asset (buying), essentially price/exchange rates. + * + * @property int $weight 2 + * + * @param string $baseAsset (mandatory) e.g. ETH + * @param string $quoteAsset (mandatory) e.g. BTC + * @param string $quoteQty (mandatory) + * + * @return array containing the response + * @throws \Exception + */ + public function bswapQuote($baseAsset, $quoteAsset, $quoteQty, array $params = []) { + $request = [ + 'quoteAsset' => $quoteAsset, + 'baseAsset' => $baseAsset, + 'quoteQty' => $quoteQty, + ]; - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); + return $this->sapiRequest("v1/bswap/quote", 'GET', array_merge($request, $params), true); + } + + /********************************************* + * + * Binance futures (fapi) functions + * + * https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info + * + *********************************************/ + + /** + * futuresTime Gets the server time + * + * $time = $api->futuresTime(); + * + * @return array with error message or array with server time key + * @throws \Exception + */ + public function futuresTime(array $params = []) + { + return $this->fapiRequest("v1/time", "GET", $params); + } + + /** + * futuresExchangeInfo - Gets the complete exchange info, including limits, currency options etc. for futures + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Exchange-Information + * + * $info = $api->futuresExchangeInfo(); + * + * @property int $weight 1 + * + * @return array containing the response + * @throws \Exception + */ + public function futuresExchangeInfo(array $params = []) + { + if (!$this->futuresExchangeInfo) { + $arr = $this->fapiRequest("v1/exchangeInfo", "GET", $params); + if ((is_array($arr) === false) || empty($arr)) { + echo "Error: unable to fetch futures exchange info" . PHP_EOL; + $arr = array(); + $arr['symbols'] = array(); + } + $this->futuresExchangeInfo = $arr; + $this->futuresExchangeInfo['symbols'] = null; + + foreach ($arr['symbols'] as $key => $value) { + $this->futuresExchangeInfo['symbols'][$value['symbol']] = $value; + } + } + + return $this->futuresExchangeInfo; + } + + /** + * futuresDepth get Market depth for futures + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Order-Book + * + * $depth = $api->futuresDepth("ETHBTC"); + * + * @property int $weight 10 + * for limit 5, 10, 20, 50 - weight 2 + * for limit 100 - weight 5 + * for limit 500 (default) - weight 10 + * for limit 1000 - weight 20 + * + * @param string $symbol (mandatory) the symbol to get the depth information for, e.g. ETHUSDT + * @param int $limit (optional) $limit set limition for number of market depth data, default 500, max 1000 (possible values are 5, 10, 20, 50, 100, 500, 1000) + * @return array with error message or array of market depth + * @throws \Exception + */ + public function futuresDepth(string $symbol, ?int $limit = null, array $params = []) + { + if (isset($symbol) === false || is_string($symbol) === false) { + // WPCS: XSS OK. + echo "asset: expected bool false, " . gettype($symbol) . " given" . PHP_EOL; + } + + $request = [ + 'symbol' => $symbol, + ]; + if ($limit) { + $request['limit'] = $limit; + } + $json = $this->fapiRequest("v1/depth", "GET", array_merge($request, $params)); + if (is_array($json) === false) { + echo "Error: unable to fetch futures depth" . PHP_EOL; + $json = []; + } + if (empty($json)) { + echo "Error: futures depth were empty" . PHP_EOL; + return $json; + } + if (isset($this->info[$symbol]) === false) { + $this->info[$symbol] = []; + $this->info[$symbol]['futures'] = []; + } + $this->info[$symbol]['futures']['firstUpdate'] = $json['lastUpdateId']; + return $this->depthData($symbol, $json, 'futures'); + } + + /** + * futuresRecentTrades - Get recent trades for a specific currency + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Recent-Trades-List + * + * $trades = $api->futuresRecentTrades("ETHBTC"); + * + * @property int $weight 5 + * + * @param string $symbol (mandatory) the symbol to query, e.g. ETHUSDT + * @param int $limit (optional) limit the amount of trades, default 500, max 1000 + * + * @return array containing the response + * @throws \Exception + */ + public function futuresRecentTrades(string $symbol, ?int $limit = null, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + if ($limit) { + $request['limit'] = $limit; + } + return $this->fapiRequest("v1/trades", "GET", array_merge($request, $params)); + } + + /** + * futuresHistoricalTrades - Get historical trades for a specific currency + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Old-Trades-Lookup + * + * $trades = $api->futuresHistoricalTrades("ETHBTC"); + * + * @property int $weight 20 + * + * @param string $symbol (mandatory) the symbol to query, e.g. ETHUSDT + * @param int $limit (optional) limit the amount of trades, default 100, max 500 + * @param int $tradeId (optional) return the orders from this orderId onwards, negative to get recent ones + * + * @return array containing the response + * @throws \Exception + */ + public function futuresHistoricalTrades(string $symbol, $limit = null, $tradeId = null, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + if ($limit) { + $request['limit'] = $limit; + } + if ($tradeId) { + $request['fromId'] = $tradeId; + } + return $this->fapiRequest("v1/historicalTrades", "GET", array_merge($request, $params), true); + } + + /** + * futuresAggTrades get Market History / Aggregate Trades + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Compressed-Aggregate-Trades-List + * + * $trades = $api->futuresAggTrades("BNBBTC"); + * + * @property int $weight 20 + * + * @param string $symbol (mandatory) the symbol to get the trade information for, e.g. ETHUSDT + * @param int $fromId (optional) ID to get aggregate trades from INCLUSIVE + * @param int $startTime (optional) timestamp in ms to get aggregate trades from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get aggregate trades until INCLUSIVE + * @param int $limit (optional) the amount of trades, default 500, max 1000 + * + * @return array with error message or array of market history + * @throws \Exception + */ + public function futuresAggTrades(string $symbol, ?int $fromId = null, ?int $startTime = null, ?int $endTime = null, ?int $limit = null, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + if ($fromId) { + $request['fromId'] = $fromId; + } + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + if ($limit) { + $request['limit'] = $limit; + } + return $this->tradesData($this->fapiRequest("v1/aggTrades", "GET", array_merge($request, $params))); + } + + /** + * futuresCandlesticks get the candles for the given intervals + * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Kline-Candlestick-Data + * + * $candles = $api->futuresCandlesticks("BNBBTC", "5m"); + * + * @property int $weight 5 + * for limit < 100 - weight 1 + * for limit < 500 - weight 2 + * for limit <= 1000 - weight 5 + * for limit > 1000 - weight 10 + * + * @param string $symbol (mandatory) the symbol to query, e.g. ETHUSDT + * @param string $interval (optional) string to request - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M (default 5m) + * @param int $limit (optional) int limit the amount of candles (default 500, max 1000) + * @param int $startTime (optional) string request candle information starting from here + * @param int $endTime (optional) string request candle information ending here + * + * @return array containing the response + * @throws \Exception + */ + public function futuresCandlesticks(string $symbol, string $interval = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + return $this->futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, 'klines', null, $params); + } + + /** + * futuresContinuousCandlesticks get the candles for the given intervals + * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Continuous-Contract-Kline-Candlestick-Data + * + * $candles = $api->futuresContinuousCandlesticks("BNBBTC", "5m"); + * + * @property int $weight 5 + * for limit < 100 - weight 1 + * for limit < 500 - weight 2 + * for limit <= 1000 - weight 5 + * for limit > 1000 - weight 10 + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $interval (optional) string to request - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M (default 5m) + * @param int $limit (optional) int limit the amount of candles (default 500, max 1000) + * @param int $startTime (optional) string request candle information starting from here + * @param int $endTime (optional) string request candle information ending here + * @param string $contractType (optional) string to request - PERPETUAL, CURRENT_QUARTER, NEXT_QUARTER (default PERPETUAL) + * + * @return array containing the response + * @throws \Exception + */ + public function futuresContinuousCandlesticks(string $symbol, string $interval = '5m', ?int $limit = null, $startTime = null, $endTime = null, $contractType = 'PERPETUAL', array $params = []) + { + return $this->futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, 'continuousKlines', $contractType, $params); + } + + /** + * futuresIndexPriceCandlesticks get the candles for the given intervals + * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Index-Price-Kline-Candlestick-Data + * + * $candles = $api->futuresIndexPriceCandlesticks("BNBBTC", "5m"); + * + * @property int $weight 5 + * for limit < 100 - weight 1 + * for limit < 500 - weight 2 + * for limit <= 1000 - weight 5 + * for limit > 1000 - weight 10 + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $interval (optional) string to request - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M (default 5m) + * @param int $limit (optional) int limit the amount of candles (default 500, max 1000) + * @param int $startTime (optional) string request candle information starting from here + * @param int $endTime (optional) string request candle information ending here + * + * @return array containing the response + * @throws \Exception + */ + public function futuresIndexPriceCandlesticks(string $symbol, string $interval = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + return $this->futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, 'indexPriceKlines', null, $params); + } + + /** + * futuresMarkPriceCandlesticks get the candles for the given intervals + * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Mark-Price-Kline-Candlestick-Data + * + * $candles = $api->futuresMarkPriceCandlesticks("BNBBTC", "5m"); + * + * @property int $weight 5 + * for limit < 100 - weight 1 + * for limit < 500 - weight 2 + * for limit <= 1000 - weight 5 + * for limit > 1000 - weight 10 + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $interval (optional) string to request - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M (default 5m) + * @param int $limit (optional) int limit the amount of candles (default 500, max 1000) + * @param int $startTime (optional) string request candle information starting from here + * @param int $endTime (optional) string request candle information ending here + * + * @return array containing the response + * @throws \Exception + */ + public function futuresMarkPriceCandlesticks(string $symbol, string $interval = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + return $this->futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, 'markPriceKlines', null, $params); + } + + /** + * futuresPremiumIndexCandlesticks get the candles for the given intervals + * 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Premium-Index-Kline-Data + * + * $candles = $api->futuresPremiumIndexCandlesticks("ETHBTC", "5m"); + * + * @property int $weight 5 + * for limit < 100 - weight 1 + * for limit < 500 - weight 2 + * for limit <= 1000 - weight 5 + * for limit > 1000 - weight 10 + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $interval (optional) string to request - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M (default 5m) + * @param int $limit (optional) int limit the amount of candles (default 500, max 1000) + * @param int $startTime (optional) string request candle information starting from here + * @param int $endTime (optional) string request candle information ending here + * + * @return array containing the response + * @throws \Exception + */ + public function futuresPremiumIndexCandlesticks(string $symbol, string $interval = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + return $this->futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, 'premiumIndexKlines', null, $params); + } + + /** + * futuresCandlesticksHelper + * helper for routing the futuresCandlesticks, futuresContinuousCandlesticks, futuresIndexPriceCandlesticks, futuresMarkPriceCandlesticks and futuresPremiumIndexKlines + */ + private function futuresCandlesticksHelper($symbol, $interval, $limit, $startTime, $endTime, $klineType, $contractType = null, $params = []) + { + if (!isset($this->charts['futures'])) { + $this->charts['futures'] = []; + } + if (!isset($this->charts['futures'][$symbol])) { + $this->charts['futures'][$symbol] = []; + } + if (!isset($this->charts['futures'][$symbol][$contractType])) { + $this->charts['futures'][$symbol][$contractType] = []; + } + if (!isset($this->charts['futures'][$symbol][$contractType][$interval])) { + $this->charts['futures'][$symbol][$contractType][$interval] = []; + } + $request = [ + 'interval' => $interval, + ]; + if ($klineType === 'continuousKlines' || $klineType === 'indexPriceKlines') { + $request['pair'] = $symbol; + } else { + $request['symbol'] = $symbol; + } + if ($limit) { + $request['limit'] = $limit; + } + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + if ($contractType) { + $request['contractType'] = $contractType; + } + + $response = $this->fapiRequest("v1/{$klineType}", 'GET', array_merge($request, $params)); + + if (is_array($response) === false) { + return []; + } + if (count($response) === 0) { + echo "warning: fapi/v1/{$klineType} returned empty array, usually a blip in the connection or server" . PHP_EOL; + return []; + } + + $candlesticks = $this->chartData($symbol, $interval, $response, 'futures', $klineType); + $this->charts['futures'][$symbol][$klineType][$interval] = $candlesticks; + return $candlesticks; + } + + /** + * futuresMarkPrice get the mark price for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Mark-Price + * + * $markPrice = $api->futuresMarkPrice(); + * $markPrice = $api->futuresMarkPrice("ETHBTC"); + * + * @property int $weight 1 + * + * @param string $symbol (optional) market symbol to get the response for, e.g. ETHUSDT + * + * @return array containing the response + * @throws \Exception + */ + public function futuresMarkPrice(?string $symbol = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + return $this->fapiRequest("v1/premiumIndex", "GET", array_merge($request, $params)); + } + + /** + * futuresFundingRateHistory get the funding rate history for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-History + * + * $fundingRate = $api->futuresFundingRateHistory(); + * $fundingRate = $api->futuresFundingRateHistory("ETHBTC"); + * + * @param string $symbol (optional) market symbol to get the response for, e.g. ETHUSDT + * @param int $limit (optional) int limit the amount of funding rate history (default 100, max 1000) + * @param int $startTime (optional) string request funding rate history starting from here + * @param int $endTime (optional) string request funding rate history ending here + * + * @return array containing the response + * @throws \Exception + */ + public function futuresFundingRateHistory(?string $symbol = null, ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + if ($limit) { + $request['limit'] = $limit; + } + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + return $this->fapiRequest("v1/fundingRate", "GET", array_merge($request, $params)); + } + + /** + * futuresFundingInfo get the funding rate history for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-Info + * + * $fundingInfo = $api->futuresFundingInfo(); + * + * @property int $weight 1 + * + * @return array containing the response + * @throws \Exception + */ + public function futuresFundingInfo(array $params = []) + { + return $this->fapiRequest("v1/fundingInfo", "GET", $params); + } + + /** + * futuresPrevDay get 24 hour price change statistics for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/24hr-Ticker-Price-Change-Statistics + * + * $ticker = $api->futuresPrevDay(); + * $ticker = $api->futuresPrevDay("ETHBTC"); + * + * @property int $weight 1 + * if the symbol parameter is omitted weight is 40 + * + * @param string $symbol (optional) market symbol to get the response for, e.g. ETHUSDT + * + * @return array containing the response + * @throws \Exception + */ + public function futuresPrevDay(?string $symbol = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + return $this->fapiRequest("v1/ticker/24hr", "GET", array_merge($request, $params)); + } + + /** + * futuresPrice get the latest price for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Price-Ticker + * + * $price = $api->futuresPrice('ETHUSDT'); + * + * @property int $weight 1 + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * + * @return array containing the response + * @throws \Exception + */ + public function futuresPrice(string $symbol, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + $ticker = $this->fapiRequest("v1/ticker/price", "GET", array_merge($request, $params)); + if (!isset($ticker['price'])) { + echo "Error: unable to fetch futures price for $symbol" . PHP_EOL; + return null; + } + return $ticker['price']; + } + + /** + * futuresPrices get the latest price for all symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Price-Ticker + * + * $price = $api->futuresPrices(); + * + * @property int $weight 2 + * + * @return array containing the response + * @throws \Exception + */ + public function futuresPrices(array $params = []) + { + return $this->priceData($this->fapiRequest("v1/ticker/price", "GET", $params)); + } + + /** + * futuresPriceV2 get the latest price for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Price-Ticker-v2 + * + * $price = $api->futuresPriceV2('ETHBTC'); + * + * @property int $weight 1 + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * + * @return array containing the response + * @throws \Exception + */ + public function futuresPriceV2(string $symbol, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + $ticker = $this->fapiRequest("v2/ticker/price", "GET", $request); + if (!isset($ticker['price'])) { + echo "Error: unable to fetch futures price for $symbol" . PHP_EOL; + return null; + } + return $ticker['price']; + } + + /** + * futuresPricesV2 get the latest price for all symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Price-Ticker-v2 + * + * $price = $api->futuresPricesV2(); + * + * @property int $weight 2 + * + * @return array containing the response + * @throws \Exception + */ + public function futuresPricesV2(array $params = []) + { + return $this->priceData($this->fapiRequest("v2/ticker/price", "GET", $params)); + } + + /** + * futuresSymbolOrderBookTicker get the best price/qty on the order book for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Order-Book-Ticker + * + * $ticker = $api->futuresSymbolOrderBookTicker(); + * $ticker = $api->futuresSymbolOrderBookTicker("ETHBTC"); + * + * @property int $weight 2 + * 5 when the symbol parameter is omitted + * + * @param string $symbol (optional) market symbol to get the response for, e.g. ETHUSDT + * + * @return array containing the response + * @throws \Exception + */ + public function futuresSymbolOrderBookTicker(?string $symbol = null, array $params = []): array + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + return $this->fapiRequest("v1/ticker/bookTicker", "GET", array_merge($request, $params)); + } + + /** + * futuresDeliveryPrice get the latest delivery price for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Delivery-Price + * + * $price = $api->futuresDeliveryPrice("ETHBTC"); + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * + * @return array containing the response + * @throws \Exception + */ + public function futuresDeliveryPrice(string $symbol, array $params = []): array + { + $request = [ + 'pair' => $symbol, + ]; - $this->assertEquals($this->asset, $params['coin']); - $this->assertEquals($this->network, $params['network']); + return $this->fapiDataRequest("delivery-price", "GET", array_merge($request, $params)); + } + + /** + * futuresOpenInterest get the open interest for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest + * + * $openInterest = $api->futuresOpenInterest("ETHBTC"); + * + * @property int $weight 1 + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * + * @return array containing the response + * @throws \Exception + */ + public function futuresOpenInterest(string $symbol, array $params = []): array + { + $request = [ + 'symbol'=> $symbol, + ]; + return $this->fapiRequest("v1/openInterest", 'GET', array_merge($request, $params)); } - public function testDepositHistory() + /** + * sapieriodLimitStartEndFuturesDataRequest + * helper for routing GET methods that require symbol, period, limit, startTime and endTime + */ + private function sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, $url, array $params = []) { - try { - $this->binance->depositHistory($this->asset); - - } catch (\Throwable $e) { + $request = [ + 'symbol' => $symbol, + 'period' => $period, + ]; + if ($limit) { + $request['limit'] = $limit; + } + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + return $this->fapiDataRequest($url, 'GET', array_merge($request, $params)); + } + + /** + * futuresOpenInterestHistory get the open interest history for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest-Statistics + * + * $openInterest = $api->futuresOpenInterestHistory("ETHBTC", 5m); + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) + * @param int $limit (optional) int limit the amount of open interest history (default 100, max 1000) + * @param int $startTime (optional) string request open interest history starting from here + * @param int $endTime (optional) string request open interest history ending here + * + * @return array containing the response + * @throws \Exception + */ + public function futuresOpenInterestHistory(string $symbol, string $period = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + return $this->sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, 'openInterestHist', $params); + } + + /** + * futuresTopLongShortPositionRatio get the proportion of net long and net short positions to total open positions of the top 20% users with the highest margin balance + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Top-Trader-Long-Short-Ratio + * + * $ratio = $api->futuresTopLongShortPositionRatio("ETHBTC", 5m); + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) + * @param int $limit (optional) int limit the amount of open interest history (default 30, max 500) + * @param int $startTime (optional) string request open interest history starting from here + * @param int $endTime (optional) string request open interest history ending here + * + * @return array containing the response + * @throws \Exception + */ + public function futuresTopLongShortPositionRatio(string $symbol, string $period = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + return $this->sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, 'topLongShortPositionRatio', $params); + } + + /** + * futuresTopLongShortAccountRatio get the proportion of net long and net short accounts to total accounts of the top 20% users with the highest margin balance + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Top-Long-Short-Account-Ratio + * + * $ratio = $api->futuresTopLongShortAccountRatio("ETHBTC", 5m); + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) + * @param int $limit (optional) int limit the amount of open interest history (default 30, max 500) + * @param int $startTime (optional) string request open interest history starting from here + * @param int $endTime (optional) string request open interest history ending here + * + * @return array containing the response + * @throws \Exception + */ + public function futuresTopLongShortAccountRatio(string $symbol, string $period = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + return $this->sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, 'topLongShortAccountRatio', $params); + } + + /** + * futuresGlobalLongShortAccountRatio get the Long/Short Ratio for symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Long-Short-Ratio + * + * $ratio = $api->futuresGlobalLongShortAccountRatio("ETHBTC", 5m); + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) + * @param int $limit (optional) int limit the amount of open interest history (default 30, max 500) + * @param int $startTime (optional) string request open interest history starting from here + * @param int $endTime (optional) string request open interest history ending here + * + * @return array containing the response + * @throws \Exception + */ + public function futuresGlobalLongShortAccountRatio(string $symbol, string $period = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + return $this->sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, 'globalLongShortAccountRatio', $params); + } + + /** + * futuresTakerLongShortRatio get the taker Long/Short Ratio for symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Taker-BuySell-Volume + * + * $ratio = $api->futuresTakerLongShortRatio("ETHBTC", 5m); + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) + * @param int $limit (optional) int limit the amount of open interest history (default 30, max 500) + * @param int $startTime (optional) string request open interest history starting from here + * @param int $endTime (optional) string request open interest history ending here + * + * @return array containing the response + * @throws \Exception + */ + public function futuresTakerLongShortRatio(string $symbol, string $period = '5m', ?int $limit = null, $startTime = null, $endTime = null, array $params = []) + { + return $this->sapieriodLimitStartEndFuturesDataRequest($symbol, $period, $limit, $startTime, $endTime, 'takerlongshortRatio', $params); + } + + + /** + * futuresBasis get future basis for symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Basis + * + * $basis = $api->futuresBasis("ETHBTC", 5m); + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $period (optional) string of period to query - 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d (default 5m) + * @param int $limit (optional) int limit the amount of open interest history (default 30, max 500) + * @param int $startTime (optional) string request open interest history starting from here + * @param int $endTime (optional) string request open interest history ending here + * @param string $contractType (optional) string of period to query - PERPETUAL, CURRENT_QUARTER, NEXT_QUARTER (default PERPETUAL) + * + * @return array containing the response + * @throws \Exception + */ + public function futuresBasis(string $symbol, string $period = '5m', int $limit = 30, $startTime = null, $endTime = null, $contractType = 'PERPETUAL', array $params = []) + { + $request = [ + 'pair' => $symbol, + 'period' => $period, + 'contractType' => $contractType, + ]; + if ($limit) { + $request['limit'] = $limit; + } + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + return $this->fapiDataRequest("basis", 'GET', array_merge($request, $params)); + } + + /** + * futuresIndexInfo get composite index symbol information + * only for composite index symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Composite-Index-Symbol-Information + * + * $indexInfo = $api->futuresIndexInfo("DEFIUSDT"); + * + * @property int $weight 1 + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. DEFIUSDT + * + * @return array containing the response + * @throws \Exception + */ + public function futuresIndexInfo(string $symbol, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + return $this->fapiRequest("v1/indexInfo", 'GET', array_merge($request, $params)); + } + + /** + * asset assetIndex get the asset index for a symbol or all symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Multi-Assets-Mode-Asset-Index + * + * $assetIndex = $api->futuresAssetIndex(); + * $assetIndex = $api->futuresAssetIndex("USDCUSD"); + * + * @property int $weight 1 + * with symbol parameter omitted weight is 10 + * + * @param string $symbol (optional) market symbol to get the response for, e.g. USDCUSD + * + * @return array containing the response + * @throws \Exception + */ + public function futuresAssetIndex(?string $symbol = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + return $this->fapiRequest("v1/assetIndex", 'GET', array_merge($request, $params)); + } + + /** + * futuresConstituents get the index price constituents + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Index-Constituents + * + * $constituents = $api->futuresConstituents("BTCUSDT"); + * + * @property int $weight 2 + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * + * @return array containing the response + * @throws \Exception + */ + public function futuresConstituents(string $symbol, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + return $this->fapiRequest("v1/constituents", 'GET', array_merge($request, $params)); + } + + /** + * createFuturesOrderRequest + * helper for creating the request for futures order + * @return array containing the request + * @throws \Exception + */ + protected function createFuturesOrderRequest(string $side, string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = []) { + $request = [ + 'symbol' => $symbol, + 'side' => $side, + 'type' => $type, + ]; + // someone has preformated there 8 decimal point double already + // dont do anything, leave them do whatever they want + if ($price && gettype($price) !== 'string') { + // for every other type, lets format it appropriately + $price = number_format($price, 8, '.', ''); + } + + if ($quantity) { + if (is_numeric($quantity) === false) { + // WPCS: XSS OK. + echo "warning: quantity expected numeric got " . gettype($quantity) . PHP_EOL; + } + if (isset($params['closePosition']) && $params['closePosition'] === true) { + // WPCS: XSS OK. + echo "warning: closePosition is set to true, quantity will be ignored" . PHP_EOL; + } else { + $request['quantity'] = $quantity; + } + } + + if ($price && is_string($price) === false) { + // WPCS: XSS OK. + echo "warning: price expected string got " . gettype($price) . PHP_EOL; + } + + if ($type === "LIMIT" || $type === "STOP_LOSS_LIMIT" || $type === "TAKE_PROFIT_LIMIT" || $type === "STOP" || $type === "TAKE_PROFIT") { + $request["price"] = $price; + if (!isset($params['timeInForce'])) { + $request['timeInForce'] = 'GTC'; + } + } + + if (isset($params['reduceOnly'])) { + $reduceOnly = $params['reduceOnly']; + if ($reduceOnly === true) { + $request['reduceOnly'] = 'true'; + } else { + $request['reduceOnly'] = 'false'; + } + unset($params['reduceOnly']); + } + + if (!isset($params['newClientOrderId'])) { + $request['newClientOrderId'] = $this->generateFuturesClientOrderId(); + } + + if (isset($params['closePosition'])) { + $closePosition = $params['closePosition']; + if ($closePosition === true) { + $request['closePosition'] = 'true'; + } else { + $request['closePosition'] = 'false'; + } + unset($params['closePosition']); + } + + if (isset($params['priceProtect'])) { + $priceProtect = $params['priceProtect']; + if ($priceProtect === true) { + $request['priceProtect'] = 'TRUE'; + } else { + $request['priceProtect'] = 'FALSE'; + } + unset($params['priceProtect']); + } + + return array_merge($request, $params); + } + + /** + * futuresOrder formats the orders before sending them to the curl wrapper function + * You can call this function directly or use the helper functions + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api + * + * @see futuresBuy() + * @see sell() + * @see marketBuy() + * @see marketSell() + * + * @param string $side (mandatory) typically "BUY" or "SELL" + * @param string $symbol (mandatory) market symbol + * @param string $quantity (optional) of the order (Cannot be sent with closePosition=true (Close-All)) + * @param string $price (optional) price per unit + * @param string $type (mandatory) is determined by the symbol bu typicall LIMIT, STOP_LOSS_LIMIT etc. + * @param array $params (optional) additional transaction options + * - @param string $params['positionSide'] position side, "BOTH" for One-way Mode; "LONG" or "SHORT" for Hedge Mode (mandatory for Hedge Mode) + * - @param string $params['timeInForce'] + * - @param bool $params['reduceOnly'] default false (Cannot be sent in Hedge Mode; cannot be sent with closePosition=true) + * - @param string $params['newClientOrderId'] new client order id + * - @param string $params['stopPrice'] stop price (Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders) + * - @param bool $params['closePosition'] Close-All (used with STOP_MARKET or TAKE_PROFIT_MARKET orders) + * - @param string $params['activationPrice'] Used with TRAILING_STOP_MARKET orders, default as the latest price (supporting different workingType) + * - @param string $params['callbackRate'] Used with TRAILING_STOP_MARKET orders, min 0.1, max 5 where 1 for 1% + * - @param string $params['workingType'] stopPrice triggered by: "MARK_PRICE", "CONTRACT_PRICE". Default "CONTRACT_PRICE" + * - @param bool $params['priceProtect'] Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders (default false) + * - @param string $params['newOrderRespType'] response type, default "RESULT", other option is "ACK" + * - @param string $params['priceMatch'] only avaliable for LIMIT/STOP/TAKE_PROFIT order; can be set to OPPONENT/ OPPONENT_5/ OPPONENT_10/ OPPONENT_20: /QUEUE/ QUEUE_5/ QUEUE_10/ QUEUE_20; Can't be passed together with price + * - @param string $params['selfTradePreventionMode'] EXPIRE_TAKER:expire taker order when STP triggers/ EXPIRE_MAKER:expire taker order when STP triggers/ EXPIRE_BOTH:expire both orders when STP triggers; default NONE + * - @param string $params['goodTillDate'] order cancel time for timeInForce GTD, mandatory when timeInforce set to GTD; order the timestamp only retains second-level precision, ms part will be ignored; The goodTillDate timestamp must be greater than the current time plus 600 seconds and smaller than 253402300799000 + * - @param int $params['recvWindow'] + * @param $test bool whether to test or not, test only validates the query + * @return array containing the response + * @throws \Exception + */ + public function futuresOrder(string $side, string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = [], $test = false) + { + $request = $this->createFuturesOrderRequest($side, $symbol, $quantity, $price, $type, $params); + $isAlgoOrder = $this->isAlgoFuturesOrderType($request['type']); + if ($isAlgoOrder) { + $algoRequest = $this->createAlgoFuturesOrderRequest($request); + return $this->fapiRequest('v1/algoOrder', 'POST', $algoRequest, true); + } else { + // regular order + $qstring = ($test === false) ? 'v1/order' : 'v1/order/test'; + return $this->fapiRequest($qstring, 'POST', $request, true); + } + } + + /** + * isAlgoFuturesOrderType helper to determine if an order type is an algo order + * @param string $type order type + * @return bool true if it is an algo order type + */ + protected function isAlgoFuturesOrderType(string $type): bool + { + $algoOrderTypes = [ + 'STOP', + 'STOP_MARKET', + 'TAKE_PROFIT', + 'TAKE_PROFIT_MARKET', + 'TRAILING_STOP_MARKET', + ]; + if (in_array($type, $algoOrderTypes)) { + return true; + } + return false; + } + + /** + * createAlgoFuturesOrderRequest + * helper for creating the request for algo futures order + * @param array $request regular order request + * @return array containing the request + */ + protected function createAlgoFuturesOrderRequest(array $request): array + { + $request['algoType'] = 'CONDITIONAL'; + if (!isset($request['clientAlgoId'])) { + $newClientOrderId = $request['newClientOrderId']; + $request['clientAlgoId'] = $newClientOrderId; + unset($request['newClientOrderId']); + } + if (!isset($request['triggerPrice']) && isset($request['stopPrice'])) { + $request['triggerPrice'] = $request['stopPrice']; + unset($request['stopPrice']); + } + return $request; + } + + /** + * futuresBuy attempts to create a buy order + * each market supports a number of order types, such as + * -LIMIT + * -MARKET + * -STOP_LOSS + * -STOP_LOSS_LIMIT + * -TAKE_PROFIT + * -TAKE_PROFIT_LIMIT + * -LIMIT_MAKER + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api + * + * You should check the @see exchangeInfo for each currency to determine + * what types of orders can be placed against specific pairs + * + * $quantity = 1; + * $price = 0.0005; + * $order = $api->futuresBuy("BNBBTC", $quantity, $price); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param string $quantity (optional) the quantity required + * @param string $price (optional) price per unit + * @param string $type (mandatory) type of order + * @param array $params (optional) addtional options for order type + * - @param string $params['positionSide'] position side, "BOTH" for One-way Mode; "LONG" or "SHORT" for Hedge Mode (mandatory for Hedge Mode) + * - @param string $params['timeInForce'] + * - @param bool $params['reduceOnly'] default false (Cannot be sent in Hedge Mode; cannot be sent with closePosition=true) + * - @param string $params['newClientOrderId'] new client order id + * - @param string $params['stopPrice'] stop price (Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders) + * - @param bool $params['closePosition'] Close-All (used with STOP_MARKET or TAKE_PROFIT_MARKET orders) + * - @param string $params['activationPrice'] Used with TRAILING_STOP_MARKET orders, default as the latest price (supporting different workingType) + * - @param string $params['callbackRate'] Used with TRAILING_STOP_MARKET orders, min 0.1, max 5 where 1 for 1% + * - @param string $params['workingType'] stopPrice triggered by: "MARK_PRICE", "CONTRACT_PRICE". Default "CONTRACT_PRICE" + * - @param bool $params['priceProtect'] Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders (default false) + * - @param string $params['newOrderRespType'] response type, default "RESULT", other option is "ACK" + * - @param string $params['priceMatch'] only avaliable for LIMIT/STOP/TAKE_PROFIT order; can be set to OPPONENT/ OPPONENT_5/ OPPONENT_10/ OPPONENT_20: /QUEUE/ QUEUE_5/ QUEUE_10/ QUEUE_20; Can't be passed together with price + * - @param string $params['selfTradePreventionMode'] EXPIRE_TAKER:expire taker order when STP triggers/ EXPIRE_MAKER:expire taker order when STP triggers/ EXPIRE_BOTH:expire both orders when STP triggers; default NONE + * - @param string $params['goodTillDate'] order cancel time for timeInForce GTD, mandatory when timeInforce set to GTD; order the timestamp only retains second-level precision, ms part will be ignored; The goodTillDate timestamp must be greater than the current time plus 600 seconds and smaller than 253402300799000 + * - @param int $params['recvWindow'] + * @return array with error message or the order details + */ + public function futuresBuy(string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = []) + { + return $this->futuresOrder('BUY', $symbol, $quantity, $price, $type, $params); + } + + /** + * futuresBuyTest attempts to create a TEST futures buy order + * + * @see futuresBuy() + * + * params and return value are the same as @see futuresBuy() + */ + public function futuresBuyTest(string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = []) + { + return $this->futuresOrder('BUY', $symbol, $quantity, $price, $type, $params, true); + } + + /** + * futuresSell creates a futures sell order + * each market supports a number of order types, such as + * -LIMIT + * -MARKET + * -STOP_LOSS + * -STOP_LOSS_LIMIT + * -TAKE_PROFIT + * -TAKE_PROFIT_LIMIT + * -LIMIT_MAKER + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api + * + * You should check the @see exchangeInfo for each currency to determine + * what types of orders can be placed against specific pairs + * + * $quantity = 1; + * $price = 0.0005; + * $order = $api->futuresSell("BNBBTC", $quantity, $price); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param string $quantity (optional) the quantity required + * @param string $price (optional) price per unit + * @param string $type (mandatory) type of order + * @param array $params (optional) addtional options for order type + * - @param string $params['positionSide'] position side, "BOTH" for One-way Mode; "LONG" or "SHORT" for Hedge Mode (mandatory for Hedge Mode) + * - @param string $params['timeInForce'] + * - @param bool $params['reduceOnly'] default false (Cannot be sent in Hedge Mode; cannot be sent with closePosition=true) + * - @param string $params['newClientOrderId'] new client order id + * - @param string $params['stopPrice'] stop price (Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders) + * - @param bool $params['closePosition'] Close-All (used with STOP_MARKET or TAKE_PROFIT_MARKET orders) + * - @param string $params['activationPrice'] Used with TRAILING_STOP_MARKET orders, default as the latest price (supporting different workingType) + * - @param string $params['callbackRate'] Used with TRAILING_STOP_MARKET orders, min 0.1, max 5 where 1 for 1% + * - @param string $params['workingType'] stopPrice triggered by: "MARK_PRICE", "CONTRACT_PRICE". Default "CONTRACT_PRICE" + * - @param bool $params['priceProtect'] Used with STOP/STOP_MARKET or TAKE_PROFIT/TAKE_PROFIT_MARKET orders (default false) + * - @param string $params['newOrderRespType'] response type, default "RESULT", other option is "ACK" + * - @param string $params['priceMatch'] only avaliable for LIMIT/STOP/TAKE_PROFIT order; can be set to OPPONENT/ OPPONENT_5/ OPPONENT_10/ OPPONENT_20: /QUEUE/ QUEUE_5/ QUEUE_10/ QUEUE_20; Can't be passed together with price + * - @param string $params['selfTradePreventionMode'] EXPIRE_TAKER:expire taker order when STP triggers/ EXPIRE_MAKER:expire taker order when STP triggers/ EXPIRE_BOTH:expire both orders when STP triggers; default NONE + * - @param string $params['goodTillDate'] order cancel time for timeInForce GTD, mandatory when timeInforce set to GTD; order the timestamp only retains second-level precision, ms part will be ignored; The goodTillDate timestamp must be greater than the current time plus 600 seconds and smaller than 253402300799000 + * - @param int $params['recvWindow'] + * @return array with error message or the order details + */ + public function futuresSell(string $symbol, $quantity = null, $price = null, string $type = 'LIMIT', array $params = []) + { + return $this->futuresOrder('SELL', $symbol, $quantity, $price, $type, $params); + } + + /** + * futuresSellTest attempts to create a TEST futures sell order + * + * @see futuresSell() + * + * params and return value are the same as @see futuresSell() + */ + public function futuresSellTest(string $symbol, $quantity = null, $price = null, $type = null, array $params = []) + { + if ($type === null) { + $type = 'LIMIT'; + } + return $this->futuresOrder('SELL', $symbol, $quantity, $price, $type, $params, true); + } + + /** + * createBatchOrdersRequest + * helper for creating the request for multiple futures orders + * @param array $orders (mandatory) array of orders to be placed + * objects in the array should contain literally the same keys as the @see futuresOrder but without the $params[recvWindow] + * + * @return array containing the request + * @throws \Exception + */ + protected function createBatchOrdersRequest(array $orders, bool $edit = false) + { + $formatedOrders = []; + for ($index = 0; $index < count($orders); $index++) { + $order = $orders[$index]; + if (!isset($order['quantity'])) { + $order['quantity'] = null; + } + if (!isset($order['price'])) { + $order['price'] = null; + } + if (!isset($order['params'])) { + $order['params'] = []; + } + if (!isset($order['type'])) { + $order['type'] = 'LIMIT'; + } + $formatedOrder = $this->createFuturesOrderRequest( + $order['side'], + $order['symbol'], + $order['quantity'], + $order['price'], + $order['type'], + $order['params'] + ); + if (isset($formatedOrder['recvWindow'])) { + // remove recvWindow from the order + unset($formatedOrder['recvWindow']); + } + if ($edit) { + if (isset($order['orderId'])) { + $formatedOrder['orderId'] = $order['orderId']; + } + if (isset($order['origClientOrderId'])) { + $formatedOrder['origClientOrderId'] = $order['origClientOrderId']; + } + unset($formatedOrder['type']); + unset($formatedOrder['newClientOrderId']); + } + $formatedOrders[$index] = $formatedOrder; + } + return $formatedOrders; + } + + /** + * futuresBatchOrders creates multiple orders in a single request + * max 5 orders + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Place-Multiple-Orders + * + * @param array $orders (mandatory) array of orders to be placed + * objects in the array should contain literally the same keys as the @see futuresOrder but without the $params['recvWindow'] + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response or error message + * @throws \Exception + */ + public function futuresBatchOrders(array $orders, array $params = []) + { + $formatedOrders = $this->createBatchOrdersRequest($orders); + if (count($formatedOrders) > 5) { + throw new \Exception('futuresBatchOrders(): max 5 orders allowed'); + } + if (count($formatedOrders) < 1) { + throw new \Exception('futuresBatchOrders(): at least 1 order required'); + } + $request = []; + + // current endpoint accepts orders list as a json string in the query string + $encodedOrders = json_encode($formatedOrders); + $url = 'v1/batchOrders?batchOrders=' . $encodedOrders; + return $this->fapiRequest($url, 'POST', array_merge($request, $params), true); + } + + /** + * futuresEditOrder edits the limit order + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Order + * + * @param string $symbol (mandatory) market symbol + * @param string $side (mandatory) "BUY" or "SELL" + * @param string $orderId (optional) order id to be modified (mandatory if $params['origClientOrderId'] is not set) + * @param string $quantity (optional) of the order (Cannot be sent for orders with closePosition=true (Close-All)) + * @param string $price (mandatory) price per unit + * @param array $params (optional) additional options + * - @param string $params['priceMatch'] only avaliable for LIMIT/STOP/TAKE_PROFIT order; can be set to OPPONENT/ OPPONENT_5/ OPPONENT_10/ OPPONENT_20: /QUEUE/ QUEUE_5/ QUEUE_10/ QUEUE_20; Can't be passed together with price + * - @param int $params['recvWindow'] + * - @param string $params['origClientOrderId'] client order id to be modified (mandatory if $orderId is not set) + * @return array containing the response + * @throws \Exception + */ + public function futuresEditOrder(string $symbol, string $side, string $quantity, string $price, $orderId = null, array $params = []) + { + $request = $this->createFuturesOrderRequest($side, $symbol, $quantity, $price, 'LIMIT', $params); + $origClientOrderId = null; + if (isset($params['origClientOrderId'])) { + $origClientOrderId = $params['origClientOrderId']; + $request['origClientOrderId'] = $origClientOrderId; + } + if (!$origClientOrderId && !$orderId) { + throw new \Exception('futuresEditOrder(): either orderId or origClientOrderId must be set'); + } + if ($orderId) { + $request['orderId'] = $orderId; + } + unset($request['type']); + unset($request['newClientOrderId']); + return $this->fapiRequest("v1/order", 'PUT', $request, true); + } + + /** + * futuresEditOrders edits the multiple limit orders + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Multiple-Orders + * + * @param array $orders (mandatory) array of orders to be modified + * objects in the array should contain literally the same keys as the @see futuresEditOrder but without the $params['recvWindow'] + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response or error message + * @throws \Exception + */ + public function futuresEditOrders(array $orders, array $params = []) + { + $formatedOrders = $this->createBatchOrdersRequest($orders, true); + $request = []; + + // current endpoint accepts orders list as a json string in the query string + $request['batchOrders'] = json_encode($formatedOrders); + return $this->fapiRequest("v1/batchOrders", 'PUT', array_merge($request, $params), true); + } + + /** + * futuresOrderAmendment get order modification history + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Get-Order-Modify-History + * + * $amendment = $api->futuresOrderAmendment("ETHUSDT"); + * + * @param string $symbol (mandatory) market symbol to get the response for, e.g. ETHUSDT + * @param string $orderId (optional) order id to get the response for + * @param string $origClientOrderId (optional) original client order id to get the response for + * @param int $startTime (optional) timestamp in ms to get modification history from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get modification history until INCLUSIVE + * @param int $limit (optional) limit the amount of open interest history (default 50, max 100) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresOrderAmendment(string $symbol, $orderId = null, $origClientOrderId = null, $startTime = null, $endTime = null, $limit = null, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + if ($orderId) { + $request['orderId'] = $orderId; + } + if ($origClientOrderId) { + $request['origClientOrderId'] = $origClientOrderId; + } + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + if ($limit) { + $request['limit'] = $limit; } - $endpoint = "https://api.binance.com/sapi/v1/capital/deposit/hisrec?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - $this->assertEquals($this->asset, $params['coin']); + return $this->fapiRequest("v1/orderAmendment", 'GET', array_merge($request, $params), true); } - public function testWithdrawHistory() + /** + * futuresCancel cancels a futures order + * + * $orderid = "123456789"; + * $order = $api->futuresCancel("BNBBTC", $orderid); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param string $orderid (optional) the orderid to cancel (mandatory if $params['origClientOrderId'] is not set) + * @param array $params (optional) additional options + * - @param string $params['origClientOrderId'] original client order id to cancel + * - @param int $params['recvWindow'] the time in milliseconds to wait for a response + * + * @return array with error message or the order details + * @throws \Exception + */ + public function futuresCancel(string $symbol, $orderid, $params = []) { - try { - $this->binance->withdrawHistory($this->asset); - - } catch (\Throwable $e) { + $request = [ + 'symbol' => $symbol, + ]; + if ($orderid) { + $request['orderId'] = $orderid; + } else if (!isset($params['origClientOrderId'])) { + throw new \Exception('futuresCancel(): either orderId or origClientOrderId must be set'); + } + return $this->fapiRequest("v1/order", 'DELETE', array_merge($request, $params), true); + } + + /** + * futuresAlgoCancel cancels a futures order + * + * $algoid = "123456789"; + * $order = $api->futuresCancel($algoid); + * + * @param string $algoid (optional) the algoid to cancel (mandatory if $params['clientalgoid'] is not set) + * @param array $params (optional) additional options + * - @param string $params['clientalgoid'] original client order id to cancel + * - @param int $params['recvWindow'] the time in milliseconds to wait for a response + * + * @return array with error message or the order details + * @throws \Exception + */ + public function futuresAlgoCancel($algoid, $params = []) + { + $request = []; + if ($algoid) { + $request['algoId'] = $algoid; + } else if (!isset($params['clientalgoid'])) { + throw new \Exception('futuresAlgoCancel(): either algoid or clientalgoid must be set'); + } + // todo: check camel-case or lower-case!!!! + return $this->fapiRequest("v1/algoOrder", 'DELETE', array_merge($request, $params), true); + } + + /** + * futuresCancelBatchOrders canceles multiple futures orders + * + * $orderIds = ["123456789", "987654321"]; + * $order = $api->futuresCancelBatchOrders("BNBBTC", $orderIds); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param array $orderIdList (optional) list of ids to cancel (mandatory if origClientOrderIdList is not set) + * @param array $origClientOrderIdList (optional) list of client order ids to cancel (mandatory if orderIdList is not set) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the orders details + * @throws \Exception + */ + public function futuresCancelBatchOrders(string $symbol, $orderIdList = null, $origClientOrderIdList = null, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + if ($orderIdList) { + $idsString = json_encode($orderIdList); + // remove quotes and spaces + $request['orderIdList'] = str_replace(' ', '', str_replace('"', '', str_replace("'", '', $idsString))); + } else if ($origClientOrderIdList) { + // remove spaces between the ids + $request['origClientOrderIdList'] = str_replace(', ', ',', json_encode($origClientOrderIdList)); + } else { + throw new \Exception('futuresCancelBatchOrders(): either orderIdList or origClientOrderIdList must be set'); + } + + return $this->fapiRequest("v1/batchOrders", 'DELETE', array_merge($request, $params), true); + } + + /** + * futuresCancelOpenOrders cancels all open futures orders for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Open-Orders + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Algo-Open-Orders + * $orders = $api->futuresCancelOpenOrders("BNBBTC"); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param bool $params['isAlgo'] true for canceling algo orders + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the orders details + * @throws \Exception + */ + public function futuresCancelOpenOrders(string $symbol, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + if (isset($params['isAlgo']) && ($params['isAlgo'] === true)) { + unset($params['isAlgo']); + return $this->fapiRequest("v1/algoOpenOrders", 'DELETE', array_merge($request, $params), true); + } else { + return $this->fapiRequest("v1/allOpenOrders", 'DELETE', array_merge($request, $params), true); + } + } + + /** + * futuresCancelOpenAlgoOrders cancels all open futures algo orders for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Algo-Open-Orders + * + * $orders = $api->futuresCancelOpenAlgoOrders("BNBBTC"); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the orders details + * @throws \Exception + */ + public function futuresCancelOpenAlgoOrders (string $symbol, array $params = []) + { + $params['isAlgo'] = true; + return $this->futuresCancelOpenOrders($symbol, $params); + } + + /** + * futuresCountdownCancelAllOrders cancels all open futures orders for a symbol at the end of the specified countdown + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Auto-Cancel-All-Open-Orders + * + * $orders = $api->futuresCountdownCancelAllOrders("BNBBTC", 10); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param int $countdownTime (mandatory) countdown in milliseconds (0 to stop the timer) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the orders details + * @throws \Exception + */ + public function futuresCountdownCancelAllOrders(string $symbol, int $countdownTime, array $params = []) + { + $request = [ + 'symbol' => $symbol, + 'countdownTime' => $countdownTime, + ]; + return $this->fapiRequest("v1/countdownCancelAll", 'POST', array_merge($request, $params), true); + } + + /** + * futuresOrderStatus gets the details of a futures order + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Order + * + * $order = $api->futuresOrderStatus("BNBBTC", "123456789"); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param string $orderId (optional) order id to get the response for (mandatory if origClientOrderId is not set) + * @param string $origClientOrderId (optional) original client order id to get the response for (mandatory if orderId is not set) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the order details + * @throws \Exception + */ + public function futuresOrderStatus(string $symbol, $orderId = null, $origClientOrderId = null, array $params = [], ) + { + $request = [ + 'symbol' => $symbol, + ]; + if ($orderId) { + $request['orderId'] = $orderId; + } else if ($origClientOrderId) { + $request['origClientOrderId'] = $origClientOrderId; + } else { + throw new \Exception('futuresOrderStatus(): either orderId or origClientOrderId must be set'); + } + + return $this->fapiRequest("v1/order", 'GET', array_merge($request, $params), true); + } + + /** + * futuresAlgoOrderStatus gets the details of a futures algo order + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Algo-Order + * + * $order = $api->futuresAlgoOrderStatus("123456789"); + * + * @param string $algoId (optional) order id to get the response for (mandatory if clientAlgoId is not set) + * @param string $clientAlgoId (optional) original client order id to get the response for (mandatory if algoId is not set) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the order details + * @throws \Exception + */ + public function futuresAlgoOrderStatus($algoId = null, $clientAlgoId = null, array $params = []) + { + $request = []; + if ($algoId) { + $request['algoId'] = $algoId; + } else if ($clientAlgoId) { + $request['clientAlgoId'] = $clientAlgoId; + } else { + throw new \Exception('futuresAlgoOrderStatus(): either algoId or clientAlgoId must be set'); + } + + return $this->fapiRequest("v1/algoOrder", 'GET', array_merge($request, $params), true); + } + + /** + * futuresAllOrders gets all orders for a symbol + * query time period must be less then 7 days (default as the recent 7 days) + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/All-Orders + * + * $orders = $api->futuresAllOrders("BNBBTC"); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param int $startTime (optional) timestamp in ms to get orders from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get orders until INCLUSIVE + * @param int $limit (optional) limit the amount of orders (default 500, max 1000) + * @param string $orderId (optional) order id to get the response from (if is set it will get orders >= that orderId) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param bool $isAlgo (optional) true for algo orders + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the orders details + * @throws \Exception + */ + public function futuresAllOrders(string $symbol, $startTime = null, $endTime = null, $limit = null, $orderId = null, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + if ($limit) { + $request['limit'] = $limit; + } + if (isset($params['isAlgo']) && $params['isAlgo'] === true) { + unset($params['isAlgo']); + if ($orderId) { + $request['algoId'] = $orderId; + } + return $this->fapiRequest("v1/allAlgoOrders", 'GET', array_merge($request, $params), true); + } else { + if ($orderId) { + $request['orderId'] = $orderId; + } + return $this->fapiRequest("v1/allOrders", 'GET', array_merge($request, $params), true); + } + } + + /** + * futuresAllAlgoOrders gets all open orders for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-All-Algo-Orders + * + * $orders = $api->futuresAllAlgoOrders("BNBBTC"); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param int $startTime (optional) timestamp in ms to get orders from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get orders until INCLUSIVE + * @param int $limit (optional) limit the amount of orders (default 500, max 1000) + * @param string $algoId (optional) order id to get the response from (if is set it will get orders >= that orderId) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the orders details + * @throws \Exception + */ + public function futuresAllAlgoOrders(string $symbol, $startTime = null, $endTime = null, $limit = null, $algoId = null, array $params = []) + { + $params['isAlgo'] = true; + return $this->futuresAllOrders($symbol, $startTime, $endTime, $limit, $algoId, $params); + } + + /** + * futuresOpenOrders gets all open orders for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Current-All-Open-Orders + * + * $orders = $api->futuresOpenOrders(); + * $orders = $api->futuresOpenOrders("BNBBTC"); + * + * @param string $symbol (optional) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param bool $params['isAlgo'] (optional) true for getting algo orders + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the orders details + * @throws \Exception + */ + public function futuresOpenOrders($symbol = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + if (isset($params['isAlgo']) && ($params['isAlgo'] === true)) { + unset($params['algo']); + return $this->fapiRequest("v1/openAlgoOrders", 'GET', array_merge($request, $params), true); + } + return $this->fapiRequest("v1/openOrders", 'GET', array_merge($request, $params), true); + } + + /** + * futuresOpenAlgoOrders gets all open orders for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Current-All-Algo-Open-Orders + * + * $orders = $api->futuresOpenAlgoOrders(); + * $orders = $api->futuresOpenAlgoOrders("BNBBTC"); + * + * @param string $symbol (optional) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the orders details + * @throws \Exception + */ + public function futuresOpenAlgoOrders($symbol = null, array $params = []) + { + $params['isAlgo'] = true; + return $this->futuresOpenOrders($symbol, $params); + } + + /** + * futuresOpenOrder gets an open futures order + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Current-Open-Order + * + * $order = $api->futuresOpenOrder("BNBBTC", "123456789"); + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param string $orderId (optional) order id to get the response for (mandatory if origClientOrderId is not set) + * @param string $origClientOrderId (optional) original client order id to get the response for (mandatory if orderId is not set) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the order details + * @throws \Exception + */ + public function futuresOpenOrder(string $symbol, $orderId = null, $origClientOrderId = null, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + if ($orderId) { + $request['orderId'] = $orderId; + } else if ($origClientOrderId) { + $request['origClientOrderId'] = $origClientOrderId; + } else { + throw new \Exception('futuresOpenOrder(): either orderId or origClientOrderId must be set'); + } + + return $this->fapiRequest("v1/openOrder", 'GET', array_merge($request, $params), true); + } + /** + * futuresForceOrders gets all futures force orders + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Users-Force-Orders + * + * $orders = $api->futuresForceOrders("BNBBTC"); + * + * @property int $weight 50 + * 20 if symbol is not passed + * + * @param string $symbol (optional) market symbol (e.g. ETHUSDT) + * @param int $startTime (optional) timestamp in ms to get orders from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get orders until INCLUSIVE + * @param int $limit (optional) limit the amount of orders (default 500, max 1000) + * @param string $autoCloseType (optional) "LIQUIDATION" for liquidation orders, "ADL" for ADL orders + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the orders details + * @throws \Exception + */ + public function futuresForceOrders($symbol = null, $startTime = null, $endTime = null, $limit = null, $autoCloseType = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + if ($limit) { + $request['limit'] = $limit; + } + if ($autoCloseType) { + $request['autoCloseType'] = $autoCloseType; + } + + return $this->fapiRequest("v1/forceOrders", 'GET', array_merge($request, $params), true); + } + + /** + * futuresMyTrades gets all futures trades for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Account-Trade-List + * + * $trades = $api->futuresMyTrades("BNBBTC"); + * + * @property int $weight 5 + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param int $startTime (optional) timestamp in ms to get trades from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get trades until INCLUSIVE + * @param int $limit (optional) limit the amount of trades (default 500, max 1000) + * @param string $orderId (optional) order id to get the trades for + * @param string $fromId (optional) trade id to get the trades from (if is set it will get trades >= that Id) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the trades details + * @throws \Exception + */ + public function futuresMyTrades(string $symbol, $startTime = null, $endTime = null, $limit = null, $orderId = null, $fromId = null, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + if ($limit) { + $request['limit'] = $limit; + } + if ($orderId) { + $request['orderId'] = $orderId; + } + if ($fromId) { + $request['fromId'] = $fromId; } - $endpoint = "https://api.binance.com/sapi/v1/capital/withdraw/history?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->asset, $params['coin']); + return $this->fapiRequest("v1/userTrades", 'GET', array_merge($request, $params), true); } - public function testWithdrawFee() + /** + * futuresHistory + * another name for futuresMyTrades (for naming compatibility with spot) + * @deprecated + */ + public function futuresHistory(string $symbol, $startTime = null, $endTime = null, $limit = null, $orderId = null, $fromId = null, array $params = []) { - try { - $this->binance->withdrawFee($this->asset); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/sapi/v1/asset/assetDetail"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + return $this->futuresMyTrades($symbol, $startTime, $endTime, $limit, $orderId, $fromId, $params); } - public function testTransfer() + /** + * futuresSetMarginMode sets the margin mode for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Change-Margin-Type + * + * $api->futuresSetMarginMode("BNBBTC", "ISOLATED"); + * + * @property int $weight 1 + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param string $marginType (mandatory) margin type, "CROSSED" or "ISOLATED" + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresSetMarginMode(string $symbol, string $marginType, array $params = []) { - try { - $this->binance->transfer($this->type, $this->asset, $this->amount, $this->fromSymbol, $this->toSymbol, [ 'recvWindow' => $this->recvWindow ]); + $request = [ + 'symbol' => $symbol, + 'marginType' => $marginType, + ]; - } catch (\Throwable $e) { + return $this->fapiRequest("v1/marginType", 'POST', array_merge($request, $params), true); + } + + /** + * futuresPositionMode gets the position mode for ALL symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Current-Position-Mode + * + * $response = $api->futuresPositionMode(); + * + * @property int $weight 30 + * + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresPositionMode(array $params = []) + { + return $this->fapiRequest("v1/positionSide/dual", 'GET', $params, true); + } + + /** + * futuresSetPositionMode sets the position mode for ALL symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Change-Position-Mode + * + * $api->futuresSetPositionMode(true); + * + * @property int $weight 1 + * + * @param bool $dualSidePosition (mandatory) true for Hedge Mode, false for One-way Mode + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresSetPositionMode(bool $dualSidePosition, array $params = []) + { + $request = [ + 'dualSidePosition' => $dualSidePosition ? 'true' : 'false', + ]; - } - $this->assertEquals("https://api.binance.com/sapi/v1/asset/transfer", self::$capturedUrl); + return $this->fapiRequest("v1/positionSide/dual", 'POST', array_merge($request, $params), true); + } + + /** + * futuresSetLeverage sets the leverage for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Change-Initial-Leverage + * + * $leverage = $api->futuresSetLeverage(10, "BTCUSDT"); + * + * @property int $weight 1 + * + * + * @param int $leverage (mandatory) leverage to be set (min 1, max 125) + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresSetLeverage(int $leverage, string $symbol, array $params = []) + { + $request = [ + 'symbol' => $symbol, + 'leverage' => $leverage, + ]; - parse_str(self::$capturedBody, $params); + return $this->fapiRequest("v1/leverage", 'POST', array_merge($request, $params), true); + } + + /** + * futuresMultiAssetsMarginMode gets the multi-assets margin mode for ALL symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Current-Multi-Assets-Mode + * + * $response = $api->futuresMultiAssetsMarginMode(); + * + * @property int $weight 30 + * + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresMultiAssetsMarginMode(array $params = []) + { + return $this->fapiRequest("v1/multiAssetsMargin", 'GET', $params, true); + } + + /** + * futuresSetMultiAssetsMarginMode sets the multi-assets margin mode for ALL symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Change-Multi-Assets-Mode + * + * $response = $api->futuresSetMultiAssetsMarginMode(true); + * + * @property int $weight 1 + * + * @param bool $multiAssetsMarginMode (mandatory) true for multi-assets mode, false for single-asset mode + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresSetMultiAssetsMarginMode(bool $multiAssetsMarginMode, array $params = []) + { + $request = [ + 'multiAssetsMarginMode' => $multiAssetsMarginMode ? 'true' : 'false', + ]; - $this->assertEquals($this->type, $params['type']); - $this->assertEquals($this->asset, $params['asset']); - $this->assertEquals($this->amount, $params['amount']); - $this->assertEquals($this->fromSymbol, $params['fromSymbol']); - $this->assertEquals($this->toSymbol, $params['toSymbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); + return $this->fapiRequest("v1/multiAssetsMarginMode", 'POST', array_merge($request, $params), true); } - public function testTransfersHistory() + /** + * modifyMarginHelper helper for adding or removing margin + * + * @see futuresAddMargin() and futuresReduceMargin() + * + * @return array containing the response + * @throws \Exception + */ + protected function modifyMarginHelper(string $symbol, string $amount, $addOrReduce, $positionSide = null, array $params = []) { - try { - $this->binance->transfersHistory($this->type, $this->startTime, $this->endTime, $this->limit, $this->current, $this->fromSymbol, $this->toSymbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/sapi/v1/asset/transfer?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); + $request = [ + 'symbol' => $symbol, + 'amount' => $amount, + 'type' => $addOrReduce, + ]; + if ($positionSide) { + $request['positionSide'] = $positionSide; + } + + return $this->fapiRequest("v1/positionMargin", 'POST', array_merge($request, $params), true); + } + + /** + * futuresAddMargin adds margin to a position + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Isolated-Position-Margin + * + * $response = $api->futuresAddMargin("BNBBTC", 10); + * + * @property int $weight 1 + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param string $amount (mandatory) amount to be added + * @param string $positionSide (optional) position side - "BOTH" for non-hedged and "LONG" or "SHORT" for hedged (mandatory for hedged positions) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresAddMargin(string $symbol, string $amount, $positionSide = null, array $params = []) + { + return $this->modifyMarginHelper($symbol, $amount, 1, $positionSide, $params); + } + + /** + * futuresReduceMargin removes margin from a position + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Isolated-Position-Margin + * + * $response = $api->futuresReduceMargin("BNBBTC", 10); + * + * @property int $weight 1 + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param string $amount (mandatory) amount to be removed + * @param string $positionSide (optional) position side - "BOTH" for non-hedged and "LONG" or "SHORT" for hedged (mandatory for hedged positions) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresReduceMargin(string $symbol, string $amount, $positionSide = null, array $params = []) + { + return $this->modifyMarginHelper($symbol, $amount, 2, $positionSide, $params); + } + + /** + * futuresPositions gets the position information for a symbol or all symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V2 + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V3 + * + * $position = $api->futuresPositions("BNBBTC"); + * + * @property int $weight 5 + * + * @param string $symbol (optional) market symbol (e.g. ETHUSDT) + * @param array $params (optional) an array of additional parameters that the API endpoint afllows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * @param string $api_version (optional) API version, "v2" or "v3" (default is v3) + * + * @return array with error message or the position details + * @throws \Exception + */ + public function futuresPositions($symbol = null, array $params = [], string $api_version = 'v3') + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + + if ($api_version !== 'v2' && $api_version !== 'v3') { + throw new \Exception('futuresPositions(): api_version must be either v2 or v3'); + } + return $this->fapiRequest($api_version . "/positionRisk", 'GET', array_merge($request, $params), true); + } + + /** futuresPositionsV2 + * @see futuresPositions + */ + public function futuresPositionsV2($symbol = null, array $params = []) + { + return $this->futuresPositions($symbol, $params, 'v2'); + } + + /** + * futuresPositionsV3 + * @see futuresPositions + */ + public function futuresPositionsV3($symbol = null, array $params = []) + { + return $this->futuresPositions($symbol, $params, 'v3'); + } + + /** + * futuresPosition gets the position information for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V2 + * + * $position = $api->futuresPosition("BNBBTC"); + * + * @property int $weight 5 + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param array $params (optional) an array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * @param string $api_version (optional) API version, "v2" or "v3" (default is v3) + * + * @return array with error message or the position details + * @throws \Exception + */ + public function futuresPosition(string $symbol, array $params = [], string $api_version = 'v3') + { + return $this->futuresPositions($symbol, $params, $api_version); + } + + /** + * futuresPositionV2 + * @see futuresPosition + */ + public function futuresPositionV2(string $symbol, array $params = []) + { + return $this->futuresPositionsV2($symbol, $params, 'v2'); + } + + /** + * futuresPositionV3 + * @see futuresPosition + */ + public function futuresPositionV3(string $symbol, array $params = []) + { + return $this->futuresPositionsV3($symbol, $params, 'v3'); + } + + /** + * futuresAdlQuantile gets the ADL quantile estimation for a symbol or all symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-ADL-Quantile-Estimation + * + * $response = $api->futuresAdlQuantile("BNBBTC"); + * + * @property int $weight 5 + * + * @param string $symbol (optional) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the ADL quantile details + * @throws \Exception + */ + public function futuresAdlQuantile($symbol = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + + return $this->fapiRequest("v1/adlQuantile", 'GET', array_merge($request, $params), true); + } + + /** + * futuresPositionMarginChangeHistory gets the position margin change history for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Get-Position-Margin-Change-History + * + * $history = $api->futuresPositionMarginChangeHistory("BNBBTC"); + * + * @property int $weight 1 + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param int $startTime (optional) timestamp in ms to get history from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get history until INCLUSIVE + * @param int $limit (optional) limit the amount of history (default 500) + * @param string $addOrReduce (optional) "ADD" or "REDUCE" to filter the history + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + */ + public function futuresPositionMarginChangeHistory(string $symbol, $startTime = null, $endTime = null, $limit = null, $addOrReduce = null, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + if ($limit) { + $request['limit'] = $limit; + } + if ($addOrReduce) { + if (is_numeric($addOrReduce)) { + $request['addOrReduce'] = $addOrReduce; + } else if (is_string($addOrReduce)) { + $addOrReduce = strtoupper($addOrReduce); + if ($addOrReduce === 'ADD' || $addOrReduce === '1') { + $request['addOrReduce'] = 1; + } else if ($addOrReduce === 'REDUCE' || $addOrReduce === '2') { + $request['addOrReduce'] = 2; + } else { + throw new \Exception('futuresPositionMarginChangeHistory(): addOrReduce must be "ADD" or "REDUCE" or 1 or 2'); + } + } else { + throw new \Exception('futuresPositionMarginChangeHistory(): addOrReduce must be "ADD" or "REDUCE" or 1 or 2'); + } + } + + return $this->fapiRequest("v1/positionMargin/history", 'GET', array_merge($request, $params), true); + } + + /** + * futuresBalances gets the balance information futures account + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Futures-Account-Balance-V2 + * + * $balances = $api->futuresBalances(); + * + * @property int $weight 5 + * + * @param array $params (optional) an array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * @param string $api_version (optional) API version, "v2" or "v3" (default is v3) + * + * @return array with error message or the balance details + * @throws \Exception + */ + public function futuresBalances(array $params = [], string $api_version = 'v3') + { + if ($api_version !== 'v2' && $api_version !== 'v3') { + throw new \Exception('futuresBalances(): api_version must be either v2 or v3'); + } + return $this->balances('futures', $params, 'v3'); + } + + /** + * futuresBalancesV2 + * see futuresBalances + */ + public function futuresBalancesV2(array $params = []) + { + return $this->futuresBalances($params, 'v2'); + } + + /** + * futuresBalancesV3 + * see futuresBalances + */ + public function futuresBalancesV3(array $params = []) + { + return $this->futuresBalances($params, 'v3'); + } + + /** + * futuresAccount get all information about the api account + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Account-Information-V2 + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Account-Information-V3 + * + * $account = $api->futuresAccount(); + * + * @property int $weight 5 + * + * @param array $params (optional) an array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * @param string $api_version (optional) API version, "v2" or "v3" (default is v3) + * + * @return array with error message or array of all the account information + * @throws \Exception + */ + public function futuresAccount(array $params = [], string $api_version = 'v3') + { + if ($api_version !== 'v2' && $api_version !== 'v3') { + throw new \Exception('futuresAccount(): api_version must be either v2 or v3'); + } + + return $this->fapiRequest($api_version . "/account", "GET", $params, true); + } + + /** + * futuresAccountV2 + * see futuresAccount + */ + public function futuresAccountV2(array $params = []) + { + return $this->futuresAccount($params, 'v2'); + } + + /** + * futuresAccountV3 + * see futuresAccount + */ + public function futuresAccountV3(array $params = []) + { + return $this->futuresAccount($params, 'v3'); + } + + /** + * futuresTradeFee gets the trade fee information for a symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/User-Commission-Rate + * + * $tradeFee = $api->futuresTradeFee("BNBBTC"); + * + * @property int $weight 20 + * + * @param string $symbol (mandatory) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the trade fee details + * @throws \Exception + */ + public function futuresTradeFee(string $symbol, array $params = []) + { + $request = [ + 'symbol' => $symbol, + ]; - $this->assertEquals($this->type, $params['type']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->limit, $params['size']); - $this->assertEquals($this->current, $params['current']); - $this->assertEquals($this->fromSymbol, $params['fromSymbol']); - $this->assertEquals($this->toSymbol, $params['toSymbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); + return $this->fapiRequest("v1/commissionRate", 'GET', array_merge($request, $params), true); + } + + /** + * futuresAccountConfig gets the account configuration information + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Account-Config + * + * $accountConfig = $api->futuresAccountConfig(); + * + * @property int $weight 5 + * + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the account configuration details + * @throws \Exception + */ + public function futuresAccountConfig(array $params = []) + { + return $this->fapiRequest("v1/accountConfig", 'GET', $params, true); + } + + /** + * futuresMarginModes gets the margin mode for all symbols or specific symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Symbol-Config + * + * $marginMode = $api->futuresMarginModes(); + * $marginModes = $api->futuresMarginModes("BNBBTC"); + * + * @property int $weight 5 + * + * @param string $symbol (optional) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the margin mode details + * @throws \Exception + */ + public function futuresMarginModes($symbol = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + + return $this->fapiRequest("v1/symbolConfig", 'GET', array_merge($request, $params), true); + } + + /** + * futuresOrderRateLimit gets the user rate limit + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Query-Rate-Limit + * + * $rateLimit = $api->futuresOrderRateLimit(); + * + * @property int $weight 1 + * + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the rate limit details + * @throws \Exception + */ + public function futuresOrderRateLimit(array $params = []) + { + return $this->fapiRequest("v1/rateLimit/order", 'GET', $params, true); + } + + /** + * futuresLeverages gets the leverage information for a symbol or all symbols + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Notional-and-Leverage-Brackets + * + * $leverage = $api->futuresLeverages("BNBBTC"); + * $leverages = $api->futuresLeverages(); + * + * @property int $weight 1 + * + * @param string $symbol (optional) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the leverage details + * @throws \Exception + */ + public function futuresLeverages($symbol = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + + return $this->fapiRequest("v1/leverageBracket", 'GET', array_merge($request, $params), true); + } + + /** + * futuresLedger fetch the history of changes, actions done by the user or operations that altered the balance of the user + * possible values for incomeType: + * - TRANSFER + * - TRANSFER + * - WELCOME_BONUS + * - REALIZED_PNL + * - FUNDING_FEE + * - COMMISSION + * - INSURANCE_CLEAR + * - REFERRAL_KICKBACK + * - COMMISSION_REBATE + * - API_REBATE + * - CONTEST_REWARD + * - CROSS_COLLATERAL_TRANSFER + * - OPTIONS_PREMIUM_FEE + * - OPTIONS_SETTLE_PROFIT + * - INTERNAL_TRANSFER + * - AUTO_EXCHANGE + * - DELIVERED_SETTELMENT + * - COIN_SWAP_DEPOSIT + * - COIN_SWAP_WITHDRAW + * - POSITION_LIMIT_INCREASE_FEE + * - STRATEGY_UMFUTURES_TRANSFER + * - FEE_RETURN + * - BFUSD_REWARD + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Income-History + * + * $income = $api->futuresLedger("BNBBTC"); + * $income = $api->futuresLedger("BNBBTC", "FUNDING_FEE"); + * + * @property int $weight 30 + * + * @param string $symbol (optional) market symbol (e.g. ETHUSDT) + * @param string $incomeType (optional) income type to filter the response + * @param int $startTime (optional) timestamp in ms to get income from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get income until INCLUSIVE + * @param int $limit (optional) limit the amount of income (default 100, max 1000) + * @param int $page (optional) number of page to get + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the income details + * @throws \Exception + */ + public function futuresLedger($symbol = null, $incomeType = null, $startTime = null, $endTime = null, $limit = null, $page = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + if ($incomeType) { + $request['incomeType'] = $incomeType; + } + if ($startTime) { + $request['startTime'] = $startTime; + } + if ($endTime) { + $request['endTime'] = $endTime; + } + if ($limit) { + $request['limit'] = $limit; + } + if ($page) { + $request['page'] = $page; + } + + return $this->fapiRequest("v1/income", 'GET', array_merge($request, $params), true); + } + + /** + * futuresTradingStatus get the futures trading quantitative rules indicators + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Futures-Trading-Quantitative-Rules-Indicators + * + * $tradingStatus = $api->futuresTradingStatus(); + * + * @property int $weight 10 + * weigth is 1 if symbol is provided + * + * @param string $symbol (optional) market symbol (e.g. ETHUSDT) + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the trading status details + * @throws \Exception + */ + public function futuresTradingStatus($symbol = null, array $params = []) + { + $request = []; + if ($symbol) { + $request['symbol'] = $symbol; + } + + return $this->fapiRequest("v1/apiTradingStatus", 'GET', array_merge($request, $params), true); + } + + /** + * futuresDownloadId + * helper for other metods for getting download id + */ + protected function futuresDownloadId($startTime, $endTime, ?array $params = null, string $url = '') + { + $request = [ + 'startTime' => $startTime, + 'endTime' => $endTime, + ]; + return $this->fapiRequest($url, 'GET', array_merge($request, $params), true); } - public function testSpotPrices() + /** + * futuresDownloadLinkByDownloadId + * helper for other metods for getting download link by download id + */ + protected function futuresDownloadLinkByDownloadId(string $downloadId, ?array $params = null, string $url = '') { - try { - $this->binance->prices(); - - } catch (\Throwable $e) { + $request = [ + 'downloadId' => $downloadId, + ]; - } - $this->assertEquals("https://api.binance.com/api/v3/ticker/price", self::$capturedUrl); - } + return $this->fapiRequest($url, 'GET', array_merge($request, $params), true); + } + + /** + * futuresDownloadIdForTransactions gets the download id for transactions + * request limitation is 5 times per month, shared by front end download page and rest api + * the time between startTime and endTime can not be longer than 1 year + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Download-Id-For-Futures-Transaction-History + * + * $downloadId = $api->futuresDownloadIdForTransactions(1744105700000, 1744105722122); + * + * @property int $weight 1000 + * + * @param int $startTime (optional) timestamp in ms to get transactions from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get transactions until INCLUSIVE + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the response + * @throws \Exception + */ + public function futuresDownloadIdForTransactions(int $startTime, int $endTime, array $params = []) + { + return $this->futuresDownloadId($startTime, $endTime, $params, "v1/income/asyn"); + } + + /** + * futuresDownloadTransactionsByDownloadId get futures transaction history download link by Id + * download link expiration: 24h + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Futures-Transaction-History-Download-Link-by-Id + * + * $downloadLink = $api->futuresDownloadTransactionsByDownloadId("downloadId"); + * + * @property int $weight 10 + * + * @param string $downloadId (mandatory) download id + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the download link + * @throws \Exception + */ + public function futuresDownloadTransactionsByDownloadId(string $downloadId, array $params = []) + { + return $this->futuresDownloadLinkByDownloadId($downloadId, $params, "v1/income/asyn/id"); + } + + /** + * futuresDownloadIdForOrders gets the download id for orders + * request limitation is 10 times per month, shared by front end download page and rest api + * the time between startTime and endTime can not be longer than 1 year + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Download-Id-For-Futures-Order-History + * + * $downloadId = $api->futuresDownloadIdForOrders(1744105700000, 1744105722122); + * + * @property int $weight 1000 + * + * @param int $startTime (optional) timestamp in ms to get orders from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get orders until INCLUSIVE + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the response + * @throws \Exception + */ + public function futuresDownloadIdForOrders(int $startTime, int $endTime, array $params = []) + { + return $this->futuresDownloadId($startTime, $endTime, $params, "v1/order/asyn"); + } + + /** + * futuresDownloadOrdersByDownloadId get futures orders history download link by Id + * download link expiration: 24h + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Futures-Order-History-Download-Link-by-Id + * + * $downloadLink = $api->futuresDownloadOrdersByDownloadId("downloadId"); + * + * @property int $weight 10 + * + * @param string $downloadId (mandatory) download id + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the download link + * @throws \Exception + */ + public function futuresDownloadOrdersByDownloadId(string $downloadId, array $params = []) + { + return $this->futuresDownloadLinkByDownloadId($downloadId, $params, "v1/order/asyn/id"); + } + + /** + * futuresDownloadIdForTrades gets the download id for trades + * request limitation is 5 times per month, shared by front end download page and rest api + * the time between startTime and endTime can not be longer than 1 year + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Download-Id-For-Futures-Trade-History + * + * $downloadId = $api->futuresDownloadIdForTrades(1744105700000, 1744105722122); + * + * @property int $weight 1000 + * + * @param int $startTime (optional) timestamp in ms to get trades from INCLUSIVE + * @param int $endTime (optional) timestamp in ms to get trades until INCLUSIVE + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the response + * @throws \Exception + */ + public function futuresDownloadIdForTrades(int $startTime, int $endTime, array $params = []) + { + return $this->futuresDownloadId($startTime, $endTime, $params, "v1/trade/asyn"); + } + + /** + * futuresDownloadTradesByDownloadId get futures trades history download link by Id + * download link expiration: 24h + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Futures-Trade-Download-Link-by-Id + * + * $downloadLink = $api->futuresDownloadTradesByDownloadId("downloadId"); + * + * @property int $weight 10 + * + * @param string $downloadId (mandatory) download id + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array with error message or the download link + * @throws \Exception + */ + public function futuresDownloadTradesByDownloadId(string $downloadId, array $params = []) + { + return $this->futuresDownloadLinkByDownloadId($downloadId, $params, "v1/trade/asyn/id"); + } + + /** + * futuresFeeBurn change user's BNB Fee Discount (Fee Discount On or Fee Discount Off ) on EVERY symbol + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Toggle-BNB-Burn-On-Futures-Trade + * + * $response = $api->futuresFeeBurn(true); + * + * @property int $weight 1 + * + * @param bool $flag (mandatory) true for BNB Fee Discount On, false for BNB Fee Discount Off + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresFeeBurn(bool $flag, array $params = []) + { + $request = [ + 'feeBurn' => $flag ? 'true' : 'false', + ]; - public function testSpotPrice() - { - try { - $this->binance->price($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/ticker/price?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - } - - public function testSpotBookPrices() - { - try { - $this->binance->bookPrices(); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://api.binance.com/api/v3/ticker/bookTicker", self::$capturedUrl); - } - - public function testSpotAccount() - { - try { - $this->binance->account(); - - } catch (\Throwable $e) { - - } - $this->assertTrue(str_starts_with(self::$capturedUrl, "https://api.binance.com/api/v3/account")); - } - - public function testSpotPrevDay() - { - try { - $this->binance->prevDay($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/ticker/24hr?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - } - - public function testSpotTradingDay() - { - try { - $this->binance->tradingDay(null, $this->symbols); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/ticker/tradingDay?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals(json_encode($this->symbols), $params['symbols']); - } - - public function testSpotRollingWindowPriceChange() - { - try { - $this->binance->rollingWindowPriceChange($this->symbol, null, '2d'); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/ticker?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals('2d', $params['windowSize']); - } - - public function testSpotAggTrades() - { - try { - $this->binance->aggTrades($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/aggTrades?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - - } - - public function testSpotHistoricalTrades() - { - try { - $this->binance->historicalTrades($this->symbol, $this->limit); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/trades?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - - } - - public function testSpotHistoricalTradesWithTradeId() - { - try { - $this->binance->historicalTrades($this->symbol, $this->limit, $this->tradeId); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/historicalTrades?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->tradeId, $params['fromId']); - - } - - public function testSpotDepth() - { - try { - $this->binance->depth($this->symbol, $this->limit); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/depth?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - } - - public function testSpotBalances() - { - try { - $this->binance->balances(); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/account"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - parse_str(self::$capturedBody, $params); - } - - public function testFuturesBalances() - { - try { - $this->binance->balances('futures', [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v2/balance?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesBalancesV3() - { - try { - $this->binance->balances('futures', [ 'recvWindow' => $this->recvWindow ], 'v3'); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v3/balance?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testCoins() - { - try { - $this->binance->coins(); - - } catch (\Throwable $e) { - - } - $this->assertTrue(str_starts_with(self::$capturedUrl, "https://api.binance.com/sapi/v1/capital/config/getall")); - } - - public function testSpotCandlesticks() - { - try { - $this->binance->candlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/klines?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->interval, $params['interval']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - } - - public function testSpotUiCandlesticks() - { - try { - $this->binance->uiCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/uiKlines?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->interval, $params['interval']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - } - - public function testSpotAccountSnapshot() - { - try { - $this->binance->accountSnapshot('SPOT', $this->nbrDays, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/sapi/v1/accountSnapshot?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals('SPOT', $params['type']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->nbrDays, $params['limit']); - - } - - public function testMarginAccountSnapshot() - { - try { - $this->binance->accountSnapshot('MARGIN', $this->nbrDays, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/sapi/v1/accountSnapshot?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals('MARGIN', $params['type']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->nbrDays, $params['limit']); - - } - - public function testPreventedMatches() - { - try { - $this->binance->preventedMatches($this->symbol, null, $this->orderId); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/myPreventedMatches?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->orderId, $params['orderId']); - - } - - public function testAllocations() - { - try { - $this->binance->allocations($this->symbol, $this->startTime, $this->endTime, $this->fromId, $this->limit, $this->orderId); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/myAllocations?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->fromId, $params['fromAllocationId']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->orderId, $params['orderId']); - } - - public function testCommissionRate() - { - try { - $this->binance->commissionRate($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/account/commission?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - } - - public function testOrderAmendments() - { - try { - $this->binance->orderAmendments($this->symbol, $this->orderId); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/order/amendments?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->orderId, $params['orderId']); - } - - public function testFuturesAccountSnapshot() - { - try { - $this->binance->accountSnapshot('FUTURES', $this->nbrDays, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/sapi/v1/accountSnapshot?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals('FUTURES', $params['type']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->nbrDays, $params['limit']); - - } - - public function testAccountStatus() - { - try { - $this->binance->accountStatus(); - - } catch (\Throwable $e) { - - } - $this->assertTrue(str_starts_with(self::$capturedUrl, "https://api.binance.com/sapi/v1/account/status")); - } - - public function testApiRestrictions() - { - try { - $this->binance->apiRestrictions(); - - } catch (\Throwable $e) { - - } - $this->assertTrue(str_starts_with(self::$capturedUrl, "https://api.binance.com/sapi/v1/account/apiRestrictions")); - } - - public function testApiTradingStatus() - { - try { - $this->binance->apiTradingStatus(); - - } catch (\Throwable $e) { - - } - $this->assertTrue(str_starts_with(self::$capturedUrl, "https://api.binance.com/sapi/v1/account/apiTradingStatus")); - } - - public function testOcoOrder() - { - try { - $this->binance->ocoOrder($this->side, $this->symbol, $this->quantity, $this->price, $this->stopprice, $this->stoplimitprice); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://api.binance.com/api/v3/order/oco", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->side, $params['side']); - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertEquals($this->price, $params['price']); - $this->assertEquals($this->stopprice, $params['stopPrice']); - $this->assertEquals($this->stoplimitprice, $params['stopLimitPrice']); - $this->assertEquals('GTC', $params['stopLimitTimeInForce']); - } - - public function testSpotAvgPrice() - { - try { - $this->binance->avgPrice($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/avgPrice?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - } - - public function testBswapQuote() - { - try { - $this->binance->bswapQuote($this->baseAsset, $this->quoteAsset, $this->quoteQty); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/sapi/v1/bswap/quote?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->quoteAsset, $params['quoteAsset']); - $this->assertEquals($this->baseAsset, $params['baseAsset']); - $this->assertEquals($this->quoteQty, $params['quoteQty']); - - } - - public function testFuturesTime() - { - try { - $this->binance->futuresTime(); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/time", self::$capturedUrl); - } - - public function testFuturesExchangeInfo() - { - try { - $this->binance->futuresExchangeInfo(); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/exchangeInfo", self::$capturedUrl); - } - - public function testFuturesDepth() - { - try { - $this->binance->futuresDepth($this->symbol, $this->limit); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/depth?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - } - - public function testFuturesRecentTrades() - { - try { - $this->binance->futuresRecentTrades($this->symbol, $this->limit); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/trades?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - - } - - public function testFuturesHistoricalTrades() - { - try { - $this->binance->futuresHistoricalTrades($this->symbol, $this->limit, $this->tradeId); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/historicalTrades?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->tradeId, $params['fromId']); - - } - - public function testFuturesAggTrades() - { - try { - $this->binance->futuresAggTrades($this->symbol, $this->fromId, $this->startTime, $this->endTime, $this->limit); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/aggTrades?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->fromId, $params['fromId']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->limit, $params['limit']); - - } - - public function testFuturesCandlesticks() - { - try { - $this->binance->futuresCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/klines?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->interval, $params['interval']); - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - - } - - public function testFuturesContinuousCandlesticks() - { - try { - $this->binance->futuresContinuousCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime, $this->contractType); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/continuousKlines?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->interval, $params['interval']); - $this->assertEquals($this->symbol, $params['pair']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->contractType, $params['contractType']); - - } - - public function testFuturesIndexPriceCandlesticks() - { - try { - $this->binance->futuresIndexPriceCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/indexPriceKlines?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->interval, $params['interval']); - $this->assertEquals($this->symbol, $params['pair']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - - } - - public function testFuturesMarkPriceCandlesticks() - { - try { - $this->binance->futuresMarkPriceCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/markPriceKlines?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->interval, $params['interval']); - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - - } - - public function testFuturesPremiumIndexCandlesticks() - { - try { - $this->binance->futuresPremiumIndexCandlesticks($this->symbol, $this->interval, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/premiumIndexKlines?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->interval, $params['interval']); - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - } - - public function testFuturesMarkPrice() - { - try { - $this->binance->futuresMarkPrice($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/premiumIndex?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - - } - - public function testFuturesFundingRateHistory() - { - try { - $this->binance->futuresFundingRateHistory($this->symbol, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/fundingRate?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - - } - - public function testFuturesFundingInfo() - { - try { - $this->binance->futuresFundingInfo(); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/fundingInfo", self::$capturedUrl); - } - - public function testFuturesPrevDay() - { - try { - $this->binance->futuresPrevDay($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/ticker/24hr?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - - } - - public function testFuturesPrice() - { - try { - $this->binance->futuresPrice($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/ticker/price?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - - } - - public function testFuturesPrices() - { - try { - $this->binance->futuresPrices(); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/ticker/price", self::$capturedUrl); - } - - public function testFuturesPriceV2() - { - try { - $this->binance->futuresPriceV2($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v2/ticker/price?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - } - - public function testFuturesSymbolOrderBookTicker() - { - try { - $this->binance->futuresSymbolOrderBookTicker($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/ticker/bookTicker?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - - } - - public function testFuturesDeliveryPrice() - { - try { - $this->binance->futuresDeliveryPrice($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/futures/data/delivery-price?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['pair']); - - } - - public function testFuturesOpenInterest() - { - try { - $this->binance->futuresOpenInterest($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/openInterest?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - - } - - public function testFuturesOpenInterestHistory() - { - try { - $this->binance->futuresOpenInterestHistory($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/futures/data/openInterestHist?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->period, $params['period']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - - } - - public function testFuturesTopLongShortPositionRatio() - { - try { - $this->binance->futuresTopLongShortPositionRatio($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/futures/data/topLongShortPositionRatio?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->period, $params['period']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - - } - - public function testFuturesTopLongShortAccountRatio() - { - try { - $this->binance->futuresTopLongShortAccountRatio($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/futures/data/topLongShortAccountRatio?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->period, $params['period']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - - } - - public function testFuturesGlobalLongShortAccountRatio() - { - try { - $this->binance->futuresGlobalLongShortAccountRatio($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/futures/data/globalLongShortAccountRatio?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->period, $params['period']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - - } - - public function testFuturesTakerLongShortRatio() - { - try { - $this->binance->futuresTakerLongShortRatio($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/futures/data/takerlongshortRatio?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->period, $params['period']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - - } - - public function testFuturesBasis() - { - try { - $this->binance->futuresBasis($this->symbol, $this->period, $this->limit, $this->startTime, $this->endTime, $this->contractType); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/futures/data/basis?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['pair']); - $this->assertEquals($this->period, $params['period']); - $this->assertEquals($this->contractType, $params['contractType']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - - } - - public function testFuturesIndexInfo() - { - try { - $this->binance->futuresIndexInfo($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/indexInfo?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - - } - - public function testFuturesAssetIndex() - { - try { - $this->binance->futuresAssetIndex($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/assetIndex?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - - } - - public function testFuturesConstituents() - { - try { - $this->binance->futuresConstituents($this->symbol); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/constituents?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - - } - - public function testFuturesOrder() - { - try { - $this->binance->futuresOrder('BUY', 'BTCUSDT', 1, 1000); - } catch(\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/order", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals("BTCUSDT", $params['symbol']); - $this->assertEquals("BUY", $params['side']); - $this->assertEquals("LIMIT", $params['type']); - $this->assertEquals(1, $params['quantity']); - $this->assertEquals(1000, $params['price']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); - } - - public function testFuturesAlgoOrder() - { - try { - $this->binance->futuresOrder('BUY', 'BTCUSDT', 1, 1000, 'STOP', [ - 'stopPrice' => 900, - ]); - } - catch(\Throwable $e) { - print_r($e->getMessage()); - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/algoOrder", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals("BTCUSDT", $params['symbol']); - $this->assertEquals("BUY", $params['side']); - $this->assertEquals("STOP", $params['type']); - $this->assertEquals(1, $params['quantity']); - $this->assertEquals(1000, $params['price']); - $this->assertEquals(900, $params['triggerPrice']); - $this->assertEquals("CONDITIONAL", $params['algoType']); - $this->assertTrue(str_starts_with($params['clientAlgoId'], $this->CONTRACT_ORDER_PREFIX)); - } - - public function testFuturesBuy() - { - try { - $this->binance->futuresBuy($this->symbol, $this->quantity, $this->price, $this->type); - - } catch (\Throwable $e) { - - } - - $endpoint = "https://fapi.binance.com/fapi/v1/order"; - $this->assertEquals($endpoint, self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertEquals($this->price, $params['price']); - $this->assertEquals($this->type, $params['type']); - $this->assertEquals("BUY", $params['side']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); - } - - public function testFuturesBuyTest() - { - try { - $this->binance->futuresBuyTest($this->symbol, $this->quantity, $this->price, $this->type); - - } catch (\Throwable $e) { - - } - - $endpoint = "https://fapi.binance.com/fapi/v1/order/test"; - $this->assertEquals($endpoint, self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertEquals($this->price, $params['price']); - $this->assertEquals($this->type, $params['type']); - $this->assertEquals("BUY", $params['side']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); - } - - public function testFuturesSell() - { - try { - $this->binance->futuresSell($this->symbol, $this->quantity, $this->price, $this->type); - - } catch (\Throwable $e) { - - } - - $endpoint = "https://fapi.binance.com/fapi/v1/order"; - $this->assertEquals($endpoint, self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertEquals($this->price, $params['price']); - $this->assertEquals($this->type, $params['type']); - $this->assertEquals("SELL", $params['side']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); - } - - public function testFuturesSellTest() - { - try { - $this->binance->futuresSellTest($this->symbol, $this->quantity, $this->price, $this->type); - - } catch (\Throwable $e) { - - } - - $endpoint = "https://fapi.binance.com/fapi/v1/order/test"; - $this->assertEquals($endpoint, self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertEquals($this->price, $params['price']); - $this->assertEquals($this->type, $params['type']); - $this->assertEquals("SELL", $params['side']); - $this->assertEquals("GTC", $params['timeInForce']); - $this->assertTrue(str_starts_with($params['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); - } - - public function testFuturesBatchOrders() - { - $order = [ - 'symbol' => $this->symbol, - 'side' => $this->side, - 'quantity' => $this->quantity, - 'price' => $this->price, - 'type' => $this->type, - ]; - - try { - $this->binance->futuresBatchOrders([ $order ], [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/batchOrders?batchOrders="; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $ordersString = substr(self::$capturedUrl, strlen($endpoint)); - - $orders = json_decode(urldecode($ordersString), true); - $this->assertEquals($this->symbol, $orders[0]['symbol']); - $this->assertEquals($this->side, $orders[0]['side']); - $this->assertEquals($this->quantity, $orders[0]['quantity']); - $this->assertEquals($this->price, $orders[0]['price']); - $this->assertEquals($this->type, $orders[0]['type']); - $this->assertEquals("GTC", $orders[0]['timeInForce']); - $this->assertTrue(str_starts_with($orders[0]['newClientOrderId'], $this->CONTRACT_ORDER_PREFIX)); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesEditOrder() - { - try { - $this->binance->futuresEditOrder($this->symbol, $this->side, $this->quantity, $this->price, $this->orderId); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/order?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $orderString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($orderString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->side, $params['side']); - $this->assertEquals($this->quantity, $params['quantity']); - $this->assertEquals($this->price, $params['price']); - $this->assertEquals($this->orderId, $params['orderId']); - $this->assertEquals('GTC', $params['timeInForce']); - } - - public function testFuturesEditOrders() - { - $order = [ - 'symbol' => $this->symbol, - 'side' => $this->side, - 'quantity' => $this->quantity, - 'price' => $this->price, - 'orderId' => $this->orderId, - ]; - - try { - $this->binance->futuresEditOrders([ $order ], [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/batchOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - $orders = json_decode($params['batchOrders'], true); - $this->assertEquals($this->symbol, $orders[0]['symbol']); - $this->assertEquals($this->side, $orders[0]['side']); - $this->assertEquals($this->quantity, $orders[0]['quantity']); - $this->assertEquals($this->price, $orders[0]['price']); - $this->assertEquals("GTC", $orders[0]['timeInForce']); - } - - public function testFuturesOrderAmendment() - { - try { - $this->binance->futuresOrderAmendment($this->symbol, $this->orderId, $this->origClientOrderId, $this->startTime, $this->endTime, $this->limit, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/orderAmendment?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->orderId, $params['orderId']); - $this->assertEquals($this->origClientOrderId, $params['origClientOrderId']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesCancel() - { - try { - $this->binance->futuresCancel($this->symbol, $this->orderid); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/order?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->orderid, $params['orderId']); - - } - - public function testFuturesCancelBatchOrdersByOrderIds() - { - try { - $this->binance->futuresCancelBatchOrders($this->symbol, $this->orderIdList, null, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/batchOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals('[123456,654321]', $params['orderIdList']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesCancelBatchOrdersByClientOrderIds() - { - try { - $this->binance->futuresCancelBatchOrders($this->symbol, null, $this->origClientOrderIdList, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/batchOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals('["test client order id 1","test client order id 2"]', $params['origClientOrderIdList']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesCancelOpenOrders() - { - try { - $this->binance->futuresCancelOpenOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/allOpenOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesCancelOpenAlgoOrders() - { - try { - $this->binance->futuresCancelOpenAlgoOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/algoOpenOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesCountdownCancelAllOrders() - { - try { - $this->binance->futuresCountdownCancelAllOrders($this->symbol, $this->countdownTime, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/countdownCancelAll", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->countdownTime, $params['countdownTime']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesOrderStatusByOrderId() - { - try { - $this->binance->futuresOrderStatus($this->symbol, $this->orderId, null, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/order?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->orderId, $params['orderId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesAlgoOrderStatusByOrderId() - { - try { - $this->binance->futuresAlgoOrderStatus($this->orderId, null, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/algoOrder?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->orderId, $params['algoId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesOrderStatusByClientOrderId() - { - try { - $this->binance->futuresOrderStatus($this->symbol, null, $this->origClientOrderId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/order?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->origClientOrderId, $params['origClientOrderId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesAlgoOrderStatusByClientOrderId() - { - try { - $this->binance->futuresAlgoOrderStatus(null, $this->origClientOrderId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/algoOrder?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->origClientOrderId, $params['clientAlgoId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesAllOrders() - { - try { - $this->binance->futuresAllOrders($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->orderId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/allOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->orderId, $params['orderId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesAllAlgoOrders() - { - try { - $this->binance->futuresAllAlgoOrders($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->orderId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/allAlgoOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->orderId, $params['algoId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesOpenOrders() - { - try { - $this->binance->futuresOpenOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/openOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesOpenAlgoOrders() - { - try { - $this->binance->futuresOpenAlgoOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/openAlgoOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesOpenOrderByOrderId() - { - try { - $this->binance->futuresOpenOrder($this->symbol, $this->orderId, null, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/openOrder?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->orderId, $params['orderId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesOpenOrderByClientOrderId() - { - try { - $this->binance->futuresOpenOrder($this->symbol, null, $this->origClientOrderId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/openOrder?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->origClientOrderId, $params['origClientOrderId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesForceOrders() - { - try { - $this->binance->futuresForceOrders($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->autoCloseType, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - - $endpoint = "https://fapi.binance.com/fapi/v1/forceOrders?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->autoCloseType, $params['autoCloseType']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesMyTrades() - { - try { - $this->binance->futuresMyTrades($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->orderId, $this->fromId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/userTrades?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->orderId, $params['orderId']); - $this->assertEquals($this->fromId, $params['fromId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesHistory() - { - try { - $this->binance->futuresHistory($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->orderId, $this->fromId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/userTrades?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->orderId, $params['orderId']); - $this->assertEquals($this->fromId, $params['fromId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesSetMarginMode() - { - try { - $this->binance->futuresSetMarginMode($this->symbol, $this->marginType, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/marginType", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->marginType, $params['marginType']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesPositionMode() - { - try { - $this->binance->futuresPositionMode([ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/positionSide/dual?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesSetPositionMode() - { - try { - $this->binance->futuresSetPositionMode($this->dualSidePosition, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/positionSide/dual", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->dualSidePosition, $params['dualSidePosition']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesSetLeverage() - { - try { - $this->binance->futuresSetLeverage($this->leverage, $this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/leverage", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->leverage, $params['leverage']); - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesMultiAssetsMarginMode() - { - try { - $this->binance->futuresMultiAssetsMarginMode([ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/multiAssetsMargin?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - - } - - public function testFuturesSetMultiAssetsMarginMode() - { - try { - $this->binance->futuresSetMultiAssetsMarginMode($this->multiAssetsMarginMode, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/multiAssetsMarginMode", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->multiAssetsMarginMode, $params['multiAssetsMarginMode']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesAddMargin() - { - try { - $this->binance->futuresAddMargin($this->symbol, $this->amount, $this->positionSide, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/positionMargin"; - $this->assertEquals($endpoint, self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->amount, $params['amount']); - $this->assertEquals($this->positionSide, $params['positionSide']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - $this->assertEquals(1, $params['type']); - } - - public function testFuturesReduceMargin() - { - try { - $this->binance->futuresReduceMargin($this->symbol, $this->amount, $this->positionSide, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/positionMargin"; - $this->assertEquals($endpoint, self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->amount, $params['amount']); - $this->assertEquals($this->positionSide, $params['positionSide']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - $this->assertEquals(2, $params['type']); - } - - public function testFuturesPositions() - { - try { - $this->binance->futuresPositions($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v3/positionRisk?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesPositionsV2() - { - try { - $this->binance->futuresPositionsV2($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v2/positionRisk?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesPositionsV3() - { - try { - $this->binance->futuresPositionsV3($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v3/positionRisk?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesPosition() - { - try { - $this->binance->futuresPosition($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v3/positionRisk?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesPositionV2() - { - try { - $this->binance->futuresPositionV2($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v2/positionRisk?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesPositionV3() - { - try { - $this->binance->futuresPositionV3($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v3/positionRisk?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesAdlQuantile() - { - try { - $this->binance->futuresAdlQuantile($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/adlQuantile?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesPositionMarginChangeHistory() - { - try { - $this->binance->futuresPositionMarginChangeHistory($this->symbol, $this->startTime, $this->endTime, $this->limit, 'ADD', [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/positionMargin/history?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals(1, $params['addOrReduce']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesAccount() - { - try { - $this->binance->futuresAccount([ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v3/account?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesAccountV2() - { - try { - $this->binance->futuresAccountV2([ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v2/account?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesAccountV3() - { - try { - $this->binance->futuresAccountV3([ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v3/account?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesTradeFee() - { - try { - $this->binance->futuresTradeFee($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/commissionRate?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesAccountConfig() - { - try { - $this->binance->futuresAccountConfig([ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/accountConfig?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesMarginModes() - { - try { - $this->binance->futuresMarginModes($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/symbolConfig?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesOrderRateLimit() - { - try { - $this->binance->futuresOrderRateLimit([ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/rateLimit/order?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testOrderRateLimit() - { - try { - $this->binance->orderRateLimit([ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://api.binance.com/api/v3/rateLimit/order?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesLeverages() - { - try { - $this->binance->futuresLeverages($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/leverageBracket?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesLedger() - { - try { - $this->binance->futuresLedger($this->symbol, $this->incomeType, $this->startTime, $this->endTime, $this->limit, $this->page, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/income?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->incomeType, $params['incomeType']); - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->limit, $params['limit']); - $this->assertEquals($this->page, $params['page']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesTradingStatus() - { - try { - $this->binance->futuresTradingStatus($this->symbol, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/apiTradingStatus?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->symbol, $params['symbol']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesDownloadIdForTransactions() - { - try { - $this->binance->futuresDownloadIdForTransactions($this->startTime, $this->endTime, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/income/asyn?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesDownloadTransactionsByDownloadId() - { - try { - $this->binance->futuresDownloadTransactionsByDownloadId($this->downloadId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/income/asyn/id?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->downloadId, $params['downloadId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesDownloadIdForOrders() - { - try { - $this->binance->futuresDownloadIdForOrders($this->startTime, $this->endTime, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/order/asyn?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesDownloadOrdersByDownloadId() - { - try { - $this->binance->futuresDownloadOrdersByDownloadId($this->downloadId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/order/asyn/id?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->downloadId, $params['downloadId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesDownloadIdForTrades() - { - try { - $this->binance->futuresDownloadIdForTrades($this->startTime, $this->endTime, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/trade/asyn?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->startTime, $params['startTime']); - $this->assertEquals($this->endTime, $params['endTime']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesDownloadTradesByDownloadId() - { - try { - $this->binance->futuresDownloadTradesByDownloadId($this->downloadId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/trade/asyn/id?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->downloadId, $params['downloadId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesFeeBurn() - { - try { - $this->binance->futuresFeeBurn(true, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/feeBurn", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals('true', $params['feeBurn']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testFuturesFeeBurnStatus() - { - try { - $this->binance->futuresFeeBurnStatus([ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/feeBurn?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testConvertExchangeInfo() - { - try { - $this->binance->convertExchangeInfo($this->fromAsset, $this->toAsset); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/convert/exchangeInfo?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->fromAsset, $params['fromAsset']); - $this->assertEquals($this->toAsset, $params['toAsset']); - } - - public function testConvertSend() - { - try { - $this->binance->convertSend($this->fromAsset, $this->toAsset, $this->fromAmount, null, $this->validTime, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/convert/getQuote", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->fromAsset, $params['fromAsset']); - $this->assertEquals($this->toAsset, $params['toAsset']); - $this->assertEquals($this->fromAmount, $params['fromAmount']); - $this->assertEquals($this->validTime, $params['validTime']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testConvertAccept() - { - try { - $this->binance->convertAccept($this->quoteId, [ 'recvWindow' => $this->recvWindow ]); - - } catch (\Throwable $e) { - - } - $this->assertEquals("https://fapi.binance.com/fapi/v1/convert/acceptQuote", self::$capturedUrl); - - parse_str(self::$capturedBody, $params); - - $this->assertEquals($this->quoteId, $params['quoteId']); - $this->assertEquals($this->recvWindow, $params['recvWindow']); - } - - public function testConvertStatusByOrderId() - { - try { - $this->binance->convertStatus($this->orderId, null); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/convert/orderStatus?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->orderId, $params['orderId']); - } - - public function testConvertStatusByQuoteId() - { - try { - $this->binance->convertStatus(null, $this->quoteId); - - } catch (\Throwable $e) { - - } - $endpoint = "https://fapi.binance.com/fapi/v1/convert/orderStatus?"; - $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); - - $queryString = substr(self::$capturedUrl, strlen($endpoint)); - parse_str($queryString, $params); - - $this->assertEquals($this->quoteId, $params['quoteId']); + return $this->fapiRequest("v1/feeBurn", 'POST', array_merge($request, $params), true); + } + + /** + * futuresFeeBurnStatus gets the BNB Fee Discount status + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-BNB-Burn-Status + * + * $response = $api->futuresFeeBurnStatus(); + * + * @property int $weight 30 + * + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function futuresFeeBurnStatus(array $params = []) + { + return $this->fapiRequest("v1/feeBurn", 'GET', $params, true); + } + + /** + * convertExchangeInfo get all convertible token pairs and the tokens’ respective upper/lower limits + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/convert + * + * $converInfo = $api->convertExchangeInfo(); + * $converInfo = $api->convertExchangeInfo("ETH", "DOGE"); + * + * @property int $weight 20 + * + * @param string $fromAsset (optional) the asset to convert from + * @param string $toAsset (optional) the asset to convert to + * + * @return array containing the response + * @throws \Exception + */ + public function convertExchangeInfo($fromAsset = null, $toAsset = null, array $params = []) + { + $request = []; + if ($fromAsset) { + $request['fromAsset'] = $fromAsset; + } + if ($toAsset) { + $request['toAsset'] = $toAsset; + } + return $this->fapiRequest("v1/convert/exchangeInfo", 'GET', array_merge($request, $params)); + } + + /** + * convertSend send a convert request + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/convert/Send-quote-request + * + * $convertRequest = $api->convertSend("ETH", "DOGE", 0.1); + * + * @property int $weight 50 + * + * @param string $fromAsset (mandatory) the asset to convert from + * @param string $toAsset (mandatory) the asset to convert to + * @param string $fromAmount (optional) mandatory if $toAmount is not set + * @param string $toAmount (optional) mandatory if $fromAmount is not set + * @param string $validTime (optional) deafault "10s" + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function convertSend(string $fromAsset, string $toAsset, $fromAmount = null, $toAmount = null, $validTime = null, array $params = []) + { + $request = [ + 'fromAsset' => $fromAsset, + 'toAsset' => $toAsset, + ]; + if ($fromAmount) { + $request['fromAmount'] = $fromAmount; + } else if ($toAmount) { + $request['toAmount'] = $toAmount; + } else { + throw new \Exception('convertSendRequest(): fromAmount or toAmount must be set'); + } + if ($validTime) { + $request['validTime'] = $validTime; + } + + return $this->fapiRequest("v1/convert/getQuote", 'POST', array_merge($request, $params), true); + } + + /** + * convertAccept accept the offered quote by quote ID + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/convert/Accept-Quote + * + * $convertAccept = $api->convertAccept("quoteId"); + * + * @property int $weight 200 + * + * @param string $quoteId (mandatory) the quote ID to accept + * @param array $params (optional) An array of additional parameters that the API endpoint allows + * - @param int $params['recvWindow'] (optional) the time in milliseconds to wait for the response + * + * @return array containing the response + * @throws \Exception + */ + public function convertAccept(string $quoteId, array $params = []) + { + $request = [ + 'quoteId' => $quoteId, + ]; + return $this->fapiRequest("v1/convert/acceptQuote", 'POST', array_merge($request, $params), true); + } + + /** + * convertStatus get the status of a convert request by orderId or quoteId + * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/convert/Order-Status + * + * $status = $api->convertStatus("orderId"); + * + * @property int $weight 50 + * + * @param string $orderId (optional) the order ID to get the status of (mandatory if $quoteId is not set) + * @param string $quoteId (optional) the quote ID to get the status of (mandatory if $orderId is not set) + * + * @return array containing the response + * @throws \Exception + */ + public function convertStatus($orderId = null, $quoteId = null, array $params = []) + { + $request = []; + if ($orderId) { + $request['orderId'] = $orderId; + } else if ($quoteId) { + $request['quoteId'] = $quoteId; + } else { + throw new \Exception('convertStatus(): orderId or quoteId must be set'); + } + return $this->fapiRequest("v1/convert/orderStatus", 'GET', array_merge($request, $params), true); } } From 54a9c58f8bc15b61893a8e3dbebffecb1fd5bcc8 Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Sun, 7 Dec 2025 22:16:43 +0300 Subject: [PATCH 09/15] test: static tests for fetching algo orders added --- tests/BinanceStaticTests.php | 44 +++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/BinanceStaticTests.php b/tests/BinanceStaticTests.php index 5ba633a..2ee6d10 100644 --- a/tests/BinanceStaticTests.php +++ b/tests/BinanceStaticTests.php @@ -2189,7 +2189,7 @@ public function testFuturesCancelOpenOrders() } - public function futuresCancelOpenAlgoOrders() + public function testFuturesCancelOpenAlgoOrders() { try { $this->binance->futuresCancelOpenAlgoOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); @@ -2325,6 +2325,29 @@ public function testFuturesAllOrders() } + public function testFuturesAllAlgoOrders() + { + try { + $this->binance->futuresAllAlgoOrders($this->symbol, $this->startTime, $this->endTime, $this->limit, $this->orderId, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/allAlgoOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->startTime, $params['startTime']); + $this->assertEquals($this->endTime, $params['endTime']); + $this->assertEquals($this->limit, $params['limit']); + $this->assertEquals($this->orderId, $params['algoId']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + public function testFuturesOpenOrders() { try { @@ -2344,6 +2367,25 @@ public function testFuturesOpenOrders() } + public function testFuturesOpenAlgoOrders() + { + try { + $this->binance->futuresOpenAlgoOrders($this->symbol, [ 'recvWindow' => $this->recvWindow ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/openAlgoOrders?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->symbol, $params['symbol']); + $this->assertEquals($this->recvWindow, $params['recvWindow']); + + } + public function testFuturesOpenOrderByOrderId() { try { From c00c03e656dd56ff6bc22a88d0ece0c90e6ba177 Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Mon, 8 Dec 2025 13:59:44 +0300 Subject: [PATCH 10/15] feat: futuresCancelAlgo updated --- php-binance-api.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/php-binance-api.php b/php-binance-api.php index f186e93..b6caec2 100755 --- a/php-binance-api.php +++ b/php-binance-api.php @@ -1795,7 +1795,7 @@ protected function httpRequest(string $url, string $method = "GET", array $param $this->setXMbxUsedWeight1m($header['x-mbx-used-weight-1m']); } if (isset($json['msg']) && !empty($json['msg'])) { - if ($json['msg'] !== 'success' && $url != 'v1/system/status' && $url != 'v3/systemStatus.html' && $url != 'v3/accountStatus.html' && $url != 'v1/allOpenOrders') { + if ($json['msg'] !== 'success' && $url != 'v1/system/status' && $url != 'v3/systemStatus.html' && $url != 'v3/accountStatus.html' && $url != 'v1/allOpenOrders' && $url != 'v1/algoOpenOrders') { // should always output error, not only on httpdebug // not outputing errors, hides it from users and ends up with tickets on github throw new \Exception('signedRequest error: '.print_r($output, true)); @@ -5192,10 +5192,10 @@ public function futuresCancel(string $symbol, $orderid, $params = []) } /** - * futuresAlgoCancel cancels a futures order + * futuresCancelAlgo cancels a futures order * * $algoid = "123456789"; - * $order = $api->futuresCancel($algoid); + * $order = $api->futuresCancelAlgo($algoid); * * @param string $algoid (optional) the algoid to cancel (mandatory if $params['clientalgoid'] is not set) * @param array $params (optional) additional options @@ -5205,13 +5205,13 @@ public function futuresCancel(string $symbol, $orderid, $params = []) * @return array with error message or the order details * @throws \Exception */ - public function futuresAlgoCancel($algoid, $params = []) + public function futuresCancelAlgo($algoId, $params = []) { $request = []; - if ($algoid) { - $request['algoId'] = $algoid; - } else if (!isset($params['clientalgoid'])) { - throw new \Exception('futuresAlgoCancel(): either algoid or clientalgoid must be set'); + if ($algoId) { + $request['algoId'] = $algoId; + } else if (!isset($params['clientAlgoId'])) { + throw new \Exception('futuresCancelAlgo(): either algoId or clientAlgoId must be set'); } // todo: check camel-case or lower-case!!!! return $this->fapiRequest("v1/algoOrder", 'DELETE', array_merge($request, $params), true); From 3470eedc28ce3761b1668a1cc59c1fa8f328cc96 Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:00:36 +0300 Subject: [PATCH 11/15] test: testFuturesCancelAlgo added Add test for futuresCancelAlgo method in BinanceStaticTests. --- tests/BinanceStaticTests.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/BinanceStaticTests.php b/tests/BinanceStaticTests.php index 2ee6d10..2edf7e7 100644 --- a/tests/BinanceStaticTests.php +++ b/tests/BinanceStaticTests.php @@ -2132,6 +2132,24 @@ public function testFuturesCancel() } + public function testFuturesCancelAlgo() + { + try { + $this->binance->futuresCancelAlgo($this->orderid); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/algoOrder?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->orderid, $params['algoId']); + + } + public function testFuturesCancelBatchOrdersByOrderIds() { try { From 9b2cb2b83f0b4679e800c2b8ba739496672eba3b Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:02:18 +0300 Subject: [PATCH 12/15] fix: comment removed --- php-binance-api.php | 1 - 1 file changed, 1 deletion(-) diff --git a/php-binance-api.php b/php-binance-api.php index b6caec2..ce1c648 100755 --- a/php-binance-api.php +++ b/php-binance-api.php @@ -5213,7 +5213,6 @@ public function futuresCancelAlgo($algoId, $params = []) } else if (!isset($params['clientAlgoId'])) { throw new \Exception('futuresCancelAlgo(): either algoId or clientAlgoId must be set'); } - // todo: check camel-case or lower-case!!!! return $this->fapiRequest("v1/algoOrder", 'DELETE', array_merge($request, $params), true); } From 75bf0ca2a5afaf363157ed30611d085e12fc7db9 Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:01:48 +0300 Subject: [PATCH 13/15] feat: futuresCancelAlgo updated --- php-binance-api.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/php-binance-api.php b/php-binance-api.php index ce1c648..68b9662 100755 --- a/php-binance-api.php +++ b/php-binance-api.php @@ -5194,12 +5194,14 @@ public function futuresCancel(string $symbol, $orderid, $params = []) /** * futuresCancelAlgo cancels a futures order * + * @link https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-Algo-Order + * * $algoid = "123456789"; * $order = $api->futuresCancelAlgo($algoid); * - * @param string $algoid (optional) the algoid to cancel (mandatory if $params['clientalgoid'] is not set) + * @param string $algoId (optional) the algoid to cancel (mandatory if $params['clientAlgoId'] is not set) * @param array $params (optional) additional options - * - @param string $params['clientalgoid'] original client order id to cancel + * - @param string $params['clientAlgoId'] original client order id to cancel * - @param int $params['recvWindow'] the time in milliseconds to wait for a response * * @return array with error message or the order details @@ -5209,8 +5211,11 @@ public function futuresCancelAlgo($algoId, $params = []) { $request = []; if ($algoId) { - $request['algoId'] = $algoId; - } else if (!isset($params['clientAlgoId'])) { + $request['algoid'] = $algoId; + } else if (isset($params['clientAlgoId'])) { + $request['clientalgoid'] = $params['clientAlgoId']; + unset($params['clientAlgoId']); + } else if (!isset($params['clientalgoid'])) { throw new \Exception('futuresCancelAlgo(): either algoId or clientAlgoId must be set'); } return $this->fapiRequest("v1/algoOrder", 'DELETE', array_merge($request, $params), true); From 63d365af47d78271a9a89e79b8364d28d3f067a3 Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:02:34 +0300 Subject: [PATCH 14/15] test: testFuturesCancelAlgo updated --- tests/BinanceStaticTests.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/BinanceStaticTests.php b/tests/BinanceStaticTests.php index 2edf7e7..b13de3f 100644 --- a/tests/BinanceStaticTests.php +++ b/tests/BinanceStaticTests.php @@ -2146,7 +2146,7 @@ public function testFuturesCancelAlgo() $queryString = substr(self::$capturedUrl, strlen($endpoint)); parse_str($queryString, $params); - $this->assertEquals($this->orderid, $params['algoId']); + $this->assertEquals($this->orderid, $params['algoid']); } From 96b7c51599ec900f4568808cfef2c65646759660 Mon Sep 17 00:00:00 2001 From: yzh-pelle <81404729+yzh-pelle@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:11:37 +0300 Subject: [PATCH 15/15] test: testFuturesCancelAlgoByClientAlgoId added --- tests/BinanceStaticTests.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/BinanceStaticTests.php b/tests/BinanceStaticTests.php index b13de3f..5da2982 100644 --- a/tests/BinanceStaticTests.php +++ b/tests/BinanceStaticTests.php @@ -2150,6 +2150,24 @@ public function testFuturesCancelAlgo() } + public function testFuturesCancelAlgoByClientAlgoId() + { + try { + $this->binance->futuresCancelAlgo(null, [ 'clientAlgoId' => $this->orderid ]); + + } catch (\Throwable $e) { + + } + $endpoint = "https://fapi.binance.com/fapi/v1/algoOrder?"; + $this->assertTrue(str_starts_with(self::$capturedUrl, $endpoint)); + + $queryString = substr(self::$capturedUrl, strlen($endpoint)); + parse_str($queryString, $params); + + $this->assertEquals($this->orderid, $params['clientalgoid']); + + } + public function testFuturesCancelBatchOrdersByOrderIds() { try {