# bot line 7.0

**Назва:** bot line 7.0

**Короткий опис:** BTC_USDT, лише купівлі. Трансформер на канві 1100 × 8
(160 год до моменту T) кожні 10 хв каже P(T у зоні дна 2.3 %); сигнал
купівлі — сума P за останні 180 хв ≥ 11.74. Тейк 1.9 % від найнижчої ціни
після входу, стоп 2 % від ціни входу, сигнал після закритої угоди без
підйому 2.3 % між сигналами — фейк, крім фейку біля нижчого дна (правило
7.1: тейк від цього дна). Режим — `paper`: бот рахує сигнал і
угоди, ордерів не шле.

Походження — дослідження 36, етапи 19–21
([`research/36-canvas600-pivots/36-canvas600-pivots.md`](../../research/36-canvas600-pivots/36-canvas600-pivots.md)):
модель — прогін `BUY_pos`, епоха 9; сигнал і правила угоди — етап 20;
правило 7.1 і тейк 1.9 % — етап 21. Одобрено 2026-09-30.

## Як отримати бота

Сесія, якій потрібен бот, читає цей файл і бере теку `bots/bot-line-7.0/`:

| Файл | Що це |
| ---- | ----- |
| [`bot-line-7.0.md`](bot-line-7.0.md) | цей файл: опис, параметри, правила торгівлі, збірка з даних |
| [`config.json`](config.json) | усі параметри моделі, сигналу й угоди; sha256 файлів моделі |
| [`model/BUY_pos_epoch_09.pt`](model/BUY_pos_epoch_09.pt) | ваги трансформера `Future` (PyTorch `state_dict`) |
| [`model/assigner.json`](model/assigner.json) | правило кластера 2 (канал 8 канви) |
| [`bot_line_7.py`](bot_line_7.py) | запускач: `signal`, `replay`, `check` |
| [`results/replay_val.json`](results/replay_val.json) | прогін бота на val з правилом 7.1 — еталон для звірки |
| [`results/replay_fresh.json`](results/replay_fresh.json) | прогін бота на даних датаколектора після val з правилом 7.1 |
| [`../../tests/test_bot_line_7.py`](../../tests/test_bot_line_7.py) | тести бота |

Бот використовує модулі проєкту (розділ «Зібрати бота з даних
датаколектора» → Залежності): переносити разом із ними або запускати в
цьому проєкті. Перед роботою `bot_line_7.py` звіряє sha256 файлів моделі
з `config.json` і зупиняється, якщо вони інші.

## Параметри

| Що | Значення |
| -- | -------- |
| пара | `BTC_USDT` |
| крок моменту T | 10 хв (T кратне 600 с, кінець запису 100 с) |
| вікно канви | 160 год до T, 1100 рядків × 8 каналів |
| канали | 7 фіч `клас210х10-1х7д/д` + P(кластер 2) |
| модель | `Future`: латки по 10 рядків, dim 64, 3 шари, 4 голови, dropout 0.1, 1 логіт → sigmoid |
| вихід | P(T у зоні дна 2.3 % ноги ≥ 7 год) |
| сигнал | `S(T)` = сума P останніх 18 наявних моментів T до T включно (180 хв) |
| поріг | `S(T) ≥ θ = 11.74064453125` (квантиль 0.97 ряду `S` на val) |
| тейк | 1.9 % від найнижчої ціни після входу (`trade.take` = 0.019) |
| стоп | 2 % від ціни входу |
| фейк | підйом 2.3 % між сигналами |
| правило 7.1 | дно нижче ціни сигналу менш ніж на 1 % (`trade.rule71_near` = 0.01), пік вищий за дно більш ніж на 2.3 % |
| витрати | комісія 0.05 % + сліпедж 0.02 % за бік |
| гроші | `Decimal`, старт $1000, кожна угода на всі гроші |

## Правила торгівлі

1. **Лише купівля** (довга позиція), одна позиція за раз.
2. **Вхід** — у момент T із `S(T) ≥ θ` і без відкритої позиції: купівля
   ціною open наступного запису 100 с після T (`rules/04-trading.md` →
   Бектест: виконання наступним баром).
3. **Тейк-профіт** — найнижча ціна від входу (ціна входу і close кожного
   запису 100 с після неї) × 1.019. Ціна після входу падає — тейк
   спускається за нею; росте — тейк стоїть.
4. **Стоп-лос** — ціна входу × 0.98, нерухомий. Позиція без стопу не
   відкривається (`rules/04-trading.md` → Ризик).
