Skip to content

Muath2000/TradeStation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TradeStation Options Bot — Intelligence Engine v3.1

Fully Autonomous Options Income Bot on TradeStation API v3 with Deep Market Intelligence

Node.js 22 Python 3.12 TypeScript 5.x Docker Compose PostgreSQL 16 License: Private


Overview

The v3.0 blueprint relied on IV Rank, EMA/RSI, and VIX as the only market signals. v3.1 adds five additional intelligence layers that transform the bot from a mechanical rules engine into a context-aware system that understands why the market is moving, not just that it is moving.

Three-Track Strategy Architecture

Track Strategy Symbols DTE Key Rules
A — Wheel Cash-Secured Put + Covered Call AAPL, MSFT, NVDA, AMZN, GOOGL 30 DTE 0.30 delta, 50% profit target, skip 7d before earnings
B — Strangle Short Strangle (SPY/QQQ only) SPY, QQQ 45 DTE 0.30 delta, 3x stop, close at 21 DTE (non-negotiable)
C — 0DTE Iron Butterfly SPX 0 DTE 10:15 AM entry, noon hard exit, Mon/Wed/Fri only
Fallback Iron Condor / Vertical Spread Individual stocks 30-45 DTE Defined-risk when IV Rank qualifies but Wheel is full

Intelligence Engine — 5-Layer Composite Score

Each symbol receives a composite Intelligence Score (0-100) before every trade decision. The bot will not trade without consulting the intelligence service first.

Intelligence Layer What It Detects Data Source
News Sentiment Positive/negative tone in headlines via FinBERT NLP RSS feeds (Reuters, Finviz, SEC EDGAR, Fed Reserve, Yahoo Finance)
Options Flow Unusual block orders, dark pool prints, sweep activity, skew TradeStation API option chains
Macro Calendar FOMC, CPI, NFP, GDP, Fed speeches, earnings dates FRED API, BLS/BEA RSS, Federal Reserve RSS
Historical Backtester Whether current parameters worked on last 90 days PostgreSQL trade history + TradeStation bars
Market Breadth VIX regime, put/call ratio, sector rotation, advance/decline TradeStation API + market breadth data

Score → Bot Behavior

Score Label Bot Behavior
85-100 STRONG GO All tracks active. 1.2x position sizing.
65-84 GO Normal operation. All tracks at standard sizing.
45-64 CAUTIOUS 0.5x size. Skip 0DTE. Require IV Rank > 40.
25-44 HOLD No new entries. Monitor and manage existing only.
0-24 DANGER Close positions near expiry. Send CRITICAL alert.

Score Weights

Layer Weight
News Sentiment 20%
Options Flow 25%
Macro Calendar 20%
Market Breadth 20%
Backtester 15%

Architecture

                    ┌──────────────────────────────────────────┐
                    │              Nginx (HTTPS)               │
                    │   TLS · Rate Limiting · CSP Headers      │
                    └────┬──────────┬──────────┬───────────────┘
                         │          │          │
              ┌──────────▼──┐  ┌───▼───┐  ┌──▼──────────────┐
              │ Trading Bot │  │ Front- │  │   Intelligence  │
              │  Node.js    │  │  end   │  │    Python       │
              │  :3001      │  │  :80   │  │    :5050/:8050  │
              └──────┬──────┘  └───────┘  └────────┬────────┘
                     │                              │
              ┌──────▼──────────────────────────────▼──────┐
              │              PostgreSQL 16                  │
              │          Shared Database (tsbot)            │
              └────────────────────────────────────────────┘
                     │              │              │
              ┌──────▼──┐   ┌──────▼──┐   ┌──────▼──┐
              │ OpenBao  │   │Promethe-│   │ Grafana │
              │ Secrets  │   │  us     │   │   :3000 │
              │  :8200   │   │  :9091  │   └─────────┘
              └──────────┘   └─────────┘

Docker Services (10 containers)

