This report documents the development, backtesting, and risk hardening of the ClosingDay Trader's new trailing stop exit strategy. The trailing stop replaces the previous fixed-target exit, allowing winning trades to capture extended moves while maintaining disciplined risk management.
ClosingDay Trader is a momentum breakout system that scans a curated 100-symbol universe for intraday price momentum combined with volume spikes. When a stock breaks out, the system enters via a limit order with a bracket exit structure (target + stop loss).
| Price Range | $5.00+ (no upper cap — position sizing naturally limits exposure) |
| 30-Bar Momentum | Close must be ≥2% above close 30 bars ago |
| Volume Spike | Current bar volume ≥20% above 50-bar average volume |
| Time of Day Filter | 09:30-11:00 ET and 14:30-16:00 ET only (midday filtered out) |
| Relative Strength | Symbol must outperform SPY by ≥1% intraday |
| ATR Stop | 1.5x ATR(14) below entry — volatility-adapted stop distance |
Before (Fixed Target): When price hit the +3% target, the bracket's limit leg filled and the position closed immediately. Parabolic moves beyond +3% were left on the table.
After (Trailing Stop): When price hits the +3% target, the bracket is cancelled and replaced with a 1% trailing stop. The stop slides up as price climbs, always staying 1% below the high water mark. When the stock inevitably reverses, the trailing stop triggers, locking in profit. Minimum locked-in profit = TARGET% - TRAIL% = 3% - 1% = 2%.
| # | Condition | Action | Dashboard Badge |
|---|---|---|---|
| 0 | Trailing stop already active | Skip all rules — let it ride | TRAILING |
| 1 | Price ≥ target price | Cancel bracket, place 1% trailing stop | TARGET |
| 2 | Price ≤ stop price (watchdog) | Force close — independent of bracket status | — |
| 3 | Bracket legs all expired/cancelled | Market sell — naked position detected | — |
| 4 | Held ≥30 min, gain <2% | Market sell — momentum dead | STALE 32m |
| 5 | Held ≥45 min (any gain) | Market sell — cut it | STALE 45m |
| — | None of the above | Bracket stop/target active on Alpaca | BRACKET |
Risk per trade: RISK_PER_TRADE_PCT (default 1%) of current realized equity, not starting equity. This means:
Window: January 16 – April 16, 2026 (90 days) | Universe: 100 symbols across 3 price tiers | Data: 1-minute bars from Alpaca IEX feed | Starting Equity: $1,000
| Metric | Fixed Target | Trailing Stop (1%) | Delta |
|---|---|---|---|
| Trades | 2,198 | 2,141 | -57 |
| Win Rate | 22.2% | 22.3% | +0.1pp |
| Avg Winner | +2.71% | +2.80% | +0.09pp |
| Avg Loser | -0.65% | -0.65% | — |
| Expectancy/Trade | +0.10% | +0.12% | +0.02pp |
| $1,000 Compounded | $6,838 | $9,600 | +$2,762 |
| Best Trade | +4.00% (SOFI) | +14.09% (MNTS) | +10.09pp |
| Worst Trade | -5.10% (MNTS) | -5.10% (MNTS) | — |
Tested 6 trail distances to find the optimal setting. All outperform the fixed target.
| Trail % | Trades | Win Rate | Expectancy | Avg Win | Avg Loss | $1,000 → | PnL |
|---|---|---|---|---|---|---|---|
| FIXED | 2,198 | 22.2% | +0.10% | +2.71% | -0.65% | $6,838 | $5,838 |
| 0.50% | 2,178 | 22.1% | +0.12% | +2.80% | -0.64% | $9,790 | $8,790 |
| 0.75% | 2,163 | 22.3% | +0.11% | +2.75% | -0.65% | $8,316 | $7,316 |
| 1.00% ◄ | 2,141 | 22.3% | +0.12% | +2.79% | -0.65% | $9,600 | $8,600 |
| 1.25% | 2,125 | 22.3% | +0.12% | +2.78% | -0.64% | $9,106 | $8,106 |
| 1.50% | 2,112 | 22.3% | +0.12% | +2.78% | -0.64% | $9,448 | $8,448 |
| 2.00% | 2,076 | 22.4% | +0.12% | +2.75% | -0.64% | $8,598 | $7,598 |
Selected: 1.00% trail — Near-optimal performance with better noise tolerance than 0.50%. Less likely to get shaken out by normal intraday volatility.
| Stop Hit | 1,671 (76%) |
| EOD Close | 280 (13%) |
| Target Hit | 247 (11%) |
| Stop Hit | 1,627 (76%) |
| EOD Close | 295 (14%) |
| Trailing Stop | 219 (10%) |
219 trailing exits averaged +4.12% vs fixed cap of +3.00%
A comprehensive risk audit was performed on all trading logic, safety rails, and broker integration. Critical and high-severity issues were identified and fixed. Below is the full audit with current status.
Bracket stop/target legs were set to TIF=day, meaning they expired at 4:00 PM market close. Positions held overnight had zero stop protection. A gap-down at next-day open would eat the full loss with no safety net.
Fix: Changed to TIF=gtc (good-til-cancelled). Stop and target legs now persist across trading sessions until they fill or are explicitly cancelled.
The new trailing stop feature also used TIF=day. If placed late in the session, it would expire at close, leaving the position naked overnight during the most vulnerable period.
Fix: Changed to TIF=gtc. Trailing stops now persist overnight.
The system relied entirely on Alpaca's bracket mechanics for stop execution. If a bracket leg got stuck in "held" status or the API glitched, the stop would never fire.
Fix: Added Rule 1b — an independent watchdog that compares current price to the proposal's stop price every scan cycle. If price ≤ stop, force close regardless of bracket status.
The sim account had no floor — no mechanism to halt trading if equity dropped to dangerous levels.
Fix: Added equity circuit breaker safety rail. If realized equity drops below 50% of starting equity, all new entries are blocked until manual review. This is the last line of defense against cascading losses.
Risk per trade was calculated as 1% of $1,000 (starting equity) regardless of current account size. After losses to $500, this meant risking 2% of actual equity — accelerating drawdowns.
Fix: Position sizing now uses current realized equity. Risk scales down after losses and up after gains, creating a natural anti-fragile sizing curve. The RISK_PER_TRADE_PCT config setting is now wired in.
Every entry must pass through all safety rails in order. The first failure blocks the trade and logs an audit record.
| # | Rail | Description | Scope |
|---|---|---|---|
| 1 | Trading Session | Block entries outside regular hours (09:30-16:00 ET) | All pools |
| 2 | Late Entry Cutoff | Block entries after 15:45 ET (not enough time for bracket) | PDT only |
| 3 | PDT Budget | Block if daily or 5-day rolling day-trade limit is reached | PDT only |
| 4 | Equity Circuit Breaker | Block if equity ≤ 50% of starting (catastrophic loss guard) | All pools |
| 5 | Position Size | Block if position cost > MAX_POSITION_USD ($1,000) | All pools |
| 6 | Max Open Positions | Block if already at MAX_OPEN_POSITIONS (3) | All pools |
| 7 | Daily Loss Halt | Block if today's realized loss ≥ MAX_DAILY_LOSS_PCT (3%) of equity | All pools |
| 8 | Sufficient Cash | Block if sim cash < position cost | All pools |
| Layer | Mechanism | Failure Mode It Catches |
|---|---|---|
| Bracket Order (GTC) | Alpaca-managed stop + target legs | Normal exits — primary defense |
| Trailing Stop (GTC) | Replaces bracket at target; rides winners | Captures parabolic moves |
| Price Watchdog | Independent check: price vs stop each cycle | Bracket leg stuck/glitched |
| Naked Position Detect | Checks if all bracket legs expired | TIF expiration, API cancellations |
| 30-min Stale Rule | Close if held 30+ min with <2% gain | Momentum decay — dead breakout |
| 45-min Hard Exit | Close unconditionally at 45 min | Any position held too long |
The PDT (Pattern Day Trader) pool operates under strict regulatory constraints that fundamentally change the strategy's behavior compared to unrestricted trading. This section analyzes the impact of PDT rules on the trailing stop strategy.
| Constraint | Setting | Impact |
|---|---|---|
| Day Trades Per Day | 1 max | Only one round-trip (buy + sell same day) allowed per trading day |
| Day Trades Per 5-Day Window | 3 max | Rolling 5-business-day count cannot exceed 3 without PDT flag |
| Account Threshold | < $25,000 | PDT rules only enforced when sim equity is under $25K |
| Late Entry Cutoff | 15:45 ET | No new entries after 15:45 — not enough time for bracket to work |
| Pre-Entry PDT Budget Check | Block | If no day-trade budget left, entry is blocked (can't open what you can't close) |
The unrestricted backtest produced 2,141 trades over 90 days (~24/day). Under PDT rules:
With fewer trades available, each trade carries more weight. The trailing stop becomes even more critical under PDT because:
The PDT pool runs two additional safety rails that the unrestricted pool does not:
| Rail | Logic | Why It Matters |
|---|---|---|
| Late Entry Cutoff | Block entries after 15:45 ET | An entry at 15:50 has 10 minutes before close. Not enough for the bracket to work. Forcing an EOD flatten burns a precious day trade on a noise print. |
| PDT Budget Gate | Block entry if day-trade budget is exhausted (daily or rolling 5-day) | Core principle: never open a position you cannot close. If the budget is full, the entry would either force an overnight hold against plan, or blow the PDT rule on exit. |
Projecting the backtest results under PDT constraints with a $1,000 starting account:
Important: PDT-constrained returns are dramatically lower than unrestricted because the edge compounds over trade volume. With ~10x fewer trades, the 90-day compounding effect is ~10x smaller. The per-trade edge is identical — it's the frequency that's throttled.
| Metric | PDT Pool | Unrestricted | Notes |
|---|---|---|---|
| Trades (90d) | ~200 | 2,141 | ~10x fewer |
| Day Trades Allowed | 1/day, 3/week | Unlimited | FINRA PDT rule |
| Overnight Holds | Frequent | Rare | PDT forces holds |
| Gap Risk Exposure | Higher | Lower | More overnight = more gaps |
| Per-Trade Edge | +0.12% | +0.12% | Identical |
| 90d Return ($1,000) | ~+25% | +860% | Volume drives compounding |
| Safety Rails | 10 | 8 | +2 PDT-specific rails |
| Trailing Stop Value | Critical | Important | Each win matters more |
Understanding how the system determines share quantity for each trade. Two rules compete — the smaller quantity wins.
Formula: qty = (Equity × RISK_PER_TRADE_PCT) ÷ Stop Distance
Example: Equity $1,063 × 1% = $10.63 risk budget. If ATR stop = $0.14/share → 10.63 ÷ 0.14 = 75 shares
Formula: qty = MAX_POSITION_USD ÷ Entry Price
Example: $1,000 ÷ $71.39 = 14 shares
In the example above (HUT at $71.39): Risk-based = 75 shares, USD cap = 14 shares → 14 shares used
Max loss = 14 × $0.14 = $1.96 (well under the $10.63 risk budget)
This is a feature, not a bug. On high-priced stocks with tight stops, the USD cap is the binding constraint. You risk less than 1% of equity — conservative by design.
| Stock Price | Stop Distance | Risk Qty | USD Cap Qty | Winner | Actual Risk |
|---|---|---|---|---|---|
| $5 (cheap) | $0.50 | 21 | 200 | Risk (21) | $10.50 (1.0%) |
| $25 (mid) | $0.80 | 13 | 40 | Risk (13) | $10.40 (1.0%) |
| $71 (expensive) | $0.14 | 75 | 14 | USD Cap (14) | $1.96 (0.2%) |
| $71 (expensive) | $2.00 | 5 | 14 | Risk (5) | $10.00 (0.9%) |
On expensive stocks with tight ATR stops, the USD cap dominates and actual risk is well below 1%. On cheap stocks with wide stops, risk-based sizing dominates and keeps exposure at exactly 1%.
| Target % | 4.0% |
| Stop Method | 1.5x ATR(14) |
| Trail % | 1.0% |
| Trailing Stop at Target | Enabled |
| Risk Per Trade | 1.0% of equity |
| Time of Day Filter | On |
| Relative Strength Filter | On |
| Scanner Interval | 60 sec |
| Max Position USD | $1,000 |
| Max Open Positions | 3 |
| Max Daily Loss | 3.0% of equity |
| Day Trades/Day (PDT) | 1 |
| Day Trades/Week (PDT) | 3 |
| Circuit Breaker | 50% drawdown |
| Mode | Paper |
| Auto Execute | Yes (paper only) |