Craft a Kraken API client with PHP8 (2/2)
Tags: php, crypto, websocket, curl
Published: 2022-05-30 20:27:20

Welcome to the second and last part of this post, [you can find the first part right here](https://dev.to/nicolasbonnici/craft-a-kraken-api-client-with-php8-12-2hc7), this first part explain the REST usage of the Kraken API.

In this part, we're going to add WebSocket support to our client since Kraken API allow us to retrieve data and perform actions in real time using [WebSocket protocol](https://en.wikipedia.org/wiki/WebSocket). 

This second part is not about how to build a WebSocket client, it just aim to cover Kraken WebSocket usage with PHP8 and [the excellent Ratchet client library AKA Pawl](https://github.com/ratchetphp/Pawl).

You can find the [official Kraken WebSocket API documention here](https://docs.kraken.com/websockets/).

To start keep in mind that just like the REST API you retrieve both public and private data. All public data concern market and exchange related data, such like exchange volumes or prices. When you need to retrieve user scoped data or perform action, you'll need to retrieve an authentication token, [on this REST API endpoint](https://docs.kraken.com/rest/#tag/Websockets-Authentication), then send it in a "token" attribute of your WebSocket payload.

## Public access

Let's cover one the the most basic usage of this WebSocket API, retrieve prices as candle. To do so we'll connect to the `ws.kraken.com` using the wss:// protocol.

When connected, we'll subscribe to receive "ohlc" AKA open high low close prices and other volume related data useful to build a candlestick related message for the BTC/EUR pair symbol with an interval of one minute, the smaller interval for this kind of message.

To cover all public/private WebSocket API usage we only need to add a new method to send request and stream data. Since the code we wrote in the first part already allow to retrieve WebSocket authentication tokens using the dedicated REST API endpoint.

Using the WebSocket client, we only need to connect then pass a custom method to execute the actions we need to do. First let's send a subscribe request then listen messages from this stream.

So let's add some WebSocket related configurations to our client, then a new stream() method that can do so.

## Use Ratchet WebSocket client

We not gonna develop a WebSocket client for this but simply add the [Ratchet client around version 0.4 AKA *Pawn*](https://github.com/ratchetphp/Pawl) as a dependency of our client, using composer.

```bash
composer require ratchet/pawn@0.4

```

Then we need to add two new constant to our client class containing both public and private WebSocket endpoints.

```php
class KrakenAPIClient
{
// ...
    public const WEBSOCKET_PUBLIC_ENDPOINT = 'ws.kraken.com';
    public const WEBSOCKET_PRIVATE_ENDPOINT = 'ws-auth.kraken.com';
// ...
```

## The stream() method

Then our new "stream" method, this method signature contain a callable function and the needed endpoint. This method simply connect then pass the callback method.

If something wrong occur while connecting this method will throw a KrakenApiException, containing error(s).

```php
public function stream(callable $callback, string $endpoint = self::WEBSOCKET_PUBLIC_ENDPOINT): void
{
    \Ratchet\Client\connect(sprintf('wss://%s', $endpoint))->then(
        $callback,
        function ($e) {
            throw new KrakenAPIException(
                sprintf('Error while connecting to Kraken API WebSocket: "%s".', $e->getMessage())
            );
        }
    );
}
```

That's all folks. We now gonna see how to use it especially how to code the callable callback parameter and interact with WebSocket messages stream. 

## How to use it

Please **note that following code is a out of context sample** from an another project using this client.  


```php
use NicolasBonnici\PhpKrakenApiClient\KrakenAPIClient;
use Ratchet\Client\WebSocket;

$client = new KrakenAPIClient();

$client->stream(function (WebSocket $conn) use ($markets, $interval) {
    $conn->send(
        json_encode([
            'event' => self::WS_SUBSCRIBE,
            'pair' => array_keys($markets),
            'subscription' => [
                'interval' => $interval,
                'name' => self::WS_MESSAGE_CANDLE
            ]
        ])
    );

    $conn->on('message', function ($msg) use ($conn, $markets, $interval) {
        $this->logger->info(sprintf('New message received: "%s".', $msg));
        $event = json_decode($msg);

        if (false === is_array($event)) {
            return;
        }

        if ($event[self::WS_MESSAGE_CANDLE_KEY_EVENT_TYPE] === sprintf('%s-%s', self::WS_MESSAGE_CANDLE, $interval)) {
            $symbol = $event[self::WS_MESSAGE_CANDLE_KEY_SYMBOL];
            $candle = $event[self::WS_MESSAGE_CANDLE_KEY_DATA];

            // Do something with the $candle data
        }
    });

    $conn->on('close', function ($msg) use ($conn) {
        $this->logger->info(sprintf('[%s] WebSocket stream closed: "%s".', self::class, $msg));
    });
});
```

The KrakenAPIClient::stream() first parameter $callback receive one Ratchet\Client\WebSocket instance as a $conn argument in this code sample.

Using this argument you can interact with single or multiple streams, send request or listen for some messages in real time. You can also access some WebSocket connection lifecycle related event. 

To listen to a particular stream of messages, you need to subscribe. In the code sample below we first send a request to subscribe to a batch of symbol pairs candle OHLC (open, high, low and close prices).


## Private access

At this level no need to edit more our client code, it is already ready to ship once packaged with composer and packagist.

First let's use the actual client to retrieve the WebSocket authentication token from the Kraken Rest API. 

```php
$client = new KrakenAPIClient('YOUR API KEY', 'YOUR API SECRET');

$response = $client->query('private/GetWebSocketsToken');
```

At this level if everything run smooth the response contain a "token" key, we'll need to send it in our WebSocket request payload later.

Let's retrieve all open orders instantly then monitor all order lifecycle related event messages in real time by subscribing to the `ownTrades` messages stream.


```php
$client->stream(function (WebSocket $conn) {
    $conn->send(
        json_encode([
            'event' => self::WS_SUBSCRIBE,
            'subscription' => [
                'name' => self::WS_MESSAGE_OWN_TRADES,
                'token' => 'YOUR TOKEN',
            ],
        ])
    );

    $conn->on('message', function ($msg) {

        $this->logger->info(sprintf('New message received: "%s".', $msg));
        $event = json_decode($msg);

        if (false === \is_array($event)) {
            return;
        }

        var_dump($event);
    });

    $conn->on('close', function ($msg) {
        $this->logger->info(sprintf('[%s] WebSocket stream closed: "%s".', self::class, $msg));
    });
},
    KrakenAPIClient::WEBSOCKET_PRIVATE_ENDPOINT
);

```

Running this code in a CLI context you'll see in real time your trades activity, otherwise ping requests to maintain WebSocket stream connexion.

## Want more?

The final client code is hosted and maintained [on this Gitlab repository](https://gitlab.com/nicolasbonnici/php-kraken-api-client), feel free to use it on your crypto related projects and of course contribute too. 

Thank you for reading this two parts post on how to build a Kraken API client with PHP 8.