- Полная история в SQLite. Раньше данные жили в буфере на 500 записей — это всего ~10 минут рынка, и статистика «за час» была неправдой. Теперь история хранится 14 суток, дубликаты отсекаются.
- Честные окна 5м / 15м / 1ч / 4ч —
/api/windows. У каждого окна есть признак complete: видно, набраны данные или нет, а не замаскировано нулями.
- Сигнал LONG / HOLD / SHORT —
/api/signal. Приток стейблов на биржи = пришли деньги покупать = LONG. Отток = деньги уводят = SHORT. Между — HOLD, полноправный третий сигнал, а не свалка «не сработало».
- Самокалибровка вместо констант. Три уровня, каждый включается сам при наборе данных: готовность системы (6 ч), якорь по 95-му перцентилю вместо жёсткого числа (24 ч целыми сутками), нормировка по времени суток (5 суток).
- Учёт суточного ритма. Замер на своих данных: 3138 транзакций в 18:00 UTC против 1403 в 05:00 — спад в 2.2 раза за ночь. Поэтому пул для перцентиля состоит только из целых суток, иначе ночные часы не проходили бы порог никогда.
- Уведомления в Telegram — только при смене сигнала, без повторов и без спама на разогреве.
Ни один порог не зашит намертво. Стартовое значение работает лишь до тех пор,
пока не наберётся история — дальше шкала едет вместе с рынком.
- Единый криптоаналитический MCP-сервер. Не ещё одна панель мониторинга, а сервис, отдающий инструменты анализа агентам. Это масштабируется, новые веб-страницы — нет.
- Поток ордеров и новости — код готов. Два протестированных модуля: поток заявок в реальном времени (окна 1–60с) и новостной сентимент через DeepSeek. Осталось подключить их эндпоинтами к серверу. См. блоки ниже.
- Детектор пробоя (breakout). Ловит выход цены из диапазона в момент зарождения, а не после подтверждения тренда — первым условием
close < range_low, ADX/EMA/BB Width/объём подтверждают. См. блок ниже.
- Объединить источники: Ethereum, Bitcoin, Bybit, Binance, AI-сервер прогнозов, новости, ончейн-метрики, потоки на биржи, активность китов.
- Один ответ, несколько независимых моделей. У каждой свои
signal, confidence, priority и reason — модель сама объясняет своё решение.
health у каждой модели отдельно, а не один общий. Тогда агент видит, что flow_ai ещё копит историю, а news_ai отвалилась — и игнорирует только их, продолжая пользоваться остальными.
- Статусы:
READY, WARMING_UP, NO_DATA, STALE, ERROR, DISCONNECTED. Агент принимает решение, не зная внутренней реализации моделей.
summary по согласию моделей. Все сказали SHORT — это условные 100%. Разошлись — предсказание слабое, и это видно честно, а не спрятано за красивым числом.
Принцип: сервер — честный репортёр, а не арбитр. Отдаёт факты каждой модели
и уровень их согласия, а решает пусть клиент.
чего ждать от /api/analysis
{
"market": {
"symbol": "BTCUSDT",
"price": 65367.4,
"exchange": "Bybit",
"timestamp": 1784867220
},
"predictions": {
"trend_ai": {
"health": {
"status": "READY",
"data_ready": true,
"bars_filled": 128,
"bars_target": 128,
"history_depth_sec": 40394
},
"signal": "LONG",
"confidence": 0.93,
"priority": 87,
"reason": [
"Trend probability 0.93",
"Drawdown risk is low",
"Trend remains stable"
],
"probabilities": {
"long": 0.93,
"hold": 0.05,
"short": 0.02
},
"predicted_time_sec": 480,
"extras": {
"drawdown_pct": 0.18,
"drawdown_score": 0.62
}
},
"flow_ai": {
"health": {
"status": "READY",
"data_ready": true,
"hour_complete": true,
"history_depth_sec": 40394,
"baseline_hours": 11
},
"signal": "SHORT",
"confidence": 0.81,
"priority": 71,
"reason": [
"Exchange inflow exceeds outflow",
"Net inflow +4.9M USD",
"Flow anomaly within normal range"
],
"probabilities": {
"long": 0.12,
"hold": 0.07,
"short": 0.81
},
"predicted_time_sec": 3600,
"extras": {
"exchange_inflow_usd_1h": 15870620,
"exchange_outflow_usd_1h": 10970619,
"net_flow_usd_1h": 4900001,
"whale_count_1h": 357,
"anomaly_ratio": 0.17,
"z_score": -0.144
}
}
},
"summary": {
"signal": "LONG",
"confidence": 0.88,
"priority": 84,
"reason": [
"Trend AI strongly favors LONG",
"Flow AI favors SHORT",
"Trend model has higher confidence",
"Overall market bias is LONG"
]
}
}
Разбор реального эпизода 27–28 июля: оркестр поймал нисходящий пробой,
но с опозданием на ~30 минут и −0.27% хода — сначала ошибочно
открыл LONG «от поддержки» на нижней границе флэта. Причина: он ждал
подтверждённый тренд (ADX > 30), а не сам факт выхода
цены из диапазона. Отдельный детектор ловит пробой раньше — первым
условием ставит выход цены за границу диапазона, а ADX, EMA, полосы
Боллинджера и объём лишь подтверждают его.
какие индикаторы и значения обозначают пробой
| Индикатор | Роль | Пороги из лога 27–28.07 |
| Выход цены из диапазона |
ГЛАВНОЕ условие пробоя |
close < range_low минимум на 0.10–0.20% |
| ADX + скорость роста |
сила и ускорение тренда |
>25 старт, >30 подтв., >35 импульс; рост 21→31 за 2 свечи |
| EMA20 / EMA50 |
направление (фильтр) |
EMA20<EMA50 и цена < EMA20 = bearish |
| BB Width — расширение |
выход из сжатия полос |
рост ×1.25–1.5 за свечу; 0.56→0.83 = ×1.48 |
| Объём × среднего |
раннее предупреждение + подтверждение |
ранний ×1.72, подтв. ×2.0+; направленный ×3.29 — не шум |
| ATR × |
не триггер — нормировка |
размер стопа и дистанция пробоя dist / ATR |
чего ждать от /api/breakout
{
"breakout": {
"signal": "SHORT",
"status": "CONFIRMED",
"confidence": 0.87,
"direction": "DOWN",
"range": {
"high": 64882.4,
"low": 64640.2,
"width_pct": 0.374,
"lookback_bars": 20
},
"price": {
"current": 64504.4,
"distance_below_range_pct": 0.210,
"close_outside_range": true,
"consecutive_closes_outside": 1
},
"trend": {
"adx": 31.37,
"adx_previous": 25.10,
"adx_delta": 6.27,
"adx_rising": true,
"ema20": 64620.3,
"ema50": 64755.8,
"ema_alignment": "BEARISH",
"price_vs_ema20_pct": -0.179
},
"volatility": {
"atr_ratio": 0.90,
"bb_width": 0.83,
"bb_width_previous": 0.56,
"bb_expansion_ratio": 1.48
},
"volume": {
"ratio": 0.60,
"confirmation": false
},
"conditions": {
"range_broken": true,
"ema_confirmed": true,
"adx_confirmed": true,
"bb_expanding": true,
"volume_confirmed": false
},
"reason": [
"Price closed below the consolidation range",
"ADX rose from 25.10 to 31.37",
"Bollinger Band width expanded by 48%",
"EMA20 is below EMA50",
"Price is below EMA20"
]
}
}
скоринг: диапазон решает, остальное подтверждает
score = 0
close < range_low → +30 # ГЛАВНОЕ — сам факт пробоя
ema20 < ema50 and close < ema20 → +15
adx >= 25 → +10
adx >= 30 → +10
adx > prev_adx → +10
bb_width / prev_bb_width >= 1.25 → +15
volume_ratio >= 1.2 → +10
0–39 нет пробоя
40–59 подготовка
60–74 вероятный пробой
75–100 подтверждённый
Главное изменение против оркестра: первым условием — фактический выход
цены из диапазона (close < range_low), а ADX / EMA / BB Width /
объём его лишь подтверждают. Тогда сигнал приходит в начале выхода, а не
после −0.27% хода. В MCP это отдельная модель breakout_ai со своим
health — наравне с trend_ai и flow_ai.
Отдельный протестированный модуль (BybitLiveIndicatorV6): читает
сделки прямо из WebSocket Bybit и считает поток заявок в реальном времени по
окнам 1с / 5с / 15с / 60с. Отдаёт не одну цифру, а раздельные
long_score и short_score — сторона с перевесом и запасом
(score_edge) выше порога и становится сигналом.
из чего складывается счёт
| Фактор | Что смотрит | Вес |
| Дельта по окнам | перевес покупок/продаж 1с·5с·15с·60с | 5 / 10 / 15 / 10 |
| Дисбаланс потока | объём buy ÷ sell за 5с | 15 |
| Цена + дельта | совпадение направления за 15с | 10 |
| CVD-дивергенция | накопл. дельта против цены | 15 |
| Ловушка (trap) | запертые покупатели / продавцы за 5с | 20 |
| Крупные сделки | перевес крупного потока за 5с | 5 |
сигналы: ENTER LONG · ENTER SHORT · NO-TRADE
защита: STALE DATA при паузе потока >3с
чего ждать от /api/order_flow
{
"order_flow": {
"health": {
"status": "READY",
"connection": "connected",
"data_age_sec": 0.3,
"trades_5s": 47
},
"symbol": "BTCUSDT",
"price": 63340.0,
"mark_price": 63341.2,
"signal": "ENTER SHORT",
"direction": "SHORT",
"confidence": 75,
"long_score": 10,
"short_score": 75,
"score_edge": 65,
"windows": {
"delta_1s": -1.83,
"delta_5s": -6.42,
"cvd": -142.7,
"cvd_divergence": "ALIGNED"
},
"book_imbalance": -0.31,
"trap_5s": "BUYERS",
"absorption_5s": false,
"reasons": [
"Δ5s -6.420BTC",
"FLOW IMB x3.2 SELL",
"PRICE+DELTA ↓",
"BUYERS TRAPPED",
"LARGE SELL FLOW"
],
"timestamp": 1785205200.4
}
}
Модуль ещё и отслеживает результат своих прошлых сигналов
(signal_stats: win/loss, PnL) — то есть у модели есть измеренная
точность, а не только самооценка. В MCP это модель order_flow_ai
со своим health.
Отдельный протестированный модуль (news_sentiment): тянет свежие
заголовки из CryptoPanic (или RSS-лент как запасной источник) и отдаёт
их в DeepSeek, который классифицирует общий настрой рынка. Кеш 120 секунд —
новости не двигаются каждую секунду, лишние запросы к модели не нужны.
что отдаёт
| Поле | Значения |
| sentiment | BULLISH · BEARISH · NEUTRAL |
| confidence | 0–100 (90+ — единодушие, 50 — слабый сигнал) |
| urgency | HIGH — новость может двинуть цену >2% за часы · MEDIUM · LOW |
| key_themes | темы, задающие настрой (ETF, регуляция, ликвидации…) |
| summary | 2 фразы: что именно движет настроем |
чего ждать от /api/news
{
"news": {
"health": {
"status": "READY",
"source": "rss",
"headline_count": 18,
"age_sec": 47
},
"sentiment": "BEARISH",
"confidence": 72,
"urgency": "MEDIUM",
"key_themes": [
"ETF outflows",
"regulatory pressure",
"long liquidations"
],
"summary": "Spot ETFs saw net outflows for a third day while leveraged longs were liquidated on the drop. No bullish catalysts in the current headlines.",
"timestamp": 1785205200.4
}
}
Для агента полезнее всего urgency: HIGH — повод
приостановить торговлю или усилить/ослабить сигнал остальных моделей.
В MCP это модель news_ai со своим health
(WARMING_UP, если заголовков нет; ERROR, если
источник или DeepSeek недоступны).
Один вызов — все 6 моделей сразу, каждая со своим health,
плюс summary, собранный по согласию. Сценарий ниже — медвежий
тренд (как 27–28.07), где модели частично расходятся: видно, что сервер
честно показывает несогласие, а не прячет его за красивым числом.
1 · flow_ai потоки стейблов работает
2 · trend_ai v10 работает
3 · order_flow_ai поток заявок код готов
4 · news_ai новости код готов
5 · breakout_ai пробой в планах
6 · levels_ai S/R + mean-reversion в планах
чего ждать от /api/analysis
{
"meta": {
"server_time": 1785205200.4, // единые часы — относительно них меряется всё
"response_id": "a3f9c1"
},
"market": {
"symbol": "BTCUSDT",
"price": 63340.0,
"exchange": "Bybit",
"timestamp": 1785205200
},
"predictions": {
"trend_ai": {
"health": { "status": "READY", "as_of": 1785205200, "age_sec": 0, "horizon_sec": 1620, "refresh_sec": 60, "bars_filled": 128 },
"signal": "SHORT", "confidence": 0.85, "priority": 82,
"probabilities": { "long": 0.08, "hold": 0.17, "short": 0.75 },
"reason": ["ADX 63 подтверждает тренд", "EMA20 < EMA50"]
},
"order_flow_ai": {
"health": { "status": "READY", "as_of": 1785205200.1, "age_sec": 0.3, "horizon_sec": 30, "refresh_sec": 1 },
"signal": "SHORT", "confidence": 0.75, "priority": 71,
"long_score": 10, "short_score": 75, "score_edge": 65,
"windows": { "delta_5s": -6.42, "cvd": -142.7, "cvd_divergence": "ALIGNED" },
"trap_5s": "BUYERS",
"reason": ["Δ5s -6.42BTC", "FLOW IMB x3.2 SELL", "BUYERS TRAPPED"]
},
"breakout_ai": {
"health": { "status": "READY", "as_of": 1785205140, "age_sec": 60, "horizon_sec": 900, "refresh_sec": 60, "lookback_bars": 20 },
"signal": "SHORT", "status_detail": "CONFIRMED", "confidence": 0.87, "priority": 78,
"direction": "DOWN",
"range": { "high": 64882.4, "low": 64640.2, "width_pct": 0.374 },
"conditions": { "range_broken": true, "ema_confirmed": true, "bb_expanding": true, "volume_confirmed": false },
"reason": ["Цена закрылась ниже диапазона", "ADX 25.1 → 31.4", "BB Width +48%"]
},
"news_ai": {
"health": { "status": "READY", "as_of": 1785205153, "age_sec": 47, "horizon_sec": 7200, "refresh_sec": 120, "source": "rss", "headline_count": 18 },
"signal": "SHORT", "confidence": 0.72, "priority": 55,
"sentiment": "BEARISH", "urgency": "MEDIUM",
"key_themes": ["ETF outflows", "long liquidations"],
"reason": ["3-й день оттока из спот-ETF", "ликвидации лонгов"]
},
"flow_ai": {
"health": { "status": "READY", "as_of": 1785205190, "age_sec": 10, "horizon_sec": 3600, "refresh_sec": 60, "data_ready": true, "anchor_source": "p95" },
"signal": "HOLD", "confidence": 0.40, "priority": 30,
"score": 0.08,
"net_flow_usd_1h": 6535676,
"reason": ["приток $17.5M, отток $11.0M — оба ниже якоря $350M"]
},
"levels_ai": {
"health": { "status": "READY", "as_of": 1785205200, "age_sec": 0, "horizon_sec": 14400, "refresh_sec": 60, "candle_depth_days": 12 },
"signal": "HOLD", "confidence": 0.30, "priority": 20,
"position_in_channel": 0.06,
"mr_score": 0.71,
"mr_suppressed": true,
"regime": "TREND",
"levels": {
"support": { "price": 63180.0, "source": "POC_1h", "strength": 0.82 },
"resistance": { "price": 64640.0, "source": "swing_4h", "strength": 0.70 }
},
"l2_at_level": { "verdict": "BREAKING", "note": "bid-wall у поддержки тает, sell-поток сквозь уровень" },
"reason": ["цена у поддержки (p=0.06) → mean-reversion хотел LONG", "но режим ТРЕНД (ADX 63) → mr выключен", "L2: поддержка ломается, не отбой"]
}
},
"summary": {
"signal": "SHORT",
"confidence": 0.79,
"priority": 80,
"agreement": { "short": 4, "hold": 2, "long": 0, "consensus_pct": 0.67 },
"reason": [
"4 из 6 моделей за SHORT (trend, order_flow, breakout, news)",
"flow_ai нейтрален — потоки в норме",
"levels_ai подавил mean-reversion LONG: тренд ломает поддержку, а не отбивает",
"консенсус 67% — сигнал сильный, но не единогласный"
]
}
}
временной контракт
meta.server_time — единые часы ответа. У каждой модели в health четыре одинаковых поля: as_of (когда сняты данные), age_sec (свежесть), horizon_sec (сколько сигнал валиден), refresh_sec (частота обновления). Клиент-ИИ (DeepSeek, Anthropic…) взвешивает блоки сам, не зная внутренностей: order_flow с горизонтом 30с — сигнал на секунды, news с горизонтом 2ч — на часы. Блоки НЕ сводятся к одному времени — это убило бы разницу. Предохранитель: age_sec > horizon_sec = сигнал протух → STALE, автоматически, без спецлогики у клиента.
Два принципа видны прямо в примере. summary честный: 4 из 6 за SHORT →
консенсус 67% и confidence 0.79, а не «единогласные 100%»; нейтральные
flow_ai и levels_ai перечислены, не спрятаны.
levels_ai не даёт повторить −37% от 27.07: цена у поддержки
(position_in_channel 0.06), сигмоида хотела LONG
(mr_score 0.71), но mr_suppressed — режим ТРЕНД, а L2 говорит
BREAKING. Блок сам блокирует лонг в проламываемую поддержку.