├── .gitignore ├── src ├── Enum │ └── MarginMode.php ├── ApiCode.php ├── Exceptions │ ├── NoAvailableWebSocketServerException.php │ ├── BusinessException.php │ ├── InvalidApiUriException.php │ └── HttpException.php ├── IAuth.php ├── Http │ ├── BaseHttp.php │ ├── IHttp.php │ ├── Response.php │ ├── ApiResponse.php │ ├── GuzzleHttp.php │ ├── SwooleHttp.php │ └── Request.php ├── KuCoinFuturesApi.php ├── PublicApi │ ├── Time.php │ ├── Status.php │ ├── Contract.php │ ├── Index.php │ └── Symbol.php ├── PrivateApi │ ├── Fee.php │ ├── Deposit.php │ ├── RiskLimitLevel.php │ ├── Fill.php │ ├── Withdrawal.php │ ├── Account.php │ ├── Position.php │ ├── Order.php │ └── WebSocketFeed.php ├── Auth.php └── Api.php ├── .travis.yml ├── examples ├── Status.php ├── Time.php ├── GoTime.php ├── Account.php ├── BenchmarkCoroutine.php ├── GoOrder.php └── WebSocketFeed.php ├── tests ├── TimeTest.php ├── StatusTest.php ├── FeeTest.php ├── RiskLimitLevelTest.php ├── TestCase.php ├── DepositTest.php ├── FillTest.php ├── WithdrawalTest.php ├── IndexTest.php ├── SymbolTest.php ├── ContractTest.php ├── PositionTest.php ├── AccountTest.php ├── WebSocketFeedTest.php └── OrderTest.php ├── LICENSE ├── phpunit.xml ├── composer.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .idea 3 | *.swap 4 | composer.lock -------------------------------------------------------------------------------- /src/Enum/MarginMode.php: -------------------------------------------------------------------------------- 1 | config = $config; 12 | } 13 | } -------------------------------------------------------------------------------- /examples/Status.php: -------------------------------------------------------------------------------- 1 | status(); 12 | var_dump($status); 13 | -------------------------------------------------------------------------------- /examples/Time.php: -------------------------------------------------------------------------------- 1 | timestamp(); 12 | var_dump($timestamp); 13 | -------------------------------------------------------------------------------- /src/Http/IHttp.php: -------------------------------------------------------------------------------- 1 | timestamp(); 16 | var_dump($timestamp); 17 | }); -------------------------------------------------------------------------------- /src/Exceptions/BusinessException.php: -------------------------------------------------------------------------------- 1 | response; 20 | } 21 | 22 | /** 23 | * @param ApiResponse $response 24 | */ 25 | public function setResponse($response) 26 | { 27 | $this->response = $response; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /tests/TimeTest.php: -------------------------------------------------------------------------------- 1 | timestamp(); 22 | $this->assertInternalType('int', $timestamp); 23 | } 24 | } -------------------------------------------------------------------------------- /src/KuCoinFuturesApi.php: -------------------------------------------------------------------------------- 1 | getOverview(); 18 | var_dump($result); 19 | } catch (HttpException $e) { 20 | var_dump($e->getMessage()); 21 | } catch (BusinessException $e) { 22 | var_dump($e->getMessage()); 23 | } 24 | -------------------------------------------------------------------------------- /src/PublicApi/Time.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/timestamp'); 25 | return $response->getApiData(); 26 | } 27 | } -------------------------------------------------------------------------------- /tests/StatusTest.php: -------------------------------------------------------------------------------- 1 | status(); 22 | $this->assertInternalType('array', $status); 23 | $this->assertArrayHasKey('msg', $status); 24 | $this->assertArrayHasKey('status', $status); 25 | } 26 | } -------------------------------------------------------------------------------- /src/PublicApi/Status.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/status'); 25 | return $response->getApiData(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Exceptions/InvalidApiUriException.php: -------------------------------------------------------------------------------- 1 | baseUri; 23 | } 24 | 25 | /** 26 | * @param string $baseUri 27 | */ 28 | public function setBaseUri($baseUri) 29 | { 30 | $this->baseUri = $baseUri; 31 | } 32 | 33 | /** 34 | * @return string 35 | */ 36 | public function getUri() 37 | { 38 | return $this->uri; 39 | } 40 | 41 | /** 42 | * @param string $uri 43 | */ 44 | public function setUri($uri) 45 | { 46 | $this->uri = $uri; 47 | } 48 | } -------------------------------------------------------------------------------- /tests/FeeTest.php: -------------------------------------------------------------------------------- 1 | getTradeFees($symbol); 24 | $this->assertInternalType('array', $data); 25 | $this->assertArrayHasKey('symbol', $data); 26 | $this->assertArrayHasKey('takerFeeRate', $data); 27 | $this->assertArrayHasKey('makerFeeRate', $data); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/BenchmarkCoroutine.php: -------------------------------------------------------------------------------- 1 | timestamp(); 25 | var_dump($timestamp); 26 | } 27 | } 28 | 29 | function asyncIO() 30 | { 31 | global $maxCount; 32 | $api = new Time(null, new SwooleHttp); 33 | for ($i = 0; $i < $maxCount; $i++) { 34 | go(function () use ($api) { 35 | $timestamp = $api->timestamp(); 36 | var_dump($timestamp); 37 | }); 38 | } 39 | Event::wait(); 40 | } -------------------------------------------------------------------------------- /src/Exceptions/HttpException.php: -------------------------------------------------------------------------------- 1 | request; 26 | } 27 | 28 | /** 29 | * @param Request $request 30 | */ 31 | public function setRequest($request) 32 | { 33 | $this->request = $request; 34 | } 35 | 36 | /** 37 | * @return Response 38 | */ 39 | public function getResponse() 40 | { 41 | return $this->response; 42 | } 43 | 44 | /** 45 | * @param Response $response 46 | */ 47 | public function setResponse($response) 48 | { 49 | $this->response = $response; 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/PrivateApi/Fee.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/trade-fees', compact('symbol')); 27 | return $response->getApiData(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 KuCoin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/GoOrder.php: -------------------------------------------------------------------------------- 1 | uniqid(), 23 | 'price' => '1', 24 | 'size' => '1', 25 | 'symbol' => 'BTC-USDT', 26 | 'type' => 'limit', 27 | 'side' => 'buy', 28 | 'remark' => 'ORDER#' . $i, 29 | ]; 30 | try { 31 | $result = $api->create($order); 32 | var_dump($result); 33 | } catch (\Throwable $e) { 34 | var_dump($e->getMessage()); 35 | } 36 | }); 37 | } 38 | }); -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | tests 12 | tests/TestCase.php 13 | 14 | 15 | 16 | 17 | ./src 18 | 19 | ./vendor 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kucoin/kucoin-futures-php-sdk", 3 | "type": "library", 4 | "license": "MIT", 5 | "description": "PHP SDK for KuCoin Futures API", 6 | "keywords": [ 7 | "kucoin", 8 | "futures", 9 | "kumex", 10 | "api", 11 | "sdk" 12 | ], 13 | "homepage": "https://github.com/Kucoin/kucoin-futures-php-sdk", 14 | "support": { 15 | "source": "https://github.com/Kucoin/kucoin-futures-php-sdk", 16 | "issues": "https://github.com/Kucoin/kucoin-futures-php-sdk/issues" 17 | }, 18 | "authors": [ 19 | { 20 | "name": "KuCoin API", 21 | "email": "api@kucoin.com" 22 | } 23 | ], 24 | "require": { 25 | "php": ">=5.5.0", 26 | "ext-json": "*", 27 | "guzzlehttp/guzzle": "^6.0|^7.0", 28 | "ratchet/pawl": "^0.4.1", 29 | "monolog/monolog": "~1.0|~2.0|~3.0" 30 | }, 31 | "require-dev": { 32 | "phpunit/phpunit": ">=5.7" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "KuCoin\\Futures\\SDK\\": "src/" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "KuCoin\\Futures\\SDK\\Tests\\": "tests/" 42 | } 43 | }, 44 | "minimum-stability": "dev", 45 | "prefer-stable": true, 46 | "config": { 47 | "optimize-autoloader": true, 48 | "secure-http": false 49 | }, 50 | "scripts": { 51 | "test": "./vendor/bin/phpunit -c phpunit.xml --filter '/::testGet\\w+/' --coverage-text --verbose" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/PrivateApi/Deposit.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/deposit-address', compact('currency')); 27 | return $response->getApiData(); 28 | } 29 | 30 | /** 31 | * Get deposit list. 32 | * 33 | * @param array $params 34 | * @param array $pagination 35 | * @return array 36 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 37 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 38 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 39 | */ 40 | public function getDeposits(array $params, array $pagination = []) 41 | { 42 | $response = $this->call(Request::METHOD_GET, '/api/v1/deposit-list', $params + $pagination); 43 | return $response->getApiData(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/PrivateApi/RiskLimitLevel.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/contracts/risk-limit/' . $symbol); 27 | return $response->getApiData(); 28 | } 29 | 30 | /** 31 | * Adjust risk Limit Level 32 | * 33 | * @param $symbol 34 | * @param $level 35 | * @return mixed|null 36 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 37 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 38 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 39 | */ 40 | public function changeRiskLimitLevel($symbol, $level) 41 | { 42 | $response = $this->call(Request::METHOD_POST, '/api/v1/position/risk-limit-level/change', ['symbol' => $symbol, 'level' => $level]); 43 | return $response->getApiData(); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Http/Response.php: -------------------------------------------------------------------------------- 1 | body = $body; 30 | $this->statusCode = $statusCode; 31 | $this->headers = $headers; 32 | } 33 | 34 | public function setRequest(Request $request) 35 | { 36 | $this->request = $request; 37 | } 38 | 39 | public function getRequest() 40 | { 41 | return $this->request; 42 | } 43 | 44 | public function getHeaders() 45 | { 46 | return $this->headers; 47 | } 48 | 49 | public function getBody($decodeJson = false) 50 | { 51 | return $decodeJson ? json_decode($this->body, true) : $this->body; 52 | } 53 | 54 | public function getStatusCode() 55 | { 56 | return $this->statusCode; 57 | } 58 | 59 | public function isSuccessful() 60 | { 61 | return $this->statusCode == 200; 62 | } 63 | 64 | public function __toString() 65 | { 66 | $str = 'respond ' . $this->getStatusCode(); 67 | $str .= ' with headers=' . json_encode($this->getHeaders(), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 68 | $str .= ' with body=' . $this->getBody(false); 69 | return $str; 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /examples/WebSocketFeed.php: -------------------------------------------------------------------------------- 1 | subscribePrivateChannel()). 16 | // $auth = new Auth('key', 'secret', 'passphrase'); 17 | $api = new WebSocketFeed($auth); 18 | 19 | // Use a custom event loop instance if you like 20 | //$loop = Factory::create(); 21 | //$loop->addPeriodicTimer(1, function () { 22 | // var_dump(date('Y-m-d H:i:s')); 23 | //}); 24 | //$api->setLoop($loop); 25 | 26 | $query = ['connectId' => uniqid('', true)]; 27 | $channels = [ 28 | ['topic' => '/contractMarket/ticker:XBTUSDM'], // Subscribe multiple channels 29 | ['topic' => '/contractMarket/ticker:XBTUSDTM'], 30 | ]; 31 | 32 | $api->subscribePublicChannels($query, $channels, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 33 | var_dump($message); 34 | 35 | // Subscribe another channel 36 | // $ws->send(json_encode($api->createSubscribeMessage('/contractMarket/ticker:ETHUSDTM'))); 37 | 38 | // Unsubscribe the channel 39 | // $ws->send(json_encode($api->createUnsubscribeMessage('/contractMarket/ticker:XBTUSDM'))); 40 | 41 | // Stop loop 42 | // $loop->stop(); 43 | }, function ($code, $reason) { 44 | echo "OnClose: {$code} {$reason}\n"; 45 | }); -------------------------------------------------------------------------------- /tests/RiskLimitLevelTest.php: -------------------------------------------------------------------------------- 1 | getRiskLimitLevel('ADAUSDTM'); 22 | $this->assertInternalType('array', $data); 23 | foreach ($data as $datum) { 24 | $this->assertArrayHasKey('symbol', $datum); 25 | $this->assertArrayHasKey('level', $datum); 26 | $this->assertArrayHasKey('maxRiskLimit', $datum); 27 | $this->assertArrayHasKey('minRiskLimit', $datum); 28 | $this->assertArrayHasKey('maxLeverage', $datum); 29 | $this->assertArrayHasKey('initialMargin', $datum); 30 | $this->assertArrayHasKey('maintainMargin', $datum); 31 | } 32 | } 33 | 34 | /** 35 | * @dataProvider apiProvider 36 | * 37 | * @param RiskLimitLevel $api 38 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 39 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 40 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 41 | */ 42 | public function testChangeRiskLimitLevel(RiskLimitLevel $api) 43 | { 44 | $data = $api->changeRiskLimitLevel('ADAUSDTM', 2); 45 | $this->assertInternalType('bool', $data); 46 | } 47 | } -------------------------------------------------------------------------------- /src/PublicApi/Contract.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/contracts/active'); 26 | return $response->getApiData(); 27 | } 28 | 29 | /** 30 | * Get the details of a contract. 31 | * 32 | * @param string $symbol 33 | * @return array 34 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 35 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 36 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 37 | */ 38 | public function getDetail($symbol) 39 | { 40 | $response = $this->call(Request::METHOD_GET, '/api/v1/contracts/' . $symbol); 41 | return $response->getApiData(); 42 | } 43 | 44 | /** 45 | * Get Latest Ticker for All Contracts. 46 | * 47 | * @return mixed|null 48 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 49 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 50 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 51 | */ 52 | public function getAllTickers() 53 | { 54 | $response = $this->call(Request::METHOD_GET, '/api/v1/allTickers'); 55 | return $response->getApiData(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | apiClass($this->apiWithAuth ? $auth : null)], 37 | [new $this->apiClass($this->apiWithAuth ? $auth : null, new GuzzleHttp(['skipVerifyTls' => $apiSkipVerifyTls]))], 38 | //[new $this->apiClass($this->apiWithAuth ? $auth : null, new SwooleHttp(['skipVerifyTls' => $apiSkipVerifyTls]))], 39 | ]; 40 | } 41 | 42 | protected function assertPagination($data) 43 | { 44 | $this->assertInternalType('array', $data); 45 | $this->assertArrayHasKey('totalNum', $data); 46 | $this->assertArrayHasKey('totalPage', $data); 47 | $this->assertArrayHasKey('pageSize', $data); 48 | $this->assertArrayHasKey('currentPage', $data); 49 | $this->assertArrayHasKey('items', $data); 50 | $this->assertInternalType('array', $data['items']); 51 | } 52 | } -------------------------------------------------------------------------------- /src/PrivateApi/Fill.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/fills', $params + $pagination); 28 | return $response->getApiData(); 29 | } 30 | 31 | /** 32 | * Get the recent orders of the latest transactions within 24 hours. 33 | * 34 | * @return array 35 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 36 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 37 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 38 | */ 39 | public function getRecentList() 40 | { 41 | $response = $this->call(Request::METHOD_GET, '/api/v1/recentFills'); 42 | return $response->getApiData(); 43 | } 44 | /** 45 | * Get a funding-history list. 46 | * 47 | * @param array $params 48 | * @param array $pagination 49 | * @return array 50 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 51 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 52 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 53 | */ 54 | public function getFundingHistory(array $params, array $pagination = []) 55 | { 56 | $response = $this->call(Request::METHOD_GET, '/api/v1/funding-history', $params + $pagination); 57 | return $response->getApiData(); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /tests/DepositTest.php: -------------------------------------------------------------------------------- 1 | getAddress('XBT'); 25 | // if ($address !== null) { 26 | // $this->assertInternalType('array', $address); 27 | // $this->assertArrayHasKey('address', $address); 28 | // $this->assertArrayHasKey('memo', $address); 29 | // } 30 | // } catch (BusinessException $e) { 31 | // // deposit.disabled 32 | // if ($e->getResponse()->getApiCode() == '260200') { 33 | // return; 34 | // } 35 | // throw $e; 36 | // } 37 | $this->assertTrue(true); 38 | } 39 | 40 | /** 41 | * @dataProvider apiProvider 42 | * @param Deposit $api 43 | * @return array|string 44 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 45 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 46 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 47 | */ 48 | public function testGetDeposits(Deposit $api) 49 | { 50 | $data = $api->getDeposits(['currency' => 'XBT'], ['currentPage' => 1, 'pageSize' => 10]); 51 | $this->assertPagination($data); 52 | foreach ($data['items'] as $item) { 53 | $this->assertArrayHasKey('currency', $item); 54 | $this->assertArrayHasKey('status', $item); 55 | $this->assertArrayHasKey('address', $item); 56 | $this->assertArrayHasKey('isInner', $item); 57 | $this->assertArrayHasKey('amount', $item); 58 | $this->assertArrayHasKey('fee', $item); 59 | $this->assertArrayHasKey('walletTxId', $item); 60 | $this->assertArrayHasKey('createdAt', $item); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/PrivateApi/Withdrawal.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/withdrawals/quotas', compact('currency')); 26 | return $response->getApiData(); 27 | } 28 | 29 | /** 30 | * Get a list of withdrawal 31 | * @param array $params 32 | * @param array $pagination 33 | * @return array 34 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 35 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 36 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 37 | */ 38 | public function getList(array $params, array $pagination = []) 39 | { 40 | $response = $this->call(Request::METHOD_GET, '/api/v1/withdrawal-list', $params + $pagination); 41 | return $response->getApiData(); 42 | } 43 | 44 | /** 45 | * Apply a withdrawal 46 | * @param array $params 47 | * @return array 48 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 49 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 50 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 51 | */ 52 | public function apply(array $params) 53 | { 54 | $response = $this->call(Request::METHOD_POST, '/api/v1/withdrawals', $params); 55 | return $response->getApiData(); 56 | } 57 | 58 | /** 59 | * Cancel a withdrawal 60 | * @param string $withdrawId 61 | * @return array 62 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 63 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 64 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 65 | */ 66 | public function cancel($withdrawId) 67 | { 68 | $response = $this->call(Request::METHOD_DELETE, '/api/v1/withdrawals/' . $withdrawId); 69 | return $response->getApiData(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Auth.php: -------------------------------------------------------------------------------- 1 | key = $key; 22 | $this->secret = $secret; 23 | $this->passphrase = $passphrase; 24 | $this->apiKeyVersion = $apiKeyVersion; 25 | } 26 | 27 | public function signature($requestUri, $body, $timestamp, $method) 28 | { 29 | // Decode $requestUri 30 | $parts = parse_url($requestUri); 31 | if (isset($parts['query'])) { 32 | parse_str($parts['query'], $queries); 33 | $queryString = ''; 34 | foreach ($queries as $key => $value) { 35 | $queryString .= sprintf('%s=%s&', $key, $value); 36 | } 37 | $queryString = rtrim($queryString, '&'); 38 | $requestUri = $parts['path'] . '?' . $queryString; 39 | if (isset($parts['fragment'])) { 40 | $requestUri .= '#' . $parts['fragment']; 41 | } 42 | } 43 | 44 | if (is_array($body)) { 45 | $body = empty($body) ? '' : json_encode($body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 46 | } else { 47 | $body = (string)$body; 48 | } 49 | $method = strtoupper($method); 50 | $plain = $timestamp . $method . $requestUri . $body; 51 | return base64_encode(hash_hmac('sha256', $plain, $this->secret, true)); 52 | } 53 | 54 | public function getHeaders($method, $requestUri, $body) 55 | { 56 | $timestamp = floor(microtime(true) * 1000); 57 | $isApiKeyVersionV1 = $this->apiKeyVersion === self::API_KEY_VERSION_V1; 58 | $headers = [ 59 | 'KC-API-KEY' => $this->key, 60 | 'KC-API-TIMESTAMP' => $timestamp, 61 | 'KC-API-PASSPHRASE' => $isApiKeyVersionV1 ? $this->passphrase : $this->signaturePassphrase(), 62 | 'KC-API-SIGN' => $this->signature($requestUri, $body, $timestamp, $method), 63 | ]; 64 | 65 | !$isApiKeyVersionV1 && $headers['KC-API-KEY-VERSION'] = $this->apiKeyVersion; 66 | return $headers; 67 | } 68 | 69 | private function signaturePassphrase() 70 | { 71 | return base64_encode(hash_hmac('sha256', $this->passphrase, $this->secret, true)); 72 | } 73 | } -------------------------------------------------------------------------------- /src/PublicApi/Index.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/index/query', $params + $pagination); 28 | return $response->getApiData(); 29 | } 30 | 31 | /** 32 | * Get mark price. 33 | * 34 | * @param string $symbol 35 | * @return array 36 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 37 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 38 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 39 | */ 40 | public function getMarkPrice($symbol) 41 | { 42 | $response = $this->call(Request::METHOD_GET, sprintf('/api/v1/mark-price/%s/current', $symbol)); 43 | return $response->getApiData(); 44 | } 45 | 46 | /** 47 | * Get a interest list of index. 48 | * 49 | * @param array $pagination 50 | * @return array 51 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 52 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 53 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 54 | */ 55 | public function getInterests(array $pagination = []) 56 | { 57 | $response = $this->call(Request::METHOD_GET, '/api/v1/interest/query', $pagination); 58 | return $response->getApiData(); 59 | } 60 | 61 | /** 62 | * Get a premium of index list. 63 | * 64 | * @param array $params 65 | * @param array $pagination 66 | * @return array 67 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 68 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 69 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 70 | */ 71 | public function getPremium(array $params, array $pagination = []) 72 | { 73 | $response = $this->call(Request::METHOD_GET, '/api/v1/premium/query', $params + $pagination); 74 | return $response->getApiData(); 75 | } 76 | 77 | /** 78 | * Get current funding rate. 79 | * 80 | * @param string $symbol 81 | * @return array 82 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 83 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 84 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 85 | */ 86 | public function getCurrentFundingRate($symbol) 87 | { 88 | $response = $this->call(Request::METHOD_GET, sprintf('/api/v1/funding-rate/%s/current', $symbol)); 89 | return $response->getApiData(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Http/ApiResponse.php: -------------------------------------------------------------------------------- 1 | httpResponse = $response; 21 | } 22 | 23 | public function getBody() 24 | { 25 | if (is_null($this->body)) { 26 | $this->body = $this->httpResponse->getBody(true); 27 | } 28 | return $this->body; 29 | } 30 | 31 | public function getApiCode() 32 | { 33 | $body = $this->getBody(); 34 | return isset($body['code']) ? $body['code'] : ''; 35 | } 36 | 37 | public function getApiMessage() 38 | { 39 | $body = $this->getBody(); 40 | return isset($body['msg']) ? $body['msg'] : ''; 41 | } 42 | 43 | public function getHttpResponse() 44 | { 45 | return $this->httpResponse; 46 | } 47 | 48 | public function isSuccessful() 49 | { 50 | if ($this->httpResponse->isSuccessful()) { 51 | if ($this->getApiCode() == ApiCode::SUCCESS) { 52 | return true; 53 | } 54 | } 55 | return false; 56 | } 57 | 58 | public function mustSuccessful() 59 | { 60 | if (!$this->httpResponse->isSuccessful()) { 61 | $msg = sprintf( 62 | '[HTTP]Failure: status code is NOT 200, %s %s with body=%s, respond code=%d body=%s', 63 | $this->httpResponse->getRequest()->getMethod(), 64 | $this->httpResponse->getRequest()->getRequestUri(), 65 | $this->httpResponse->getRequest()->getBodyParams(), 66 | $this->httpResponse->getStatusCode(), 67 | $this->httpResponse->getBody() 68 | ); 69 | $exception = new HttpException($msg, $this->httpResponse->getStatusCode()); 70 | $exception->setRequest($this->httpResponse->getRequest()); 71 | $exception->setResponse($this->httpResponse); 72 | throw $exception; 73 | } 74 | 75 | if (!$this->isSuccessful()) { 76 | $msg = sprintf( 77 | '[API]Failure: api code is NOT %s, %s %s with body=%s, respond code=%s message="%s" body=%s', 78 | ApiCode::SUCCESS, 79 | $this->httpResponse->getRequest()->getMethod(), 80 | $this->httpResponse->getRequest()->getRequestUri(), 81 | $this->httpResponse->getRequest()->getBodyParams(), 82 | $this->getApiCode(), 83 | $this->getApiMessage(), 84 | $this->httpResponse->getBody() 85 | ); 86 | $exception = new BusinessException($msg, is_numeric($this->getApiCode()) ? $this->getApiCode() : 110); 87 | $exception->setResponse($this); 88 | throw $exception; 89 | } 90 | } 91 | 92 | /** 93 | * @return mixed 94 | * @throws BusinessException 95 | * @throws HttpException 96 | */ 97 | public function getApiData() 98 | { 99 | $this->mustSuccessful(); 100 | $body = $this->getBody(); 101 | if (!isset($body['data'])) { 102 | return null; 103 | } 104 | return $body['data']; 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /src/Http/GuzzleHttp.php: -------------------------------------------------------------------------------- 1 | getBaseUri() && strpos($request->getUri(), '://') === false) { 38 | $exception = new InvalidApiUriException('Invalid base_uri or uri, must set base_uri or set uri to a full url'); 39 | $exception->setBaseUri($request->getBaseUri()); 40 | $exception->setUri($request->getUri()); 41 | throw $exception; 42 | } 43 | 44 | $config = [ 45 | 'base_uri' => $request->getBaseUri(), 46 | 'timeout' => $timeout, 47 | 'connect_timeout' => 30, 48 | 'http_errors' => false, 49 | 'verify' => isset($this->config['verify']) ? $this->config['verify'] : empty($this->config['skipVerifyTls']), 50 | ] + $this->config; 51 | $client = static::getClient($config); 52 | $options = [ 53 | 'headers' => $request->getHeaders(), 54 | ]; 55 | $method = $request->getMethod(); 56 | $params = $request->getParams(); 57 | $hasParam = !empty($params); 58 | switch ($method) { 59 | case Request::METHOD_GET: 60 | case Request::METHOD_DELETE: 61 | $hasParam AND $options['query'] = $params; 62 | break; 63 | case Request::METHOD_PUT: 64 | case Request::METHOD_POST: 65 | if ($hasParam) { 66 | $options['headers']['Content-Type'] = 'application/json'; 67 | $options['body'] = $request->getBodyParams(); 68 | } 69 | break; 70 | default: 71 | $exception = new HttpException('Unsupported method ' . $method, 0); 72 | $exception->setRequest($request); 73 | throw $exception; 74 | } 75 | try { 76 | $guzzleResponse = $client->request($request->getMethod(), $request->getUri(), $options); 77 | $response = new Response($guzzleResponse->getBody()->__toString(), $guzzleResponse->getStatusCode(), $guzzleResponse->getHeaders()); 78 | $response->setRequest($request); 79 | return $response; 80 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 81 | $exception = new HttpException($e->getMessage(), $e->getCode(), $e); 82 | $exception->setRequest($request); 83 | throw $exception; 84 | } catch (\Exception $e) { 85 | $exception = new HttpException($e->getMessage(), $e->getCode(), $e); 86 | $exception->setRequest($request); 87 | throw $exception; 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/Http/SwooleHttp.php: -------------------------------------------------------------------------------- 1 | getBaseUri() && strpos($request->getUri(), '://') === false) { 47 | $exception = new InvalidApiUriException('Invalid base_uri or uri, must set base_uri or set uri to a full url'); 48 | $exception->setBaseUri($request->getBaseUri()); 49 | $exception->setUri($request->getUri()); 50 | throw $exception; 51 | } 52 | 53 | $config = [ 54 | 'base_uri' => $request->getBaseUri(), 55 | 'timeout' => $timeout, 56 | 'use_pool' => true, 57 | 'ssl_verify_peer' => isset($this->config['ssl_verify_peer']) ? $this->config['ssl_verify_peer'] : empty($this->config['skipVerifyTls']), 58 | ] + $this->config; 59 | $client = static::getClient($config); 60 | $options['headers'] = $request->getHeaders(); 61 | 62 | $method = $request->getMethod(); 63 | $requestUri = $request->getRequestUri(); 64 | try { 65 | switch ($method) { 66 | case Request::METHOD_GET: 67 | case Request::METHOD_DELETE: 68 | /**@var \Swlib\Saber\Response $saberResponse */ 69 | $saberResponse = $client->{strtolower($method)}($requestUri, $options); 70 | break; 71 | case Request::METHOD_PUT: 72 | case Request::METHOD_POST: 73 | $data = $request->getBodyParams(); 74 | $options['headers']['Content-Type'] = ContentType::JSON; 75 | /**@var \Swlib\Saber\Response $saberResponse */ 76 | $saberResponse = $client->{strtolower($method)}($requestUri, $data, $options); 77 | break; 78 | default: 79 | $exception = new HttpException('Unsupported method ' . $method, 0); 80 | $exception->setRequest($request); 81 | throw $exception; 82 | } 83 | $response = new Response($saberResponse->getBody()->__toString(), $saberResponse->getStatusCode(), $saberResponse->getHeaders()); 84 | $response->setRequest($request); 85 | return $response; 86 | } catch (\Exception $e) { 87 | if ($e instanceof RequestException && $e->hasResponse()) { 88 | $saberResponse = $e->getResponse(); 89 | $response = new Response($saberResponse->getBody()->__toString(), $saberResponse->getStatusCode(), $saberResponse->getHeaders()); 90 | $response->setRequest($request); 91 | return $response; 92 | } 93 | $exception = new HttpException($e->getMessage(), $e->getCode(), $e); 94 | $exception->setRequest($request); 95 | throw $exception; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tests/FillTest.php: -------------------------------------------------------------------------------- 1 | getFills([], ['currentPage' => 1, 'pageSize' => 10]); 22 | var_dump($data); 23 | $this->assertPagination($data); 24 | foreach ($data['items'] as $item) { 25 | $this->assertArrayHasKey('symbol', $item); 26 | $this->assertArrayHasKey('side', $item); 27 | $this->assertArrayHasKey('forceTaker', $item); 28 | $this->assertArrayHasKey('orderId', $item); 29 | $this->assertArrayHasKey('fee', $item); 30 | $this->assertArrayHasKey('liquidity', $item); 31 | $this->assertArrayHasKey('feeRate', $item); 32 | $this->assertArrayHasKey('createdAt', $item); 33 | $this->assertArrayHasKey('size', $item); 34 | $this->assertArrayHasKey('stop', $item); 35 | $this->assertArrayHasKey('price', $item); 36 | $this->assertArrayHasKey('tradeId', $item); 37 | $this->assertArrayHasKey('settleCurrency', $item); 38 | $this->assertArrayHasKey('tradeTime', $item); 39 | } 40 | } 41 | 42 | /** 43 | * @dataProvider apiProvider 44 | * @param Fill $api 45 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 46 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 47 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 48 | */ 49 | public function testGetRecentList(Fill $api) 50 | { 51 | $items = $api->getRecentList(); 52 | $this->assertInternalType('array', $items); 53 | foreach ($items as $item) { 54 | $this->assertArrayHasKey('symbol', $item); 55 | $this->assertArrayHasKey('side', $item); 56 | $this->assertArrayHasKey('forceTaker', $item); 57 | $this->assertArrayHasKey('orderId', $item); 58 | $this->assertArrayHasKey('fee', $item); 59 | $this->assertArrayHasKey('liquidity', $item); 60 | $this->assertArrayHasKey('feeRate', $item); 61 | $this->assertArrayHasKey('createdAt', $item); 62 | $this->assertArrayHasKey('size', $item); 63 | $this->assertArrayHasKey('stop', $item); 64 | $this->assertArrayHasKey('price', $item); 65 | $this->assertArrayHasKey('tradeId', $item); 66 | $this->assertArrayHasKey('settleCurrency', $item); 67 | $this->assertArrayHasKey('tradeTime', $item); 68 | } 69 | } 70 | 71 | 72 | /** 73 | * 74 | * @dataProvider apiProvider 75 | * @param Fill $api 76 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 77 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 78 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 79 | */ 80 | public function testGetFundingHistory(Fill $api) 81 | { 82 | $params = [ 83 | 'symbol' => 'XBTUSDM' 84 | ]; 85 | $data = $api->getFundingHistory($params); 86 | $this->assertInternalType('array', $data); 87 | foreach ($data['dataList'] as $item) { 88 | $this->assertArrayHasKey('symbol', $item); 89 | $this->assertArrayHasKey('fundingRate', $item); 90 | $this->assertArrayHasKey('timePoint', $item); 91 | $this->assertArrayHasKey('markPrice', $item); 92 | $this->assertArrayHasKey('positionQty', $item); 93 | $this->assertArrayHasKey('positionCost', $item); 94 | $this->assertArrayHasKey('funding', $item); 95 | $this->assertArrayHasKey('settleCurrency', $item); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /tests/WithdrawalTest.php: -------------------------------------------------------------------------------- 1 | getQuotas('XBT'); 22 | $this->assertInternalType('array', $data); 23 | $this->assertArrayHasKey('limitAmount', $data); 24 | $this->assertArrayHasKey('withdrawMinFee', $data); 25 | $this->assertArrayHasKey('innerWithdrawMinFee', $data); 26 | $this->assertArrayHasKey('usedAmount', $data); 27 | $this->assertArrayHasKey('availableAmount', $data); 28 | $this->assertArrayHasKey('remainAmount', $data); 29 | $this->assertArrayHasKey('precision', $data); 30 | $this->assertArrayHasKey('currency', $data); 31 | $this->assertArrayHasKey('isWithdrawEnabled', $data); 32 | $this->assertArrayHasKey('withdrawMinSize', $data); 33 | } 34 | 35 | /** 36 | * @dataProvider apiProvider 37 | * @param Withdrawal $api 38 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 39 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 40 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 41 | */ 42 | public function testApply(Withdrawal $api) 43 | { 44 | $params = [ 45 | 'currency' => 'USDT', 46 | 'address' => '1BcTdvq6Qdh7GnviHTYHq4tBvU32FfUbGz', 47 | 'amount' => 0.3, 48 | 'remark' => 'test apply withdrawal', 49 | 'chain' => 'OMNI' 50 | ]; 51 | $data = $api->apply($params); 52 | $this->assertInternalType('array', $data); 53 | $this->assertArrayHasKey('withdrawId', $data); 54 | } 55 | 56 | 57 | /** 58 | * @dataProvider apiProvider 59 | * @param Withdrawal $api 60 | * @return array 61 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 62 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 63 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 64 | */ 65 | public function testGetList(Withdrawal $api) 66 | { 67 | $params = [ 68 | 'currency' => 'BTC', 69 | ]; 70 | $pagination = [ 71 | 'currentPage' => 1, 72 | 'pageSize' => 10, 73 | ]; 74 | $data = $api->getList($params, $pagination); 75 | $this->assertPagination($data); 76 | foreach ($data['items'] as $item) { 77 | $this->assertInternalType('array', $item); 78 | $this->assertArrayHasKey('withdrawalId', $item); 79 | $this->assertArrayHasKey('walletTxId', $item); 80 | $this->assertArrayHasKey('address', $item); 81 | $this->assertArrayHasKey('memo', $item); 82 | $this->assertArrayHasKey('currency', $item); 83 | $this->assertArrayHasKey('amount', $item); 84 | $this->assertArrayHasKey('fee', $item); 85 | $this->assertArrayHasKey('isInner', $item); 86 | $this->assertArrayHasKey('status', $item); 87 | $this->assertArrayHasKey('createdAt', $item); 88 | } 89 | } 90 | 91 | /** 92 | * @dataProvider apiProvider 93 | * @param Withdrawal $api 94 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 95 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 96 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 97 | */ 98 | public function testCancel(Withdrawal $api) 99 | { 100 | $data = $api->cancel('5c1cb7bb03aa6774239b772c'); 101 | $this->assertInternalType('array', $data); 102 | $this->assertArrayHasKey('cancelledWithdrawIds', $data); 103 | } 104 | } -------------------------------------------------------------------------------- /src/Http/Request.php: -------------------------------------------------------------------------------- 1 | method; 53 | } 54 | 55 | /** 56 | * @param string $method Request::METHOD_XXX 57 | */ 58 | public function setMethod($method) 59 | { 60 | $this->method = strtoupper($method); 61 | } 62 | 63 | /** 64 | * @return string 65 | */ 66 | public function getBaseUri() 67 | { 68 | return $this->baseUri; 69 | } 70 | 71 | /** 72 | * @param string $baseUri 73 | */ 74 | public function setBaseUri($baseUri) 75 | { 76 | $this->baseUri = $baseUri ? rtrim($baseUri, '/') : null; 77 | } 78 | 79 | /** 80 | * @return string 81 | */ 82 | public function getUri() 83 | { 84 | return $this->uri; 85 | } 86 | 87 | /** 88 | * @param string $uri 89 | */ 90 | public function setUri($uri) 91 | { 92 | $this->uri = rtrim($uri, '/'); 93 | } 94 | 95 | /** 96 | * @return string 97 | */ 98 | public function getRequestUri() 99 | { 100 | if ($this->requestUri) { 101 | return $this->requestUri; 102 | } 103 | 104 | // GET/DELETE: move parameters into query 105 | if ($this->isGetOrDeleteMethod() && !empty($this->params)) { 106 | $query = http_build_query($this->params); 107 | if ($query !== '') { 108 | $this->uri .= strpos($this->uri, '?') === false ? '?' : '&'; 109 | $this->uri .= $query; 110 | } 111 | } 112 | 113 | $url = $this->baseUri . $this->uri; 114 | $this->requestUri = substr($url, strpos($url, '/', 8)); 115 | return $this->requestUri; 116 | } 117 | 118 | /** 119 | * @return array 120 | */ 121 | public function getHeaders() 122 | { 123 | return $this->headers; 124 | } 125 | 126 | /** 127 | * @param array $headers 128 | */ 129 | public function setHeaders($headers) 130 | { 131 | $this->headers = $headers; 132 | } 133 | 134 | /** 135 | * @return array 136 | */ 137 | public function getParams() 138 | { 139 | return $this->params; 140 | } 141 | 142 | /** 143 | * @param array $params 144 | */ 145 | public function setParams(array $params) 146 | { 147 | $this->params = $params; 148 | } 149 | 150 | /** 151 | * @return string 152 | */ 153 | public function getBodyParams() 154 | { 155 | if ($this->bodyParams === null) { 156 | if ($this->isGetOrDeleteMethod()) { 157 | $this->bodyParams = ''; 158 | } else { 159 | $this->bodyParams = empty($this->params) ? '' : json_encode($this->params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); 160 | } 161 | } 162 | return $this->bodyParams; 163 | } 164 | 165 | protected function isGetOrDeleteMethod() 166 | { 167 | return in_array($this->getMethod(), [self::METHOD_GET, self::METHOD_DELETE], true); 168 | } 169 | 170 | public function __toString() 171 | { 172 | $str = $this->getMethod() . ' ' . $this->getRequestUri(); 173 | $str .= ' with headers=' . json_encode($this->getHeaders(), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 174 | $str .= ' with body=' . $this->getBodyParams(); 175 | return $str; 176 | } 177 | } -------------------------------------------------------------------------------- /tests/IndexTest.php: -------------------------------------------------------------------------------- 1 | easy@kucoin.com 4 | * Date: 2019/6/17 下午3:08 5 | */ 6 | 7 | namespace KuCoin\Futures\SDK\Tests; 8 | 9 | use \KuCoin\Futures\SDK\PublicApi\Index; 10 | 11 | 12 | class IndexTest extends TestCase 13 | { 14 | 15 | protected $apiClass = Index::class; 16 | protected $apiWithAuth = false; 17 | 18 | /** 19 | * 20 | * @dataProvider apiProvider 21 | * @param Index $api 22 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 23 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 24 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 25 | */ 26 | public function testGetList(Index $api) 27 | { 28 | $params = [ 29 | 'symbol' => '.BXBT' 30 | ]; 31 | $data = $api->getList($params); 32 | $this->assertInternalType('array', $data); 33 | foreach ($data['dataList'] as $item) { 34 | $this->assertArrayHasKey('symbol', $item); 35 | $this->assertArrayHasKey('granularity', $item); 36 | $this->assertArrayHasKey('timePoint', $item); 37 | $this->assertArrayHasKey('value', $item); 38 | $this->assertArrayHasKey('decomposionList', $item); 39 | } 40 | } 41 | 42 | /** 43 | * 44 | * @dataProvider apiProvider 45 | * @param Index $api 46 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 47 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 48 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 49 | */ 50 | public function testGetInterests(Index $api) 51 | { 52 | $params = [ 53 | 'symbol' => '.XBTINT' 54 | ]; 55 | $data = $api->getInterests($params); 56 | $this->assertInternalType('array', $data); 57 | foreach ($data['dataList'] as $item) { 58 | $this->assertArrayHasKey('symbol', $item); 59 | $this->assertArrayHasKey('granularity', $item); 60 | $this->assertArrayHasKey('timePoint', $item); 61 | $this->assertArrayHasKey('value', $item); 62 | } 63 | } 64 | 65 | /** 66 | * 67 | * @dataProvider apiProvider 68 | * @param Index $api 69 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 70 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 71 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 72 | */ 73 | public function testGetMarkPrice(Index $api) 74 | { 75 | $data = $api->getMarkPrice('XBTUSDM'); 76 | $this->assertInternalType('array', $data); 77 | $this->assertArrayHasKey('symbol', $data); 78 | $this->assertArrayHasKey('granularity', $data); 79 | $this->assertArrayHasKey('timePoint', $data); 80 | $this->assertArrayHasKey('value', $data); 81 | $this->assertArrayHasKey('indexPrice', $data); 82 | } 83 | 84 | /** 85 | * 86 | * @dataProvider apiProvider 87 | * @param Index $api 88 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 89 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 90 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 91 | */ 92 | public function testGetPremium(Index $api) 93 | { 94 | $params = [ 95 | 'symbol' => '.BXBT' 96 | ]; 97 | $data = $api->getPremium($params); 98 | $this->assertInternalType('array', $data); 99 | foreach ($data['dataList'] as $item) { 100 | $this->assertArrayHasKey('symbol', $item); 101 | $this->assertArrayHasKey('granularity', $item); 102 | $this->assertArrayHasKey('timePoint', $item); 103 | $this->assertArrayHasKey('value', $item); 104 | } 105 | } 106 | 107 | /** 108 | * 109 | * @dataProvider apiProvider 110 | * @param Index $api 111 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 112 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 113 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 114 | */ 115 | public function testGetCurrentFundingRate(Index $api) 116 | { 117 | $data = $api->getCurrentFundingRate('.XBTUSDMFPI8H'); 118 | $this->assertInternalType('array', $data); 119 | $this->assertArrayHasKey('symbol', $data); 120 | $this->assertArrayHasKey('granularity', $data); 121 | $this->assertArrayHasKey('timePoint', $data); 122 | $this->assertArrayHasKey('value', $data); 123 | $this->assertArrayHasKey('predictedValue', $data); 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /src/Api.php: -------------------------------------------------------------------------------- 1 | &self::$skipVerifyTls]); 75 | } 76 | $this->auth = $auth; 77 | $this->http = $http; 78 | } 79 | 80 | /** 81 | * @return string 82 | */ 83 | public static function getBaseUri() 84 | { 85 | return static::$baseUri; 86 | } 87 | 88 | /** 89 | * @param string $baseUri 90 | */ 91 | public static function setBaseUri($baseUri) 92 | { 93 | static::$baseUri = $baseUri; 94 | } 95 | 96 | /** 97 | * @return bool 98 | */ 99 | public static function isSkipVerifyTls() 100 | { 101 | return static::$skipVerifyTls; 102 | } 103 | 104 | /** 105 | * @param bool $skipVerifyTls 106 | */ 107 | public static function setSkipVerifyTls($skipVerifyTls) 108 | { 109 | static::$skipVerifyTls = $skipVerifyTls; 110 | } 111 | 112 | /** 113 | * @return bool 114 | */ 115 | public static function isDebugMode() 116 | { 117 | return self::$debugMode; 118 | } 119 | 120 | /** 121 | * @param bool $debugMode 122 | */ 123 | public static function setDebugMode($debugMode) 124 | { 125 | self::$debugMode = $debugMode; 126 | } 127 | 128 | /** 129 | * @param LoggerInterface $logger 130 | */ 131 | public static function setLogger(LoggerInterface $logger) 132 | { 133 | self::$logger = $logger; 134 | } 135 | 136 | /** 137 | * @return Logger|LoggerInterface 138 | * @throws \Exception 139 | */ 140 | public static function getLogger() 141 | { 142 | if (self::$logger === null) { 143 | self::$logger = new Logger('kucoin-futures-sdk'); 144 | $handler = new RotatingFileHandler(static::getLogPath() . '/kucoin-futures.log', 0, static::$logLevel); 145 | $formatter = new LineFormatter(null, null, false, true); 146 | $handler->setFormatter($formatter); 147 | self::$logger->pushHandler($handler); 148 | } 149 | return self::$logger; 150 | } 151 | 152 | /** 153 | * @return string 154 | */ 155 | public static function getLogPath() 156 | { 157 | return self::$logPath; 158 | } 159 | 160 | /** 161 | * @param string $logPath 162 | */ 163 | public static function setLogPath($logPath) 164 | { 165 | self::$logPath = $logPath; 166 | } 167 | 168 | /** 169 | * @return int 170 | */ 171 | public static function getLogLevel() 172 | { 173 | return self::$logLevel; 174 | } 175 | 176 | /** 177 | * @param int $logLevel 178 | */ 179 | public static function setLogLevel($logLevel) 180 | { 181 | self::$logLevel = $logLevel; 182 | } 183 | 184 | /** 185 | * @param array $headers 186 | */ 187 | public static function setCustomHeaders(array $headers) 188 | { 189 | self::$customHeaders = $headers; 190 | } 191 | 192 | /** 193 | * @return array 194 | */ 195 | public static function getCustomHeaders() 196 | { 197 | return self::$customHeaders; 198 | } 199 | 200 | /** 201 | * @param string $method 202 | * @param string $uri 203 | * @param array $params 204 | * @param array $headers 205 | * @param int $timeout 206 | * @return Response 207 | * @throws Exceptions\HttpException 208 | * @throws Exceptions\InvalidApiUriException 209 | */ 210 | public function call($method, $uri, array $params = [], array $headers = [], $timeout = 30) 211 | { 212 | $request = new Request(); 213 | $request->setMethod($method); 214 | $request->setBaseUri(static::getBaseUri()); 215 | $request->setUri($uri); 216 | $request->setParams($params); 217 | 218 | if ($this->auth) { 219 | $authHeaders = $this->auth->getHeaders( 220 | $request->getMethod(), 221 | $request->getRequestUri(), 222 | $request->getBodyParams() 223 | ); 224 | $headers = array_merge($headers, $authHeaders); 225 | } 226 | $headers['User-Agent'] = 'KuCoin-Futures-PHP-SDK/' . static::VERSION; 227 | 228 | if (self::$customHeaders) { 229 | $headers = array_merge($headers, self::$customHeaders); 230 | } 231 | 232 | $request->setHeaders($headers); 233 | 234 | $requestId = uniqid(); 235 | 236 | if (self::isDebugMode()) { 237 | static::getLogger()->debug(sprintf('Sent a HTTP request#%s: %s', $requestId, $request)); 238 | } 239 | $requestStart = microtime(true); 240 | $response = $this->http->request($request, $timeout); 241 | if (self::isDebugMode()) { 242 | $cost = (microtime(true) - $requestStart) * 1000; 243 | static::getLogger()->debug(sprintf('Received a HTTP response#%s: cost %.2fms, %s', $requestId, $cost, $response)); 244 | } 245 | 246 | return $response; 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/PublicApi/Symbol.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/ticker', compact('symbol')); 27 | return $response->getApiData(); 28 | } 29 | 30 | /** 31 | * Get the snapshot details of a symbol. 32 | * 33 | * @param string $symbol 34 | * @return array 35 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 36 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 37 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 38 | */ 39 | public function getLevel2Snapshot($symbol) 40 | { 41 | $response = $this->call(Request::METHOD_GET, '/api/v1/level2/snapshot', compact('symbol')); 42 | return $response->getApiData(); 43 | } 44 | 45 | /** 46 | * Get the snapshot details of a symbol. 47 | * 48 | * @param string $symbol 49 | * @return array 50 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 51 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 52 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 53 | */ 54 | public function getLevel3Snapshot($symbol) 55 | { 56 | $response = $this->call(Request::METHOD_GET, '/api/v1/level3/snapshot', compact('symbol')); 57 | return $response->getApiData(); 58 | } 59 | 60 | /** 61 | * Get the snapshot details of a symbol. 62 | * 63 | * @param string $symbol 64 | * @return array 65 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 66 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 67 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 68 | */ 69 | public function getV2Level3Snapshot($symbol) 70 | { 71 | $response = $this->call(Request::METHOD_GET, '/api/v2/level3/snapshot', compact('symbol')); 72 | return $response->getApiData(); 73 | } 74 | 75 | /** 76 | * Get the level2 message of a symbol. 77 | * 78 | * @param string $symbol 79 | * @param int $start 80 | * @param int $end 81 | * @return array 82 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 83 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 84 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 85 | * @deprecated 86 | */ 87 | public function getLevel2Message($symbol, $start, $end) 88 | { 89 | $response = $this->call(Request::METHOD_GET, '/api/v1/level2/message/query', 90 | compact('symbol', 'start', 'end') 91 | ); 92 | return $response->getApiData(); 93 | } 94 | 95 | /** 96 | * Get the level3 message of a symbol. 97 | * @param string $symbol 98 | * @param int $start 99 | * @param int $end 100 | * @return array 101 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 102 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 103 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 104 | * @deprecated 105 | */ 106 | public function getLevel3Message($symbol, $start, $end) 107 | { 108 | $response = $this->call(Request::METHOD_GET, '/api/v1/level3/message/query', 109 | compact('symbol', 'start', 'end') 110 | ); 111 | return $response->getApiData(); 112 | } 113 | 114 | /** 115 | * Get the trade history details of a symbol. 116 | * 117 | * @param string $symbol 118 | * @return array 119 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 120 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 121 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 122 | */ 123 | public function getTradeHistory($symbol) 124 | { 125 | $response = $this->call(Request::METHOD_GET, '/api/v1/trade/history', compact('symbol')); 126 | return $response->getApiData(); 127 | } 128 | 129 | /** 130 | * Get KLines for a symbol. Data are returned in grouped buckets based on granularity. 131 | * 132 | * The granularity (granularity parameter of K-line) represents the number of minutes, 133 | * the available granularity scope is: 1,5,15,30,60,120,240,480,720,1440,10080. 134 | * 135 | * @param string $symbol 136 | * @param int $from 137 | * @param int $to 138 | * @param int $granularity 139 | * @return array 140 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 141 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 142 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 143 | */ 144 | public function getKLines($symbol, $from, $to, $granularity) 145 | { 146 | $response = $this->call( 147 | Request::METHOD_GET, 148 | '/api/v1/kline/query', 149 | compact('symbol', 'from', 'to', 'granularity') 150 | ); 151 | return $response->getApiData(); 152 | } 153 | 154 | /** 155 | * Get the depth20 of level2. 156 | * @param string $symbol 157 | * @return mixed 158 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 159 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 160 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 161 | */ 162 | public function getLevel2Depth20($symbol) 163 | { 164 | $response = $this->call(Request::METHOD_GET, '/api/v1/level2/depth20', ['symbol' => $symbol]); 165 | return $response->getApiData(); 166 | } 167 | 168 | /** 169 | * Get the depth100 of level2. 170 | * @param string $symbol 171 | * @return mixed 172 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 173 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 174 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 175 | */ 176 | public function getLevel2Depth100($symbol) 177 | { 178 | $response = $this->call(Request::METHOD_GET, '/api/v1/level2/depth100', ['symbol' => $symbol]); 179 | return $response->getApiData(); 180 | } 181 | 182 | /** 183 | * Get Public Funding History. 184 | * 185 | * @param $symbol 186 | * @param $from 187 | * @param $to 188 | * @return mixed|null 189 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 190 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 191 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 192 | */ 193 | public function getFundingRates($symbol, $from, $to) 194 | { 195 | $params = compact('symbol', 'from', 'to'); 196 | $response = $this->call(Request::METHOD_GET, '/api/v1/contract/funding-rates', $params); 197 | return $response->getApiData(); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /tests/SymbolTest.php: -------------------------------------------------------------------------------- 1 | getTicker('XBTUSDM'); 23 | $this->assertInternalType('array', $data); 24 | $this->assertArrayHasKey('sequence', $data); 25 | $this->assertArrayHasKey('symbol', $data); 26 | $this->assertArrayHasKey('size', $data); 27 | $this->assertArrayHasKey('price', $data); 28 | $this->assertArrayHasKey('bestBidSize', $data); 29 | $this->assertArrayHasKey('bestAskSize', $data); 30 | $this->assertArrayHasKey('ts', $data); 31 | } 32 | 33 | /** 34 | * @dataProvider apiProvider 35 | * @param Symbol $api 36 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 37 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 38 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 39 | */ 40 | public function testGetLevel2Snapshot(Symbol $api) 41 | { 42 | $data = $api->getLevel2Snapshot('XBTUSDM'); 43 | $this->assertInternalType('array', $data); 44 | $this->assertArrayHasKey('sequence', $data); 45 | $this->assertArrayHasKey('symbol', $data); 46 | $this->assertArrayHasKey('asks', $data); 47 | $this->assertArrayHasKey('bids', $data); 48 | } 49 | 50 | /** 51 | * @dataProvider apiProvider 52 | * @param Symbol $api 53 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 54 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 55 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 56 | */ 57 | public function testGetLevel3Snapshot(Symbol $api) 58 | { 59 | $data = $api->getLevel3Snapshot('XBTUSDM'); 60 | $this->assertInternalType('array', $data); 61 | $this->assertArrayHasKey('sequence', $data); 62 | $this->assertArrayHasKey('symbol', $data); 63 | $this->assertArrayHasKey('asks', $data); 64 | $this->assertArrayHasKey('bids', $data); 65 | } 66 | 67 | /** 68 | * @dataProvider apiProvider 69 | * @param Symbol $api 70 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 71 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 72 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 73 | */ 74 | public function testGetV2Level3Snapshot(Symbol $api) 75 | { 76 | $data = $api->getV2Level3Snapshot('XBTUSDM'); 77 | $this->assertInternalType('array', $data); 78 | $this->assertArrayHasKey('sequence', $data); 79 | $this->assertArrayHasKey('symbol', $data); 80 | $this->assertArrayHasKey('asks', $data); 81 | $this->assertArrayHasKey('bids', $data); 82 | $this->assertArrayHasKey('ts', $data); 83 | } 84 | 85 | /** 86 | * @dataProvider apiProvider 87 | * @param Symbol $api 88 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 89 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 90 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 91 | */ 92 | public function testGetLevel2Message(Symbol $api) 93 | { 94 | $data = $api->getLevel2Message('XBTUSDM', 1, 100); 95 | $this->assertInternalType('array', $data); 96 | foreach ($data as $item) { 97 | $this->assertArrayHasKey('symbol', $item); 98 | $this->assertArrayHasKey('sequence', $item); 99 | $this->assertArrayHasKey('change', $item); 100 | } 101 | } 102 | 103 | /** 104 | * @dataProvider apiProvider 105 | * @param Symbol $api 106 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 107 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 108 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 109 | */ 110 | public function testGetLevel3Message(Symbol $api) 111 | { 112 | // $data = $api->getLevel3Message('XBTUSDM', 1, 100); 113 | // $this->assertInternalType('array', $data); 114 | // foreach ($data as $item) { 115 | // $this->assertArrayHasKey('symbol', $item); 116 | // $this->assertArrayHasKey('sequence', $item); 117 | // $this->assertArrayHasKey('orderId', $item); 118 | // $this->assertArrayHasKey('type', $item); 119 | // } 120 | } 121 | 122 | /** 123 | * @dataProvider apiProvider 124 | * @param Symbol $api 125 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 126 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 127 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 128 | */ 129 | public function testGetTradeHistory(Symbol $api) 130 | { 131 | $data = $api->getTradeHistory('XBTUSDM'); 132 | $this->assertInternalType('array', $data); 133 | foreach ($data as $item) { 134 | $this->assertArrayHasKey('sequence', $item); 135 | $this->assertArrayHasKey('tradeId', $item); 136 | $this->assertArrayHasKey('takerOrderId', $item); 137 | $this->assertArrayHasKey('makerOrderId', $item); 138 | $this->assertArrayHasKey('price', $item); 139 | } 140 | } 141 | 142 | /** 143 | * @dataProvider apiProvider 144 | * @param Symbol $api 145 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 146 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 147 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 148 | */ 149 | public function testGetLevel2Depth20(Symbol $api) 150 | { 151 | $data = $api->getLevel2Depth20('XBTUSDM'); 152 | 153 | $this->assertInternalType('array', $data); 154 | $this->assertArrayHasKey('symbol', $data); 155 | $this->assertArrayHasKey('sequence', $data); 156 | $this->assertArrayHasKey('asks', $data); 157 | $this->assertInternalType('array', $data['asks']); 158 | $this->assertInternalType('array', $data['bids']); 159 | } 160 | 161 | /** 162 | * @dataProvider apiProvider 163 | * @param Symbol $api 164 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 165 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 166 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 167 | */ 168 | public function testGetLevel2Depth100(Symbol $api) 169 | { 170 | $data = $api->getLevel2Depth100('XBTUSDM'); 171 | 172 | $this->assertInternalType('array', $data); 173 | $this->assertArrayHasKey('symbol', $data); 174 | $this->assertArrayHasKey('sequence', $data); 175 | $this->assertArrayHasKey('asks', $data); 176 | $this->assertInternalType('array', $data['asks']); 177 | $this->assertInternalType('array', $data['bids']); 178 | } 179 | 180 | /** 181 | * @dataProvider apiProvider 182 | * @param Symbol $api 183 | * @return void 184 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 185 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 186 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 187 | */ 188 | public function testGetFundingRates(Symbol $api) 189 | { 190 | $from = strtotime(date('Y-m-d', time() - 86400)); 191 | $to = $from + 86400; 192 | $data = $api->getFundingRates('ETHUSDTM', $from * 1000, $to * 1000); 193 | $this->assertInternalType('array', $data); 194 | foreach ($data as $item) { 195 | $this->assertInternalType('array', $item); 196 | $this->assertArrayHasKey('symbol', $item); 197 | $this->assertArrayHasKey('fundingRate', $item); 198 | $this->assertArrayHasKey('timepoint', $item); 199 | } 200 | } 201 | } -------------------------------------------------------------------------------- /src/PrivateApi/Account.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/account-overview', $params); 26 | return $response->getApiData(); 27 | } 28 | 29 | /** 30 | * Get a transaction history of accounts. 31 | * 32 | * @param array $params 33 | * @param array $pagination 34 | * @return array 35 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 36 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 37 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 38 | */ 39 | public function getTransactionHistory(array $params = [], array $pagination = []) 40 | { 41 | $response = $this->call(Request::METHOD_GET, '/api/v1/transaction-history', $params + $pagination); 42 | return $response->getApiData(); 43 | } 44 | 45 | /** 46 | * KuCoin transfer to kuCoin futures account. 47 | * @param string amount 48 | * @return array 49 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 50 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 51 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 52 | */ 53 | public function transferIn($amount) 54 | { 55 | $response = $this->call(Request::METHOD_POST, '/api/v1/transfer-in', compact('amount')); 56 | return $response->getApiData(); 57 | } 58 | 59 | /** 60 | * kuCoin futures transfer to KuCoin account. 61 | * 62 | * @param string bizNo 63 | * @param string amount 64 | * @return array 65 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 66 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 67 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 68 | */ 69 | public function transferOut($bizNo, $amount) 70 | { 71 | $response = $this->call(Request::METHOD_POST, '/api/v1/transfer-out', compact('bizNo', 'amount')); 72 | return $response->getApiData(); 73 | } 74 | 75 | /** 76 | * Cancel an transfer out. 77 | * @param string $applyId 78 | * @return array 79 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 80 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 81 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 82 | * @deprecated 83 | */ 84 | public function cancelTransferOut($applyId) 85 | { 86 | $response = $this->call(Request::METHOD_DELETE, '/api/v1/cancel/transfer-out?applyId=' . $applyId); 87 | return $response->getApiData(); 88 | } 89 | 90 | /** 91 | * Get a transfer list. 92 | * 93 | * @param array $params 94 | * @param array $pagination 95 | * @return array 96 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 97 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 98 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 99 | */ 100 | public function getTransferList(array $params = [], array $pagination = []) 101 | { 102 | $response = $this->call(Request::METHOD_GET, '/api/v1/transfer-list', $params + $pagination); 103 | return $response->getApiData(); 104 | } 105 | 106 | /** 107 | * kuCoin futures transfer to KuCoin account. 108 | * [It is recommended to call transferOutV3() instead] 109 | * @param string bizNo 110 | * @param string amount 111 | * @param string currency 112 | * @return array 113 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 114 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 115 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 116 | */ 117 | public function transferOutV2($bizNo, $amount, $currency) 118 | { 119 | $response = $this->call(Request::METHOD_POST, '/api/v2/transfer-out', compact('bizNo', 'amount', 'currency')); 120 | return $response->getApiData(); 121 | } 122 | 123 | /** 124 | * Get list of Futures APIs pertaining to a sub-accounts. 125 | * 126 | * @param array $params 127 | * @return array 128 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 129 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 130 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 131 | */ 132 | public function getSubApikey(array $params) 133 | { 134 | $response = $this->call(Request::METHOD_GET, '/api/v1/sub/api-key', $params); 135 | return $response->getApiData(); 136 | } 137 | 138 | /** 139 | * Create futures APIs for sub-accounts. 140 | * 141 | * @param array $params 142 | * @return array 143 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 144 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 145 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 146 | */ 147 | public function createSubApikey(array $params) 148 | { 149 | $response = $this->call(Request::METHOD_POST, '/api/v1/sub/api-key', $params); 150 | return $response->getApiData(); 151 | } 152 | 153 | /** 154 | * Modify futures APIs for sub-accounts. 155 | * 156 | * @param array $params 157 | * @return array 158 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 159 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 160 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 161 | */ 162 | public function modifySubApikey(array $params) 163 | { 164 | $response = $this->call(Request::METHOD_POST, '/api/v1/sub/api-key/update', $params); 165 | return $response->getApiData(); 166 | } 167 | 168 | /** 169 | * Delete futures APIs for sub-accounts. 170 | * 171 | * @param array $params 172 | * @return array 173 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 174 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 175 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 176 | */ 177 | public function deleteSubApikey(array $params) 178 | { 179 | $response = $this->call(Request::METHOD_DELETE, '/api/v1/sub/api-key', $params); 180 | return $response->getApiData(); 181 | } 182 | 183 | /** 184 | * kuCoin futures transfer to KuCoin account. 185 | * 186 | * @param string recAccountType 187 | * @param string amount 188 | * @param string currency 189 | * @return array 190 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 191 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 192 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 193 | */ 194 | public function transferOutV3($recAccountType, $amount, $currency) 195 | { 196 | $response = $this->call(Request::METHOD_POST, '/api/v3/transfer-out', compact('recAccountType', 'amount', 'currency')); 197 | return $response->getApiData(); 198 | } 199 | 200 | /** 201 | * Get all account asset information. 202 | * @param string $currency 203 | * @return array 204 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 205 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 206 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 207 | */ 208 | public function getAccountOverviewAll($currency) 209 | { 210 | $response = $this->call(Request::METHOD_GET, '/api/v1/account-overview-all', compact('currency')); 211 | return $response->getApiData(); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/PrivateApi/Position.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_GET, '/api/v1/positions'); 26 | return $response->getApiData(); 27 | } 28 | 29 | /** 30 | * Get the position details of a symbol. 31 | * 32 | * @param string $symbol 33 | * @return array 34 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 35 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 36 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 37 | */ 38 | public function getDetail($symbol) 39 | { 40 | $response = $this->call(Request::METHOD_GET, '/api/v1/position', compact('symbol')); 41 | return $response->getApiData(); 42 | } 43 | 44 | /** 45 | * Change auto append status. 46 | * 47 | * @param string $symbol 48 | * @param boolean $status 49 | * @return array 50 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 51 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 52 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 53 | */ 54 | public function changeAutoAppendStatus($symbol, $status) 55 | { 56 | $response = $this->call(Request::METHOD_POST, '/api/v1/position/margin/auto-deposit-status', 57 | compact('symbol', 'status') 58 | ); 59 | return $response->getApiData(); 60 | } 61 | 62 | /** 63 | * Get whether to automatically add margin status. 64 | * 65 | * @deprecated 66 | * 67 | * @param string $symbol 68 | * @return array 69 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 70 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 71 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 72 | */ 73 | public function getMarginAppend($symbol, $status) 74 | { 75 | $response = $this->call(Request::METHOD_GET, '/api/v1/position/margin/append', 76 | compact('symbol', 'status') 77 | ); 78 | return $response->getApiData(); 79 | } 80 | 81 | /** 82 | * Margin Append. 83 | * 84 | * @param array $params 85 | * @return array 86 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 87 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 88 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 89 | */ 90 | public function marginAppend(array $params) 91 | { 92 | $response = $this->call(Request::METHOD_POST, 93 | '/api/v1/position/margin/deposit-margin', $params 94 | ); 95 | return $response->getApiData(); 96 | } 97 | 98 | 99 | /** 100 | * Get Max Withdraw Margin. 101 | * 102 | * @param $symbol 103 | * @return mixed|null 104 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 105 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 106 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 107 | */ 108 | public function getMaxWithdrawMargin($symbol) 109 | { 110 | $response = $this->call(Request::METHOD_GET, '/api/v1/margin/maxWithdrawMargin', compact('symbol')); 111 | return $response->getApiData(); 112 | } 113 | 114 | /** 115 | * Remove Margin Manually. 116 | * 117 | * @param $symbol 118 | * @param $withdrawAmount 119 | * @return mixed|null 120 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 121 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 122 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 123 | */ 124 | public function withdrawMargin($symbol, $withdrawAmount) 125 | { 126 | $response = $this->call(Request::METHOD_POST, '/api/v1/margin/withdrawMargin', compact('symbol', 'withdrawAmount')); 127 | return $response->getApiData(); 128 | } 129 | 130 | /** 131 | * Get Positions History. 132 | * 133 | * @param array $params 134 | * @return \KuCoin\Futures\SDK\Http\Response 135 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 136 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 137 | */ 138 | public function getHistoryPositions(array $params) 139 | { 140 | $response = $this->call(Request::METHOD_GET, '/api/v1/history-positions', $params); 141 | return $response->getApiData(); 142 | } 143 | 144 | /** 145 | * Get Maximum Open Position Size. 146 | * 147 | * @param $symbol 148 | * @param $price 149 | * @param $leverage 150 | * @return mixed|null 151 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 152 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 153 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 154 | */ 155 | public function getMaxOpenSize($symbol, $price, $leverage) 156 | { 157 | $response = $this->call(Request::METHOD_GET, '/api/v2/getMaxOpenSize', compact('symbol', 'price', 'leverage')); 158 | return $response->getApiData(); 159 | } 160 | 161 | /** 162 | * This interface can modify the current symbol’s cross-margin leverage multiple. 163 | * 164 | * @param $symbol 165 | * @param $leverage 166 | * @return mixed|null 167 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 168 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 169 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 170 | */ 171 | public function modifyCrossUserLeverage($symbol, $leverage) 172 | { 173 | $response = $this->call(Request::METHOD_POST, '/api/v2/changeCrossUserLeverage', compact('symbol', 'leverage')); 174 | return $response->getApiData(); 175 | } 176 | 177 | /** 178 | * This interface can query the current symbol’s cross-margin leverage multiple. 179 | * 180 | * @param $symbol 181 | * @return mixed|null 182 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 183 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 184 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 185 | */ 186 | public function getCrossUserLeverage($symbol) 187 | { 188 | $response = $this->call(Request::METHOD_GET, '/api/v2/getCrossUserLeverage', compact('symbol')); 189 | return $response->getApiData(); 190 | } 191 | 192 | /** 193 | * This interface can modify the margin mode of the current symbol 194 | * 195 | * @param $symbol 196 | * @param $marginMode 197 | * @return mixed|null 198 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 199 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 200 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 201 | */ 202 | public function modifyMarginMode($symbol, $marginMode) 203 | { 204 | $response = $this->call(Request::METHOD_POST, '/api/v2/position/changeMarginMode', compact('symbol', 'marginMode')); 205 | return $response->getApiData(); 206 | } 207 | 208 | /** 209 | * This interface can query the margin mode of the current symbol. 210 | * 211 | * @param $symbol 212 | * @return mixed|null 213 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 214 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 215 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 216 | */ 217 | public function getMarginMode($symbol) 218 | { 219 | $response = $this->call(Request::METHOD_GET, '/api/v2/position/getMarginMode', compact('symbol')); 220 | return $response->getApiData(); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /tests/ContractTest.php: -------------------------------------------------------------------------------- 1 | easy@kucoin.com 4 | * Date: 2019/6/17 下午3:08 5 | */ 6 | 7 | namespace KuCoin\Futures\SDK\Tests; 8 | 9 | use KuCoin\Futures\SDK\PublicApi\Contract; 10 | 11 | 12 | class ContractTest extends TestCase 13 | { 14 | 15 | protected $apiClass = Contract::class; 16 | protected $apiWithAuth = false; 17 | 18 | /** 19 | * @dataProvider apiProvider 20 | * @param Contract $api 21 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 22 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 23 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 24 | */ 25 | public function testGetList(Contract $api) 26 | { 27 | $data = $api->getList(); 28 | $this->assertInternalType('array', $data); 29 | 30 | foreach ($data as $item) { 31 | $this->assertInternalType('array', $item); 32 | 33 | $this->assertArrayHasKey('symbol', $item); 34 | $this->assertArrayHasKey('rootSymbol', $item); 35 | $this->assertArrayHasKey('type', $item); 36 | $this->assertArrayHasKey('firstOpenDate', $item); 37 | $this->assertArrayHasKey('expireDate', $item); 38 | $this->assertArrayHasKey('settleDate', $item); 39 | $this->assertArrayHasKey('baseCurrency', $item); 40 | $this->assertArrayHasKey('quoteCurrency', $item); 41 | $this->assertArrayHasKey('settleCurrency', $item); 42 | $this->assertArrayHasKey('maxOrderQty', $item); 43 | $this->assertArrayHasKey('maxPrice', $item); 44 | $this->assertArrayHasKey('lotSize', $item); 45 | $this->assertArrayHasKey('tickSize', $item); 46 | $this->assertArrayHasKey('indexPriceTickSize', $item); 47 | $this->assertArrayHasKey('multiplier', $item); 48 | $this->assertArrayHasKey('initialMargin', $item); 49 | $this->assertArrayHasKey('maintainMargin', $item); 50 | $this->assertArrayHasKey('maxRiskLimit', $item); 51 | $this->assertArrayHasKey('minRiskLimit', $item); 52 | $this->assertArrayHasKey('riskStep', $item); 53 | $this->assertArrayHasKey('makerFeeRate', $item); 54 | $this->assertArrayHasKey('takerFeeRate', $item); 55 | $this->assertArrayHasKey('takerFixFee', $item); 56 | $this->assertArrayHasKey('makerFixFee', $item); 57 | $this->assertArrayHasKey('settlementFee', $item); 58 | $this->assertArrayHasKey('isDeleverage', $item); 59 | $this->assertArrayHasKey('isQuanto', $item); 60 | $this->assertArrayHasKey('isInverse', $item); 61 | $this->assertArrayHasKey('markMethod', $item); 62 | $this->assertArrayHasKey('fairMethod', $item); 63 | $this->assertArrayHasKey('fundingBaseSymbol', $item); 64 | $this->assertArrayHasKey('fundingQuoteSymbol', $item); 65 | $this->assertArrayHasKey('fundingRateSymbol', $item); 66 | $this->assertArrayHasKey('indexSymbol', $item); 67 | $this->assertArrayHasKey('settlementSymbol', $item); 68 | $this->assertArrayHasKey('status', $item); 69 | $this->assertArrayHasKey('fundingFeeRate', $item); 70 | $this->assertArrayHasKey('predictedFundingFeeRate', $item); 71 | $this->assertArrayHasKey('openInterest', $item); 72 | $this->assertArrayHasKey('turnoverOf24h', $item); 73 | $this->assertArrayHasKey('volumeOf24h', $item); 74 | $this->assertArrayHasKey('markPrice', $item); 75 | $this->assertArrayHasKey('indexPrice', $item); 76 | $this->assertArrayHasKey('lastTradePrice', $item); 77 | $this->assertArrayHasKey('nextFundingRateTime', $item); 78 | $this->assertArrayHasKey('maxLeverage', $item); 79 | $this->assertArrayHasKey('lowPrice', $item); 80 | $this->assertArrayHasKey('highPrice', $item); 81 | $this->assertArrayHasKey('priceChgPct', $item); 82 | $this->assertArrayHasKey('priceChg', $item); 83 | } 84 | } 85 | 86 | /** 87 | * @dataProvider apiProvider 88 | * @param Contract $api 89 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 90 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 91 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 92 | */ 93 | public function testGetDetail(Contract $api) 94 | { 95 | $data = $api->getDetail('XBTUSDM'); 96 | $this->assertInternalType('array', $data); 97 | 98 | $this->assertArrayHasKey('symbol', $data); 99 | $this->assertArrayHasKey('rootSymbol', $data); 100 | $this->assertArrayHasKey('type', $data); 101 | $this->assertArrayHasKey('firstOpenDate', $data); 102 | $this->assertArrayHasKey('expireDate', $data); 103 | $this->assertArrayHasKey('settleDate', $data); 104 | $this->assertArrayHasKey('baseCurrency', $data); 105 | $this->assertArrayHasKey('quoteCurrency', $data); 106 | $this->assertArrayHasKey('settleCurrency', $data); 107 | $this->assertArrayHasKey('maxOrderQty', $data); 108 | $this->assertArrayHasKey('maxPrice', $data); 109 | $this->assertArrayHasKey('lotSize', $data); 110 | $this->assertArrayHasKey('tickSize', $data); 111 | $this->assertArrayHasKey('indexPriceTickSize', $data); 112 | $this->assertArrayHasKey('multiplier', $data); 113 | $this->assertArrayHasKey('initialMargin', $data); 114 | $this->assertArrayHasKey('maintainMargin', $data); 115 | $this->assertArrayHasKey('maxRiskLimit', $data); 116 | $this->assertArrayHasKey('minRiskLimit', $data); 117 | $this->assertArrayHasKey('riskStep', $data); 118 | $this->assertArrayHasKey('makerFeeRate', $data); 119 | $this->assertArrayHasKey('takerFeeRate', $data); 120 | $this->assertArrayHasKey('takerFixFee', $data); 121 | $this->assertArrayHasKey('makerFixFee', $data); 122 | $this->assertArrayHasKey('settlementFee', $data); 123 | $this->assertArrayHasKey('isDeleverage', $data); 124 | $this->assertArrayHasKey('isQuanto', $data); 125 | $this->assertArrayHasKey('isInverse', $data); 126 | $this->assertArrayHasKey('markMethod', $data); 127 | $this->assertArrayHasKey('fairMethod', $data); 128 | $this->assertArrayHasKey('fundingBaseSymbol', $data); 129 | $this->assertArrayHasKey('fundingQuoteSymbol', $data); 130 | $this->assertArrayHasKey('fundingRateSymbol', $data); 131 | $this->assertArrayHasKey('indexSymbol', $data); 132 | $this->assertArrayHasKey('settlementSymbol', $data); 133 | $this->assertArrayHasKey('status', $data); 134 | $this->assertArrayHasKey('fundingFeeRate', $data); 135 | $this->assertArrayHasKey('predictedFundingFeeRate', $data); 136 | $this->assertArrayHasKey('openInterest', $data); 137 | $this->assertArrayHasKey('turnoverOf24h', $data); 138 | $this->assertArrayHasKey('volumeOf24h', $data); 139 | $this->assertArrayHasKey('markPrice', $data); 140 | $this->assertArrayHasKey('indexPrice', $data); 141 | $this->assertArrayHasKey('lastTradePrice', $data); 142 | $this->assertArrayHasKey('nextFundingRateTime', $data); 143 | $this->assertArrayHasKey('maxLeverage', $data); 144 | $this->assertArrayHasKey('lowPrice', $data); 145 | $this->assertArrayHasKey('highPrice', $data); 146 | $this->assertArrayHasKey('priceChgPct', $data); 147 | $this->assertArrayHasKey('priceChg', $data); 148 | } 149 | 150 | /** 151 | * @dataProvider apiProvider 152 | * 153 | * @param Contract $api 154 | * @return void 155 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 156 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 157 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 158 | */ 159 | public function testGetAllTickers(Contract $api) 160 | { 161 | $data = $api->getAllTickers(); 162 | $this->assertInternalType('array', $data); 163 | foreach ($data as $item) { 164 | $this->assertArrayHasKey('symbol', $item); 165 | $this->assertArrayHasKey('sequence', $item); 166 | $this->assertArrayHasKey('side', $item); 167 | $this->assertArrayHasKey('size', $item); 168 | $this->assertArrayHasKey('tradeId', $item); 169 | $this->assertArrayHasKey('price', $item); 170 | $this->assertArrayHasKey('bestBidPrice', $item); 171 | $this->assertArrayHasKey('bestBidSize', $item); 172 | $this->assertArrayHasKey('bestAskPrice', $item); 173 | $this->assertArrayHasKey('bestAskSize', $item); 174 | $this->assertArrayHasKey('ts', $item); 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /src/PrivateApi/Order.php: -------------------------------------------------------------------------------- 1 | call(Request::METHOD_POST, '/api/v1/orders', $order); 30 | return $response->getApiData(); 31 | } 32 | 33 | /** 34 | * Cancel an order. 35 | * 36 | * @param string $orderId 37 | * @return array 38 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 39 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 40 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 41 | */ 42 | public function cancel($orderId) 43 | { 44 | $response = $this->call(Request::METHOD_DELETE, '/api/v1/orders/' . $orderId); 45 | return $response->getApiData(); 46 | } 47 | 48 | /** 49 | * Batch cancel orders. 50 | * 51 | * @param string|null $symbol 52 | * @return array 53 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 54 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 55 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 56 | */ 57 | public function batchCancel($symbol = null) 58 | { 59 | $response = $this->call(Request::METHOD_DELETE, '/api/v1/orders', compact('symbol')); 60 | return $response->getApiData(); 61 | } 62 | 63 | /** 64 | * Batch cancel stop orders. 65 | * 66 | * @param string|null $symbol 67 | * @return array 68 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 69 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 70 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 71 | */ 72 | public function stopOrders($symbol = null) 73 | { 74 | $response = $this->call(Request::METHOD_DELETE, '/api/v1/stopOrders', compact('symbol')); 75 | return $response->getApiData(); 76 | } 77 | 78 | /** 79 | * List orders. 80 | * 81 | * @param array $params 82 | * @param array $pagination 83 | * @return array 84 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 85 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 86 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 87 | */ 88 | public function getList(array $params = [], array $pagination = []) 89 | { 90 | $response = $this->call(Request::METHOD_GET, '/api/v1/orders', $params + $pagination); 91 | return $response->getApiData(); 92 | } 93 | 94 | /** 95 | * Stop orders list. 96 | * 97 | * @param array $params 98 | * @param array $pagination 99 | * @return array 100 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 101 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 102 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 103 | */ 104 | public function getStopOrders(array $params = [], array $pagination = []) 105 | { 106 | $response = $this->call(Request::METHOD_GET, '/api/v1/stopOrders', $params + $pagination); 107 | return $response->getApiData(); 108 | } 109 | 110 | /** 111 | * 24 hour done of orders. 112 | * 113 | * @param array $params 114 | * @param array $pagination 115 | * @return array 116 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 117 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 118 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 119 | */ 120 | public function getRecentDoneOrders(array $params = [], array $pagination = []) 121 | { 122 | $response = $this->call(Request::METHOD_GET, '/api/v1/recentDoneOrders', $params + $pagination); 123 | return $response->getApiData(); 124 | } 125 | 126 | /** 127 | * Get an order. 128 | * 129 | * @param string $orderId 130 | * @return array 131 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 132 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 133 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 134 | */ 135 | public function getDetail($orderId) 136 | { 137 | $response = $this->call(Request::METHOD_GET, '/api/v1/orders/' . $orderId, []); 138 | return $response->getApiData(); 139 | } 140 | 141 | /** 142 | * Get open order statistics. 143 | * 144 | * @param string|null $symbol 145 | * @return array 146 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 147 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 148 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 149 | */ 150 | public function getOpenOrderStatistics($symbol = null) 151 | { 152 | $response = $this->call(Request::METHOD_GET, '/api/v1/openOrderStatistics', compact('symbol')); 153 | return $response->getApiData(); 154 | } 155 | 156 | /** 157 | * Get an order By ClientOid. 158 | * 159 | * @param $clientOid 160 | * @return array 161 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 162 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 163 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 164 | */ 165 | public function getDetailByClientOid($clientOid) 166 | { 167 | $response = $this->call(Request::METHOD_GET, '/api/v1/orders/byClientOid', ['clientOid' => $clientOid]); 168 | return $response->getApiData(); 169 | } 170 | 171 | /** 172 | * Cancel Order by clientOid. 173 | * 174 | * @param $clientOid 175 | * @param $symbol 176 | * @return array 177 | * @throws BusinessException 178 | * @throws HttpException 179 | * @throws InvalidApiUriException 180 | */ 181 | public function cancelByClientOid($clientOid, $symbol) 182 | { 183 | $response = $this->call(Request::METHOD_DELETE, '/api/v1/orders/client-order/' . $clientOid, ['symbol' => $symbol]); 184 | return $response->getApiData(); 185 | } 186 | 187 | /** 188 | * Order test endpoint, the request parameters and return parameters of this endpoint are exactly the same as the order endpoint, and can be used to verify whether the signature is correct and other operations. After placing an order, the order will not enter the matching system, and the order cannot be queried. 189 | * 190 | * @param array $order 191 | * @return mixed|null 192 | * @throws BusinessException 193 | * @throws HttpException 194 | * @throws InvalidApiUriException 195 | */ 196 | public function createTest(array $order) 197 | { 198 | $response = $this->call(Request::METHOD_POST, '/api/v1/orders/test', $order); 199 | return $response->getApiData(); 200 | } 201 | 202 | /** 203 | * You can place up to 20 orders at one time, including limit orders, market orders, and stop orders. 204 | * 205 | * @param array $orders 206 | * @return mixed|null 207 | * @throws BusinessException 208 | * @throws HttpException 209 | * @throws InvalidApiUriException 210 | */ 211 | public function createMultiOrders(array $orders) 212 | { 213 | $response = $this->call(Request::METHOD_POST, '/api/v1/orders/multi', $orders); 214 | return $response->getApiData(); 215 | } 216 | 217 | /** 218 | * This interface supports both take-profit and stop-loss functions, and other functions are exactly the same as the place order interface. 219 | * 220 | * You can place two types of orders: limit and market. Orders can only be placed if your account has sufficient funds. Once an order is placed, your funds will be put on hold for the duration of the order. The amount of funds on hold depends on the order type and parameters specified. 221 | * 222 | * Please be noted that the system would hold the fees from the orders entered the orderbook in advance. Read Get Fills to learn more. 223 | * 224 | * @param array $order 225 | * @return mixed|null 226 | * @throws BusinessException 227 | * @throws HttpException 228 | * @throws InvalidApiUriException 229 | */ 230 | public function createStOrder(array $order) 231 | { 232 | $response = $this->call(Request::METHOD_POST, '/api/v1/st-orders', $order); 233 | return $response->getApiData(); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/PrivateApi/WebSocketFeed.php: -------------------------------------------------------------------------------- 1 | loop === null) { 33 | $this->loop = Factory::create(); 34 | } 35 | return $this->loop; 36 | } 37 | 38 | /** 39 | * Set the event loop instance 40 | * @param LoopInterface $loop 41 | */ 42 | public function setLoop(LoopInterface $loop) 43 | { 44 | $this->loop = $loop; 45 | } 46 | 47 | /** 48 | * Get the server list and temporary token 49 | * @return array 50 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 51 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 52 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 53 | */ 54 | public function getPublicBullet() 55 | { 56 | $response = $this->call(Request::METHOD_POST, '/api/v1/bullet-public'); 57 | return $response->getApiData(); 58 | } 59 | 60 | /** 61 | * Get the server list and authorized token 62 | * @return array 63 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 64 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 65 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 66 | */ 67 | public function getPrivateBullet() 68 | { 69 | $response = $this->call(Request::METHOD_POST, '/api/v1/bullet-private'); 70 | return $response->getApiData(); 71 | } 72 | 73 | /** 74 | * Get the url of WebSocket 75 | * @param bool $private 76 | * @param array $params 77 | * @return array 78 | * @throws NoAvailableWebSocketServerException 79 | */ 80 | public function getServer($private = false, array $params = []) 81 | { 82 | $bulletMethod = $private ? 'getPrivateBullet' : 'getPublicBullet'; 83 | $bullet = $this->$bulletMethod(); 84 | if (empty($bullet['instanceServers'])) { 85 | throw new NoAvailableWebSocketServerException(); 86 | } 87 | $server = $bullet['instanceServers'][array_rand($bullet['instanceServers'])]; 88 | $params['token'] = $bullet['token']; 89 | $url = sprintf('%s%s%s', $server['endpoint'], strpos($server['endpoint'], '?') === false ? '?' : '&', http_build_query($params)); 90 | $server['connectUrl'] = $url; 91 | return $server; 92 | } 93 | 94 | /** 95 | * Get the url of WebSocket for public channels 96 | * @param array $params 97 | * @return array 98 | * @throws NoAvailableWebSocketServerException 99 | */ 100 | public function getPublicServer(array $params = []) 101 | { 102 | return $this->getServer(false, $params); 103 | } 104 | 105 | /** 106 | * Get the url of WebSocket for private channels 107 | * @param array $params 108 | * @return array 109 | * @throws NoAvailableWebSocketServerException 110 | */ 111 | public function getPrivateServer(array $params = []) 112 | { 113 | return $this->getServer(true, $params); 114 | } 115 | 116 | /** 117 | * Subscribe multiple channels by url 118 | * @param array $server 119 | * @param array $channels 120 | * @param callable $onMessage 121 | * @param callable|null $onClose 122 | * @param array $options 123 | * @throws \Exception|\Throwable 124 | */ 125 | public function subscribeChannels(array $server, array $channels, callable $onMessage, callable $onClose = null, array $options = []) 126 | { 127 | if (!isset($options['tls']['verify_peer'])) { 128 | $options['tls']['verify_peer'] = !static::isSkipVerifyTls(); 129 | } 130 | 131 | $loop = $this->getLoop(); 132 | $reactConnector = new SocketConnector($loop, $options); 133 | $connector = new RatchetConnector($loop, $reactConnector); 134 | /** 135 | * @var \Exception|\Throwable $exception 136 | */ 137 | $exception = null; 138 | $connector($server['connectUrl'])->then(function (WebSocket $ws) use ($server, $channels, $onMessage, $onClose, $loop) { 139 | // Add timer to send ping message 140 | $pingTimer = $loop->addPeriodicTimer($server['pingInterval'] / 1000 - 1, function () use ($ws) { 141 | try { 142 | $ping = $this->createPingMessage(); 143 | $pingStr = json_encode($ping); 144 | if (self::isDebugMode()) { 145 | static::getLogger()->debug(sprintf('Sent a WebSocket message: %s', $pingStr)); 146 | } 147 | // fputs(STDIN, print_r($ping, true)); 148 | $ws->send($pingStr); 149 | } catch (\Exception $e) { 150 | // Ignore this exception 151 | } 152 | }); 153 | $ws->on('message', function (MessageInterface $msg) use ($server, $ws, $channels, $onMessage, $loop, $pingTimer) { 154 | $msgStr = $msg->__toString(); 155 | if (self::isDebugMode()) { 156 | static::getLogger()->debug(sprintf('Received a WebSocket message: %s', $msgStr)); 157 | } 158 | $msgArray = json_decode($msgStr, true); 159 | if (!isset($msgArray['type'])) { 160 | throw new BusinessException('Invalid format of message without type: ' . $msgStr); 161 | } 162 | switch ($msgArray['type']) { 163 | case 'welcome': 164 | // Do subscribe 165 | if (!isset($msgArray['id']) || $msgArray['id'] === $server['connectId']) { 166 | foreach ($channels as $channel) { 167 | $ws->send(json_encode($channel)); 168 | } 169 | } 170 | break; 171 | case 'ack': 172 | case 'ping': 173 | case 'pong': 174 | // fputs(STDIN, print_r($msgArray, true)); 175 | break; 176 | case 'error': 177 | $loop->cancelTimer($pingTimer); 178 | throw new BusinessException('Error: ' . $msg); 179 | case 'message': 180 | call_user_func($onMessage, $msgArray, $ws, $loop); 181 | break; 182 | default: 183 | throw new BusinessException('Unknown type: ' . $msgArray['type']); 184 | } 185 | }); 186 | $ws->on('close', function ($code = null, $reason = null) use ($onClose, $loop, $pingTimer) { 187 | if (is_callable($onClose)) { 188 | call_user_func($onClose, $code, $reason); 189 | } 190 | $loop->cancelTimer($pingTimer); 191 | }); 192 | }, function ($e) use ($loop, &$exception) { 193 | $exception = $e; 194 | }); 195 | 196 | $loop->run(); 197 | 198 | if ($exception !== null) { 199 | throw $exception; 200 | } 201 | } 202 | 203 | /** 204 | * Subscribe multiple public channels 205 | * @param array $query The query of websocket url 206 | * @param array $channels 207 | * @param callable $onMessage 208 | * @param callable|null $onClose 209 | * @param array $options 210 | * @throws \Exception|\Throwable 211 | */ 212 | public function subscribePublicChannels(array $query, array $channels, callable $onMessage, callable $onClose = null, array $options = []) 213 | { 214 | if (!isset($channels[0])) { 215 | $channels = [$channels]; 216 | } 217 | array_walk($channels, function (&$channel) { 218 | if (!isset($channel['id'])) { 219 | $channel['id'] = uniqid('', true); 220 | } 221 | $channel['type'] = 'subscribe'; 222 | $channel['privateChannel'] = false; 223 | }); 224 | if (!isset($query['connectId'])) { 225 | $query['connectId'] = uniqid('', true); 226 | } 227 | $server = $this->getPublicServer($query); 228 | $server['connectId'] = $query['connectId']; 229 | $this->subscribeChannels($server, $channels, $onMessage, $onClose, $options); 230 | } 231 | 232 | /** 233 | * Subscribe multiple private channels 234 | * @param array $query The query of websocket url 235 | * @param array $channels 236 | * @param callable $onMessage 237 | * @param callable|null $onClose 238 | * @param array $options 239 | * @throws \Exception|\Throwable 240 | */ 241 | public function subscribePrivateChannels(array $query, array $channels, callable $onMessage, callable $onClose = null, array $options = []) 242 | { 243 | if (!isset($channels[0])) { 244 | $channels = [$channels]; 245 | } 246 | array_walk($channels, function (&$channel) { 247 | if (!isset($channel['id'])) { 248 | $channel['id'] = uniqid('', true); 249 | } 250 | $channel['type'] = 'subscribe'; 251 | $channel['privateChannel'] = true; 252 | }); 253 | if (!isset($query['connectId'])) { 254 | $query['connectId'] = uniqid('', true); 255 | } 256 | $server = $this->getPrivateServer($query); 257 | $server['connectId'] = $query['connectId']; 258 | $this->subscribeChannels($server, $channels, $onMessage, $onClose, $options); 259 | } 260 | 261 | /** 262 | * Subscribe one public channel 263 | * @param array $query The query of websocket url 264 | * @param array $channel 265 | * @param callable $onMessage 266 | * @param callable|null $onClose 267 | * @param array $options 268 | * @throws \Exception|\Throwable 269 | */ 270 | public function subscribePublicChannel(array $query, array $channel, callable $onMessage, callable $onClose = null, array $options = []) 271 | { 272 | $this->subscribePublicChannels($query, [$channel], $onMessage, $onClose, $options); 273 | } 274 | 275 | /** 276 | * Subscribe one private channel 277 | * @param array $query The query of websocket url 278 | * @param array $channel 279 | * @param callable $onMessage 280 | * @param callable|null $onClose 281 | * @param array $options 282 | * @throws \Exception|\Throwable 283 | */ 284 | public function subscribePrivateChannel(array $query, array $channel, callable $onMessage, callable $onClose = null, array $options = []) 285 | { 286 | $this->subscribePrivateChannels($query, [$channel], $onMessage, $onClose, $options); 287 | } 288 | 289 | /** 290 | * Create a ping message 291 | * @param string $id 292 | * @return array 293 | */ 294 | public function createPingMessage($id = null) 295 | { 296 | return ['id' => $id ?: uniqid('', true), 'type' => 'ping']; 297 | } 298 | 299 | /** 300 | * Create a subscription message 301 | * @param string $topic 302 | * @param bool $privateChannel 303 | * @param bool $response 304 | * @param string $id 305 | * @return array 306 | */ 307 | public function createSubscribeMessage($topic, $privateChannel = false, $response = true, $id = null) 308 | { 309 | return ['id' => $id ?: uniqid('', true), 'type' => 'subscribe', 'topic' => $topic, 'privateChannel' => $privateChannel, 'response' => $response]; 310 | } 311 | 312 | /** 313 | * Create an unsubscribe message 314 | * @param string $topic 315 | * @param bool $privateChannel 316 | * @param bool $response 317 | * @param string $id 318 | * @return array 319 | */ 320 | public function createUnsubscribeMessage($topic, $privateChannel = false, $response = true, $id = null) 321 | { 322 | return ['id' => $id ?: uniqid('', true), 'type' => 'unsubscribe', 'topic' => $topic, 'privateChannel' => $privateChannel, 'response' => $response]; 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /tests/PositionTest.php: -------------------------------------------------------------------------------- 1 | getList(); 23 | // $this->assertPagination($data); 24 | foreach ($data as $item) { 25 | $this->assertArrayHasKey('id', $item); 26 | $this->assertArrayHasKey('symbol', $item); 27 | $this->assertArrayHasKey('autoDeposit', $item); 28 | $this->assertArrayHasKey('maintMarginReq', $item); 29 | $this->assertArrayHasKey('riskLimit', $item); 30 | $this->assertArrayHasKey('realLeverage', $item); 31 | $this->assertArrayHasKey('crossMode', $item); 32 | $this->assertArrayHasKey('delevPercentage', $item); 33 | // $this->assertArrayHasKey('openingTimestamp', $item); 34 | $this->assertArrayHasKey('currentTimestamp', $item); 35 | $this->assertArrayHasKey('currentQty', $item); 36 | $this->assertArrayHasKey('currentCost', $item); 37 | $this->assertArrayHasKey('currentComm', $item); 38 | $this->assertArrayHasKey('unrealisedCost', $item); 39 | $this->assertArrayHasKey('realisedGrossCost', $item); 40 | $this->assertArrayHasKey('realisedCost', $item); 41 | $this->assertArrayHasKey('isOpen', $item); 42 | $this->assertArrayHasKey('markPrice', $item); 43 | $this->assertArrayHasKey('markValue', $item); 44 | $this->assertArrayHasKey('posCost', $item); 45 | $this->assertArrayHasKey('posCross', $item); 46 | $this->assertArrayHasKey('posInit', $item); 47 | $this->assertArrayHasKey('posComm', $item); 48 | $this->assertArrayHasKey('posLoss', $item); 49 | $this->assertArrayHasKey('posMargin', $item); 50 | $this->assertArrayHasKey('posMaint', $item); 51 | $this->assertArrayHasKey('maintMargin', $item); 52 | $this->assertArrayHasKey('realisedGrossPnl', $item); 53 | $this->assertArrayHasKey('realisedPnl', $item); 54 | $this->assertArrayHasKey('unrealisedPnl', $item); 55 | $this->assertArrayHasKey('unrealisedPnlPcnt', $item); 56 | $this->assertArrayHasKey('avgEntryPrice', $item); 57 | $this->assertArrayHasKey('liquidationPrice', $item); 58 | $this->assertArrayHasKey('bankruptPrice', $item); 59 | } 60 | } 61 | 62 | /** 63 | * @dataProvider apiProvider 64 | * @param Position $api 65 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 66 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 67 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 68 | */ 69 | public function testGetDetail(Position $api) 70 | { 71 | $item = $api->getDetail("XBTUSDM"); 72 | $this->assertArrayHasKey('id', $item); 73 | $this->assertArrayHasKey('symbol', $item); 74 | $this->assertArrayHasKey('autoDeposit', $item); 75 | $this->assertArrayHasKey('maintMarginReq', $item); 76 | $this->assertArrayHasKey('riskLimit', $item); 77 | $this->assertArrayHasKey('realLeverage', $item); 78 | $this->assertArrayHasKey('crossMode', $item); 79 | $this->assertArrayHasKey('delevPercentage', $item); 80 | $this->assertArrayHasKey('openingTimestamp', $item); 81 | $this->assertArrayHasKey('currentTimestamp', $item); 82 | $this->assertArrayHasKey('currentQty', $item); 83 | $this->assertArrayHasKey('currentCost', $item); 84 | $this->assertArrayHasKey('currentComm', $item); 85 | $this->assertArrayHasKey('unrealisedCost', $item); 86 | $this->assertArrayHasKey('realisedGrossCost', $item); 87 | $this->assertArrayHasKey('realisedCost', $item); 88 | $this->assertArrayHasKey('isOpen', $item); 89 | $this->assertArrayHasKey('markPrice', $item); 90 | $this->assertArrayHasKey('markValue', $item); 91 | $this->assertArrayHasKey('posCost', $item); 92 | $this->assertArrayHasKey('posCross', $item); 93 | $this->assertArrayHasKey('posInit', $item); 94 | $this->assertArrayHasKey('posComm', $item); 95 | $this->assertArrayHasKey('posLoss', $item); 96 | $this->assertArrayHasKey('posMargin', $item); 97 | $this->assertArrayHasKey('posMaint', $item); 98 | $this->assertArrayHasKey('maintMargin', $item); 99 | $this->assertArrayHasKey('realisedGrossPnl', $item); 100 | $this->assertArrayHasKey('realisedPnl', $item); 101 | $this->assertArrayHasKey('unrealisedPnl', $item); 102 | $this->assertArrayHasKey('unrealisedPnlPcnt', $item); 103 | $this->assertArrayHasKey('avgEntryPrice', $item); 104 | $this->assertArrayHasKey('liquidationPrice', $item); 105 | $this->assertArrayHasKey('bankruptPrice', $item); 106 | } 107 | 108 | /** 109 | * @dataProvider apiProvider 110 | * @param Position $api 111 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 112 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 113 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 114 | */ 115 | public function testChangeAutoAppendStatus(Position $api) 116 | { 117 | $data = $api->changeAutoAppendStatus('XBTUSDM', true); 118 | // $this->assertInternalType('array', $data); 119 | $this->assertNull($data); 120 | } 121 | 122 | /** 123 | * @dataProvider apiProvider 124 | * @param Position $api 125 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 126 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 127 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 128 | */ 129 | public function testMarginAppend(Position $api) 130 | { 131 | $params = [ 132 | 'symbol' => 'XBTUSDM', 133 | 'margin' => 1000, 134 | 'bizNo' => '123123', 135 | ]; 136 | $data = $api->marginAppend($params); 137 | // $this->assertInternalType('array', $data); 138 | $this->assertNull($data); 139 | } 140 | 141 | /** 142 | * @dataProvider apiProvider 143 | * 144 | * @param Position $api 145 | * @return void 146 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 147 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 148 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 149 | */ 150 | public function testGetMaxWithdrawMargin(Position $api) 151 | { 152 | $symbol = 'XBTUSDM'; 153 | $result = $api->getMaxWithdrawMargin($symbol); 154 | $this->assertGreaterThanOrEqual('0', $result); 155 | } 156 | 157 | /** 158 | * @dataProvider apiProvider 159 | * 160 | * @param Position $api 161 | * @return void 162 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 163 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 164 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 165 | */ 166 | public function testWithdrawMargin(Position $api) 167 | { 168 | $symbol = 'XBTUSDTM'; 169 | $result = $api->withdrawMargin($symbol, '0.1'); 170 | $this->assertEquals('0.1', $result); 171 | } 172 | 173 | /** 174 | * @dataProvider apiProvider 175 | * 176 | * @param Position $api 177 | * @return void 178 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 179 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 180 | */ 181 | public function testGetGetHistoryPositions(Position $api) 182 | { 183 | $params = [ 184 | 'limit' => 2, 185 | 'pageId' => 1, 186 | ]; 187 | $result = $api->getHistoryPositions($params); 188 | $this->assertPagination($result); 189 | foreach ($result['items'] as $item) { 190 | $this->assertArrayHasKey('closeId', $item); 191 | $this->assertArrayHasKey('userId', $item); 192 | $this->assertArrayHasKey('symbol', $item); 193 | $this->assertArrayHasKey('settleCurrency', $item); 194 | $this->assertArrayHasKey('leverage', $item); 195 | $this->assertArrayHasKey('type', $item); 196 | $this->assertArrayHasKey('pnl', $item); 197 | $this->assertArrayHasKey('realisedGrossCost', $item); 198 | $this->assertArrayHasKey('withdrawPnl', $item); 199 | $this->assertArrayHasKey('tradeFee', $item); 200 | $this->assertArrayHasKey('fundingFee', $item); 201 | $this->assertArrayHasKey('openTime', $item); 202 | $this->assertArrayHasKey('closeTime', $item); 203 | $this->assertArrayHasKey('openPrice', $item); 204 | $this->assertArrayHasKey('closePrice', $item); 205 | } 206 | } 207 | 208 | /** 209 | * @dataProvider apiProvider 210 | * 211 | * @param Position $api 212 | * @return void 213 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 214 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 215 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 216 | */ 217 | public function testGetMaxOpenSize(Position $api) 218 | { 219 | $symbol = 'XBTUSDTM'; 220 | $price = '60000'; 221 | $leverage = 2; 222 | $result = $api->getMaxOpenSize($symbol, $price, $leverage); 223 | $this->assertInternalType('array', $result); 224 | $this->assertArrayHasKey('symbol', $result); 225 | $this->assertArrayHasKey('maxBuyOpenSize', $result); 226 | $this->assertArrayHasKey('maxSellOpenSize', $result); 227 | } 228 | 229 | /** 230 | * @dataProvider apiProvider 231 | * 232 | * @param Position $api 233 | * @return void 234 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 235 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 236 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 237 | */ 238 | public function testModifyMarginMode(Position $api) 239 | { 240 | $symbol = 'XBTUSDM'; 241 | $marginMode = MarginMode::ISOLATED; 242 | $result = $api->modifyMarginMode($symbol, $marginMode); 243 | $this->assertInternalType('array', $result); 244 | $this->assertArrayHasKey('symbol', $result); 245 | $this->assertArrayHasKey('marginMode', $result); 246 | } 247 | 248 | /** 249 | * @dataProvider apiProvider 250 | * 251 | * @param Position $api 252 | * @return void 253 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 254 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 255 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 256 | */ 257 | public function testGetMarginMode(Position $api) 258 | { 259 | $symbol = 'XBTUSDM'; 260 | $marginMode = MarginMode::ISOLATED; 261 | $api->modifyMarginMode($symbol, $marginMode); 262 | $result = $api->getMarginMode($symbol); 263 | $this->assertInternalType('array', $result); 264 | $this->assertArrayHasKey('symbol', $result); 265 | $this->assertArrayHasKey('marginMode', $result); 266 | $this->assertEquals($marginMode, $result['marginMode']); 267 | } 268 | 269 | /** 270 | * @dataProvider apiProvider 271 | * 272 | * @param Position $api 273 | * @return void 274 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 275 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 276 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 277 | */ 278 | public function testModifyCrossUserLeverage(Position $api) 279 | { 280 | $symbol = 'XBTUSDM'; 281 | $leverage = 2; 282 | $api->modifyMarginMode($symbol, MarginMode::CROSS); 283 | $result = $api->modifyCrossUserLeverage($symbol, $leverage); 284 | $this->assertEquals(true, $result); 285 | } 286 | 287 | /** 288 | * @dataProvider apiProvider 289 | * 290 | * @param Position $api 291 | * @return void 292 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 293 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 294 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 295 | */ 296 | public function testGetCrossUserLeverage(Position $api) 297 | { 298 | $symbol = 'XBTUSDM'; 299 | $leverage = 2; 300 | $api->modifyMarginMode($symbol, MarginMode::CROSS); 301 | $api->modifyCrossUserLeverage($symbol, $leverage); 302 | $result = $api->getCrossUserLeverage($symbol); 303 | $this->assertInternalType('array', $result); 304 | $this->assertArrayHasKey('symbol', $result); 305 | $this->assertArrayHasKey('leverage', $result); 306 | $this->assertEquals($leverage, $result['leverage']); 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /tests/AccountTest.php: -------------------------------------------------------------------------------- 1 | getOverview(['currency' => 'XBT']); 24 | $this->assertInternalType('array', $accounts); 25 | $this->assertArrayHasKey('accountEquity', $accounts); 26 | $this->assertArrayHasKey('unrealisedPNL', $accounts); 27 | $this->assertArrayHasKey('marginBalance', $accounts); 28 | $this->assertArrayHasKey('positionMargin', $accounts); 29 | $this->assertArrayHasKey('orderMargin', $accounts); 30 | $this->assertArrayHasKey('frozenFunds', $accounts); 31 | $this->assertArrayHasKey('availableBalance', $accounts); 32 | } 33 | 34 | /** 35 | * @dataProvider apiProvider 36 | * @param Account $api 37 | * @throws BusinessException 38 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 39 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 40 | */ 41 | public function testGetTransactionHistory(Account $api) 42 | { 43 | $accounts = $api->getTransactionHistory(['currency' => 'XBT']); 44 | $this->assertInternalType('array', $accounts); 45 | foreach ($accounts['dataList'] as $item) { 46 | $this->assertArrayHasKey('time', $item); 47 | $this->assertArrayHasKey('type', $item); 48 | $this->assertArrayHasKey('amount', $item); 49 | $this->assertArrayHasKey('accountEquity', $item); 50 | $this->assertArrayHasKey('status', $item); 51 | $this->assertArrayHasKey('offset', $item); 52 | } 53 | } 54 | 55 | /** 56 | * @param Account $api 57 | * @throws BusinessException 58 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 59 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 60 | * @deprecated 61 | * @dataProvider apiProvider 62 | */ 63 | // public function testTransferIn(Account $api) 64 | // { 65 | // $amount = 0.1; 66 | // $accounts = $api->transferIn($amount); 67 | // $this->assertInternalType('array', $accounts); 68 | // if (isset($accounts['applyId'])) { 69 | // $this->assertArrayHasKey('applyId', $accounts); 70 | // } 71 | // } 72 | 73 | /** 74 | * @dataProvider apiProvider 75 | * @param Account $api 76 | * @throws BusinessException 77 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 78 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 79 | */ 80 | public function testTransferOut(Account $api) 81 | { 82 | $bizNo = rand(1, 9999); 83 | $amount = 0.1; 84 | $accounts = $api->transferOut($bizNo, $amount); 85 | $this->assertInternalType('array', $accounts); 86 | if (isset($accounts['applyId'])) { 87 | $this->assertArrayHasKey('applyId', $accounts); 88 | } 89 | } 90 | 91 | /** 92 | * @dataProvider apiProvider 93 | * @param Account $api 94 | * @throws BusinessException 95 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 96 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 97 | */ 98 | public function testCancelTransferOut(Account $api) 99 | { 100 | $applyId = $this->getTransferId($api); 101 | $accounts = $api->cancelTransferOut($applyId); 102 | $this->assertNull($accounts); 103 | } 104 | 105 | /** 106 | * @dataProvider apiProvider 107 | * @param Account $api 108 | * @throws BusinessException 109 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 110 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 111 | */ 112 | public function testGetTransferList(Account $api) 113 | { 114 | $accounts = $api->getTransactionHistory(); 115 | $this->assertInternalType('array', $accounts); 116 | foreach ($accounts['dataList'] as $item) { 117 | // $this->assertArrayHasKey('applyId', $item); 118 | // $this->assertArrayHasKey('currency', $item); 119 | $this->assertArrayHasKey('status', $item); 120 | $this->assertArrayHasKey('amount', $item); 121 | $this->assertArrayHasKey('offset', $item); 122 | } 123 | } 124 | 125 | /** 126 | * @dataProvider apiProvider. 127 | * 128 | * @param Account $api 129 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 130 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 131 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 132 | */ 133 | private function getTransferId($api) 134 | { 135 | $bizNo = '10000000001'; 136 | $amount = 0.1; 137 | $accounts = $api->transferOut($bizNo, $amount); 138 | $this->assertInternalType('array', $accounts); 139 | return $accounts['applyId']; 140 | } 141 | 142 | 143 | /** 144 | * @dataProvider apiProvider 145 | * @param Account $api 146 | * @throws BusinessException 147 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 148 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 149 | */ 150 | public function testTransferOutV2(Account $api) 151 | { 152 | $bizNo = uniqid('t_', false); 153 | $amount = 0.01; 154 | $currency = 'USDT'; 155 | $data = $api->transferOutV2($bizNo, $amount, $currency); 156 | 157 | $this->assertInternalType('array', $data); 158 | 159 | $this->assertArrayHasKey('applyId', $data); 160 | $this->assertArrayHasKey('bizNo', $data); 161 | $this->assertArrayHasKey('payAccountType', $data); 162 | $this->assertArrayHasKey('payTag', $data); 163 | $this->assertArrayHasKey('remark', $data); 164 | $this->assertArrayHasKey('recAccountType', $data); 165 | $this->assertArrayHasKey('recTag', $data); 166 | $this->assertArrayHasKey('recRemark', $data); 167 | $this->assertArrayHasKey('recSystem', $data); 168 | $this->assertArrayHasKey('status', $data); 169 | $this->assertArrayHasKey('currency', $data); 170 | $this->assertArrayHasKey('amount', $data); 171 | $this->assertArrayHasKey('fee', $data); 172 | $this->assertArrayHasKey('sn', $data); 173 | } 174 | 175 | /** 176 | * @dataProvider apiProvider 177 | * @param Account $api 178 | * @throws BusinessException 179 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 180 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 181 | */ 182 | public function testGetSubApikey(Account $api) 183 | { 184 | $params = ['subName' => 'phpunittest', 'apiKey' => '647da940d35150000196a56c']; 185 | $data = $api->getSubApikey($params); 186 | $this->assertInternalType('array', $data); 187 | $this->assertArrayHasKey('apiKey', $data); 188 | $this->assertArrayHasKey('createdAt', $data); 189 | $this->assertArrayHasKey('ipWhitelist', $data); 190 | $this->assertArrayHasKey('permission', $data); 191 | $this->assertArrayHasKey('remark', $data); 192 | $this->assertArrayHasKey('subName', $data); 193 | } 194 | 195 | /** 196 | * @dataProvider apiProvider 197 | * @param Account $api 198 | * @throws BusinessException 199 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 200 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 201 | */ 202 | public function testCreateSubApikey(Account $api) 203 | { 204 | $params = ['subName' => 'phpunittest', 'passphrase' => 'phpunit2023', 'remark' => 'remark']; 205 | $data = $api->createSubApikey($params); 206 | $this->assertInternalType('array', $data); 207 | $this->assertArrayHasKey('apiKey', $data); 208 | $this->assertArrayHasKey('createdAt', $data); 209 | $this->assertArrayHasKey('ipWhitelist', $data); 210 | $this->assertArrayHasKey('permission', $data); 211 | $this->assertArrayHasKey('remark', $data); 212 | $this->assertArrayHasKey('subName', $data); 213 | } 214 | 215 | /** 216 | * @dataProvider apiProvider 217 | * @param Account $api 218 | * @throws BusinessException 219 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 220 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 221 | */ 222 | public function testModifySubApikey(Account $api) 223 | { 224 | $params = ['subName' => 'phpunittest', 'passphrase' => 'phpunit2023', 'apiKey' => '647d8f588de0cc0001751b6e']; 225 | $data = $api->modifySubApikey($params); 226 | $this->assertInternalType('array', $data); 227 | $this->assertArrayHasKey('apiKey', $data); 228 | $this->assertArrayHasKey('ipWhitelist', $data); 229 | $this->assertArrayHasKey('permission', $data); 230 | $this->assertArrayHasKey('subName', $data); 231 | } 232 | 233 | /** 234 | * @dataProvider apiProvider 235 | * @param Account $api 236 | * @throws BusinessException 237 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 238 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 239 | */ 240 | public function testDeleteSubApikey(Account $api) 241 | { 242 | $params = ['subName' => 'phpunittest', 'passphrase' => 'phpunit2023', 'apiKey' => '647d8f588de0cc0001751b6e']; 243 | $data = $api->deleteSubApikey($params); 244 | $this->assertInternalType('array', $data); 245 | $this->assertArrayHasKey('apiKey', $data); 246 | $this->assertArrayHasKey('subName', $data); 247 | } 248 | 249 | /** 250 | * @dataProvider apiProvider 251 | * @param Account $api 252 | * @throws BusinessException 253 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 254 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 255 | */ 256 | public function testTransferOutV3(Account $api) 257 | { 258 | $recAccountType = 'MAIN'; 259 | $amount = 0.01; 260 | $currency = 'USDT'; 261 | $data = $api->transferOutV3($recAccountType, $amount, $currency); 262 | 263 | $this->assertInternalType('array', $data); 264 | 265 | $this->assertArrayHasKey('applyId', $data); 266 | $this->assertArrayHasKey('bizNo', $data); 267 | $this->assertArrayHasKey('payAccountType', $data); 268 | $this->assertArrayHasKey('payTag', $data); 269 | $this->assertArrayHasKey('remark', $data); 270 | $this->assertArrayHasKey('recAccountType', $data); 271 | $this->assertArrayHasKey('recTag', $data); 272 | $this->assertArrayHasKey('recRemark', $data); 273 | $this->assertArrayHasKey('recSystem', $data); 274 | $this->assertArrayHasKey('status', $data); 275 | $this->assertArrayHasKey('currency', $data); 276 | $this->assertArrayHasKey('amount', $data); 277 | $this->assertArrayHasKey('fee', $data); 278 | $this->assertArrayHasKey('sn', $data); 279 | $this->assertArrayHasKey('reason', $data); 280 | $this->assertArrayHasKey('createdAt', $data); 281 | $this->assertArrayHasKey('updatedAt', $data); 282 | } 283 | 284 | /** 285 | * @dataProvider apiProvider 286 | * @param Account $api 287 | * @throws BusinessException 288 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 289 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 290 | */ 291 | public function testGetAccountOverviewAll(Account $api) 292 | { 293 | $currency = 'USDT'; 294 | $data = $api->getAccountOverviewAll($currency); 295 | $this->assertInternalType('array', $data); 296 | $this->assertInternalType('array', $data['summary']); 297 | $this->assertArrayHasKey('accountEquityTotal', $data['summary']); 298 | $this->assertArrayHasKey('unrealisedPNLTotal', $data['summary']); 299 | $this->assertArrayHasKey('marginBalanceTotal', $data['summary']); 300 | $this->assertArrayHasKey('positionMarginTotal', $data['summary']); 301 | $this->assertArrayHasKey('orderMarginTotal', $data['summary']); 302 | $this->assertArrayHasKey('frozenFundsTotal', $data['summary']); 303 | $this->assertArrayHasKey('availableBalanceTotal', $data['summary']); 304 | $this->assertArrayHasKey('currency', $data['summary']); 305 | $this->assertEquals($currency, $data['summary']['currency']); 306 | $this->assertInternalType('array', $data['accounts']); 307 | foreach ($data['accounts'] as $item) { 308 | $this->assertArrayHasKey('accountName', $item); 309 | $this->assertArrayHasKey('accountEquity', $item); 310 | $this->assertArrayHasKey('unrealisedPNL', $item); 311 | $this->assertArrayHasKey('marginBalance', $item); 312 | $this->assertArrayHasKey('positionMargin', $item); 313 | $this->assertArrayHasKey('orderMargin', $item); 314 | $this->assertArrayHasKey('frozenFunds', $item); 315 | $this->assertArrayHasKey('availableBalance', $item); 316 | $this->assertArrayHasKey('currency', $item); 317 | $this->assertEquals($currency, $item['currency']); 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /tests/WebSocketFeedTest.php: -------------------------------------------------------------------------------- 1 | getPublicBullet(); 24 | $this->assertInternalType('array', $data); 25 | $this->assertArrayHasKey('token', $data); 26 | $this->assertArrayHasKey('instanceServers', $data); 27 | $this->assertInternalType('array', $data['instanceServers']); 28 | foreach ($data['instanceServers'] as $instanceServer) { 29 | $this->assertArrayHasKey('endpoint', $instanceServer); 30 | $this->assertArrayHasKey('protocol', $instanceServer); 31 | $this->assertArrayHasKey('encrypt', $instanceServer); 32 | $this->assertInternalType('array', $instanceServer); 33 | } 34 | } 35 | 36 | /** 37 | * @dataProvider apiProvider 38 | * @param WebSocketFeed $api 39 | * @return array 40 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 41 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 42 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 43 | */ 44 | public function testGetPrivateBullet(WebSocketFeed $api) 45 | { 46 | $data = $api->getPrivateBullet(); 47 | $this->assertInternalType('array', $data); 48 | $this->assertArrayHasKey('token', $data); 49 | $this->assertArrayHasKey('instanceServers', $data); 50 | $this->assertInternalType('array', $data['instanceServers']); 51 | 52 | $wsAddress = null; 53 | foreach ($data['instanceServers'] as $instanceServer) { 54 | $this->assertInternalType('array', $instanceServer); 55 | $this->assertArrayHasKey('endpoint', $instanceServer); 56 | $this->assertArrayHasKey('protocol', $instanceServer); 57 | $this->assertArrayHasKey('encrypt', $instanceServer); 58 | if ($instanceServer['protocol'] === 'websocket') { 59 | $wsAddress = $instanceServer['endpoint']; 60 | } 61 | } 62 | return ['address' => $wsAddress, 'token' => $data['token']]; 63 | } 64 | 65 | /** 66 | * @dataProvider apiProvider 67 | * @param WebSocketFeed $api 68 | * @throws \Exception|\Throwable 69 | */ 70 | public function testSubscribePublicChannel(WebSocketFeed $api) 71 | { 72 | $query = ['connectId' => uniqid('', true),]; 73 | $channel = ['topic' => '/contractMarket/ticker:XBTUSDM']; 74 | 75 | $options = [ 76 | // 'tls' => [ 77 | // 'verify_peer' => false, 78 | // ], 79 | ]; 80 | $api->subscribePublicChannel($query, $channel, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 81 | $this->assertInternalType('array', $message); 82 | $this->assertArrayHasKey('type', $message); 83 | $this->assertArrayHasKey('channelType', $message); 84 | $this->assertEquals('message', $message['type']); 85 | 86 | // Dynamic output 87 | fputs(STDIN, print_r($message, true)); 88 | 89 | // Stop for phpunit 90 | $loop->stop(); 91 | }, function ($code, $reason) { 92 | echo "OnClose: {$code} {$reason}\n"; 93 | }, $options); 94 | } 95 | 96 | /** 97 | * @dataProvider apiProvider 98 | * @param WebSocketFeed $api 99 | * @throws \Exception|\Throwable 100 | */ 101 | public function testSubscribePublicChannels(WebSocketFeed $api) 102 | { 103 | $query = ['connectId' => uniqid('', true),]; 104 | $channels = [ 105 | ['topic' => '/contractMarket/ticker:XBTUSDM'], 106 | ['topic' => '/contractMarket/ticker:XBTUSDM'], 107 | ]; 108 | 109 | $options = [ 110 | // 'tls' => [ 111 | // 'verify_peer' => false, 112 | // ], 113 | ]; 114 | $api->subscribePublicChannels($query, $channels, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 115 | $this->assertInternalType('array', $message); 116 | $this->assertArrayHasKey('type', $message); 117 | $this->assertArrayHasKey('channelType', $message); 118 | $this->assertEquals('message', $message['type']); 119 | 120 | // Dynamic output 121 | fputs(STDIN, print_r($message, true)); 122 | 123 | // Stop for phpunit 124 | $loop->stop(); 125 | }, function ($code, $reason) { 126 | echo "OnClose: {$code} {$reason}\n"; 127 | }, $options); 128 | } 129 | 130 | 131 | /** 132 | * @dataProvider apiProvider 133 | * @param WebSocketFeed $api 134 | * @throws \Exception|\Throwable 135 | */ 136 | public function testUnsubscribePublicChannel(WebSocketFeed $api) 137 | { 138 | $query = ['connectId' => uniqid('', true),]; 139 | $channel = ['topic' => '/contractMarket/ticker:XBTUSDM']; 140 | 141 | $options = [ 142 | // 'tls' => [ 143 | // 'verify_peer' => false, 144 | // ], 145 | ]; 146 | $api->subscribePublicChannel($query, $channel, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 147 | $this->assertInternalType('array', $message); 148 | $this->assertArrayHasKey('type', $message); 149 | $this->assertArrayHasKey('channelType', $message); 150 | $this->assertEquals('message', $message['type']); 151 | 152 | // Dynamic output 153 | fputs(STDIN, print_r($message, true)); 154 | 155 | // Stop for phpunit 156 | $loop->stop(); 157 | }, function ($code, $reason) { 158 | echo "OnClose: {$code} {$reason}\n"; 159 | }, $options); 160 | } 161 | 162 | /** 163 | * @dataProvider apiProvider 164 | * @param WebSocketFeed $api 165 | * @throws \Exception|\Throwable 166 | */ 167 | public function testSubscribePrivateChannel(WebSocketFeed $api) 168 | { 169 | $query = ['connectId' => uniqid('', true),]; 170 | $channel = ['topic' => '/contract/position:XBTUSDM']; 171 | 172 | $options = [ 173 | // 'tls' => [ 174 | // 'verify_peer' => false, 175 | // ], 176 | ]; 177 | $api->subscribePrivateChannel($query, $channel, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 178 | $this->assertInternalType('array', $message); 179 | $this->assertArrayHasKey('type', $message); 180 | $this->assertArrayHasKey('channelType', $message); 181 | $this->assertEquals('message', $message['type']); 182 | // Dynamic output 183 | fputs(STDIN, print_r($message, true)); 184 | 185 | // Stop for phpunit 186 | $loop->stop(); 187 | }, function ($code, $reason) { 188 | echo "OnClose: {$code} {$reason}\n"; 189 | }, $options); 190 | } 191 | 192 | /** 193 | * @dataProvider apiProvider 194 | * @param WebSocketFeed $api 195 | * @throws \Exception|\Throwable 196 | */ 197 | public function testSubscribePrivateChannels(WebSocketFeed $api) 198 | { 199 | $query = ['connectId' => uniqid('', true),]; 200 | $channels = [ 201 | ['topic' => '/contract/position:XBTUSDM'], 202 | ['topic' => '/contract/position:XBTUSDM'], 203 | ]; 204 | 205 | $options = [ 206 | // 'tls' => [ 207 | // 'verify_peer' => false, 208 | // ], 209 | ]; 210 | $api->subscribePrivateChannels($query, $channels, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 211 | $this->assertInternalType('array', $message); 212 | $this->assertArrayHasKey('type', $message); 213 | $this->assertArrayHasKey('channelType', $message); 214 | $this->assertEquals('message', $message['type']); 215 | // Dynamic output 216 | fputs(STDIN, print_r($message, true)); 217 | 218 | // Stop for phpunit 219 | $loop->stop(); 220 | }, function ($code, $reason) { 221 | echo "OnClose: {$code} {$reason}\n"; 222 | }, $options); 223 | } 224 | 225 | /** 226 | * @dataProvider apiProvider 227 | * @param WebSocketFeed $api 228 | * @throws \Throwable 229 | */ 230 | public function testSubscribeLevel3v2(WebSocketFeed $api) 231 | { 232 | $query = ['connectId' => uniqid('t_', false),]; 233 | $channel = ['topic' => '/contractMarket/level3v2:XBTUSDM']; 234 | 235 | $options = []; 236 | $api->subscribePublicChannel($query, $channel, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 237 | // Dynamic output 238 | fwrite(STDIN, print_r($message, true)); 239 | 240 | $this->assertInternalType('array', $message); 241 | $this->assertArrayHasKey('type', $message); 242 | $this->assertArrayHasKey('topic', $message); 243 | $this->assertArrayHasKey('subject', $message); 244 | $this->assertArrayHasKey('data', $message); 245 | $this->assertInternalType('array', $message['data']); 246 | $this->assertArrayHasKey('symbol', $message['data']); 247 | $this->assertArrayHasKey('sequence', $message['data']); 248 | $this->assertArrayHasKey('orderId', $message['data']); 249 | 250 | // Stop for phpunit 251 | $loop->stop(); 252 | }, function ($code, $reason) { 253 | echo "OnClose: {$code} {$reason}\n"; 254 | }, $options); 255 | } 256 | 257 | /** 258 | * @dataProvider apiProvider 259 | * @param WebSocketFeed $api 260 | * @throws \Throwable 261 | */ 262 | public function testSubscribeTickerV2(WebSocketFeed $api) 263 | { 264 | $query = ['connectId' => uniqid('t_', false),]; 265 | $channel = ['topic' => '/contractMarket/tickerV2:XBTUSDM']; 266 | 267 | $options = []; 268 | $api->subscribePublicChannel($query, $channel, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 269 | // Dynamic output 270 | fwrite(STDIN, print_r($message, true)); 271 | 272 | $this->assertInternalType('array', $message); 273 | $this->assertArrayHasKey('type', $message); 274 | $this->assertArrayHasKey('topic', $message); 275 | $this->assertArrayHasKey('subject', $message); 276 | $this->assertArrayHasKey('data', $message); 277 | $this->assertArrayHasKey('symbol', $message['data']); 278 | $this->assertArrayHasKey('sequence', $message['data']); 279 | $this->assertArrayHasKey('bestBidSize', $message['data']); 280 | $this->assertArrayHasKey('bestBidPrice', $message['data']); 281 | $this->assertArrayHasKey('bestAskPrice', $message['data']); 282 | $this->assertArrayHasKey('bestAskSize', $message['data']); 283 | $this->assertArrayHasKey('ts', $message['data']); 284 | // Stop for phpunit 285 | $loop->stop(); 286 | }, function ($code, $reason) { 287 | echo "OnClose: {$code} {$reason}\n"; 288 | }, $options); 289 | } 290 | 291 | /** 292 | * @dataProvider apiProvider 293 | * @param WebSocketFeed $api 294 | * @throws \Throwable 295 | */ 296 | public function testSubscribeTradeOrders(WebSocketFeed $api) 297 | { 298 | $query = ['connectId' => uniqid('t_', false),]; 299 | $channel = ['topic' => '/contractMarket/tradeOrders:XBTUSDM']; 300 | 301 | $options = []; 302 | $api->subscribePrivateChannel($query, $channel, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 303 | // Dynamic output 304 | fwrite(STDIN, print_r($message, true)); 305 | 306 | $this->assertInternalType('array', $message); 307 | $this->assertArrayHasKey('type', $message); 308 | $this->assertArrayHasKey('topic', $message); 309 | $this->assertArrayHasKey('subject', $message); 310 | $this->assertArrayHasKey('data', $message); 311 | // Stop for phpunit 312 | $loop->stop(); 313 | }, function ($code, $reason) { 314 | echo "OnClose: {$code} {$reason}\n"; 315 | }, $options); 316 | } 317 | 318 | /** 319 | * @dataProvider apiProvider 320 | * @param WebSocketFeed $api 321 | * @throws \Throwable 322 | */ 323 | public function testSubscribeWalletAvailableBalanceChange(WebSocketFeed $api) 324 | { 325 | $query = ['connectId' => uniqid('t_', false),]; 326 | $channel = ['topic' => '/contractAccount/wallet']; 327 | 328 | $options = []; 329 | $api->subscribePrivateChannel($query, $channel, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 330 | // Dynamic output 331 | fwrite(STDIN, print_r($message, true)); 332 | 333 | $this->assertInternalType('array', $message); 334 | $this->assertArrayHasKey('type', $message); 335 | $this->assertArrayHasKey('topic', $message); 336 | $this->assertArrayHasKey('subject', $message); 337 | $this->assertArrayHasKey('data', $message); 338 | 339 | if ($message['subject'] === 'availableBalance.change') { 340 | $this->assertArrayHasKey('currency', $message['data']); 341 | $this->assertArrayHasKey('holdBalance', $message['data']); 342 | $this->assertArrayHasKey('availableBalance', $message['data']); 343 | $this->assertArrayHasKey('timestamp', $message['data']); 344 | } 345 | 346 | if ($message['subject'] === 'withdrawHold.change') { 347 | $this->assertArrayHasKey('currency', $message['data']); 348 | $this->assertArrayHasKey('withdrawHold', $message['data']); 349 | $this->assertArrayHasKey('timestamp', $message['data']); 350 | } 351 | 352 | // Stop for phpunit 353 | $loop->stop(); 354 | }, function ($code, $reason) { 355 | echo "OnClose: {$code} {$reason}\n"; 356 | }, $options); 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /tests/OrderTest.php: -------------------------------------------------------------------------------- 1 | uniqid(), 24 | 'type' => 'limit', 25 | 'side' => 'buy', 26 | 'symbol' => 'XBTUSDM', 27 | 'leverage' => 2, 28 | 'remark' => '\中文备注 ', 29 | 30 | 'price' => 100, 31 | 'size' => 1, 32 | ]; 33 | $data = $api->create($order); 34 | $this->assertInternalType('array', $data); 35 | $this->assertArrayHasKey('orderId', $data); 36 | } 37 | 38 | /** 39 | * @dataProvider apiProvider 40 | * @param Order $api 41 | * @return array|string 42 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 43 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 44 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 45 | */ 46 | public function testCreateMarket(Order $api) 47 | { 48 | $order = [ 49 | 'clientOid' => uniqid(), 50 | 'type' => 'market', 51 | 'side' => 'buy', 52 | 'symbol' => 'XBTUSDM', 53 | 'leverage' => 2, 54 | 'remark' => 'Test Order ' . time(), 55 | 56 | 'size' => 1, 57 | ]; 58 | $data = $api->create($order); 59 | $this->assertInternalType('array', $data); 60 | $this->assertArrayHasKey('orderId', $data); 61 | } 62 | 63 | /** 64 | * @dataProvider apiProvider 65 | * @param Order $api 66 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 67 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 68 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 69 | */ 70 | public function testGetList(Order $api) 71 | { 72 | $data = $api->getList(['symbol' => 'XBTUSDM'], ['currentPage' => 1, 'pageSize' => 10]); 73 | $this->assertPagination($data); 74 | foreach ($data['items'] as $item) { 75 | $this->assertArrayHasKey('symbol', $item); 76 | $this->assertArrayHasKey('hidden', $item); 77 | $this->assertArrayHasKey('type', $item); 78 | $this->assertArrayHasKey('iceberg', $item); 79 | $this->assertArrayHasKey('createdAt', $item); 80 | $this->assertArrayHasKey('stopTriggered', $item); 81 | $this->assertArrayHasKey('id', $item); 82 | $this->assertArrayHasKey('timeInForce', $item); 83 | $this->assertArrayHasKey('side', $item); 84 | $this->assertArrayHasKey('dealSize', $item); 85 | $this->assertArrayHasKey('stp', $item); 86 | $this->assertArrayHasKey('postOnly', $item); 87 | $this->assertArrayHasKey('size', $item); 88 | $this->assertArrayHasKey('stop', $item); 89 | $this->assertArrayHasKey('settleCurrency', $item); 90 | $this->assertArrayHasKey('status', $item); 91 | $this->assertArrayHasKey('updatedAt', $item); 92 | $this->assertArrayHasKey('orderTime', $item); 93 | } 94 | } 95 | 96 | /** 97 | * @dataProvider apiProvider 98 | * @param Order $api 99 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 100 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 101 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 102 | */ 103 | public function testGetDetail(Order $api) 104 | { 105 | $data = $api->getList(['symbol' => 'XBTUSDM'], ['currentPage' => 1, 'pageSize' => 10]); 106 | $this->assertPagination($data); 107 | $orders = $data['items']; 108 | if (isset($orders[0])) { 109 | $order = $api->getDetail($orders[0]['id']); 110 | $this->assertArrayHasKey('symbol', $order); 111 | $this->assertArrayHasKey('hidden', $order); 112 | $this->assertArrayHasKey('type', $order); 113 | $this->assertArrayHasKey('iceberg', $order); 114 | $this->assertArrayHasKey('createdAt', $order); 115 | $this->assertArrayHasKey('stopTriggered', $order); 116 | $this->assertArrayHasKey('id', $order); 117 | $this->assertArrayHasKey('timeInForce', $order); 118 | $this->assertArrayHasKey('side', $order); 119 | $this->assertArrayHasKey('dealSize', $order); 120 | $this->assertArrayHasKey('stp', $order); 121 | $this->assertArrayHasKey('postOnly', $order); 122 | $this->assertArrayHasKey('size', $order); 123 | $this->assertArrayHasKey('stop', $order); 124 | $this->assertArrayHasKey('settleCurrency', $order); 125 | $this->assertArrayHasKey('status', $order); 126 | $this->assertArrayHasKey('updatedAt', $order); 127 | $this->assertArrayHasKey('orderTime', $order); 128 | } 129 | } 130 | 131 | /** 132 | * @dataProvider apiProvider 133 | * @param Order $api 134 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 135 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 136 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 137 | */ 138 | public function testCancel($api) 139 | { 140 | $result = $api->cancel($this->getOrderId($api)); 141 | $this->assertInternalType('array', $result); 142 | $this->assertArrayHasKey('cancelledOrderIds', $result); 143 | } 144 | 145 | /** 146 | * @dataProvider apiProvider 147 | * @param Order $api 148 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 149 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 150 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 151 | */ 152 | public function testBatchCancel($api) 153 | { 154 | $result = $api->batchCancel('XBTUSDM'); 155 | $this->assertInternalType('array', $result); 156 | $this->assertArrayHasKey('cancelledOrderIds', $result); 157 | } 158 | 159 | /** 160 | * @dataProvider apiProvider 161 | * @param Order $api 162 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 163 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 164 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 165 | */ 166 | public function testGetRecentList(Order $api) 167 | { 168 | $items = $api->getRecentDoneOrders(); 169 | foreach ($items as $order) { 170 | $this->assertArrayHasKey('symbol', $order); 171 | $this->assertArrayHasKey('hidden', $order); 172 | $this->assertArrayHasKey('type', $order); 173 | $this->assertArrayHasKey('iceberg', $order); 174 | $this->assertArrayHasKey('createdAt', $order); 175 | $this->assertArrayHasKey('stopTriggered', $order); 176 | $this->assertArrayHasKey('id', $order); 177 | $this->assertArrayHasKey('timeInForce', $order); 178 | $this->assertArrayHasKey('side', $order); 179 | $this->assertArrayHasKey('dealSize', $order); 180 | $this->assertArrayHasKey('stp', $order); 181 | $this->assertArrayHasKey('postOnly', $order); 182 | $this->assertArrayHasKey('size', $order); 183 | $this->assertArrayHasKey('stop', $order); 184 | } 185 | } 186 | 187 | /** 188 | * @dataProvider apiProvider 189 | * @param Order $api 190 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 191 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 192 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 193 | */ 194 | public function testOpenOrderStatistics($api) 195 | { 196 | $result = $api->getOpenOrderStatistics('XBTUSDM'); 197 | $this->assertInternalType('array', $result); 198 | $this->assertArrayHasKey('openOrderBuySize', $result); 199 | $this->assertArrayHasKey('openOrderSellSize', $result); 200 | $this->assertArrayHasKey('openOrderBuyCost', $result); 201 | $this->assertArrayHasKey('openOrderSellCost', $result); 202 | } 203 | 204 | /** 205 | * @dataProvider apiProvider. 206 | * 207 | * @param Order $api 208 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 209 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 210 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 211 | */ 212 | private function getOrderId($api) 213 | { 214 | $order = [ 215 | 'clientOid' => uniqid(), 216 | 'type' => 'limit', 217 | 'side' => 'buy', 218 | 'symbol' => 'XBTUSDM', 219 | 'leverage' => 2, 220 | 'remark' => '\中文备注 ', 221 | 222 | 'price' => 100, 223 | 'size' => 1, 224 | ]; 225 | $data = $api->create($order); 226 | $this->assertInternalType('array', $data); 227 | $this->assertArrayHasKey('orderId', $data); 228 | return $data['orderId']; 229 | } 230 | 231 | /** 232 | * @dataProvider apiProvider 233 | * @param Order $api 234 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 235 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 236 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 237 | */ 238 | public function testGetDetailByClientOid(Order $api) 239 | { 240 | $clientOid = uniqid(); 241 | $order = [ 242 | 'clientOid' => $clientOid, 243 | 'type' => 'limit', 244 | 'side' => 'buy', 245 | 'symbol' => 'XBTUSDTM', 246 | 'leverage' => 1, 247 | 'remark' => 'create test order', 248 | 'price' => '1', 249 | 'size' => '1', 250 | ]; 251 | 252 | $api->create($order); 253 | $order = $api->getDetailByClientOid($clientOid); 254 | $this->assertArrayHasKey('symbol', $order); 255 | $this->assertArrayHasKey('hidden', $order); 256 | $this->assertArrayHasKey('type', $order); 257 | $this->assertArrayHasKey('iceberg', $order); 258 | $this->assertArrayHasKey('createdAt', $order); 259 | $this->assertArrayHasKey('stopTriggered', $order); 260 | $this->assertArrayHasKey('id', $order); 261 | $this->assertArrayHasKey('timeInForce', $order); 262 | $this->assertArrayHasKey('side', $order); 263 | $this->assertArrayHasKey('dealSize', $order); 264 | $this->assertArrayHasKey('stp', $order); 265 | $this->assertArrayHasKey('postOnly', $order); 266 | $this->assertArrayHasKey('size', $order); 267 | $this->assertArrayHasKey('stop', $order); 268 | $this->assertArrayHasKey('settleCurrency', $order); 269 | $this->assertArrayHasKey('status', $order); 270 | $this->assertArrayHasKey('updatedAt', $order); 271 | $this->assertArrayHasKey('orderTime', $order); 272 | $api->cancel($order['id']); 273 | } 274 | 275 | /** 276 | * @dataProvider apiProvider 277 | * @param Order $api 278 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 279 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 280 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 281 | */ 282 | public function testCancelByClientOid($api) 283 | { 284 | $clientId = uniqid(); 285 | $symbol = 'DOTUSDTM'; 286 | $order = [ 287 | 'clientOid' => $clientId, 288 | 'type' => 'limit', 289 | 'side' => 'buy', 290 | 'symbol' => $symbol, 291 | 'leverage' => 5, 292 | 'remark' => 'test cancel order', 293 | 294 | 'price' => 6, 295 | 'size' => 1, 296 | ]; 297 | 298 | $api->create($order); 299 | $result = $api->cancelByClientOid($clientId, $symbol); 300 | $this->assertInternalType('array', $result); 301 | $this->assertArrayHasKey('clientOid', $result); 302 | } 303 | 304 | /** 305 | * @dataProvider apiProvider 306 | * @param Order $api 307 | * @return void 308 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 309 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 310 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 311 | */ 312 | public function testCreateTest(Order $api) 313 | { 314 | $order = [ 315 | 'clientOid' => uniqid(), 316 | 'type' => 'limit', 317 | 'side' => 'buy', 318 | 'symbol' => 'DOTUSDM', 319 | 'leverage' => 2, 320 | 'remark' => 'create test order', 321 | 322 | 'price' => '1', 323 | 'size' => '1', 324 | ]; 325 | $data = $api->createTest($order); 326 | $this->assertInternalType('array', $data); 327 | $this->assertArrayHasKey('orderId', $data); 328 | } 329 | 330 | /** 331 | * @dataProvider apiProvider 332 | * 333 | * @param Order $api 334 | * @return void 335 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 336 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 337 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 338 | */ 339 | public function testCreateMultiOrders(Order $api) 340 | { 341 | $orders = [ 342 | [ 343 | 'clientOid' => uniqid(), 344 | 'type' => 'limit', 345 | 'side' => 'buy', 346 | 'symbol' => 'AAVEUSDTM', 347 | 'leverage' => 5, 348 | 'remark' => 'create test order', 349 | 'price' => '1', 350 | 'size' => '1', 351 | ], 352 | [ 353 | 'clientOid' => uniqid(), 354 | 'type' => 'limit', 355 | 'side' => 'buy', 356 | 'symbol' => 'ETHUSDTM', 357 | 'leverage' => 5, 358 | 'remark' => 'create test order', 359 | 'price' => '1', 360 | 'size' => '1', 361 | ], 362 | ]; 363 | 364 | $result = $api->createMultiOrders($orders); 365 | foreach ($result as $item) { 366 | $this->assertInternalType('array', $item); 367 | $this->assertArrayHasKey('orderId', $item); 368 | $this->assertArrayHasKey('clientOid', $item); 369 | $this->assertArrayHasKey('symbol', $item); 370 | } 371 | } 372 | 373 | /** 374 | * @dataProvider apiProvider 375 | * 376 | * @param Order $api 377 | * @return void 378 | * @throws \KuCoin\Futures\SDK\Exceptions\BusinessException 379 | * @throws \KuCoin\Futures\SDK\Exceptions\HttpException 380 | * @throws \KuCoin\Futures\SDK\Exceptions\InvalidApiUriException 381 | */ 382 | public function testCreateStOrder(Order $api) 383 | { 384 | $order = [ 385 | 'clientOid' => uniqid(), 386 | 'type' => 'limit', 387 | 'side' => 'buy', 388 | 'symbol' => 'XBTUSDTM', 389 | 'leverage' => 1, 390 | 'remark' => 'create test order', 391 | 'price' => '800', 392 | 'size' => 1, 393 | 'stopPriceType' => 'TP', 394 | 'triggerStopUpPrice' => '9000', 395 | 'triggerStopDownPrice' => '700', 396 | 'marginMode' => 'ISOLATED', 397 | ]; 398 | $result = $api->createStOrder($order); 399 | $this->assertInternalType('array', $result); 400 | $this->assertArrayHasKey('orderId', $result); 401 | $this->assertArrayHasKey('clientOid', $result); 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP SDK for KuCoin Futures API 2 | 3 | > The detailed document [https://docs.kucoin.com/futures/](https://docs.kucoin.com/futures/), in order to receive the latest API change notifications, please `Watch` this repository. 4 | 5 | [![Latest Version](https://img.shields.io/github/release/Kucoin/kucoin-futures-php-sdk.svg)](https://github.com/Kucoin/kucoin-futures-php-sdk/releases) 6 | [![PHP Version](https://img.shields.io/packagist/php-v/kucoin/kucoin-futures-php-sdk.svg?color=green)](https://secure.php.net) 7 | [![Build Status](https://travis-ci.org/Kucoin/kucoin-futures-php-sdk.svg?branch=master)](https://travis-ci.org/Kucoin/kucoin-futures-php-sdk) 8 | [![Total Downloads](https://poser.pugx.org/kucoin/kucoin-futures-php-sdk/downloads)](https://packagist.org/packages/kucoin/kucoin-futures-php-sdk) 9 | [![License](https://poser.pugx.org/kucoin/kucoin-futures-php-sdk/license)](LICENSE) 10 | 11 | ## Requirements 12 | 13 | | Dependency | Requirement | 14 | |-------------------------------------------------------|-----------------------------| 15 | | [PHP](https://secure.php.net/manual/en/install.php) | `>=5.5.0` `Recommend PHP7+` | 16 | | [guzzlehttp/guzzle](https://github.com/guzzle/guzzle) | `^6.0\|^7.0` | 17 | 18 | ## Install 19 | 20 | > Install package via [Composer](https://getcomposer.org/). 21 | 22 | ```shell 23 | composer require "kucoin/kucoin-futures-php-sdk:~1.0.0" 24 | ``` 25 | 26 | ## Usage 27 | 28 | ### Choose environment 29 | 30 | | Environment | BaseUri | 31 | |--------------|-------------------------------------------| 32 | | *Production* | `https://api-futures.kucoin.com(DEFAULT)` | 33 | 34 | ```php 35 | use KuCoin\Futures\SDK\KuCoinFuturesApi; 36 | // Switch to the sandbox environment 37 | KuCoinFuturesApi::setBaseUri('https://api-sandbox-futures.kucoin.com'); 38 | ``` 39 | 40 | ### Debug mode & logging 41 | 42 | ```php 43 | use KuCoin\Futures\SDK\KuCoinFuturesApi; 44 | // Debug mode will record the logs of API and WebSocket to files in the directory "KuCoinFuturesApi::getLogPath()" according to the minimum log level "KuCoinFuturesApi::getLogLevel()". 45 | KuCoinFuturesApi::setDebugMode(true); 46 | 47 | // Logging in your code 48 | // KuCoinFuturesApi::setLogPath('/tmp'); 49 | // KuCoinFuturesApi::setLogLevel(Monolog\Logger::DEBUG); 50 | KuCoinFuturesApi::getLogger()->debug("I'm a debug message"); 51 | ``` 52 | 53 | ### Examples 54 | 55 | > See the [test case](tests) for more examples. 56 | 57 | #### Example of API `without` authentication 58 | 59 | ```php 60 | use KuCoin\Futures\SDK\PublicApi\Time; 61 | 62 | $api = new Time(); 63 | $timestamp = $api->timestamp(); 64 | var_dump($timestamp); 65 | ``` 66 | 67 | #### Example of API `with` authentication 68 | 69 | ##### **Note** 70 | 71 | To reinforce the security of the API, KuCoin upgraded the API key to version 2.0, the validation logic has also been changed. It is recommended to create(https://www.kucoin.com/account/api) and update your API key to version 2.0. The API key of 72 | version 1.0 will be still valid until May 1, 2021 73 | 74 | ```php 75 | use KuCoin\Futures\SDK\Auth; 76 | use KuCoin\Futures\SDK\PrivateApi\Account; 77 | use KuCoin\Futures\SDK\Exceptions\HttpException; 78 | use KuCoin\Futures\SDK\Exceptions\BusinessException; 79 | 80 | // Auth version v2 (recommend) 81 | $auth = new Auth('key', 'secret', 'passphrase', Auth::API_KEY_VERSION_V2); 82 | // Auth version v1 83 | // $auth = new Auth('key', 'secret', 'passphrase'); 84 | 85 | $api = new Account($auth); 86 | 87 | try { 88 | $result = $api->getOverview(); 89 | var_dump($result); 90 | } catch (HttpException $e) { 91 | var_dump($e->getMessage()); 92 | } catch (BusinessException $e) { 93 | var_dump($e->getMessage()); 94 | } 95 | ``` 96 | 97 | #### Example of WebSocket feed 98 | 99 | ```php 100 | use KuCoin\Futures\SDK\Auth; 101 | use KuCoin\Futures\SDK\KuCoinFuturesApi; 102 | use KuCoin\Futures\SDK\PrivateApi\WebSocketFeed; 103 | use Ratchet\Client\WebSocket; 104 | use React\EventLoop\Factory; 105 | use React\EventLoop\LoopInterface; 106 | 107 | $auth = null; 108 | // Need to pass the Auth parameter when subscribing to a private channel($api->subscribePrivateChannel()). 109 | // Auth version v2 (recommend) 110 | // $auth = new Auth('key', 'secret', 'passphrase', Auth::API_KEY_VERSION_V2); 111 | // Auth version v1 112 | // $auth = new Auth('key', 'secret', 'passphrase'); 113 | $api = new WebSocketFeed($auth); 114 | 115 | // Use a custom event loop instance if you like 116 | //$loop = Factory::create(); 117 | //$loop->addPeriodicTimer(1, function () { 118 | // var_dump(date('Y-m-d H:i:s')); 119 | //}); 120 | //$api->setLoop($loop); 121 | 122 | $query = ['connectId' => uniqid('', true)]; 123 | $channels = [ 124 | ['topic' => '/market/ticker:KCS-BTC'], // Subscribe multiple channels 125 | ['topic' => '/market/ticker:ETH-BTC'], 126 | ]; 127 | 128 | $api->subscribePublicChannels($query, $channels, function (array $message, WebSocket $ws, LoopInterface $loop) use ($api) { 129 | var_dump($message); 130 | 131 | // Subscribe another channel 132 | // $ws->send(json_encode($api->createSubscribeMessage('/contractMarket/ticker:ETHUSDTM'))); 133 | 134 | // Unsubscribe the channel 135 | // $ws->send(json_encode($api->createUnsubscribeMessage('/contractMarket/ticker:XBTUSDM'))); 136 | 137 | // Stop loop 138 | // $loop->stop(); 139 | }, function ($code, $reason) { 140 | echo "OnClose: {$code} {$reason}\n"; 141 | }); 142 | ``` 143 | 144 | #### ⚡️Coroutine HTTP client for asynchronous IO 145 | 146 | > See the [benchmark](examples/BenchmarkCoroutine.php), almost `20x` faster than `curl`. 147 | 148 | ```bash 149 | pecl install swoole 150 | composer require swlib/saber 151 | ``` 152 | 153 | ```php 154 | use KuCoin\Futures\SDK\Auth; 155 | use KuCoin\Futures\SDK\Http\SwooleHttp; 156 | use KuCoin\Futures\SDK\KuCoinFuturesApi; 157 | use KuCoin\Futures\SDK\PrivateApi\Order; 158 | use KuCoin\Futures\SDK\PublicApi\Time; 159 | 160 | // Require PHP 7.1+ and Swoole 2.1.2+ 161 | // Require running in cli mode 162 | 163 | go(function () { 164 | $api = new Time(null, new SwooleHttp); 165 | $timestamp = $api->timestamp(); 166 | var_dump($timestamp); 167 | }); 168 | 169 | go(function () { 170 | // Auth version v2 (recommend) 171 | $auth = new Auth('key', 'secret', 'passphrase', Auth::API_KEY_VERSION_V2); 172 | // Auth version v1 173 | // $auth = new Auth('key', 'secret', 'passphrase'); 174 | $api = new Order($auth, new SwooleHttp); 175 | // Create 50 orders CONCURRENTLY in 1 second 176 | for ($i = 0; $i < 50; $i++) { 177 | go(function () use ($api, $i) { 178 | $order = [ 179 | 'clientOid' => uniqid(), 180 | 'price' => '1', 181 | 'size' => '1', 182 | 'symbol' => 'BTC-USDT', 183 | 'type' => 'limit', 184 | 'side' => 'buy', 185 | 'remark' => 'ORDER#' . $i, 186 | ]; 187 | try { 188 | $result = $api->create($order); 189 | var_dump($result); 190 | } catch (\Throwable $e) { 191 | var_dump($e->getMessage()); 192 | } 193 | }); 194 | } 195 | }); 196 | ``` 197 | 198 | ### API list 199 | 200 |
201 | KuCoin\Futures\SDK\PrivateApi\Account 202 | 203 | | API | Authentication | Description | 204 | |----------------------------------------------------------------|----------------|-------------------------------------------------------------------------------------| 205 | | KuCoin\Futures\SDK\PrivateApi\Account::getOverview() | YES | https://docs.kucoin.com/futures/#account | 206 | | KuCoin\Futures\SDK\PrivateApi\Account::getTransactionHistory() | YES | https://docs.kucoin.com/futures/#get-transaction-history | 207 | | KuCoin\Futures\SDK\PrivateApi\Account::transferIn() | YES | `deprecated` | 208 | | KuCoin\Futures\SDK\PrivateApi\Account::transferOut() | YES | `deprecated` https://docs.kucoin.com/futures/#transfer-funds-to-kucoin-main-account | 209 | | KuCoin\Futures\SDK\PrivateApi\Account::transferOutV2() | YES | https://docs.kucoin.com/futures/#transfer-funds-to-kucoin-main-account-2 | 210 | | KuCoin\Futures\SDK\PrivateApi\Account::cancelTransferOut() | YES | `deprecated` https://docs.kucoin.com/futures/#cancel-transfer-out-request | 211 | | KuCoin\Futures\SDK\PrivateApi\Account::getTransferList() | YES | https://docs.kucoin.com/futures/#get-transfer-out-request-records | 212 | | KuCoin\Futures\SDK\PrivateApi\Account::getSubApikey() | YES | https://docs.kucoin.com/futures/#get-sub-account-futures-api-list | 213 | | KuCoin\Futures\SDK\PrivateApi\Account::createSubApikey() | YES | https://docs.kucoin.com/futures/#create-futures-apis-for-sub-account | 214 | | KuCoin\Futures\SDK\PrivateApi\Account::modifySubApikey() | YES | https://docs.kucoin.com/futures/#modify-sub-account-futures-apis | 215 | | KuCoin\Futures\SDK\PrivateApi\Account::deleteSubApikey() | YES | https://docs.kucoin.com/futures/#delete-sub-account-futures-apis | 216 | | KuCoin\Futures\SDK\PrivateApi\Account::transferOutV3() | YES | https://docs.kucoin.com/futures/#transfer-to-main-or-trade-account | 217 | 218 |
219 | 220 |
221 | KuCoin\Futures\SDK\PrivateApi\Deposit 222 | 223 | | API | Authentication | Description | 224 | |------------------------------------------------------|----------------|------------------------------------------------------| 225 | | KuCoin\Futures\SDK\PrivateApi\Deposit::getAddress() | YES | https://docs.kucoin.com/futures/#get-deposit-address | 226 | | KuCoin\Futures\SDK\PrivateApi\Deposit::getDeposits() | YES | https://docs.kucoin.com/futures/#get-deposit-list | 227 | 228 |
229 | 230 |
231 | KuCoin\Futures\SDK\PrivateApi\Fill 232 | 233 | | API | Authentication | Description | 234 | |-----------------------------------------------------|----------------|-----------------------------------------------| 235 | | KuCoin\Futures\SDK\PrivateApi\Fill::getFills() | YES | https://docs.kucoin.com/futures/#get-fills | 236 | | KuCoin\Futures\SDK\PrivateApi\Fill::getRecentList() | YES | https://docs.kucoin.com/futures/#recent-fills | 237 | 238 |
239 | 240 | 241 |
242 | KuCoin\Futures\SDK\PrivateApi\Fee 243 | 244 | | API | Authentication | Description | 245 | |---------------------------------------------------|----------------|------------------------------------------------------------------------------------| 246 | | KuCoin\Futures\SDK\PrivateApi\Fee::getTradeFees() | YES | https://www.kucoin.com/docs/rest/funding/trade-fee/trading-pair-actual-fee-futures | 247 | 248 |
249 | 250 |
251 | KuCoin\Futures\SDK\PrivateApi\Order 252 | 253 | | API | Authentication | Description | 254 | |---------------------------------------------------------------|----------------|-----------------------------------------------------------------------------------------------| 255 | | KuCoin\Futures\SDK\PrivateApi\Order::create() | YES | https://docs.kucoin.com/futures/#place-an-order | 256 | | KuCoin\Futures\SDK\PrivateApi\Order::cancel() | YES | https://docs.kucoin.com/futures/#cancel-an-order | 257 | | KuCoin\Futures\SDK\PrivateApi\Order::batchCancel() | YES | https://docs.kucoin.com/futures/#limit-order-mass-cancelation | 258 | | KuCoin\Futures\SDK\PrivateApi\Order::stopOrders() | YES | https://docs.kucoin.com/futures/#stop-order-mass-cancelation | 259 | | KuCoin\Futures\SDK\PrivateApi\Order::getList() | YES | https://docs.kucoin.com/futures/#get-order-list | 260 | | KuCoin\Futures\SDK\PrivateApi\Order::getStopOrders() | YES | https://docs.kucoin.com/futures/#get-untriggered-stop-order-list | 261 | | KuCoin\Futures\SDK\PrivateApi\Order::getRecentDoneOrders() | YES | https://docs.kucoin.com/futures/#get-list-of-orders-completed-in-24h | 262 | | KuCoin\Futures\SDK\PrivateApi\Order::getDetail() | YES | https://docs.kucoin.com/futures/#get-details-of-a-single-order | 263 | | KuCoin\Futures\SDK\PrivateApi\Order::getDetailByClientOid() | YES | https://docs.kucoin.com/futures/#get-details-of-a-single-order | 264 | | KuCoin\Futures\SDK\PrivateApi\Order::getOpenOrderStatistics() | YES | https://docs.kucoin.com/futures/#active-order-value-calculation | 265 | | KuCoin\Futures\SDK\PrivateApi\Order::cancelByClientOid() | YES | https://www.kucoin.com/docs/rest/futures-trading/orders/cancel-order-by-clientoid | 266 | | KuCoin\Futures\SDK\PrivateApi\Order::createTest() | YES | https://www.kucoin.com/docs/rest/futures-trading/orders/place-order-test | 267 | | KuCoin\Futures\SDK\PrivateApi\Order::createStOrder() | YES | https://www.kucoin.com/docs/rest/futures-trading/orders/place-take-profit-and-stop-loss-order | 268 | | KuCoin\Futures\SDK\PrivateApi\Order::createMultiOrders() | YES | https://www.kucoin.com/docs/rest/futures-trading/orders/place-multiple-orders | 269 | 270 |
271 |
272 | KuCoin\Futures\SDK\PrivateApi\Position 273 | 274 | | API | Authentication | Description | 275 | |-------------------------------------------------------------------|----------------|-------------------------------------------------------------------------------------------| 276 | | KuCoin\Futures\SDK\PrivateApi\Position::getList() | YES | https://docs.kucoin.com/futures/#get-position-list | 277 | | KuCoin\Futures\SDK\PrivateApi\Position::getDetail() | YES | https://docs.kucoin.com/futures/#get-position-details | 278 | | KuCoin\Futures\SDK\PrivateApi\Position::changeAutoAppendStatus() | YES | https://docs.kucoin.com/futures/#enable-disable-of-auto-deposit-margin | 279 | | KuCoin\Futures\SDK\PrivateApi\Position::marginAppend() | YES | https://docs.kucoin.com/futures/#add-margin-manually | 280 | | KuCoin\Futures\SDK\PrivateApi\Position::getMaxWithdrawMargin() | YES | https://www.kucoin.com/docs/rest/futures-trading/positions/get-max-withdraw-margin | 281 | | KuCoin\Futures\SDK\PrivateApi\Position::withdrawMargin() | YES | https://www.kucoin.com/docs/rest/futures-trading/positions/remove-margin-manually | 282 | | KuCoin\Futures\SDK\PrivateApi\Position::getHistoryPositions() | YES | https://www.kucoin.com/docs/rest/futures-trading/positions/get-positions-history | 283 | | KuCoin\Futures\SDK\PrivateApi\Position::getMaxOpenSize() | YES | https://www.kucoin.com/docs/rest/futures-trading/positions/get-maximum-open-position-size | 284 | | KuCoin\Futures\SDK\PrivateApi\Position::modifyCrossUserLeverage() | YES | https://www.kucoin.com/docs/rest/futures-trading/positions/modify-cross-margin-leverage | 285 | | KuCoin\Futures\SDK\PrivateApi\Position::getCrossUserLeverage() | YES | https://www.kucoin.com/docs/rest/futures-trading/positions/get-cross-margin-leverage | 286 | | KuCoin\Futures\SDK\PrivateApi\Position::modifyMarginMode() | YES | https://www.kucoin.com/docs/rest/futures-trading/positions/modify-margin-mode | 287 | | KuCoin\Futures\SDK\PrivateApi\Position::getMarginMode() | YES | https://www.kucoin.com/docs/rest/futures-trading/positions/get-margin-mode | 288 | 289 |
290 | 291 |
292 | KuCoin\Futures\SDK\PrivateApi\WebSocketFeed 293 | 294 | | API | Authentication | Description | 295 | |-------------------------------------------------------------------------|----------------|------------------------------------------------------| 296 | | KuCoin\Futures\SDK\PrivateApi\WebSocketFeed::getPublicServer() | NO | https://docs.kucoin.com/futures/#apply-connect-token | 297 | | KuCoin\Futures\SDK\PrivateApi\WebSocketFeed::getPrivateServer() | YES | https://docs.kucoin.com/futures/#apply-connect-token | 298 | | KuCoin\Futures\SDK\PrivateApi\WebSocketFeed::subscribePublicChannel() | NO | https://docs.kucoin.com/futures/#public-channels | 299 | | KuCoin\Futures\SDK\PrivateApi\WebSocketFeed::subscribePublicChannels() | NO | https://docs.kucoin.com/futures/#public-channels | 300 | | KuCoin\Futures\SDK\PrivateApi\WebSocketFeed::subscribePrivateChannel() | YES | https://docs.kucoin.com/futures/#private-channels | 301 | | KuCoin\Futures\SDK\PrivateApi\WebSocketFeed::subscribePrivateChannels() | YES | https://docs.kucoin.com/futures/#private-channels | 302 | 303 |
304 | 305 |
306 | KuCoin\Futures\SDK\PrivateApi\Withdrawal 307 | 308 | | API | Authentication | Description | 309 | |-------------------------------------------------------|----------------|-------------------------------------------------------| 310 | | KuCoin\Futures\SDK\PrivateApi\Withdrawal::getQuotas() | YES | https://docs.kucoin.com/futures/#get-withdrawal-limit | 311 | | KuCoin\Futures\SDK\PrivateApi\Withdrawal::getList() | YES | https://docs.kucoin.com/futures/#get-withdrawal-list | 312 | | KuCoin\Futures\SDK\PrivateApi\Withdrawal::apply() | YES | https://docs.kucoin.com/futures/#withdraw-funds | 313 | | KuCoin\Futures\SDK\PrivateApi\Withdrawal::cancel() | YES | https://docs.kucoin.com/futures/#cancel-withdrawal | 314 | 315 |
316 | 317 |
318 | KuCoin\Futures\SDK\PrivateApi\RiskLimitLevel 319 | 320 | | API | Authentication | Description | 321 | |----------------------------------------------------------------------|----------------|------------------------------------------------------------------| 322 | | KuCoin\Futures\SDK\PrivateApi\RiskLimitLevel::getRiskLimitLevel | YES | https://docs.kucoin.com/futures/#obtain-futures-risk-limit-level | 323 | | KuCoin\Futures\SDK\PrivateApi\RiskLimitLevel::changeRiskLimitLevel() | YES | https://docs.kucoin.com/futures/#adjust-risk-limit-level | 324 | 325 |
326 | 327 |
328 | KuCoin\Futures\SDK\PublicApi\Symbol 329 | 330 | | API | Authentication | Description | 331 | |------------------------------------------------------------|----------------|------------------------------------------------------------------------------------------| 332 | | KuCoin\Futures\SDK\PublicApi\Symbol::getTicker() | NO | https://docs.kucoin.com/futures/#get-ticker | 333 | | KuCoin\Futures\SDK\PublicApi\Symbol::getLevel2Snapshot() | NO | https://docs.kucoin.com/futures/#get-full-order-book-level-2 | 334 | | KuCoin\Futures\SDK\PublicApi\Symbol::getLevel3Snapshot() | NO | https://docs.kucoin.com/futures/#get-full-order-book-level-3 | 335 | | KuCoin\Futures\SDK\PublicApi\Symbol::getV2Level3Snapshot() | NO | https://docs.kucoin.com/futures/#get-full-order-book-level-3-v2 | 336 | | KuCoin\Futures\SDK\PublicApi\Symbol::getLevel2Message() | NO | `deprecated` https://docs.kucoin.com/futures/##level-2-pulling-messages | 337 | | KuCoin\Futures\SDK\PublicApi\Symbol::getLevel3Message() | NO | `deprecated` https://docs.kucoin.com/futures/##level-3-pulling-messages | 338 | | KuCoin\Futures\SDK\PublicApi\Symbol::getTradeHistory() | NO | https://docs.kucoin.com/futures/#get-trade-histories | 339 | | KuCoin\Futures\SDK\PublicApi\Symbol::getKLines() | NO | https://docs.kucoin.com/futures/?lang=en_US#get-k-line-data-of-contract | 340 | | KuCoin\Futures\SDK\PublicApi\Symbol::getLevel2Depth20 | NO | https://docs.kucoin.com/futures/cn/#level-2-2 | 341 | | KuCoin\Futures\SDK\PublicApi\Symbol::getLevel2Depth100 | NO | https://docs.kucoin.com/futures/cn/#level-2-2 | 342 | | KuCoin\Futures\SDK\PublicApi\Symbol::getFundingRates | NO | https://www.kucoin.com/docs/rest/futures-trading/funding-fees/get-public-funding-history | 343 | 344 |
345 | 346 |
347 | KuCoin\Futures\SDK\PublicApi\Contract 348 | 349 | | API | Authentication | Description | 350 | |--------------------------------------------------------|----------------|--------------------------------------------------------------------------------------------------| 351 | | KuCoin\Futures\SDK\PublicApi\Contract::getList() | NO | https://www.kucoin.com/docs/rest/futures-trading/market-data/get-symbols-list | 352 | | KuCoin\Futures\SDK\PublicApi\Contract::getDetail() | NO | https://www.kucoin.com/docs/rest/futures-trading/market-data/get-symbol-detail | 353 | | KuCoin\Futures\SDK\PublicApi\Contract::getAllTickers() | NO | https://www.kucoin.com/docs/rest/futures-trading/market-data/get-latest-ticker-for-all-contracts | 354 | 355 |
356 | 357 |
358 | KuCoin\Futures\SDK\PublicApi\Time 359 | 360 | | API | Authentication | Description | 361 | |------------------------------------------------|----------------|----------------------------------------------| 362 | | KuCoin\Futures\SDK\PublicApi\Time::timestamp() | NO | https://docs.kucoin.com/futures/#server-time | 363 | 364 |
365 | 366 |
367 | KuCoin\Futures\SDK\PublicApi\Status 368 | 369 | | API | Authentication | Description | 370 | |-----------------------------------------------|----------------|---------------------------------------------------------| 371 | | KuCoin\Futures\SDK\PublicApi\Status::status() | NO | https://docs.kucoin.com/futures/#get-the-service-status | 372 | 373 |
374 | 375 | ## Run tests 376 | 377 | > Modify your API key in `phpunit.xml` first. 378 | 379 | ```shell 380 | # Add your API configuration items into the environmental variable first 381 | export API_BASE_URI=https://api-futures.kucoin.com 382 | export API_KEY=key 383 | export API_SECRET=secret 384 | export API_PASSPHRASE=passphrase 385 | export API_KEY_VERSION=2 386 | export API_DEBUG_MODE=1 387 | 388 | composer test 389 | ``` 390 | 391 | ## License 392 | 393 | [MIT](LICENSE) 394 | --------------------------------------------------------------------------------