Торговый агент нашёл маршрут, оценил затраты и получил положительный результат проверки. Через несколько десятков миллисекунд он готов передать заявку дальше. Цена на экране прежняя. Расчётная маржа тоже. Но доступного объёма уже недостаточно.
Что именно разрешает первоначальный результат: действие в наблюдавшихся условиях или любое последующее действие с похожими параметрами?
Мы разобрали этот вопрос на небольшом открытом примере RESONANCE Verify. Все цены, состояния и времена заданы синтетически. Пример проверяет логику допуска в памяти; к бирже он не подключается и ордеров не создаёт. Его можно воспроизвести из зафиксированного исходного кода.
Одна заявка, два состояния
Условный маршрут начинается с 1 000 USDT и проходит через BTC и ETH обратно в USDT. Модель учитывает по 5 базисных пунктов комиссии и проскальзывания на каждом шаге. Порог допуска — 30 базисных пунктов после этих затрат.
Первая проверка проходит: расчётная маржа составляет около +94,66 базисного пункта. В терминах движка это EXECUTE_SIM — допуск исключительно в симуляции.
На первом шаге цена продажи BTC в стакане равна 80 000 USDT за BTC. Изначально по этой цене доступно 2 BTC, то есть ёмкость первого уровня составляет 160 000 USDT. Затем объём уменьшается до 0,0075 BTC. Ёмкость становится 600 USDT — меньше требуемых 1 000.
| Момент фикстуры | Наблюдаемое состояние | Результат |
|---|---|---|
| 1 000 мс: первичная проверка | Ёмкость первого шага 160 000 USDT | EXECUTE_SIM |
| 1 075 мс: следующий снимок | Та же цена; ёмкость уже 600 USDT | Новые входные данные |
| 1 080 мс: повторная проверка | Требуется 1 000 USDT; доступно 600 | REJECT, CAPACITY_EXCEEDED:0 |
Эти 80 мс — заданный интервал сценария, а не измеренная задержка реальной торговой системы. Возраст нового снимка в момент решения составляет 5 мс при разрешённом максимуме 100 мс.
Свежесть не заменяет ёмкость
В основном сценарии цена и модель затрат сохранены. Расчётная маржа по-прежнему положительна. Снимок проходит проверку возраста. Меняется доступный объём первого шага — и именно нехватка ёмкости становится причиной отказа.
Это делает результат проверяемым: мы можем отделить влияние объёма от влияния цены и времени. Правило свежести само по себе этот случай не остановит, потому что новый снимок действительно свежий.
При повторном использовании старого разрешения пример записывает допуск в памяти. При новой проверке движок строит маршрут из текущих снимков и возвращает CAPACITY_EXCEEDED:0. Допуск снимается. Никакого фактического вызова отправки на биржу в обоих вариантах нет.
Разрешение сохраняет смысл вместе с условиями, на которых оно было получено.
Контроль должен уметь пропускать
Пример, который отвергает всё подряд, мало говорит о полезности проверки. Поэтому рядом с изменившимся стаканом есть положительный контроль: свежий снимок без изменений сохраняет EXECUTE_SIM.
В полной фикстуре семь вариантов. Пять заранее выбранных неблагоприятных состояний снимают допуск. Седьмой вариант показывает предел подхода.
| Вариант | Итоговый вердикт | Допуск в памяти |
|---|---|---|
| Свежий стакан без изменений | EXECUTE_SIM | Да |
| Ёмкость первого шага падает до 600 USDT | REJECT | Нет |
| Изменение цены убирает положительную расчётную маржу | REJECT | Нет |
| Расчётная маржа положительна, но ниже порога | OBSERVE | Нет |
Статус торгов в фикстуре меняется на HALTED | REJECT | Нет |
| Возраст снимка превышает лимит | REJECT | Нет |
| Глубина меняется после последней проверки | EXECUTE_SIM | Да; поздняя диагностика даёт REJECT |
Проверки ёмкости, цены, затрат и возраста выполняет существующий движок. Условие HALTED добавлено отдельной обёрткой именно для этой фикстуры: основной evaluate_route не принимает статус биржи. Отчёт сохраняет оба уровня решения раздельно.
OBSERVE также не даёт допуска. Повторная проверка не может превратить более строгое первоначальное решение в разрешение: исходные REJECT или OBSERVE не превращаются в EXECUTE_SIM из-за последующего улучшения снимка.
Ещё двадцать миллисекунд
В последнем варианте повторная проверка видит достаточный объём и сохраняет допуск в момент 1 080 мс. Затем объём исчезает. В момент условного прибытия, 1 100 мс, та же модель уже возвращает REJECT.
Это ожидаемый незакрытый случай. Решение не использует будущие данные. Поздний снимок анализируется только после него, как диагностика. Тест отдельно проверяет: изменение данных прибытия не меняет более ранний вердикт.
Повторная проверка работает с доступным наблюдением. Она не резервирует ликвидность и не связывает своё решение атомарно с состоянием биржи. Незамеченное изменение возможно и между последним снимком и самой проверкой.
Практический вывод для интеграции: нужно отдельно описать, какие ограничения исполнитель способен проверить при приёме заявки и каким наблюдаемым результатом он завершает действие. Свежий локальный снимок не отвечает на эти вопросы автоматически.
Что именно удалось подтвердить
В проверенной версии прошли 12 тестов демо. Они проверяют воспроизведение, точные причины решений, границу возраста снимка, единицы ёмкости на втором шаге, запрет использования будущего снимка, сохранение более строгого первоначального решения и обнаружение изменения отчёта.
Повторный запуск воспроизводит полный JSON-результат с тем же SHA-256. Отчёт связывает фикстуру, правила, параметры затрат, состояния и байты семи модулей реализации. Это позволяет заметить изменение входов или кода.
Пять остановленных синтетических вариантов не дают статистической оценки качества обнаружения. Двенадцать пройденных тестов не подтверждают поведение реальной биржи. Хеши удостоверяют совпадение данных, а не подлинность рыночных наблюдений.
Статус результата — UNASSESSED_REPLAY_SOURCE. Модель — NONE_ADMISSION_ONLY: проверяется допуск в памяти. Здесь нет исполнения по нескольким уровням стакана, позиции в очереди, частичных исполнений, реальной прибыли или измеренной экономии средств. Системы Quantilan не тестировались; публикация опирается на открытый синтетический пример и не заявляет одобрения со стороны команды.
Воспроизвести результат
Нужен Python 3.11 или новее. Проверка выполнена на Linux с Python 3.12.13. Установка Python-пакетов и доступ к сети во время самого прогона не требуются.
Получите репозиторий и выберите точную версию демо:
git clone https://github.com/safal207/resonance-arbitrage-graph.git
cd resonance-arbitrage-graph
git checkout d6317c30833fed7d19edbcaf4f9f7467faa58519
Выполните проверку сохранённого отчёта:
PYTHONPATH=src python3 -m resonance_arbitrage_graph.exchange_state_demo \
--fixture examples/quantilan/fixture.json \
--check examples/quantilan/expected-report.json
Ожидаемая строка:
Replay matches: 313084ae4464b057423496f800225015a4a2b565a0ae81be748aed02c0f5bad0
Для запуска 12 тестов:
PYTHONPATH=src python3 -m unittest discover \
-s tests -p test_exchange_state_demo.py -v
Следующий вопрос к реальному процессу
Демо даёт небольшую, конкретную отправную точку для обсуждения интеграции: какой снимок состояния доступен непосредственно перед допуском и какие ограничения остаются проверяемыми в момент приёма заявки исполнителем?
Следующий полезный эксперимент — один публичный или синтетический пример от другой команды с явно определёнными полями состояния и временными метками. По нему можно сравнить ожидаемый отказ, фактическую причину и воспроизводимость результата. Ключи доступа и закрытая стратегия для этого не нужны.
Исторически корректное решение становится пригодным для следующего действия только в пределах условий, которые система действительно умеет проверять.
Источники и граница проверки
- Описание демо и его ограничений.
- Синтетическая фикстура.
- Полный ожидаемый отчёт.
- Модуль сравнения решений.
- Двенадцать тестов.
- Основной движок проверки маршрута.
Автор: Алексей Сафонов. Проверка исходных файлов и локальное воспроизведение: 5 сентября 2026 года. Результат относится к указанному коммиту и этой фикстуре; независимый внешний прогон в данной публикации не заявляется.
Исходник и доказательства