Skip to content

tigusigalpa/bcs-trade-go

Repository files navigation

BCS Trade Go Client

БКС Брокер API Golang

Go Reference Go Report Card License: MIT

Golang-клиент для BCS Trade API — торгового API БКС Брокера.

Пакет позволяет подключаться к бирже через БКС из Go-приложений: получать котировки и свечи, торговать акциями и облигациями на Московской бирже, следить за портфелем и ордерами в реальном времени через WebSocket. Подходит для написания торговых ботов, аналитических сервисов и любых систем, которым нужен программный доступ к брокеру.

Реализованы все HTTP-эндпоинты и все WebSocket-каналы из документации БКС. Токен обновляется автоматически, клиент потокобезопасен, все методы принимают context.Context.

Зачем этот пакет

BCS Trade API — относительно новый REST + WebSocket интерфейс от БКС для алготрейдинга и автоматизации торговли на MOEX. Документация есть, но готовых клиентов на Go до сих пор не было. Этот пакет закрывает этот пробел: не нужно вручную разбираться с OAuth2-токенами, пагинацией свечей и переподключением WebSocket — всё это уже сделано.

Если вы пишете торгового бота на Go, строите дашборд с рыночными данными Московской биржи или просто хотите автоматизировать свой портфель у БКС — можно не писать обёртку с нуля, а взять готовую.

Установка

go get github.com/tigusigalpa/bcs-trade-go

Требуется Go 1.21+.

Настройка

Для работы нужен refresh token, который можно получить в личном кабинете БКС Мир Инвестиций в разделе настроек API.

Есть два скоупа:

  • trade-api-read (по умолчанию) — чтение данных: котировки, портфель, справочники
  • trade-api-write — торговля: создание, редактирование и отмена ордеров

Самый простой способ — задать переменные окружения:

export BCS_TRADE_REFRESH_TOKEN="ваш-refresh-token"
export BCS_TRADE_CLIENT_ID="trade-api-read"   # необязательно, это значение по умолчанию

Быстрый старт

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/tigusigalpa/bcs-trade-go"
)

func main() {
    ctx := context.Background()

    client, err := bcstrade.NewFromEnv(ctx)
    if err != nil {
        log.Fatal(err)
    }

    portfolio, err := client.Portfolio.Get(ctx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Стоимость портфеля: %.2f %s\n",
        portfolio.Summary.TotalValue,
        portfolio.Summary.Currency)
}

Если нужна ручная настройка:

cfg := &bcstrade.Config{
    RefreshToken:  "ваш-refresh-token",
    ClientID:      bcstrade.ClientIDWrite,
    Timeout:       30 * time.Second,
    RetryAttempts: 3,
    RetryDelay:    500 * time.Millisecond,
}

client, err := bcstrade.New(ctx, cfg)

Примеры

Исторические свечи

params := models.GetCandlesParams{
    ClassCode: "TQBR",
    Ticker:    "SBER",
    StartDate: time.Now().AddDate(0, 0, -7),
    EndDate:   time.Now(),
    TimeFrame: models.TimeFrameH1,
}

candles, err := client.MarketData.GetCandles(ctx, params)
if err != nil {
    log.Fatal(err)
}

for _, bar := range candles.Bars {
    fmt.Printf("%s: O=%.2f H=%.2f L=%.2f C=%.2f V=%.0f\n",
        bar.Time.Format("2006-01-02 15:04"),
        bar.Open, bar.High, bar.Low, bar.Close, bar.Volume)
}

API отдаёт максимум 1000 баров за запрос. Если нужно больше — используйте GetCandlesPaginated, он сам разобьёт запрос на части и склеит результат:

candles, err := client.MarketData.GetCandlesPaginated(ctx, params)

Справочники

instrument, err := client.Information.GetInstrumentByTicker(ctx, "TQBR", "SBER")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("%s (%s), лот: %d\n", instrument.Name, instrument.ISIN, instrument.LotSize)

schedule, err := client.Information.GetDailySchedule(ctx, "TQBR", "SBER")

Торговля

Для торговли нужен скоуп trade-api-write.

price := 250.50
order := &models.CreateOrderRequest{
    Side:          models.OrderSideBuy,
    OrderType:     models.OrderTypeLimit,
    OrderQuantity: 10,
    Ticker:        "SBER",
    ClassCode:     "TQBR",
    Price:         &price,
    // ClientOrderID сгенерируется сам, если не задан
}

resp, err := client.Orders.Create(ctx, order)

Отмена и проверка статуса:

_, err := client.Orders.Cancel(ctx, "order-uuid")

status, err := client.Orders.Status(ctx, "order-uuid")
fmt.Printf("%s: исполнено %d/%d\n", status.Status, status.FilledQuantity, status.OrderQuantity)

WebSocket

Все WebSocket-методы возвращают два канала — данные и ошибки. Подписка живёт пока жив контекст. При обрыве соединения клиент переподключается сам с экспоненциальной задержкой.

Котировки:

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

