feat: IBKR broker connector + trading infrastructure improvements#81
Merged
luokerenx4 merged 17 commits intomasterfrom Mar 22, 2026
Merged
feat: IBKR broker connector + trading infrastructure improvements#81luokerenx4 merged 17 commits intomasterfrom
luokerenx4 merged 17 commits intomasterfrom
Conversation
- 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Test plan
pnpm buildpassespnpm test— 880 tests passpnpm test:e2e— 31 passed, 17 skipped (IBKR skipped when TWS unavailable)🤖 Generated with Claude Code