Background
On February 26, 2026 — the system's first live paper trade — MorningEdge submitted its initial batch of orders using TimeInForce.OPG (Market-on-Open) orders via the Alpaca paper trading API. The OPG order type is designed to participate in the opening auction at 9:30 AM ET, which is the standard institutional mechanism for capturing the opening print.
This report documents the failure of OPG orders in the Alpaca paper trading environment, the root cause analysis, the fix implemented, and the validation of the replacement approach.
Attempt 1: OPG Orders at 8:54 AM ET
Five OPG market orders were submitted at 08:54:40 AM ET:
| Ticker | Shares | Result |
|---|---|---|
| INTU | 26 | 0 filled — expired |
| CLBT | 728 | 0 filled — expired |
| JOBY | 964 | 0 filled — expired |
| IBIT | 259 | 128 filled (49.4% partial) |
| TARA | 1,675 | 0 filled — expired |
Fill rate: 1/5 orders (20%), and even that was only a partial fill.
Root Cause Analysis
- Narrow fill window: The paper engine's OPG simulation has approximately a 30-second window around 9:30 AM.
- Limited simulated liquidity: The paper engine does not simulate full auction depth.
- Not representative of live behavior: On a real exchange, OPG orders participate in the full opening auction order book.
Key insight: This was a platform limitation, not a strategy failure.
The Fix: DAY Market Orders + wait_for_market_open()
- Order type changed from
TimeInForce.OPGtoTimeInForce.DAY - Added
wait_for_market_open()— polls until 9:30:01 AM ET before submitting orders
Attempt 2: DAY Market Orders at 9:47 AM ET
| Ticker | Shares | Fill Price | Result |
|---|---|---|---|
| NVAX | 914 | $10.96 | Filled instantly |
| STLA | 1,231 | $8.13 | Filled instantly |
| INTU | 25 | $395.85 | Filled instantly |
| OPEN | 1,912 | $5.24 | Filled instantly |
| RGTI | 538 | $18.59 | Filled instantly |
Fill rate: 5/5 (100%). All five orders filled immediately.
Comparison
| Metric | OPG Orders | DAY Market Orders |
|---|---|---|
| Fill rate | 1/5 (20%) | 5/5 (100%) |
| Partial fills | 1 (IBIT: 128/259) | 0 |
| Time to fill | ~36 min queue, then failed | Instant |
| Ghost positions | Yes (manual cleanup) | None |
Implications
- Keep DAY market orders for paper validation. 100% fill rate eliminates execution uncertainty from the test results.
- Phase C (live trading) will use marketable limit orders (Ask + $0.05) to control slippage while maintaining fill reliability.
- wait_for_market_open() is permanent infrastructure. It ensures the opening print has established a market price before orders fire, regardless of order type.