5. **Вихід** — перший запис 100 с, чий close дійшов до тейку чи стопу:
   продаж ціною open наступного запису.
6. **Підтвердження** — сигнали купівлі, поки позиція відкрита, нічого не
   міняють.
7. **Фейк** — позиції немає і вже була закрита угода: сигнал ігнорується,
   якщо найбільший close між попереднім сигналом (будь-яким, і під час
   угоди теж) і поточним менший за close поточного × 1.023. До першої
   угоди фейків немає.
7.1. **Фейк біля нижчого дна** — перевіряється лише для сигналу, який
   правило 7 назвало фейком. Пік — найбільший close записів 100 с від
   попереднього сигналу (без нього) до поточного (з ним); дно —
   найменший close записів після піку до поточного сигналу (без нього).
   Якщо пік > дно × 1.023, дно нижче за close сигналу і
   `close сигналу − дно < 0.01 × дно` — угода відкривається за правилами
   2, 4, 5 і 8, а тейк рахується від дна: min(дно, найнижча ціна після
   входу) × 1.019. Інакше сигнал — фейк.
8. **Витрати** — комісія 0.05 % і сліпедж 0.02 % на кожен бік угоди;
   результат угоди `r = (1 − 0.0007) · (продаж / купівля) · (1 − 0.0007) − 1`.

## Зібрати бота з даних датаколектора

Бот збирається з самих посекундних даних — нічого з датасетів дослідження
йому не треба.

### 1. Дані

Датаколектор (`/Users/at/projects/data_collector_candle/engine`) пише
добові CSV:

```
<root>/<рік>/<місяць>/BTC_USDT/1S_<YYYY-MM-DD>_BTC_USDT.csv
```

- Одна секунда — один рядок; дата в назві — місцева дата збирача (файл
  починається о 21:00 / 22:00 UTC попередньої доби), тому бот читає файли
  з запасом у добу з кожного боку і сам ріже секунди за `timestamp` (UTC).
- Беруться 10 колонок (`rules/scripts/data_source.py` → `SOURCE_COLUMNS`):
  `timestamp, price_avg, const_resist, resist_plus, resist_minus,
  const_support, support_plus, support_minus, vol_buy, vol_sell`; решта
  колонок збирача не читається. Дублікати `timestamp` відкидаються.
- `root` за замовчуванням — робоча копія `data/` проєкту; інша тека —
  ключем `--root` (лише читання).
- Потрібно секунд: 160 год + 1 год до першого T і до останнього T.

### 2. Хід від секунд до рішення

1. **Записи** — секунди → записи 100 с і 30 с (`bars30.bars_of_frame`):
   сума кожної колонки, open — перша `price_avg` запису, close — остання.
2. **Момент T** — кінець запису 100 с, T кратне 10 хв; T без запису, що
   кінчається рівно в T, пропускається.
3. **Канва 1100 × 7** — записи 100 с від T − 160 год до T стискаються в
   1100 рядків; на кожному рядку сім фіч `клас210х10-1х7д/д` з секунд
   рядка, нормування — лише на свою канву (`canvas1100.canvas_of`).
4. **Канал 8: P(кластер 2)** — канва 600 того самого вікна на записах 30 с
   (`canvas600_data.canvas_of`), правило кластера 1 з `model/assigner.json`
   ковзним вікном з 3 рядків (`canvas600_c1.rule_rows`, `cover`),
   P(кластер 2) = 1 − p, на рядки канви 1100 — рядок бере рядок канви 600,
   що закінчився не пізніше за нього (`canvas1100_future.rows_from`). Усе
   це — `canvas1100_buy.sample_of`.
5. **Округлення** — канва переводиться у float16 і назад (так зберігались
   канви, на яких модель вчилась і міряна).
6. **Модель** — `Future` з `model/BUY_pos_epoch_09.pt`, sigmoid логіта →
   P(T), округлена до float16 (так зберігалась видача, на якій підібрано θ;
   без цього сума на порозі зсувається на крок — вхід 24.08 04:40 замість
   04:50).
7. **Сигнал** — `S(T)` = сума P останніх 18 **наявних** моментів до T
   включно: момент без запису 100 с пропускається, а не рахується нулем
   (на val таких 94 з 5359); купівля — `S(T) ≥ 11.74064453125`. T без
   запису — сигналу немає.
8. **Угода** — правила вище (`canvas1100_buy_signal.simulate` з
   `Exits(..., trail=True)`, стоп 0.02, `rule71` = 0.01 —
   `canvas1100_buy_signal.rescue`).

### 3. Залежності

