A personal Android trading client for Interactive Brokers, with UI inspired by Longbridge / ้ฟๆกฅ่ฏๅธ.
Built because the official IBKR mobile app is slow, ugly, and missing the things a Longbridge user takes for granted: snappy K-lines with crosshair, intraday session classification (็ๅ / ็ไธญ / ็ๅ / ๅค็), red-up / green-down (็บขๆถจ็ปฟ่ท), aggregated positions PnL, one-tap quick-actions on long-press, and a UI that doesn't look like it was designed in 2008.
โ ๏ธ Disclaimer This is a personal project for educational purposes. It is not financial advice, not affiliated with Interactive Brokers or Longbridge, and is provided as-is with no warranty. Trading involves substantial risk of loss. Use a paper account first. You are solely responsible for any orders submitted through this software.
From left: Positions with aggregated PnL ยท K-line with MA/MACD/cost basis ยท Intraday with VWAP and pre/regular/post/overnight session coloring ยท Market overview with movers
More screenshots
Long-press quick actions ยท Company panorama ยท Financials ยท Watchlist empty state
All screenshots are from the bundled mock mode running against a freshly-built emulator โ no real account is needed to see the app like this. See Quickstart below.
Don't have an IBKR or LongPort account and just want to look around?
git clone https://github.com/whtis/ibkr-mobile.git
cd ibkr-mobile/backend
cp .env.example .env
echo "MOCK_MODE=yes" >> .env # one line is the only required change
echo "API_TOKEN=anything-you-want" >> .env
uv sync
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000Then in another shell:
cd ibkr-mobile/android
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apkOpen the app โ Settings โ backend URL http://10.0.2.2:8000 (emulator) or http://<your-LAN-ip>:8000 (real device), token = whatever you put in .env โ save. Done. Mock mode serves a synthesized portfolio of AAPL, TSLA, NVDA, MSFT, GOOGL, BABA, SPY with realistic K-lines, intraday charts, and option chains.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Android App (Kotlin + Jetpack Compose) โ
โ - Material 3, Canvas charts โ
โ - WebSocket realtime quotes โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโ
โ HTTPS + Bearer token
โ (LAN Wi-Fi, Tailscale, or public TLS)
โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโ
โ FastAPI + ib_async โ
โ - REST + WebSocket โ
โ - SQLite (execution history) โ
โ - LongPort SDK (free L1 quotes) โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโ
โ TWS binary socket :4002 (paper) / :4001 (live)
โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโ
โ IB Gateway in Docker (gnzsnz image) โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
IBKR servers
The backend is the only place that holds IBKR credentials. The Android app authenticates via a bearer token, configurable in Settings. No credentials, accounts, or executions ever live on the phone outside of an OS-protected DataStore.
- Watchlist with auto-refresh and pull-to-update
- Search by symbol with debounced lookup
- Stock detail page with three sub-tabs (่กๆ
/ ๅ
จๆฏ / ่ดขๅก):
- Native Canvas K-line chart (
1m / 5m / 15m / 30m / 60m / 1d / 1w / 1mo) with crosshair, zoom, pan - Intraday chart with 4-channel session classification (pre-market / regular / after-hours / overnight)
- MACD sub-chart with linked crosshair
- Company info + key metrics (P/E TTM, P/B, EPS, BPS, dividend yield)
- Native Canvas K-line chart (
- Fullscreen chart mode (landscape lock + larger viewport)
- Option chain browser with strike grid and side/expiry selector
- Aggregated PnL across positions (more accurate than IBKR per-account summary)
- Four sort modes: market value / unrealized PnL / daily PnL / symbol
- Long-press quick actions: ๅ ไป / ๅไป / ๆฅ็่ฏฆๆ / ๅคๅถไปฃ็
- Active orders panel with one-tap cancel
- Market / Limit / Stop / Stop-Limit
- TIF: DAY / GTC / IOC / FOK
- RTH-only toggle
- Stocks + Options (with expiry / strike / right pre-filled from option chain)
- Live preview of margin impact
- Single WebSocket multiplex with ref-counted subscriptions
- Auto-reconnect with exponential backoff
- LongPort L1 stream (free, real-time) + IBKR fallback
- ็บขๆถจ็ปฟ่ท (Chinese convention: red = up, green = down)
- Material 3 dark theme tuned to Longbridge palette
- Adaptive icon (animated
Cpulse) - Bottom-bar navigation, 4 tabs (่ช้ / ่กๆ / ๆไป / ่ฎพ็ฝฎ)
Backend (backend/)
- FastAPI, Uvicorn, Pydantic v2
ib_async2.1.0 โ modern async IBKR API clientlongportPython SDK 3.0.23 โ free L1 quotes + K-lines- SQLite for execution history persistence
uvfor dependency management- Docker Compose +
gnzsnz/ib-gateway
Android (android/)
- Kotlin 2.1.20, Jetpack Compose BOM 2026.05.01
- Material 3
- Navigation Compose 2.9.8
- Ktor + OkHttp engine (HTTP + WebSocket)
- DataStore (settings persistence)
- kotlinx.serialization (JSON)
- Compose Canvas (native charts โ no WebView)
- AGP 8.10.1 / Gradle 8.13 / minSdk 26 / compileSdk 36
cd backend
cp .env.example .env
# edit .env โ at minimum:
# TWS_USERID=<your IBKR paper-trading username>
# TWS_PASSWORD=<your IBKR paper-trading password>
# API_TOKEN=$(openssl rand -hex 32)
# LONGPORT_APP_KEY / SECRET / ACCESS_TOKEN โ optional, but recommended for free quotes
# (get at https://open.longportapp.com โ Developer Center โ Create App)
#
# OR: skip the IBKR + LongPort fields entirely and set MOCK_MODE=yes for synthetic data.
# See "Try it in 60 seconds" above.
# pull and start IB Gateway (Docker)
docker compose pull
docker compose up -d
# wait ~30s, watch Gateway login
docker compose logs -f ibgateway # look for "API server listening on port 4002"
# install Python deps (first time only)
uv sync
# run FastAPI
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000 --reloadVerify:
TOKEN=$(grep ^API_TOKEN .env | cut -d= -f2)
curl -s http://localhost:8000/health | jq # ib_connected: true
curl -s -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/account/positions | jqSee backend/README.md for the full smoke-test set and troubleshooting.
cd android
./gradlew assembleDebug
# APK at: app/build/outputs/apk/debug/app-debug.apkInstall on device:
adb install -r app/build/outputs/apk/debug/app-debug.apkOpen the app โ Settings tab โ fill in:
- Backend URL:
http://<Mac LAN IP>:8000(orhttps://your.domainif cloud-hosted) - API Token: same value as
API_TOKENin backend.env - Tap ๆต่ฏ่ฟๆฅ โ should turn green
- Tap ไฟๅญ
Now the Positions / Market / Watchlist tabs will populate from your paper account.
Quote-only deployments (LongPort quotes without IBKR trading) can run on any small VPS โ no Mac required for the Gateway:
# on your server
git clone <this repo>
cd ibkr-mobile/backend
cp .env.example .env
# fill LONGPORT_* keys, leave TWS_* blank or set READ_ONLY_API=yes
uv sync
# put behind nginx + Let's Encrypt + systemd, point your domain at itFull IBKR trading needs IB Gateway, which has stricter networking + 2FA requirements. The Mac-as-gateway model is recommended for the trading path; the VPS can serve as a public quote endpoint.
ibkr-mobile/
โโโ android/ Kotlin + Compose app
โ โโโ app/src/main/
โ โ โโโ java/com/tis/ibkr/
โ โ โ โโโ data/ API client, DataStore, models
โ โ โ โโโ ui/screens/ Watchlist, Market, Positions, StockDetail, ...
โ โ โ โโโ ui/components/ Charts, sub-bars, quick-action sheet
โ โ โ โโโ ui/theme/ Longbridge-inspired Material 3 theme
โ โ โ โโโ viewmodel/ One ViewModel per screen
โ โ โโโ res/ icons (adaptive), strings
โ โโโ gradle/libs.versions.toml
โโโ backend/ FastAPI + ib_async
โ โโโ app/
โ โ โโโ main.py FastAPI app, lifespan, CORS
โ โ โโโ ibkr.py IB Gateway connector
โ โ โโโ longbridge.py LongPort SDK wrapper
โ โ โโโ db.py SQLite executions store
โ โ โโโ auth.py Bearer auth dependency
โ โ โโโ routes/
โ โ โโโ account.py summary, positions
โ โ โโโ orders.py place, cancel, list active
โ โ โโโ executions.py history (SQLite + ib_async)
โ โ โโโ options.py option chain, contract lookup
โ โ โโโ quote.py quote, intraday, bars (K-line)
โ โ โโโ ws_quotes.py WebSocket realtime stream
โ โโโ docker-compose.yml IB Gateway container
โ โโโ pyproject.toml
โโโ BUILD.md Architecture decisions
โโโ DESIGN_NOTES.md UI/UX spec referencing Longbridge
โโโ ONBOARDING.md Setup gotchas (IBKR auth quirks, etc.)
โโโ README.md โ you are here
| Decision | Reason |
|---|---|
| Compose Canvas charts (not WebView) | First paint ~50 ms vs ~800 ms for a WebView+JS chart lib. Memory ~5 MB vs ~50 MB. |
| LongPort SDK + IBKR fallback | LongPort gives free real-time L1 + K-lines for US + HK + CN; IBKR market data is paid. Trading still goes through IBKR. |
| ib_async over ibapi/ib-insync | ib-insync is no longer maintained; ib_async is its modern fork, native async, type-hinted. |
gnzsnz/ib-gateway in Docker |
Headless IB Gateway with IBC auto-login, VNC for debugging, daily restart. Avoids manual Gateway baby-sitting. |
| One Mac as the gateway host | Gateway needs persistent network identity + 2FA approval. A always-on Mac is simpler than fighting Docker NAT + cloud IP rotation. |
| WebSocket subscription multiplex with refcounts | Multiple Compose screens can subscribe to the same symbol cheaply; cleanup is automatic when the last subscriber leaves. |
| SQLite execution history | IBKR reqExecutions only returns 7 days. Local persistence preserves full history. |
uv over pip |
~10ร faster install, lockfile, pyproject-native. |
ROADMAP.mdโ v2 plan: multi-user, Longbridge SDK in-app, dynamic Gateway lifecycleBUILD.mdโ architecture, topology, decisionsDESIGN_NOTES.mdโ Longbridge UI spec, screen-by-screenONBOARDING.mdโ IBKR auth gotchas + Android/Gradle troubleshootingbackend/README.mdโ backend setup + smoke testsopenspec/changes/multi-user-v2/โ v2 detailed spec (proposal, design, tasks)
- v1: shipped, in personal daily use. Single-user, paper account.
- v2: design complete, implementation pending โ see
ROADMAP.md. Adds per-device multi-user, moves Longbridge SDK in-app, makes the backend credential-free.
Active development. APIs and screens may change without notice.
Tested with: Pixel-class Android devices and emulators, IBKR paper account, LongPort developer account.
PRs are welcome, especially ones aligned with ROADMAP.md. For anything non-trivial, please open an issue first so we can discuss direction before you write the code.
See CONTRIBUTING.md for dev setup, code style, what kinds of PRs are most likely to land, and what to avoid. Note that mock mode (described in Quickstart above) lets you contribute without any brokerage credentials โ start there.
Looking for somewhere to start? Check the good first issue and help wanted labels.
This project follows a Code of Conduct.
MIT. See LICENSE.
The MIT license grants you broad rights, but it does not grant you a right to safety. Read the disclaimer above. Trade with money you can afford to lose.
Built by @whtis. Contributions from anyone who finds it useful.







