Skip to content

yu2001-s/war-room

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

War Room IBKR Trading Stack

This repo is a paper-first starter environment for algorithmic trading on Interactive Brokers with a clean local dashboard.

It gives you:

  • FastAPI backend for broker sync, local performance storage, and risk metrics.
  • SQLite performance database for account snapshots, positions, orders, fills, strategy states, and alerts.
  • React/Vite dashboard for equity, P&L, drawdown, exposure, positions, orders, fills, and strategy status.
  • Market-data routing that uses Futu OpenAPI for US quotes, Sinotrade/Shioaji for Taiwan quotes, and yfinance as the global/fallback source.
  • Mock mode that runs immediately without IB Gateway.
  • IBKR mode that connects to TWS or IB Gateway after you enable socket API access.

Architecture

flowchart LR
  Strategy["Strategy runner"] --> Risk["Risk checks"]
  Risk --> Broker["Broker adapter"]
  Broker --> IBKR["IB Gateway / TWS"]
  API --> MarketData["Market-data router"]
  MarketData --> Futu["Futu OpenD"]
  MarketData --> Sinotrade["Sinotrade / Shioaji"]
  MarketData --> YFinance["yfinance"]
  Broker --> Store["SQLite performance store"]
  Store --> API["FastAPI API"]
  API --> Dashboard["React dashboard"]
Loading

Quickstart

cp .env.example .env
make install
make dev

This starts both the FastAPI backend and Vite frontend in one process with prefixed logs. When MARKET_DATA_US_PROVIDER=futu, it starts Futu OpenD before the app. When BROKER_MODE=ibkr, it starts IB Gateway, autofills the login if local credentials are configured, and starts the IBKR ingestion worker so account data stays fresh. You can also run the underlying npm script directly:

npm run dev

For an IBKR-connected development session, set BROKER_MODE=ibkr and use make dev. The worker defaults to client ID 19 so it does not conflict with the API server's IBKR_CLIENT_ID; override it with IBKR_WORKER_CLIENT_ID=20 make dev if another client already uses 19. Set IBKR_WORKER_AUTOSTART=false only when you intentionally want to run the worker yourself.

Open the dashboard at http://127.0.0.1:5173.

The backend is at http://127.0.0.1:8000/api/dashboard.

IBKR Setup

  1. Install and log into IB Gateway or Trader Workstation.
  2. In TWS/Gateway, enable API socket access: Configure or Global Configuration -> API -> Settings -> enable Enable ActiveX and Socket Clients.
  3. Start with paper trading:
    • IB Gateway paper: IBKR_PORT=4002
    • TWS paper: IBKR_PORT=7497
  4. Keep API read-only until the dashboard shows stable account, position, and order data.
  5. Set BROKER_MODE=ibkr in .env, then restart the backend.
  6. Keep ENABLE_ORDER_SUBMISSION=false until paper-trade tests and risk limits are proven.

make dev can launch the local broker apps for you on macOS. The launcher auto-discovers common app locations such as ~/Applications/IB Gateway */IB Gateway *.app and ~/Applications/Futu_OpenD.app. If IB Gateway or Futu OpenD is already open, it waits for the local API socket instead of opening the app again. Override paths if needed:

IBKR_GATEWAY_APP=/Users/you/Applications/IB Gateway 10.45/IB Gateway 10.45.app
FUTU_OPEND_APP=/Users/you/Applications/Futu_OpenD.app

To autofill the IB Gateway login prompt, keep credentials only in your local .env:

IBKR_USERNAME=your_ibkr_username
IBKR_PASSWORD=your_ibkr_password
IBKR_GATEWAY_TRADING_MODE=live

If macOS blocks autofill, grant your terminal app accessibility permission in System Settings -> Privacy & Security -> Accessibility. Two-factor approval still has to be completed manually. Set IBKR_GATEWAY_AUTOSTART=false, IBKR_GATEWAY_LOGIN_AUTOFILL=false, or FUTU_OPEND_AUTOSTART=false when you want to manage those apps yourself.

make dev prefers Homebrew Node at /opt/homebrew/bin/node or /usr/local/bin/node when present, so native frontend packages load with the same Node toolchain used by local npm installs. Set DEV_NODE_BIN=/path/to/node if you need to force another Node binary.

For an advisor account with multiple linked client/subaccounts, the worker auto-detects every account returned by IB Gateway managedAccounts(). Leave both account fields empty to reconcile all accessible subaccounts:

IBKR_ACCOUNT=
IBKR_ACCOUNTS=

Set IBKR_ACCOUNTS=DU1111111,DU2222222 only when you want an explicit allowlist. Set IBKR_ACCOUNT=DU1111111 if you want one account selected by default.

To keep specific accounts out of the frontend account dropdown without changing backend reconciliation, set a comma-separated UI-only hide list:

VITE_ACCOUNT_MENU_HIDDEN_ACCOUNTS=F1234567,U1234567

Run one read-only reconciliation:

make ibkr-once

Run the continuous ingestion worker:

make ibkr-worker

With BROKER_MODE=ibkr in .env, make dev runs the full local app with the worker included.

ENABLE_ORDER_SUBMISSION=false is the application kill switch for order placement. IBKR_API_READONLY controls the API session mode. Full order/fill reconciliation may require IBKR_API_READONLY=false, because Gateway/TWS read-only API mode can reject order and execution requests. The dashboard order ticket stays blocked unless ENABLE_ORDER_SUBMISSION=true, IBKR_API_READONLY=false, and the order preview passes account checks. Set ENFORCE_GROSS_EXPOSURE_LIMIT=false to keep exposure visible in the dashboard without blocking manual orders.

IBKR is not used for market-data quote requests. The dashboard uses a separate market-data router:

MARKET_DATA_US_PROVIDER=futu
MARKET_DATA_TW_PROVIDER=sinotrade
MARKET_DATA_GLOBAL_PROVIDER=yfinance
FUTU_OPEND_HOST=127.0.0.1
FUTU_OPEND_PORT=11111
FUTU_CONNECT_TIMEOUT_SECONDS=1
SINOTRADE_STREAM_INTERVAL_SECONDS=2
QUOTE_CACHE_TTL_SECONDS=10

For US stocks and ETFs, run Futu OpenD locally so order previews and watchlist refreshes can use Futu snapshots. For Taiwan stocks, Sinotrade/Shioaji resolves contract metadata and streams snapshot quotes. For other global symbols, use the Yahoo Finance ticker format; yfinance prices are labeled indicative and market orders are blocked from using them as execution-grade quotes. Limit orders can still use the explicit limit price you enter.

If you place a trade from IBKR Mobile, TWS, or another client, the worker should ingest it as an external trade through IBKR executions/positions and surface it in the dashboard after reconciliation.

Data Integrity

IBKR is treated as the source of truth. Each reconciliation:

  • Pulls current account values, positions, open orders, and executions from the selected IBKR account.
  • In advisor mode, repeats reconciliation for each detected or allowlisted subaccount in the same Gateway session.
  • Extracts IBKR ExchangeRate account-value rows and stores an FX snapshot for USD reporting.
  • Rewrites only the scoped current-position projection for that account, so stale local positions are removed.
  • Marks local open orders as NotOpen when IBKR no longer reports them as open.
  • Upserts fills by IBKR execution ID, making execution ingestion idempotent.
  • Builds normalized broker and database snapshots, hashes both, and stores a row in reconciliation_audits.
  • Updates sync_state.integrity_status plus position/order/fill mismatch counts for dashboard visibility.

The dashboard should show integrity_status=ok and zero mismatch counts after a healthy sync. If any post-reconciliation mismatch remains, treat the dashboard as stale and investigate before trusting P&L or position exposure.

Native prices and P&L are preserved in the database, but dashboard-facing prices, exposures, fills, commissions, and risk calculations are normalized into REPORTING_CURRENCY (USD by default).

Historical Backfill

For history before the local worker was running, use IBKR Activity Flex Queries in XML format. The equity curve needs Net Asset Value (NAV) Summary in Base. Group performance needs daily symbol-level position data, so create a second Activity Flex Query with:

  • Open Positions: Account ID, Currency, FX Rate to Base, Symbol, Conid, Report Date, Quantity, Mark Price, Position Value, Open Price or Cost Basis Price, Cost Basis Money, FIFO Unrealized PNL, Side, and Level of Detail. Summary level is preferred; lot-level rows are aggregated by symbol/date.
  • Mark-to-Market Performance Summary in Base: Account ID, Symbol, Conid, Report Date, Previous Close Quantity, Close Quantity, Close Price, Transaction MTM PNL, Prior Open MTM PNL, Commissions, Other, and Total.

Configure Flex Web Service in IBKR Portal, then set:

IBKR_FLEX_TOKEN=...
IBKR_FLEX_QUERY_ID=...
IBKR_FLEX_POSITION_QUERY_ID=...

Sinotrade Setup

Keep Sinotrade/Shioaji credentials only in your local .env, which is already ignored by git:

SINOTRADE_API_KEY=your_sinotrade_api_key
SINOTRADE_API_SECRET=your_sinotrade_api_secret
SINOTRADE_CERT_PATH=/absolute/path/to/Sinopac.pfx
SINOTRADE_CERT_PASSWORD=your_certificate_password
SINOTRADE_ENABLED=true
SINOTRADE_HISTORY_DAYS=90
SINOTRADE_TWD_TO_USD=0.031
SINOTRADE_ORDER_LOT=IntradayOdd
MARKET_DATA_TW_PROVIDER=sinotrade
SINOTRADE_STREAM_INTERVAL_SECONDS=2

These values are loaded by the backend as settings.sinotrade_api_key and settings.sinotrade_api_secret, with the certificate available as settings.sinotrade_cert_path. The optional certificate password is loaded as settings.sinotrade_cert_password. Do not add them as VITE_* variables, because those are exposed to the browser bundle.

When BROKER_MODE=ibkr, broker sync also reconciles the Sinotrade stock account if Sinotrade credentials are configured. The Settings screen can link raw IBKR and Sinotrade accounts into a single display account; dashboard, reports, positions, orders, and fills then aggregate through that linked account while order submission still routes to the broker account that owns the instrument. Taiwan stock orders route through Shioaji for Sinotrade accounts and stay behind ENABLE_ORDER_SUBMISSION; live orders also require certificate activation with the certificate password. The default order lot is IntradayOdd, so TW stock quantities are interpreted as shares (). Set SINOTRADE_ORDER_LOT=Common only when you intentionally want quantities interpreted as board lots ().

Run a date-range backfill:

FLEX_FROM=2026-01-01 FLEX_TO=2026-05-28 make flex-backfill

Or import a downloaded XML file:

cd backend
BROKER_MODE=ibkr uv run python -m app.workers.flex_backfill --file /path/to/flex.xml

Useful current references:

Operating Rules

  • Use paper mode first. Do not connect this to live trading until the full path has been tested with small orders and monitored exits.
  • Keep one unique IBKR_CLIENT_ID per running bot or dashboard process.
  • Record every account snapshot, order, fill, and strategy state before trusting P&L.
  • Add hard limits before enabling order submission: max order notional, max gross exposure, and a manual pause switch.
  • Do not run headless IB Gateway/TWS without planning for authentication prompts, reconnects, and daily restarts.

This is infrastructure, not trading advice.

About

Paper-first Interactive Brokers trading dashboard

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors