Skip to content

feat: IBKR broker connector + trading infrastructure improvements#81

Merged
luokerenx4 merged 17 commits intomasterfrom
dev
Mar 22, 2026
Merged

feat: IBKR broker connector + trading infrastructure improvements#81
luokerenx4 merged 17 commits intomasterfrom
dev

Conversation

@luokerenx4
Copy link
Contributor

Summary

  • IbkrBroker implementation: Full IBroker adapter for Interactive Brokers TWS/Gateway via RequestBridge (callback→Promise bridging layer). Supports all IBroker methods: account data, positions, contract search, order lifecycle, market data snapshots, market clock.
  • Health/data pipeline separation: UTA queries check health before calling broker; offline accounts return 503 with nudgeRecovery instead of 500 errors.
  • Structured error system: BrokerError with typed codes (CONFIG, AUTH, NETWORK, EXCHANGE, MARKET_CLOSED) flows from broker → tool → AI with retry guidance.
  • Platform layer elimination: Removed unnecessary Platform→Broker→UTA hierarchy; one config = one UTA.
  • E2E test infrastructure: Standardized beforeEach skip pattern, split test files by broker, added IBKR broker-level and UTA-level E2E tests, refactored setup.ts safety mechanism to unified isPaper().
  • Credential handling: Replaced hardcoded apiKey/apiSecret assumptions with convention-based masking and per-broker credential ownership.
  • SDK fix: Clear handshake timeout on success to prevent uncaught TypeError crash.

Test plan

  • pnpm build passes
  • pnpm test — 880 tests pass
  • pnpm test:e2e — 31 passed, 17 skipped (IBKR skipped when TWS unavailable)
  • Manual: verify IBKR connection with TWS paper trading
  • Manual: verify frontend IBKR account creation wizard

🤖 Generated with Claude Code

Ame and others added 17 commits March 22, 2026 09:30
- Replace star SVG with alice.ico pixel art across sidebar logo, chat avatar, and empty state
- Restructure sidebar nav into 4 labeled sections (Core/Agent/Data/System)
- Add blue-to-purple gradient accent on primary buttons and active indicator
- Add assistant chat bubble background/border for visual separation
- Float chat input area with top shadow
- Bump PageHeader title to text-lg font-bold
- Enlarge Portfolio hero metrics numbers with tabular-nums
- Add button press scale effect, card hover glow, sidebar indicator animation
- Change toast animation to slide-in from right
- Upgrade EmptyState with default icon container, Spinner with accent tint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop the blue-to-purple gradient on primary buttons and sidebar indicator —
too "AI starter template". Buttons now use bg-tertiary + border with subtle
accent border on hover, matching the GitHub Dark aesthetic. Sidebar active
indicator back to solid accent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cancelOrder returned boolean, causing parseOperationResult to reject all
cancel operations (typeof boolean !== 'object'). Changed to PlaceOrderResult
across IBroker interface and all 3 broker implementations.

parseOperationResult now derives status from orderState.status instead of
hardcoding 'submitted' — Filled/Cancelled/Inactive map correctly. Async
exchanges unaffected (they return Submitted → 'submitted' as before).

Also fixed modifyOrder parameter type: Order → Partial<Order> to match
actual usage, removing the unsafe cast in UTA dispatcher.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a third backend option "OpenAI" to the AI Provider page as a
simplified facade over Vercel AI SDK (provider=openai). Shows a
streamlined form with just model selector, API key, and base URL.
Auto-detects OpenAI config on page load. Full Vercel AI SDK form
remains for advanced users.

Also replace gradient buttons with muted dark style (bg-tertiary +
border) and change sidebar active indicator to solid accent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Platform (IPlatform, CcxtPlatform, AlpacaPlatform) was a stateless factory
with zero business logic — just merged config fields and called `new Broker()`.

Now: accounts.json carries all fields (type, exchange, sandbox, credentials)
directly. `createBroker(config)` replaces the entire Platform layer.

- Merge platform + account schemas into discriminated union on `type`
- Delete CcxtPlatform.ts, AlpacaPlatform.ts, IPlatform interface
- Simplify main.ts init: single loop, no platformRegistry
- Simplify reconnect: no platform rebuild
- Remove platform CRUD routes, flatten UI to account-only
- Remove UTA.platformId (no longer needed)

Result: -431 lines, one config file, cleaner mental model.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r logging

Backend switch only updated the `backend` field, leaving `provider`/`model`
from the previous backend (e.g. gpt-5.2-pro) — causing Agent SDK to crash
with an opaque "exit code 1". Unified all switch branches into a single
BACKEND_DEFAULTS lookup. Also surface stderr/cause/stack in Agent SDK errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BrokerError.from() classifies raw SDK errors by message patterns into
typed error codes (NETWORK, AUTH, EXCHANGE, MARKET_CLOSED, CONFIG).

All broker query methods now wrap errors as BrokerError instead of
letting raw Error objects escape. UTA._callBroker() adds a safety net
for any remaining untyped errors.

Tool layer catches errors on query tools (getAccount, getPortfolio,
getOrders, getQuote, getMarketClock) and returns structured responses
with { error, code, transient, hint } so the AI can decide whether
to retry or report to the user.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
uta-real-broker: sync assertions now handle both synchronous fills
(status='filled' at push time, sync has nothing to do) and async
fills (status='submitted', sync confirms fill).

alpaca-paper: split into connectivity (any time) and trading (market
hours only) groups. Non-trading tests no longer fail when market is
closed — they skip gracefully instead of hitting 403.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clearer naming: broker-level tests (alpaca-paper, ccxt-bybit) vs
UTA-level tests (uta-alpaca, uta-bybit, uta-lifecycle).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace per-test `if (!broker) return` / `if (!marketOpen) return` with
beforeEach(({ skip }) => ...) at describe level. Tests now show as
'skipped' (yellow) instead of 'passed' (green) when preconditions aren't met.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Last 3 instances of `if (!x) return` in test bodies converted to
`skip()` for accurate reporting.

Added E2E README documenting conventions: file naming, precondition
pattern (beforeEach skip), market hours handling, setup.ts usage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Position interface now matches IBKR's updatePortfolio() parameters —
removed leverage, margin, liquidationPrice (crypto-specific fields).

FundingRate, OrderBook, OrderBookLevel moved from shared types.ts
to ccxt-types.ts with a new CcxtPosition extension type.

CCXT tool descriptions now indicate "(CCXT/crypto accounts only)".
Added localSymbol semantics documentation to contract-ext.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Data queries (getAggregatedEquity, searchContracts, web routes) now
check UTA health before calling broker APIs:

- Offline UTA → skip + nudgeRecovery() (resets backoff timer for
  immediate retry) + return null/503
- Healthy UTA call fails → 503 (transient) or 500 (permanent)
- Write operations (push/reject) unchanged — failures must surface

Removed throttled console.warn from getAggregatedEquity — health
changes already pushed to frontend via SSE.

Added queryAccount() helper in trading routes for consistent
health-aware error handling with proper HTTP status codes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…teway

Implements the full IBroker interface for IBKR via a RequestBridge layer
that bridges the callback-based @traderalice/ibkr SDK to Promise-based
IBroker methods. Three callback routing modes: reqId-based (contract
search, market data snapshots), orderId-based (order lifecycle), and
single-slot (account download for positions + account values).

New files: ibkr-types.ts, ibkr-contracts.ts, request-bridge.ts,
IbkrBroker.ts. Integrated into config schema, broker factory, and UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add `paper` field to IBKR config schema (default true)
- Refactor E2E setup.ts: replace hardcoded isSafe with unified isPaper()
  function that works across all broker types (ccxt sandbox/demo, alpaca
  paper, ibkr paper). Add TCP reachability check for local-process brokers.
- New ibkr-paper.e2e.spec.ts: broker-level tests (connectivity + trading)
- New uta-ibkr.e2e.spec.ts: UTA Trading-as-Git lifecycle test
- Update E2E README with IBKR documentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- main.ts: remove apiKey gate — let each broker's init() validate its
  own credentials (BrokerError CONFIG for missing keys)
- trading-config.ts: replace hardcoded apiKey/apiSecret masking with
  convention-based maskSecrets() (matches key|secret|password|token);
  remove credential check from test-connection endpoint
- TradingPage.tsx: create wizard supports IBKR (1-step, no credentials);
  edit panel credentials section keyed by broker type, not negation
- SDKSelector.tsx: add IBKR to platform type options

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
waitForHandshake() registered a setTimeout but never cleared it after
resolve. 10s later the timeout callback fired, hit this.conn (already
null after disconnect) and threw an uncaught TypeError that crashed
the process.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@luokerenx4 luokerenx4 merged commit 680cf40 into master Mar 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant