Context
User reported zero options trading and zero day trading activity in the #trading Discord channel. The trading agent was deployed on 2026-05-06 with options and day trading support, but no trades were executing.
Root Cause 1: Alpaca Auth Failure
The daytrade daemon imports engine/portfolio.py and engine/executor.py before loading .env. Both modules read Alpaca credentials into module-level constants at import time via os.getenv(), which returned empty strings since .env had not been parsed yet. Every Alpaca API call failed with “You must supply a method of authentication” for roughly 21 hours.
Fix: Moved credential reads from module-level constants into the functions that use them. Credentials are now read at call time, after .env is loaded.
Root Cause 2: Missing Cron Jobs
The fast loop (run.sh every 30 min) and deep loop (run.sh –deep at 8:30 AM and 5:30 PM ET) were never added to the VM crontab. Only PM2 processes were configured. All swing trading strategies never executed autonomously.
Fix: Added 4 cron entries: fast loop every 30 min during market hours, deep loop pre-market and after-hours, Monday through Friday.
Follow-up: Usage Threshold Override
The global usage gate (75%) was blocking the deep loop at 77% usage. Trading is the primary value-producing agent and should keep running while other agents are paused.
Fix: Both run.sh and daytrade/daemon.py now use a 90% threshold instead of the global 75%. They call check-usage.sh –json, parse the max bucket percentage, and compare against TRADING_USAGE_THRESHOLD=90. Each trading run costs about $0.18, so the budget impact is minimal.
Verification
- Portfolio snapshot: 13 positions, $100k paper account
- Test order: 1 share SPY buy accepted, then cancelled
- Daytrade daemon: restarted cleanly, no auth errors
- Full dry-run: built prompt, gathered data correctly
Pattern
When Python modules read environment variables at module level (os.getenv outside any function), the values are captured at import time. If .env is loaded after the import, the variables will be empty. Always read credentials inside functions that use them when .env loading happens at runtime.