# SwingTrader — Session Learnings
**Date:** April 26, 2026

---

## What We Built

A fully automated daily TSX swing trade scanning system that:
- Screens the full TSX universe (not just a fixed watchlist) every weekday at 9am MT
- Generates buy/hold/exit signals based on a structured methodology
- Tracks open positions and flags exits automatically
- Writes a dated JSON output file each morning

---

## Session Chronology

### 1. Project Initialization
- Ran `/init` to create `CLAUDE.md` — documents the project for future Claude sessions
- Key insight: this is a **single markdown document project** (`agent_and_tips.md`), not a software app
- CLAUDE.md captures the document structure and frequently referenced trading rules so future sessions don't re-read the full guide

### 2. First Stock Scan (swing_trade_2026-04-26.json)
- Read the full `agent_and_tips.md` guide to extract screening criteria
- Ran 10+ individual web searches — one per ticker (AEM, ABX, WPM, SU, CNQ, ENB, RY, MFC, BMO, TRP)
- Filtered against: RSI 35–65, above 200d EMA, MACD positive, no earnings within 3 days, market cap >$500M, P/E 12–28, D/E <1.5, FCF positive
- Produced a verbose JSON with full rationale, green/red flags, trade plans, and pre-trade checklists per stock
- **This approach worked but was expensive**: re-read 423 lines, ran 10+ searches, wrote a large JSON

**Stocks that passed:**
AEM, ABX, WPM (materials/gold), SU, CNQ, ENB (energy), RY, MFC, BMO (financials), TRP (conditional)

**Stocks excluded and why:**
- CVE: RSI 74 (overbought) + earnings April 30 (within 3 days)
- TD: RSI 72 (overbought)
- TECK.B: RSI 72 (post-earnings run)
- FM: Earnings April 28 (within 3-day window)
- SLF: RSI overbought

### 3. Token & Timeout Optimization
**Problems identified with the first approach:**
| Issue | Impact |
|-------|--------|
| Re-reading 423-line guide every run | ~85% of avoidable token use |
| Searching each ticker individually | Only covers stocks you already know |
| Verbose JSON with prose estimates | Large output, slow to write |
| No position tracking | Can't generate hold/exit signals |

**Solutions implemented:**

**`screen_criteria.json`** — Compact 60-line reference replacing the full guide for daily scans. Contains all filter rules, signal type definitions, screener sources, green/red flags, and sector catalysts. Future sessions read this instead of `agent_and_tips.md`.

**`positions.json`** — Tracks open positions (entry price, stop, targets, days held). The daily scan reads this to generate HOLD/EXIT signals without re-researching stocks already held. Update this file every time you enter or exit a trade.

**Screener-first approach** — Instead of searching individual tickers, query 4 screener sites that pre-filter the full TSX universe:
1. `canada.swingtradebot.com` — Pattern alerts across full TSX
2. `barchart.com/ca/stocks/stocks-screener` — Technical rating rankings
3. `theglobeandmail.com/investing` — Daily TSX breakout lists
4. `tradethatswing.com` — Curated Canadian swing trade candidates

This covers 1,500+ TSX stocks in 4 searches instead of 10 searches covering only known names.

**Token savings achieved:**
| Area | Before | After | Saving |
|------|--------|-------|--------|
| Guide reading | 423-line file | 60-line criteria JSON | ~85% |
| Stock searches | 10 individual queries | 4 screener queries | ~60% |
| JSON output | Verbose with prose | Compact schema, null for unknowns | ~50% |
| Position review | Full re-research | Read positions.json | ~30% |

### 4. Daily Scan Prompt
Created `daily_scan_prompt.md` as a copy-paste ready prompt. Key design decisions:
- No placeholders — Claude resolves today's date automatically from session context
- References files by absolute path (screen_criteria.json, positions.json)
- Hard rule: do NOT read agent_and_tips.md (criteria already in screen_criteria.json)
- Hard rule: do NOT search individual tickers (screeners only, max 2 follow-up lookups)
- Compact JSON schema with `null` for missing data instead of prose estimates
- Target: complete scan in 4–6 searches total

### 5. Automation — Shell Script + Crontab

**Why not a remote scheduled agent (CCR)?**
Remote CCR agents run in Anthropic's cloud sandbox — they have zero access to local files on `/media/usb/`. A GitHub repo would be required to bridge the gap, but a prior attempt at this (the DayTrader project) confirmed the remote/GitHub approach is more complex than it's worth for a local use case.

**Local approach chosen:** Shell script invoked by crontab — full access to USB drive, no GitHub dependency.

**`run_daily_scan.sh` key design decisions:**
- Modelled exactly after the working DayTrader script pattern
- Uses a **heredoc** (`<< 'PROMPT'`) to pass the prompt — avoids all shell quoting/escaping issues that would arise from reading a file or using a variable with backticks
- `export HOME=/home/neilm` — required for cron, which runs with a stripped environment and can cause auth failures without it
- `--dangerously-skip-permissions` — required for unattended runs (no interactive permission prompts)
- `--allowedTools "Bash,Read,Write,WebSearch,WebFetch"` — explicit tool allowlist
- USB mount check at startup — writes to `/tmp/swingtrader_error.log` if drive not found, then exits cleanly
- Appends to `daily_run.log` — single persistent log file

