A production-quality options paper trading bot for Interactive Brokers, designed to run on a headless Ubuntu server with IB Gateway in Docker.
- Put Credit Spread Strategy: Automated scanning and execution of SPY/QQQ put credit spreads
- Safety First: Multiple kill switches and risk controls (trading disabled by default)
- Comprehensive Logging: SQLite database + rotating log files
- Full Diagnostics:
bot doctorcommand verifies connectivity and market data - Risk Management: Position sizing, daily loss limits, trade count limits
- Docker and Docker Compose installed
- Python 3.11 or higher
uvrecommended (or usepip/venv)- Interactive Brokers account (paper trading account recommended)
git clone <repo-url>
cd PaperTradecp .env.example .env
# Edit .env with your settings (see Configuration section)docker compose up -dFirst-time setup:
- Check gateway logs:
docker compose logs -f ib-gateway - Access the gateway web interface (if available) or use VNC
- Complete login with your IB credentials
- Complete 2FA if prompted
- Ensure gateway shows "Connected" status
Note: The gateway container persists login state in the ib-gateway-data volume.
# Using uv (recommended)
uv run bot doctor
# Or using Python directly
python -m options_bot.cli doctorThe doctor command will verify:
- ✓ IB Gateway connection
- ✓ Paper account detection
- ✓ Market data (SPY quote)
- ✓ Options chain access
- ✓ Greeks availability
uv run bot scanThis prints candidate put credit spreads per ticker.
# Run for 120 minutes (default)
uv run bot run --session 120
# Or specify duration
uv run bot run --session 60Important: Trading is disabled by default (TRADING_DISABLED=true). The bot will scan and manage positions but will NOT place orders until you explicitly enable trading.
Key environment variables in .env:
TRADING_DISABLED=true- Default: trading disabled. Set tofalseto enable order placement.ACCOUNT_SIZE=1000- Account size for position sizingRISK_PER_TRADE_PCT=0.02- Max risk per trade (2% of account)MAX_DAILY_LOSS_PCT=0.03- Max daily loss kill switch (3% of account)MAX_TRADES_PER_DAY=2- Maximum trades per day
UNDERLYINGS=SPY,QQQ- Comma-separated list of symbolsDTE_MIN=7/DTE_MAX=21- Days to expiration rangeDELTA_MIN=0.15/DELTA_MAX=0.25- Delta range for short putSPREAD_WIDTH=1.0- Strike width between short and long puts (1.0 or 2.0)REQUIRE_GREEKS=true- Reject candidates without Greeks (default: true)OTM_TARGET_PCT=0.04- Fallback: select strike 4% OTM when Greeks unavailable
TP_CAPTURE_PCT=0.50- Take profit at 50% of credit capturedSL_MULTIPLE=2.0- Stop loss at 2.0x initial creditTIME_EXIT_DTE=3- Close when DTE <= 3
ENTRY_WINDOW_START=10:00/ENTRY_WINDOW_END=11:00- Entry window (ET)MANAGE_INTERVAL_SECONDS=300- Management loop interval (5 minutes)ENTRY_MAX_SLIPPAGE=0.05- Max slippage toleranceENTRY_RETRY_SECONDS=60- Retry timeout for orders
Verifies connectivity, paper account, market data, and options chain. Run this first to ensure everything is configured correctly.
uv run bot doctorScans for candidate put credit spreads and prints them (no orders placed).
uv run bot scanRuns a trading session:
- Scans for candidates during entry window
- Places orders (if
TRADING_DISABLED=false) - Manages open positions (TP/SL/time exits)
- Logs everything to database and files
uv run bot run --session 120Manages existing positions only (no new entries).
uv run bot managePrints today's trades, P/L, win rate, and open risk.
uv run bot reportExports trades/orders/fills to CSV.
uv run bot export --csv trades.csv- Trading Disabled by Default:
TRADING_DISABLED=true- bot will never place orders until explicitly enabled - Max Daily Loss Kill Switch: Stops opening new trades if daily loss exceeds
MAX_DAILY_LOSS_PCT - Max Risk Per Trade: Position sizing enforces
RISK_PER_TRADE_PCTlimit - Max Trades Per Day: Prevents exceeding
MAX_TRADES_PER_DAY - Entry Window: Only opens trades during configured hours
- Duplicate Prevention: One open trade per ticker maximum
Important: The bot requires real-time or delayed market data for:
- Underlying stock quotes (bid/ask)
- Option quotes (bid/ask)
- Greeks (delta) from market data
If you see warnings about missing bid/ask or Greeks:
- Delayed data may be available (15-20 minute delay)
- The bot will still function but may miss opportunities
- Consider subscribing to real-time data for production use
The bot requires Greeks (delta) for selection when REQUIRE_GREEKS=true:
- Greeks come from
ib_insynctickermodelGreeksoroptionGreeks - If unavailable, set
REQUIRE_GREEKS=falseto use OTM fallback bot doctorexplicitly reports Greeks availability
All trades, orders, fills, and market snapshots are stored in SQLite:
- Location:
./data/bot.db(configurable viaDB_PATH) - Tables:
bot_runs,trades,orders,fills,market_snapshots,daily_stats
Logs are written to:
- Console: Structured output
- File:
./logs/bot.log(rotating, max 10MB, 5 backups)
WARNING: The bot is designed for paper trading. To use with a live account:
- Change IB Gateway mode: Set
TRADING_MODE=livein docker-compose.yml (or use live TWS) - Update account: Set
IB_ACCOUNT_IDin.envto your live account - Verify:
bot doctorshould show a non-DU account (with warning) - Enable trading: Set
TRADING_DISABLED=falseonly after thorough testing - Start small: Use small
ACCOUNT_SIZEandRISK_PER_TRADE_PCTinitially
The bot does NOT distinguish between paper and live accounts in its logic - it relies on you to configure it correctly.
PaperTrade/
├── README.md
├── .env.example
├── docker-compose.yml
├── pyproject.toml
├── src/
│ └── options_bot/
│ ├── config.py # Configuration from env
│ ├── logging_setup.py # Logging configuration
│ ├── cli.py # CLI entrypoint
│ ├── time_utils.py # ET timezone handling
│ ├── db/ # Database models and repo
│ ├── ibkr/ # IBKR integration
│ ├── strategy/ # Strategy logic
│ └── services/ # High-level services
└── tests/ # Unit tests
# Using uv (recommended)
uv sync
# Or using pip
pip install -e .pytest tests/- Check gateway is running:
docker compose ps - Check gateway logs:
docker compose logs ib-gateway - Verify port matches
IB_PORTin.env - Ensure gateway is logged in (check logs for "Connected")
- Verify market data subscriptions in IB account
- Check if market is open (bot works during market hours)
- Run
bot doctorto diagnose
- Check market data subscription includes options data
- Try setting
REQUIRE_GREEKS=falseto use OTM fallback - Verify with
bot doctor- it reports Greeks availability
- Check
TRADING_DISABLEDin.env(default:true) - Bot will scan and manage but not place orders when disabled
[Add your license here]
This software is for educational and paper trading purposes only. Trading options involves substantial risk. Use at your own risk. The authors are not responsible for any losses.