From d369df1a508218e6f46a6c21ed7c714bdff88606 Mon Sep 17 00:00:00 2001 From: Victory Efekpogua Date: Thu, 18 Jun 2026 13:20:04 +0100 Subject: [PATCH 1/2] feat: add demo accounts, expert advisors, quotas, and known trading servers resources --- README.md | 138 ++++++++++++++++++ src/MetaApiClient.php | 24 +++ .../AccountManagement/DemoAccount.php | 29 ++++ .../AccountManagement/ExpertAdvisor.php | 38 +++++ src/Resources/AccountManagement/MtServer.php | 15 ++ src/Resources/AccountManagement/Quota.php | 25 ++++ tests/DemoAccountTest.php | 69 +++++++++ tests/ExpertAdvisorTest.php | 62 ++++++++ tests/MetaApiClientTest.php | 8 + tests/MtServerTest.php | 16 ++ tests/QuotaTest.php | 40 +++++ 11 files changed, 464 insertions(+) create mode 100644 src/Resources/AccountManagement/DemoAccount.php create mode 100644 src/Resources/AccountManagement/ExpertAdvisor.php create mode 100644 src/Resources/AccountManagement/MtServer.php create mode 100644 src/Resources/AccountManagement/Quota.php create mode 100644 tests/DemoAccountTest.php create mode 100644 tests/ExpertAdvisorTest.php create mode 100644 tests/MtServerTest.php create mode 100644 tests/QuotaTest.php diff --git a/README.md b/README.md index 368660b..677a069 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ A modern PHP SDK for selected MetaApi services: - MetaTrader account management - Provisioning profiles - Account replicas +- MT4/MT5 demo accounts +- Expert advisors +- User quota management +- Known trading servers - MetaApi REST terminal API - CopyFactory - MetaStats @@ -235,6 +239,140 @@ Available methods: - `generateReplicaCodeSample(string $accountId, string $replicaId, string $platform)` - `increaseReplicaReliability(string $accountId, string $replicaId)` +## Demo Accounts + +```php +$demoAccounts = $metaapi->demoAccounts(); +``` + +Create a MetaTrader 4 demo account: + +```php +$response = $demoAccounts->createMT4DemoAccount( + 'provisioning-profile-id', + [ + 'accountType' => 'type', + 'balance' => 1000, + 'email' => 'user@example.com', + 'leverage' => 10, + 'name' => 'Test User', + 'phone' => '+12345678901', + 'serverName' => 'Example-Server', + 'keywords' => ['Example Broker Ltd'], + ], + transactionId: bin2hex(random_bytes(16)) +); + +if ($response->isCreated()) { + echo "Demo account created. Login: " . $response->body()['login']; +} + +if ($response->shouldRetry()) { + echo "Request accepted. Retry after: " . ($response->retryAfter() ?? 'not specified'); +} +``` + +Create a MetaTrader 5 demo account: + +```php +$response = $demoAccounts->createMT5DemoAccount( + 'provisioning-profile-id', + [ + 'accountType' => 'type', + 'balance' => 1000, + 'email' => 'user@example.com', + 'leverage' => 10, + 'name' => 'Test User', + 'phone' => '+12345678901', + 'serverName' => 'Example-Server', + 'keywords' => ['Example Broker Ltd'], + ], + transactionId: bin2hex(random_bytes(16)) +); +``` + +Both methods return `ActionResponse` — the same type used by `accounts()->create()`. + +## Expert Advisors + +```php +$eas = $metaapi->expertAdvisors(); +``` + +```php +// List all expert advisors on an account +$advisors = $eas->expertAdvisors('account-id'); + +// Read a specific expert advisor +$advisor = $eas->expertAdvisor('account-id', 'expert-id'); + +// Update or create an expert advisor +$eas->updateExpertAdvisor('account-id', 'expert-id', [ + 'symbol' => 'EURUSD', + 'period' => '1H', + 'preset' => 'base64-encoded-preset', +]); + +// Upload an expert advisor file +$eas->uploadExpertAdvisorFile('account-id', 'expert-id', '/path/to/expert.ex5'); + +// Delete an expert advisor +$eas->deleteExpertAdvisor('account-id', 'expert-id'); +``` + +Available methods: + +- `expertAdvisors(string $accountId)` +- `expertAdvisor(string $accountId, string $expertId)` +- `updateExpertAdvisor(string $accountId, string $expertId, array $data)` +- `uploadExpertAdvisorFile(string $accountId, string $expertId, string $filePath)` +- `deleteExpertAdvisor(string $accountId, string $expertId)` + +## Quota + +```php +$quota = $metaapi->quotas(); +``` + +```php +// Get user quota and usage for all regions +$quotas = $quota->quotas(); + +// Get quota update requests +$updateRequests = $quota->quotaUpdateRequests(); + +// Request a region quota update +$quota->requestRegionQuotaUpdate('vint-hill', [ + 'maxAccounts' => 150, + 'maxDeployedG1Accounts' => 25, + 'maxDeployedG2Accounts' => 120, + 'maxDeployedCopyFactoryAccounts' => 120, + 'maxDedicatedIpv4' => 100, + 'justification' => 'quota update request justification message', +]); +``` + +Available methods: + +- `quotas()` +- `quotaUpdateRequests()` +- `requestRegionQuotaUpdate(string $region, array $data)` + +## Mt Servers + +```php +$mtServers = $metaapi->mtServers(); +``` + +```php +// Search known trading servers by MT version +$servers = $mtServers->knownTradingServers(5, 'icmarketssc'); +``` + +Available methods: + +- `knownTradingServers(int $version, string $query)` + ## CopyFactory ```php diff --git a/src/MetaApiClient.php b/src/MetaApiClient.php index 65f3285..3d993ee 100644 --- a/src/MetaApiClient.php +++ b/src/MetaApiClient.php @@ -5,7 +5,11 @@ use GuzzleHttp\ClientInterface; use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\Account; use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\AccountReplica; +use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\DemoAccount; +use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\ExpertAdvisor; +use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\MtServer; use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\ProvisioningProfile; +use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\Quota; class MetaApiClient { @@ -49,6 +53,26 @@ public function accountReplicas(): AccountReplica return new AccountReplica($this->accountHttp); } + public function demoAccounts(): DemoAccount + { + return new DemoAccount($this->accountHttp); + } + + public function expertAdvisors(): ExpertAdvisor + { + return new ExpertAdvisor($this->accountHttp); + } + + public function quotas(): Quota + { + return new Quota($this->accountHttp); + } + + public function mtServers(): MtServer + { + return new MtServer($this->accountHttp); + } + public function copyFactory(?string $region = null, ?string $serverUrl = null): CopyFactory { return new CopyFactory( diff --git a/src/Resources/AccountManagement/DemoAccount.php b/src/Resources/AccountManagement/DemoAccount.php new file mode 100644 index 0000000..c4e9820 --- /dev/null +++ b/src/Resources/AccountManagement/DemoAccount.php @@ -0,0 +1,29 @@ +http->postAction( + "/users/current/provisioning-profiles/{$profileId}/mt4-demo-accounts", + $data, + ['transaction-id' => $transactionId] + ); + } + + public function createMT5DemoAccount(string $profileId, array $data, string $transactionId): ActionResponse + { + return $this->http->postAction( + "/users/current/provisioning-profiles/{$profileId}/mt5-demo-accounts", + $data, + ['transaction-id' => $transactionId] + ); + } +} diff --git a/src/Resources/AccountManagement/ExpertAdvisor.php b/src/Resources/AccountManagement/ExpertAdvisor.php new file mode 100644 index 0000000..7633f18 --- /dev/null +++ b/src/Resources/AccountManagement/ExpertAdvisor.php @@ -0,0 +1,38 @@ +http->get("/users/current/accounts/{$accountId}/expert-advisors"); + } + + public function expertAdvisor(string $accountId, string $expertId): array|string|null + { + return $this->http->get("/users/current/accounts/{$accountId}/expert-advisors/{$expertId}"); + } + + public function updateExpertAdvisor(string $accountId, string $expertId, array $data): array|string|null + { + return $this->http->put("/users/current/accounts/{$accountId}/expert-advisors/{$expertId}", $data); + } + + public function uploadExpertAdvisorFile(string $accountId, string $expertId, string $filePath): array|string|null + { + return $this->http->upload( + "/users/current/accounts/{$accountId}/expert-advisors/{$expertId}/file", + $filePath + ); + } + + public function deleteExpertAdvisor(string $accountId, string $expertId): array|string|null + { + return $this->http->delete("/users/current/accounts/{$accountId}/expert-advisors/{$expertId}"); + } +} diff --git a/src/Resources/AccountManagement/MtServer.php b/src/Resources/AccountManagement/MtServer.php new file mode 100644 index 0000000..61d780e --- /dev/null +++ b/src/Resources/AccountManagement/MtServer.php @@ -0,0 +1,15 @@ +http->get("/known-mt-servers/{$version}/search", ['query' => $query]); + } +} diff --git a/src/Resources/AccountManagement/Quota.php b/src/Resources/AccountManagement/Quota.php new file mode 100644 index 0000000..bc0cf6f --- /dev/null +++ b/src/Resources/AccountManagement/Quota.php @@ -0,0 +1,25 @@ +http->get('/users/current/quotas'); + } + + public function quotaUpdateRequests(): array|string|null + { + return $this->http->get('/users/current/quota-update-requests'); + } + + public function requestRegionQuotaUpdate(string $region, array $data): array|string|null + { + return $this->http->patch("/users/current/regions/{$region}/quotas", $data); + } +} diff --git a/tests/DemoAccountTest.php b/tests/DemoAccountTest.php new file mode 100644 index 0000000..ea5200e --- /dev/null +++ b/tests/DemoAccountTest.php @@ -0,0 +1,69 @@ +demoAccounts()->createMT4DemoAccount('profile-id', [ + 'accountType' => 'type', + 'balance' => 1000, + 'email' => 'user@example.com', + 'leverage' => 10, + 'name' => 'Test User', + 'phone' => '+12345678901', + 'serverName' => 'Example-Server', + ], 'transaction-id-1234567890123456'); + + expect($response)->toBeInstanceOf(ActionResponse::class); + expect($response->isCreated())->toBeTrue(); + expect($response->body())->toBe([ + 'login' => '86053193', + 'password' => '2y8kpft', + 'investorPassword' => 'dc56esco', + 'serverName' => 'Example-Server', + ]); + expect($history[0]['request'])->toHaveSentRequest('POST', '/users/current/provisioning-profiles/profile-id/mt4-demo-accounts'); + expect($history[0]['request']->getHeaderLine('transaction-id'))->toBe('transaction-id-1234567890123456'); +}); + +it('creates mt5 demo account with transaction id header', function (): void { + $history = []; + $metaapi = metaApiClientWithHistory([ + new Response(201, [], '{"login":"86053193","password":"2y8kpft","investorPassword":"dc56esco","serverName":"Example-Server"}'), + ], $history); + + $response = $metaapi->demoAccounts()->createMT5DemoAccount('profile-id', [ + 'accountType' => 'type', + 'balance' => 1000, + 'email' => 'user@example.com', + 'leverage' => 10, + 'name' => 'Test User', + 'phone' => '+12345678901', + 'serverName' => 'Example-Server', + ], 'transaction-id-1234567890123456'); + + expect($response)->toBeInstanceOf(ActionResponse::class); + expect($response->isCreated())->toBeTrue(); + expect($history[0]['request'])->toHaveSentRequest('POST', '/users/current/provisioning-profiles/profile-id/mt5-demo-accounts'); + expect($history[0]['request']->getHeaderLine('transaction-id'))->toBe('transaction-id-1234567890123456'); +}); + +it('exposes accepted demo account creation retry metadata', function (): void { + $metaapi = metaApiClientWithHistory([ + new Response(202, ['Retry-After' => '10'], '{"id":"request-id","state":"DRAFT"}'), + ]); + + $response = $metaapi->demoAccounts()->createMT4DemoAccount('profile-id', [], 'transaction-id'); + + expect($response)->toBeInstanceOf(ActionResponse::class); + expect($response->isAccepted())->toBeTrue(); + expect($response->shouldRetry())->toBeTrue(); + expect($response->retryAfter())->toBe('10'); + expect($response->id())->toBe('request-id'); + expect($response->state())->toBe('DRAFT'); +}); diff --git a/tests/ExpertAdvisorTest.php b/tests/ExpertAdvisorTest.php new file mode 100644 index 0000000..3b956ce --- /dev/null +++ b/tests/ExpertAdvisorTest.php @@ -0,0 +1,62 @@ +expertAdvisors()->expertAdvisors('account-id'); + + expect($result)->toBe([['expertId' => 'test', 'period' => '1H', 'symbol' => 'EURUSD', 'fileUploaded' => false]]); + expect($history[0]['request'])->toHaveSentRequest('GET', '/users/current/accounts/account-id/expert-advisors'); +}); + +it('reads a single expert advisor', function (): void { + $history = []; + $metaapi = metaApiClientWithHistory([ + new Response(200, [], '{"expertId":"test","period":"1H","symbol":"EURUSD","fileUploaded":true}'), + ], $history); + + $result = $metaapi->expertAdvisors()->expertAdvisor('account-id', 'test'); + + expect($result)->toBe(['expertId' => 'test', 'period' => '1H', 'symbol' => 'EURUSD', 'fileUploaded' => true]); + expect($history[0]['request'])->toHaveSentRequest('GET', '/users/current/accounts/account-id/expert-advisors/test'); +}); + +it('updates an expert advisor', function (): void { + $history = []; + $metaapi = metaApiClientWithHistory([new Response(204)], $history); + + $metaapi->expertAdvisors()->updateExpertAdvisor('account-id', 'test', [ + 'symbol' => 'EURUSD', + 'period' => '1H', + 'preset' => 'base64-encoded-preset', + ]); + + expect($history[0]['request'])->toHaveSentRequest('PUT', '/users/current/accounts/account-id/expert-advisors/test'); +}); + +it('uploads an expert advisor file', function (): void { + $filePath = sys_get_temp_dir() . '/test-expert.ex5'; + file_put_contents($filePath, 'fake-expert-content'); + + $history = []; + $metaapi = metaApiClientWithHistory([new Response(204)], $history); + + $metaapi->expertAdvisors()->uploadExpertAdvisorFile('account-id', 'test', $filePath); + + unlink($filePath); + + expect($history[0]['request'])->toHaveSentRequest('PUT', '/users/current/accounts/account-id/expert-advisors/test/file'); +}); + +it('deletes an expert advisor', function (): void { + $history = []; + $metaapi = metaApiClientWithHistory([new Response(204)], $history); + + expect($metaapi->expertAdvisors()->deleteExpertAdvisor('account-id', 'test'))->toBeNull(); + expect($history[0]['request'])->toHaveSentRequest('DELETE', '/users/current/accounts/account-id/expert-advisors/test'); +}); diff --git a/tests/MetaApiClientTest.php b/tests/MetaApiClientTest.php index 4aa8dd6..760f7b7 100644 --- a/tests/MetaApiClientTest.php +++ b/tests/MetaApiClientTest.php @@ -12,7 +12,11 @@ use Victorycodedev\MetaapiCloudPhpSdk\TerminalApi; use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\Account; use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\AccountReplica; +use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\DemoAccount; +use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\ExpertAdvisor; +use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\MtServer; use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\ProvisioningProfile; +use Victorycodedev\MetaapiCloudPhpSdk\Resources\AccountManagement\Quota; use Victorycodedev\MetaapiCloudPhpSdk\Resources\Copyfactory\Configuration; use Victorycodedev\MetaapiCloudPhpSdk\Resources\Copyfactory\CopyTrade; use Victorycodedev\MetaapiCloudPhpSdk\Resources\Copyfactory\History; @@ -32,6 +36,10 @@ expect($client->accounts())->toBeInstanceOf(Account::class); expect($client->provisioningProfiles())->toBeInstanceOf(ProvisioningProfile::class); expect($client->accountReplicas())->toBeInstanceOf(AccountReplica::class); + expect($client->demoAccounts())->toBeInstanceOf(DemoAccount::class); + expect($client->expertAdvisors())->toBeInstanceOf(ExpertAdvisor::class); + expect($client->quotas())->toBeInstanceOf(Quota::class); + expect($client->mtServers())->toBeInstanceOf(MtServer::class); expect($copyFactory)->toBeInstanceOf(CopyFactory::class); expect($copyFactory->configuration())->toBeInstanceOf(Configuration::class); expect($copyFactory->copyTrade())->toBeInstanceOf(CopyTrade::class); diff --git a/tests/MtServerTest.php b/tests/MtServerTest.php new file mode 100644 index 0000000..8bee239 --- /dev/null +++ b/tests/MtServerTest.php @@ -0,0 +1,16 @@ +mtServers()->knownTradingServers(5, 'icmarketssc'); + + expect($result)->toBe(['Raw Trading Ltd' => ['ICMarketsSC-Demo', 'ICMarketsSC-MT5']]); + expect($history[0]['request'])->toHaveSentRequest('GET', '/known-mt-servers/5/search'); + expect($history[0]['request']->getUri()->getQuery())->toBe('query=icmarketssc'); +}); diff --git a/tests/QuotaTest.php b/tests/QuotaTest.php new file mode 100644 index 0000000..33bb4f1 --- /dev/null +++ b/tests/QuotaTest.php @@ -0,0 +1,40 @@ +quotas()->quotas(); + + expect($result)->toBe([['region' => 'vint-hill', 'maxAccounts' => ['quota' => 100]]]); + expect($history[0]['request'])->toHaveSentRequest('GET', '/users/current/quotas'); +}); + +it('gets quota update requests', function (): void { + $history = []; + $metaapi = metaApiClientWithHistory([ + new Response(200, [], '[{"region":"vint-hill","maxAccounts":150}]'), + ], $history); + + $result = $metaapi->quotas()->quotaUpdateRequests(); + + expect($result)->toBe([['region' => 'vint-hill', 'maxAccounts' => 150]]); + expect($history[0]['request'])->toHaveSentRequest('GET', '/users/current/quota-update-requests'); +}); + +it('requests a region quota update', function (): void { + $history = []; + $metaapi = metaApiClientWithHistory([new Response(204)], $history); + + $metaapi->quotas()->requestRegionQuotaUpdate('vint-hill', [ + 'maxAccounts' => 150, + 'maxDeployedG1Accounts' => 25, + 'justification' => 'quota update request justification message', + ]); + + expect($history[0]['request'])->toHaveSentRequest('PATCH', '/users/current/regions/vint-hill/quotas'); +}); From 7fc23670bfefaae691b99bd697333f311d16c6ea Mon Sep 17 00:00:00 2001 From: Victory Efekpogua Date: Thu, 18 Jun 2026 13:31:16 +0100 Subject: [PATCH 2/2] feat: auto-generate transaction IDs for account and demo account creation --- README.md | 15 +++++++++--- src/Resources/AccountManagement/Account.php | 2 +- .../AccountManagement/AccountReplica.php | 7 +++++- .../AccountManagement/DemoAccount.php | 13 ++++++---- tests/AccountManagementTest.php | 24 +++++++++++++++++++ tests/DemoAccountTest.php | 24 +++++++++++++++++++ 6 files changed, 76 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 677a069..b8237c1 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,6 @@ $response = $replicas->createReplica( 'magic' => 123456, 'region' => 'london', ], - transactionId: bin2hex(random_bytes(16)) ); if ($response->shouldRetry()) { @@ -226,6 +225,8 @@ if ($response->shouldRetry()) { } ``` +A transaction ID is auto-generated. Pass your own `transactionId` to retry an accepted request. + Available methods: - `replicas(string $accountId)` @@ -260,7 +261,6 @@ $response = $demoAccounts->createMT4DemoAccount( 'serverName' => 'Example-Server', 'keywords' => ['Example Broker Ltd'], ], - transactionId: bin2hex(random_bytes(16)) ); if ($response->isCreated()) { @@ -272,6 +272,16 @@ if ($response->shouldRetry()) { } ``` +A transaction ID is auto-generated for you. You can also pass your own if you need to retry an accepted request: + +```php +$response = $demoAccounts->createMT4DemoAccount( + 'provisioning-profile-id', + [...], + transactionId: 'my-custom-32-char-transaction-id' +); +``` + Create a MetaTrader 5 demo account: ```php @@ -287,7 +297,6 @@ $response = $demoAccounts->createMT5DemoAccount( 'serverName' => 'Example-Server', 'keywords' => ['Example Broker Ltd'], ], - transactionId: bin2hex(random_bytes(16)) ); ``` diff --git a/src/Resources/AccountManagement/Account.php b/src/Resources/AccountManagement/Account.php index 10f1545..985336c 100644 --- a/src/Resources/AccountManagement/Account.php +++ b/src/Resources/AccountManagement/Account.php @@ -89,7 +89,7 @@ public function createConfigurationLink(string $accountId, ?int $ttlInDays = nul private function transactionHeader(?string $transactionId): array { - return $transactionId ? ['transaction-id' => $transactionId] : []; + return ['transaction-id' => $transactionId ?? bin2hex(random_bytes(16))]; } private function apiVersionHeader(?int $apiVersion): array diff --git a/src/Resources/AccountManagement/AccountReplica.php b/src/Resources/AccountManagement/AccountReplica.php index 5af8bd0..ddde4f7 100644 --- a/src/Resources/AccountManagement/AccountReplica.php +++ b/src/Resources/AccountManagement/AccountReplica.php @@ -24,10 +24,15 @@ public function createReplica(string $accountId, array $data, ?string $transacti return $this->http->postAction( "/users/current/accounts/{$accountId}/replicas", $data, - $transactionId ? ['transaction-id' => $transactionId] : [] + $this->transactionHeader($transactionId) ); } + private function transactionHeader(?string $transactionId): array + { + return ['transaction-id' => $transactionId ?? bin2hex(random_bytes(16))]; + } + public function updateReplica(string $accountId, string $replicaId, array $data): array|string|null { return $this->http->put("/users/current/accounts/{$accountId}/replicas/{$replicaId}", $data); diff --git a/src/Resources/AccountManagement/DemoAccount.php b/src/Resources/AccountManagement/DemoAccount.php index c4e9820..d26f630 100644 --- a/src/Resources/AccountManagement/DemoAccount.php +++ b/src/Resources/AccountManagement/DemoAccount.php @@ -9,21 +9,26 @@ class DemoAccount { public function __construct(private readonly Http $http) {} - public function createMT4DemoAccount(string $profileId, array $data, string $transactionId): ActionResponse + public function createMT4DemoAccount(string $profileId, array $data, ?string $transactionId = null): ActionResponse { return $this->http->postAction( "/users/current/provisioning-profiles/{$profileId}/mt4-demo-accounts", $data, - ['transaction-id' => $transactionId] + $this->transactionHeader($transactionId) ); } - public function createMT5DemoAccount(string $profileId, array $data, string $transactionId): ActionResponse + public function createMT5DemoAccount(string $profileId, array $data, ?string $transactionId = null): ActionResponse { return $this->http->postAction( "/users/current/provisioning-profiles/{$profileId}/mt5-demo-accounts", $data, - ['transaction-id' => $transactionId] + $this->transactionHeader($transactionId) ); } + + private function transactionHeader(?string $transactionId): array + { + return ['transaction-id' => $transactionId ?? bin2hex(random_bytes(16))]; + } } diff --git a/tests/AccountManagementTest.php b/tests/AccountManagementTest.php index 2d9874b..5d980fd 100644 --- a/tests/AccountManagementTest.php +++ b/tests/AccountManagementTest.php @@ -38,6 +38,18 @@ expect($history[0]['request']->getHeaderLine('transaction-id'))->toBe('12345678901234567890123456789012'); }); +it('auto-generates transaction id for account create when not provided', function (): void { + $history = []; + $metaapi = metaApiClientWithHistory([new Response(201, [], '{}')], $history); + + $metaapi->accounts()->create(['name' => 'Demo']); + + $transactionId = $history[0]['request']->getHeaderLine('transaction-id'); + expect($transactionId)->not->toBeEmpty(); + expect(strlen($transactionId))->toBe(32); + expect(ctype_xdigit($transactionId))->toBeTrue(); +}); + it('exposes accepted account creation retry metadata', function (): void { $metaapi = metaApiClientWithHistory([ new Response(202, ['Retry-After' => '10'], '{"id":"account-id","state":"DRAFT"}'), @@ -86,6 +98,18 @@ expect($history[0]['request']->getHeaderLine('transaction-id'))->toBe('transaction-id'); }); +it('auto-generates transaction id for replica create when not provided', function (): void { + $history = []; + $metaapi = metaApiClientWithHistory([new Response(201, [], '{}')], $history); + + $metaapi->accountReplicas()->createReplica('account-id', ['magic' => 123456]); + + $transactionId = $history[0]['request']->getHeaderLine('transaction-id'); + expect($transactionId)->not->toBeEmpty(); + expect(strlen($transactionId))->toBe(32); + expect(ctype_xdigit($transactionId))->toBeTrue(); +}); + it('exposes accepted account replica creation retry metadata', function (): void { $metaapi = metaApiClientWithHistory([ new Response(202, ['Retry-After' => '15'], '{"id":"replica-id","state":"DRAFT"}'), diff --git a/tests/DemoAccountTest.php b/tests/DemoAccountTest.php index ea5200e..b991d81 100644 --- a/tests/DemoAccountTest.php +++ b/tests/DemoAccountTest.php @@ -53,6 +53,30 @@ expect($history[0]['request']->getHeaderLine('transaction-id'))->toBe('transaction-id-1234567890123456'); }); +it('auto-generates transaction id for mt4 demo account when not provided', function (): void { + $history = []; + $metaapi = metaApiClientWithHistory([new Response(201, [], '{}')], $history); + + $metaapi->demoAccounts()->createMT4DemoAccount('profile-id', []); + + $transactionId = $history[0]['request']->getHeaderLine('transaction-id'); + expect($transactionId)->not->toBeEmpty(); + expect(strlen($transactionId))->toBe(32); + expect(ctype_xdigit($transactionId))->toBeTrue(); +}); + +it('auto-generates transaction id for mt5 demo account when not provided', function (): void { + $history = []; + $metaapi = metaApiClientWithHistory([new Response(201, [], '{}')], $history); + + $metaapi->demoAccounts()->createMT5DemoAccount('profile-id', []); + + $transactionId = $history[0]['request']->getHeaderLine('transaction-id'); + expect($transactionId)->not->toBeEmpty(); + expect(strlen($transactionId))->toBe(32); + expect(ctype_xdigit($transactionId))->toBeTrue(); +}); + it('exposes accepted demo account creation retry metadata', function (): void { $metaapi = metaApiClientWithHistory([ new Response(202, ['Retry-After' => '10'], '{"id":"request-id","state":"DRAFT"}'),