Изображение блога
БКС Мир Инвестиций
БКС Мир Инвестиций Блог компании БКС Мир инвестиций
Сегодня в 10:40

Как автоматизировать торговлю через API. Первый скрипт за 15 минут

Как автоматизировать торговлю через API. Первый скрипт за 15 минут

У ручной торговли есть предел, и он не связан с опытом. Вы не можете одновременно следить за десятками инструментов, реагировать на движение цены за доли секунды и при этом сохранять холодную голову. Когда рынок падает — рука тянется закрыть позицию. Когда растет — жалко продавать. Эмоции и скорость реакции — два барьера, которые мешают торговать по плану.

Алгоритм этих барьеров не знает. Он отрабатывает сигнал мгновенно, торгует строго по заданным правилам и не передумывает в последний момент. Именно поэтому алгоритмическая торговля давно стала стандартом среди профессионалов рынка.

И хорошая новость: чтобы попробовать, не нужно быть программистом. Запустить первого торгового робота проще, чем кажется. БКС предоставляет открытый API — через него ваша программа может получать котировки и выставлять заявки напрямую, без терминала и ручных кликов.

Сегодня напишем первый рабочий скрипт: он узнает текущую цену Сбербанка и выставит заявку на покупку. Все на стандартном Python, без сторонних библиотек — буквально 15 минут от начала до результата.

В примерах используем акции Сбербанка (SBER) — один из самых ликвидных инструментов Мосбиржи. Логика универсальна: заменить тикер на любой другой инструмент — дело одной строки в коде.

Где писать и запускать код

Прежде чем перейти к делу — разберем момент, который часто сбивает с толку новичков. Это сэкономит вам много времени.

Адреса запросов — это не сайты для браузера

В коде вы встретите адреса вроде be.broker.ru/...orders. В браузер их вставлять не нужно — это не страницы, а адреса, по которым скрипт сам отправляет запросы. Всю работу с ними берет на себя код.

Код пишется в файл и запускается двумя способами

Сам скрипт — это обычный текстовый файл с расширением .py (например, trade.py). Вы создаете файл, вставляете в него код и запускаете командой python3 trade.py. Запустить его можно двумя способами:

На своем компьютере На VPS-сервере
Подходит, чтобы быстро попробовать и понять, как все работает. Нужен только установленный Python. Минус: скрипт работает, только пока компьютер включен и есть интернет. Закрыли ноутбук — робот остановился. Подходит для постоянной работы робота. VPS — это удаленный компьютер в дата-центре, который работает круглосуточно и не зависит от вашего интернета. Это рекомендуемый вариант для реальной торговли.


Принцип в обоих случаях одинаковый: создаете файл, вставляете код, запускаете. Разница только в том, где этот файл живет и как долго работает.

Хотите, чтобы робот работал 24/7?  Тогда вам нужен VPS. Как его выбрать, арендовать и подключиться к нему — мы подробно разобрали в первых двух статьях серии:

Дальше команды показаны для Linux-сервера — это операционная система, на которой обычно работают VPS (такой мы настраивали в Статье 2). Если запускаете на своём компьютере, код будет тот же самый. Отличается только команда запуска: на Linux и macOS — python3 trade.py, на Windows — python trade.py.

Выпускаем токен для торговли

Чтобы скрипт мог действовать от вашего имени — запрашивать данные и выставлять заявки — ему нужен ключ доступа. Этот ключ называется refresh-токен, и выпускается он в личном кабинете БКС. Делается один раз, перед началом работы.

Выпустить токен
  1. Войдите в веб-версию личного кабинета БКС Мир инвестиций.
  2. Откройте раздел Профиль и выберите счет, к которому подключаете API.
  3. В разделе о счете найдите пункт Токены API.
  4. Выберите тип токена «Для торговли» — он позволяет выставлять заявки (токен «Для чтения данных» умеет только смотреть котировки, заявку им не выставить).
  5. Нажмите «Выпустить токен» и сразу скопируйте его.

