Skip to content

Harvester v1 — план до рабочего, протестированного, единого бота ​

Цель: ОДИН бот, доведённый до измеримого вердикта о доходности Harvester, с конечным объёмом работ и видимым концом. ОГОВОРКА 2026-08-21: слова о бэктестах в этом плане недействительны — офлайн-бэктест снят решением оператора, проверка идёт на живых сессиях и телеметрии. Это документ-план-of-record. История правок — в git/memory, не здесь (SSOT-правило).

Статус на 2026-08-22.

  • Переход pm-bot → pm-engine завершён. Крейты pm-bot / pm-dashboard удалены; проверенный live-срез (order-клиент, redeem, closure, orderbook, order trace) перенесён в pm-engine. Пункт 2 «Ратифицированного решения» ниже предполагал сохранить pm-bot донором — этого не произошло, донор удалён.
  • Ф1–Ф3 пройдены, Ф4 идёт: движок торгует РЕАЛЬНЫМИ деньгами. Инстанс harvester-engine работает в единственном live execution path; налитые ордера, открытые позиции и закрытия по цели подтверждены в боевой engine.db. Ограничение капитала — directional.total_budget_micro_usd в config.db.
  • A/B на двух инстансах (пункт 4 решения) НЕ выполняется: боевой инстанс один. Требование «сверено на ≥2 инстансах» из финишной черты остаётся невыполненным.
  • Финишная черта не достигнута. Вердикт edge_margin > 0 с нижней границей кластерного доверительного интервала выше нуля на ≥238 кластерах «город-дата» не собран: форвард-данных такого объёма нет, а каждый выкат обнуляет форвардное окно.

Наблюдаемость — что вердикт стало чем измерять (обновление 22.08).

  • Вопросы ОТБОРА отвечаются на боевых данных уже сейчас. Воронка входа ведётся в хранилище: за наблюдаемый период 9.3 млн оценок, и видно поимённо, где рынки отсеиваются (остановка входа 4.4 млн, фильтр исхода 2.4 млн, ценовой коридор 1.3 млн, расстояние до фаворита 873 тыс.). Наполнены десять таблиц: попытки входа с формой книги, кадры стоп-решений, витрины заявок и позиций, переходы, разрывы доставки.
  • Вопросы ИСПОЛНЕНИЯ до выката не отвечаются. Механизмы написаны и проверены, но в бою их нет: причина снятия заявки (все отмены в бою без причины), принадлежность несостоявшегося входа событию, история отбора целиком, срабатывания защит отдельным фактом, здоровье лент во времени.
  • ★ГЛАВНОЕ ОГРАНИЧЕНИЕ ПОСЛЕ ВЫКАТА — ЧИТАТЕЛЬ, А НЕ ДАННЫЕ.scripts/analysis/forensics.sh ходит в SQLite движка по SSH, а не в хранилище. Данные будут, ответов на торговые вопросы поверх них не будет: только ручной SQL. Перевод форензики — самая ценная работа после выката.
  • Эпоха как средство против обнуления форвардного окна. Каждая строка наблюдения несёт идентичность эпохи (версия конфигурации + счётчик сбросов). Это НЕ отменяет риск «forward-clock reset trap» из раздела «Риски» — правки config.db по-прежнему рвут окно, — но делает разрыв ВИДИМЫМ в данных, а сравнение разных эпох — явной ошибкой, а не незаметной.
  • Выборка для вердикта пока мала: в витрине позиций шесть строк. Ни один вывод о доходности на таком объёме не обоснован.

Ратифицированное решение (2026-06-24) ​

  1. Единый продукт v1 = pm-engine (архитектура + стратегия Harvester) — он же будущий хребет под другие стратегии. Bake-off закрывается слиянием в один бот, не вечным сравнением.
  2. pm-bot останавливается и отключается на стенде. Double-spend исчезает, кошелёк свободен для pm-engine. pm-bot остаётся как донор проверенного live-кода (order-клиент, redeem, RTDS) для будущего слияния — не удаляется.
  3. Сначала ответ про деньги, потом слияние. Вердикт «зарабатывает ли Harvester» НЕ требует слияния F1–F10. Сначала доводим pm-engine до доверенного EV-вердикта минимальной подготовкой; полное слияние live-кода (F1–F10) — ТОЛЬКО после +EV.
  4. Вердикт собираем на pm-engine + pm-engine-2 (A/B-инстанс). Так вердикт меряет именно того бота, которого запустим, и переносится корректно (у pm-bot и pm-engine разные модели филла — вердикт pm-bot на pm-engine не переносится).
  5. Стоп v1 = hold-to-resolution / FOK-kill-and-hold. На стоп не опираемся (режет будущих победителей через mean-reversion). Отсутствие RTDS в engine → НЕ блокер v1. Обратимо (config.db), пересматриваем на финальном вердикте.