Service Port Purpose
nginx 80, 443 HTTPS termination, JWT gate, rate limiting
trading-bot 3001, 9090 Node.js bot API + Prometheus metrics
intelligence 5050, 8050 Python FastAPI intelligence + Dash dashboard
frontend 80 (internal) React SPA dashboard
grafana 3000 Metrics dashboards
prometheus 9091 Metrics scraper (90-day retention)
postgres 5432 (internal) PostgreSQL 16 database
openbao 8200 (internal) Secrets management (HashiCorp Vault fork)
mailserver 25, 587 SMTP for email alerts
watchtower Auto-update trading-bot from Docker Hub

Technology Stack

Bot (Node.js)

  • Runtime: Node.js 22 LTS, TypeScript 5.x (strict mode)
  • API: Express 4.x
  • ORM: Drizzle ORM
  • Auth: JWT (jsonwebtoken) + Zod validation
  • Scheduling: node-cron (America/New_York timezone)
  • Metrics: prom-client (Prometheus)
  • Secrets: OpenBao (HashiCorp Vault fork) + Docker Secrets fallback
  • Alerts: Nodemailer (12 event types, 4 severity levels)

Intelligence (Python)

  • Runtime: Python 3.12
  • API: FastAPI + Uvicorn
  • NLP: FinBERT (ProsusAI/finbert) via Hugging Face Transformers + PyTorch
  • Dashboard: Plotly Dash with Bootstrap
  • Reports: WeasyPrint (PDF) + Jinja2 templates
  • ORM: SQLAlchemy 2.x (async with asyncpg)
  • Scheduling: APScheduler

Frontend

  • Framework: React 18 + TypeScript
  • Build: Vite
  • Styling: TailwindCSS (dark trading-terminal theme)
  • State: Zustand (JWT in memory only — not localStorage)
  • Data: TanStack Query (polling at 15-60s intervals)
  • Charts: Recharts

TradeStation API v3 — Key Endpoints

All interactions use https://api.tradestation.com/v3 (live) or https://sim-api.tradestation.com/v3 (simulation).

Endpoint Method Rate Limit Purpose
/marketdata/quotes/{symbols} GET 240/min Real-time quotes
/marketdata/barcharts/{symbol} GET 120/min OHLCV historical bars
/marketdata/symbollists/optionexpirations/{symbol} GET 60/min Option expiration dates
/marketdata/options/chains/{symbol} GET 60/min Full option chain with Greeks
/accounts/{accountID}/orders POST Monitored Place orders (single or multi-leg)
/accounts/{accountID}/positions GET 240/min Open positions
/accounts/{accountID}/balances GET 240/min Buying power, equity, margin

Symbol Format Reference

Symbol API Format Notes
SPX Index $SPX.X Dollar sign prefix, .X suffix
SPX Options SPX No prefix for option chains
VIX Index $VIX.X Dollar sign prefix, .X suffix
OCC Option SPY 250321C00500000 Symbol + space + YYMMDD + C/P + 8-digit strike

Risk Management — 9-Layer Safety Gate

Every trading cycle passes through a 9-layer portfolio risk check before any trade routing occurs:

  1. Intelligence Score Gate — Block if score < 45 (HOLD/DANGER)
  2. Daily Loss Limit — Block if daily loss exceeds 5% of equity
  3. Macro Block — Block if intelligence reports macro event within window
  4. Max Drawdown — Block if portfolio drawdown exceeds 10%
  5. VIX Ceiling — Block if VIX > 35
  6. Open Position Limit — Block if max concurrent positions reached
  7. Per-Track Allocation — Block if track exceeds allocation limit (Wheel 35%, Strangle 45%, 0DTE 15%)
  8. Portfolio Delta — Block if absolute portfolio delta > 0.50
  9. Buying Power Reserve — Ensure minimum reserve after trade

Hard Limits (Non-Configurable)

Limit Value Rationale
21 DTE universal close All multi-day positions Gamma explosion protection
0DTE noon hard exit 12:00 PM ET Scheduled at startup, never skipped
Strangles on SPY/QQQ only Never individual stocks Index-only for undefined risk
Order retry safety Never retry possibly-filled Prevents double fills