Токен показывается только один раз — сразу после выпуска. Скопируйте его и сохраните в надежном месте. Это ключ от вашего торгового счета: не передавайте его третьим лицам и не публикуйте в открытом коде. Если потеряете — просто выпустите новый.

Авторизация: даем скрипту доступ к счету

Зачем это нужно

Токен, который вы выпустили в личном кабинете, — это refresh-токен, долгоживущий ключ на 90 дней. Но напрямую для запросов он не используется. Перед работой его нужно обменять на access-токен — короткоживущий ключ на 24 часа, которым и подписываются все запросы к API.

Зачем так сложно? Ради безопасности. Refresh-токен хранится у вас и никуда не отправляется в каждом запросе. А «в эфире» работает только access-токен — и даже если его перехватят, через сутки он станет бесполезным.

На практике вам не нужно вникать в эти тонкости — скрипт сделает обмен сам. Для этого он обращается к адресу авторизации и передает три параметра:


Параметр Значение Описание
client_id trade-api-write Тип токена. trade-api-write — для торговли, trade-api-read — только для чтения данных
refresh_token <ВАШ_ТОКЕН> Refresh-токен из личного кабинета БКС
grant_type refresh_token Тип запроса. Всегда передаём значение refresh_token


Создаем файл и пишем первый блок

Создаем файл скрипта. На Linux-сервере это делается командой в терминале (на своем компьютере можно создать файл trade.py в любом текстовом редакторе):

nano trade.py

Откроется пустой редактор. Вставляем в него первый блок — настройки и авторизацию:

import http.client
import json
import uuid

# ── Настройки — заполните перед запуском ──────────────────────────────
REFRESH_TOKEN = "ВАШ_REFRESH_ТОКЕН_ИЗ_ЛК_БКС"
TICKER        = "SBER"
CLASS_CODE    = "TQBR"
QUANTITY      = 1         # количество лотов
DISCOUNT      = 0.99      # выставляем на 1% ниже рынка
HOST          = "be.broker.ru"

# ── Шаг 1: получаем access-токен ──────────────────────────────────────
conn = http.client.HTTPSConnection(HOST)
payload = json.dumps({
    "client_id": "trade-api-write",
    "refresh_token": REFRESH_TOKEN,
    "grant_type": "refresh_token"
})

conn.request(
    "POST",
    "/trade-api-keycloak/realms/tradeapi/protocol/openid-connect/token",
    payload,
    {"Content-Type": "application/json"}
)
auth_data    = json.loads(conn.getresponse().read().decode("utf-8"))
ACCESS_TOKEN = auth_data["access_token"]
print("[1/3] Access-токен получен ✓")

# Формируем заголовки, которые будем использовать во всех следующих запросах
AUTH_HEADERS = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": f"Bearer {ACCESS_TOKEN}"
}

После успешного выполнения этого блока в переменной ACCESS_TOKEN будет лежать ключ доступа. Мы вынесли заголовки в отдельную переменную AUTH_HEADERS — чтобы не дублировать их в каждом следующем запросе.

Вставьте ваш refresh-токен в строку REFRESH_TOKEN точно как он есть — без пробелов, кавычек и переносов строк. Даже один лишний символ вернет ошибку авторизации.


Узнаем, почем сейчас Сбер

Зачем это нужно

Мы собираемся выставить лимитную заявку — то есть заявку с конкретной ценой: «куплю, но не дороже, чем за столько-то». Чтобы назначить разумную цену, сначала нужно узнать, где Сбер торгуется прямо сейчас.

Скрипт запросит котировку SBER и возьмет из ответа поле last — цену последней сделки. От нее мы оттолкнемся: поставим цену заявки чуть ниже рынка, чтобы она не сработала мгновенно. Так у вас будет время увидеть заявку в терминале и при желании отменить.