Финишная черта (измеримая, не «на глаз») ​

Harvester «готов и зарабатывает» = чистый +EV-вердикт по эталонной модели harvester_yield (crates/pm-analytics/src/harvester_yield.rs; формула — docs/strategies/harvester-yield.md; docs/Harvester Yield Calculator v2.pdf — исторический файл, не SSOT):

edge_margin = w − w* > 0, где w* = (p_e − r·s_eff)/(exit_price − r·s_eff), при всех условиях:

  1. Нижняя граница bootstrap-доверительного интервала > 0 на ≥238 кластерах «город-дата» (наивный счёт по рынкам завышает в ~9×: одно показание температуры решает целую лестницу).
  2. Garbage-in исключён: w/p_e из fill-checked realised-исходов; открытые проигравшие резолвить (никакого survivorship); r/s_eff из реальных стоп-филлов; никакого ride-to-resolution counterfactual.
  3. p_e = фактическая цена исполненного BUY, связанная с decision-time best_ask в live-телеметрии и order trace, НЕ Gamma-mid. Край ~2¢ внутри спреда ~10¢ — это убийца, не сноска.
  4. exit_price = 0.99 (maker-harvest), НЕ 1.0 (redeem@1.0 — UMA-риск, взаимоисключим).
  5. Минус комиссии (сейчас 0, несущие) и проскальзывание; устойчиво к ~2–3% ошибок UMA-резолюции.
  6. Замерено форвардом на замороженном бинаре, сверено на ≥2 инстансах (pm-engine + pm-engine-2) под объявленным config-freeze; REFUSE-VERDICT при n < min.

Финиш определяется входным краем + охватом, НЕ эффективностью стопа.

Откуда берётся край (замер 2026-08-12, живые стаканы CLOB) ​

Источник края — надбавка рынка: сумма лучших предложений «да» по всем корзинам события превышает единицу. Замер по четырём событиям погодной лестницы (11 корзин каждое) дал ΣAsk = 1.096…1.107, то есть надбавку +9.6…+10.7 %.

Практическое следствие, определяющее конструкцию входа:

  • покупка всех «нет» пассивно (по своей цене, entry_price_base=BestBid) — примерно +1.0 % на вложенное за цикл;
  • покупка тех же «нет» агрессивно (по чужой цене) — примерно −0.2 %.

Край целиком лежит в спреде. Отсюда два ограничения, которые нельзя обойти тюнингом: край забирается только полным набором корзин события (неполный набор теряет арифметическую определённость «одна проигрывает, остальные выигрывают»), и только той частью, которая переживает отбор при наливе — пассивную заявку исполняют тогда, когда кто-то хочет продать, то есть систематически перед движением против нас.

Границы замера: одна временная точка, четыре события, оценка вероятностей — середина стакана с нормировкой к единице. Это обоснование конструкции, НЕ вердикт о доходности; вердикт по-прежнему требует условий финишной черты выше.

Честная правда (не обещание прибыли) ​

  • Утверждение «оба бота реальными деньгами не торговали ни разу» устарело и снято. pm-engine исполняет сделки за реальные деньги; боевой путь подтверждён налитыми ордерами и закрытиями по цели выхода. Runtime-симуляция филлов и --dry-run удалены 2026-08-22.
  • Переход к реальным деньгам сам по себе вердиктом не является. Он снял риск «симфиллы ≠ реальные филлы», но не заменил требуемый объём наблюдений.
  • План ведёт к решаемому вердикту, который может быть NO-GO. Обещается не прибыль, а конец неопределённости.

Bug-hunt 2026-06-24 (verdict-tooling): −EV оказался багом измерения ​

Первый прогон forensics.sh ev --engine дал edge=−0.244 (катастрофа). Это баг измерения, подтверждён через Gamma ground-truth по всем 174 позициям:

  • forensics ev взял дефолт r=0/s_eff=0, хотя в данных 60 реальных стопов (s_eff=0.587, r=0.857), и посчитал стоп-выходы как полные resolution-убытки.
  • engine.db расходится с Gamma на 34 позиции — стоп-выходы, что выиграли бы при hold-to-resolution, но записаны убытками. Стоп срезал 34 победителя.
  • p_e ЧИСТ: записанный entry == фактический BUY-филл == limit, бит-в-бит.
  • Истинный hold-to-resolution: w=138/174=0.793 (не 0.598); edge≈−0.04 (exit 1.0); 95% ДИ w=[0.733,0.853] включает break-even → статистически неотличимо от +EV на n=174.