Email Alerts — 12 Events

Event Level Throttle
Bot started INFO 1/hr
Bot shutdown WARNING 1/hr
Daily loss limit hit CRITICAL 1/hr
Max drawdown exceeded CRITICAL 1/hr
VIX > 35 — halted WARNING 1/hr
Strangle stop 3x CRITICAL Per position
TS API auth failing CRITICAL 1/hr
OpenBao unreachable CRITICAL 1/hr
Token renewal failed CRITICAL 1/hr
0DTE noon force-close INFO Per position
Health check degraded DOWNTIME 1/hr
End-of-day P&L summary INFO Unlimited

Production Deployment Guide

Prerequisites

  • Linux server (Ubuntu 22.04+ or Debian 12+ recommended)
  • Docker Engine 24+ and Docker Compose v2
  • A registered domain with DNS pointing to your server
  • TLS certificate (Let's Encrypt or similar)
  • TradeStation API v3 credentials (Client ID, Client Secret, Refresh Token)

Step 1: Clone the Repository

git clone https://github.com/YOUR_USERNAME/ts-options-bot.git
cd ts-options-bot

Step 2: Create Docker Secrets

Every secret must be created via Docker Secrets. The bot reads credentials from /run/secrets/* at runtime. Never put secrets in environment variables or config files.

# Initialize Docker Swarm (required for Docker Secrets)
docker swarm init

# TradeStation API credentials
echo "YOUR_TS_CLIENT_ID"      | docker secret create ts_client_id -
echo "YOUR_TS_CLIENT_SECRET"  | docker secret create ts_client_secret -
echo "YOUR_TS_REFRESH_TOKEN"  | docker secret create ts_refresh_token -

# Database password (use a strong random password)
openssl rand -base64 32       | docker secret create db_password -

# JWT secret for dashboard authentication
openssl rand -hex 32           | docker secret create jwt_secret -

# OpenBao token (generated during OpenBao init)
echo "YOUR_OPENBAO_BOT_TOKEN" | docker secret create openbao_token -

# SMTP password for email alerts
echo "YOUR_SMTP_PASSWORD"     | docker secret create smtp_password -

Step 3: Configure TLS Certificates

Place your TLS certificate and private key in the Nginx certs directory:

mkdir -p docker/nginx/certs
cp /path/to/fullchain.pem docker/nginx/certs/fullchain.pem
cp /path/to/privkey.pem docker/nginx/certs/privkey.pem
chmod 600 docker/nginx/certs/privkey.pem

Using Let's Encrypt (recommended):

# Install certbot
sudo apt install certbot

# Generate certificate
sudo certbot certonly --standalone -d yourdomain.com

# Copy certs
sudo cp /etc/letsencrypt/live/yourdomain.com/fullchain.pem docker/nginx/certs/
sudo cp /etc/letsencrypt/live/yourdomain.com/privkey.pem docker/nginx/certs/

Step 4: Configure Environment Variables

Edit docker-compose.yml and update the non-secret environment variables:

trading-bot:
  environment:
    - TS_SIM=true                   # KEEP true until all paper trading phases pass
    - DASHBOARD_URL=https://yourdomain.com
    - SMTP_HOST=mailserver          # Or smtp.gmail.com for Gmail
    - SMTP_PORT=587
    - ALERT_EMAIL=your@email.com

mailserver:
  domainname: yourdomain.com        # Your actual domain

intelligence:
  environment:
    - INTELLIGENCE_API_KEY=<generate-a-strong-random-key>

Generate the intelligence API key:

openssl rand -hex 32

Step 5: Initialize the Database

# Start PostgreSQL first
docker compose up -d postgres

# Wait for it to be healthy
docker compose exec postgres pg_isready -U botuser -d tsbot

# Run Drizzle migrations to create tables
docker compose run --rm trading-bot npm run db:push

Step 6: Initialize OpenBao (Secrets Vault)

# Start OpenBao
docker compose up -d openbao

# Initialize the vault
docker compose exec openbao bao operator init \
  -key-shares=5 \
  -key-threshold=3

# IMPORTANT: Save the unseal keys and root token securely!
# You need 3 of 5 keys to unseal after every restart.

# Unseal (repeat 3 times with different keys)
docker compose exec openbao bao operator unseal <KEY_1>
docker compose exec openbao bao operator unseal <KEY_2>
docker compose exec openbao bao operator unseal <KEY_3>

# Enable KV secrets engine
docker compose exec openbao bao secrets enable -path=trading-bot kv-v2

# Store the TradeStation credentials in OpenBao
docker compose exec openbao bao kv put trading-bot/credentials \
  ts_client_id=YOUR_CLIENT_ID \
  ts_client_secret=YOUR_CLIENT_SECRET \
  ts_refresh_token=YOUR_REFRESH_TOKEN

# Create a policy for the bot
docker compose exec openbao bao policy write trading-bot - <<'EOF'
path "trading-bot/data/*" {
  capabilities = ["read", "create", "update", "delete"]
}
EOF

# Create a token for the bot service
docker compose exec openbao bao token create \
  -policy=trading-bot \
  -ttl=24h \
  -renewable=true
# → Use this token as the openbao_token Docker Secret

Step 7: Build and Start All Services

# Build all images
docker compose build

# Start the full stack
docker compose up -d

# Verify all services are healthy
docker compose ps

# Check trading bot logs
docker compose logs -f trading-bot

Step 8: Verify the Deployment

# Health check
curl https://yourdomain.com/health

# Expected response:
# { "status": "healthy", "uptime": 120, "checks": { "db": true, "openbao": true, "intelligence": true } }

# Test email alerts
docker compose exec trading-bot node dist/scripts/test-alert.js

Step 9: Access the Dashboard

URL Service
https://yourdomain.com/ React Trading Dashboard
https://yourdomain.com/intel/ Intelligence Plotly Dash Dashboard
https://yourdomain.com/grafana/ Grafana Metrics Dashboard
https://yourdomain.com/health Health Check (public)

Dashboard Login: The password is the first 16 characters of your JWT secret.


Going Live — Phased Rollout

Do not skip phases. Each phase validates a critical subsystem.

Phase 1: Build & Test (2 weeks)

TS_SIM=true
  • All unit tests pass (npm test — 65+ tests)
  • Frontend builds successfully (cd frontend && npm run build)
  • Docker Compose starts without errors
  • Health check returns healthy for all dependencies
  • Email alerts work (npm run test:alerts fires all 4 levels)
  • Intelligence score returns for SPY, QQQ, AAPL

Phase 2a: Paper — 0DTE (2 weeks)

  • 30+ 0DTE butterfly fills logged
  • Noon hard exit fires every Mon/Wed/Fri at 12:00 PM ET
  • P&L tracking matches TradeStation account
  • Wing width and entry time are sensible

Phase 2b: Paper — Strangles (2 weeks)

  • 20+ strangles opened and closed
  • 21 DTE rule closes them automatically
  • 3x stop loss triggers correctly
  • Only SPY/QQQ (never individual stocks)

Phase 2c: Paper — Wheel (4 weeks)

  • Full CSP → assignment → CC → called away cycle on 2+ symbols
  • Wheel state machine transitions correctly
  • Cost basis tracking is accurate
  • Earnings skip (7-day window) works

Phase 3: Micro Live (4 weeks)

TS_SIM=false    # LIVE TRADING — read carefully before changing

Before flipping to live:

  • All Phase 2 paper trading criteria met
  • Reviewed every trade in the audit log
  • 1-contract maximum enforced
  • All email alerts verified
  • Monitoring dashboards showing correct data
  • Kill switch tested (bot stops immediately)
  • Daily backup procedure established
# To go live, change in docker-compose.yml:
# TS_SIM=false

# Then rebuild and restart:
docker compose build trading-bot
docker compose up -d trading-bot

Phase 4: Scale (Monthly Review)

  • Increase position size only after positive months
  • Add new Wheel symbols one at a time
  • Review intelligence score accuracy monthly
  • Never increase risk during drawdowns

Monitoring & Observability

Prometheus Metrics

Metric Type Description
bot_trades_total Counter Total trades by track, symbol, result
bot_portfolio_value Gauge Current portfolio equity
bot_daily_pnl Gauge Today's realized P&L
bot_iv_rank Gauge Current IV Rank per symbol
bot_portfolio_delta Gauge Net portfolio delta
bot_intel_score Gauge Intelligence score per symbol
bot_api_latency Histogram TradeStation API response time

Grafana Dashboard

Pre-provisioned dashboard at /grafana/ shows:

  • P&L by track (stacked bar chart)
  • Win rate by strategy
  • Portfolio Greeks (delta, theta, vega)
  • Open positions count
  • Intelligence score timeline
  • API latency percentiles

Database Schema

Four PostgreSQL tables managed by Drizzle ORM:

positions         — All open/closed positions across all tracks
wheel_states      — Wheel strategy state machine per symbol
daily_pnl         — Daily P&L aggregation by track
audit_log         — Full audit trail of every bot event

Migrations

# Generate migration from schema changes
npm run db:generate

# Apply migrations
npm run db:migrate

# Push schema directly (development only)
npm run db:push

Development

Local Development (without Docker)

# Prerequisites
# - Node.js 22+, Python 3.12+, PostgreSQL 16

# Bot
npm install
npm run dev          # Runs with tsx (hot reload)

# Intelligence service
cd intelligence
pip install -r requirements.txt
uvicorn app.main:app --port 5050 --reload

# Frontend
cd frontend
npm install
npm run dev          # Vite dev server at localhost:5173 (proxies to :3001)

Running Tests

# TypeScript unit tests
npm test

# TypeScript type checking
npm run typecheck

# Frontend type checking
cd frontend && npx tsc --noEmit

# Python (from intelligence/)
cd intelligence
pytest -v --asyncio-mode=auto

CI/CD Pipeline

GitHub Actions runs on every PR:

  • TypeScript type check + unit tests (65+ tests)
  • Frontend build verification
  • Python lint + tests
  • Docker Compose config validation

On git tag v*.*.*:

  • Multi-stage Docker build for all 3 services (bot, intelligence, frontend)
  • Push to Docker Hub (private repositories)
  • Watchtower auto-updates production (labeled services only)

Docker Hub Images

Image Source Size
youruser/ts-bot Dockerfile (Node.js multi-stage) ~50 MB
youruser/ts-intel intelligence/Dockerfile (Python + FinBERT) ~3-4 GB
youruser/ts-frontend frontend/Dockerfile (Vite → nginx:alpine) ~50 MB

Deploying from Docker Hub (Private Registry)

For production, you deploy pre-built images from Docker Hub — no source code needed on the server.

One-Time Setup: Docker Hub

  1. Create 3 private repositories on hub.docker.com:

    • youruser/ts-bot
    • youruser/ts-intel
    • youruser/ts-frontend
  2. Set GitHub Secrets (Settings → Secrets → Actions):

    • DOCKERHUB_USERNAME — your Docker Hub username
    • DOCKERHUB_TOKEN — Docker Hub Access Token (not password)

    Generate a token at: hub.docker.com/settings/security

Releasing a New Version

# Tag and push — GitHub Actions builds all 3 images automatically
git tag v1.0.0
git push origin v1.0.0

GitHub Actions will:

  1. Build ts-bot, ts-intel, ts-frontend images
  2. Push each with :v1.0.0 and :latest tags
  3. Watchtower (on production server) detects new :latest within 5 minutes

Production Server Setup (First Time)

# 1. Install Docker Engine + Compose
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# 2. Initialize Docker Swarm (required for Docker Secrets)
docker swarm init

# 3. Login to Docker Hub (for private repos)
docker login -u youruser

# 4. Create Docker Secrets (see Step 2 in Production Deployment Guide above)

# 5. Copy only the compose files + config to the server (NO source code needed)
# Required files:
#   docker-compose.yml
#   docker-compose.prod.yml
#   docker/nginx/nginx.conf
#   docker/nginx/certs/fullchain.pem
#   docker/nginx/certs/privkey.pem
#   docker/openbao/config.hcl
#   docker/prometheus/prometheus.yml
#   docker/grafana/provisioning/

# 6. Create .env with your Docker Hub user
cat > .env << 'EOF'
DOCKERHUB_USER=youruser
TAG=v1.0.0
INTELLIGENCE_API_KEY=your_generated_key_here
EOF

# 7. Pull and start all services
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# 8. Verify
docker compose ps
curl https://yourdomain.com/health

Updating Production

Automatic (Watchtower): After pushing a new git tag, Watchtower polls Docker Hub every 5 minutes and auto-updates labeled services (trading-bot, intelligence, frontend).

Manual:

# Pull specific version
export TAG=v1.1.0
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Rollback

# Roll back to previous version
export TAG=v1.0.0
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Production Compose Override

The docker-compose.prod.yml file overrides the base docker-compose.yml:

  • Removes build: directives (images pulled from Docker Hub, not built locally)
  • Sets TS_SIM=false for live trading
  • All other config (secrets, networks, depends_on) inherited from base
# Development (builds locally):
docker compose up -d

# Production (pulls from Docker Hub):
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Cron Schedule (All Times Eastern)

Job Schedule Description
Main Cycle */15 9-15 * * 1-5 Every 15 min during market hours
0DTE Entry 15 10 * * 1,3,5 10:15 AM Mon/Wed/Fri
0DTE Monitor */5 10,11 * * 1,3,5 Every 5 min 10-11 AM
0DTE Noon Exit 0 12 * * 1,3,5 12:00 PM HARD EXIT
Greeks Refresh */10 9-15 * * 1-5 Every 10 min market hours
EOD Report 5 16 * * 1-5 4:05 PM daily
TS Token Refresh */10 * * * * Every 10 min (tokens expire in 20 min)
OpenBao Renewal 0 0 * * * Midnight daily

Security Model

  • Secrets: Docker Secrets (/run/secrets/*) with OpenBao (HashiCorp Vault) as primary store
  • Auth: JWT with timing-safe password comparison, Zod-validated login requests
  • Network: Two Docker networks — botnet (internal) and webnet (public-facing via Nginx only)
  • Headers: CSP, HSTS, X-Frame-Options DENY, X-Content-Type-Options nosniff
  • Rate Limiting: 5 req/min on login, 10 req/sec on API (via Nginx)
  • Container: Non-root user (botuser) in all application containers
  • Frontend: JWT stored in memory only (not localStorage) — re-login required on page refresh
  • Intelligence API: API key validated with secrets.compare_digest() (timing-safe)

Troubleshooting

Bot won't start — "Missing critical secrets"

Ensure all Docker Secrets are created. Run docker secret ls to verify all 7 secrets exist.

TradeStation API returns 401

Access tokens expire every 20 minutes. The bot refreshes them proactively via cron. If you see persistent 401s, your refresh token may have expired (365-day lifetime). Generate a new one via TradeStation developer portal.

Intelligence service returns 503

The FinBERT model takes 30-60 seconds to load on first request. If it persists, check docker compose logs intelligence for memory issues (FinBERT requires ~2GB RAM).

0DTE noon exit didn't fire

The noon exit is scheduled via node-cron with America/New_York timezone. Verify your server timezone: timedatectl. The cron fires at exactly 12:00 PM ET regardless of server timezone.

Database connection refused

Ensure PostgreSQL is healthy: docker compose exec postgres pg_isready -U botuser -d tsbot. Check that db_password Docker Secret matches what PostgreSQL was initialized with.

OpenBao sealed after restart

OpenBao seals on every container restart. You must unseal with 3 of 5 keys: docker compose exec openbao bao operator unseal <KEY>.


License

Private — All rights reserved.


Built with TypeScript, Python, and the TradeStation API v3. Intelligence Engine v3.1.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors