Files
atm/README.md
Claude Agent 34fde8328c docs: rewrite README to match current CLI + workflow
Reflects everything that shipped after the initial teammate pass:
calibrate region-select flow, startup delays, auto-focus, canary startup
check, --start-at/--stop-at, atm debug, samples/ auto-save, fires/
annotated screenshots, Task Scheduler setup, current troubleshooting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:06:12 +00:00

217 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ATM — Automated Trading Monitor
Personal Faza-1 tool for the **M2D strategy**. Watches the M2D MAPS colored-dot strip on a TradeStation chart, runs a phased state machine (ARMED→PRIMED→FIRE), pushes Discord + Telegram alerts with an annotated screenshot on BUY/SELL. You execute the trade manually in TradeLocker.
No auto-execution. Faza 2 (auto-execute) is blocked on prop-firm TOS audit — see `docs/phase2-prop-firm-audit.md`.
---
## Project layout
```
atm/
├── configs/ # calibration outputs + current.txt marker
├── logs/
│ ├── YYYY-MM-DD.jsonl # per-cycle audit log, rotates at local midnight
│ ├── dead_letter.jsonl # alerts that failed after retries
│ ├── fires/ # annotated screenshots, one per BUY/SELL trigger
│ └── calibrate_capture_*.png / debug_*.png # gitignored debug artifacts
├── samples/ # full frames saved automatically on each colour change
├── src/atm/ # package
│ ├── config.py # frozen dataclass + TOML loader
│ ├── vision.py # ROI crop, phash, pixel↔price, Hough, connected-components
│ ├── state_machine.py # 5-state phased FSM, per-direction lockout
│ ├── detector.py # capture → crop → find rightmost dot → classify → debounce
│ ├── canary.py # layout phash drift watchdog with pause-file gating
│ ├── levels.py # Phase-B SL/TP line extraction
│ ├── notifier/ # FanoutNotifier + Discord webhook + Telegram bot
│ ├── audit.py # line-buffered JSONL, daily rotation
│ ├── calibrate.py # Tk wizard (region-select + click-sample)
│ ├── labeler.py # Tk UI → labels.json
│ ├── dryrun.py # replay corpus, precision/recall gate
│ ├── journal.py # trade entries
│ ├── report.py # weekly R-multiple PnL
│ └── main.py # unified CLI
├── tests/ # 105 pytest cases
└── TODOS.md # P1/P2/P3 backlog, Faza 2 items
```
---
## Install
Python 3.11+ required. Clone, then:
```bash
pip install -e ".[windows]" # Windows: live capture + window focus
pip install -e . # Linux / macOS: dev / dryrun only (no live)
atm --help
```
`[windows]` pulls `mss`, `pygetwindow`, `pywin32`.
---
## Calibration
One-time per chart layout. Run on the machine that will do live capture.
```powershell
atm calibrate # 3s default countdown; use --delay 10 if you want more time
```
Flow:
1. Dialog: substring of the chart window title (e.g. `TradeStation` or `DIA`). Stored in config for later auto-focus.
2. **"Ready?" message** → click OK → 3s countdown in terminal. Alt-tab TradeStation to the foreground and minimize anything covering it.
3. Full-desktop screenshot is captured and shown in a scaled Tk window.
4. **Drag a rectangle** over the chart (include the M2D MAPS strip). Enter = confirm. Esc = cancel.
5. Step-by-step clicks on the selected region:
- M2D MAPS strip: top-left + bottom-right corners
- One click on each of: turquoise, yellow, dark_green, dark_red, light_green, light_red, gray dot + chart background (8 total — "Skip" if a colour isn't currently visible)
- Chart area: top-left + bottom-right (for Phase-B line detection)
- Two known price levels on the y-axis (pixel y → enter price)
- Canary region: top-left + bottom-right on a stable UI element (axis label, title bar)
6. **Save** → writes `configs/YYYY-MM-DD-HHMM.toml` + marker `configs/current.txt`. Pulls Discord/Telegram creds from env (`ATM_DISCORD_URL`, `ATM_TG_TOKEN`, `ATM_TG_CHAT`) if set; otherwise `REPLACE_ME` placeholders — edit the TOML manually.
What gets written:
- `chart_window_region = {x, y, w, h}` — virtual-desktop absolute rectangle. Runtime capture crops the same box, so the window must stay in that position.
- `dot_roi`, `chart_roi`, `canary.roi` — coords relative to the selected region.
- Per-colour RGB (sampled via saturation-snap within 15px of the click, mean of 5x5 around the snapped centre).
- `y_axis` linear-interp pair.
- `canary.baseline_phash` of the canary ROI.
Sampling tips:
- Click colours that are **actually present** in the current dot history. If a colour isn't visible, skip it — `atm dryrun` will tell you if the skipped value doesn't match real dots.
- Default tolerance is 60 for dot colours, 25 for background. Tighten via TOML after dryrun if misclassifications creep in.
---
## Smoke-test after calibration
```powershell
atm debug --delay 5
```
Captures one frame. Saves `logs/debug_full_<ts>.png`, `logs/debug_dot_roi_<ts>.png`, `logs/debug_annotated_<ts>.png`. Prints:
```
window_found: True
dot_found: True
rgb: (114, 114, 114)
classified: gray distance=24 confidence=0.79
accepted: True color=gray
```
Open the annotated PNG: yellow rectangle = `dot_roi`, red circle = detected dot. The circle should land on the ACTUAL rightmost colored dot in the M2D MAPS strip. If not:
- Circle mid-strip → wrong window under the capture region (bring TradeStation to front).
- Circle on a non-dot UI element → `dot_roi` boundaries capture too much; recalibrate narrower.
- `color=None` + `UNKNOWN` → tolerances too tight OR sampled RGBs don't match real dots; recalibrate clicking on actual dots.
---
## Live run
```powershell
# Today's session 16:3023:00 Romania local
atm run --start-at 16:30 --stop-at 23:00
# Indefinite
atm run
# Fixed duration (hours)
atm run --duration 2
# Linux / headless smoke (reads samples/*.png in a loop)
atm run --capture-stub --duration 0.05
```
Startup sequence:
1. Wall-clock wait until `--start-at` (if set).
2. `pygetwindow.activate()` on the first window matching `cfg.window_title` — brings TradeStation to the foreground automatically (restores if minimised).
3. 5s countdown (`--startup-delay`).
4. Capture first frame + canary check. Status (`drift=X/Y` or `capture_failed`) is included in the startup ping.
5. **"ATM started" ping** on Discord + Telegram.
6. Main loop: every `loop_interval_s` (default 5s) — capture → canary → detect → state machine → maybe notify → maybe Phase-B.
7. At `--stop-at` (or `--duration`): **"ATM stopped" ping**, then exit.
Per-cycle behaviour:
- Canary drift → auto-pause (logs `paused`, skips detection). Clear by running `atm run` again with the pause-file removed.
- Detector reports UNKNOWN → stays in current state (logged as `noise`).
- 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.
- Phase-B complete → "Levels SL=… TP1=… TP2=…" push.
- Heartbeat every `heartbeat_min` minutes.
Keep PowerShell minimized during the session so it doesn't cover TradeStation.
---
## After the session
```powershell
atm label samples # Tk UI — label each saved frame with true dot colour
atm dryrun samples # replay through detector + FSM; exits 0 if precision=100%, recall>=95%
```
If the gate fails, tune per-colour `tolerance` in `configs/<current>.toml`, or recalibrate colour samples that didn't match. Re-run `atm dryrun` until it passes. Only then do you trust live signals.
Trade record-keeping:
```powershell
atm journal # interactive entry after a real trade
atm report --week 2026-16 # weekly win rate + R PnL + slippage
```
---
## DPI / multi-monitor notes
- Calibration region is virtual-desktop-absolute; runtime capture uses the same rectangle. **Don't move the TradeStation window** after calibrating. Canary will catch drift and pause automatically.
- Changing DPI scaling or moving to a different monitor with different DPI → recalibrate.
- RDP / virtual desktops: `mss` can return black frames over RDP. Run locally on the same physical machine as TradeStation.
---
## Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| `capture_failed` in startup ping | `chart_window_region` references coords off-screen (different monitor layout) | Recalibrate. |
| Startup canary `drift=X/8` with X >> 8 | Wrong window is in the capture region | Make sure TradeStation is the window at `cfg.chart_window_region`. Relaunch. |
| `WARN: no window contains 'xxx'` at startup | `cfg.window_title` substring matches nothing | Edit `window_title` in TOML to a substring that's unique to TradeStation. |
| No alerts even after trigger ought to fire | Check `logs/YYYY-MM-DD.jsonl` for `event=tick` entries — are colours accepted? Is `trigger` ever set? | If always UNKNOWN → tolerances too tight. If `trigger` but `locked=true` → lockout from prior fire, normal. |
| Discord OK, Telegram silent (or vice versa) | `logs/dead_letter.jsonl` contains failed alerts with error | Fix credentials in TOML, restart. |
| Debug circle on mid-strip instead of right edge | Anti-aliasing bridges dots in the mask | Already fixed via erosion+connected-components — ensure `git pull` is current. |
| Wizard window is tiny / image not visible | Tk geometry default on Windows | Already fixed — `git pull`. Image is scaled to fit screen. |
---
## Windows Task Scheduler (production)
For hands-off daily runs surviving reboots:
1. Task Scheduler → Create Task → name `ATM M2D Monitor`
2. **General**: "Run only when user is logged on", "Run with highest privileges"
3. **Triggers**: New → Daily, Start `16:30`
4. **Actions**: New → Program `C:\path\to\python.exe`, Arguments `-m atm run --stop-at 23:00`, Start in `D:\PROIECTE\atm`
5. **Conditions**: uncheck "Start only if AC power" (if laptop)
6. **Settings**: "If task runs longer than 7 hours → stop"
Click-right → Run to test manually. Manual DST-change check twice a year (Mar / Oct first week).
---
## Quick command reference
```
atm calibrate [--screenshot PATH] [--delay SEC] # Tk wizard
atm debug [--delay SEC] # one-shot capture + detect
atm label SAMPLES_DIR # Tk labeling
atm dryrun SAMPLES_DIR # corpus gate
atm run [--duration H] [--start-at HH:MM] [--stop-at HH:MM] [--startup-delay SEC] [--capture-stub]
atm journal [--file PATH] # interactive trade entry
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.