Python 3.11, `numpy`, `pandas`, `torch` (перевірено: torch 2.11.0, numpy
2.4.4, pandas 3.0.2). Модулі проєкту, які бот імпортує:

| Модуль | Що бере бот |
| ------ | ----------- |
| [`rules/scripts/data_source.py`](../../rules/scripts/data_source.py) | `load_day` — читання добового CSV |
| [`research/35-leg-fullness/scripts/bars30.py`](../../research/35-leg-fullness/scripts/bars30.py) | `day_files`, `bars_of_frame`, `read_seconds`, канва на записах |
| [`research/35-leg-fullness/scripts/classes30.py`](../../research/35-leg-fullness/scripts/classes30.py) | фічі `клас210х10-1х7д/д` |
| [`research/36-canvas600-pivots/scripts/canvas1100.py`](../../research/36-canvas600-pivots/scripts/canvas1100.py) | `canvas_of` — канва 1100 × 7 |
| [`research/36-canvas600-pivots/scripts/canvas600_data.py`](../../research/36-canvas600-pivots/scripts/canvas600_data.py), [`canvas600_c1.py`](../../research/36-canvas600-pivots/scripts/canvas600_c1.py) | канва 600 і правило кластера 1 |
| [`research/36-canvas600-pivots/scripts/canvas1100_future.py`](../../research/36-canvas600-pivots/scripts/canvas1100_future.py), [`canvas1100_future_net.py`](../../research/36-canvas600-pivots/scripts/canvas1100_future_net.py) | `rows_from`, мережа `Future` |
| [`research/36-canvas600-pivots/scripts/canvas1100_early.py`](../../research/36-canvas600-pivots/scripts/canvas1100_early.py), [`canvas1100_buy.py`](../../research/36-canvas600-pivots/scripts/canvas1100_buy.py) | `start_of`, `cluster2_rows`, `sample_of` |
| [`research/36-canvas600-pivots/scripts/canvas1100_buy_signal.py`](../../research/36-canvas600-pivots/scripts/canvas1100_buy_signal.py) | `rolling_sum`, `simulate`, `Exits`, `peaks_of` |

### 4. Команди

```bash
# сигнал у момент T (UTC, кратне 10 хв)
python3 bots/bot-line-7.0/bot_line_7.py signal --at "2026-09-15 16:00"
# бот на відрізку: P кожні 10 хв, сума, угоди, гроші
python3 bots/bot-line-7.0/bot_line_7.py replay --start "2026-08-15 00:00" --end "2026-09-21 05:00" \
    --out bots/bot-line-7.0/results/replay_val.json
# звірка P бота з видачею дослідження на val
python3 bots/bot-line-7.0/bot_line_7.py check --count 24
# угоди заново на сумах P збереженого прогону (без моделі); --rule71 0 — без правила 7.1
python3 bots/bot-line-7.0/bot_line_7.py resim --from bots/bot-line-7.0/results/replay_val.json
# з даних датаколектора напряму
python3 bots/bot-line-7.0/bot_line_7.py signal --at "2026-09-15 16:00" --root /Users/at/DATA
# тести
python3 -m unittest discover -s tests -t tests -p "test_bot_line_7.py"
```

`signal` друкує JSON: `t`, `p` (P у T), `sum`, `theta`, `buy`, `steps`
(скільки наявних моментів у сумі). `replay` і `resim` — гроші, угоди (сигнал,
вхід, вихід, ціни, причина, результат, `rule` — «7.1» чи null, `base` —
дно правила 7.1), фейки, число сигналів, `hold`
(«купив і тримав»: open запису після першого T → close запису останнього
T, ті самі витрати); `--out` — те саме плюс P, сума й close на кожен T.

### 5. Перевірка, що бот зібрано правильно

- `check --count 24` — P бота проти `BUY_pos_val.npz` епохи 9 дослідження
  на 24 рівномірних T val: `max_abs_diff` 0.0, `missing` 0.
- `signal --at "2026-09-15 16:10"` — `sum` 11.98236083984375, `buy` true
  (з `--root /Users/at/DATA` — те саме); у 16:00 — `sum`
  11.64495849609375, `buy` false.
- `replay` на val (15.08 00:00 … 21.09 05:00 UTC) з `--rule71 0` — ті
  самі 8 угод, 32 фейки, 158 моментів із сигналом і $1103.17, що в
  дослідженні 36 (етап 20, «Результат 27»); з правилом 7.1 — 9 угод,
  15 фейків, $1107.36 з тейком 2 % (етап 21, «Результат 28»); з тейком
  бота 1.9 % — 9 угод, 15 фейків, $1100.85.

