Skip to content

Commit

Permalink
Merge pull request #30 from timirey/release/2-0-0-rc2
Browse files Browse the repository at this point in the history
Release/2 0 0 rc2
  • Loading branch information
timirey committed Jul 15, 2024
2 parents 06fbe07 + b6437af commit 608fc55
Show file tree
Hide file tree
Showing 23 changed files with 324 additions and 260 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Release Notes

## [2.0.0-rc2](https://github.com/timirey/xapi-php/compare/2.0.0-alpha...2.0.0-rc) - 2024-07-15

* Removed WebSocket package dependency.
* Instead of WebSocket it uses [php sockets](https://www.php.net/manual/en/book.sockets.php).
* Updated tests.

## [2.0.0-rc](https://github.com/timirey/xapi-php/compare/2.0.0-alpha...2.0.0-rc) - 2024-07-09

* Added documentation for streaming commands.
Expand Down
41 changes: 2 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

This PHP library provides a comprehensive and user-friendly interface for interacting with the X-Trade Brokers (XTB)
xStation5 Trading API. It supports a wide range of functionalities, including account management, trade execution,
market data retrieval, and real-time streaming commands via WebSocket, making it an ideal tool for developers looking to
market data retrieval, and real-time streaming commands via socket, making it an ideal tool for developers looking to
integrate advanced trading features and live market data into their applications.

## Table of contents
Expand Down Expand Up @@ -135,57 +135,20 @@ $streamClient = new StreamClient(
host: StreamHost::DEMO
);

// It is a better practice to handle subscriptions through a separate worker, ex.: Laravel cron job.
// Meant to be a daemon, run as separate process.
$streamClient->getTickPrices(
symbol: 'EURUSD',
callback: static function (GetTickPricesStreamResponse $tickPricesStreamResponse): void {
/**
* @var TickStreamRecord $tickStreamRecord
*/
$record = $tickPricesStreamResponse->tickStreamRecord;

print_r($record);
}
);

// Unreachable code.
```

Currently, the only way to shut down the subscriber is to unsubscribe from the callback body, or exit the script
externally.

```PHP
use Timirey\XApi\Responses\GetTickPricesStreamResponse;
use Timirey\XApi\StreamClient;

/**
* @var int $tickCount
*/
$tickCount = 0;

/**
* @var $streamClient StreamClient
*/
$streamClient->getTickPrices(
symbol: 'EURUSD',
callback: static function (GetTickPricesStreamResponse $tickPricesStreamResponse) use ($streamClient): void {
$tickCount++;

// If we got 5 tick prices, unsubscribe.
if ($tickCount > 5) {
$streamClient->unsubscribe();
}
}
);

// Reachable code.

/**
* @var LogoutResponse $logoutResponse
*/
$logoutResponse = $client->logout();
```

## Available commands

Request-Reply commands are performed on main connection socket. The reply is sent by main connection socket.
Expand Down
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@
}
],
"require": {
"php": "^8.1",
"phrity/websocket": "^3.0"
"php": "^8.1"
},
"require-dev": {
"pestphp/pest": "^2.34",
Expand Down
22 changes: 13 additions & 9 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
namespace Timirey\XApi;

use DateTime;
use Exception;
use JsonException;
use Timirey\XApi\Connections\Socket;
use Timirey\XApi\Enums\Cmd;
use Timirey\XApi\Enums\Host;
use Timirey\XApi\Enums\Level;
use Timirey\XApi\Exceptions\ErrorResponseException;
use Timirey\XApi\Exceptions\InvalidResponseException;
use Timirey\XApi\Exceptions\SocketException;
use Timirey\XApi\Payloads\AbstractPayload;
use Timirey\XApi\Payloads\Data\ChartLastInfoRecord;
use Timirey\XApi\Payloads\Data\ChartRangeInfoRecord;
Expand Down Expand Up @@ -64,28 +67,29 @@
use Timirey\XApi\Responses\PingResponse;
use Timirey\XApi\Responses\TradeTransactionResponse;
use Timirey\XApi\Responses\TradeTransactionStatusResponse;
use WebSocket\Client as WebSocketClient;

/**
* Client class for interacting with the xStation5 API.
*/
class Client
{
/**
* @var WebSocketClient XTB WebSocket client instance.
* @var Socket Socket client instance.
*/
protected WebSocketClient $client;
protected Socket $socket;

/**
* Constructor for the Client class.
*
* @param integer $userId User ID.
* @param string $password User password.
* @param Host $host WebSocket host URL.
* @param integer $userId User ID.
* @param string $password User password.
* @param Host $host Host URL.
*
* @throws SocketException If socket is unable to init.
*/
public function __construct(protected int $userId, protected string $password, protected Host $host)
{
$this->client = new WebSocketClient($this->host->value);
$this->socket = new Socket($this->host->value);
}

/**
Expand Down Expand Up @@ -505,8 +509,8 @@ public function getVersion(): GetVersionResponse
*/
protected function request(AbstractPayload $payload, string $responseClass): AbstractResponse
{
$this->client->text($payload);
$this->socket->send($payload);

return $responseClass::instantiate($this->client->receive()->getContent());
return $responseClass::instantiate($this->socket->receive());
}
}
79 changes: 79 additions & 0 deletions src/Connections/Socket.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace Timirey\XApi\Connections;

use Exception;
use Timirey\XApi\Exceptions\SocketException;

/**
* Represents a socket connection for sending and receiving data.
*/
class Socket
{
/**
* @var false|resource The socket resource or false if failed to create.
*/
protected $socket;

/**
* Constructor to initialize and connect the socket.
*
* @param string $address The address to connect to.
* @param float|null $timeout The connection timeout in seconds.
* @param integer $flags The flags to use for the connection.
*
* @throws SocketException If socket is unable to init.
*/
public function __construct(
string $address,
?float $timeout = null,
int $flags = STREAM_CLIENT_CONNECT
) {
$this->socket = stream_socket_client(
$address,
$errorCode,
$errorMessage,
$timeout,
$flags
);

if ($this->socket === false) {
throw new SocketException("$errorCode: $errorMessage");
}
}

/**
* Sends data through the socket.
*
* @param string $payload The data to send.
* @param integer|null $length The length of data to send. Defaults to the full length of the payload.
*
* @return false|integer The number of bytes written, or false on failure.
*/
public function send(string $payload, ?int $length = null): false|int
{
return fwrite($this->socket, $payload, $length);
}

/**
* Receives data from the socket.
*
* @param integer $length The maximum number of bytes to read.
*
* @return false|string The read data, or false on failure.
*/
public function receive(int $length = 4096): false|string
{
return fread($this->socket, $length);
}

/**
* Closes the socket connection.
*
* @return boolean True on success, false on failure.
*/
public function close(): bool
{
return fclose($this->socket);
}
}
32 changes: 32 additions & 0 deletions src/Connections/StreamSocket.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Timirey\XApi\Connections;

use Generator;
use Timirey\XApi\Exceptions\SocketException;

/**
* Extends the Socket class to provide additional functionality for
* listening to data from a stream socket connection.
*/
class StreamSocket extends Socket
{
/**
* Listen to the stream socket and yield data as it is received.
*
* @return Generator Yields data received from the socket.
* @throws SocketException If socket is empty.
*/
public function listen(): Generator
{
while (!feof($this->socket)) {
$response = $this->receive();

if (!empty($response)) {
yield $response;
}
}

throw new SocketException('Unable to subscribe. Empty socket response.');
}
}
6 changes: 3 additions & 3 deletions src/Enums/Host.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
namespace Timirey\XApi\Enums;

/**
* Enum representing the WebSocket host URLs for xStation5 API.
* Enum representing the socket host URLs for xStation5 API.
*/
enum Host: string
{
/**
* Demo account host URL.
*/
case DEMO = 'wss:https://ws.xtb.com/demo';
case DEMO = 'ssl:https://xapi.xtb.com:5124';

/**
* Real account host URL.
*/
case REAL = 'wss:https://ws.xtb.com/real';
case REAL = 'ssl:https://xapi.xtb.com:5112';
}
6 changes: 3 additions & 3 deletions src/Enums/StreamHost.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
namespace Timirey\XApi\Enums;

/**
* Enum representing the WebSocket stream host URLs for xStation5 API.
* Enum representing the socket stream host URLs for xStation5 API.
*/
enum StreamHost: string
{
/**
* Demo account stream host URL.
*/
case DEMO = 'wss:https://ws.xtb.com/demoStream';
case DEMO = 'ssl:https://xapi.xtb.com:5125';

/**
* Real account stream host URL.
*/
case REAL = 'wss:https://ws.xtb.com/realStream';
case REAL = 'ssl:https://xapi.xtb.com:5113';
}
2 changes: 1 addition & 1 deletion src/Exceptions/ErrorResponseException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Timirey\XApi\Exceptions;

use WebSocket\Exception\Exception;
use Exception;

/**
* Custom error response exception.
Expand Down
12 changes: 12 additions & 0 deletions src/Exceptions/SocketException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Timirey\XApi\Exceptions;

use Exception;

/**
* Custom exception for sockets.
*/
class SocketException extends Exception
{
}
Loading

0 comments on commit 608fc55

Please sign in to comment.