tigusigalpa/bcs-trade-php

PHP/Laravel client for BCS Broker Trade API

Maintainers

Package info

github.com/tigusigalpa/bcs-trade-php

pkg:composer/tigusigalpa/bcs-trade-php

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-02-27 05:24 UTC

This package is auto-updated.

Last update: 2026-02-27 05:29:43 UTC


README

БКС Брокер PHP/Laravel API клиент

Latest Version License PHP Version

PHP/Laravel клиент для BCS Trade API — торгового API брокера БКС. Работает по HTTP и WebSocket.

Возможности

  • Все HTTP эндпоинты BCS Trade API
  • Все WebSocket каналы (real-time данные)
  • Автообновление OAuth 2.0 токенов
  • Кастомные исключения для разных типов ошибок
  • Повторные попытки при сетевых сбоях
  • Обработка rate limiting (HTTP 429)
  • Валидация запросов (лимит 1000 баров для свечей)
  • Автопагинация при запросе больших объёмов исторических данных
  • PHP 8.1+ (enums, readonly properties)
  • Laravel 9, 10, 11, 12
  • PSR-12
  • Тесты на Pest

Требования

  • PHP 8.1 или выше
  • Laravel 9.x, 10.x, 11.x или 12.x
  • Composer

Установка

Через Composer:

composer require tigusigalpa/bcs-trade-php

Публикация конфига:

php artisan vendor:publish --tag=bcs-trade-config

Конфигурация

Получение Refresh Token

  1. Войдите в БКС Мир Инвестиций
  2. Перейдите в раздел API
  3. Создайте новый токен и скопируйте refresh_token
  4. Токен действителен 90 дней

Переменные окружения

В .env:

BCS_TRADE_REFRESH_TOKEN=your_refresh_token_here
BCS_TRADE_CLIENT_ID=trade-api-read  # или trade-api-write для торговых операций
BCS_TRADE_CACHE_STORE=file

Параметры конфигурации

config/bcs-trade.php:

Параметр Описание По умолчанию
refresh_token Refresh token из БКС Мир Инвестиций -
client_id Scope токена: trade-api-read или trade-api-write trade-api-read
cache_store Хранилище кеша для токенов file
cache_prefix Префикс ключей кеша bcs_trade_
timeout Таймаут HTTP запросов (секунды) 30
connect_timeout Таймаут подключения (секунды) 10
retry_attempts Количество повторных попыток 3
retry_delay Задержка между попытками (мс) 500

Использование

Базовая инициализация

use Tigusigalpa\BCSTrade\BCSTradeClient;

// Через конструктор
$client = new BCSTradeClient(config('bcs-trade'));

// Через Laravel Container
$client = app(BCSTradeClient::class);

// Через Facade
use Tigusigalpa\BCSTrade\Facades\BCSTrade;

$limits = BCSTrade::limits()->get();

HTTP API

Аутентификация

use Tigusigalpa\BCSTrade\Http\Responses\TokenResponse;

// Получить токен вручную
$tokenResponse = $client->auth()->getToken(
    'your_refresh_token',
    'trade-api-read'
);

echo $tokenResponse->accessToken;
echo $tokenResponse->expiresIn;

// Токены управляются автоматически, ручной вызов не требуется

Лимиты портфеля

$limits = $client->limits()->get();

// Структура ответа:
// [
//     'depoLimit' => [...],
//     'futureHolding' => [...],
//     'moneyLimits' => [...],
//     'futuresLimits' => [...]
// ]

Портфель

$portfolio = $client->portfolio()->get();

Справочная информация

// Расписание торгов
$schedule = $client->information()->getDailySchedule('TQBR', 'SBER');

// Инструменты по ISIN
$instruments = $client->information()->getByIsins(['RU0009029540']);

// Инструмент по тикеру
$instrument = $client->information()->getByTicker('SBER', 'TQBR');

// Инструменты по типу
$stocks = $client->information()->getByType('stock');

// Статусы торгов
$statuses = $client->information()->getTradingStatuses(
    ['SBER', 'GAZP'],
    ['TQBR', 'TQBR']
);

Рыночные данные (свечи)

use Tigusigalpa\BCSTrade\Enums\TimeFrame;

// Получить исторические свечи (до 1000 баров)
$candles = $client->marketData()->getCandles(
    classCode: 'TQBR',
    ticker: 'SBER',
    startDate: '2025-01-01T07:00:00Z',
    endDate: '2025-01-31T20:00:00Z',
    timeFrame: TimeFrame::D
);