quoteCh, errCh, err := client.WebSocket.Quotes(ctx, []string{"TQBR:SBER", "TQBR:GAZP"})
if err != nil {
    log.Fatal(err)
}

for {
    select {
    case q := <-quoteCh:
        fmt.Printf("%s: Bid=%.2f Ask=%.2f Last=%.2f\n", q.Ticker, q.Bid, q.Ask, q.Last)
    case err := <-errCh:
        log.Printf("ws error: %v", err)
    case <-ctx.Done():
        return
    }
}

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

execCh, errCh, err := client.WebSocket.OrderExecutionStatus(ctx)

for {
    select {
    case e := <-execCh:
        fmt.Printf("Ордер %s: %s, заполнено %d, средняя %.2f\n",
            e.ClientOrderID, e.Status, e.FilledQty, e.AveragePrice)
    case err := <-errCh:
        log.Printf("ws error: %v", err)
    case <-ctx.Done():
        return
    }
}

Аналогично работают OrderBook, LastCandle, AllTrades, Portfolio, Limits, MarginalIndicators, TransactionStatus.

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

Все ошибки типизированы, их можно проверять через errors.As:

resp, err := client.Orders.Create(ctx, order)
if err != nil {
    var authErr *bcstrade.AuthError
    var forbiddenErr *bcstrade.ForbiddenError
    var rateLimitErr *bcstrade.RateLimitError
    var validationErr *bcstrade.ValidationError

    switch {
    case errors.As(err, &authErr):
        // refresh token протух — нужно получить новый в ЛК
    case errors.As(err, &forbiddenErr):
        // не тот скоуп (нужен trade-api-write)
    case errors.As(err, &rateLimitErr):
        // слишком частые запросы, в RetryAfter — сколько ждать
    case errors.As(err, &validationErr):
        // ошибка валидации до отправки запроса (нет обязательного поля и т.п.)
    }
}

При получении HTTP 401 клиент сам пробует обновить токен и повторить запрос — если не помогло, вернёт AuthError.

Что реализовано

HTTP-эндпоинты — всё, что есть в BCS Trade REST API:

  • Аутентификация (OAuth2, автоматический refresh)
  • Портфель и лимиты счёта
  • Справочники: инструменты по ISIN, тикеру или типу, расписание торгов, торговые статусы
  • Исторические свечи (OHLCV) с автоматической пагинацией при запросе больше 1000 баров
  • Ордера: выставление, редактирование, отмена, получение статуса и списка
  • Сделки и скидки на инструменты

WebSocket-каналы — подписки на данные в реальном времени:

  • Котировки (bid/ask/last) и лента анонимных сделок
  • Стакан заявок (Level 2 order book)
  • Свечи в реальном времени (последняя свеча по инструменту)
  • Портфель, лимиты и маржинальные показатели
  • Статус исполнения ордеров и транзакций

Подробное описание каждого метода — в документации на pkg.go.dev.

Особенности реализации

Несколько вещей, которые стоит знать:

  • OAuth2 под капотом. Вы отдаёте refresh token — дальше клиент сам получает access token и обновляет его за минуту до истечения. При 401 пробует обновить токен и повторить запрос.
  • Пагинация свечей. BCS Trade API с марта 2026 отдаёт не больше 1000 баров за раз. GetCandlesPaginated сам оценивает нужное количество запросов, выполняет их и склеивает результат.
  • WebSocket с переподключением. При обрыве связи клиент переподключается с экспоненциальной задержкой (от 1 до 60 секунд), каждый раз обновляя токен. Отмена контекста корректно закрывает соединение.
  • Потокобезопасность. Токен хранится под мьютексом, клиентом можно пользоваться из нескольких горутин одновременно.
  • UUID для ордеров. Если не передать ClientOrderID при создании ордера — он сгенерируется автоматически (UUID v4).

Тесты

go test ./...           # базовый прогон
go test -race ./...     # с детектором гонок
go test -cover ./...    # с покрытием

Зависимости

  • golang.org/x/oauth2
  • github.com/google/uuid
  • github.com/gorilla/websocket
  • github.com/stretchr/testify (только тесты)

Лицензия

MIT. Подробности в файле LICENSE.

Автор — Игорь Сазонов (sovletig@gmail.com).

Это неофициальная библиотека, автор не связан с БКС Брокером.

Похожие проекты

Если вам нужен доступ к другим брокерам с Московской биржи из Go:

  • invest-api-go-sdk — официальный SDK Т-Инвестиций (бывший Тинькофф Инвестиции)
  • go-quik — работа с QUIK через DDE/Trans2Quik

Если знаете другие Go-библиотеки для российских брокеров — присылайте PR, добавлю.


Документация BCS Trade API · pkg.go.dev · GitHub

About

Golang-клиент для BCS Trade API (БКС Брокер). Котировки, свечи, стакан, портфель, ордера — через REST и WebSocket. Автоматическое обновление OAuth2-токена, пагинация свечей, переподключение WebSocket. Подходит для торговых ботов и алготрейдинга на Московской бирже (MOEX). Go 1.21+, MIT.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages