diff --git a/docs/claude-master-design-20260415-atm-trading.md b/docs/claude-master-design-20260415-atm-trading.md deleted file mode 100644 index 6906a43..0000000 --- a/docs/claude-master-design-20260415-atm-trading.md +++ /dev/null @@ -1,149 +0,0 @@ -# Design: ATM — Automated Trading Monitor (M2D Strategy) - -Generated by /office-hours on 2026-04-15 -Branch: master -Repo: /workspace/atm (greenfield) -Status: APPROVED -Mode: Builder (personal live-trading tool, high-stakes) - -## Problem Statement - -User trades the M2D strategy on DIA (TradeStation chart with custom indicator) with execution on TradeLocker US30 CFD (prop firm account). Same strategy also applies to GLD → XAUUSD. Bridging signal source (TradeStation Windows app) with execution (TradeLocker web) currently requires user to watch both screens for 4 hours per evening. Goal: bot detects the trigger signal automatically and notifies user via Telegram/Discord with chart screenshot + SL/TP levels so user can execute the trade in TradeLocker. - -## Strategy M2D — Full Spec - -**Setup:** TradeStation, 3-minute chart, DIA (or GLD) symbol, custom indicator "M2D MAPS" that renders a horizontal strip of colored dots below the price panel. Dots are indexed by time, y-position is fixed. - -### BUY sequence (sequential in time, rightmost N dots): -1. **Turquoise dot** — 15-minute buy trigger -2. **Dark green dot** — 3-minute sell -3. **Light green dot** — 3-minute buy → **TRIGGER** - -At trigger: -- Execute BUY on TradeLocker, instrument US30 CFD -- Stop Loss 0.6% -- Volume 0.1 lots maximum -- TP1, TP2, SL are drawn automatically as horizontal lines on the TradeStation chart after entry -- User manual lifecycle: at TP1 close half, move SL to ~breakeven; at TP2 close remaining half - -### SELL sequence (mirror): -1. **Yellow dot** — 15-minute sell (red 15min candle) -2. **Dark red dot** — 3-minute buy -3. **Light red dot** — 3-minute sell → **TRIGGER** - -Same size (0.1 lots), same SL %, same TP management. - -### Instrument mapping (intentional asymmetry): -- DIA chart (TradeStation) ↔ US30 CFD (TradeLocker) -- GLD chart (TradeStation) ↔ XAUUSD CFD (TradeLocker) - -### Trading window: -- NY open first 2 hours + NY close last 2 hours -- RO summer time: 16:30-18:30 and 21:00-23:00 -- Typical frequency: 1 trade per evening - -## Constraints - -- **Prop firm account on TradeLocker.** Faza 2 (auto-execution) requires reading prop TOS first — many prop firms prohibit automation or detect robotic timing patterns. -- No API on TradeLocker. No signal export on TradeStation for compiled custom indicator. -- Bot runs on the same Windows machine as TradeStation. Cross-machine (RDP/VNC) screenshot adds latency and fragility. - -## Premises (agreed) - -1. Screenshot + visual detection is the only viable bridge. -2. Notification-first (Faza 1) is the right sequencing. Zero-click MVP removes all financial bug risk. -3. M2D MAPS dot strip has stable y-position on fixed TradeStation layout → ROI color sampling is the right detection method. -4. DIA→US30 price divergence is acceptable risk (user's judgment, has been trading this pairing live). -5. Bot runs on the same Windows machine as TradeStation. - -## Recommended Approach — B: Structured Service with Dry-Run and Audit Log - -Python package on Windows, structured for clean extension to Faza 2. - -### Components: -- **Detector core:** `mss` screenshot of TradeStation window (located by title via `pygetwindow`) → crop M2D MAPS ROI → scan rightmost N dot positions → classify each by closest-color match with tolerance → feed into state machine that tracks 3-dot sequences (turquoise→dark-green→light-green = BUY trigger; yellow→dark-red→light-red = SELL trigger). -- **Level extractor:** after trigger, scan chart region for horizontal colored lines (SL/TP1/TP2). Convert pixel y to price via calibration of y-axis scale. -- **Calibration tool (Tkinter):** interactive — user clicks on each dot color sample, captures RGB + tolerance, clicks on ROI corners, captures y-axis price references. Writes to `config.toml`. -- **Dry-run mode:** runs detector against a folder of saved screenshots (recorded during normal operation). Shows what notification WOULD have been sent for each. Used to validate new color thresholds or strategy tweaks without live risk. -- **Notifier abstraction:** interface with Discord webhook and Telegram bot implementations. Sends: annotated screenshot + decoded SL/TP1/TP2 prices + signal type (BUY/SELL) + timestamp. -- **Audit log (JSONL):** every detection cycle — timestamp, detected dots, classification, decision, notification sent y/n. Replayable, debuggable. -- **Scheduler:** Windows Task Scheduler entry, auto-start/stop at 16:30 / 18:30 / 21:00 / 23:00 local time (summer/winter offset aware). - -### Structure: -``` -atm/ -├── pyproject.toml -├── config.toml # populated by calibration tool -├── src/atm/ -│ ├── detector.py # screenshot + color classification + state machine -│ ├── levels.py # SL/TP1/TP2 pixel-to-price extraction -│ ├── notifier/ -│ │ ├── __init__.py # abstract Notifier -│ │ ├── discord.py -│ │ └── telegram.py -│ ├── audit.py # JSONL logger -│ ├── calibrate.py # Tkinter UI -│ ├── dryrun.py # replay on saved screenshots -│ └── main.py # orchestration + scheduler hooks -├── samples/ # saved screenshots for dry-run corpus -└── logs/ # JSONL audit -``` - -### Detection algorithm (core loop): -1. Every 1 second during trading window: - - Locate TradeStation window - - If not foreground or minimized, log + skip - - Screenshot M2D MAPS ROI (fixed offsets from window bounds) - - For rightmost N=5 dot positions, sample center pixel, classify to nearest labeled color within tolerance - - Update rolling window of last 10 dots with their timestamps - - Evaluate state machine: did the last 3 classified dots (within a bounded time window) complete a BUY or SELL sequence? - - If trigger fired AND not already fired for this bar: extract SL/TP1/TP2 levels, send notification, log, mark fired. - -### Anti-duplicate logic: -- Each trigger dot is keyed by (x-pixel position at capture, color). Once fired, stored in "recently fired" set with 10-minute TTL. Prevents re-fire if same dot persists across cycles. - -### Sanity guards: -- If classification confidence (color distance) low for 3+ cycles in a row → push "bot lost sight" alert to user. Layout may have changed. -- If TradeStation window not found for 60 seconds → push "bot cannot find chart" alert. - -## Open Questions (non-blocking) - -- Exact color tolerance values — determined during calibration session, not a design question. -- GLD/XAUUSD: same M2D indicator on GLD chart? Assume yes, confirm during calibration. -- Multi-symbol monitoring — single window switched manually, or two TradeStation windows side by side? Defer; v1 = single chart at a time, user switches manually. - -## Success Criteria (Faza 1) - -- Over 20 live trading sessions, bot detects ≥95% of signals user also spotted manually. -- Zero false-positive notifications during the bot's first 5 sessions (tune tolerances aggressively). -- Notification delivered within 3 seconds of trigger dot appearing. -- Audit log lets user reproduce "why was no notification sent" for any missed signal. - -## Distribution Plan - -Personal tool, single user. No distribution channel needed — runs locally on user's Windows box. Git repo at `/workspace/atm`. `pyproject.toml` + `pip install -e .` for local dev. No CI/CD; user's own `scheduled task` starts/stops it. - -## Risk Flag — Faza 2 (deferred) - -Before extending to auto-execution in TradeLocker: -1. Read prop firm TOS (search for "EA", "automation", "bot", "copy trading", "external signal"). If prohibited, **Faza 2 is off the table** — tool stays notification-only. -2. If permitted, implement via Playwright browser automation against TradeLocker web UI. -3. Add human-like click timing randomization (100-400ms jitter) to avoid robotic detection. -4. Dry-run mode then becomes: "click coordinates resolved, action NOT sent" — user reviews the intended click before enabling live. - -## Next Steps (concrete) - -1. Init `/workspace/atm` as Python project. `pyproject.toml`, basic structure. -2. Build calibration tool first. Without calibrated config, nothing works. -3. Record 20-30 sample screenshots across several trading sessions (can start this today — doesn't need any code yet; just `mss` screenshot on a 5-second timer dumping to disk). -4. Build detector + state machine. Validate against recorded screenshots in dry-run mode. -5. Wire Discord webhook first (simpler than Telegram bot). Test end-to-end on live session. -6. Add audit log. -7. Schedule Windows task for trading hours. - -## What I noticed about how you think - -- You explicitly asked for dry-run before writing a line of code. "Să verific dacă vrea să apese corect, fără să apese efectiv." That's not a common instinct for someone building their own tool; it's the instinct of someone who has already had something break expensively. -- You phased the project yourself — "faza 2 după ce mă conving că merge." That's the right ordering and you arrived at it unprompted. -- When I challenged the API premise, you answered with specifics: the indicator is custom, the account doesn't support API. You knew the constraint, not guessed it. -- You flagged the prop account almost casually at the end. A lot of builders would have skipped that detail. It turned out to be the most important constraint in the entire design. diff --git a/docs/happy-swinging-mccarthy.md b/docs/happy-swinging-mccarthy.md deleted file mode 100644 index 1d31520..0000000 --- a/docs/happy-swinging-mccarthy.md +++ /dev/null @@ -1,43 +0,0 @@ -# Plan: ATM Eng Review — Findings Applied - -## Context - -User ran `/plan-eng-review` on `partitioned-honking-unicorn.md` (ATM trading monitor, Faza 1). Eng review complete. All 4 decisions resolved, obvious fixes applied, plan file updated in place. - -## Where the changes live - -The reviewed plan (with all eng-review edits) is at: -**`/home/claude/.claude/plans/partitioned-honking-unicorn.md`** - -Test plan artifact at: -**`~/.gstack/projects/romfast-workspace/claude-master-eng-review-test-plan-20260415-212932.md`** - -## What changed in the reviewed plan - -### 4 decisions (AskUserQuestion) -1. **Bar flicker** → debounce depth=1 (configurable); screenshot in alert = visual check. -2. **Phase A entry price** → dropped; Phase A is direction + screenshot only; user puts manual 0.6% SL in TradeLocker; Phase B sends real levels from chart. -3. **Notifier blocking** → fire-and-forget worker threads per backend, bounded queue, retry + dead-letter. -4. **Alert SPoF** → Discord + Telegram parallel from day 1. - -### Obvious fixes (stated, applied) -- Exhaustive state transition table (default-noise rule, SELL mirror explicit, phase-skip handling). -- Python 3.11+ pin → drop `tomli`, use stdlib `tomllib`. -- Windows symlink replaced by `configs/current.txt` marker file. -- New `vision.py` shared module (ROI/hash/interp/Hough). -- `@dataclass Config` with load-time validation. -- DPI check added to calibrate + README note. - -### Test coverage -Expanded from state-machine-only to: every module + 1 E2E replay harness. Acceptance gate unchanged (precision=100%, recall≥95% on labeled corpus). - -## Verification (post-implementation) - -Run the full verification checklist from `partitioned-honking-unicorn.md` (sections 1-9). Specifically: -- `pytest tests/` — all new unit tests + E2E replay pass. -- `atm dryrun ./samples` hits acceptance gate. -- Live 2-session test: both Discord and Telegram fire; kill one mid-session and confirm the other still delivers + dead-letter file gets the failed alert. - -## Status - -**CEO + ENG CLEARED.** No further reviews required before implementation. Design + DX reviews properly skipped (no UI scope; personal single-user tool). Run `/ship` after implementation. diff --git a/docs/image.png b/docs/image.png deleted file mode 100644 index be291ad..0000000 Binary files a/docs/image.png and /dev/null differ diff --git a/docs/partitioned-honking-unicorn.md b/docs/partitioned-honking-unicorn.md deleted file mode 100644 index b2eca75..0000000 --- a/docs/partitioned-honking-unicorn.md +++ /dev/null @@ -1,258 +0,0 @@ -# Plan: ATM — Automated Trading Monitor (M2D, Faza 1) — ENG-REVIEWED - -**Source plan:** `/home/claude/.claude/plans/swirling-drifting-starfish.md` -**CEO plan artifact:** `~/.gstack/projects/romfast-workspace/ceo-plans/2026-04-15-atm-trading.md` -**Eng review mode:** FULL_REVIEW (4 decisions made, 0 unresolved) -**Design doc:** `~/.gstack/projects/romfast-workspace/claude-master-design-20260415-atm-trading.md` (APPROVED) -**Eng test plan:** `~/.gstack/projects/romfast-workspace/claude-master-eng-review-test-plan-20260415-212932.md` - ---- - -## Context - -User trades M2D strategy manually on DIA (TradeStation) with execution on TradeLocker US30 CFD (prop firm). Same strategy on GLD → XAUUSD. 4h/evening dual-screen monitoring. Faza 1 goal: bot auto-detects M2D trigger, sends Discord/Telegram notification with screenshot + SL/TP1/TP2 levels; user executes manually in TradeLocker. Faza 2 (auto-execution) deferred until prop firm TOS verified and Faza 1 proven over 20+ sessions. - -**Review changed two things from the original plan:** -1. **State machine spec corrected.** Original "last 3 consecutive non-gray dots" is wrong. Actual M2D is phased: Phase 1 arming (turquoise → gray/dark-green) → Phase 2 trigger (light-green). -2. **Levels extraction corrected.** Original plan had levels.py extracting SL/TP at trigger. But those lines only appear on TradeStation chart *after* user enters trade in TradeLocker. Corrected to two-phase: spec-math at trigger, chart-scan after entry. - -Plus 5 accepted expansions (labeled corpus, level fallback, layout canary, trade journal, TOS checklist). - ---- - -## Approach: B (Structured Python service, dry-run, audit log) + CEO-reviewed additions - -Runs on Windows machine alongside TradeStation. `mss` screenshots → ROI color-sample on M2D MAPS strip → phased state machine → Discord webhook + Telegram bot → JSONL audit + trade journal → dry-run replay against labeled corpus. - ---- - -## State Machine Spec (corrected + exhaustive) - -States: -- `IDLE` -- `ARMED_BUY` — turquoise seen -- `PRIMED_BUY` — turquoise + at least one dark-green seen -- `ARMED_SELL` — yellow seen -- `PRIMED_SELL` — yellow + at least one dark-red seen - -**Default rule:** any (state, event) pair not listed below → stay in current state, no action, log as `noise`. - -Transitions — BUY side: - -| From | Event | To | Action | -|------|-------|-----|--------| -| IDLE | turquoise | ARMED_BUY | log arm_ts | -| IDLE | yellow | ARMED_SELL | log arm_ts (sell) | -| IDLE | dark-green / dark-red / light-green / light-red / gray | IDLE | noise (log phase-skip if light-green/light-red) | -| ARMED_BUY | gray | ARMED_BUY | persist | -| ARMED_BUY | turquoise | ARMED_BUY | refresh arm_ts | -| ARMED_BUY | dark-green | PRIMED_BUY | log prime_ts | -| ARMED_BUY | yellow | ARMED_SELL | opposite rearm | -| ARMED_BUY | dark-red | ARMED_BUY | ignore (minority noise) | -| ARMED_BUY | light-green | IDLE | **skip detected** — no FIRE, log phase_skip | -| ARMED_BUY | light-red | IDLE | skip detected, log | -| PRIMED_BUY | dark-green | PRIMED_BUY | accumulate | -| PRIMED_BUY | dark-red | PRIMED_BUY | ignore (minority noise) | -| PRIMED_BUY | **light-green** | IDLE | **FIRE BUY**, lockout(BUY)=4min | -| PRIMED_BUY | light-red | IDLE | skip detected (wrong trigger) | -| PRIMED_BUY | gray | IDLE | **COOLED** — signal dead, log | -| PRIMED_BUY | turquoise | ARMED_BUY | rearm fresh | -| PRIMED_BUY | yellow | ARMED_SELL | opposite rearm | - -SELL side mirrors exactly: swap turquoise↔yellow, dark-green↔dark-red, light-green↔light-red, BUY↔SELL. - -Notes: -- No time-based TTL on ARMED/PRIMED. State persists until trigger fires, cooled by gray after PRIMED, opposite-color rearm, or process restart (Windows Task Scheduler stops bot at session end → natural session-boundary reset). -- Cooling rule: "gray after dark-green" = signal racit (user's term). Gray during ARMED_BUY (before any dark-green) is OK. -- After FIRE: 4-minute lockout per-direction. BUY lockout doesn't block SELL and vice versa. Single timestamp per direction. -- Opposite-color-Phase-1 triggers rearm to opposite side (captures direction flip). -- Phase-skip (arming color → trigger color with no phase-2 step) → IDLE, no FIRE, logged. Would be legitimate only if indicator collapses phases, which it doesn't per observed behavior. - ---- - -## Detection Details - -- **Loop interval:** 5 seconds (36 cycles per 3-min bar; stays well inside notification-latency target). -- **Rightmost-dot detection:** scan ROI from right edge leftward, find first non-background pixel cluster → that's the rightmost dot. Don't hardcode x-pixel positions (chart scrolls; hardcoded positions drift). -- **Debounce:** configurable `debounce_depth` in config.toml (default `1` — single-read acceptance). Increase if future sessions show mid-bar color flicker. Screenshot-in-notification is the user's visual verification on top. -- **Rolling window:** keep last 20 classified dots with their detection timestamps. State machine consumes the newest *accepted* (post-debounce) dot per cycle. -- **Classification:** nearest-color match in RGB Euclidean distance, per-color tolerance from calibration. Report confidence = `1 - distance_nearest / distance_second_nearest`. Log confidence every cycle. If all distances > tolerance → `UNKNOWN`, state unchanged. - ---- - -## Levels Extraction (two-phase, simplified) - -**Phase A — at trigger (immediate alert to Discord + Telegram):** -- No entry-price compute. No spec-math SL/TP. User places a manual 0.6% SL in TradeLocker at entry; actual TP1/TP2/SL come in Phase B from the chart. -- Notification: `🟢 BUY signal DIA→US30 | 22:47:03` + annotated screenshot (detected dot highlighted). - -**Phase B — after user trades (chart-scan confirmation):** -- After Phase A fires, detector keeps watching the chart ROI for horizontal colored lines (red=SL, green=TP1/TP2). -- When lines appear (user has entered trade in TradeLocker and TradeStation drew them) → scan y-pixels via Hough + color mask, convert via y-axis calibration → send second alert to both channels: `✅ Levels: SL=484.35 | TP1=485.20 | TP2=485.88`. -- If chart-line scan times out (no lines in 10 min) → silent (user didn't trade). -- If only 2 lines detected (user didn't set TP2 or line not rendered yet) → partial-result alert. -- Phase B overlap with next signal: guarded by per-direction lockout + Phase-B completion flag; a new FIRE cannot issue until prior Phase B closes (timeout or success). - ---- - -## Dedup / Lockout - -- Time-based lockout: after any FIRE, block re-fire for 4 minutes (one 3-min bar + 1 min safety). -- Tracked per-direction: BUY lockout doesn't block SELL. -- Stored as single timestamp per direction (not pixel-keyed). - ---- - -## Observability - -- **Heartbeat:** every 30 min to a separate Discord thread (not main alerts channel): `🟢 22:00 alive | 0 triggers | confidence avg 0.85 | chart OK`. Silence >35 min = watchdog concern (user notices). -- **Layout canary:** every 60 cycles (5 min), hash a stable reference region (axis labels, chart border). Stored baseline in config. On significant divergence (>threshold) → `⚠️ Layout changed — auto-paused, recalibrate` to alerts channel. Bot pauses detection until operator acknowledges (touch a pause-file or restart). -- **Low-confidence alert:** 3+ consecutive cycles with confidence below threshold → `⚠️ Bot lost sight` (already in original plan). -- **Window-lost alert:** TradeStation window not found for 60s → `⚠️ Cannot find chart`. -- **Audit JSONL:** per-cycle, daily rotation (`logs/YYYY-MM-DD.jsonl`), fields: `{ts, window_found, roi_ok, rightmost_dot_color, confidence, state, transition, trigger, notified, reason}`. - ---- - -## Files to Create - -- `/workspace/atm/pyproject.toml` — Python 3.11+ required. Deps: `mss`, `opencv-python`, `numpy`, `requests`, `pygetwindow`, `pywin32` (DPI + window capture), `rich` (CLI), `pillow` (screenshot annotation). **No `tomli` — use stdlib `tomllib`.** -- `/workspace/atm/config.toml` — populated by calibration tool (ROI coords, per-color RGB + tolerance, `debounce_depth`, y-axis scale, canary-region baseline hash, Discord webhook URL, Telegram bot token + chat_id) -- `/workspace/atm/src/atm/config.py` — **[ENG-REVIEW]** `@dataclass Config` with `Config.load(path)` that validates on load (RGB tuples, positive tolerances, both notifier credentials present, y-axis 2-point pair). Fail fast at startup. -- `/workspace/atm/src/atm/vision.py` — **[ENG-REVIEW]** shared primitives: ROI crop, perceptual hash, pixel-to-price linear interp, Hough line detection with color mask. Used by detector/canary/levels to avoid drift. -- `/workspace/atm/src/atm/detector.py` — screenshot loop, rightmost-dot scan, color classification, rolling window, debounce -- `/workspace/atm/src/atm/state_machine.py` — explicit phased state machine (spec above), exhaustive transition table -- `/workspace/atm/src/atm/levels.py` — Phase B chart-scan only (Phase A entry-price compute removed after ENG-REVIEW) -- `/workspace/atm/src/atm/canary.py` — layout fingerprint hash + drift check + auto-pause -- `/workspace/atm/src/atm/notifier/__init__.py` — abstract `Notifier` protocol: `send_alert()`, `send_heartbeat()`, `send_levels_confirm()` -- `/workspace/atm/src/atm/notifier/fanout.py` — **[ENG-REVIEW]** `FanoutNotifier` wraps N backends, each with its own worker thread + bounded queue (size 50, drop-oldest on overflow) + retry with exponential backoff + dead-letter file on total failure. Main loop never blocks. -- `/workspace/atm/src/atm/notifier/discord.py` — webhook POST, annotated screenshot upload (multipart) -- `/workspace/atm/src/atm/notifier/telegram.py` — **[ENG-REVIEW]** built in parallel with Discord (no longer deferred); bot API, photo upload -- `/workspace/atm/src/atm/audit.py` — JSONL logger with daily local-midnight rotation, line-buffered write for crash safety -- `/workspace/atm/src/atm/calibrate.py` — Tkinter: window pick → DPI check → ROI corners → per-color sample → y-axis scale → canary region → save versioned config -- `/workspace/atm/src/atm/labeler.py` — **[EXPANSION]** Tkinter label UI → `labels.json` -- `/workspace/atm/src/atm/dryrun.py` — replay with precision/recall/confusion matrix when labels present -- `/workspace/atm/src/atm/journal.py` — **[EXPANSION]** `atm journal` CLI → `trades.jsonl` -- `/workspace/atm/src/atm/report.py` — **[EXPANSION]** weekly aggregation -- `/workspace/atm/src/atm/main.py` — CLI: `atm calibrate`, `atm label