| 1 | # Interactive Brokers Provider |
| 2 | |
| 3 | ## CURRENT STATE |
| 4 | |
| 5 | The repository contains an `IBKRBrokerProvider` adapter for a read-only Interactive Brokers foundation. The intended account structure is an Individual / retail IBKR account. |
| 6 | |
| 7 | Real integration status: |
| 8 | |
| 9 | - Real IBKR login attempted: NO |
| 10 | - Real IBKR connection used: NO |
| 11 | - Real trades executed: NO |
| 12 | - Real account, position, cash, or market-data response validated against IBKR: NO |
| 13 | |
| 14 | ## INDIVIDUAL ACCOUNT AUTH METHOD |
| 15 | |
| 16 | Official IBKR documentation identifies Client Portal Gateway as the authentication path for retail and individual clients. The user authenticates directly with Interactive Brokers through the gateway login UI. The platform must never collect or store IBKR username, password, MFA, browser cookies, or raw session tokens. |
| 17 | |
| 18 | Official source: |
| 19 | |
| 20 | - https://www.interactivebrokers.com/docs/web-api/getting-started |
| 21 | - https://www.interactivebrokers.com/docs/web-api/authentication/cpgw/installation-authentication |
| 22 | - https://www.interactivebrokers.com/docs/web-api/authentication/cpgw/request-requirements |
| 23 | - https://www.interactivebrokers.com/docs/web-api/authentication/cpgw/limitations-of-the-client-portal-gateway |
| 24 | - https://www.interactivebrokers.com/docs/web-api/authentication/cpgw/client-portal-gateway-faq |
| 25 | |
| 26 | Implementation target for Individual accounts: |
| 27 | |
| 28 | - Auth method: `client-portal-gateway` |
| 29 | - Broker-service talks to a broker connector endpoint, not directly to the gateway host |
| 30 | - Browser login: required |
| 31 | - Server-side login automation: not supported by IBKR |
| 32 | - Daily reauthentication: required |
| 33 | |
| 34 | ## GLOBAL CONNECTOR ARCHITECTURE |
| 35 | |
| 36 | The platform is a multi-user SaaS system. A single Client Portal Gateway session must never be shared between users. |
| 37 | |
| 38 | The broker path is: |
| 39 | |
| 40 | Authenticated user -> broker connection -> broker service -> broker connector abstraction -> user-specific IBKR connector runtime -> IBKR Client Portal Gateway -> Interactive Brokers. |
| 41 | |
| 42 | `BrokerProvider` is business-facing and broker-neutral. It exposes read-only capabilities and provider status. IBKR HTTP/gateway details live behind the `BrokerConnector` contract. |
| 43 | |
| 44 | Connector metadata is persisted as safe metadata only: |
| 45 | |
| 46 | - `connector_id` |
| 47 | - `user_id` |
| 48 | - `broker_type` |
| 49 | - `runtime_mode` |
| 50 | - `runtime_status` |
| 51 | - `auth_status` |
| 52 | - heartbeat/auth timestamps |
| 53 | - idle/session timeout settings |
| 54 | |
| 55 | The connector schema does not persist broker passwords, MFA values, raw cookies, raw session tokens, or browser secrets. If future runtime implementations need secret material, they must use opaque secret references owned by the authenticated user. |
| 56 | |
| 57 | ## RUNTIME MODES |
| 58 | |
| 59 | The connector abstraction supports: |
| 60 | |
| 61 | - `KUBERNETES`: platform-managed connector runtime. |
| 62 | - `LOCAL_AGENT`: user-device or local connector runtime exposing the same internal connector contract. |
| 63 | |
| 64 | Broker-service must not care where the connector runs. It communicates with the configured connector service endpoint and uses authenticated user ownership checks for connector status, login, sync, and snapshot access. |
| 65 | |
| 66 | IBKR states that Client Portal Gateway is intended to run locally. Therefore, this repository separates architecture capability from production deployment approval. The architecture can support Kubernetes-managed or local-agent connector runtimes, but AKS-hosted Client Portal Gateway is not marked production-approved until IBKR confirms that deployment/location model is acceptable for a global SaaS product. |
| 67 | |
| 68 | ## INTERACTIVE AUTHENTICATION |
| 69 | |
| 70 | The flow is: |
| 71 | |
| 72 | 1. User clicks Connect IBKR. |
| 73 | 2. Broker-service creates a user-owned connector instance and broker connection. |
| 74 | 3. The connector returns an authentication URL/state. |
| 75 | 4. The user authenticates directly with IBKR. |
| 76 | 5. The connector checks `GET /iserver/auth/status`. |
| 77 | 6. The broker connection becomes `CONNECTED` only when `authenticated=true` and `connected=true`. |
| 78 | |
| 79 | The application never asks for or stores IBKR password/MFA data. |
| 80 | |
| 81 | ## OAUTH 2.0 RESTRICTION |
| 82 | |
| 83 | OAuth 2.0 is not the production authentication model for this Individual IBKR integration. |
| 84 | |
| 85 | Official IBKR documentation states OAuth 2.0 is for licensed Organizations, Financial Advisors, and IBrokers. It is not available to Individual account structures. |
| 86 | |
| 87 | Official source: |
| 88 | |
| 89 | - https://www.interactivebrokers.com/docs/web-api/authentication/oauth-2/introduction |
| 90 | |
| 91 | The platform must not request or configure OAuth2 client IDs, client key IDs, private keys, public-key registration metadata, or OAuth2 secrets for the Individual IBKR path. |
| 92 | |
| 93 | ## OAUTH 1.0A STATUS |
| 94 | |
| 95 | OAuth 1.0a is documented by IBKR for licensed Financial Advisors, Organizations, IBrokers, and third-party services. It can access IBKR endpoints directly without the local Client Portal Gateway after registration and token/session setup. |
| 96 | |
| 97 | Official sources: |
| 98 | |
| 99 | - https://www.interactivebrokers.com/docs/web-api/authentication/oauth-1a/introduction |
| 100 | - https://www.interactivebrokers.com/docs/web-api/authentication/oauth-1a/request-requirements |
| 101 | - https://www.interactivebrokers.com/docs/web-api/authentication/oauth-1a/first-party-oauth/registration-process |
| 102 | - https://www.interactivebrokers.com/docs/web-api/authentication/oauth-1a/first-party-oauth/first-party-o-auth-workflow |
| 103 | - https://www.interactivebrokers.com/docs/web-api/authentication/oauth-1a/lst/obtain-live-session-token-signature |
| 104 | - https://www.interactivebrokers.com/docs/web-api/authentication/oauth-1a/lst/compute-live-session-token |
| 105 | - https://www.interactivebrokers.com/docs/web-api/authentication/oauth-1a/lst/validate-live-session-token |
| 106 | - https://www.interactivebrokers.com/docs/web-api/authentication/oauth-1a/authenticated-requests |
| 107 | |
| 108 | OAuth 1.0a is not selected for the Individual account implementation until IBKR eligibility and registration approval requirements are confirmed for this specific account structure and use case. |
| 109 | |
| 110 | ## SESSION REQUIREMENTS |
| 111 | |
| 112 | IBKR Web API sessions are two-tiered: |
| 113 | |
| 114 | - An outer read-only session required to make Web API requests. |
| 115 | - A brokerage session required for `/iserver` endpoints, market data, and trading-capable functionality. |
| 116 | |
| 117 | Official source: |
| 118 | |
| 119 | - https://www.interactivebrokers.com/docs/web-api/authentication/sessions |
| 120 | |
| 121 | Client Portal Gateway requirements: |
| 122 | |
| 123 | - User must log in through a browser on the same machine as the Client Portal Gateway. |
| 124 | - API calls must be made from the same machine where the gateway was authenticated. |
| 125 | - IBKR does not support automated Client Portal Gateway authentication. |
| 126 | - Clients must reauthenticate daily. |
| 127 | - Sessions can time out without regular requests or `/tickle`. |
| 128 | - `/iserver/auth/status` reports session authentication state. |
| 129 | - `/iserver/auth/ssodh/init` initializes brokerage session when needed. |
| 130 | |
| 131 | Official sources: |
| 132 | |
| 133 | - https://www.interactivebrokers.com/docs/web-api/authentication/cpgw/limitations-of-the-client-portal-gateway |
| 134 | - https://www.interactivebrokers.com/docs/web-api/authentication/cpgw/client-portal-gateway-faq |
| 135 | - https://www.interactivebrokers.com/docs/web-api/authentication/faq |
| 136 | - https://www.interactivebrokers.com/docs/web-api/v1/endpoints/session/initialize-brokerage-session |
| 137 | |
| 138 | ## VERIFIED READ-ONLY ENDPOINTS |
| 139 | |
| 140 | ### Brokerage Session Status |
| 141 | |
| 142 | - Method: `GET` |
| 143 | - Path: `/iserver/auth/status` |
| 144 | - Purpose: determine current authentication status |
| 145 | - Required auth: authenticated Client Portal Gateway session |
| 146 | - Response shape: includes `authenticated`, `connected`, `competing`, `established`, and message/status fields |
| 147 | - Verification source: https://www.interactivebrokers.com/docs/web-api/api-reference/trading/trading-session/get-brokerage-status |
| 148 | |
| 149 | ### Accounts |
| 150 | |
| 151 | - Method: `GET` |
| 152 | - Path: `/portfolio/accounts` |
| 153 | - Purpose: list accounts for which the user can view position and account information |
| 154 | - Required auth: authenticated Web API session |
| 155 | - Response shape: account objects including `id`, `accountId`, `displayName`, `accountAlias`, `currency`, and metadata |
| 156 | - Verification source: https://www.interactivebrokers.com/docs/web-api/trading/portfolio-and-positions/querying-your-accounts |
| 157 | |
| 158 | ### Positions |
| 159 | |
| 160 | - Method: `GET` |
| 161 | - Path: `/portfolio/{accountId}/positions/{pageId}` |
| 162 | - Purpose: list positions for the given account |
| 163 | - Required auth: authenticated Web API session; `/portfolio/accounts` or `/portfolio/subaccounts` must be called first |
| 164 | - Pagination: page ID starts at 0; each page returns up to 100 positions |
| 165 | - Response shape: position objects including `acctId`, `conid`, `contractDesc`, `position`, `mktPrice`, `avgCost`, `currency`, `listingExchange`, `assetClass`, and `ticker` |
| 166 | - Verification source: https://www.interactivebrokers.com/docs/web-api/v1/endpoints/portfolio/positions |
| 167 | |
| 168 | ### Cash / Currency Balances |
| 169 | |
| 170 | - Method: `GET` |
| 171 | - Path: `/portfolio/{accountId}/ledger` |
| 172 | - Purpose: retrieve cash balances by currency |
| 173 | - Required auth: authenticated Web API session; `/portfolio/accounts` or `/portfolio/subaccounts` must be called first |
| 174 | - Response shape: object keyed by currency, including `cashbalance`, `currency`, `settledcash`, `netliquidationvalue`, and other ledger values |
| 175 | - Verification source: https://www.interactivebrokers.com/docs/web-api/trading/portfolio-and-positions/querying-currency-balances |
| 176 | |
| 177 | ### Market Data |
| 178 | |
| 179 | - Method: `GET` |
| 180 | - Path: `/iserver/marketdata/snapshot` |
| 181 | - Purpose: retrieve top-of-book market data snapshots for conids and requested field tags |
| 182 | - Required auth: authenticated brokerage session and relevant market data permissions |
| 183 | - Response shape: array of market data objects keyed by numeric field tags plus fields such as `conid`, `conidEx`, `_updated`, and `server_id` |
| 184 | - Entitlement requirements: carried by the IBKR username |
| 185 | - Verification source: https://www.interactivebrokers.com/docs/web-api/trading/market-data/top-of-book-snapshots |
| 186 | |
| 187 | Market data remains disabled in this platform unless explicitly enabled by verified entitlement and configuration. |
| 188 | |
| 189 | ## READ-ONLY CAPABILITIES |
| 190 | |
| 191 | When disabled, documentation-unverified, OAuth2-configured, or missing connector configuration, IBKR advertises no capabilities. |
| 192 | |
| 193 | When explicitly enabled with the verified Individual-account Client Portal Gateway method, the current code may advertise: |
| 194 | |
| 195 | - `ACCOUNTS_READ` |
| 196 | - `ACCOUNT_METADATA_READ` |
| 197 | - `PORTFOLIO_READ` |
| 198 | - `POSITIONS_READ` |
| 199 | - `CASH_READ` |
| 200 | |
| 201 | `MARKET_DATA_READ` is not enabled by default. |
| 202 | |
| 203 | ## NOT SUPPORTED |
| 204 | |
| 205 | - Order placement |
| 206 | - Order modification |
| 207 | - Order cancellation |
| 208 | - Buy/sell actions |
| 209 | - Trade execution |
| 210 | - Browser login automation |
| 211 | - Stored broker usernames or passwords |
| 212 | - Stored OTP, MFA, CAPTCHA, raw access-token, OAuth2 private-key, or OAuth2 client secret values |
| 213 | - Shared IBKR Gateway sessions across users |
| 214 | - Broker-service direct dependency on localhost, host machine aliases, Pod IPs, or Node IPs |
| 215 | |
| 216 | ## MULTI-USER IMPLICATIONS |
| 217 | |
| 218 | Each authenticated application user owns their own broker connection and connector instance. Client Portal Gateway has machine/session constraints: |
| 219 | |
| 220 | - A gateway session is authenticated by one IBKR username. |
| 221 | - A single IBKR username can have only one brokerage session active at a time. |
| 222 | - API calls must originate on the same machine as the authenticated gateway. |
| 223 | - Multiple application users are isolated at the application data and connector layers, and each user's real IBKR connectivity requires a separate authenticated gateway/session boundary. |
| 224 | |
| 225 | The platform must not share one user's IBKR gateway session with another application user. |
| 226 | |
| 227 | ## CURRENT PROVIDER STATUS |
| 228 | |
| 229 | - Default: `NOT_CONFIGURED` |
| 230 | - Enabled without official documentation verification: `DOCUMENTATION_REQUIRED` |
| 231 | - Enabled with OAuth2/private_key_jwt: `DOCUMENTATION_REQUIRED` for Individual account integration |
| 232 | - Enabled with Client Portal Gateway but missing connector endpoint: `NOT_CONFIGURED` |
| 233 | - Enabled with Client Portal Gateway but unauthenticated session: `AUTHENTICATION_REQUIRED` |
| 234 | - Real provider enabled for real use: NO |
| 235 | |
| 236 | ## REQUIRED BEFORE REAL CREDENTIAL USE |
| 237 | |
| 238 | Before real IBKR credentials or sessions can be configured: |
| 239 | |
| 240 | 1. Complete the connector runtime implementation for either `KUBERNETES`, `LOCAL_AGENT`, or both. |
| 241 | 2. Ensure no server-side browser automation is used. |
| 242 | 3. Define how users will perform interactive browser login and daily reauthentication. |
| 243 | 4. Define how the platform detects session timeout and prompts reauth safely. |
| 244 | 5. Verify read-only scopes/capabilities in a controlled non-trading test plan. |
| 245 | 6. Keep PRD Individual IBKR runtime disabled until IBKR approves the production connector runtime location for this SaaS use case. |
| 246 | |
| 247 | ## PACKAGE AND LICENSING |
| 248 | |
| 249 | Do not commit the IBKR Gateway ZIP/package or derivative redistribution artifact to source control unless redistribution rights are verified. DEV should use an external/local artifact path or an approved image build process. PRD must treat IBKR Gateway packaging as a deployment prerequisite, not an assumed right. |
| 250 | |
| 251 | ## PHASE 5B DEV REAL PACKAGE VALIDATION |
| 252 | |
| 253 | This validation uses the user's locally downloaded and extracted official Client Portal Gateway package. The package path is supplied at execution time only and must not be committed or baked into any image. |
| 254 | |
| 255 | Package checks: |
| 256 | |
| 257 | ```powershell |
| 258 | $IbkrPackage = "<LOCAL_EXTRACTED_CLIENT_PORTAL_GATEWAY_PATH>" |
| 259 | Test-Path "$IbkrPackage\bin\run.sh" |
| 260 | Test-Path "$IbkrPackage\root\conf.yaml" |
| 261 | Get-ChildItem "$IbkrPackage" | Select-Object -First 20 |
| 262 | ``` |
| 263 | |
| 264 | DEV package injection uses a Kubernetes PVC mounted into the `ibkr-connector` pod: |
| 265 | |
| 266 | ```powershell |
| 267 | kubectl create namespace ai-investment --dry-run=client -o yaml | kubectl apply -f - |
| 268 | @' |
| 269 | apiVersion: v1 |
| 270 | kind: PersistentVolumeClaim |
| 271 | metadata: |
| 272 | name: ibkr-gateway-package-dev |
| 273 | spec: |
| 274 | accessModes: |
| 275 | - ReadWriteOnce |
| 276 | resources: |
| 277 | requests: |
| 278 | storage: 1Gi |
| 279 | '@ | kubectl apply -n ai-investment -f - |
| 280 | kubectl run ibkr-package-loader -n ai-investment --image=busybox:1.36 --restart=Never --command -- sh -c "mkdir -p /package && sleep 3600" --overrides '{"spec":{"containers":[{"name":"ibkr-package-loader","image":"busybox:1.36","command":["sh","-c","mkdir -p /package && sleep 3600"],"volumeMounts":[{"name":"pkg","mountPath":"/package"}]}],"volumes":[{"name":"pkg","persistentVolumeClaim":{"claimName":"ibkr-gateway-package-dev"}}]}}' |
| 281 | kubectl wait -n ai-investment --for=condition=Ready pod/ibkr-package-loader --timeout=120s |
| 282 | kubectl cp "$IbkrPackage\." ai-investment/ibkr-package-loader:/package |
| 283 | kubectl delete pod ibkr-package-loader -n ai-investment |
| 284 | ``` |
| 285 | |
| 286 | Deploy DEV with the external package mounted. The Gateway remains inside the connector pod and broker-service talks only to `http://ibkr-connector`: |
| 287 | |
| 288 | ```powershell |
| 289 | helm upgrade --install ai-investment-platform infrastructure/helm/ai-investment-platform ` |
| 290 | --namespace ai-investment --create-namespace ` |
| 291 | --values infrastructure/helm/ai-investment-platform/values-dev.yaml ` |
| 292 | --set ibkr.connectorBaseUrl=http://ibkr-connector ` |
| 293 | --set ibkr.connectorInternalToken=dev-internal-connector-token-change-me ` |
| 294 | --set ibkrConnector.gatewayPackagePath=/opt/ibkr/clientportal.gw ` |
| 295 | --set ibkrConnector.gatewayBaseUrl=https://127.0.0.1:5000/v1/api ` |
| 296 | --set ibkrConnector.gatewayTlsVerify=false ` |
| 297 | --set ibkrConnector.loginPublicBaseUrl=http://localhost:18080 ` |
| 298 | --set ibkrConnector.packageVolumeClaim=ibkr-gateway-package-dev |
| 299 | ``` |
| 300 | |
| 301 | Initial connector validation: |
| 302 | |
| 303 | ```powershell |
| 304 | kubectl rollout status deployment/ibkr-connector -n ai-investment --timeout=300s |
| 305 | kubectl exec -n ai-investment deploy/ibkr-connector -- sh -c "ps -ef | grep '[b]in/run.sh'" |
| 306 | kubectl exec -n ai-investment deploy/ibkr-connector -- sh -c "python - <<'PY' |
| 307 | import socket |
| 308 | s=socket.create_connection(('127.0.0.1',5000),5) |
| 309 | s.close() |
| 310 | print('port 5000 listening') |
| 311 | PY" |
| 312 | ``` |
| 313 | |
| 314 | The user-facing login URL must be obtained through broker-service or the API gateway, not by browsing to a Pod IP or Kubernetes service DNS. For local DEV ingress the URL should start with: |
| 315 | |
| 316 | ```text |
| 317 | http://localhost:18080/connector-sessions/ |
| 318 | ``` |
| 319 | |
| 320 | Current limitation: this DEV runtime supports one active IBKR session per connector pod. Dynamic per-user Kubernetes pod orchestration is not implemented yet. |