## Результат на val BTC

`replay` з посекундних даних, 15.08 00:00 … 21.09 05:00 UTC, 5265 моментів
T, з правилом 7.1 і тейком 1.9 % — [`results/replay_val.json`](results/replay_val.json):

| Міра | Значення |
| ---- | -------- |
| гроші | $1000 → $1100.85 (PnL +$100.85); з тейком 2 % — $1107.36; без правила 7.1 — $1097.92 |
| угод | 9, усі тейком, стопів 0; з них за правилом 7.1 — 1 |
| фейків | 15 |
| моментів із сигналом | 158 |
| еталон — ідеальна торгівля купівлями по зігзагу 2.3 % | $2140.27, 12 ніг вгору |
| «купив і тримав» | $1366.30 |

| сигнал і вхід (UTC) | ціна входу | правило | дно 7.1 | вихід | ціна виходу | чому | результат |
| ------------------- | ---------: | ------- | ------: | ----- | ----------: | ---- | --------: |
| 2026-08-16 02:10 | 63046.36 | — | — | 2026-08-17 15:05 | 63921.76 | тейк | 1.25 % |
| 2026-08-24 04:50 | 76950.75 | — | — | 2026-08-24 11:36 | 78367.42 | тейк | 1.70 % |
| 2026-08-28 17:00 | 77788.46 | — | — | 2026-08-30 12:33 | 78850.00 | тейк | 1.22 % |
| 2026-09-02 16:00 | 77292.01 | — | — | 2026-09-03 12:53 | 78506.56 | тейк | 1.43 % |
| 2026-09-04 14:50 | 78692.03 | — | — | 2026-09-06 23:05 | 80220.00 | тейк | 1.80 % |
| 2026-09-07 22:40 | 78863.46 | 7.1 | 78713.21 | 2026-09-09 04:45 | 79184.76 | тейк | 0.27 % |
| 2026-09-10 14:40 | 77251.00 | — | — | 2026-09-11 12:51 | 78041.22 | тейк | 0.88 % |
| 2026-09-11 18:10 | 77072.65 | — | — | 2026-09-14 09:48 | 77980.01 | тейк | 1.04 % |
| 2026-09-15 16:10 | 76325.79 | — | — | 2026-09-16 18:08 | 76503.78 | тейк | 0.09 % |

## Результат на даних датаколектора після val

`replay` на 21.09 05:00 … 30.09 16:10 UTC — дані, яких бот не бачив ні
при навчанні, ні при підборі сигналу (дослідження 36, етап 21):
[`results/replay_fresh.json`](results/replay_fresh.json).

```bash
python3 bots/bot-line-7.0/bot_line_7.py replay --start "2026-09-21 05:00" --end "2026-09-30 16:10" \
    --out bots/bot-line-7.0/results/replay_fresh.json
```

| Міра | Значення |
| ---- | -------- |
| моментів T | 1296 з 1364 (решта — пропуски датаколектора) |
| гроші | $1000 → $1032.92 (PnL +$32.92); з тейком 2 % — $1034.12; без правила 7.1 — $1008.45 |
| угод | 3, усі тейком; з них за правилом 7.1 — 2 |
| фейків | 0 |
| моментів із сигналом | 41 |
| «купив і тримав» | $1035.51 |

| сигнал і вхід (UTC) | ціна входу | правило | дно 7.1 | вихід | ціна виходу | чому | результат |
| ------------------- | ---------: | ------- | ------: | ----- | ----------: | ---- | --------: |
| 2026-09-23 16:40 | 84027.71 | — | — | 2026-09-24 14:28 | 84856.60 | тейк | 0.85 % |
| 2026-09-25 14:50 | 83811.35 | 7.1 | 83261.89 | 2026-09-27 08:16 | 84844.01 | тейк | 1.09 % |
| 2026-09-29 16:30 | 83258.01 | 7.1 | 82621.70 | 2026-09-30 12:31 | 84476.79 | тейк | 1.32 % |

## Обмеження

- Параметри сигналу (вікно, θ, стоп) підібрані на тому самому val, на
  якому міряний результат: 12 ніг зігзага 2.3 %; поза ним бот перевірено
  лише на 9.5 доби даних датаколектора після val (3 угоди).
- Бот проти «купив і тримав» на val програє (+$100.85 проти +$366.30):
  тейк 1.9 % бере малу частину довгих ніг вгору.
- `live` не вмикається: бот не шле ордерів, торгівля — лише `paper`
  (`rules/04-trading.md`).