API возвращает не только последнюю цену, но и полный набор данных по бумаге: цены открытия и закрытия, максимум и минимум за день, лучшие цены покупки и продажи. Нам сейчас нужна только last.

Добавляем второй блок в файл

Дописываем этот код в тот же файл trade.py, ниже первого блока:

# ── Шаг 2: получаем текущую цену ─────────────────────────────────────
conn = http.client.HTTPSConnection(HOST)
payload = json.dumps({
    "instruments": [{
        "ticker": TICKER,
        "classCode": CLASS_CODE
    }]
})

conn.request(
    "POST",
    "/trade-api-market-data-connector/api/v1/quotes",
    payload,
    AUTH_HEADERS
)
quote_data  = json.loads(conn.getresponse().read().decode("utf-8"))
last_price  = quote_data["records"][0]["last"]
limit_price = round(last_price * DISCOUNT, 2)
print(f"[2/3] Цена {TICKER}: {last_price} ₽  →  лимитная цена заявки: {limit_price} ₽")

Переменная DISCOUNT задана в настройках как 0,99 — это означает «на 1% ниже рынка». Например, если SBER торгуется по 312,50 руб., лимитная цена составит 309,37 руб. Заявка встанет в стакан и будет ждать, пока цена не опустится до этого уровня.

Если хотите, чтобы заявка исполнилась быстрее — уменьшите отступ, например DISCOUNT = 0,999 (0,1% ниже рынка). Для первого теста лучше держать 1% — это безопаснее.

Выставляем заявку на покупку

Что произойдет

Это главный момент — скрипт отправит на биржу заявку: «купи 1 лот Сбера по цене не выше заданной». Биржа поставит ее в стакан заявок и будет ждать, пока не найдется продавец с подходящей ценой. Если цена не достигнута — заявка просто висит активной до конца торгового дня.

Этим лимитная заявка и отличается от рыночной: вы сами контролируете цену, по которой готовы купить, а не соглашаетесь на любую текущую. Для алгоритмической торговли это обычно важнее.

Параметры заявки

В этом запросе мы описываем заявку набором параметров. Вот что означает каждый:

Параметр Значение Описание
clientOrderId uuid.uuid4() Уникальный ID заявки — генерируем сами. Нужен, чтобы потом проверить статус и избежать дублей
side «1» Направление: 1 — покупка, 2 — продажа
orderType «2» Тип заявки: 1 — рыночная (по текущей цене), 2 — лимитная (по заданной цене)
orderQuantity 1 Количество в лотах. Начинаем с 1 — минимально возможного
ticker «SBER» Тикер инструмента
classCode «TQBR» Класс бумаги на Мосбирже. Для большинства акций — TQBR
price limit_price Наша лимитная цена, рассчитанная на шаге 2


Это реальная заявка — прочитайте перед запуском

Скрипт выставит настоящую биржевую заявку, которая может быть исполнена. Убедитесь, что:
• QUANTITY равен 1 — не меняйте до тех пор, пока не убедитесь, что все работает.
• DISCOUNT равен 0,99 — цена заявки будет на 1% ниже рынка, мгновенного исполнения не будет.
• На счете достаточно средств для покупки 1 лота SBER.
• После запуска откройте терминал БКС и найдите заявку — убедитесь, что все верно.
• При необходимости отмените заявку вручную в терминале.

Добавляем третий блок в файл

Дописываем в trade.py, ниже предыдущего кода:

# ── Шаг 3: выставляем лимитную заявку ───────────────────────────────
conn     = http.client.HTTPSConnection(HOST)
order_id = str(uuid.uuid4())   # генерируем уникальный ID заявки

payload = json.dumps({
    "clientOrderId": order_id,
    "side": "1",        # 1 = покупка
    "orderType": "2",   # 2 = лимитная заявка
    "orderQuantity": QUANTITY,
    "ticker": TICKER,
    "classCode": CLASS_CODE,
    "price": limit_price
})