Следствия: (1) −EV красный флаг снят — это была контаминация стопом, не стратегия. (2) Hold-to-resolution валидирован эмпирически — стоп строго −EV. (3) Остаточные −0.04 — шум + вероятно entry-timing (falling-knife: 34% входов сразу падали к 0.59) → драйвер для fix-directional-admission-pipeline (Ф2). (4) forensics ev дефолт r=0 на стоп-данных — footgun; для hold-to-resolution вердикта стоп-позиции ре-резолвить к terminal-исходу.

Историческая последовательность до отказа от симуляторов ​

Раздел до «Ближайшего порядка работ» сохранён как история решения и отменён 2026-08-22. dry-run, Backtest, replay, capture и симуляционный A/B больше не являются фазами проекта или доказательством. Текущий путь — неизменяемые когорты реальных сессий, factual order trace, подтверждённые fill/settlement и эталонная EV-модель.

Главный вопрос — стратегический (входной край + охват), а слияние F1–F10 — это водопровод исполнения и расширяемость под будущие стратегии. Ставить ~2 месяца слияния перед ответом «зарабатывает ли стратегия» — риск рефакторить архитектуру под убыток. Поэтому: дешёвый стратегический ответ first; дорогое слияние — только если +EV. Дополнительный выигрыш: dry-run-сбор не рискует капиталом — если стратегия не +EV даже в симуляции, останавливаемся, не потратив ни доллара.

Исторические фазы (отменены) ​

Ф0  Ратификация + этот план + ре-бакетинг openspec        1–2 дня   [сделано]
    выход: решение зафиксировано; F1–F10 → отложено за вердикт; прочее → extension-later
Ф1  pm-engine backtest-готов (dry-run A/B)                 ~1 неделя   [сделано, КРОМЕ A/B]
    [VERIFIED 2026-06-24] живой harvester-engine ТОРГУЕТ: 333 FILLED ордера, 174 позиции,
    свежие directional-entry + stoploss-confirmed → engine-не-торгует ОПРОВЕРГНУТО.
    gamma empty-cid (~3.5%) — benign, корректно пропускается, НЕ блокер.
    - поднять pm-engine-2 (второй dry-run инстанс для A/B)
    - подтвердить НАТИВНЫЙ backtest движка на ЕГО captures (EngineReplayDriver / --capture-only);
      cross-bot replay-compare фиделити-баг (engine=0 решений через мост pm-bot→engine) ОТЛОЖЕН
      как инструмент ретайред-bake-off, не на критическом пути
    - базовая безопасность капитала (secrets/auth, budget single-source) — старт
    выход: pm-engine + pm-engine-2 крутят Harvester в dry-run; нативный backtest даёт результат
    [кошелёк pm-engine + pm-bot off → перенесено в Ф4: dry-run не делает реальных ордеров,
     double-spend невозможен до реальных денег]
Ф2  Доверенный бэктест: sim↔live паритет + maker-вход      ~2 недели   [maker-вход сделан]
    - sim partial-fill ≥5-или-0; бэктест и live мерят ОДНУ модель филла
    - вход maker (post-only); честный admission (без silent ZERO→LowProb, stale-Gamma)
    выход: dry-run pm-engine = доверенная модель; replay даёт EV без фантомных филлов
Ф3  Config-freeze + форвард-сбор → ПРЕДВАРИТ. вердикт  ≥1–2 недели календарных, БЕЗ рестартов
    [НЕ ВЫПОЛНЯЕТСЯ: config-freeze не объявлялся, выкаты и правки config.db идут постоянно,
     форвардное окно обнуляется каждым из них. Это реализовавшийся риск «forward-clock reset
     trap» из раздела «Риски» — пока он не закрыт дисциплиной заморозки, вердикт недостижим
     независимо от качества стратегии.]
    - pm-engine + pm-engine-2 без рестартов; входы fill-checked, без survivorship
    выход: harvester_yield на форвард-данных; предварит. знак EV (на симфиллах, без капитала)
