ib_trade_dart 0.1.2 copy "ib_trade_dart: ^0.1.2" to clipboard
ib_trade_dart: ^0.1.2 copied to clipboard

Interactive Brokers (IBKR) Client Portal SDK for Dart and Flutter, supporting portfolio adaptation, order lifecycle, market data, and real-time streaming.

Interactive Brokers Trading SDK for Dart & Flutter #

A high-performance, cross-platform Dart & Flutter SDK for interacting with the Interactive Brokers (IBKR) Client Portal Gateway.

This SDK is divided into two modules:

  1. ib_trade_core: The foundational networking and protocol layer (Session tracking, HTTP cookie jar, WebSocket reconnects, and Automatic Challenge/Compliance resolution).
  2. ib_trade_dart: The high-level developer-facing service layer (Market data snapshots, real-time streams, orders, portfolio, and scanner services).

Architecture Overview #

graph TD
    UI[Flutter UI Dashboard / Buttons] -->|Service Invocation| Services[SDK Service Layer: MarketData, Order, Scanner]
    Services -->|HTTP & WS API Calls| SDKClient[IbTradeClient & IbWebSocketManager]
    SDKClient -->|Cookie Management & Keepalive| Session[CookieClient & SessionTickler]
    SDKClient -->|Challenge Interception| Challenge[ChallengeHandler & AutoConfirm]
    SDKClient -->|Platform Socket Handlers| WSConn[IbWebSocketConnection]
    Session -->|REST Requests| Gateway((IBKR Gateway API))
    WSConn -->|WS Frames| Gateway

UI Button Interaction Map #

Below is a detailed trace of how typical trading dashboard UI buttons map to code executions: from the button callback, through the SDK service, to the core transport logic, and finally to the gateway REST/WS endpoints.

1. Connect Gateway Button #

  • UI Trigger Action: User clicks the "Connect Gateway" button to start real-time streams.
  • UI Code Example:
    onPressed: () async {
      await wsManager.connect();
    }
    
  • SDK Method / Function: IbWebSocketManager.connect (located in lib/src/websocket/ib_websocket_manager.dart)
  • Core Method / Function: IbWebSocketConnection.connect (located in lib/src/client/websocket_connection.dart of ib_trade_core) $\rightarrow$ delegates to the factory function connectWebSocket (located in lib/src/client/websocket_impl.dart of ib_trade_core).
  • Gateway Target: wss://localhost:5000/v1/api/ws (WebSocket connection endpoint)
  • Execution Flow:
    1. The UI button calls the connect() function in IbWebSocketManager.
    2. IbWebSocketManager calls the low-level _connection.connect() in IbWebSocketConnection.
    3. IbWebSocketConnection updates connection state to IbWebSocketState.connecting.
    4. It reads active session cookies from the getter CookieClient.cookies (located in lib/src/client/cookie_client.dart of ib_trade_core) and appends them to the WebSocket connection headers as Cookie: ....
    5. It executes connectWebSocket() which uses VM-specific (websocket_impl_io.dart) or web-specific (websocket_impl_web.dart) bindings to establish the socket.
    6. On success, state becomes IbWebSocketState.connected. A background periodic timer fires a tickle string frame every 45 seconds to keep the socket alive.

2. Check Auth Status Button #

  • UI Trigger Action: User opens the application or clicks "Verify Authentication" to check if the session is validated.
  • UI Code Example:
    onPressed: () async {
      final status = await client.checkAuthStatus();
      setState(() => _authenticated = status.authenticated);
    }
    
  • SDK Method / Function: IbTradeClient.checkAuthStatus (located in lib/src/client/ib_trade_client.dart)
  • Core Method / Function: IbTradeClient.get helper (located in lib/src/client/ib_trade_client.dart) $\rightarrow$ delegates to the underlying CookieClient.get (located in lib/src/client/cookie_client.dart of ib_trade_core).
  • Gateway Target: GET /iserver/auth/status
  • Execution Flow:
    1. UI calls checkAuthStatus() which performs a REST GET request relative to the gateway config.
    2. The request is processed by CookieClient, appending the current session cookie (rekey, JSESSIONID, etc.).
    3. The gateway returns a JSON payload containing authentication flags.
    4. If response code is 200, the JSON is parsed into an AuthStatus object and returned to the UI. If not, an IbException is thrown.

3. Start Session Keeper Button / Switch #

  • UI Trigger Action: User toggles a switch to run keep-alive pings in the background.
  • UI Code Example:
    onChanged: (enabled) {
      enabled ? client.startSessionTickler() : client.stopSessionTickler();
    }
    
  • SDK Method / Function: IbTradeClient.startSessionTickler (located in lib/src/client/ib_trade_client.dart)
  • Core Method / Function: SessionTickler.start (located in lib/src/client/session_tickler.dart of ib_trade_core)
  • Gateway Target: GET /tickle
  • Execution Flow:
    1. UI calls startSessionTickler(), which invokes SessionTickler.start().
    2. The tickler fires an immediate asynchronous keepalive request to /tickle.
    3. A periodic Timer triggers the request again every 45 seconds.
    4. Incoming response cookies (set-cookie header) are captured by CookieClient to renew the local cookie jar automatically.

4. Get Price Snapshot Button #

  • UI Trigger Action: User clicks "Refresh Price" on a static watchlist item.
  • UI Code Example:
    onPressed: () async {
      final list = await marketDataService.getSnapshot([265598]); // conid for AAPL
      setState(() => _price = list.first.last);
    }
    
  • SDK Method / Function: MarketDataService.getSnapshot (located in lib/src/market_data/market_data_service.dart)
  • Core Method / Function: IbTradeClient.get helper (located in lib/src/client/ib_trade_client.dart) $\rightarrow$ delegates to the underlying CookieClient.get (located in lib/src/client/cookie_client.dart of ib_trade_core).
  • Gateway Target: GET /iserver/marketdata/snapshot?conids=265598
  • Execution Flow:
    1. UI triggers getSnapshot(), which joins contract IDs with commas.
    2. It executes a query parameters-enriched REST GET request through the IbTradeClient.
    3. The JSON response (an array of ticker property-value pairs) is decoded and mapped into MarketDataSnapshot objects.

5. Subscribe Real-Time Ticks Switch #

  • UI Trigger Action: User clicks "Stream Market Data" to listen to live ticks.
  • UI Code Example:
    onChanged: (active) {
      if (active) {
        marketDataService.subscribeMarketData([265598]);
      } else {
        marketDataService.unsubscribeMarketData([265598]);
      }
    }
    
  • SDK Method / Function: MarketDataService.subscribeMarketData (located in lib/src/market_data/market_data_service.dart)
  • Core Method / Function: IbWebSocketManager.subscribeMarketData (located in lib/src/websocket/ib_websocket_manager.dart) $\rightarrow$ delegates to IbWebSocketConnection.send (located in lib/src/client/websocket_connection.dart of ib_trade_core).
  • Gateway Target: WebSocket frame text 'smd+265598+{"fields":[]}'
  • Execution Flow:
    1. UI calls subscribeMarketData(), which requests the IbWebSocketManager to transmit a subscription command.
    2. The manager converts this to the IBKR WebSocket command format smd+{conid}+{json_fields_string}.
    3. IbWebSocketConnection validates that the socket is connected, and sends the string payload.
    4. Incoming real-time market ticks match the smd+ topic key, prompting IbWebSocketManager to parse and push them down marketDataStream (located in lib/src/websocket/ib_websocket_manager.dart) for the UI to consume.

6. Run Market Scanner Button #

  • UI Trigger Action: User clicks "Find Gainers" to execute a stock scan filter.
  • UI Code Example:
    onPressed: () async {
      final req = ScannerRequest(instrument: 'STK', type: 'TOP_PERC_GAIN', location: 'STK.US.MAJOR');
      final list = await scannerService.runScanner(req);
      setState(() => _scannedStocks = list);
    }
    
  • SDK Method / Function: ScannerService.runScanner (located in lib/src/scanner/scanner_service.dart)
  • Core Method / Function: IbTradeClient.post (located in lib/src/client/ib_trade_client.dart) $\rightarrow$ delegates to the underlying CookieClient.post (located in lib/src/client/cookie_client.dart of ib_trade_core).
  • Gateway Target: POST /iserver/scanner/run
  • Execution Flow:
    1. UI calls runScanner() which serializes the ScannerRequest object.
    2. IbTradeClient transmits a POST request to /iserver/scanner/run.
    3. The array response payload containing rankings, tickers, and prices is parsed into a List<ScannerResult> where the rank index is appended to each item.

