DEV Community

Cover image for Craft a Kraken API client with PHP8 (2/2)
Nicolas Bonnici
Nicolas Bonnici

Posted on

Craft a Kraken API client with PHP8 (2/2)

Welcome to the second and last part of this post, you can find the first part right here, 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.

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.

You can find the official Kraken WebSocket API documention here.

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, 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 as a dependency of our client, using composer.

composer require ratchet/pawn@0.4

Enter fullscreen mode Exit fullscreen mode

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

class KrakenAPIClient
{
// ...
    public const WEBSOCKET_PUBLIC_ENDPOINT = 'ws.kraken.com';
    public const WEBSOCKET_PRIVATE_ENDPOINT = 'ws-auth.kraken.com';
// ...
Enter fullscreen mode Exit fullscreen mode

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).

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())
            );
        }
    );
}
Enter fullscreen mode Exit fullscreen mode

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.

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));
    });
});
Enter fullscreen mode Exit fullscreen mode

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.

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

$response = $client->query('private/GetWebSocketsToken');
Enter fullscreen mode Exit fullscreen mode

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.

$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
);

Enter fullscreen mode Exit fullscreen mode

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, 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.

Top comments (0)