Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/2 0 0 rc2 #30

Merged
merged 8 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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