7. Submit Order Button (BUY/SELL) #

  • UI Trigger Action: User clicks "Submit Trade" or "Buy Shares".
  • UI Code Example:
    onPressed: () async {
      final req = OrderRequest(acctId: 'U1234567', conid: 265598, orderType: OrderType.limit, side: OrderSide.buy, quantity: 50, price: 175.5);
      final status = await orderService.placeOrder('U1234567', req);
      print('Order placed: ${status.first.status}');
    }
    
  • SDK Method / Function: OrderService.placeOrder (located in lib/src/orders/order_service.dart)
  • Core Method / Function: IbTradeClient.post (located in lib/src/client/ib_trade_client.dart) $\rightarrow$ delegates to ChallengeHandler.handleChallenge (located in lib/src/client/challenge_handler.dart of ib_trade_core).
  • Gateway Target: POST /iserver/account/U1234567/orders
  • Execution Flow:
    1. UI calls placeOrder() which wraps the order details inside a payload structure containing 'orders': [...].
    2. It calls IbTradeClient.post().
    3. Challenge Check: If the gateway returns a compliance challenge (e.g. asking the user to confirm trading outside regular hours or confirming risks), the client intercepts it.
    4. ChallengeHandler (located in lib/src/client/challenge_handler.dart of ib_trade_core) iterates through registered resolvers. By default, it automatically confirms the challenge and replies to POST /iserver/reply/{challengeId} with {"confirmed": true}.
    5. After resolving, the client automatically retries the initial POST request.
    6. The final array of order statuses is parsed into List<OrderStatus> and returned.

8. Cancel Order Button #

  • UI Trigger Action: User clicks "Cancel" next to a pending order in their list.
  • UI Code Example:
    onPressed: () async {
      final success = await orderService.cancelOrder('U1234567', '987654321');
      if (success) _refreshOrders();
    }
    
  • SDK Method / Function: OrderService.cancelOrder (located in lib/src/orders/order_service.dart)
  • Core Method / Function: IbTradeClient.delete (located in lib/src/client/ib_trade_client.dart) $\rightarrow$ delegates to CookieClient.delete (located in lib/src/client/cookie_client.dart of ib_trade_core).
  • Gateway Target: DELETE /iserver/account/U1234567/order/987654321
  • Execution Flow:
    1. UI calls cancelOrder() which initiates a REST DELETE request with the accounts and orders parameters.
    2. On receiving a HTTP 200 response from the gateway, it returns true indicating the cancellation request was logged.

9. Get Positions Button #

  • UI Trigger Action: User clicks the "Portfolio" tab in the dashboard navigation.
  • UI Code Example:
    onPressed: () async {
      final list = await orderService.getPositions('U1234567');
      setState(() => _positions = list);
    }
    
  • SDK Method / Function: OrderService.getPositions (located in lib/src/orders/order_service.dart)
  • Core Method / Function: IbTradeClient.get (located in lib/src/client/ib_trade_client.dart) $\rightarrow$ delegates to CookieClient.get (located in lib/src/client/cookie_client.dart of ib_trade_core).
  • Gateway Target: GET /portfolio/U1234567/positions/0
  • Execution Flow:
    1. UI calls getPositions() which performs a GET request for a specific portfolio page (defaults to page 0).
    2. The list of active holdings is returned by the gateway.
    3. The SDK parses JSON elements (position sizes, market value, unrealized PnL) into Position instances.

10. Subscribe Account Summary Switch #

  • UI Trigger Action: User opens their dashboard page and wants live balance updates.
  • UI Code Example:
    onChanged: (active) {
      if (active) {
        wsManager.subscribeAccount();
      } else {
        wsManager.unsubscribeAccount();
      }
    }
    
  • SDK Method / Function: IbWebSocketManager.subscribeAccount (located in lib/src/websocket/ib_websocket_manager.dart)
  • Core Method / Function: IbWebSocketConnection.send (located in lib/src/client/websocket_connection.dart of ib_trade_core)
  • Gateway Target: WebSocket frame text 'sact+{}'
  • Execution Flow:
    1. UI calls subscribeAccount(), which calls the core socket layer to transmit 'sact+{}'.
    2. Incoming frames prefixed with topic sact (or containing fields like netLiquidation) are captured by the parser in IbWebSocketManager._parseAndDispatch.
    3. The manager serializes these frames into AccountSummary updates and broadcasts them to the accountSummaryStream (located in lib/src/websocket/ib_websocket_manager.dart).

Detailed Data Flows #

HTTP Challenge Resolution Cycle (Order Placement Example) #

sequenceDiagram
    autonumber
    participant UI as Flutter UI
    participant Service as OrderService
    participant Client as IbTradeClient
    participant Handler as ChallengeHandler
    participant Jar as CookieClient
    participant Gateway as IBKR Gateway

    UI->>Service: placeOrder(acctId, OrderRequest)
    Service->>Client: post('iserver/account/.../orders', body)
    Client->>Jar: post(uri, headers, body)
    Note over Jar: Inject session cookies
    Jar->>Gateway: POST /iserver/account/{acctId}/orders
    Gateway-->>Jar: HTTP 200 [{'id': 'challenge_1', 'type': 'warning', 'message': 'Confirm RTH'}]
    Jar-->>Client: Response (Contains Challenge JSON)
    
    rect rgb(30, 30, 46)
        Note over Client: Challenge Detected!
        Client->>Handler: handleChallenge(challenge)
        Handler->>Handler: Resolve (AutoConfirm -> true)
        Handler->>Jar: post('/iserver/reply/challenge_1', '{"confirmed": true}')
        Jar->>Gateway: POST /iserver/reply/challenge_1
        Gateway-->>Jar: HTTP 200 {"status": "ok"}
        Jar-->>Handler: True (Challenge cleared)
    end

    Client->>Jar: Retry original post(uri, headers, body)
    Jar->>Gateway: POST /iserver/account/{acctId}/orders
    Gateway-->>Jar: HTTP 200 [{"order_id": "123", "status": "Submitted"}]
    Jar-->>Client: Response (Successful Order Placement)
    Client-->>Service: List<OrderStatus>
    Service-->>UI: List<OrderStatus> (Display Order Status)

WebSocket Dispatch Loop #

sequenceDiagram
    autonumber
    participant Gateway as IBKR Gateway
    participant Conn as IbWebSocketConnection
    participant Manager as IbWebSocketManager
    participant UI as Watchers / Streams

    Gateway->>Conn: Raw WS Frame (Text)
    Conn->>Manager: Push to message stream
    
    rect rgb(35, 45, 35)
        Note over Manager: Parse Frame topic & JSON
        alt topic starts with 'smd+' or contains '_field'
            Manager->>UI: marketDataStream (MarketDataTick)
        else topic starts with 'sor' or contains 'orderId'
            Manager->>UI: orderStatusStream (OrderStatus)
        else topic starts with 'sact' or contains 'netLiquidation'
            Manager->>UI: accountSummaryStream (AccountSummary)
        end
    end

SDK Module Function Reference #

1. ib_trade_dart #

IbTradeClient

  • Constructor: IbTradeClient({GatewayConfig? config, HttpClient? httpClient, CookieClient? cookieClient, SessionTickler? sessionTickler, ChallengeHandler? challengeHandler})
    • Location: lib/src/client/ib_trade_client.dart:L28-L47
    • Description: Instantiates the central high-level client. Sets up default cookie client, session tickler, and challenge-handling instances if they are not explicitly injected.
  • Future<AuthStatus> checkAuthStatus()
  • void startSessionTickler()
  • void stopSessionTickler()
  • Future<http.Response> get(String endpoint, {Map<String, String>? headers, Map<String, dynamic>? queryParameters})
  • Future<http.Response> post(String endpoint, {dynamic body, Map<String, String>? headers, bool autoHandleChallenges = true})
    • Location: lib/src/client/ib_trade_client.dart:L123-L157
    • Description: Executes an HTTP POST request relative to the gateway's base URI. When autoHandleChallenges is true, intercepts and confirms compliance alerts, then retries the request.
  • Future<http.Response> delete(String endpoint, {Map<String, String>? headers})
  • Future<http.Response> resolveChallengesAndRetry(dynamic responseJson, Future<http.Response> Function() retryAction)
  • void close()

MarketDataService

OrderService

ScannerService

IbWebSocketManager


2. ib_trade_core #

GatewayConfig

CookieClient

SessionTickler

ChallengeHandler

IbWebSocketConnection

2
likes
160
points
85
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Interactive Brokers (IBKR) Client Portal SDK for Dart and Flutter, supporting portfolio adaptation, order lifecycle, market data, and real-time streaming.

Repository (GitHub)
View/report issues

License

Apache-2.0 (license)

Dependencies

http, ib_trade_core, meta

More

Packages that depend on ib_trade_dart