Горячие клавиши

← и →: предыдущая и следующая глава

S или /: поиск по книге

?: показать эту подсказку

Esc: закрыть подсказку или поиск

Потолки заявок и режим только чтения

Брокер проверяет свое: хватает ли денег, не вышла ли цена за допустимые пределы. Сколько бот вообще имеет право поставить, брокер не знает. Это решает человек, чей счет. Для этого у клиента есть три настройки: режим «только чтение» и два потолка на одну заявку. Они нужны прежде всего тогда, когда код пишет или запускает ИИ-агент: человек дает ему посмотреть настоящий счет, но не торговать, или торговать, но не больше заданной суммы.

Проверка идет в клиенте до отправки. Отказ не тратит квоту лимитов, а cid отказанной заявки остается неиспользованным: с ним можно поставить заявку, которая в потолок укладывается.

Настройки

НастройкаЧто делаетПеременная окружения
read_only = trueНи одна команда не уходит к брокеру: place, replace, cancel, place_stop, cancel_stop. Чтения и подписки работают, reconcile тоже: он ничего не меняет у брокераBROKAPI_READ_ONLY=1
max_order_qtyСамое большое количество одной заявки, в единицах инструмента (штуки, контракты), как в самой заявкеBROKAPI_MAX_ORDER_QTY
max_order_valueСамая большая стоимость одной заявки: количество, умноженное на цену, в деньгах ценыBROKAPI_MAX_ORDER_VALUE

Где задать:

  • Python: brokapi.connect("bcs", read_only=True), brokapi.connect("tbank", max_order_value="50000"). Число можно строкой или целым.
  • Гейтвей: те же ключи в [gateway.settings] файла brokapi.toml (Конфиг). Тогда потолки действуют на всех ботов этого гейтвея, бот обойти их не может. brokapi check показывает их строкой limits on orders: ..., а неверное число считает ошибкой конфига.
  • Окружение: переменные из таблицы. Их читают и Python, и гейтвей.

Если потолок задан и в настройке, и в окружении, действует более строгий. read_only включен, если он включен хотя бы в одном месте, а из двух потолков берется меньший. Почему так: переменную окружения ставит человек, а настройку пишет код бота. Если бы код мог переписать переменную, агент одной строкой read_only=False снял бы защиту, которую поставил человек. Так код может защиту только ужесточить.

Что считается

  • Стоимость лимитной заявки - количество, умноженное на ее цену. Стоп-заявки - на цену лимита, а если ее нет, на цену срабатывания.
  • Рыночная заявка при заданном max_order_value не проходит. Цены у нее нет, считать стоимость не по чему, а цена из стакана к моменту исполнения уже другая. Ставьте лимитную. max_order_qty к рыночной заявке применяется как обычно.
  • replace проверяется по новой заявке: заданное количество и цена, а то, что не задано, берется из заменяемой заявки (для этого клиент один раз читает ее состояние).
  • Деньги цены. Стоимость - в той валюте, в которой цена: рубли у российских брокеров, USDT у Bybit. У фьючерса цена в пунктах, и стоимость тоже в пунктах, а не в рублях. Для фьючерсов надежнее max_order_qty.
  • Потолок на одну заявку, не на день и не на позицию. Сто заявок по потолку пройдут все. Предел на день или на позицию бот считает сам.

Отказ

Отказ - Rejected с кодом. В тексте сказано, что ничего не отправлено.

codeКогда
read_onlyЛюбая команда в режиме только чтения
max_order_qtyКоличество больше потолка
max_order_valueСтоимость больше потолка, или рыночная заявка при заданном потолке стоимости

Повторять ту же заявку нет смысла: она снова получит отказ. Бот уменьшает заявку или останавливается и сообщает человеку.

async def look_but_do_not_trade() -> list[str]:
    said = []
    async with await mock_bcs() as mock:
        # On a real account: brokapi.connect("bcs", read_only=True), or BROKAPI_READ_ONLY=1 in the environment.
        client = await mock.connect(read_only=True)
        sber = await client.instrument("SBER")
        book = await client.order_book(sber, depth=1)  # reads work
        said.append(f"bid {book.bids[0].price}")
        await client.reconcile([])
        try:
            await client.place(cid=brokapi.new_cid(), instrument=sber, side=brokapi.Side.BUY, qty=1, price=Decimal("240"))
        except brokapi.Rejected as refused:  # checked before anything is sent: no quota spent, the cid is still good
            said.append(f"refused: {refused.code}")

        capped = await mock.connect(name="capped", max_order_value="10000")
        await capped.reconcile([])
        try:
            await capped.place(
                cid=brokapi.new_cid(), instrument=sber, side=brokapi.Side.BUY, qty=100, price=Decimal("240")
            )
        except brokapi.Rejected as refused:  # 100 x 240 = 24000, above the cap
            said.append(f"refused: {refused.code}")
    return said

Пример - crates/python/examples/guard.py, его запускает tests/test_guides.py. Все случаи из этой страницы проверяет tests/test_guard.py, в том числе гейтвей и переменные окружения.

raw в режиме только чтения

Через raw можно вызвать любой метод брокера, в том числе постановку заявки. Флаг command ставит вызывающий код, поэтому режим только чтения ему не верит и пропускает только вызовы, которые видны как чтение по самому имени: метод gRPC, имя которого начинается с Get (Т-Банк, Финам), и HTTP GET (БКС, АЛОР, Bybit). Остальное получает отказ read_only, даже если это тоже чтение (у Финама Bars, у БКС поиск заявок через POST). Это надежно, но не полно: такие чтения доступны через общие методы клиента.

Где потолков нет

Потолки живут в клиенте: им пользуются пакет для Python, гейтвей и программа brokapi. Коннекторы Rust, если создавать их напрямую (UserDataConnector::start), потолков не знают: тип brokapi_client::Client с Client::start_with(config, OrderGuard { .. }, ...) их дает. Через connect_gateway бот получает потолки гейтвея, а свои настройки потолков в connect_gateway не передаются.

Листать можно клавишами Влево и Вправо.

brokapi by RunInfra