Ф4  Проба реальными деньгами → ФИНАЛ                       2–4 дня (потолок твой)   [ИДЁТ]
    [Начата БЕЗ предварительного +EV-вердикта Ф3 — условие «ЕСЛИ предв. +EV» не выполнялось.
     Решение оператора; зафиксировано здесь, чтобы последовательность не читалась как пройденная.
     Реальные BUY и SELL исполнены, симфиллы против реальных филлов больше не гипотеза.]
    - ТОЛЬКО ЗДЕСЬ: pm-bot off + кошелёк → pm-engine (real-money устраняет double-spend)
    - перед пробой решить: перенести проверенный order-клиент pm-bot (тонкий срез) vs
      capped native — выбираем по состоянию live-пути pm-engine после Ф2
    - ≥1 реальный BUY+SELL FILLED подтверждает: симфиллы ≈ реальные → вердикт переносится
    выход: финальный вердикт. CI-нижняя > 0 → v1 ЗАРАБАТЫВАЕТ. Иначе → тюнинг полосы (config.db) / стоп
    ЕСЛИ предв. NO-GO → крутим полосу входа (config.db) и пересбор, либо стоп. Капитал не тронут.
─────────────────────────────────────────────────────────────────────────────────
Ф5+ ТОЛЬКО ЕСЛИ +EV: слияние F1–F10 — engine как робастный хребет          3–5 недель
    перенос проверенного live-кода pm-bot (order/redeem/positions/closure/orderbook/sim)
    через shared-порты; убрать дублирование I/O. Фундамент под «добавлять другие стратегии».

Реалистично до go/no-go: ~4–6 недель (чуть больше «быстрой дороги через pm-bot» из-за gamma-фикса и sim-паритета pm-engine, но радикально меньше ~9–11 недель «сначала слияние»). Слияние F1–F10 — отдельные ~3–5 недель после подтверждённого +EV.

Ближайший порядок работ (обновлено 22.08) ​

Не фаза плана, а очередь на сегодня — то, что стоит между текущим состоянием и возможностью что-либо утверждать о доходности.

  1. Выкат наблюдаемости. Всё перечисленное в статусе как «написано, но в бою нет» доезжает одним выкатом. До него пять живых приёмок заявки analytics-fidelity-and-table-inventory закрыть нечем — а без них заявка не считается выполненной: три из пяти дефектов предыдущей работы вскрылись именно на живом прогоне, а не на тестах.
  2. Перевод форензики на хранилище (analytics-telemetry-to-clickhouse §9). Без него выкат даёт данные, но не ответы. Самая ценная работа очереди.
  3. Дисциплина заморозки конфигурации для набора форвардного окна. Пока правки config.db идут в произвольный момент, вердикт недостижим независимо от качества стратегии и полноты наблюдения — это по-прежнему главный риск.

Отложено ​

  • F1–F10 полное слияние — за вердикт (Ф5+, только при +EV).
  • flow-imbalance-стоп, нативная пересборка engine без переноса, все стратегии кроме directional/Harvester, оставшиеся pm-engine-only hardening не на пути к вердикту.
  • Поверхность чтения для анализа (analytics-mcp-read-surface) — заявка есть, сделано 5 задач из 43. Её ценность появляется только после пункта 2 очереди выше: читать нечего, пока читатель не переведён.

Риски (держать в голове на каждой фазе) ​

  • Только live-телеметрия доказывает поведение на бирже: order trace, подтверждённые fills, settlement и денежная сверка обязательны; тестовые doubles доказывают лишь контракт кода.
  • Край тонкий и хрупкий: ~2¢ в спреде ~10¢; taker-комиссия / ошибка UMA / замер по Gamma-mid вместо исполнимого ask — любой переворачивает знак.
  • Статистический мираж: без кластер-bootstrap «+EV» вероятно шум.
  • Forward-clock reset trap: каждый деплой обнуляет форвард-данные. Без дисциплины config-freeze (Ф3) проект зацикливается навсегда.
  • pm-engine к вердикту — больше подготовки, чем pm-bot: gamma-гидрация сломана, live-путь не торговал, история stop-disabled→−EV. Ф1–Ф2 это закрывают, но это реальная работа.
  • Net-negative baseline: возможно, стратегия НЕ +EV при исполнимых ценах. План обязан уметь вернуть NO-GO.

Решения оператора, отложенные до своих фаз (config.db, твой выбор тогда) ​

  • Потолок реальных денег для пробы Ф4 — РЕШЕНО, живёт в directional.total_budget_micro_usd (config.db). Прежняя авторизация $20 отменена; действующее значение смотреть в боевой config.db, а не здесь (RULE-NEW-01: единственный источник — config.db).
  • Длина окна config-freeze (Ф3) — ОТКРЫТО и блокирует вердикт. Задаёт календарную дату вердикта и n. Пока не объявлено, Ф3 не может начаться.
  • Полоса входа v1 — РЕШЕНО для текущего эксперимента, действующие границы — в config.db (entry_price_band_lower_milli / max_entry_price_milli, сторона входа entry_outcome_filter). Историческая оценка [0.80,0.85] относилась к другой стороне и другому набору ручек; сравнивать с текущими значениями напрямую нельзя.