Тема
Uncertainty:0.09
ИСТОРИЧЕСКИЙ ДОКУМЕНТ. Это прежняя инструкция аудита коллектора для Backtest. После отказа от replay, runtime simulation и standalone capture 2026-08-22 она не является текущей спецификацией или процедурой проверки. Реальная торговая телеметрия, включая полную трассировку ордеров, описана в
docs/target/observability.mdиopenspec/specs/.
Ниже — рабочая инструкция аудита Rust-системы сбора истории Polymarket из WS / RTDS / CLOB / REST для дальнейшего backtest.
Документ рассчитан на то, что аудитор имеет доступ к репозиторию, .env.example, конфигам, тестовой БД, логам и может запускать код локально.
Технический аудит Polymarket History Collector на Rust
0. Цель аудита
Проверить, что система:
Корректно получает данные из всех заявленных источников:
- Polymarket WebSocket market/user/sports channels;
- RTDS;
- CLOB REST;
- Gamma/Data API, если используются как reference/source-of-truth.
Корректно нормализует данные:
- market id;
- condition id;
- token id;
- outcome;
- side;
- price;
- size;
- timestamp;
- sequence/update id, если есть;
- source;
- latency metadata.
Корректно пишет историю:
- без потерь;
- без неконтролируемых дублей;
- с воспроизводимым порядком событий;
- с возможностью построить backtest без look-ahead bias.
Корректно восстанавливается после:
- reconnect;
- пропуска сообщений;
- rate limiting;
- schema drift;
- деградации одного из источников;
- частичного отказа БД.
Имеет доказуемое покрытие тестами, property-тестами, интеграционными тестами и reconciliation-проверками.
1. Важные внешние предположения
Перед аудитом код сверяется с актуальной документацией Polymarket.
На момент проверки Polymarket документирует WebSocket-каналы для near-real-time orderbook/trade/user activity, включая market, user, sports и RTDS. RTDS используется для streaming comments, crypto/equity prices и работает через wss://ws-live-data.polymarket.com; часть user-specific потоков может требовать gamma_auth. (docs.polymarket.com)
Gamma API, Data API и read-only CLOB endpoints публичные и не требуют authentication; CLOB trading endpoints требуют authentication. Для исторического сборщика это важно: read-only market-data paths и authenticated trading paths не должны смешиваться в collector runtime. (docs.polymarket.com)
CLOB у Polymarket — hybrid-decentralized: off-chain order matching и on-chain settlement. Это означает, что аудит должен отдельно проверять off-chain market data, on-chain activity/settlement и их reconciliation, а не считать один источник полностью достаточным. (docs.polymarket.com)
Для market discovery официальная документация рекомендует использовать Gamma events/markets, включая active=true&closed=false, pagination через limit/offset, а также tag filtering. CLOB предоставляет read endpoints для price, prices, book, books, midpoint, spread и prices-history. (docs.polymarket.com)
API rate limits у Polymarket проходят через Cloudflare throttling, то есть превышение лимитов может проявляться как задержка/очередь, а не только как явный отказ. Аудит должен проверять latency, timeout, retry и backpressure, а не только HTTP status. (docs.polymarket.com)
Отдельно учесть, что в changelog указано: CLOB V2 live on production с 2026-04-28. Аудит должен проверить, что код не завязан на устаревшие V1-форматы, endpoints, SDK semantics или несовместимые assumptions. (docs.polymarket.com)
2. Цикл аудита
Аудит проводится по одинаковому циклу для каждого компонента:
text
1. Найти релевантный модуль.
2. Сформулировать expected behavior.
3. Найти фактическое поведение в коде.
4. Проверить тестами / логами / synthetic input.
5. Сравнить с внешней документацией и внутренними invariants.
6. Зафиксировать проблему, если поведение не доказано.
7. Предложить минимальный план исправления.
8. Добавить regression test proposal.Запрещено считать компонент корректным только потому, что "код выглядит правильно". Корректность должна быть подтверждена одним из способов:
text
- unit test;
- integration test;
- replay test;
- property-based test;
- deterministic fixture;
- reconciliation query;
- production log sample;
- database invariant query;
- explicit proof from code path.3. Обязательный формат отчёта по каждой найденной проблеме
Каждая проблема должна быть оформлена так:
markdown
## ISSUE-NNN: <краткое название>
Severity: Critical | High | Medium | Low
Component: ws | rtds | clob | discovery | normalizer | db | backtest | config | observability | ops
Status: Confirmed | Suspected | Needs reproduction
### Summary
Краткое описание проблемы.
### Evidence
- Файл:
- Функция:
- Строки:
- Лог / тест / SQL / fixture:
- Как воспроизвести:
### Expected behavior
Что должно происходить.
### Actual behavior
Что происходит сейчас.
### Impact
Чем это опасно для backtest / PnL-модели / completeness / correctness.
### Root cause hypothesis
Вероятная причина.
### Fix plan
1.
2.
3.
### Regression test
Какой тест должен гарантировать, что проблема не вернётся.
### Backfill / data repair required
Yes | No | Unknown
### Confidence
0.00–1.004. Severity model
Critical
Проблема делает историю непригодной для backtest или создаёт скрытый look-ahead bias.
Примеры:
text
- потеря WS-сообщений без обнаружения;
- неправильный timestamp, из-за которого события оказываются в будущем/прошлом;
- перепутаны token_id/outcome;
- price/size пишутся с неправильной точностью;
- reconnect делает silent gap;
- snapshot применён после delta без ordering check;
- CLOB book собирается неконсистентно;
- дедупликация удаляет реальные события;
- backtest использует данные, которые на тот момент ещё не были доступны.High
Проблема искажает PnL, liquidity, fill simulation или eligibility рынков.
text
- нет надёжного reconciliation между WS и REST snapshot;
- market discovery пропускает активные рынки;
- rate limit приводит к silent stale state;
- не хранится source timestamp;
- нет versioning схемы;
- replay недетерминированный.Medium
Проблема снижает надёжность, но не всегда портит данные.
text
- недостаточные метрики;
- слабие retry limits;
- нет алерта на reconnect storm;
- не покрыты edge cases;
- нет fixtures для schema drift.Low
Качество, читаемость, minor observability.
5. Карта компонентов для аудита
Аудит начинается с построения карты репозитория.
5.1 Найти entrypoints
Команды:
bash
find . -maxdepth 3 -type f \( -name "main.rs" -o -name "lib.rs" -o -name "mod.rs" \)
find . -maxdepth 4 -type f \( -name "*.rs" -o -name "*.toml" -o -name "*.sql" -o -name "*.yaml" -o -name "*.yml" -o -name ".env.example" \)Составить таблицу:
markdown
| Component | Path | Responsibility | External deps | Writes DB | Reads network | Test coverage |
|---|---|---|---|---|---|---|5.2 Найти все network clients
Искать:
bash
rg -n "reqwest|tokio_tungstenite|tungstenite|websocket|wss://|https://|gamma|clob|polymarket|rtds|ws-live-data|prices-history|book|books|midpoint|spread"Проверить:
text
- где создаётся HTTP client;
- где создаётся WS client;
- есть ли timeout;
- есть ли retry;
- есть ли backoff;
- есть ли rate limit;
- есть ли circuit breaker;
- есть ли per-source metrics;
- есть ли request id / correlation id;
- логируется ли endpoint без секретов;
- не захардкожены ли production/staging endpoints.5.3 Найти все database writes
Искать:
bash
rg -n "INSERT|UPDATE|UPSERT|ON CONFLICT|COPY|execute|query|sqlx|diesel|sea_orm|postgres|sqlite|clickhouse|timescale"Проверить:
text
- какие таблицы пишутся;
- какие уникальные ключи;
- какие индексы;
- как работает idempotency;
- есть ли transaction boundary;
- есть ли batch insert;
- что происходит при partial failure;
- как обрабатываются duplicate events;
- как пишутся timestamps;
- есть ли raw payload archive.6. Аудит конфигурации
6.1 .env / config schema
Проверить наличие и корректность:
text
POLYMARKET_CLOB_BASE_URL
POLYMARKET_GAMMA_BASE_URL
POLYMARKET_DATA_BASE_URL
POLYMARKET_WS_URL
POLYMARKET_RTDS_WS_URL
DATABASE_URL
RUST_LOG
COLLECTOR_MARKET_FILTERS
COLLECTOR_TAG_FILTERS
COLLECTOR_RECONNECT_MIN_MS
COLLECTOR_RECONNECT_MAX_MS
COLLECTOR_HTTP_TIMEOUT_MS
COLLECTOR_WS_PING_INTERVAL_MS
COLLECTOR_SNAPSHOT_INTERVAL_MS
COLLECTOR_BACKFILL_ENABLEDЕсли используются другие имена — описать фактические.
6.2 Проверки
text
[ ] Нет обязательных config values без validation. R-CFG-REQUIRED-VALIDATED
[ ] Нет silently default-to-production поведения. R-CFG-NO-SILENT-DEFAULT-PROD
[ ] Нет секретов в логах. R-CFG-NO-SECRETS-IN-LOGS
[ ] Можно явно выбрать staging/prod. R-CFG-EXPLICIT-ENV-SELECT
[ ] Невалидный URL даёт fail-fast. R-CFG-INVALID-URL-FAIL-FAST
[ ] Невалидный market filter даёт fail-fast. R-CFG-INVALID-FILTER-FAIL-FAST
[ ] Все durations парсятся безопасно. R-CFG-DURATIONS-SAFE-PARSE
[ ] Все числовые лимиты имеют min/max validation. R-CFG-NUMERIC-MINMAX
[ ] Config dump на старте маскирует sensitive values. R-CFG-DUMP-MASKS-SECRETS6.3 Дефекты, которые нужно искать специально
text
- unwrap()/expect() на env vars; R-CFG-DEFECT-UNWRAP-ON-ENV
- default timeout = infinite; R-CFG-DEFECT-INFINITE-TIMEOUT
- hardcoded endpoint; R-CFG-DEFECT-HARDCODED-ENDPOINT
- разные endpoint versions в разных модулях; R-CFG-DEFECT-MIXED-ENDPOINT-VERSIONS
- mixed staging/prod; R-CFG-DEFECT-MIXED-ENV
- silent fallback на пустой market list; R-CFG-DEFECT-SILENT-EMPTY-MARKET-FALLBACK
- отсутствие schema version в config. R-CFG-DEFECT-NO-SCHEMA-VERSION7. Аудит market discovery
Market discovery — критический компонент, потому что неполный universe рынков делает backtest смещённым.
7.1 Expected behavior
Collector должен уметь:
text
1. Получить список активных рынков. R-DISC-FETCH-ACTIVE
2. Получить markets внутри events. R-DISC-FETCH-MARKETS-IN-EVENTS
3. Обработать pagination. R-DISC-PAGINATION
4. Применить фильтры tag/category/slug/active/closed. R-DISC-FILTERS-APPLIED
5. Извлечь: R-DISC-EXTRACT-FIELDS
- event_id;
- market_id;
- condition_id;
- question;
- slug;
- outcomes;
- token_ids;
- end_date;
- resolution source;
- active/closed/archived flags;
- neg-risk/group metadata, если используется.
6. Обновлять metadata при изменениях. R-DISC-METADATA-REFRESHED
7. Не терять рынки при временной ошибке Gamma API. R-DISC-NO-LOSS-ON-TRANSIENT-ERROR7.2 Checklist
text
[ ] Используется pagination, а не только первый page. R-DISC-PAGINATION-USED
[ ] offset/limit цикл завершается корректно. R-DISC-PAGINATION-TERMINATES
[ ] Есть защита от infinite pagination loop. R-DISC-PAGINATION-NO-INFINITE-LOOP
[ ] active/closed фильтры соответствуют ожидаемой стратегии. R-DISC-FILTERS-MATCH-STRATEGY
[ ] closed markets не теряются, если нужны для historical backfill.R-DISC-CLOSED-RETAINED-FOR-BACKFILL
[ ] tag filtering не исключает related рынки случайно. R-DISC-TAG-FILTER-NO-OVER-EXCLUSION
[ ] token_ids соответствуют outcomes по индексу. R-DISC-TOKEN-OUTCOME-INDEX-ALIGNED
[ ] condition_id не путается с market_id. R-DISC-CONDITION-NOT-MARKET
[ ] slug не используется как единственный immutable id. R-DISC-SLUG-NOT-SOLE-ID
[ ] market metadata версионируется. R-DISC-METADATA-VERSIONED
[ ] end_date/start_date парсятся с timezone. R-DISC-DATES-WITH-TZ
[ ] resolved/closed рынки корректно переходят в terminal state. R-DISC-TERMINAL-STATE
[ ] delisted/archived markets обрабатываются явно. R-DISC-ARCHIVED-HANDLED
[ ] unsupported market types логируются, а не silently ignored. R-DISC-UNSUPPORTED-LOGGED7.3 Тесты
Fixtures для проверки:
text
- single binary market; R-DISC-FIXTURE-BINARY
- multi-outcome market; R-DISC-FIXTURE-MULTI-OUTCOME
- neg-risk event; R-DISC-FIXTURE-NEG-RISK
- market без token_ids; R-DISC-FIXTURE-NO-TOKEN-IDS
- market с переставленными outcomes; R-DISC-FIXTURE-REORDERED-OUTCOMES
- closed market; R-DISC-FIXTURE-CLOSED
- active market; R-DISC-FIXTURE-ACTIVE
- market с missing end_date; R-DISC-FIXTURE-MISSING-END-DATE
- market с unusual decimal price; R-DISC-FIXTURE-UNUSUAL-DECIMAL
- paginated response на 3 страницы; R-DISC-FIXTURE-PAGINATED-3
- response с duplicate market. R-DISC-FIXTURE-DUPLICATE-MARKETМинимальный тест — R-DISC-TEST-PRESERVE-OUTCOME-TOKEN-ALIGNMENT:
rust
#[test]
fn discovery_preserves_outcome_token_alignment() {
// Given Gamma market with outcomes ["Yes", "No"] and token_ids ["111", "222"]
// When normalized
// Then outcome "Yes" maps to token_id "111", "No" maps to token_id "222"
}8. Аудит CLOB REST snapshots
8.1 Что проверять
CLOB REST используется как reference для:
text
- initial order book snapshot; R-CLOB-PURPOSE-INITIAL-SNAPSHOT
- periodic reconciliation; R-CLOB-PURPOSE-RECONCILIATION
- price/midpoint/spread sanity checks; R-CLOB-PURPOSE-SANITY-CHECK
- gap recovery after WS reconnect; R-CLOB-PURPOSE-GAP-RECOVERY
- historical price sanity, если используется prices-history. R-CLOB-PURPOSE-HISTORY-SANITY8.2 Checklist
text
[ ] Для каждого subscribed token_id есть initial snapshot. R-CLOB-SNAPSHOT-PER-TOKEN
[ ] Snapshot timestamp сохраняется отдельно от ingest timestamp.R-CLOB-SNAPSHOT-TS-SEPARATE
[ ] bids/asks сортируются правильно. R-CLOB-LEVELS-SORTED
[ ] best_bid <= best_ask invariant проверяется. R-CLOB-BBO-INVARIANT-CHECKED
[ ] Цена хранится как Decimal / fixed-point, не f64. R-CLOB-DEC-PRICE-NO-F64
[ ] Size хранится как Decimal / fixed-point, не f64. R-CLOB-DEC-SIZE-NO-F64
[ ] Пустой стакан допустим и обрабатывается явно. R-CLOB-EMPTY-BOOK-HANDLED
[ ] Snapshot не перетирает более свежие deltas. R-CLOB-SNAPSHOT-NO-OVERWRITE-FRESH-DELTA
[ ] Snapshot имеет source = clob_rest_snapshot. R-CLOB-SNAPSHOT-SOURCE-TAGGED
[ ] REST timeout конечный. R-CLOB-TIMEOUT-FINITE
[ ] Retry ограничен. R-CLOB-RETRY-BOUNDED
[ ] Backoff с jitter. R-CLOB-BACKOFF-JITTER
[ ] Rate limiting не превращается в silent stale book. R-CLOB-RATE-LIMIT-NO-STALE-BOOK
[ ] HTTP 429/5xx/Cloudflare throttling логируются отдельно. R-CLOB-THROTTLING-DISTINCT-LOG
[ ] Есть per-endpoint latency histogram. R-CLOB-LATENCY-HISTOGRAM
[ ] Есть sanity check между /book, /midpoint и /spread. R-CLOB-CROSS-ENDPOINT-SANITY8.3 Инварианты order book
Для каждого token_id:
R-CLOB-INV-BBO-NOT-CROSSED
Best bid не должен быть выше best ask.
sql
SELECT token_id, ts
FROM orderbook_best
WHERE best_bid_price > best_ask_price;R-CLOB-INV-PRICE-BOUNDED
Цена должна быть в диапазоне [0, 1].
sql
SELECT *
FROM orderbook_levels
WHERE price < 0 OR price > 1;R-CLOB-INV-SIZE-NON-NEGATIVE
Размер на уровне не может быть отрицательным.
sql
SELECT *
FROM orderbook_levels
WHERE size < 0;R-CLOB-INV-NO-DUPLICATE-LEVEL
Duplicate active level после нормализации недопустим.
sql
SELECT token_id, side, price, ts, COUNT(*)
FROM orderbook_levels
GROUP BY token_id, side, price, ts
HAVING COUNT(*) > 1;8.4 Специальная проверка precision
Поиск всех мест, где используются float-типы — R-CLOB-DEC-GREP-FLOATS:
bash
rg -n "f32|f64|as f|parse::<f|Decimal|rust_decimal|BigDecimal|fixed"Критично:
text
- price не должен проходить через f64 до записи; R-CLOB-DEC-PRICE-NO-F64-PATH
- size не должен проходить через f64 до записи; R-CLOB-DEC-SIZE-NO-F64-PATH
- PnL/backtest не должен использовать binary floating point для денег; R-CLOB-DEC-NO-BINARY-FLOAT-MONEY
- JSON numeric/string formats должны парситься предсказуемо. R-CLOB-DEC-JSON-DETERMINISTIC-PARSEРекомендуемый стандарт:
text
price: Decimal scale 6 или integer микродоллары/микроценты; R-CLOB-DEC-STANDARD-PRICE
size: Decimal scale 6 или выше; R-CLOB-DEC-STANDARD-SIZE
notional: Decimal; R-CLOB-DEC-STANDARD-NOTIONAL
timestamps: i64 nanoseconds/microseconds + original source timestamp. R-CLOB-DEC-STANDARD-TIMESTAMPS9. Аудит WebSocket market channel
9.1 Expected behavior
WS collector должен:
text
1. Подключиться. R-WS-CONNECT
2. Подписаться на нужные markets/token_ids. R-WS-SUBSCRIBE
3. Получить snapshot или дождаться внешнего REST snapshot. R-WS-SNAPSHOT-BOOTSTRAP
4. Применять incremental updates в правильном порядке. R-WS-ORDER-PRESERVATION
5. Обнаруживать gap/stale stream. R-WS-GAP-DETECT
6. Reconnect with backoff. R-WS-RECONNECT-BACKOFF
7. После reconnect делать resync. R-WS-RESYNC-AFTER-RECONNECT
8. Писать raw events и normalized events. R-WS-DUAL-RAW-AND-NORMALIZED
9. Не блокировать receive loop на DB writes. R-WS-NONBLOCKING-RECEIVE9.2 Checklist
text
[ ] Subscribe payload соответствует актуальной документации. R-WS-SUBSCRIBE-PAYLOAD-CURRENT
[ ] Все token_ids из discovery реально подписываются. R-WS-SUBSCRIBE-COVERS-DISCOVERY
[ ] Есть лимит размера subscription batch. R-WS-SUBSCRIBE-BATCH-LIMIT
[ ] Большие universe разбиваются на shards. R-WS-SUBSCRIBE-SHARDED-LARGE-UNIVERSE
[ ] Receive loop не содержит тяжёлых sync операций. R-WS-RECEIVE-LOOP-NO-HEAVY-SYNC
[ ] Parsing error не убивает весь процесс без restart policy. R-WS-PARSE-ERROR-RESTART-POLICY
[ ] Unknown message type сохраняется в raw payload или dead-letter. R-WS-UNKNOWN-MSG-RAW-OR-DEADLETTER
[ ] Ping/pong реализован. R-WS-PING-PONG
[ ] Idle timeout реализован. R-WS-IDLE-TIMEOUT
[ ] Reconnect exponential backoff с cap. R-WS-RECONNECT-EXPBACKOFF-CAP
[ ] Reconnect storm защищён. R-WS-RECONNECT-STORM-PROTECT
[ ] После reconnect выполняется REST resnapshot. R-WS-RESNAPSHOT-AFTER-RECONNECT
[ ] Есть gap marker в БД. R-WS-GAP-MARKER-DB
[ ] Есть stream session id. R-WS-STREAM-SESSION-ID
[ ] Есть monotonic ingest sequence. R-WS-MONOTONIC-INGEST-SEQ
[ ] Есть source_receive_ts. R-WS-SOURCE-RECEIVE-TS
[ ] Есть normalized_event_ts. R-WS-NORMALIZED-EVENT-TS
[ ] Есть raw payload hash. R-WS-RAW-PAYLOAD-HASH
[ ] Есть dedup key. R-WS-DEDUP-KEY9.3 Нельзя допускать
text
- применять delta к неизвестному snapshot; R-WS-NO-DELTA-BEFORE-SNAPSHOT
- применять события после reconnect без resync; R-WS-NO-EVENTS-AFTER-RECONNECT-WITHOUT-RESYNC
- считать local receive time временем market event; R-WS-NO-RECEIVE-AS-EVENT-TIME
- удалять unknown fields; R-WS-NO-FIELD-STRIPPING
- silently skip malformed messages; R-WS-NO-SILENT-MALFORMED-SKIP
- хранить только final book без event log; R-WS-NO-BOOK-WITHOUT-EVENT-LOG
- делать backtest по book state, собранному после reorder без отметки uncertainty. R-WS-NO-REORDER-WITHOUT-UNCERTAINTY-MARK9.4 Reconnect audit
Должны быть покрыты сценарии:
text
1. WS disconnect. R-WS-RECONNECT-CASE-DISCONNECT
2. Half-open connection. R-WS-RECONNECT-CASE-HALF-OPEN
3. Ping timeout. R-WS-RECONNECT-CASE-PING-TIMEOUT
4. Server sends malformed message. R-WS-RECONNECT-CASE-MALFORMED
5. Network pause 30s. R-WS-RECONNECT-CASE-NET-PAUSE
6. DB unavailable 10s. R-WS-RECONNECT-CASE-DB-UNAVAIL
7. Collector receives burst after reconnect. R-WS-RECONNECT-CASE-BURST
8. Subscription rejected. R-WS-RECONNECT-CASE-SUB-REJECTED
9. Partial subscription success. R-WS-RECONNECT-CASE-PARTIAL-SUB
10. Token removed/market closed. R-WS-RECONNECT-CASE-TOKEN-REMOVEDДля каждого сценария аудит подтверждает:
text
[ ] событие залогировано; R-WS-RECONNECT-EVIDENCE-LOGGED
[ ] метрика увеличилась; R-WS-RECONNECT-EVIDENCE-METRIC
[ ] состояние source стало degraded/recovering; R-WS-RECONNECT-EVIDENCE-DEGRADED-STATE
[ ] после восстановления сделан snapshot; R-WS-RECONNECT-EVIDENCE-POST-RESNAPSHOT
[ ] в БД есть gap/recovery marker; R-WS-RECONNECT-EVIDENCE-GAP-MARKER
[ ] backtest может исключить или пометить этот интервал. R-WS-RECONNECT-EVIDENCE-BACKTEST-EXCLUDABLE10. Аудит RTDS
RTDS нельзя смешивать с CLOB orderbook data. Это отдельный realtime stream, который может содержать comments, crypto/equity prices и другие live-data темы. В документации RTDS имеет отдельный endpoint и subscription model. (docs.polymarket.com)
10.1 Checklist
text
[ ] RTDS endpoint отделён от CLOB WS endpoint. R-RTDS-ENDPOINT-SEPARATE
[ ] RTDS topics явно перечислены в config. R-RTDS-TOPICS-IN-CONFIG
[ ] RTDS message types явно типизированы. R-RTDS-MSG-TYPES-TYPED
[ ] filters валидируются. R-RTDS-FILTERS-VALIDATED
[ ] gamma_auth используется только где требуется. R-RTDS-AUTH-ONLY-WHERE-REQUIRED
[ ] comments не смешиваются с trades. R-RTDS-COMMENTS-NOT-MIXED-WITH-TRADES
[ ] external prices не смешиваются с Polymarket prices. R-RTDS-EXT-PRICES-SEPARATE
[ ] is_carried_forward сохраняется, если поле присутствует. R-RTDS-CARRIED-FORWARD-PRESERVED
[ ] stale ticks не используются как live signal. R-RTDS-NO-STALE-AS-LIVE
[ ] RTDS source timestamp сохраняется. R-RTDS-SOURCE-TS-SAVED
[ ] RTDS ingest latency измеряется. R-RTDS-LATENCY-MEASURED
[ ] Unknown RTDS topics идут в dead-letter. R-RTDS-UNKNOWN-TOPIC-DEADLETTER10.2 Специальная проверка stale data
Для external prices:
text
[ ] Есть поле live/stale/carried_forward. R-RTDS-FRESHNESS-FIELD-EXISTS
[ ] Backtest не использует carried_forward tick как новое рыночное изменение. R-RTDS-BACKTEST-NO-CARRIED-AS-LIVE
[ ] Есть отдельный флаг freshness. R-RTDS-FRESHNESS-FLAG
[ ] Есть max_age_ms для использования сигнала. R-RTDS-MAX-AGE-MSR-RTDS-INV-NO-CARRIED-AS-LIVE-SIGNAL
Если такой query возвращает строки — это потенциальный Critical/High defect.
sql
SELECT *
FROM rtds_ticks
WHERE is_carried_forward = true
AND used_as_live_signal = true;11. Аудит trade/activity ingestion
Если система собирает trades/activity через Data API или WS:
11.1 Checklist
text
[ ] Trade id / transaction id / order id сохраняются. R-TRADE-IDS-PRESERVED
[ ] side нормализуется однозначно. R-TRADE-SIDE-NORMALIZED
[ ] maker/taker, если есть, не теряются. R-TRADE-MAKER-TAKER-PRESERVED
[ ] price/size/notional точные. R-TRADE-PRICE-SIZE-NOTIONAL-PRECISE
[ ] token_id связан с market/outcome. R-TRADE-TOKEN-LINKED-TO-MARKET
[ ] transaction hash, если есть, сохраняется отдельно. R-TRADE-TX-HASH-SEPARATE
[ ] on-chain timestamp не смешивается с off-chain execution timestamp. R-TRADE-ONCHAIN-OFFCHAIN-TS-SEPARATE
[ ] activity types явно перечислены. R-TRADE-ACTIVITY-TYPES-ENUM
[ ] SPLIT/MERGE/REDEEM/CONVERSION не классифицируются как обычный TRADE. R-TRADE-NON-TRADE-NOT-CLASSIFIED-AS-TRADE
[ ] Duplicate activity не создаёт fake volume. R-TRADE-DUPLICATE-NO-FAKE-VOLUME
[ ] Late-arriving activity не ломает ordering. R-TRADE-LATE-DOES-NOT-BREAK-ORDERING11.2 Проверка semantic correctness
Поиск enum/model для activity type — R-TRADE-GREP-ACTIVITY-ENUM:
bash
rg -n "TRADE|SPLIT|MERGE|REDEEM|REWARD|CONVERSION|MAKER_REBATE|activity|trade_type"Проверить:
text
[ ] Unknown type не падает. R-TRADE-UNKNOWN-NO-PANIC
[ ] Unknown type не мапится в TRADE по default. R-TRADE-UNKNOWN-NOT-DEFAULT-TRADE
[ ] Все типы имеют raw representation. R-TRADE-RAW-REPRESENTATION
[ ] Backtest может фильтровать non-trade activity. R-TRADE-BACKTEST-FILTER-NON-TRADE12. Аудит нормализации данных
12.1 Canonical entities
Должны существовать canonical model для:
text
Event R-NORM-MODEL-EVENT
Market R-NORM-MODEL-MARKET
Outcome R-NORM-MODEL-OUTCOME
Token R-NORM-MODEL-TOKEN
OrderBookSnapshot R-NORM-MODEL-ORDERBOOK-SNAPSHOT
OrderBookDelta R-NORM-MODEL-ORDERBOOK-DELTA
Trade R-NORM-MODEL-TRADE
Activity R-NORM-MODEL-ACTIVITY
RTDSTick R-NORM-MODEL-RTDS-TICK
SourceSession R-NORM-MODEL-SOURCE-SESSION
IngestionGap R-NORM-MODEL-INGESTION-GAP
RawPayload R-NORM-MODEL-RAW-PAYLOADЕсли модели размазаны по коду — зафиксировать как архитектурный риск (R-NORM-MODELS-NOT-SCATTERED).
12.2 Обязательные поля normalized event
Для каждого market-data event:
text
event_id/internal_id R-NORM-FIELD-EVENT-ID
source R-NORM-FIELD-SOURCE
source_session_id R-NORM-FIELD-SOURCE-SESSION-ID
source_sequence nullable R-NORM-FIELD-SOURCE-SEQUENCE
source_ts nullable R-NORM-FIELD-SOURCE-TS
receive_ts R-NORM-FIELD-RECEIVE-TS
ingest_ts R-NORM-FIELD-INGEST-TS
market_id nullable R-NORM-FIELD-MARKET-ID
condition_id nullable R-NORM-FIELD-CONDITION-ID
token_id nullable R-NORM-FIELD-TOKEN-ID
outcome nullable R-NORM-FIELD-OUTCOME
event_type R-NORM-FIELD-EVENT-TYPE
price nullable R-NORM-FIELD-PRICE
size nullable R-NORM-FIELD-SIZE
side nullable R-NORM-FIELD-SIDE
raw_payload_hash R-NORM-FIELD-RAW-PAYLOAD-HASH
schema_version R-NORM-FIELD-SCHEMA-VERSION
normalizer_version R-NORM-FIELD-NORMALIZER-VERSION12.3 Checklist
text
[ ] Нормализация deterministic. R-NORM-DETERMINISTIC
[ ] Raw payload сохраняется или доступен через hash/archive. R-NORM-RAW-PAYLOAD-ACCESSIBLE
[ ] Нет lossy преобразования важных полей. R-NORM-NO-LOSSY-IMPORTANT-FIELDS
[ ] Все nullable поля осознанны. R-NORM-NULLABLE-INTENTIONAL
[ ] Ошибки нормализации идут в dead-letter. R-NORM-ERRORS-TO-DEADLETTER
[ ] Dead-letter имеет причину ошибки. R-NORM-DEADLETTER-HAS-REASON
[ ] Schema version пишется в каждую запись или batch. R-NORM-SCHEMA-VERSION-STAMPED
[ ] Normalizer version фиксируется. R-NORM-NORMALIZER-VERSION-STAMPED
[ ] Migration меняет version. R-NORM-MIGRATION-BUMPS-VERSION
[ ] Старые события можно replay через текущий или legacy normalizer. R-NORM-LEGACY-REPLAYABLE13. Аудит timestamp model
Это один из самых критичных блоков.
13.1 Обязательные timestamps
Система должна различать:
text
source_ts — время события по источнику, если есть; R-TS-SOURCE-TS-EXISTS
receive_ts — время получения collector'ом; R-TS-RECEIVE-TS-EXISTS
ingest_ts — время записи/commit в БД; R-TS-INGEST-TS-EXISTS
snapshot_ts — время получения REST snapshot; R-TS-SNAPSHOT-TS-EXISTS
exchange_ts — если источник даёт exchange/server timestamp; R-TS-EXCHANGE-TS-EXISTS
chain_ts — если событие on-chain; R-TS-CHAIN-TS-EXISTS
backtest_visible_ts — earliest timestamp, когда событие можно использовать в симуляции. R-TS-VISIBLE-TS-EXISTS13.2 Checklist
text
[ ] Все timestamps хранятся в UTC или явно с timezone. R-TS-UTC-OR-EXPLICIT-TZ
[ ] Нет naive local datetime. R-TS-NO-NAIVE-LOCAL
[ ] Нет смешивания seconds/ms/us/ns. R-TS-CONSISTENT-UNITS
[ ] Все timestamp units проверены тестами. R-TS-UNITS-TESTED
[ ] receive_ts берётся максимально близко к network receive. R-TS-RECEIVE-NEAR-NETWORK
[ ] ingest_ts не используется как market event time. R-TS-INGEST-NOT-AS-EVENT-TIME
[ ] backtest использует visible_ts, а не source_ts blindly. R-TS-BACKTEST-USES-VISIBLE
[ ] Есть latency = receive_ts - source_ts, если source_ts есть. R-TS-LATENCY-COMPUTED
[ ] Negative latency алертится. R-TS-NEGATIVE-LATENCY-ALERT
[ ] Clock skew мониторится. R-TS-CLOCK-SKEW-MONITORED13.3 SQL-инварианты
R-TS-RECEIVE-NOT-BEFORE-SOURCE
receive_ts не должен быть сильно раньше source_ts.
sql
SELECT *
FROM normalized_events
WHERE source_ts IS NOT NULL
AND receive_ts < source_ts - INTERVAL '5 seconds';R-TS-INGEST-NOT-BEFORE-RECEIVE
ingest_ts не должен быть раньше receive_ts.
sql
SELECT *
FROM normalized_events
WHERE ingest_ts < receive_ts;R-TS-NO-FUTURE-TIMESTAMPS
Подозрительные timestamps из будущего.
sql
SELECT *
FROM normalized_events
WHERE source_ts > NOW() + INTERVAL '60 seconds'
OR receive_ts > NOW() + INTERVAL '60 seconds';14. Аудит database schema
14.1 Checklist
text
[ ] Есть migrations. R-DB-MIGRATIONS-EXIST
[ ] Migrations применяются в CI. R-DB-MIGRATIONS-IN-CI
[ ] Нет ручного drift между schema и models. R-DB-NO-SCHEMA-MODEL-DRIFT
[ ] Primary keys стабильные. R-DB-STABLE-PRIMARY-KEYS
[ ] Unique constraints отражают idempotency. R-DB-UNIQUE-REFLECTS-IDEM
[ ] Индексы покрывают backtest queries. R-DB-INDEXES-COVER-BACKTEST
[ ] Raw payload таблица не раздувает hot path. R-DB-RAW-NOT-ON-HOT-PATH
[ ] Partitioning/retention продуманы. R-DB-PARTITION-RETENTION
[ ] Insert path выдерживает burst. R-DB-INSERT-BURST-OK
[ ] Batch insert атомарен или имеет recovery. R-DB-BATCH-ATOMIC-OR-RECOVERABLE
[ ] Dead-letter таблица есть. R-DB-DEADLETTER-TABLE
[ ] Gap/recovery таблица есть. R-DB-GAP-TABLE
[ ] Source sessions таблица есть. R-DB-SOURCE-SESSIONS-TABLE
[ ] Schema version фиксируется. R-DB-SCHEMA-VERSION-RECORDED14.2 Минимальный набор таблиц
Отсутствие любой таблицы фиксируется как gap:
text
source_sessions R-DB-TABLE-SOURCE-SESSIONS
raw_payloads R-DB-TABLE-RAW-PAYLOADS
normalized_events R-DB-TABLE-NORMALIZED-EVENTS
markets R-DB-TABLE-MARKETS
market_versions R-DB-TABLE-MARKET-VERSIONS
tokens R-DB-TABLE-TOKENS
orderbook_snapshots R-DB-TABLE-ORDERBOOK-SNAPSHOTS
orderbook_levels R-DB-TABLE-ORDERBOOK-LEVELS
orderbook_deltas R-DB-TABLE-ORDERBOOK-DELTAS
trades R-DB-TABLE-TRADES
activities R-DB-TABLE-ACTIVITIES
rtds_ticks R-DB-TABLE-RTDS-TICKS
ingestion_gaps R-DB-TABLE-INGESTION-GAPS
dead_letters R-DB-TABLE-DEAD-LETTERS
collector_health R-DB-TABLE-COLLECTOR-HEALTH14.3 Idempotency keys
Ключи не должны быть слишком широкими и не слишком узкими.
Примеры:
text
raw_payload: R-DB-IDEM-RAW-PAYLOAD
source + source_session_id + receive_seq
или source + raw_hash + receive_ts_bucket
trade: R-DB-IDEM-TRADE
source + trade_id
или tx_hash + log_index
или market_id + token_id + price + size + source_ts + participant/order ids, если trade_id нет
orderbook_delta: R-DB-IDEM-ORDERBOOK-DELTA
source + source_session_id + source_sequence
если sequence нет:
source + token_id + side + price + size + source_ts + raw_hash
snapshot: R-DB-IDEM-SNAPSHOT
source + token_id + snapshot_ts + raw_hashЕсли dedup key основан только на token_id + price + size + timestamp — это риск: реальные разные события могут схлопнуться (R-DB-IDEM-NO-NARROW-DEDUP-KEY).
15. Аудит ordering и replay
15.1 Цель
Backtest должен воспроизводить состояние рынка на любой момент времени без неявного использования будущей информации.
15.2 Checklist
text
[ ] Есть append-only event log. R-ORD-APPEND-ONLY-LOG
[ ] Event log можно replay deterministic. R-ORD-REPLAY-DETERMINISTIC
[ ] Replay результат совпадает с materialized book. R-ORD-REPLAY-MATCHES-MATERIALIZED
[ ] События сортируются по visible_ts + ingest_sequence, а не только source_ts. R-ORD-SORT-VISIBLE-PLUS-SEQ
[ ] При равном timestamp есть tie-breaker. R-ORD-TIE-BREAKER
[ ] Gaps представлены явно. R-ORD-GAPS-EXPLICIT
[ ] После gap book state помечается uncertain до resnapshot. R-ORD-POST-GAP-UNCERTAIN
[ ] Snapshot/delta ordering формализован. R-ORD-SNAPSHOT-DELTA-FORMALIZED
[ ] Late events не переписывают историю без versioning. R-ORD-LATE-NO-DESTRUCTIVE-REWRITE
[ ] Corrections пишутся append-only, не destructive update. R-ORD-CORRECTIONS-APPEND-ONLY15.3 Replay test
Обязательный тест — R-ORD-REPLAY-TEST:
text
Input:
1. REST snapshot at T0.
2. Delta A at T1.
3. Delta B at T2.
4. Reconnect gap at T3.
5. REST snapshot at T4.
6. Delta C at T5.
Expected:
- book valid from T0 to T3;
- interval T3–T4 marked uncertain;
- after T4 book valid again;
- Delta C applies to post-resnapshot book.16. Аудит backtest-readiness
16.1 Проверить, что история содержит достаточно данных
Для realistic backtest нужны:
text
- best bid/ask history; R-BT-DATA-BBO-HISTORY
- depth levels, если стратегия зависит от fill probability; R-BT-DATA-DEPTH-LEVELS
- trades history; R-BT-DATA-TRADES-HISTORY
- market metadata history; R-BT-DATA-METADATA-HISTORY
- liquidity/spread history; R-BT-DATA-LIQ-SPREAD-HISTORY
- source gaps; R-BT-DATA-SOURCE-GAPS
- stale markers; R-BT-DATA-STALE-MARKERS
- latency; R-BT-DATA-LATENCY
- eligibility state at time T; R-BT-DATA-ELIGIBILITY-STATE
- resolution/closed state; R-BT-DATA-RESOLUTION-STATE
- fees/slippage assumptions separately versioned. R-BT-DATA-FEES-VERSIONED16.2 Checklist
text
[ ] Backtest не использует current market metadata для прошлого. R-BT-LA-NO-FUTURE-METADATA
[ ] Market eligibility пересчитывается исторически. R-BT-LA-ELIGIBILITY-RECOMPUTED
[ ] Исторические tags/category изменения учтены или frozen. R-BT-LA-TAGS-FROZEN-OR-VERSIONED
[ ] Closed/resolved outcome не виден до времени resolution. R-BT-LA-OUTCOME-HIDDEN-UNTIL-RESOLUTION
[ ] Resolution source/final result не попадает в signal features до публикации. R-BT-LA-RESOLUTION-NOT-IN-SIGNAL
[ ] Orderbook после reconnect gap не используется как continuous truth. R-BT-LA-BOOK-AFTER-GAP-NOT-CONTINUOUS
[ ] Fill simulation использует доступную на тот момент depth. R-BT-LA-FILL-SIM-HISTORICAL-DEPTH
[ ] События сортируются по visible_ts. R-BT-LA-EVENTS-SORTED-BY-VISIBLE-TS
[ ] Есть data_quality_score по market/time interval. R-BT-DATA-QUALITY-SCORE16.3 Look-ahead bias checks
Искать в коде backtest:
bash
# R-BT-LA-GREP-FUTURE-REFERENCES
rg -n "now\\(|Utc::now|SystemTime::now|latest|current|resolved|closed|winner|outcome|final|end_date|resolution"Подозрительно:
text
- использовать current closed/resolved status при симуляции прошлого; R-BT-LA-NO-CURRENT-STATUS-IN-PAST
- использовать final winning outcome до resolution; R-BT-LA-NO-FINAL-OUTCOME-PRE-RESOLUTION
- использовать final market metadata вместо versioned metadata; R-BT-LA-NO-FINAL-METADATA
- фильтровать markets по сегодняшней liquidity для прошлого периода; R-BT-LA-NO-CURRENT-LIQUIDITY-FILTER
- использовать complete dataset для выбора markets без historical availability cutoff. R-BT-LA-HISTORICAL-AVAILABILITY-CUTOFF17. Аудит observability
17.1 Обязательные метрики
text
collector_ws_connected{source} R-OBS-METRIC-WS-CONNECTED
collector_ws_reconnect_total{source} R-OBS-METRIC-WS-RECONNECT-TOTAL
collector_ws_message_total{source,type} R-OBS-METRIC-WS-MESSAGE-TOTAL
collector_ws_parse_error_total{source,type} R-OBS-METRIC-WS-PARSE-ERROR-TOTAL
collector_ws_unknown_message_total{source} R-OBS-METRIC-WS-UNKNOWN-MESSAGE-TOTAL
collector_http_request_total{endpoint,status} R-OBS-METRIC-HTTP-REQUEST-TOTAL
collector_http_latency_ms{endpoint} R-OBS-METRIC-HTTP-LATENCY
collector_http_timeout_total{endpoint} R-OBS-METRIC-HTTP-TIMEOUT-TOTAL
collector_db_insert_total{table} R-OBS-METRIC-DB-INSERT-TOTAL
collector_db_insert_error_total{table} R-OBS-METRIC-DB-INSERT-ERROR-TOTAL
collector_db_insert_latency_ms{table} R-OBS-METRIC-DB-INSERT-LATENCY
collector_dead_letter_total{source,reason} R-OBS-METRIC-DEAD-LETTER-TOTAL
collector_gap_total{source,market} R-OBS-METRIC-GAP-TOTAL
collector_snapshot_total{source} R-OBS-METRIC-SNAPSHOT-TOTAL
collector_reconciliation_mismatch_total{source} R-OBS-METRIC-RECONCILIATION-MISMATCH-TOTAL
collector_event_lag_ms{source} R-OBS-METRIC-EVENT-LAG
collector_queue_depth{name} R-OBS-METRIC-QUEUE-DEPTH
collector_dropped_event_total{source,reason} R-OBS-METRIC-DROPPED-EVENT-TOTAL17.2 Checklist
text
[ ] Logs structured JSON. R-OBS-LOGS-STRUCTURED-JSON
[ ] Есть correlation id. R-OBS-CORRELATION-ID
[ ] Есть source_session_id. R-OBS-SOURCE-SESSION-ID
[ ] Ошибки содержат source, market_id/token_id, endpoint. R-OBS-ERROR-CONTEXT
[ ] Нет секретов в логах. R-OBS-NO-SECRETS-IN-LOGS
[ ] Есть health endpoint или health command. R-OBS-HEALTH-ENDPOINT
[ ] Есть readiness/liveness separation. R-OBS-READINESS-LIVENESS
[ ] Есть dashboards или хотя бы metric names. R-OBS-DASHBOARDS
[ ] Есть alert thresholds. R-OBS-ALERT-THRESHOLDS17.3 Минимальные алерты
text
- WS disconnected > 30s. R-OBS-ALERT-WS-DISCONNECTED
- Reconnect count > N за 5 минут. R-OBS-ALERT-RECONNECT-COUNT
- Dead-letter rate > 0.1%. R-OBS-ALERT-DEAD-LETTER-RATE
- DB insert errors > 0. R-OBS-ALERT-DB-INSERT-ERRORS
- Event lag p95 > заданного SLA. R-OBS-ALERT-EVENT-LAG
- Snapshot reconciliation mismatch > 0. R-OBS-ALERT-RECONCILIATION-MISMATCH
- No messages for active subscription > X секунд. R-OBS-ALERT-SUBSCRIPTION-SILENT
- Queue depth растёт > Y минут. R-OBS-ALERT-QUEUE-DEPTH-RISING
- Unknown schema/message type появился. R-OBS-ALERT-UNKNOWN-SCHEMA18. Аудит performance и backpressure
18.1 Rust async checklist
text
[ ] Receive loop не блокируется sync DB write. R-PERF-RECEIVE-NONBLOCKING
[ ] Используется bounded channel. R-PERF-BOUNDED-CHANNEL
[ ] Backpressure policy явная. R-PERF-EXPLICIT-BACKPRESSURE
[ ] Нет unbounded memory growth. R-PERF-NO-UNBOUNDED-MEMORY
[ ] CPU-heavy normalization вынесена из hot receive path. R-PERF-CPU-HEAVY-OFF-HOT-PATH
[ ] Batch writer имеет flush by size/time. R-PERF-BATCH-FLUSH-SIZE-TIME
[ ] На shutdown выполняется drain. R-PERF-SHUTDOWN-DRAIN
[ ] Есть graceful shutdown signal handling. R-PERF-GRACEFUL-SHUTDOWN
[ ] JoinHandles awaited. R-PERF-JOINHANDLES-AWAITED
[ ] Ошибки task не теряются. R-PERF-TASK-ERRORS-NOT-LOST
[ ] Panic в child task приводит к controlled restart/fail-fast. R-PERF-PANIC-CONTROLLEDПоиск async-опасностей — R-PERF-GREP-ASYNC-HAZARDS:
bash
rg -n "spawn|tokio::spawn|mpsc::unbounded|unbounded_channel|Mutex|RwLock|block_on|std::thread::sleep|tokio::time::sleep|unwrap\\(|expect\\("18.2 Load test
Synthetic load сценарии:
text
- 1 market / 2 token_ids / low traffic; R-PERF-LOAD-1-MARKET
- 100 markets / 200 token_ids; R-PERF-LOAD-100-MARKETS
- 1,000 token_ids synthetic messages; R-PERF-LOAD-1K-TOKENS
- burst 10,000 messages за 10 секунд; R-PERF-LOAD-BURST-10K
- DB pause 5 секунд; R-PERF-LOAD-DB-PAUSE
- network reconnect loop; R-PERF-LOAD-RECONNECT-LOOP
- malformed 1% messages. R-PERF-LOAD-MALFORMED-1PCTМетрики:
text
- dropped events; R-PERF-LOAD-METRIC-DROPPED
- p50/p95/p99 receive-to-insert latency; R-PERF-LOAD-METRIC-LATENCY
- queue depth; R-PERF-LOAD-METRIC-QUEUE-DEPTH
- memory; R-PERF-LOAD-METRIC-MEMORY
- CPU; R-PERF-LOAD-METRIC-CPU
- DB batch size; R-PERF-LOAD-METRIC-BATCH-SIZE
- reconnect recovery time. R-PERF-LOAD-METRIC-RECONNECT-RECOVERY19. Аудит error handling
19.1 Checklist
text
[ ] Нет unwrap/expect в runtime path. R-ERR-NO-UNWRAP-RUNTIME
[ ] Ошибки типизированы. R-ERR-TYPED
[ ] Ошибки имеют context. R-ERR-WITH-CONTEXT
[ ] Network errors отделены от parse errors. R-ERR-NETWORK-PARSE-SEPARATE
[ ] DB errors отделены от constraint conflicts. R-ERR-DB-CONSTRAINT-SEPARATE
[ ] Constraint conflict может быть нормальной дедупликацией. R-ERR-CONSTRAINT-AS-DEDUP
[ ] Fatal/non-fatal errors классифицированы. R-ERR-FATAL-CLASSIFIED
[ ] Unknown schema не приводит к silent skip. R-ERR-UNKNOWN-SCHEMA-NO-SKIP
[ ] Retry не бесконечный без backoff. R-ERR-RETRY-BOUNDED
[ ] После max retries состояние source = failed/degraded. R-ERR-MAX-RETRY-DEGRADEDПоиск опасных паттернов — R-ERR-GREP-HAZARDS:
bash
rg -n "unwrap\\(|expect\\(|panic!|todo!|unimplemented!|\\.ok\\(\\)|let _ =|ignore|TODO|FIXME"Особенно опасно — R-ERR-NO-IGNORED-SEND:
rust
let _ = sender.send(event);Если send failed игнорируется — возможна silent data loss.
20. Аудит reconciliation
20.1 WS vs REST snapshot
Для каждого token_id:
text
1. Собрать WS book state. R-RECON-COLLECT-WS-STATE
2. Периодически взять REST /book. R-RECON-PERIODIC-REST-BOOK
3. Сравнить top N levels. R-RECON-COMPARE-TOP-N
4. Если mismatch: R-RECON-ON-MISMATCH
- записать mismatch event;
- пометить interval uncertain;
- resync book;
- сохранить оба состояния для анализа.20.2 Checklist
text
[ ] Reconciliation есть. R-RECON-EXISTS
[ ] Частота reconciliation configurable. R-RECON-FREQUENCY-CONFIGURABLE
[ ] Сравнивается не только best bid/ask, но и depth N. R-RECON-DEPTH-N-COMPARED
[ ] Decimal comparison учитывает scale/tick size. R-RECON-DECIMAL-AWARE-COMPARE
[ ] Mismatch не игнорируется. R-RECON-MISMATCH-NOT-IGNORED
[ ] Есть threshold по size dust. R-RECON-SIZE-DUST-THRESHOLD
[ ] Есть automatic resync. R-RECON-AUTO-RESYNC
[ ] Есть audit trail. R-RECON-AUDIT-TRAIL20.3 SQL для mismatch
R-RECON-INV-RECENT-MISMATCHES
sql
SELECT *
FROM reconciliation_mismatches
WHERE created_at >= NOW() - INTERVAL '24 hours'
ORDER BY created_at DESC;21. Аудит schema drift
Polymarket может менять поля/форматы. Аудит должен проверить устойчивость к schema drift.
21.1 Checklist
text
[ ] Serde models используют deny_unknown_fields только там, где это безопасно. R-SCHEMA-DENY-UNKNOWN-SCOPED
[ ] Unknown fields могут сохраняться в raw. R-SCHEMA-UNKNOWN-FIELDS-RAW-SAVED
[ ] Missing optional fields обрабатываются. R-SCHEMA-MISSING-OPTIONAL-HANDLED
[ ] Required fields валидируются. R-SCHEMA-REQUIRED-VALIDATED
[ ] Unknown enum variants поддерживаются. R-SCHEMA-UNKNOWN-ENUM-SUPPORTED
[ ] Есть schema snapshot fixtures. R-SCHEMA-SNAPSHOT-FIXTURES
[ ] Есть тесты на старые и новые payloads. R-SCHEMA-OLD-AND-NEW-PAYLOAD-TESTS
[ ] Changelog/API docs проверяются перед релизом. R-SCHEMA-CHANGELOG-PRE-RELEASE21.2 Тестовые payloads
Fixtures для проверки устойчивости:
text
- payload с extra field; R-SCHEMA-FIXTURE-EXTRA-FIELD
- payload без optional field; R-SCHEMA-FIXTURE-NO-OPTIONAL
- payload с unknown enum value; R-SCHEMA-FIXTURE-UNKNOWN-ENUM
- payload с null вместо string; R-SCHEMA-FIXTURE-NULL-INSTEAD-STRING
- payload с numeric string; R-SCHEMA-FIXTURE-NUMERIC-STRING
- payload с number вместо string; R-SCHEMA-FIXTURE-NUMBER-INSTEAD-STRING
- payload с пустым array; R-SCHEMA-FIXTURE-EMPTY-ARRAY
- payload с огромным orderbook; R-SCHEMA-FIXTURE-HUGE-ORDERBOOK
- payload с duplicate levels. R-SCHEMA-FIXTURE-DUPLICATE-LEVELS22. Аудит безопасности
Даже read-only collector может утечь secrets или повредить данные.
22.1 Checklist
text
[ ] Private keys/API keys не нужны для read-only collector. R-SEC-NO-KEYS-FOR-READONLY
[ ] Если auth используется — secrets не логируются. R-SEC-AUTH-SECRETS-NOT-LOGGED
[ ] .env не коммитится. R-SEC-ENV-NOT-COMMITTED
[ ] Config dump маскирует secrets. R-SEC-CONFIG-DUMP-MASKED
[ ] DB credentials не попадают в panic/error. R-SEC-DB-CREDS-NOT-IN-PANIC
[ ] Dependency audit выполняется. R-SEC-DEPENDENCY-AUDIT
[ ] TLS verification не отключён. R-SEC-TLS-VERIFY-ENABLED
[ ] Нет accept_invalid_certs. R-SEC-NO-INVALID-CERTS
[ ] Нет произвольной загрузки URL из внешних данных. R-SEC-NO-DYNAMIC-URL-LOAD
[ ] Raw payload storage не создаёт injection-risk в downstream tools. R-SEC-RAW-PAYLOAD-NO-INJECTIONПоиск secret-leak паттернов — R-SEC-GREP-SECRETS:
bash
rg -n "PRIVATE|SECRET|KEY|TOKEN|PASSWORD|accept_invalid|danger|insecure|tls|Authorization|POLY_"23. Аудит dependency и supply chain
Команды:
bash
cargo tree
cargo audit
cargo deny check
cargo outdated
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo fmt --checkЕсли cargo audit или cargo deny не настроены — зафиксировать Medium issue (R-DEP-AUDIT-AND-DENY-CONFIGURED).
Проверить:
text
[ ] Cargo.lock committed. R-DEP-CARGO-LOCK-COMMITTED
[ ] Нет git dependencies без pinned revision. R-DEP-GIT-DEPS-PINNED
[ ] Нет wildcard versions. R-DEP-NO-WILDCARD-VERSIONS
[ ] Нет deprecated crates в hot path. R-DEP-NO-DEPRECATED-HOT-PATH
[ ] TLS/HTTP/WebSocket crates актуальны. R-DEP-TLS-HTTP-WS-CURRENT
[ ] Decimal/time crates устойчивы. R-DEP-DECIMAL-TIME-STABLE24. Аудит CI/CD
24.1 Checklist
text
[ ] CI запускает fmt. R-CI-FMT
[ ] CI запускает clippy. R-CI-CLIPPY
[ ] CI запускает unit tests. R-CI-UNIT-TESTS
[ ] CI запускает integration tests с test DB. R-CI-INTEGRATION-TESTS
[ ] CI применяет migrations на clean DB. R-CI-MIGRATIONS-ON-CLEAN-DB
[ ] CI запускает property tests хотя бы smoke. R-CI-PROPERTY-TESTS-SMOKE
[ ] CI проверяет cargo audit/deny. R-CI-CARGO-AUDIT-DENY
[ ] CI собирает release build. R-CI-RELEASE-BUILD
[ ] CI не требует production secrets. R-CI-NO-PROD-SECRETS
[ ] Есть reproducible test fixtures. R-CI-REPRODUCIBLE-FIXTURES24.2 Release gates
Релиз collector нельзя считать безопасным, если не проходят:
text
- discovery pagination test; R-CI-GATE-DISCOVERY-PAGINATION
- timestamp unit test; R-CI-GATE-TIMESTAMP-UNIT
- price precision test; R-CI-GATE-PRICE-PRECISION
- WS reconnect/resnapshot test; R-CI-GATE-WS-RECONNECT-RESNAPSHOT
- DB idempotency test; R-CI-GATE-DB-IDEMPOTENCY
- replay determinism test; R-CI-GATE-REPLAY-DETERMINISM
- backtest look-ahead guard test; R-CI-GATE-BT-LOOKAHEAD
- reconciliation mismatch test. R-CI-GATE-RECONCILIATION25. Аудит data quality scoring
Для backtest лучше не просто хранить данные, а иметь quality model.
25.1 Минимальная модель качества
Для каждого market_id/token_id/time_bucket:
text
quality_score: 0.0–1.0 R-DQ-FIELD-SCORE
has_ws: bool R-DQ-FIELD-HAS-WS
has_snapshot: bool R-DQ-FIELD-HAS-SNAPSHOT
has_gap: bool R-DQ-FIELD-HAS-GAP
gap_duration_ms R-DQ-FIELD-GAP-DURATION
dead_letter_count R-DQ-FIELD-DEAD-LETTER-COUNT
reconciliation_mismatch_count R-DQ-FIELD-RECONCILIATION-MISMATCH-COUNT
max_event_lag_ms R-DQ-FIELD-MAX-EVENT-LAG
stale_duration_ms R-DQ-FIELD-STALE-DURATION
source_count R-DQ-FIELD-SOURCE-COUNT25.2 Checklist
text
[ ] Есть таблица или view data_quality. R-DQ-TABLE-OR-VIEW
[ ] Backtest может исключить poor-quality intervals. R-DQ-BACKTEST-CAN-EXCLUDE
[ ] Gaps влияют на score. R-DQ-GAPS-AFFECT-SCORE
[ ] Reconciliation mismatches влияют на score. R-DQ-RECONCILIATION-AFFECTS-SCORE
[ ] Stale RTDS ticks влияют на score. R-DQ-STALE-TICKS-AFFECT-SCORE
[ ] Высокая latency влияет на score. R-DQ-HIGH-LATENCY-AFFECTS-SCORE26. Аудит конкретных failure modes
Каждый failure mode помечается как покрытый / не покрытый / неизвестно (R-FM-COVERAGE-MATRIX).
markdown
| Failure mode | Rule-ID | Covered? | Evidence | Severity if uncovered |
|---|---|---:|---|---|
| WS disconnect | R-FM-WS-DISCONNECT | | | Critical |
| WS half-open | R-FM-WS-HALF-OPEN | | | High |
| Subscription rejected | R-FM-SUB-REJECTED | | | High |
| Partial subscription | R-FM-PARTIAL-SUB | | | High |
| Message parse error | R-FM-PARSE-ERROR | | | High |
| Unknown message type | R-FM-UNKNOWN-MSG | | | Medium |
| REST timeout | R-FM-REST-TIMEOUT | | | High |
| Cloudflare throttling | R-FM-CLOUDFLARE-THROTTLING | | | High |
| DB unavailable | R-FM-DB-UNAVAILABLE | | | Critical |
| Duplicate event | R-FM-DUPLICATE-EVENT | | | High |
| Lost event | R-FM-LOST-EVENT | | | Critical |
| Out-of-order event | R-FM-OUT-OF-ORDER | | | Critical |
| Snapshot/delta race | R-FM-SNAPSHOT-DELTA-RACE | | | Critical |
| Market metadata drift | R-FM-METADATA-DRIFT | | | High |
| Token/outcome mismatch | R-FM-TOKEN-OUTCOME-MISMATCH | | | Critical |
| Decimal precision loss | R-FM-DECIMAL-PRECISION-LOSS | | | Critical |
| Timestamp unit mismatch | R-FM-TIMESTAMP-UNIT-MISMATCH | | | Critical |
| Local timezone bug | R-FM-LOCAL-TZ-BUG | | | Critical |
| Backtest look-ahead | R-FM-BACKTEST-LOOKAHEAD | | | Critical |
| Schema drift | R-FM-SCHEMA-DRIFT | | | High |
| Reconnect storm | R-FM-RECONNECT-STORM | | | Medium |
| Memory leak / unbounded channel | R-FM-MEMORY-LEAK | | | High |
| Dead-letter ignored | R-FM-DEAD-LETTER-IGNORED | | | Medium |27. Минимальный набор regression tests
27.1 Unit tests
text
[ ] parse_market_metadata_binary R-TEST-UNIT-PARSE-MARKET-BINARY
[ ] parse_market_metadata_multi_outcome R-TEST-UNIT-PARSE-MARKET-MULTI
[ ] preserve_outcome_token_alignment R-TEST-UNIT-OUTCOME-TOKEN-ALIGN
[ ] parse_decimal_price_without_f64 R-TEST-UNIT-DECIMAL-PRICE
[ ] parse_decimal_size_without_f64 R-TEST-UNIT-DECIMAL-SIZE
[ ] timestamp_ms_vs_s_detection R-TEST-UNIT-TIMESTAMP-UNITS
[ ] unknown_enum_variant_goes_to_unknown R-TEST-UNIT-UNKNOWN-ENUM
[ ] malformed_payload_goes_to_dead_letter R-TEST-UNIT-MALFORMED-DEADLETTER
[ ] config_invalid_url_fails_fast R-TEST-UNIT-CONFIG-FAIL-FAST
[ ] env_secret_masking R-TEST-UNIT-ENV-MASKING27.2 Integration tests
text
[ ] discovery_pagination_three_pages R-TEST-INT-DISCOVERY-PAGINATION
[ ] clob_snapshot_insert_idempotent R-TEST-INT-CLOB-SNAPSHOT-IDEM
[ ] ws_delta_after_snapshot_updates_book R-TEST-INT-WS-DELTA-AFTER
[ ] ws_delta_before_snapshot_is_rejected_or_buffered R-TEST-INT-WS-DELTA-BEFORE
[ ] reconnect_creates_gap_and_resnapshot R-TEST-INT-RECONNECT-GAP-RESNAPSHOT
[ ] db_partial_failure_does_not_drop_uncommitted_events R-TEST-INT-DB-PARTIAL-FAILURE
[ ] duplicate_trade_does_not_duplicate_volume R-TEST-INT-DUPLICATE-TRADE
[ ] rtds_carried_forward_not_live_signal R-TEST-INT-RTDS-CARRIED-FORWARD
[ ] reconciliation_mismatch_creates_audit_event R-TEST-INT-RECONCILIATION-AUDIT27.3 Property-based tests
text
[ ] arbitrary orderbook levels always produce sorted valid book R-TEST-PROP-ORDERBOOK-VALID
[ ] duplicate events are idempotent R-TEST-PROP-DUPLICATE-IDEM
[ ] replay same events twice gives same final state R-TEST-PROP-REPLAY-STABLE
[ ] event order tie-breaker deterministic R-TEST-PROP-TIE-BREAKER
[ ] decimal roundtrip stable R-TEST-PROP-DECIMAL-ROUNDTRIP28. Рекомендуемая декомпозиция зон ответственности
Аудит может быть распараллелен по 5 зонам. Зона = логический срез по компонентам, не привязка к конкретному исполнителю.
Зона A — External API & Discovery
Scope:
text
- Gamma/Data/CLOB endpoints;
- market discovery;
- pagination;
- token/outcome mapping;
- endpoint configuration;
- CLOB V2 compatibility.Deliverables:
text
- API assumption report;
- discovery test gaps;
- ISSUE list;
- recommended fixtures.Зона B — WS/RTDS Runtime
Scope:
text
- WebSocket connection lifecycle;
- RTDS lifecycle;
- subscription batching;
- reconnect;
- ping/pong;
- stale detection;
- gap markers.Deliverables:
text
- stream lifecycle diagram;
- failure mode coverage table;
- reconnect/resync issues;
- synthetic WS test plan.Зона C — Normalization & Schema
Scope:
text
- Rust models;
- serde parsing;
- Decimal/timestamps;
- DB schema;
- idempotency;
- raw payload/dead-letter.Deliverables:
text
- canonical model diff;
- schema invariant report;
- SQL checks;
- precision/timestamp issues.Зона D — Replay & Backtest Correctness
Scope:
text
- event ordering;
- visible_ts;
- replay determinism;
- look-ahead bias;
- data quality scoring;
- historical metadata versioning.Deliverables:
text
- backtest-readiness report;
- look-ahead risk list;
- replay tests;
- data-quality model proposal.Зона E — Reliability, Performance & Ops
Scope:
text
- async Rust hot path;
- queues;
- DB writer;
- backpressure;
- observability;
- CI/CD;
- dependency audit.Deliverables:
text
- performance bottlenecks;
- runtime safety issues;
- missing metrics/alerts;
- CI gate recommendations.29. Финальный сводный отчёт
По завершении аудита собрать единый документ:
markdown
# Polymarket Collector Technical Audit Report
Audit date: YYYY-MM-DD
Repository commit: <hash>
## Executive Summary
- Overall status: Pass | Pass with risks | Fail
- Backtest readiness: Ready | Limited | Not ready
- Critical issues: N
- High issues: N
- Medium issues: N
- Low issues: N
## System Map
<component table>
## Confirmed Correct Behaviors
<список доказанных корректных свойств>
## Critical Issues
<ISSUE links>
## High Issues
<ISSUE links>
## Data Integrity Risks
<описание>
## Backtest Bias Risks
<описание>
## Missing Tests
<список>
## Required Fixes Before Production Use
1.
2.
3.
## Recommended Improvements
1.
2.
3.
## Appendix
- SQL invariant queries
- Test fixtures
- Reproduction commands
- API assumptions
- Known unknowns30. Definition of Done для аудита
Аудит считается завершённым только если:
text
[ ] Построена карта компонентов. R-DOD-COMPONENT-MAP
[ ] Проверены все network clients. R-DOD-NETWORK-CLIENTS
[ ] Проверен market discovery. R-DOD-DISCOVERY
[ ] Проверены WS и RTDS lifecycle. R-DOD-WS-RTDS-LIFECYCLE
[ ] Проверены CLOB snapshots. R-DOD-CLOB-SNAPSHOTS
[ ] Проверены timestamps. R-DOD-TIMESTAMPS
[ ] Проверена Decimal precision. R-DOD-DECIMAL-PRECISION
[ ] Проверена DB schema. R-DOD-DB-SCHEMA
[ ] Проверена idempotency. R-DOD-IDEMPOTENCY
[ ] Проверен replay. R-DOD-REPLAY
[ ] Проверен backtest на look-ahead bias. R-DOD-BACKTEST-LOOKAHEAD
[ ] Проверена observability. R-DOD-OBSERVABILITY
[ ] Проверены failure modes. R-DOD-FAILURE-MODES
[ ] Запущены cargo fmt/clippy/test. R-DOD-CARGO-CHECKS
[ ] Составлен список всех unwrap/expect в runtime path. R-DOD-UNWRAP-LIST
[ ] Составлен список всех мест, где используется f32/f64. R-DOD-FLOAT-LIST
[ ] Составлен список всех мест, где timestamp парсится/конвертируется. R-DOD-TIMESTAMP-LIST
[ ] Все проблемы оформлены в ISSUE format. R-DOD-ISSUES-FORMATTED
[ ] Для каждой High/Critical проблемы есть fix plan. R-DOD-FIX-PLAN
[ ] Для каждой High/Critical проблемы есть regression test proposal. R-DOD-REGRESSION-PROPOSAL
[ ] Явно указано, можно ли использовать текущую историю для backtest. R-DOD-BACKTEST-VERDICT31. Главный критерий оценки
Система не считается корректной, пока не доказано следующее свойство (R-MAIN-CRITERION):
text
Для любого market/token_id и момента времени T backtest получает только те данные,
которые collector реально мог знать к T, с корректными price/size/timestamp,
с явной маркировкой gaps/stale/mismatch intervals.Если это свойство не доказано — система может быть полезна как raw data logger, но не как надёжная база для quantitative backtest.