conn.request(
    "POST",
    "/trade-api-bff-operations/api/v1/orders",
    payload,
    AUTH_HEADERS
)
order_data = json.loads(conn.getresponse().read().decode("utf-8"))
print(f"[3/3] Заявка выставлена ✓  ID: {order_id}  Статус: {order_data.get('status')}")

Обратите внимание на clientOrderId — мы сохраняем его в переменную order_id. Этот идентификатор понадобится на следующем шаге, чтобы запросить статус заявки. Именно по нему API находит конкретную заявку среди всех ваших.

Проверяем, что заявка встала в стакан

Зачем это нужно

Если запрос на создание заявки прошел успешно — это значит лишь, что API принял команду. Но заявка еще может не дойти до стакана: биржа отклоняет заявки из-за нарушения ценовых ограничений, нехватки средств на счете или технических причин. Поэтому стоит отдельно запросить статус и убедиться, что заявка действительно активна.

Какие бывают статусы

В ответе придет текущий статус заявки. Чаще всего вы увидите одно из значений:

  • NEW — заявка принята и ждет исполнения в стакане. Это то, что нам нужно.
  • FILLED — заявка полностью исполнена, сделка прошла.
  • CANCELLED — заявка отменена (вами или биржей).
  • REJECTED — заявка отклонена. Стоит проверить цену и остаток средств на счете.

Добавляем последний блок и запускаем

Дописываем финальный блок в trade.py:

# ── Шаг 4: проверяем статус заявки ──────────────────────────────────
conn = http.client.HTTPSConnection(HOST)

conn.request(
    "GET",
    f"/trade-api-bff-operations/api/v1/orders/{order_id}",
    headers=AUTH_HEADERS
)
status_data = json.loads(conn.getresponse().read().decode("utf-8"))
print(f"Статус заявки: {status_data}")

Сохраняем файл (в редакторе nano: Ctrl+O, затем Enter, затем Ctrl+X) и запускаем скрипт командой:

python3 trade.py

Ожидаемый вывод в терминале:

[1/3] Access-токен получен ✓
[2/3] Цена SBER: 312.45 ₽  →  лимитная цена заявки: 309.33 ₽
[3/3] Заявка выставлена ✓  ID: 3fa85f64-5717-4562-b3fc-2c963f66afa6  Статус: NEW
Статус заявки: {'clientOrderId': '3fa85f64-...', 'status': 'NEW'}

Статус NEW — это хороший результат. Заявка принята биржей и ждет своего часа в стакане. Откройте терминал БКС и найдите ее в разделе активных заявок.

Полный скрипт

Если вы собирали скрипт по частям — вот он целиком, для удобства. Можно скопировать его одним куском в trade.py, вставить свой токен в первую строку настроек и запустить:

python3 trade.py

 
import http.client
import json
import uuid

# ══════════════════════════════════════════════════════════════════════
# НАСТРОЙКИ — заполните перед запуском
# ══════════════════════════════════════════════════════════════════════
REFRESH_TOKEN = "ВАШ_REFRESH_ТОКЕН_ИЗ_ЛК_БКС"
TICKER        = "SBER"
CLASS_CODE    = "TQBR"
QUANTITY      = 1         # количество лотов
DISCOUNT      = 0.99      # лимит = 1% ниже рынка
HOST          = "be.broker.ru"

# ── Шаг 1: получаем access-токен ─────────────────────────────────────
conn = http.client.HTTPSConnection(HOST)
conn.request("POST",
    "/trade-api-keycloak/realms/tradeapi/protocol/openid-connect/token",
    json.dumps({"client_id": "trade-api-write",
               "refresh_token": REFRESH_TOKEN,
               "grant_type": "refresh_token"}),
    {"Content-Type": "application/json"})
