aleedhillon/meta-five
A Laravel/PHP package for dynamic SEO metadata. MetaFive provides a simple API for page titles, descriptions, Open Graph and Twitter Card tags, canonical URLs, and robots directives, with Blade integration for managing meta tags in your views.
The package is a thin Laravel wrapper around the MetaTrader 5 Web API — a separate MetaQuotes product that runs on the Windows machine hosting the MT5 terminal. Before touching composer, confirm you actually have that Web API server reachable (default https://<host>:443), because the package is useless without it. This is the single biggest "gotcha" that people miss: the Laravel package is the client, not the server.
Minimal steps:
composer require aleedhillon/meta-five
php artisan vendor:publish --provider="Aleedhillon\\MetaFive\\MetaFiveServiceProvider"
Then inspect config/meta-five.php in vendor/aleedhillon/meta-five/config/ if the publish tag differs — with 35 stars the README is likely terse, so the config file itself is your source of truth. Expect keys roughly like:
// config/meta-five.php
return [
'host' => env('MT5_HOST', '127.0.0.1'),
'port' => env('MT5_PORT', 443),
'login' => env('MT5_LOGIN'),
'password' => env('MT5_PASSWORD'),
'server' => env('MT5_SERVER'), // broker's server name, e.g. "ICMarkets-Live"
];
First use case — authenticate and confirm you can read the account:
use Aleedhillon\MetaFive\Facades\MetaFive;
MetaFive::connect();
$account = MetaFive::accountInfo();
dump($account['Balance'], $account['Equity'], $account['Currency']);
If connect() succeeds and accountInfo() returns your balance, everything downstream is just calling the right endpoint. Read the package's src/ directory first — the facade proxies to a single client class, and its public methods are the API surface you'll actually be using. Skim src/Facades/, src/MetaFive.php (or similar), and src/Http/ before writing any business logic.
Wrap the client in your own service class. The raw facade leaks MT5 wire-format assumptions (string decimals, server-side timestamps, broker-specific symbol suffixes) directly into your controllers. Put a TradingService or MetaTrader wrapper in app/Services/ so you have one seam to test and one place to translate.
final class TradingService
{
public function __construct(private MetaFive $client) {}
public function balance(): float
{
return (float) $this->client->accountInfo()['Balance'];
}
public function buy(string $symbol, float $volume): array
{
return $this->client->trade([
'action' => 'DEAL',
'type' => 'BUY',
'symbol' => $this->normalizeSymbol($symbol),
'volume' => number_format($volume, 2, '.', ''),
]);
}
}
Poll on a schedule, not on request. MT5 prices and positions change continuously; hitting the Web API inside a user request will make your app slow and hammer the trading server. Use a scheduled command that writes into Redis or a DB table, and serve reads from your own store.
// app/Console/Kernel.php
$schedule->job(new SyncAccountSnapshot)->everyMinute();
$schedule->job(new SyncOpenPositions)->everyThirtySeconds();
Treat auth as stateful and re-auth on failure. The MT5 Web API uses a challenge/response handshake, and sessions expire. In job handle() methods, catch connection errors and retry after reconnecting rather than assuming a long-lived session:
try {
$client->connect();
$client->positions();
} catch (Throwable $e) {
$client->connect(); // re-handshake
throw $e;
}
Queue trade executions. Order placement should never happen in an HTTP request. Dispatch a job, return 202 Accepted, and let the job record the result. This also gives you an audit trail — brokers care about timestamps and you'll need them if a trade "didn't go through."
Log every trade payload and response to a dedicated channel. MT5 trades are financial events; if something goes wrong you want the exact request body, not a stack trace.
Use it as the write side of a read-model. For dashboards, keep a local positions and account_snapshots table. The MT5 Web API is for acting on the account, not for serving UI.
connect() hangs or times out, that is almost always the cause — not the package.EURUSD may be EURUSD.a, EURUSDm, or EURUSD.r. Never hardcode; fetch the symbol list once and cache it, or normalize suffixes in config."Bid": "1.08432" as a string to preserve tick precision. Don't (float) cast before sending a trade — you'll get rejected orders or slippage. number_format($x, $digits, '.', '') is the safe path.php artisan config:cache will freeze credentials; run config:clear after editing .env, and be aware that long-running queue workers won't pick up changed env either.php artisan tinker with the facade is the fastest loop before you write jobs: call connect(), then dump raw responses. If the package swallows errors, add a log call around the client in AppServiceProvider::boot() or extend the client class.MT5_LIVE=false env flag and a TradingService::assertNotLive() guard.Http facade — it affects whether you can mock it in tests via Http::fake() or need a Guzzle mock handler. In feature tests, mock TradingService, not the underlying client.How can I help you explore Laravel packages today?