docs: reflect Telegram /pause/resume, operating hours, phase-skip backstop, validate-calibration

README gets: operating-hours config + CLI override flags, Telegram command table
with /pause /resume [force] semantics, validate-calibration usage + exit codes,
new audit event reference, phase-skip backstop note, and test count bump.

CLAUDE.md quick reference now lists the new subcommand, CLI flags, and
Telegram commands so future sessions pick them up without re-reading main.py.

TODOS.md marks the 2026-04-17 hang fix, canary drift notification,
phase-skip backstop, operating-hours window, and validate-calibration as
done with commit pointers; adds exchange-calendar holidays as known gap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-18 12:09:44 +03:00
parent 40cc67b4c6
commit 37f0b14468
3 changed files with 108 additions and 6 deletions

View File

@@ -6,14 +6,31 @@ Personal Faza-1 tool for the M2D strategy. Python 3.11+.
```bash ```bash
pip install -e ".[windows]" # Windows: live capture pip install -e ".[windows]" # Windows: live capture
pip install -e . # Linux/macOS: dev/dryrun only pip install -e ".[dev]" # Linux/macOS: dev + tests (WSL: create venv first)
atm calibrate # Tk wizard atm calibrate # Tk wizard
atm debug --delay 5 # one-shot capture + detect atm debug --delay 5 # one-shot capture + detect
atm run --start-at 16:30 --stop-at 23:00 # live session atm validate-calibration samples/calibration_labels.json # offline color gate
atm run --start-at 16:30 --stop-at 23:00 # live session
atm run --tz America/New_York --oh-start 09:30 --oh-stop 16:00 # NYSE window override
atm dryrun samples # corpus gate atm dryrun samples # corpus gate
pytest # run tests pytest -q # 184 tests
``` ```
## Telegram commands (live)
`/ss` `/status` `/pause` `/resume` `/resume force` `/3` (interval min) `/stop`
- `/resume` clears only user pause; Canary drift requires `/resume force`.
- Drift-pause now emits a single Telegram alert (was silent pre-refactor — root cause of the 2026-04-17 hang).
## Operating-hours config
`[options.operating_hours]` in TOML: `enabled`, `timezone` (NYSE local, e.g. `America/New_York`), `weekdays`, `start_hhmm`, `stop_hhmm`. Timezone validated at load; `_tz_cache` reused per tick. Boundary crossings log `market_open` / `market_closed` and notify once. Startup in-window is silent.
## Phase-skip backstop
`[options.alerts] fire_on_phase_skip = true` (default) — ARMED→light_* direct (dark_* missed) still emits a `⚠️ PHASE SKIP` alert using FSM lockout to suppress spam.
## Skill routing ## Skill routing
When the user's request matches an available skill, ALWAYS invoke it using the Skill When the user's request matches an available skill, ALWAYS invoke it using the Skill

View File

@@ -32,7 +32,7 @@ atm/
│ ├── journal.py # trade entries │ ├── journal.py # trade entries
│ ├── report.py # weekly R-multiple PnL │ ├── report.py # weekly R-multiple PnL
│ └── main.py # unified CLI │ └── main.py # unified CLI
├── tests/ # 105 pytest cases ├── tests/ # 184 pytest cases
└── TODOS.md # P1/P2/P3 backlog, Faza 2 items └── TODOS.md # P1/P2/P3 backlog, Faza 2 items
``` ```
@@ -135,13 +135,74 @@ Startup sequence:
7. At `--stop-at` (or `--duration`): **"ATM stopped" ping**, then exit. 7. At `--stop-at` (or `--duration`): **"ATM stopped" ping**, then exit.
Per-cycle behaviour: Per-cycle behaviour:
- Canary drift → auto-pause (logs `paused`, skips detection). Clear by running `atm run` again with the pause-file removed. - Canary drift → auto-pause + **single-shot Telegram alert** (`⚠️ Canary drift=N — monitorizare pauzată`). Clear via `/resume force` in Telegram, or restart with pause-file removed.
- Detector reports UNKNOWN → stays in current state (logged as `noise`). - Detector reports UNKNOWN → stays in current state (logged as `noise`).
- Colour change → full frame saved to `samples/YYYYMMDD_HHMMSS_<color>.png` (for corpus). - Colour change → full frame saved to `samples/YYYYMMDD_HHMMSS_<color>.png` (for corpus).
- FIRE (BUY/SELL, not locked) → annotated PNG saved to `logs/fires/`, attached to the alert, `LevelsExtractor` armed. - FIRE (BUY/SELL, not locked) → annotated PNG saved to `logs/fires/`, attached to the alert, `LevelsExtractor` armed.
- **Phase skip backstop** (`fire_on_phase_skip=true` default) → ARMED → light_red/light_green direct (dark_red/dark_green missed) still emits `⚠️ PHASE SKIP` alert with screenshot. FSM lockout suppresses spam.
- Phase-B complete → "Levels SL=… TP1=… TP2=…" push. - Phase-B complete → "Levels SL=… TP1=… TP2=…" push.
- Heartbeat every `heartbeat_min` minutes. - Heartbeat every `heartbeat_min` minutes.
### Operating hours window
Configure via `[options.operating_hours]` in TOML (source of truth: NYSE local time, timezone-aware so DST is handled automatically):
```toml
[options.operating_hours]
enabled = true
timezone = "America/New_York" # fail-fast validated at config load
weekdays = ["MON", "TUE", "WED", "THU", "FRI"]
start_hhmm = "09:30" # NYSE open
stop_hhmm = "16:00" # NYSE close
```
Out-of-window ticks are skipped (logged only on transition). On boundary crossings the bot emits `market_open` / `market_closed` Telegram status messages exactly once per transition. **Startup in-window does not emit a spurious `market_open` alert.**
CLI overrides (beat TOML):
```
atm run --tz America/New_York --weekdays MON,TUE,WED,THU,FRI --oh-start 09:30 --oh-stop 16:00
```
> `--oh-start / --oh-stop` are **different** from `--start-at / --stop-at`. The `--start-at / --stop-at` pair controls wall-clock session bounds (when the process starts and quits); `--oh-*` controls the NYSE trading window inside the session (what hours detection actually runs). They compose.
### Telegram commands
Send to the bot chat:
| Command | Effect |
|---|---|
| `/ss` or `/screenshot` | Take and send a screenshot now |
| `/status` | State + pause reason + window open/closed |
| `/pause` | Suspend detection (heartbeats continue) |
| `/resume` | Clear user pause only. If Canary is drift-paused it **stays paused** — use `/resume force` |
| `/resume force` | Also clear Canary drift-pause (use after recalibration) |
| `/3` or `/interval 3` | Set auto-screenshot interval to 3 min |
| `/stop` | Stop the scheduler |
Only `allowed_chat_ids` are accepted. After 3 consecutive `401`s the poller enters degraded mode.
### Calibration validation (offline gate)
Validate that the current calibration classifies known-labeled frames correctly **without waiting for a live session**:
```bash
atm validate-calibration samples/calibration_labels.json
```
Input JSON:
```json
[
{"path": "logs/fires/20260417_201500_arm_sell.png", "expected": "yellow", "note": "first arm"},
{"path": "logs/fires/20260417_205302_ss.png", "expected": "dark_red"},
{"path": "logs/fires/20260417_210441_ss.png", "expected": "light_red"}
]
```
Output: per-sample PASS/FAIL with detected color + top-3 candidates by RGB distance + suggestion pixels for misclassifications.
Exit code: `0` if 100% PASS, `1` on any FAIL, `2` on malformed/missing input. Suitable for CI or a pre-`atm run` sanity check.
Keep PowerShell minimized during the session so it doesn't cover TradeStation. Keep PowerShell minimized during the session so it doesn't cover TradeStation.
--- ---
@@ -209,9 +270,27 @@ atm calibrate [--screenshot PATH] [--delay SEC] # Tk wizard
atm debug [--delay SEC] # one-shot capture + detect atm debug [--delay SEC] # one-shot capture + detect
atm label SAMPLES_DIR # Tk labeling atm label SAMPLES_DIR # Tk labeling
atm dryrun SAMPLES_DIR # corpus gate atm dryrun SAMPLES_DIR # corpus gate
atm validate-calibration LABEL_FILE.json # offline color-classification gate
atm run [--duration H] [--start-at HH:MM] [--stop-at HH:MM] [--startup-delay SEC] [--capture-stub] atm run [--duration H] [--start-at HH:MM] [--stop-at HH:MM] [--startup-delay SEC] [--capture-stub]
[--tz TZNAME] [--weekdays MON,TUE,...] [--oh-start HH:MM] [--oh-stop HH:MM]
atm journal [--file PATH] # interactive trade entry atm journal [--file PATH] # interactive trade entry
atm report [--week YYYY-WW] [--file PATH] # weekly summary atm report [--week YYYY-WW] [--file PATH] # weekly summary
``` ```
Exit code: `atm dryrun` exits 0 if gate passes, 1 otherwise. Other commands follow standard convention. Exit codes:
- `atm dryrun` — 0 pass, 1 fail.
- `atm validate-calibration` — 0 all PASS, 1 any FAIL, 2 bad input.
- Others: standard convention.
## Audit log events
Events written to `logs/YYYY-MM-DD.jsonl`. Added by the lifecycle+canary work:
| Event | Payload | When |
|---|---|---|
| `canary_drift_paused` | `distance` | First drift tick after clean; emits Telegram alert |
| `user_paused` | — | `/pause` received |
| `user_resumed` | `was_drift`, `was_user`, `force` | `/resume` or `/resume force` |
| `market_open` / `market_closed` | `reason` | Operating-hours window boundary (once per transition; **not** at startup) |
| `phase_skip_fire` | `direction` | Backstop alert when ARMED→light_* direct |
| `command_error` | `action`, `error` | Dispatch exception (isolated from detection loop) |

View File

@@ -60,6 +60,12 @@ Price overlay (from Telegram commands feature) uses `y_axis` linear interpolatio
## Quality debt ## Quality debt
- [x] **Integration test for run_live loop**: lifecycle async test added in `tests/test_main.py` (IDLE→ARMED→PRIMED auto-poll→FIRE auto-stop). - [x] **Integration test for run_live loop**: lifecycle async test added in `tests/test_main.py` (IDLE→ARMED→PRIMED auto-poll→FIRE auto-stop).
- [x] **Detection-loop hang on canary pause** (2026-04-17 incident): `_drain_cmd_queue` now runs unconditionally; helpers extracted to module scope for testability (commit `c5024ce`).
- [x] **Silent canary drift-pause**: single-shot Telegram alert on `not_paused → paused` (commit `9cf49ca`).
- [x] **Phase-skip backstop**: `fire_on_phase_skip` (default on) emits alert when ARMED→light_* direct (commit `8b53b8d`).
- [x] **Operating hours window**: NYSE-timezone-aware gate with `/pause` `/resume` `/resume force` control (commits `54f5575`, `2386577`).
- [x] **Offline calibration gate**: `atm validate-calibration` replays labeled frames through detector (commit `8bae507`).
- [ ] **Coverage report**: run `pytest --cov=atm --cov-report=term-missing`, aim for ≥ 85% per module. - [ ] **Coverage report**: run `pytest --cov=atm --cov-report=term-missing`, aim for ≥ 85% per module.
- [ ] **Typing strictness**: run `pyright src/` with strict mode, fix reported issues. - [ ] **Typing strictness**: run `pyright src/` with strict mode, fix reported issues.
- [ ] **Perf baseline**: profile one detection cycle on a representative frame; ensure < 100ms so 5s loop has ample headroom. - [ ] **Perf baseline**: profile one detection cycle on a representative frame; ensure < 100ms so 5s loop has ample headroom.
- [ ] **Exchange calendar holidays**: operating_hours doesn't know about NYSE closures (MLK, Thanksgiving, Good Friday). User `/pause`s manually for now.