diff --git a/composer.json b/composer.json index 46f07a82..e4067240 100755 --- a/composer.json +++ b/composer.json @@ -1,16 +1,16 @@ { - "name": "jaggedsoft/php-binance-api", + "name": "bahadorbzd/php-binance-api", "description": "PHP Binance API is an asynchronous PHP library for the Binance API designed to be easy to use.", "keywords": ["Binance","cryptocurrency","WebSocket"], "license": "MIT", "repositories": [ { "type": "vcs", - "url": "https://github.com/jaggedsoft/php-binance-api" + "url": "https://github.com/bahadorbzd/php-binance-api" }], "require": { "php": ">=7.0", "ext-curl": "*", - "ratchet/pawl": "^0.4.0", + "ratchet/pawl": "^0.3.0", "react/socket": "^1.0 || ^0.8 || ^0.7", "ratchet/rfc6455": "^0.3" }, diff --git a/php-binance-api.php b/php-binance-api.php old mode 100755 new mode 100644 index 2b2d3309..ffc2dfdc --- a/php-binance-api.php +++ b/php-binance-api.php @@ -10,6 +10,7 @@ * ============================================================ * A curl HTTP REST wrapper for the binance currency exchange */ + namespace Binance; use Exception; @@ -33,8 +34,6 @@ class API protected $baseTestnet = 'https://testnet.binance.vision/api/'; // /< Testnet REST endpoint for the currency exchange protected $wapi = 'https://api.binance.com/wapi/'; // /< REST endpoint for the withdrawals protected $sapi = 'https://api.binance.com/sapi/'; // /< REST endpoint for the supporting network API - protected $fapi = 'https://fapi.binance.com/'; // /< REST endpoint for the futures API - protected $bapi = 'https://www.binance.com/bapi/'; // /< REST endpoint for the internal Binance API protected $stream = 'wss://stream.binance.com:9443/ws/'; // /< Endpoint for establishing websocket connections protected $streamTestnet = 'wss://testnet.binance.vision/ws/'; // /< Testnet endpoint for establishing websocket connections protected $api_key; // /< API key that you created in the binance website member area @@ -58,7 +57,7 @@ class API protected $btc_total = 0.00; // /< value of available onOrder assets - + protected $exchangeInfo = null; protected $lastRequest = []; @@ -230,19 +229,19 @@ protected function setupProxyConfigFromFile(string $file = null) * -TAKE_PROFIT_LIMIT * -LIMIT_MAKER * - * You should check the @see exchangeInfo for each currency to determine + * You should check the @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 $flags array addtional options for order type + * @return array with error message or the order details + * @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 $flags 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 $flags = []) { @@ -252,14 +251,14 @@ public function buy(string $symbol, $quantity, $price, string $type = "LIMIT", a /** * 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 $flags array config * @return array with error message or empty or the order details + * @see buy() + * */ public function buyTest(string $symbol, $quantity, $price, string $type = "LIMIT", array $flags = []) { @@ -277,19 +276,19 @@ public function buyTest(string $symbol, $quantity, $price, string $type = "LIMIT * -TAKE_PROFIT_LIMIT * -LIMIT_MAKER * - * You should check the @see exchangeInfo for each currency to determine + * You should check the @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 $flags array addtional options for order type + * @return array with error message or the order details + * @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 $flags 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 $flags = []) { @@ -299,14 +298,14 @@ public function sell(string $symbol, $quantity, $price, string $type = "LIMIT", /** * 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 $flags array config * @return array with error message or empty or the order details + * @see sell() + * */ public function sellTest(string $symbol, $quantity, $price, string $type = "LIMIT", array $flags = []) { @@ -334,12 +333,12 @@ public function marketQuoteBuy(string $symbol, $quantity, array $flags = []) /** * 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 $flags array additional options for order type * @return array with error message or the order details + * @see marketBuy() + * */ public function marketQuoteBuyTest(string $symbol, $quantity, array $flags = []) { @@ -367,19 +366,19 @@ public function marketBuy(string $symbol, $quantity, array $flags = []) /** * 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 $flags array addtional options for order type * @return array with error message or the order details + * @see marketBuy() + * */ public function marketBuyTest(string $symbol, $quantity, array $flags = []) { return $this->order("BUY", $symbol, $quantity, 0, "MARKET", $flags, true); } - - + + /** * numberOfDecimals() returns the signifcant digits level based on the minimum order amount. * @@ -410,7 +409,14 @@ public function numberOfDecimals($val = 0.00000001) public function marketQuoteSell(string $symbol, $quantity, array $flags = []) { $flags['isQuoteOrder'] = true; - $c = $this->numberOfDecimals($this->exchangeInfo()['symbols'][$symbol]['filters'][2]['minQty']); + $exchangeInfo = $this->exchangeInfo(); + $filters = $exchangeInfo['symbols'][$symbol]['filters']; + $lotSize = array_filter($filters, function ($filter) { + return $filter['filterType'] === 'LOT_SIZE'; + }); + $lotSize = array_shift($lotSize); + $minQty = $lotSize['minQty']; + $c = $this->numberOfDecimals($minQty); $quantity = $this->floorDecimal($quantity, $c); return $this->order("SELL", $symbol, $quantity, 0, "MARKET", $flags); @@ -419,12 +425,12 @@ public function marketQuoteSell(string $symbol, $quantity, array $flags = []) /** * 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 $flags array additional options for order type * @return array with error message or the order details + * @see marketSellTest() + * */ public function marketQuoteSellTest(string $symbol, $quantity, array $flags = []) { @@ -446,7 +452,14 @@ public function marketQuoteSellTest(string $symbol, $quantity, array $flags = [] */ public function marketSell(string $symbol, $quantity, array $flags = []) { - $c = $this->numberOfDecimals($this->exchangeInfo()['symbols'][$symbol]['filters'][2]['minQty']); + $exchangeInfo = $this->exchangeInfo(); + $filters = $exchangeInfo['symbols'][$symbol]['filters']; + $lotSize = array_filter($filters, function ($filter) { + return $filter['filterType'] === 'LOT_SIZE'; + }); + $lotSize = array_shift($lotSize); + $minQty = $lotSize['minQty']; + $c = $this->numberOfDecimals($minQty); $quantity = $this->floorDecimal($quantity, $c); return $this->order("SELL", $symbol, $quantity, 0, "MARKET", $flags); @@ -455,12 +468,12 @@ public function marketSell(string $symbol, $quantity, array $flags = []) /** * 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 $flags array addtional options for order type * @return array with error message or the order details + * @see marketSellTest() + * */ public function marketSellTest(string $symbol, $quantity, array $flags = []) { @@ -632,89 +645,57 @@ public function time() } /** - * exchangeInfo - Gets the complete exchange info, including limits, currency options etc. - * - * @link https://binance-docs.github.io/apidocs/spot/en/#exchange-information + * exchangeInfo Gets the complete exchange info, including limits, currency options etc. * * $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 + * @return array with error message or exchange info array * @throws \Exception */ - public function exchangeInfo($symbols = null) + public function exchangeInfo() { if (!$this->exchangeInfo) { - $arr = array(); - $arr['symbols'] = array(); - $parameters = []; - - if ($symbols) { - if (gettype($symbols) == "string") { - $parameters["symbol"] = $symbols; - $arr = $this->httpRequest("v3/exchangeInfo", "GET", $parameters); - } - if (gettype($symbols) == "array") { - $arr = $this->httpRequest('v3/exchangeInfo?symbols=' . '["' . implode('","', $symbols) . '"]'); - } - } else { - $arr = $this->httpRequest("v3/exchangeInfo"); - } - + $arr = $this->httpRequest("v3/exchangeInfo"); + $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 = '') + public function assetDetail() { $params["sapi"] = true; - if ($asset != '' && gettype($asset) == 'string') - $params['asset'] = $asset; $arr = $this->httpRequest("v1/asset/assetDetail", 'GET', $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 + // wrap into another array for backwards compatibility with the old wapi one if (!empty($arr['BTC']['withdrawFee'])) { return array( - 'success' => 1, + 'success' => 1, 'assetDetail' => $arr, - ); + ); } else { return array( - 'success' => 0, + 'success' => 0, 'assetDetail' => array(), - ); - + ); + } } - + /** * userAssetDribbletLog - Log of the conversion of the dust assets to BNB * @deprecated @@ -725,17 +706,17 @@ public function userAssetDribbletLog() 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. - * + * + * @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 */ @@ -751,34 +732,14 @@ public function dustLog($startTime = NULL, $endTime = NULL) } /** - * 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) - { - $params["sapi"] = true; - $params["asset"] = $assets; - - return $this->httpRequest("v1/asset/dust", 'POST', $params, true); - } - - /** + * @param string $symbol + * @return mixed * @deprecated * * 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) { @@ -787,22 +748,22 @@ public function tradeFee(string $symbol) "wapi" => true, ]; trigger_error('Function tradeFee is deprecated and will be removed from Binance on Aug 1, 2021. Please use $api->commissionFee', E_USER_DEPRECATED); - + return $this->httpRequest("v3/tradeFee.html", 'GET', $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 - * + * + * @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 = '') { $params = array('sapi' => true); @@ -814,23 +775,23 @@ public function commissionFee($symbol = '') /** * 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 - * + * + * @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 */ @@ -840,17 +801,15 @@ public function withdraw(string $asset, string $address, $amount, $addressTag = "coin" => $asset, "address" => $address, "amount" => $amount, + "transactionFeeFlag" => $transactionFeeFlag, "sapi" => true, ]; - if (is_null($addressName) === false && empty($addressName) === false) { $options['name'] = str_replace(' ', '%20', $addressName); } if (is_null($addressTag) === false && empty($addressTag) === false) { $options['addressTag'] = $addressTag; } - if ($transactionFeeFlag) $options['transactionFeeFlag'] = true; - if (is_null($network) === false && empty($network) === false) { $options['network'] = $network; } @@ -862,14 +821,14 @@ public function withdraw(string $asset, string $address, $amount, $addressTag = /** * 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 - * + * + * @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 */ @@ -882,13 +841,13 @@ public function depositAddress(string $asset, $network = null) if (is_null($network) === false && empty($network) === false) { $params['network'] = $network; } - + $return = $this->httpRequest("v1/capital/deposit/address", "GET", $params, true); // Adding for backwards compatibility with wapi $return['asset'] = $return['coin']; $return['addressTag'] = $return['tag']; - + if (!empty($return['address'])) { $return['success'] = 1; } else { @@ -900,14 +859,14 @@ public function depositAddress(string $asset, $network = null) /** * 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 - * + * + * @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 */ @@ -920,24 +879,24 @@ public function depositHistory(string $asset = null, array $params = []) $return = $this->httpRequest("v1/capital/deposit/hisrec", "GET", $params, true); // Adding for backwards compatibility with wapi - foreach ($return as $key=>$item) { + foreach ($return as $key => $item) { $return[$key]['asset'] = $item['coin']; } - + 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 - * + * + * @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 */ @@ -950,8 +909,8 @@ public function withdrawHistory(string $asset = null, array $params = []) // Wrapping in array for backwards compatibility with wapi $return = array( 'withdrawList' => $this->httpRequest("v1/capital/withdraw/history", "GET", $params, true) - ); - + ); + // Adding for backwards compatibility with wapi $return['success'] = 1; @@ -960,13 +919,13 @@ public function withdrawHistory(string $asset = null, array $params = []) /** * withdrawFee - Get the withdrawal fee for an asset - * - * @property int $weight 1 - * - * @param string $asset (mandatory) An asset, e.g. BTC - * + * + * @param string $asset (mandatory) An asset, e.g. BTC + * * @return array containing the response * @throws \Exception + * @property int $weight 1 + * */ public function withdrawFee(string $asset) { @@ -1068,7 +1027,7 @@ public function aggTrades(string $symbol) "symbol" => $symbol, ])); } - + /** * historicalTrades - Get historical trades for a specific currency * @@ -1078,9 +1037,9 @@ public function aggTrades(string $symbol) * @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 + * @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 @@ -1173,7 +1132,7 @@ public function balances($priceData = false) */ public function coins() { - return $this->httpRequest('v1/capital/config/getall', 'GET', [ 'sapi' => true ], true); + return $this->httpRequest('v1/capital/config/getall', 'GET', ['sapi' => true], true); } /** @@ -1244,17 +1203,17 @@ public function setProxy(array $proxyconf) * 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 + * @see buy() + * @see sell() + * @see marketBuy() + * @see marketSell() $this->httpRequest( "https://api.binance.com/api/v1/ticker/24hr"); + * */ protected function httpRequest(string $url, string $method = "GET", array $params = [], bool $signed = false) { @@ -1270,7 +1229,7 @@ protected function httpRequest(string $url, string $method = "GET", array $param $curl = curl_init(); curl_setopt($curl, CURLOPT_VERBOSE, $this->httpDebug); - $query = $this->binance_build_query($params); + $query = http_build_query($params, '', '&'); // signed with params if ($signed === true) { @@ -1292,7 +1251,7 @@ protected function httpRequest(string $url, string $method = "GET", array $param unset($params['wapi']); $base = $this->wapi; } - + if (isset($params['sapi'])) { if ($this->useTestnet) { throw new \Exception("sapi endpoints are not available in testnet"); @@ -1301,22 +1260,13 @@ protected function httpRequest(string $url, string $method = "GET", array $param $base = $this->sapi; } - if (isset($params['fapi'])) { - unset($params['fapi']); - $base = $this->fapi; - } - - if (isset($params['bapi'])) { - unset($params['bapi']); - $base = $this->bapi; - } - $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.] + $query = http_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 + $query = http_build_query($params, '', '&'); // rebuilding query } else { $endpoint = $base . $url . '?' . $query . '&signature=' . $signature; } @@ -1325,12 +1275,10 @@ protected function httpRequest(string $url, string $method = "GET", array $param curl_setopt($curl, CURLOPT_HTTPHEADER, array( 'X-MBX-APIKEY: ' . $this->api_key, )); - } - // params so buildquery string and append to url + } // params so buildquery string and append to url elseif (count($params) > 0) { curl_setopt($curl, CURLOPT_URL, $this->getRestEndpoint() . $url . '?' . $query); - } - // no params so just the base url + } // no params so just the base url else { curl_setopt($curl, CURLOPT_URL, $this->getRestEndpoint() . $url); curl_setopt($curl, CURLOPT_HTTPHEADER, array( @@ -1383,15 +1331,15 @@ protected function httpRequest(string $url, string $method = "GET", array $param // 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, @@ -1409,10 +1357,11 @@ protected function httpRequest(string $url, string $method = "GET", array $param } if (isset($json['msg']) && !empty($json['msg'])) { - if ( $url != 'v1/system/status' && $url != 'v3/systemStatus.html' && $url != 'v3/accountStatus.html') { + if ($url != 'v1/system/status' && $url != 'v3/systemStatus.html' && $url != 'v3/accountStatus.html') { // 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)); + $jsonOutput=json_decode($output); + throw new \Exception($jsonOutput->msg,$jsonOutput->code); } } $this->transfered += strlen($output); @@ -1420,35 +1369,6 @@ protected function httpRequest(string $url, string $method = "GET", array $param 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 * @@ -1475,11 +1395,6 @@ public function get_headers_from_curl_response(string $header) * order formats the orders before sending them to the curl wrapper function * You can call this function directly or use the helper functions * - * @see buy() - * @see sell() - * @see marketBuy() - * @see marketSell() $this->httpRequest( "https://api.binance.com/api/v1/ticker/24hr"); - * * @param $side string typically "BUY" or "SELL" * @param $symbol string to buy or sell * @param $quantity string in the order @@ -1489,6 +1404,11 @@ public function get_headers_from_curl_response(string $header) * @param $test bool whether to test or not, test only validates the query * @return array containing the response * @throws \Exception + * @see buy() + * @see sell() + * @see marketBuy() + * @see marketSell() $this->httpRequest( "https://api.binance.com/api/v1/ticker/24hr"); + * */ public function order(string $side, string $symbol, $quantity, $price, string $type = "LIMIT", array $flags = [], bool $test = false) { @@ -1538,11 +1458,7 @@ public function order(string $side, string $symbol, $quantity, $price, string $t if (isset($flags['newOrderRespType'])) { $opt['newOrderRespType'] = $flags['newOrderRespType']; } - - if (isset($flags['newClientOrderId'])) { - $opt['newClientOrderId'] = $flags['newClientOrderId']; - } - + $qstring = ($test === false) ? "v3/order" : "v3/order/test"; return $this->httpRequest($qstring, "POST", $opt, true); } @@ -1749,7 +1665,7 @@ protected function tickerStreamHandler(\stdClass $json) } /** - * executionHandler Convert WebSocket trade execution into array + * tickerStreamHandler Convert WebSocket trade execution into array * * $data = $this->executionHandler( $json ); * @@ -1993,9 +1909,9 @@ public function displayDepth(array $array) { $output = ''; foreach ([ - 'asks', - 'bids', - ] as $type) { + 'asks', + 'bids', + ] as $type) { $entries = $array[$type]; if ($type === 'asks') { $entries = array_reverse($entries); @@ -2625,7 +2541,7 @@ public function userData(&$balance_callback, &$execution_callback = false) $this->subscriptions['@userdata'] = true; $loop = \React\EventLoop\Factory::create(); - $loop->addPeriodicTimer(30*60, function () { + $loop->addPeriodicTimer(30 * 60, function () { $listenKey = $this->listenKey; $this->httpRequest("v1/userDataStream?listenKey={$listenKey}", "PUT", []); }); @@ -2633,7 +2549,7 @@ public function userData(&$balance_callback, &$execution_callback = false) // @codeCoverageIgnoreStart // phpunit can't cover async function - $connector($this->getWsEndpoint() . $this->listenKey)->then(function ($ws) use ($loop) { + $connector($this->getWsEndpoint() . $this->listenKey)->then(function ($ws) { $ws->on('message', function ($data) use ($ws) { if ($this->subscriptions['@userdata'] === false) { //$this->subscriptions[$endpoint] = null; @@ -2652,15 +2568,13 @@ public function userData(&$balance_callback, &$execution_callback = false) } } }); - $ws->on('close', function ($code = null, $reason = null) use ($loop) { + $ws->on('close', function ($code = null, $reason = null) { // WPCS: XSS OK. echo "userData: WebSocket Connection closed! ({$code} - {$reason})" . PHP_EOL; - $loop->stop(); }); - }, function ($e) use ($loop) { + }, function ($e) { // WPCS: XSS OK. echo "userData: Could not connect: {$e->getMessage()}" . PHP_EOL; - $loop->stop(); }); $loop->run(); @@ -2744,12 +2658,12 @@ public function bookTicker(callable $callback) $json = json_decode($data, true); $markets = [ - "updateId" => $json['u'], - "symbol" => $json['s'], + "updateId" => $json['u'], + "symbol" => $json['s'], "bid_price" => $json['b'], - "bid_qty" => $json['B'], + "bid_qty" => $json['B'], "ask_price" => $json['a'], - "ask_qty" => $json['A'], + "ask_qty" => $json['A'], ]; call_user_func($callback, $this, $markets); }); @@ -2812,44 +2726,44 @@ protected function downloadCurlCaBundle() fwrite($fp, $result); fclose($fp); } - - protected function floorDecimal($n, $decimals=2) + + protected function floorDecimal($n, $decimals = 2) { return floor($n * pow(10, $decimals)) / pow(10, $decimals); } - protected function setXMbxUsedWeight(int $usedWeight) : void + protected function setXMbxUsedWeight(int $usedWeight): void { $this->xMbxUsedWeight = $usedWeight; } - protected function setXMbxUsedWeight1m(int $usedWeight1m) : void + protected function setXMbxUsedWeight1m(int $usedWeight1m): void { $this->xMbxUsedWeight1m = $usedWeight1m; } - public function getXMbxUsedWeight() : int + public function getXMbxUsedWeight(): int { - return $this->xMbxUsedWeight; + $this->xMbxUsedWeight; } - public function getXMbxUsedWeight1m() : int + public function getXMbxUsedWeight1m(): int { - return $this->xMbxUsedWeight1m; + $this->xMbxUsedWeight1m; } - private function getRestEndpoint() : string + private function getRestEndpoint(): string { return $this->useTestnet ? $this->baseTestnet : $this->base; } - private function getWsEndpoint() : string + private function getWsEndpoint(): string { return $this->useTestnet ? $this->streamTestnet : $this->stream; } - public function isOnTestnet() : bool + public function isOnTestnet(): bool { return $this->useTestnet; } @@ -2859,9 +2773,9 @@ public function isOnTestnet() : bool * * @link https://binance-docs.github.io/apidocs/spot/en/#test-connectivity * @link https://binance-docs.github.io/apidocs/spot/en/#system-status-system - * + * * @property int $weight 2 - * + * * @return array containing the response * @throws \Exception */ @@ -2869,28 +2783,28 @@ public function systemStatus() { $arr = array(); $api_status = $this->httpRequest("v3/ping", 'GET'); - if ( empty($api_status) ) { - $arr['api']['status'] = 'ping ok'; + if (empty($api_status)) { + $arr['api']['status'] = 'ping ok'; } else { - $arr['api']['status'] = $api_status; + $arr['api']['status'] = $api_status; } - - $arr['sapi'] = $this->httpRequest("v1/system/status", 'GET', [ 'sapi' => true ], true); + + $arr['sapi'] = $this->httpRequest("v1/system/status", 'GET', ['sapi' => true], true); 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 - * + * + * @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 */ @@ -2898,184 +2812,53 @@ public function accountSnapshot($type, $nbrDays = 5, $startTime = 0, $endTime = { if ($nbrDays < 5 || $nbrDays > 30) $nbrDays = 5; - + $params = [ 'sapi' => true, 'type' => $type, - ]; - + ]; + if ($startTime > 0) $params['startTime'] = $startTime; if ($endTime > 0) $params['endTime'] = $startTime; if ($nbrDays != 5) $params['limit'] = $nbrDays; - + return $this->httpRequest("v1/accountSnapshot", 'GET', $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() { $arr = array(); - $arr['sapi'] = $this->httpRequest("v1/account/status", 'GET', [ 'sapi' => true ], true); + $arr['sapi'] = $this->httpRequest("v1/account/status", 'GET', ['sapi' => true], true); return $arr; } /** - * apiRestriction - Fetch a set of API restrictions + * apiTradingStatus - Fetch account API trading status detail. * - * @link https://binance-docs.github.io/apidocs/spot/en/#get-api-key-permission-user_data + * @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 apiRestrictions() - { - return $this->httpRequest("v1/account/apiRestrictions", 'GET', ['sapi' => true], 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() { $arr = array(); - $arr['sapi'] = $this->httpRequest("v1/account/apiTradingStatus", 'GET', [ 'sapi' => true ], true); + $arr['sapi'] = $this->httpRequest("v1/account/apiTradingStatus", 'GET', ['sapi' => true], true); return $arr; } - - /** - * 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 $flags (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 $flags = []) - { - $opt = [ - "symbol" => $symbol, - "side" => $side, - "recvWindow" => 60000, - ]; - - if (is_numeric($quantity) === false) { - $error = "Parameter quantity expected numeric for ' $side . ' ' . $symbol .', got " . gettype($quantity); - trigger_error($error, E_USER_ERROR); - } else { - $opt['quantity'] = $quantity; - } - - if (is_numeric($price) === false) { - $error = "Parameter price expected numeric for ' $side . ' ' . $symbol .', got " . gettype($price); - trigger_error($error, E_USER_ERROR); - } else { - $opt['price'] = $price; - } - - if (is_numeric($stopprice) === false) { - $error = "Parameter stopprice expected numeric for ' $side . ' ' . $symbol .', got " . gettype($stopprice); - trigger_error($error, E_USER_ERROR); - } else { - $opt['stopPrice'] = $stopprice; - } - - if (is_null($stoplimitprice) === false && empty($stoplimitprice) === false) { - $opt['stopLimitPrice'] = $stoplimitprice; - if ( ($stoplimittimeinforce == 'FOK') || ($stoplimittimeinforce == 'IOC') ) { - $opt['stopLimitTimeInForce'] = $stoplimittimeinforce; - } else { - $opt['stopLimitTimeInForce'] = 'GTC'; // `Good 'till cancel`. Needed if flag `stopLimitPrice` used. - } - } - - // Check other flags - foreach (array('icebergQty','stopIcebergQty','listClientOrderId','limitClientOrderId','stopClientOrderId','newOrderRespType') as $flag) { - if ( isset($flags[$flag]) && !empty($flags[$flag]) ) - $opt[$flag] = $flags[$flag]; - } - - return $this->httpRequest("v3/order/oco", "POST", $opt, true); - } - - /** - * avgPrice - get the average price of a symbol based on the last 5 minutes - * - * $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) - { - $ticker = $this->httpRequest("v3/avgPrice", "GET", ["symbol" => $symbol]); - 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) { - $opt = [ - 'sapi' => true, - 'quoteAsset' => $quoteAsset, - 'baseAsset' => $baseAsset, - 'quoteQty' => $quoteQty, - ]; - - return $this->httpRequest("v1/bswap/quote", 'GET', $opt, true); - } }