diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffdaa00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +vendor +composer.lock +.idea +build +phpunit.xml \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b652e0e --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2017, Vladymyr Svyryd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d132a66 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# bitfinex-api +A simple PHP wrapper for [Bitfinex API](https://docs.bitfinex.com/docs). [Bitfinex](https://www.bitfinex.com) The world's largest and most advanced cryptocurrency trading platform +## Requirements + +- PHP >= 7.1 + +## Installation + +The preferred way to install this extension is through [composer](http://getcomposer.org/download/). + +Either run + +```bash +$ composer require codenix-sv/bitfinex-api:~0.2 +``` +or add + +```json +"codenix-sv/bitfinex-api" : "~0.2" +``` + +to the require section of your application's `composer.json` file. + +## Basic usage + +### Example +```php +use codenixsv\Bitfinex\BitfinexManager; + +$manager = new BitfinexManager(); +$client = $manager->createClient(); + +$responce = $client->getTicker('btcusd'); +``` +### Available methods + +#### Public API + +##### Get ticker +```php +$responce = $client->getTicker('btcusd'); +``` +##### Various statistics about the requested pair. +```php +$responce = $client->getStats('btcusd'); +``` +##### Get the full margin funding book. +```php +$responce = $client->getFundingBook('usd'); +``` +##### Get the full order book.. +```php +$responce = $client->getOrderBook('btcusd'); +``` +##### Get a list of the most recent trades for the given symbol. +```php +$responce = $client->getTrades('btcusd'); +``` +##### Get a list of the most recent funding data for the given currency: total amount provided and Flash Return Rate (in % by 365 days) over time. +```php +$responce = $client->getLends('usd'); +``` +##### Get a list of symbol names. +```php +$responce = $client->getSymbols(); +``` +##### Get a list of valid symbol IDs and the pair details. +```php +$responce = $client->getSymbolsDetails(); +``` \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..e8ff3ad --- /dev/null +++ b/composer.json @@ -0,0 +1,39 @@ +{ + "name": "codenix-sv/bitfinex-api", + "description": "PHP client for the Bitfinex API", + "keywords": [ + "bitfinex", + "bitcoin", + "btc", + "bitfinex-api" + ], + "type": "library", + "license": "BSD-3-Clause", + "support": { + "issues": "https://github.com/codenix-sv/bitfinex-api/issues?state=open", + "source": "https://github.com/codenix-sv/bitfinex-api" + }, + "authors": [ + { + "name": "Vladymyr Svyryd", + "email": "codenix.sv@gmail.com", + "role": "Developer" + } + ], + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~6.0" + }, + "autoload": { + "psr-4": { + "codenixsv\\Bitfinex\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "codenixsv\\B itfinex\\": "tests/" + } + } +} \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..27f417c --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + ./tests/unit + + + ./tests/functional + + + + + ./src/ + + + + + + + + \ No newline at end of file diff --git a/src/BitfinexManager.php b/src/BitfinexManager.php new file mode 100644 index 0000000..c09ca0b --- /dev/null +++ b/src/BitfinexManager.php @@ -0,0 +1,31 @@ +publicRequestManager = $publicRequestManager; + } + + /* + *************************** Public API ************************** + */ + + /** + * @param string $symbol + * @return mixed + * @see https://bitfinex.readme.io/v1/reference#rest-public-ticker + */ + public function getTicker(string $symbol) + { + $request = $this->publicRequestManager->createGetRequest('/pubticker/' . $symbol); + + return $this->get($request); + } + + /** + * Various statistics about the requested pair. + * @param string $symbol + * @return mixed + * @see https://bitfinex.readme.io/v1/reference#rest-public-stats + */ + public function getStats(string $symbol) + { + $request = $this->publicRequestManager->createGetRequest('/stats/' . $symbol); + + return $this->get($request); + } + + /** + * Get the full margin funding book + * @param string $currency + * @return mixed + * @see https://bitfinex.readme.io/v1/reference#rest-public-fundingbook + */ + public function getFundingBook(string $currency) + { + $request = $this->publicRequestManager->createGetRequest('/lendbook/' . $currency); + + return $this->get($request); + } + + /** + * Get the full order book + * @param string $symbol + * @return mixed + * @see https://bitfinex.readme.io/v1/reference#rest-public-orderbook + */ + public function getOrderBook(string $symbol) + { + $request = $this->publicRequestManager->createGetRequest('/book/' . $symbol); + + return $this->get($request); + } + + /** + * Get a list of the most recent trades for the given symbol. + * @param string $symbol + * @return mixed + * @see https://bitfinex.readme.io/v1/reference#rest-public-trades + */ + public function getTrades(string $symbol) + { + $request = $this->publicRequestManager->createGetRequest('/trades/' . $symbol); + + return $this->get($request); + } + + /** + * Get a list of the most recent funding data for the given currency: total amount provided and + * Flash Return Rate (in % by 365 days) over time. + * @param string $currency + * @return mixed + * @see https://bitfinex.readme.io/v1/reference#rest-public-lends + */ + public function getLends(string $currency) + { + $request = $this->publicRequestManager->createGetRequest('/lends/' . $currency); + + return $this->get($request); + } + + /** + * A list of symbol names. + * @return mixed + * @see https://bitfinex.readme.io/v1/reference#rest-public-symbols + */ + public function getSymbols() + { + $request = $this->publicRequestManager->createGetRequest('/symbols'); + + return $this->get($request); + } + + /** + * Get a list of valid symbol IDs and the pair details. + * @return mixed + * @see https://bitfinex.readme.io/v1/reference#rest-public-symbol-details + */ + public function getSymbolsDetails() + { + $request = $this->publicRequestManager->createGetRequest('/symbols_details'); + + return $this->get($request); + } +} diff --git a/src/Clients/Client.php b/src/Clients/Client.php new file mode 100644 index 0000000..3d0e7e6 --- /dev/null +++ b/src/Clients/Client.php @@ -0,0 +1,55 @@ +httpClient = $httpClient; + } + + /** + * @param Request $request + * @return mixed + */ + public function get(Request $request) + { + $response = $this->httpClient->get($request->getUrl(), $request->getHeaders()); + + return $response; + } + + /** + * @param Request $request + * @return mixed + */ + public function post(Request $request) + { + $response = $this->httpClient->post($request->getUrl(), $request->getParameters(), $request->getHeaders()); + + return $response; + } +} diff --git a/src/Exceptions/CurlException.php b/src/Exceptions/CurlException.php new file mode 100644 index 0000000..36cf34e --- /dev/null +++ b/src/Exceptions/CurlException.php @@ -0,0 +1,17 @@ + $value) { + $httpHeaders[] = $name . ': ' . $value; + } + + return $httpHeaders; + } + + /** + * @param $array + * @return bool + */ + public static function isArrayAssociative($array) + { + $array = array_keys($array); + return ($array !== array_keys($array)); + } +} diff --git a/src/Http/CurlHttpClient.php b/src/Http/CurlHttpClient.php new file mode 100644 index 0000000..073f955 --- /dev/null +++ b/src/Http/CurlHttpClient.php @@ -0,0 +1,81 @@ +checkExceptions($ch); + + return $response; + } + + /** + * @param $url + * @param array $parameters + * @param array $headers + * @return mixed + * @throws CurlException + */ + public function post($url, array $parameters = [], array $headers = []) + { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + + if (!empty($headers)) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + + if (!empty($parameters)) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $parameters); + } + + $response = curl_exec($ch); + + $this->checkExceptions($ch); + + curl_close($ch); + + return $response; + } + + /** + * @param $ch + * @throws CurlException + */ + private function checkExceptions($ch) + { + if (curl_errno($ch)) { + $errorMessage = curl_error($ch); + curl_close($ch); + throw new CurlException($errorMessage); + } + } +} diff --git a/src/Http/HttpClient.php b/src/Http/HttpClient.php new file mode 100644 index 0000000..532e997 --- /dev/null +++ b/src/Http/HttpClient.php @@ -0,0 +1,31 @@ +url = $url; + $this->headers = $headers; + $this->parameters = $parameters; + } + + /** + * @return string + */ + abstract public function getUrl(): string; + + /** + * @return array + */ + abstract public function getHeaders(): array; + + /** + * @return array + */ + abstract public function getParameters(): array; +} diff --git a/src/Requests/BitfinexRequest.php b/src/Requests/BitfinexRequest.php new file mode 100644 index 0000000..2d0e6f6 --- /dev/null +++ b/src/Requests/BitfinexRequest.php @@ -0,0 +1,40 @@ +url; + } + + /** + * @return array + */ + public function getHeaders(): array + { + return $this->headers; + } + + /** + * @return array + */ + public function getParameters(): array + { + return $this->parameters; + } +} diff --git a/src/Requests/Managers/BitfinexRequestManager.php b/src/Requests/Managers/BitfinexRequestManager.php new file mode 100644 index 0000000..09b58ec --- /dev/null +++ b/src/Requests/Managers/BitfinexRequestManager.php @@ -0,0 +1,63 @@ +generateUrlForGetRequest($path, $parameters); + + $httpHeaders = $this->generateHttpHeaders($headers); + + return new BitfinexRequest($url, $httpHeaders, $parameters); + } + + /** + * @param string $path + * @param array $parameters + * @param array $headers + * @return Request + */ + public function createPostRequest(string $path, array $parameters = [], array $headers = []): Request + { + $url = $this->generateUrlForPostRequest($path); + + $httpHeaders = $this->generateHttpHeaders($headers); + + return new BitfinexRequest($url, $httpHeaders, $parameters); + } + + /** + * @param string $path + * @param array $parameters + * @return string + */ + private function generateUrlForGetRequest(string $path, array $parameters = []): string + { + return $this->getBaseUrl() . $path . '?' . http_build_query($parameters); + } + + /** + * @param string $path + * @return string + */ + private function generateUrlForPostRequest(string $path): string + { + return $this->getBaseUrl() . $path; + } +} diff --git a/src/Requests/Managers/RequestManager.php b/src/Requests/Managers/RequestManager.php new file mode 100644 index 0000000..c93130e --- /dev/null +++ b/src/Requests/Managers/RequestManager.php @@ -0,0 +1,34 @@ +createClient(); + $this->assertInstanceOf(BitfinexClient::class, $client); + } +} diff --git a/tests/unit/Helpers/CommonHelperTest.php b/tests/unit/Helpers/CommonHelperTest.php new file mode 100644 index 0000000..1567427 --- /dev/null +++ b/tests/unit/Helpers/CommonHelperTest.php @@ -0,0 +1,60 @@ + 'foo', 'b' => 'bar', 'c' => 'ced']; + + $this->assertTrue(CommonHelper::isArrayAssociative($input)); + } + + /** + * test + */ + public function testIsArrayAssociativeWrong() + { + $input = [0, 2, 5, 8]; + + $this->assertFalse(CommonHelper::isArrayAssociative($input)); + } + + /** + * test + */ + public function testArrayToHttpHeaders() + { + $input = ['a' => 'foo', 'b' => 'bar', 'c' => 'ced']; + + $output = CommonHelper::arrayToHttpHeaders($input); + + $expected = ['a: foo', 'b: bar', 'c: ced']; + + $this->assertEquals($expected, $output); + } + + /** + * test + */ + public function testArrayToHttpHeadersException() + { + $input = [0, 2, 5, 8]; + + $this->expectException(\InvalidArgumentException::class); + + CommonHelper::arrayToHttpHeaders($input); + } +}