261 lines
25 KiB
Markdown
261 lines
25 KiB
Markdown
# Crypto Spot TradeBot
|
||
|
||
Spot-бот для демо-торговли криптовалютой на реальных данных Bybit. По умолчанию работает только в `paper`-режиме со стартовым балансом `100 USDT`; live-режим заблокирован до явного включения через env-переменные.
|
||
|
||
## Что реализовано
|
||
|
||
- Реальные market data Bybit Spot: REST bootstrap и WebSocket-обновления.
|
||
- Торговый universe автоматически строится из актуальных Bybit Spot-инструментов: выбираются до 12 ликвидных USDT-пар по `turnover24h`, исключаются stablecoin-to-stablecoin и leveraged-token пары; фиксированный список можно задать только явным `SYMBOLS`.
|
||
- Paper trading с учетом cash, комиссий, проскальзывания, stop-loss, take-profit и trailing stop.
|
||
- Spot-only логика: покупка базовой монеты за USDT и продажа обратно, без short и без плеча.
|
||
- Live spot-ордеры явно отправляются без плеча: `category=spot`, `isLeverage=0`.
|
||
- Основная стратегия `torch_forecast`: входы и forecast-выходы идут только от свежей экспортированной PyTorch LSTM/GRU модели с успешным quality gate; MACD/RSI/дневная EMA не являются условиями входа в этом режиме. Rebound fallback без модели выключен по умолчанию. Спред, ликвидность, stop-loss, ATR trailing stop, запрет DCA и лимиты экспозиции остаются защитой исполнения и риска.
|
||
- При `TIME_SERIES_TREND_FALLBACK_ENABLED=true` отсутствие принятой свежей Torch-модели включает самостоятельную fallback-стратегию. `TIME_SERIES_FALLBACK_MODE=legacy` разрешён только для paper и даёт многорежимные виртуальные входы; live всегда принудительно использует более строгий `trend_macd`. Отклонённый artifact не используется, fallback явно отражается в readiness и диагностике сигналов, а после появления принятой модели выключается автоматически.
|
||
- Основная стратегия `trend_macd`: вход на `1h`, дневной фильтр тренда на `1d`, long только если цена выше дневной EMA200 и дневная EMA50 выше EMA200.
|
||
- Вход `trend_macd`: MACD на `1h` пересекает signal вверх, цена выше EMA50, RSI в диапазоне `45..65`, спред и ликвидность проходят runtime-фильтры.
|
||
- Выход `trend_macd`: MACD пересекает signal вниз, `1h` свеча закрылась ниже EMA50, сработал стоп `4%` или ATR trailing stop `2.2 ATR`.
|
||
- Риск `trend_macd`: размер позиции считается как `equity * RISK_PER_TRADE_PERCENT / STOP_LOSS_PERCENT`, по умолчанию риск не выше `1%` депозита на сделку.
|
||
- DCA/мартингейл отключены: в режиме `trend_macd` брокер не разрешает вторую позицию по той же паре.
|
||
- Grid, rebound, adaptive learning, Kelly sizing и time-series forecast выключены по умолчанию и не участвуют в принятии решений `trend_macd`.
|
||
- Быстрый режим торговли: отдельный короткий интервал цикла, короткий cooldown после выхода и лимит новых входов в минуту; выходы по риску этим лимитом не блокируются.
|
||
- Защищённый JSON API: equity, cash, PnL, позиции, сделки, сигналы, события, свечи, управление paper-циклом и состоянием обучения.
|
||
- Android-монитор в `android/TradeBotMonitor`: русский мобильный интерфейс для динамического списка Bybit-пар, свечей, Torch/Kelly параметров, WorkManager-расписания удалённого retrain и live-чеклиста.
|
||
- SQLite runtime-хранилище в `runtime/tradebot.sqlite3`.
|
||
- Liveness `/api/health`, readiness `/api/ready`, объединенный mobile snapshot `/api/mobile/snapshot` и Prometheus-compatible `/metrics`.
|
||
- Все приватные API endpoints требуют токен или подтвержденный reverse-proxy user header; health и metrics остаются доступными для локального мониторинга.
|
||
- Hardened Docker Compose для установки на Dell/Linux: non-root user, read-only root filesystem, dropped capabilities, healthcheck и ротация container logs.
|
||
- Live trading guard: live не стартует без `ENABLE_LIVE_TRADING=true`, `LIVE_TRADING_CONFIRM=I_ACCEPT_REAL_RISK` и Bybit API-ключей.
|
||
- Внешний production endpoint сохраняется на `https://tb.kusoft.xyz`; Caddy завершает TLS и проксирует API к контейнеру на loopback.
|
||
|
||
## Источники и принятые параметры
|
||
|
||
Официальная документация Bybit V5 указывает, что единый V5 API использует параметр `category`, включая `spot`; поэтому бот везде запрашивает `category=spot` и не использует futures/linear endpoints: <https://bybit-exchange.github.io/docs/v5/intro>.
|
||
|
||
Список инструментов Bybit Spot берется из `/v5/market/instruments-info`; документация Bybit описывает для Spot поля `baseCoin`, `quoteCoin`, `status`, `priceFilter`, `lotSizeFilter`, `basePrecision` и `minOrderAmt`, поэтому размеры paper/live-ордеров в коде валидируются по данным инструмента: <https://bybit-exchange.github.io/docs/v5/market/instrument>.
|
||
|
||
Популярность пар определяется через `/v5/market/tickers`, потому что Bybit Spot ticker возвращает `turnover24h`, `volume24h`, `bid1Price`, `ask1Price` и `lastPrice`: <https://bybit-exchange.github.io/docs/v5/market/tickers>.
|
||
|
||
Для текущего paper/training-развёртывания используется официальный региональный endpoint `api.bybit.kz`; Bybit перечисляет его в Integration Guidance. `BYBIT_REST_BASE_URL` и `BYBIT_WEBSOCKET_URL` остаются явными настройками, потому что перед будущим live-режимом домен обязан соответствовать площадке выпуска API-ключа: <https://bybit-exchange.github.io/docs/v5/guide>.
|
||
|
||
Лучшие bid/ask берутся из `/v5/market/orderbook`; документация Bybit описывает `GET /v5/market/orderbook` с `category=spot`: <https://bybit-exchange.github.io/docs/v5/market/orderbook>.
|
||
|
||
WebSocket-стакан использует topic `orderbook.{depth}.{symbol}`; Bybit документирует snapshot/delta-поведение и частоты push для Spot depth 1/50/200/1000: <https://bybit-exchange.github.io/docs/v5/websocket/public/orderbook>.
|
||
|
||
Live market orders используют `/v5/order/create`; Bybit документирует для Spot `orderType=Market`, `side`, `qty`, `category=spot`, а для market buy по умолчанию qty может быть в quote currency через `marketUnit=quoteCoin`: <https://bybit-exchange.github.io/docs/v5/order/create-order>.
|
||
|
||
Функции уровня коммерческих automated trading systems взяты из проверяемых источников:
|
||
|
||
- Investopedia перечисляет важные свойства algo trading software: real-time market data, low latency, configurability, backtesting, broker/exchange integration, fees/costs и APIs: <https://www.investopedia.com/articles/active-trading/090815/picking-right-algorithmic-trading-software.asp>.
|
||
- Investopedia отдельно указывает, что automated trading systems задают правила entry/exit/money management, но требуют мониторинга и несут риск mechanical failures и over-optimization: <https://www.investopedia.com/articles/trading/11/automated-trading-systems.asp>.
|
||
- QuantInsti описывает типовой путь разработки: стратегия, backtesting, paper trading, затем live trading, плюс GUI, order management и risk management: <https://www.quantinsti.com/articles/automated-trading-system/>.
|
||
- Hochreiter и Schmidhuber описали LSTM как recurrent neural network architecture для последовательностей; обучение LSTM/GRU в проекте выполняется локально через PyTorch, а Dell исполняет только прошедшие quality gate экспортированные JSON-веса без PyTorch runtime: <https://direct.mit.edu/neco/article/9/8/1735/6109/Long-Short-Term-Memory>.
|
||
|
||
Я не могу подтвердить, что эта стратегия будет прибыльной. Источники выше описывают технические свойства и риски автоматической торговли, но не гарантируют прибыль.
|
||
|
||
## Быстрый старт локально
|
||
|
||
```powershell
|
||
python -m venv .venv
|
||
.venv\Scripts\Activate.ps1
|
||
pip install -r requirements.txt
|
||
Copy-Item .env.example .env
|
||
python -m crypto_spot_bot.main
|
||
```
|
||
|
||
Liveness: <http://127.0.0.1:8787/api/health>
|
||
|
||
## Локальное обучение PyTorch LSTM
|
||
|
||
Обучение запускается на основной Windows-машине, а Dell остается для исполнения торгового цикла. PyTorch нужен только на машине обучения; в JSON экспортируются веса, а runtime на Dell считает inference обычным Python-кодом:
|
||
|
||
```powershell
|
||
.\.venv\Scripts\python.exe -m pip install torch --index-url https://download.pytorch.org/whl/cpu
|
||
.\.venv\Scripts\python.exe tools\train_torch_recurrent_forecaster.py `
|
||
--limit 3000 `
|
||
--architectures lstm `
|
||
--lookbacks 64 `
|
||
--hidden-sizes 64,96 `
|
||
--layers 2 `
|
||
--dropouts 0.15 `
|
||
--horizon 3 `
|
||
--horizons 1,3,6,12 `
|
||
--context-symbols BTCUSDT,ETHUSDT `
|
||
--epochs 70
|
||
```
|
||
|
||
Новый artifact версии 6 обучается как торговая multi-task multi-horizon модель: вход включает доходности, форму свечи, объем, ATR%, realized volatility, RSI/MACD/EMA slopes, 4h/24h rolling trend, дневные EMA-признаки, BTC/ETH cross-asset признаки и числовые признаки текущего шаблона пары. Для каждой точки симулируется вход по open следующей свечи; затем до каждого горизонта проверяется, что было достигнуто раньше — take-profit или stop-loss. Денежная цель равна чистому log-PnL при первом барьере либо закрытии по горизонту после комиссий и проскальзывания. Вторая цель — вероятность `P(TP before SL)`. Если одна OHLC-свеча касается обоих барьеров, разметка консервативно считает stop-loss первым. Модель прогнозирует горизонты `3/6/12/24` и quantile-оценки `q10/q50/q90` чистого результата.
|
||
|
||
Последний tail (`--holdout-window`, по умолчанию 1000 samples на символ) полностью исключается из training и early stopping. Между train/validation/holdout оставляется purge по максимальному forecast horizon. Threshold walk-forward и guard работают только на этом untouched holdout; calibration и guard криптографически привязаны к SHA-256 конкретного model artifact. В каждом walk-forward fold торговать могут только пары, которые получили жизнеспособный порог на предшествующей train-части; общий порог больше не возвращает в портфель нестабильные пары.
|
||
|
||
Файл из `TIME_SERIES_LSTM_MODEL_PATH` читается ботом автоматически, если `TIME_SERIES_FORECAST_ENABLED=true`. В стратегии `torch_forecast` экспортированная PyTorch LSTM/GRU модель является единственным направляющим сигналом для входа и forecast-выхода. Экспортированные модели появляются в dashboard как `PyTorch LSTM` или `PyTorch GRU`; старый легкий reservoir LSTM-кандидат и все встроенные не-torch прогнозы удалены.
|
||
|
||
Локальный retrain на Windows запускает PyTorch trainer, пишет лог в `runtime/torch_retrain.log` и защищается от параллельных запусков:
|
||
|
||
```powershell
|
||
powershell -ExecutionPolicy Bypass -File tools\run_torch_retrain.ps1
|
||
```
|
||
|
||
Для удалённого запуска с телефона или с бота используется Windows training agent. Бот на `tb.kusoft.xyz` хранит очередь заданий, а Windows-машина сама подключается к интернету, забирает задания, обучает модель и загружает артефакты обратно:
|
||
|
||
```powershell
|
||
powershell -ExecutionPolicy Bypass -File tools\install_windows_training_agent.ps1 -ApiAuth "<TRADEBOT_TRAINING_TOKEN>" -StartNow
|
||
```
|
||
|
||
Установщик сохраняет worker-токен через Windows DPAPI, удаляет его старую plaintext-копию из пользовательского окружения и включает постоянный запуск агента. С правами администратора используется Scheduled Task с watchdog; без повышения прав — штатный ярлык в пользовательской папке Startup. Сервер выдаёт каждой попытке 10-минутную возобновляемую lease; зависшая попытка автоматически возвращается в очередь, а устаревший процесс не может загрузить артефакты по старой lease.
|
||
|
||
По умолчанию Windows-agent обучает одну pooled PyTorch LSTM на динамическом наборе пар и `4000` часовых свечах на пару. Базовый профиль использует lookback `64`, hidden size `64`, два recurrent-слоя, dropout `0.20`, до `50` эпох, seed-ensemble `7/19`, три validation-fold и AdamW с learning rate `0.0007`/weight decay `0.0005`. Untouched holdout и quality gate не ослабляются. Параметр задания `pooled=false` включает независимые модели по парам; `architectures=gru` оставлен только как явная экспериментальная опция. Параметры можно переопределить через env: `TORCH_RETRAIN_SYMBOLS`, `TORCH_RETRAIN_LIMIT`, `TORCH_RETRAIN_LOOKBACKS`, `TORCH_RETRAIN_ARCHITECTURES`, `TORCH_RETRAIN_HIDDEN_SIZES`, `TORCH_RETRAIN_LAYERS`, `TORCH_RETRAIN_DROPOUTS`, `TORCH_RETRAIN_HORIZON`, `TORCH_RETRAIN_HORIZONS`, `TORCH_RETRAIN_CONTEXT_SYMBOLS`, `TORCH_RETRAIN_FEATURES`, `TORCH_RETRAIN_SEED`, `TORCH_RETRAIN_ENSEMBLE_SEEDS`, `TORCH_RETRAIN_SELECTION_FOLDS`, `TORCH_RETRAIN_LEARNING_RATE`, `TORCH_RETRAIN_WEIGHT_DECAY`, `TORCH_RETRAIN_EPOCHS`, `TORCH_RETRAIN_PATIENCE`, `TORCH_RETRAIN_INTERVAL`, `TORCH_RETRAIN_ENV`.
|
||
|
||
Loss и выбор гиперпараметров учитывают after-cost trading utility, ошибку ожидаемого чистого PnL, quantile-loss и focal BCE для события `TP before SL`, а не только MAE направления цены. В каждом walk-forward fold вероятность успеха калибруется Platt-моделью исключительно на train-части; затем на этой же train-части выбираются глобальные и per-symbol пороги, которые применяются к test-части. Для выбора порога требуется минимум 24 непересекающиеся сделки, а финальный quality gate по-прежнему требует не менее 30 OOS-сделок. Калибратор не имеет fallback на единичные сделки: если минимальная статистика не набрана, кандидат получает `calibration_insufficient` и не может пройти gate.
|
||
|
||
Основной decision horizon — `12h`, дополнительные горизонты — `3/6/12/24`. Размеры обучающих барьеров берутся из `STOP_LOSS_PERCENT` и `TAKE_PROFIT_PERCENT`, а round-trip cost — из fee/slippage настроек. Threshold search оценивается тем же execution replay со stop-loss, take-profit, ATR trailing и forecast-exit, который используется в walk-forward. `holdout_skill` остаётся только в финальном отчёте и никогда не участвует в фильтрации входов или подборе порогов.
|
||
|
||
Внутри recurrent модели используются exportable attention pooling и LayerNorm. После recurrent-контекста добавлена нелинейная GELU-проекция и две отдельные экспортируемые головы: одна для ожидаемого PnL/quantiles, вторая для `P(TP before SL)`. Принятый bundle загружается агентом через защищённый API `tb.kusoft.xyz`, проходит серверную проверку SHA-256/guard/calibration и атомарно становится активным на Dell.
|
||
|
||
## Docker
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
docker compose up -d --build
|
||
docker compose logs -f tradebot
|
||
```
|
||
|
||
Локальная проверка: `http://127.0.0.1:8787/api/health`; внешний адрес: `https://tb.kusoft.xyz`.
|
||
|
||
На Dell проект использует `python:3.12-slim`, без Node.js build step. Runtime-данные лежат в bind mount `./runtime:/app/runtime`; корневая файловая система контейнера read-only, процесс работает как UID/GID 1000, а container logs ротируются по `10 MiB × 3`. Порт по умолчанию привязан к `127.0.0.1`; для текущей схемы с Caddy на отдельном LAN-хосте в серверном `.env` задаётся `TRADEBOT_BIND_ADDRESS=0.0.0.0`, чтобы сохранить `https://tb.kusoft.xyz`.
|
||
|
||
## Основные env-параметры
|
||
|
||
```env
|
||
TRADING_MODE=paper
|
||
STARTING_BALANCE_USDT=100
|
||
TRADEBOT_BIND_ADDRESS=127.0.0.1
|
||
BYBIT_REST_BASE_URL=https://api.bybit.kz
|
||
BYBIT_WEBSOCKET_URL=wss://stream.bybit.kz/v5/public/spot
|
||
AUTO_SELECT_SYMBOLS=true
|
||
TOP_SYMBOLS_COUNT=12
|
||
SYMBOLS=
|
||
STRATEGY_MODE=torch_forecast
|
||
BASE_INTERVAL=60
|
||
TREND_INTERVAL=D
|
||
TREND_KLINE_LIMIT=260
|
||
LOOP_INTERVAL_SECONDS=5
|
||
FAST_TRADING_ENABLED=false
|
||
FAST_LOOP_INTERVAL_SECONDS=1
|
||
FAST_ENTRY_COOLDOWN_SECONDS=20
|
||
MAX_ENTRIES_PER_MINUTE=12
|
||
WEBSOCKET_ENABLED=true
|
||
MIN_SIGNAL_CONFIDENCE=0.64
|
||
PATTERN_ANALYSIS_ENABLED=true
|
||
PATTERN_SCORE_WEIGHT=0.18
|
||
LEARNING_ENABLED=true
|
||
LEARNING_LOOKBACK_TRADES=120
|
||
LEARNING_MIN_SAMPLES=3
|
||
LEARNING_MAX_ADJUSTMENT=0.12
|
||
LEARNING_MAX_POSITION_MULTIPLIER=1.6
|
||
MIN_POSITION_USDT=1
|
||
MAX_POSITION_USDT=8
|
||
MAX_SYMBOL_EXPOSURE_USDT=25
|
||
MAX_TOTAL_EXPOSURE_USDT=75
|
||
MAX_OPEN_POSITIONS=24
|
||
MAX_POSITIONS_PER_SYMBOL=6
|
||
GRID_TRADING_ENABLED=false
|
||
GRID_ENTRY_CONFIDENCE=0.58
|
||
GRID_BUY_ZONE=0.45
|
||
GRID_MAX_POSITION_USDT=8
|
||
REBOUND_TRADING_ENABLED=true
|
||
REBOUND_ENTRY_CONFIDENCE=0.55
|
||
REBOUND_MIN_PROBABILITY=0.55
|
||
REBOUND_MAX_POSITION_USDT=6
|
||
KELLY_SIZING_ENABLED=true
|
||
KELLY_FRACTION=0.25
|
||
KELLY_MAX_FRACTION=0.20
|
||
RISK_PER_TRADE_PERCENT=0.01
|
||
RISK_GUARD_ENABLED=true
|
||
RISK_SYMBOL_GUARD_ENABLED=false
|
||
RISK_RECENT_TRADE_WINDOW=20
|
||
RISK_MAX_CONSECUTIVE_LOSSES=4
|
||
RISK_MIN_RECENT_PROFIT_FACTOR=0.85
|
||
RISK_REDUCE_MULTIPLIER=0.50
|
||
ATR_TRAILING_MULTIPLIER=2.2
|
||
TREND_RSI_MIN=45
|
||
TREND_RSI_MAX=65
|
||
TIME_SERIES_FORECAST_ENABLED=true
|
||
TIME_SERIES_MIN_CANDLES=120
|
||
TIME_SERIES_FORECAST_HORIZON=3
|
||
TIME_SERIES_MIN_EDGE_PERCENT=0.10
|
||
TIME_SERIES_MIN_PROBABILITY_UP=0.47
|
||
TIME_SERIES_MIN_CONFIDENCE=0.4
|
||
TIME_SERIES_MAX_ADJUSTMENT=0.08
|
||
TIME_SERIES_LSTM_ENABLED=true
|
||
TIME_SERIES_LSTM_MODEL_PATH=runtime/lstm_forecaster.json
|
||
TIME_SERIES_PROBE_ENABLED=true
|
||
TIME_SERIES_PROBE_MIN_EDGE_PERCENT=0.02
|
||
TIME_SERIES_PROBE_MIN_PROBABILITY_UP=0.55
|
||
TIME_SERIES_PROBE_SIZE_MULTIPLIER=0.40
|
||
TIME_SERIES_REBOUND_FALLBACK_ENABLED=false
|
||
TIME_SERIES_TREND_FALLBACK_ENABLED=true
|
||
TIME_SERIES_FALLBACK_MODE=legacy
|
||
TIME_SERIES_REQUIRE_QUALITY_GATE=true
|
||
TIME_SERIES_REQUIRE_FRESH_MODEL=true
|
||
TIME_SERIES_MODEL_MAX_AGE_HOURS=48
|
||
MARKET_TICKER_MAX_AGE_SECONDS=45
|
||
STOP_LOSS_PERCENT=0.04
|
||
TAKE_PROFIT_PERCENT=0.035
|
||
TRAILING_STOP_PERCENT=0.015
|
||
MIN_HOLD_SECONDS=180
|
||
ENTRY_COOLDOWN_SECONDS=180
|
||
MAX_DAILY_DRAWDOWN_USDT=6
|
||
TAKER_FEE_RATE=0.001
|
||
SLIPPAGE_RATE=0.0003
|
||
```
|
||
|
||
## Быстрая торговля
|
||
|
||
Быстрый режим включается через web-переключатель или через `FAST_TRADING_ENABLED=true`. Тогда фактический цикл принятия решений берется из `FAST_LOOP_INTERVAL_SECONDS`, а cooldown после закрытия позиции — из `FAST_ENTRY_COOLDOWN_SECONDS`. Параметр `MAX_ENTRIES_PER_MINUTE` ограничивает только новые покупки; продажи по stop-loss, take-profit, trailing stop и другим правилам выхода не блокируются этим лимитом.
|
||
|
||
Для быстрого режима рекомендуется оставлять `WEBSOCKET_ENABLED=true`: WebSocket дает частые рыночные обновления, а REST используется как периодическая сверка. Я не могу подтвердить, что быстрый режим повысит прибыльность; он только уменьшает техническую задержку реакции стратегии.
|
||
|
||
## Live-режим
|
||
|
||
Live-режим специально заблокирован. Для включения нужны все значения:
|
||
|
||
```env
|
||
TRADING_MODE=live
|
||
ENABLE_LIVE_TRADING=true
|
||
LIVE_TRADING_CONFIRM=I_ACCEPT_REAL_RISK
|
||
BYBIT_API_KEY=...
|
||
BYBIT_API_SECRET=...
|
||
LIVE_ORDER_MAX_USDT=10
|
||
LIVE_ORDER_FILL_TIMEOUT_SECONDS=20
|
||
LIVE_RECONCILIATION_INTERVAL_SECONDS=30
|
||
LIVE_PROTECTIVE_STOP_ENABLED=true
|
||
TRADEBOT_API_TOKEN=
|
||
TRADEBOT_TRAINING_TOKEN=
|
||
TRUSTED_PROXY_USER_HEADER=
|
||
HOLD_SIGNAL_SAMPLE_SECONDS=60
|
||
STORAGE_RETENTION_DAYS=30
|
||
```
|
||
|
||
Live-исполнение ведет журнал order intent до отправки, подтверждает фактические fills через Bybit executions/order history, записывает фактическую цену/количество/комиссию, периодически сверяет wallet и открытые ордера и блокирует новые входы при расхождении. После подтвержденной покупки создается биржевой spot TP/SL stop-order; если защитный ордер создать не удалось, позиция немедленно закрывается. Перед первым использованием реальных средств этот контур все равно необходимо проверить на Bybit testnet с API-ключом без права вывода.
|
||
|
||
## API
|
||
|
||
- `GET /api/health` — healthcheck.
|
||
- `GET /api/status` — статус бота, account snapshot, позиции.
|
||
- `GET /api/markets` — пары, ticker, свечи, инструменты.
|
||
- `GET /api/trades` — последние сделки.
|
||
- `GET /api/signals` — последние сигналы стратегии.
|
||
- `GET /api/events` — события.
|
||
- `GET /api/config` — безопасная конфигурация без секретов.
|
||
- `POST /api/config/fast-trading` — включение/выключение быстрой торговли из dashboard.
|
||
- `POST /api/control/start` — старт цикла.
|
||
- `POST /api/control/stop` — остановка цикла.
|
||
- `GET /metrics` — Prometheus-compatible метрики.
|
||
|
||
## Проверка
|
||
|
||
```bash
|
||
python -m pip install -r requirements-dev.txt
|
||
python -m pytest
|
||
```
|