// Или используйте строку
$candles = $client->marketData()->getCandles(
    'TQBR',
    'SBER',
    '2025-01-01T07:00:00Z',
    '2025-01-31T20:00:00Z',
    'H1'
);

// Автоматическая пагинация для больших диапазонов
$candles = $client->marketData()->getCandlesPaginated(
    'TQBR',
    'SBER',
    '2024-01-01T07:00:00Z',
    '2025-12-31T20:00:00Z',
    TimeFrame::D
);

// Доступные таймфреймы
TimeFrame::M1   // 1 минута
TimeFrame::M5   // 5 минут
TimeFrame::M15  // 15 минут
TimeFrame::M30  // 30 минут
TimeFrame::H1   // 1 час
TimeFrame::H4   // 4 часа
TimeFrame::D    // День
TimeFrame::W    // Неделя
TimeFrame::MN   // Месяц

Торговые операции (Orders)

Важно: Для торговых операций требуется client_id = 'trade-api-write'

use Tigusigalpa\BCSTrade\Enums\OrderSide;
use Tigusigalpa\BCSTrade\Enums\OrderType;

// Создать рыночный ордер на покупку
$order = $client->orders()->create([
    'side' => OrderSide::Buy,
    'orderType' => OrderType::Market,
    'orderQuantity' => 10,
    'ticker' => 'SBER',
    'classCode' => 'TQBR',
]);

// Создать лимитный ордер на продажу
$order = $client->orders()->create([
    'side' => OrderSide::Sell,
    'orderType' => OrderType::Limit,
    'orderQuantity' => 5,
    'ticker' => 'GAZP',
    'classCode' => 'TQBR',
    'price' => 150.50,
]);

// Создать ордер с собственным UUID
$order = $client->orders()->create([
    'clientOrderId' => 'my-unique-uuid',
    'side' => '1', // или OrderSide::Buy
    'orderType' => '2', // или OrderType::Limit
    'orderQuantity' => 10,
    'ticker' => 'SBER',
    'classCode' => 'TQBR',
    'price' => 250.00,
]);

// Получить статус ордера
$status = $client->orders()->status($clientOrderId);

// Получить все ордера
$allOrders = $client->orders()->all();

// Изменить ордер
$updated = $client->orders()->edit($clientOrderId, [
    'orderQuantity' => 15,
    'price' => 251.00,
]);

// Отменить ордер
$cancelled = $client->orders()->cancel($clientOrderId);

Сделки

// Получить все сделки
$deals = $client->deals()->all();

Скидки на инструменты

// Получить скидки для всех инструментов
$discounts = $client->discounts()->get();

// Получить скидки для конкретных тикеров
$discounts = $client->discounts()->get(['SBER', 'GAZP']);

WebSocket API

Все WebSocket каналы поддерживают автоматическое переподключение с экспоненциальной задержкой.

Лимиты (Real-time)

$client->websocket()->limits(function ($data) {
    echo "Обновление лимитов: " . json_encode($data) . PHP_EOL;
});

Портфель (Real-time)

$client->websocket()->portfolio(function ($data) {
    echo "Обновление портфеля: " . json_encode($data) . PHP_EOL;
});

Маржинальные показатели

$client->websocket()->marginalIndicators(function ($data) {
    echo "Маржинальные показатели: " . json_encode($data) . PHP_EOL;
});

Последняя свеча

$client->websocket()->lastCandle('TQBR', 'SBER', function ($data) {
    echo "Новая свеча SBER: " . json_encode($data) . PHP_EOL;
});

Стакан котировок (Order Book)

$client->websocket()->orderBook('TQBR', 'SBER', function ($data) {
    echo "Обновление стакана SBER: " . json_encode($data) . PHP_EOL;
});

Котировки

$client->websocket()->quotes(['TQBR:SBER', 'TQBR:GAZP'], function ($data) {
    echo "Котировки: " . json_encode($data) . PHP_EOL;
});

Обезличенные сделки (All Trades)

$client->websocket()->allTrades('TQBR', 'SBER', function ($data) {
    echo "Новая сделка SBER: " . json_encode($data) . PHP_EOL;
});

Статус исполнения ордеров

$client->websocket()->orderExecutionStatus(function ($data) {
    echo "Статус ордера: " . json_encode($data) . PHP_EOL;
});

Статус транзакций

$client->websocket()->transactionStatus(function ($data) {
    echo "Статус транзакции: " . json_encode($data) . PHP_EOL;
});

Обработка ошибок

Для каждого типа ошибок — своё исключение:

use Tigusigalpa\BCSTrade\Exceptions\BCSAuthException;
use Tigusigalpa\BCSTrade\Exceptions\BCSRateLimitException;
use Tigusigalpa\BCSTrade\Exceptions\BCSForbiddenException;
use Tigusigalpa\BCSTrade\Exceptions\BCSApiException;

try {
    $limits = $client->limits()->get();
} catch (BCSAuthException $e) {
    // Ошибка аутентификации (401)
    // Возможно, истек refresh_token (90 дней)
    echo "Ошибка аутентификации: " . $e->getMessage();
} catch (BCSForbiddenException $e) {
    // Доступ запрещен (403)
    // Проверьте scope токена (trade-api-read vs trade-api-write)
    echo "Доступ запрещен: " . $e->getMessage();
} catch (BCSRateLimitException $e) {
    // Превышен лимит запросов (429)
    $retryAfter = $e->getRetryAfter(); // секунды до повторной попытки
    echo "Rate limit. Повторить через: " . $retryAfter . " сек";
} catch (BCSApiException $e) {
    // Общая ошибка API (4xx, 5xx)
    $statusCode = $e->getStatusCode();
    $responseBody = $e->getResponseBody();
    echo "API ошибка {$statusCode}: " . json_encode($responseBody);
}

Примеры использования

Пример 1: Получение текущей цены акции

$candles = $client->marketData()->getCandles(
    'TQBR',
    'SBER',
    date('Y-m-d\TH:i:s\Z', strtotime('-1 day')),
    date('Y-m-d\TH:i:s\Z'),
    TimeFrame::M5
);

$lastCandle = end($candles['bars']);
$currentPrice = $lastCandle['close'];

echo "Текущая цена SBER: {$currentPrice} руб.";

Пример 2: Создание и отслеживание ордера

use Ramsey\Uuid\Uuid;

$clientOrderId = Uuid::uuid4()->toString();

// Создаем лимитный ордер
$order = $client->orders()->create([
    'clientOrderId' => $clientOrderId,
    'side' => OrderSide::Buy,
    'orderType' => OrderType::Limit,
    'orderQuantity' => 10,
    'ticker' => 'SBER',
    'classCode' => 'TQBR',
    'price' => 250.00,
]);

echo "Ордер создан: {$clientOrderId}\n";

// Проверяем статус
$status = $client->orders()->status($clientOrderId);
echo "Статус: " . json_encode($status) . "\n";

// Если нужно изменить цену
$updated = $client->orders()->edit($clientOrderId, [
    'price' => 249.50,
]);

echo "Ордер обновлен\n";

Пример 3: Мониторинг портфеля в реальном времени

// Запускаем WebSocket для отслеживания изменений портфеля
$client->websocket()->portfolio(function ($data) {
    $totalValue = 0;
    
    foreach ($data['positions'] ?? [] as $position) {
        $totalValue += $position['marketValue'] ?? 0;
    }
    
    echo date('H:i:s') . " - Общая стоимость портфеля: {$totalValue} руб.\n";
});

Пример 4: Получение исторических данных за год

// Автоматическая пагинация для больших периодов
$candles = $client->marketData()->getCandlesPaginated(
    'TQBR',
    'SBER',
    '2024-01-01T07:00:00Z',
    '2024-12-31T20:00:00Z',
    TimeFrame::D
);

echo "Получено {$candles['bars']} свечей\n";

// Расчет доходности
$firstPrice = $candles['bars'][0]['open'];
$lastPrice = end($candles['bars'])['close'];
$return = (($lastPrice - $firstPrice) / $firstPrice) * 100;

echo "Доходность за год: " . number_format($return, 2) . "%\n";

Тестирование

Тесты написаны на Pest.

# Установить зависимости
composer install

# Запустить тесты
./vendor/bin/pest

# Запустить тесты с покрытием
./vendor/bin/pest --coverage

Документация API

Документация BCS Trade API: https://trade-api.bcs.ru

Разделы:

Важные ограничения

  1. Refresh Token: Действителен 90 дней. После истечения необходимо получить новый через веб-интерфейс БКС.
  2. Исторические свечи: Максимум 1000 баров за один запрос (с 02.03.2026). Используйте getCandlesPaginated() для больших периодов.
  3. Rate Limiting: API имеет ограничения на количество запросов. Обрабатывайте BCSRateLimitException.
  4. Торговые операции: Требуют client_id = 'trade-api-write'.

Changelog

См. CHANGELOG.md.

Лицензия

MIT. См. LICENSE.

Автор

Igor Sazonov

Поддержка

Баги и предложения — в Issues, пулл-реквесты — сюда.

Дисклеймер: пакет не является официальным продуктом БКС. Используйте на свой риск.