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

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

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

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

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

Ошибки и что с ними делать

Справочник: ошибка или состояние, что оно значит, почему бывает, что делать и куда смотреть дальше. Текст в первой колонке - то, что печатает ошибка в коде (Display), а не пересказ: по нему можно искать страницу поиском. Тест в репозитории проверяет, что тексты ошибок из brokapi::core::Error лежат на этой странице. Объяснение, откуда эти исходы берутся: Круг команд, Круг жизни, Круг событий. У каждой строки таблиц есть якорь (errors.html#unknown, errors.html#rate-limited, errors.html#duplicate_place): на нее можно сослаться из своего кода и документов.

Python. У каждой ошибки есть одноименное исключение в brokapi.errors (и на верхнем уровне пакета: brokapi.Unknown, brokapi.Rejected и так далее), текст str(error) равен тексту в первой колонке. Error::class() - это error.error_class (ErrorClass.RETRYABLE, FATAL, UNKNOWN). Поля вариантов - атрибуты: Rejected.code, Rejected.message, RateLimited.retry_after (timedelta), Unsupported.capability. Подробнее: Как Python связан с ядром.

Две вещи помогают разобраться быстрее. Метод Error::class() (тип brokapi::core::ErrorClass) отвечает на вопрос «можно ли повторить»: Retryable (сеть, лимит), Fatal (тот же вызов упадет снова), Unknown (исход команды неизвестен). И правило для всех команд: команда не повторяется автоматически, а повторять вручную можно только после того, как вы узнали, что заявки нет.

Ошибки вызова (brokapi::core::Error)

Ошибка или состояниеЧто значитПочему бываетЧто делатьПодробнее
configuration error: <причина> (Config)Неверная настройка или вход, который не подходит этому брокеру. Ничего не отправленоТокен или секрет не приняты брокером (у всех трех: БКС, Т-Банк UNAUTHENTICATED, Финам), счет не найден, формат cid не вмещается, идентификатор инструмента другого брокера, маршрут лимитов указывает на несуществующее ведроИсправить настройку или вход. Повтор не поможет, вызов упадет так жеБрокеры
transport error: <причина> (Transport)Сбой связи, ответа брокера нет. Для чтения - повторить можноОбрыв, TLS, таймаут чтения, оборванный поток, нечитаемый ответ. На команде означает, что у коннектора нет признаков, что она дошла, но доказать это он не всегда может: считайте, что заявка могла дойтиЧтение: повторить позже. Команда: сначала посмотреть заявку (order_state), потом решать. Новая заявка - с новым cidЕсли что-то пошло не так
rate limited, retry after <время> (RateLimited)Не хватило квоты: у счетчика библиотеки или брокер ответил «слишком много запросов» (у Т-Банка RESOURCE_EXHAUSTED с номером 80002, 80003, 80006 и без известного номера. Остальные номера этого статуса - Rejected, см. таблицу ниже). В сеть ничего не ушло (если отказал счетчик)Бот шлет быстрее квоты, один счетчик делят разные коннекторы, или общее ведро исчерпано чтениемПодождать retry_after, отправить снова. Если отказал счетчик библиотеки, cid не потрачен и годится снова, но новый cid безопаснее всегда. Если ответил брокер, только новый cid. Библиотека сама не ждет и не повторяетЛимиты запросов
rejected: <код>: <текст> (Rejected)Брокер (или библиотека до отправки) точно отказал, заявки нетЦена вне коридора, нет денег, неизвестная заявка, заявка уже исполнена, не хватает цены у лимитной. Конкретная причина - в code и messageНе повторять тот же запрос. Исправить причину и отправить с новым cid. Сравнивайте code, а не messageкоды отказов - таблица ниже
command outcome unknown: do not send it again, ask the broker with order_state(cid) (Unknown)Команда ушла, ответа нет. Заявка могла встать, могла не дойтиТаймаут, обрыв после отправки, ошибка сервера 5xx, нечитаемый ответНе повторять. Спросить брокера: order_state(cid), при сомнении active_orders, executions, reconcile. Новую заявку - только когда брокер подтвердил, что первой нет, и с новым cidЕсли что-то пошло не так, Почему нет автоповтора
unsupported capability: <возможность> (Unsupported)Брокер или коннектор этого не умеет. Проверено без сетиНапример, replace у Финама, свечи 10 минут у БКС, поток стоп-заявок у всехЭто не сбой. Убрать вызов или проверить caps() заранее. Библиотека не подменяет отсутствующий метод опросомСобытия и возможности
not reconciled: call reconcile() before trading commands (NotReconciled)Шлюз команд закрыт: после запуска не было успешной сверки. Ничего не отправленоБот сразу ставит заявку, не вызвав reconcile, или сверка упала с ошибкойВызвать reconcile(&журнал) (пустой список, если журнала нет), потом повторить команду. cid годенКруг жизни, Запуск, рестарт и остановка
connector degraded: events may be lost, read the event channel to the end and call reconcile() (Degraded)Шлюз закрыт: переполнился буфер подписки без потерь или оборвался и открылся заново поток своих заявок или исполнений (Event::Reconnected), события о своих заявках могли пропасть. Ничего не отправлено. Верно и для команды, которая стояла в очереди по бумаге, когда шлюз закрылсяБот читает канал медленнее, чем идут события, или оборвалась связь с потоком заявокДочитать канал до пустого, прочитать состояние у брокера, вызвать reconcile, после успеха шлюз открыт. Потом сделать канал быстрееЕсли что-то пошло не так, Круг событий
shutting down: the connector takes no new trading commands (ShuttingDown)Идет остановка коннектора, шлюз закрыт навсегда. Ничего не отправленоВызван shutdown, команда пришла из другой задачиНичего не отправлять. Нужна торговля - запустить новый коннектор. Заявки на бирже остановка не снимаетЗапуск, рестарт и остановка

Коды Rejected

Коды ставит коннектор, они не общие для всех брокеров. Часть отказов библиотека возвращает до отправки (в сеть ничего не уходит), часть выводит из ответа брокера.

КодЧто значитКогдаЧто делать
duplicate_placeЭтот cid уже отправлялся (place, place_stop)Повтор после таймаута, повтор после отказа брокера. Единица квоты на этот вызов уже потрачена, в сеть ничего не ушлоНе повторять. Выяснить судьбу первой заявки, новая - с новым cid
invalid_qtyКоличество не подходит: нуль, отрицательное, дробное там, где брокер берет целые (БКС, Т-Банк), не целое число лотов (Т-Банк всегда, БКС и Финам если лот передан в price_grid())Проверка входа до отправки. Ничего не округляется, ничего не отправлено, cid годенИсправить количество. Оно в единицах инструмента, не в лотах (Деньги, цены и количества)
invalid_orderФинам: форма заявки неверна (например, не удалось собрать символ)Проверка входаИсправить заявку
missing_priceЛимитная заявка без цены (у Т-Банка еще и с ценой не больше нуля)Проверка входаУказать цену. cid годен
qty_requiredБКС: заявка частично исполнена, в replace нужно указать количествоreplace без qty на частично исполненнойУказать количество явно
already_filledБКС: заявка исполнилась раньше, чем дошла отменаГонка отмены и исполненияЧитать итог: order_state, executions
not_foundБрокер не знает такую заявку (cancel, replace, order_state) или инструмент. Один и тот же код у всех брокеров (константа brokapi::core::NOT_FOUND, проверка Error::is_not_found())Заявка не ставилась, уже закрыта, неизвестный инструмент, или запрос после обрыва еще в путиПосле Unknown не окончательно: подождать и спросить еще раз. См. recovery
REJECTEDТ-Банк: брокер принял запрос, но ответил статусом «отклонено»Решение биржи или брокера, причина в messageИсправить причину, новая заявка с новым cid
rejected, cancel_rejectedБКС: заявка отклонена в ответе на постановку, отмена отклоненаПричина в messageИсправить причину
validationБКС: брокер не принял форму запроса (HTTP 400, ошибки полей в message)Неверное поле в запросеИсправить запрос
subscription_quotaФинам: уже открыто максимум потоковСлишком много подписок на соединениеСнять ненужные подписки
unknown_subscriptionВсе брокеры: unsubscribe по номеру, которого нетПодписка уже снята или номер чужойНе снимать повторно
stop_orders_limitТ-Банк: по инструменту набрано максимум стоп-заявок (ответ 80007)place_stop по инструменту, где уже есть предельное число стоповСнять одну из стоп-заявок. Повтор не поможет. Заявки нет, cid годен
streams_limit, no_subscriptions, stream_lifetimeТ-Банк: открыто максимум потоков (80001), в потоке нет подписок (80004), поток прожил свой срок (80005)Ответ RESOURCE_EXHAUSTED с этими номерами. Это не лимит запросовЗакрыть ненужный поток, подписаться, подключиться заново
stop_orders_response_limitТ-Банк: стоп-заявок слишком много для одного ответа (30261), и чтение по периодам не помоглоactive_stop_orders при очень длинном спискеСнять ненужные стоп-заявки или читать период за периодом через raw() (Т-Банк)
range_too_longИстория сделок (executions) не помещается в то, что умеет читать коннектор: Т-Банк длиннее 20 страниц по 1000 операций, БКС список упирается в предел 1000 последних сделок, Финам больше 100 страниц или больше сделок с одной временной меткой, чем помещается в ответСлишком большой периодЧитать по частям: разбить период. Ничего из прочитанного не возвращается, чтобы обрезок не сошел за всю историю
unsupported_expiry, missing_limit_price, invalid_price, unsupported_limit_price, unknown_stop_orderСтоп-заявкиФорма запроса или неизвестный стопСтоп-заявки

События и состояния, которые не являются ошибкой вызова

СостояниеЧто значитПочему бываетЧто делатьПодробнее
Event::Overflow { sub, lost_from }Буфер Lossless-подписки заполнился, события начиная с lost_from могли быть потеряны. Приходит один раз на подписку. Следом шлюз закрывается (Degraded)Бот отстает от потока своих заявок и сделокДочитать канал, reconcile, восстановить пропущенное из снимков. Для рыночной подписки: снять и оформить зановоЕсли что-то пошло не так
Event::Reconnected { sub, gap_from }Поток своих заявок или своих исполнений оборвался и открыт снова, за время обрыва могли пройти события, которых бот не видел. Следом шлюз закрывается (Degraded)Обрыв связи с потоком заявок или исполненийДочитать канал, прочитать состояние у брокера (active_orders, order_state, executions с запасом раньше gap_from), вызвать reconcileКруг событий, Если что-то пошло не так
Event::Resync { sub, instrument, reason }Стакан мог потерять данные, прежнему верить нельзяSubscriptionReset: поток переоткрыт после обрыва. SequenceGap: брокер пропустил данные (Т-Банк, Финам)Выбросить хранимый стакан, не торговать по нему, дождаться следующего Event::OrderBookКруг событий
OrderState::UnknownСудьба заявки неизвестна. Из него библиотека сама не выходитТаймаут или обрыв на команде, неизвестный статус Т-Банка, у БКС статус 6 «отменяется» (заявка еще жива)Запросить состояние (order_state) или сделать сверкуКруг команд, recovery
Список foreign в отчете сверкиНа счете есть заявки, которых нет в журнале ботаПоставлены руками, из приложения или другим роботомНе трогать. Показать человекуКруг жизни
Список closed_offline в отчете сверкиЗаявка из журнала не найдена среди живых у брокераИсполнилась, снята или не была принята, пока бот был выключенУзнать итог: order_state, executions, занести в журналКруг жизни
reconcile вернул ошибкуСверка не состоялась, шлюз не открытНет связи, лимит, у БКС ошибка или пустой ответ по одному из cid (ответ «брокер не знает номер» ошибкой не считается, это closed_offline)Подождать, вызвать еще раз. Ошибка ничего не говорит о заявкахКруг жизни
События пропали, ошибок нетПоток оборван, идет переподключениеОбрыв связиПодождать. Когда поток откроется, придет Reconnected (заявки и исполнения) или Resync (стакан). У Финама смотреть sub_status. При сомнении reconcileКруг событий
Подписка не подтверждаетсяsubscribe_* вернул transport error: the subscription was not confirmed by the broker within <время> (Transport), либо Rejected { subscription_refused }Transport: поток не открылся за subscribe_ack_timeout (сеть, медленный брокер, обрыв). Rejected: брокер отказал (нет такого инструмента, нет прав). Текст ошибки содержит причину. У Финама по таймауту ошибки нет, вызов возвращает Ok и поток дооткрывается в фоне (sub_status)Transport: подписка уже снята, повторить позже. Rejected: исправить запрос, повтор ничего не изменитКруг событий

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

brokapi by RunInfra