**Crontab entry:**
```
0 9 * * 1-5 /media/usb/claude/SwingTrader/run_daily_scan.sh
```
- `0 9` = **9:00am MDT** (system timezone: America/Edmonton)
- `* * 1-5` = every weekday
- **Important:** cron uses the system timezone, not UTC. This system is `America/Edmonton (MDT, -0600)`. The original entry was `0 15 * * 1-5` under the mistaken assumption that cron used UTC — it would have fired at 3pm MDT. Fixed 2026-04-27 to `0 9`.

### 7. positions.json Auto-Enrichment (2026-04-27)

**Problem:** The original `positions.json` schema required manually entering company name, stop loss, T1, T2, and T3 for each new position — all of which are either lookups or standard math.

**Solution:** Added Step 1b to `daily_scan_prompt.md`. When the scan detects a position missing `company`, `stop_loss_cad`, `target_1_cad`, or `target_2_cad`, it:
1. Searches for the current ATR14 for that ticker
2. Calculates: stop = entry − (2.5 × ATR14), T1 = 1:1, T2 = 1:2, T3 = null (trailing)
3. Fills in company name from the same search
4. Writes the enriched data back to `positions.json` before the rest of the scan runs

**Minimum position entry is now just 4 fields:**
```json
{ "ticker": "TSX:XYZ", "entry_date": "YYYY-MM-DD", "entry_price_cad": 0.00, "shares": 0 }
```

### 6. DayTrader Removal
The predecessor project (`/media/usb/claude/DayTrader/`) was deleted along with its crontab entry. It failed due to complexity around the remote CCR + GitHub approach. SwingTrader supersedes it with a simpler, proven local architecture.

---

## File Structure

```
/media/usb/claude/SwingTrader/
├── CLAUDE.md                        # Project guide for Claude sessions
├── agent_and_tips.md                # Full trading methodology (source of truth)
├── screen_criteria.json             # Compact daily scan criteria (read this, not the full guide)
├── positions.json                   # Open positions tracker — UPDATE when entering/exiting trades
├── daily_scan_prompt.md             # Copy-paste prompt for manual scans
├── run_daily_scan.sh                # Automated scan script (runs via crontab)
├── daily_run.log                    # Cron output log
├── learnings.md                     # This file
└── swing_trade_YYYY-MM-DD.json      # Daily output files (one per trading day)
```

---

## Daily Workflow

**Automated (9am MT every weekday):**
Crontab triggers `run_daily_scan.sh` → Claude runs the scan → writes `swing_trade_YYYY-MM-DD.json`

**Your morning routine:**
1. Open `swing_trade_YYYY-MM-DD.json` (today's file)
2. Review `signals.exits` — action any stops or time stops immediately
3. Review `signals.holds` — check if T1/T2 hit; tighten stops if indicated
4. Review `signals.new_buys` — ranked by setup score; enter your preferred setups
5. Review `signals.watchlist` — stocks one confirmation away from a valid setup
6. After entering or exiting any trade, update `positions.json`

**Manual scan (optional):**
Paste the prompt from `daily_scan_prompt.md` directly into a Claude Code session

---

## Key Rules (from agent_and_tips.md)

| Rule | Value |
|------|-------|
| Max risk per trade | 1% of account |
| Stop loss method | 2.5x ATR (14-period) below entry |
| Min risk:reward | 1:2 |
| T1 exit | 30% of position at 1:1 R:R, move stop to breakeven |
| T2 exit | 50% of position at 1:2 R:R, trail stop on runner |
| T3 exit | Final 20% at 1:3 R:R or trailing stop |
| Time stop | Exit after 3 days with no movement toward T1 |
| Max positions | 5–8 simultaneously |
| Cash reserve | 20–30% always available |
| Earnings rule | No entry within 3 trading days of earnings |
| Liquidity floor | 100k+ shares/day, $1M+ dollar volume/day |
| Long only | Price must be above 200-day EMA |

---

## Known Limitations & Things to Watch

- **Screener search results depend on web availability** — if SwingTradeBot or Globe and Mail are slow/down, the scan may return fewer candidates than usual. The log file will show what happened.
- **positions.json requires manual entry/exit updates** — the script cannot know when you actually traded. Add a 4-field stub when you enter a position; the next scan enriches it automatically. Remove or move entries to `closed_positions` when you exit.
- **Winter DST shift** — cron runs at 9:00am local time year-round (system timezone: America/Edmonton). In winter (MST) this is still 9am MST, which is before TSX open (9:30am ET = 7:30am MT). No adjustment needed.
- **USB drive must be mounted** — if the drive isn't mounted when cron fires at 15:00 UTC, the script exits and logs to `/tmp/swingtrader_error.log`. No data is lost; the scan just doesn't run that day.
- **P/E and D/E ratios** — web searches often don't return these reliably. The JSON output uses `null` for unconfirmed values. Always verify P/E and D/E on TMX Money or TradingView before entering a position.
