Потолки заявок и режим только чтения
Брокер проверяет свое: хватает ли денег, не вышла ли цена за допустимые пределы. Сколько бот вообще имеет право поставить, брокер не знает. Это решает человек, чей счет. Для этого у клиента есть три настройки: режим «только чтение» и два потолка на одну заявку. Они нужны прежде всего тогда, когда код пишет или запускает ИИ-агент: человек дает ему посмотреть настоящий счет, но не торговать, или торговать, но не больше заданной суммы.
Проверка идет в клиенте до отправки. Отказ не тратит квоту лимитов, а 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 не передаются.
Листать можно клавишами Влево и Вправо.