Контекст
В рамках собственной системы спортивной аналитики я хотел получить real-time доступ к данным о движении коэффициентов — в частности, с платформы pickingodds.com. У сервиса интересная фича — визуализация графика изменения линии по каждому событию. Это потенциально полезный источник вторичных сигналов (например, для обнаружения аномалий, связанных с резкой коррекцией маркет-мейкеров).
Изначальный план был прост: интегрироваться по REST API, выкачивать данные раз в несколько минут, писать в TSDB, использовать далее для анализа и фичей в ML-пайплайнах. На практике же всё быстро ушло в зону нетривиальной оптимизации.
Проблемы, с которыми столкнулся
1. Rate limit и «невидимая стена»
Pickingodds явно не ориентирован на машинных клиентов. Несмотря на публичность API, ограничения весьма жёсткие — до 10 rps на IP, с блоками при burst-запросах. Причём лимит не фиксируется в заголовках ответа — только в виде 5xx и банов на 10–15 минут.
Что сделал:
обёртка с token bucket rate limiter (реализован через
asyncio.Semaphore
+aiojobs
);adaptive throttling — задержки динамически подстраиваются под реальное время ответа и частоту 429/5xx;
IP rotation через proxy pool — чтобы равномерно распределять нагрузку.
2. Нестандартизированная структура ответа
API отдаёт deeply nested JSON, без нормализованных схем. Некоторые события приходят без ID матча, рынки различаются по структуре, часть данных подтягивается только по secondary endpoint’ам.
Что сделал:
построил модульную схему парсинга с
pydantic
-моделями, поддержкой fallback-парсинга и частичной валидацией;создал DSL-маршрутизатор, маппящий типы рынков на кастомные обработчики (
@register_market('1X2') → parse_mainline()
и т.д.);добавил schema-aware кеш — raw JSON хранится в Redis с TTL, повторный запрос сверяется по SHA256-сигнатуре payload’a.
3. Нет подписки / push-фида
Отсутствие WebSocket или хотя бы Server-Sent Events означает необходимость постоянного pull-запроса. Для большого количества матчей это критично — особенно если нужен частый опрос (раз в 10–30 сек).
Что сделал:
поднял асинхронный
fetch_orchestrator
наaiohttp + trio
, где каждый матч — отдельная coroutine, управляемая через event loop;написал mini-FSM для каждого запроса:
INIT → WAITING → FETCHING → BACKOFF → IDLE
;реализовал слоистую приоритезацию событий — top-лиги и “подозрительные” события (по ML-флагу) имеют более высокий polling rate;
данные сериализуются и стримятся в Kafka для дальнейшей агрегации и мониторинга.
ML в продакшене: от фильтра до сигнала
Чтобы не тянуть 1000+ матчей и не забивать лимит, я внедрил предварительный ML-фильтр.
Он определяет, какие события потенциально интересны (на основе профиля движения линии в первые 3–5 минут и исторических паттернов).
Методология:
pipeline на Scikit-Learn / LightGBM;
фичи:
delta_coef
,volatility_window
,source_count
,market_spread
,momentum_score
;метка —
is_significant_move
(бинарная, размечено по историческим данным);инференс — в онлайне, каждые 5 минут пересчёт при изменении event’а.
Фильтр снизил количество активных трекеров на ~60% без потери ценных сигналов.
Архитектура итоговой системы
[PickingOdds API]
|
[Fetcher Pool]
|
[Async Orchestrator]
|
+-------+--------+
| |
Redis (raw) Kafka (events)
| |
TimescaleDB ML worker pool
| |
Grafana Alert Engine
Технический стек
Python 3.11, Trio, aiohttp, Pydantic v2
Redis (сжатие через LZ4)
Kafka + Faust для стрим-пайплайнов
TimescaleDB для агрегатов и ретроспективного анализа
Prometheus + Grafana для realtime мониторинга
ML inference на FastAPI в отдельном inference layer
Выводы
Ожидал простую интеграцию, получил высоконагруженную систему с приоритетным планировщиком, ML-фильтрацией и rate-aware оркестрацией.
TL;DR:
REST API — не всегда «достаточно», особенно без подписки и при агрессивных лимитах.
Асинхронность, кеширование и ML-фильтрация спасают инфраструктуру от перегрузки.
Pickingodds — интересный источник, но требует глубокой адаптации.
Если у вас был опыт парсинга нестандартизированных спортивных фидов — напишите в комментариях. Сравним подходы.
Комментарии (3)
artem_uljanovc
24.07.2025 13:49token bucket + adaptive throttling - ок, но как pickingodds ведет себя когда после бана ты возвращаешься в поток?
pavel_shutovy
24.07.2025 13:49Было бы интересно, если бы ты в будущем сделал компонент для аномалий через прометей алертинг + отдельный ML layer (вроде anomaly_score pipeline)
grigorij_mikhajlovu
Очень похоже на мой опыт с oddsfire и betdata, тоже приходилось городить свой мини фреймворк для дедупа и throttle-aware polling