9.3 KiB
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):
- Turquoise dot — 15-minute buy trigger
- Dark green dot — 3-minute sell
- 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):
- Yellow dot — 15-minute sell (red 15min candle)
- Dark red dot — 3-minute buy
- 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)
- Screenshot + visual detection is the only viable bridge.
- Notification-first (Faza 1) is the right sequencing. Zero-click MVP removes all financial bug risk.
- M2D MAPS dot strip has stable y-position on fixed TradeStation layout → ROI color sampling is the right detection method.
- DIA→US30 price divergence is acceptable risk (user's judgment, has been trading this pairing live).
- 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:
mssscreenshot of TradeStation window (located by title viapygetwindow) → 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):
- 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:
- 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.
- If permitted, implement via Playwright browser automation against TradeLocker web UI.
- Add human-like click timing randomization (100-400ms jitter) to avoid robotic detection.
- Dry-run mode then becomes: "click coordinates resolved, action NOT sent" — user reviews the intended click before enabling live.
Next Steps (concrete)
- Init
/workspace/atmas Python project.pyproject.toml, basic structure. - Build calibration tool first. Without calibrated config, nothing works.
- Record 20-30 sample screenshots across several trading sessions (can start this today — doesn't need any code yet; just
mssscreenshot on a 5-second timer dumping to disk). - Build detector + state machine. Validate against recorded screenshots in dry-run mode.
- Wire Discord webhook first (simpler than Telegram bot). Test end-to-end on live session.
- Add audit log.
- 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.