ACCESS_TOKEN = json.loads(conn.getresponse().read())["access_token"]
print("[1/3] Access-токен получен ✓")
AUTH_HEADERS = {"Content-Type": "application/json", "Accept": "application/json",
               "Authorization": f"Bearer {ACCESS_TOKEN}"}

# ── Шаг 2: получаем текущую цену ─────────────────────────────────────
conn = http.client.HTTPSConnection(HOST)
conn.request("POST",
    "/trade-api-market-data-connector/api/v1/quotes",
    json.dumps({"instruments": [{"ticker": TICKER, "classCode": CLASS_CODE}]}),
    AUTH_HEADERS)
quote    = json.loads(conn.getresponse().read())["records"][0]
last_price  = quote["last"]
limit_price = round(last_price * DISCOUNT, 2)
print(f"[2/3] Цена {TICKER}: {last_price} ₽  →  лимит: {limit_price} ₽")

# ── Шаг 3: выставляем лимитную заявку ───────────────────────────────
conn     = http.client.HTTPSConnection(HOST)
order_id = str(uuid.uuid4())
conn.request("POST",
    "/trade-api-bff-operations/api/v1/orders",
    json.dumps({"clientOrderId": order_id, "side": "1", "orderType": "2",
               "orderQuantity": QUANTITY, "ticker": TICKER,
               "classCode": CLASS_CODE, "price": limit_price}),
    AUTH_HEADERS)
order_data = json.loads(conn.getresponse().read())
print(f"[3/3] Заявка выставлена ✓  ID: {order_id}  Статус: {order_data.get('status')}")

# ── Шаг 4: проверяем статус ──────────────────────────────────────────
conn = http.client.HTTPSConnection(HOST)
conn.request("GET",
    f"/trade-api-bff-operations/api/v1/orders/{order_id}",
    headers=AUTH_HEADERS)
status_data = json.loads(conn.getresponse().read())
print(f"Статус заявки: {status_data}")

Итоговый чеклист

  • Выпустили токен «Для торговли» в личном кабинете БКС.
  • Создали файл trade.py и авторизовались через API.
  • Узнали текущую цену Сбера через API котировок.
  • Рассчитали цену заявки — на 1% ниже рынка.
  • Выставили заявку на покупку 1 лота.
  • Проверили статус — получили NEW — и нашли заявку в терминале БКС.

Что дальше? Скрипт работает — и это уже настоящий торговый инструмент. Но пока он делает одно действие и останавливается. В следующих статьях добавим логику: стратегию входа, управление рисками, автоматический мониторинг. А пока — поиграйте с параметрами: попробуйте другой тикер, поменяйте DISCOUNT, посмотрите, как меняется цена заявки.

Где смотреть все возможности API

В этой статье мы затронули три метода: авторизацию, котировки и заявки. Но API БКС умеет гораздо больше — отмена заявок, история сделок, состояние портфеля, потоковые данные. Все методы, параметры и примеры ответов подробно описаны в официальной документации.

Совет на старте: попробуйте Postman

Если хочется поэкспериментировать с запросами, прежде чем писать код, — обратите внимание на Postman. Это бесплатный сервис, в котором можно отправлять запросы к API в визуальном интерфейсе, без программирования: указываете адрес и параметры — и сразу видите ответ. Удобно, чтобы разобраться, как устроены методы, и проверить токен.

Чтобы упростить старт, мы подготовили готовую коллекцию всех запросов к API БКС. Перейдите по ссылке — и можете сразу выбирать нужные запросы и отправлять их, подставив свой токен: https://www.postman.com/mybroker/workspace/api.

Подробный разбор работы через Postman — тестирование запросов без единой строки кода — будет в отдельной статье серии.

Подписывайтесь на наш канал для трейдеров в «Профите», чтобы быть в курсе последних обновлений, обмениваться опытом с единомышленниками и узнавать лайфхаки.

Данная публикация является личным мнением автора. Мнение владельца сайта может не совпадать с мнением автора.
0 Комментариев

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн