py-ib-controller 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,490 @@
1
+ Metadata-Version: 2.4
2
+ Name: py-ib-controller
3
+ Version: 0.0.0
4
+ Summary: Login/launch controller for TWS and IBKR Gateway, via an in-process Java agent
5
+ Author-email: gnzsnz <8376642+gnzsnz@users.noreply.github.com>
6
+ License-Expression: Apache-2.0
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE.txt
13
+ Requires-Dist: aeventkit
14
+ Requires-Dist: attrs
15
+ Requires-Dist: cattrs
16
+ Requires-Dist: python-dotenv>=1.2.3
17
+ Requires-Dist: platformdirs>=4.11.7
18
+ Requires-Dist: typer>=0.15.1
19
+ Requires-Dist: typed-settings
20
+ Requires-Dist: anyio>=4.15.1
21
+ Dynamic: license-file
22
+
23
+ # ibcontroller
24
+
25
+ An ibgateway/TWS automation tool.
26
+
27
+ Heavily inspired by [IBC](https://github.com/IbcAlpha/IBC) project, now archived.
28
+
29
+ ## What it does?
30
+
31
+ - Finds ibgateway/TWS installation, JRE and launchs it.
32
+ - Automates user/password entry, waits for MFA.
33
+ - Automates ibgatewa/TWS settings.
34
+ - Manages pop-ups and automatically accept.
35
+
36
+ - Manages ibgateway/TWS restarts.
37
+
38
+ It provides a "**declarative**" engine, so settings and pop-ups can be "declared" by configuration entries.
39
+
40
+ ## How to use it
41
+
42
+ ```bash
43
+ # 1. Build the Java agent jar and install the package (editable, for a source checkout)
44
+ make dist
45
+ uv sync
46
+
47
+ # 2. Scaffold a starter config directory (platformdirs default, or $IBC_APP_DIR)
48
+ uv run ibcontroller init
49
+
50
+ # 3. Export IBC_USERID / IBC_PASSWORD (see Credentials below) -- the
51
+ # only settings actually required. Everything else in the base ibcontroller.toml
52
+ # is commented out and already safe to run as-is: trading_mode defaults to "paper",
53
+ # and tws_version is auto-detected from standard tws_path.
54
+
55
+ # 4. Run it -- Ctrl-C for a graceful shutdown
56
+ uv run ibcontroller run
57
+ ```
58
+
59
+ `ibcontroller run` scaffolds the config directory automatically the first time
60
+ `ibcontroller.toml` is missing (same effect as running `init` first), so it can also be
61
+ run directly on a fresh install -- it will still fail with a clear error naming whichever
62
+ setting is actually missing (in practice, just the two credential env vars).
63
+
64
+ `ibcontroller version` prints the installed package version.
65
+
66
+ ### CLI parameters
67
+
68
+ You can use the following cli parameters:
69
+
70
+ ```text
71
+ Usage: ibcontroller [OPTIONS] COMMAND [ARGS]...
72
+
73
+ Login/launch controller for TWS and IBKR Gateway.
74
+
75
+ ╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
76
+ │ --install-completion Install completion for the current shell. │
77
+ │ --show-completion Show completion for the current shell, to copy it or customize the installation. │
78
+ │ --help Show this message and exit. │
79
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
80
+ ╭─ Commands ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
81
+ │ init Scaffold the config directory with starter ibcontroller.toml/ │
82
+ │ ibkr_settings.toml.example/labels.json.example files. Never overwrites a file │
83
+ │ that already exists unless --force is given. │
84
+ │ run Run one ibcontroller instance until it stops (Ctrl-C for a graceful shutdown, │
85
+ │ or Gateway/TWS exiting or restarting on its own). Scaffolds the config directory │
86
+ │ first if it's missing, same as `ibcontroller init`. │
87
+ │ version Print the installed ibcontroller version. │
88
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
89
+ ```
90
+
91
+ And the `ibcontroller run` parameters:
92
+
93
+ ```text
94
+ Usage: ibcontroller run [OPTIONS]
95
+
96
+ Run one ibcontroller instance until it stops (Ctrl-C for a graceful shutdown, or Gateway/TWS exiting or restarting on its own). Scaffolds the config directory first if it's
97
+ missing, same as `ibcontroller init`.
98
+
99
+ `--trading-mode`/`--tws-path`/`--tws-channel`/`--program`/`--tws-settings-path`/
100
+ `--instance` let one invocation pick these `Config` fields directly, overriding
101
+ TOML/env for this run only -- e.g. `ibcontroller run --trading-mode=live
102
+ --dotenv=.env-live` and `ibcontroller run --trading-mode=paper
103
+ --dotenv=.env-paper` run side by side from one shared config.
104
+
105
+ ╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
106
+ │ --dotenv <path> Optional .env file to load into the environment before reading config (only fills variables not already set). │
107
+ │ --app-dir <path> Override IBC_APP_DIR for this invocation -- config/log/run all move under {app_dir}/{config,log,run} instead of the platform │
108
+ │ default. │
109
+ │ --trading-mode <live|paper> Override Config.trading_mode ('live'/'paper') for this invocation. │
110
+ │ --tws-path <path> Override Config.tws_path (the TWS/Gateway install-path inference) for this invocation. │
111
+ │ --tws-channel <str> Override Config.tws_channel ('stable'/'latest', filters install auto-detection) for this invocation. │
112
+ │ --program <str> Override Config.program ('gateway'/'tws') for this invocation. │
113
+ │ --tws-settings-path <path> Override Config.tws_settings_path (where TWS/Gateway stores its own settings) for this invocation. │
114
+ │ --instance <str> Override Config.instance (per-instance log/trace/socket names) for this invocation. Defaults to '{program}-{trading_mode}', so │
115
+ │ --trading-mode alone is usually enough to keep paper/live instances apart. │
116
+ │ --help Show this message and exit. │
117
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
118
+ ```
119
+
120
+ ### Running paper and live in parallel
121
+
122
+ To run parallel instances in `live`and `paper`trading mode, we need to pass credentials and trading mode:
123
+
124
+ ```bash
125
+ # terminal 1
126
+ ibcontroller run --trading-mode=live --dotenv=.env-live
127
+ # terminal 2
128
+ ibcontroller run --trading-mode=paper --dotenv=.env-paper
129
+ ```
130
+
131
+ `--app-dir` (also accepted by `init`) overrides `IBC_APP_DIR` for one
132
+ invocation, for pointing each instance's config/log/run dirs somewhere different too.
133
+
134
+ ## Configuration
135
+
136
+ `ibcontroller` is configured through a **flat** TOML file (no `[section]` headers) and/or
137
+ environment variables, with precedence `defaults < TOML file < .env < environment`. Environment
138
+ can be loaded from a `.env` file. Credentials are environment-variable-only and must
139
+ never appear in the config file.
140
+
141
+ A sample file with all settings is provided as an [example](./ibcontroller.env).
142
+
143
+ `ibcontroller init` (or the first `ibcontroller run`) copies starter templates into the
144
+ config directory: `ibcontroller.toml` (every key commented out -- safe to run as-is,
145
+ since it changes nothing versus the defaults below), `ibkr_settings.toml.example`
146
+ (*extra* declarative Global Configuration settings, on top of the always-applied
147
+ built-in ones -- see "Declarative configuration" below), and `labels.json.example` (an
148
+ optional, sparse override for window/button labels and dismiss rules, applied once
149
+ renamed to `labels.json` -- see `labels.py`'s own docstring for the merge mechanism).
150
+ Both `.example` files are deliberately left inert (kept under their `.example` name)
151
+ until renamed -- neither auto-applies just by existing. The templates themselves are
152
+ bundled inside the installed package (`ibcontroller/data/`), not kept in this
153
+ repository.
154
+
155
+ ### Credentials (env only, never in the configuration file)
156
+
157
+ A single pair is used regardless of `trading_mode`.
158
+
159
+ | Env var | Description |
160
+ | --- | --- |
161
+ | `IBC_USERID` | Account user id |
162
+ | `IBC_PASSWORD` | Account password |
163
+
164
+ Each also accepts a `_FILE`-suffixed variant (`IBC_USERID_FILE`,
165
+ `IBC_PASSWORD_FILE`) that reads the value from a file instead —
166
+ Docker/Compose secrets, so a value never has to sit in the process environment.
167
+
168
+ ### File location
169
+
170
+ When environment variable `IBC_APP_DIR` is not set, then
171
+ `ibcontroller` will use the standard platform directories.
172
+
173
+ | Location | Directory | Platform |
174
+ | --- | --- | --- |
175
+ | `config_dir` | `~/.config/ibcontroller` | Linux |
176
+ | `config_dir` | `~/Library/Application Support/ibcontroller` | macOS |
177
+ | `log_dir` | `~/.local/share/ibcontroller` | Linux |
178
+ | `log_dir` | `~/Library/Logs/ibcontroller` | macOS |
179
+ | `socket_dir` | `/run/user/<uid>/ibcontroller` | Linux |
180
+ | `socket_dir` | `~/Library/Caches/TemporaryItems/ibcontroller` | macOS |
181
+
182
+ When `IBC_APP_DIR` is set then:
183
+
184
+ - `config_dir`-> `IBC_APP_DIR/config`
185
+ - `log_dir`-> `IBC_APP_DIR/log`
186
+ - `socket_dir` -> `IBC_APP_DIR/run`
187
+
188
+ ### Settings
189
+
190
+ `ibcontroller`ships with sensible defaults. **Only the two credential env vars below are actually mandatory**
191
+ everything else here has a safe default or is auto-detected.
192
+
193
+ `None` on a `TWS/Gateway setting` row is a deliberate third state, not just
194
+ "unset" yes/no/unset convention: set it explicitly to apply value, or leave
195
+ it `None` to leave TWS/Gateway's existing setting alone. This is the actual
196
+ enable/disable mechanism for these fields, not a side effect of the type
197
+ allowing `None`. `Config`'s schema is closed — an unknown key in
198
+ `ibcontroller.toml` fails at startup with a clean error (`ConfigError`).
199
+
200
+ | TOML key | Env var or CLI flag | Description | Default |
201
+ | --- | --- | --- | --- |
202
+ | `instance` | `IBC_INSTANCE`, `--instance` | Instance name; separate log/trace files per instance | `"{program}-{trading_mode}"` (e.g. `"gateway-paper"`) |
203
+ | `program` | `IBC_PROGRAM`, `--program` | `"gateway"` or `"tws"` | `"gateway"` |
204
+ | `tws_version` | `IBC_TWS_VERSION` | Installed TWS/Gateway version, e.g. `"10.50"` | `None` — auto-detected (see below) |
205
+ | `tws_channel` | `IBC_TWS_CHANNEL`, `--tws-channel` | `"stable"`/`"latest"`; narrows auto-detection to one update channel | `"stable"` |
206
+ | `tws_path` | `IBC_TWS_PATH`, `--tws-path` | Override TWS/gateway install-path inference | `None` |
207
+ | `tws_settings_path` | `IBC_TWS_SETTINGS_PATH`, `--tws-settings-path` | TWS/Gateway settings dir (per instance) | `None` |
208
+ | `settings_file` | `IBC_SETTINGS_FILE` | Extra `ibkr_settings.toml`, merged on top of the built-in settings -- see "Declarative configuration" below | `None` |
209
+ | `trace_enabled` | `IBC_TRACE_ENABLED` | Enable verbose raw wire trace (`cmd-{instance}.jsonl`/`events-{instance}.jsonl`) | `false` |
210
+ | `log_dir` | `IBC_LOG_DIR` | Where ibcontroller's own log file lives; always resolved at startup | platform default |
211
+ | `log_level` | `IBC_LOG_LEVEL` | `debug`/`info`/`warning`/`error` | `info` |
212
+ | `log_sink` | `IBC_LOG_SINK` | `"std"` (console only) or `"file"` (only, under `log_dir`) — exclusive, not both. Covers `ibcontroller-{instance}.log` and `gateway-{instance}.log` only; the Java agent log and the wire trace are always file, unaffected — see "Logging" below | `"std"` |
213
+ | `diagnostic_scope` | `IBC_DIAGNOSTIC_SCOPE` | `"known"`/`"unknown"`/`"all"` — which windows get a structure dump logged (see `docs/Configuration.md`'s "Diagnostics") | `"known"` |
214
+ | `diagnostic_when` | `IBC_DIAGNOSTIC_WHEN` | `"open"`/`"openclose"`/`"never"` — when to log a structure dump | `"never"` |
215
+ | (env only) | `IBC_APP_DIR`, `--app-dir` | Override file locations (config/log/run) for container mode; `--app-dir`/`init`/`run` set the env var for the invocation, same effect |
216
+
217
+ TWS/ibgateway settings
218
+
219
+ | TOML key | Env var or CLI flag | Description | Default |
220
+ | --- | --- | --- | --- |
221
+ | `trading_mode` | `IBC_TRADING_MODE`, `--trading-mode` | `"live"` or `"paper"`; which account to authenticate as | `"paper"` |
222
+ | `read_only_login` | `IBC_READ_ONLY_LOGIN` | TWS-only; **not yet wired to any behavior** (loaded, unused — see TODO.md) | `false` |
223
+ | `read_only_api` | `IBC_READ_ONLY_API` | `true`/`false` sets it; omit to leave unchanged. Applied to Gateway/TWS automatically -- see "Declarative configuration" below | `None` |
224
+ | `accept_incoming_connections` | `IBC_ACCEPT_INCOMING_CONNECTIONS` | `manual`/`accept`/`reject` | `"manual"` |
225
+ | `existing_session_action` | `IBC_EXISTING_SESSION_ACTION` | `manual`/`primary`/`primaryoverride`/`secondary` | `"manual"` |
226
+ | `login_dialog_display_timeout` | `IBC_LOGIN_DIALOG_DISPLAY_TIMEOUT` | Seconds to wait for the login dialog | `60.0` |
227
+ | `mfa_timeout` | `IBC_MFA_TIMEOUT` | IB's 2FA timeout in seconds | `180.0` |
228
+ | `relogin_after_mfa_timeout` | `IBC_RELOGIN_AFTER_MFA_TIMEOUT` | Restart login if 2FA times out | `false` |
229
+ | `mfa_exit_interval` | `IBC_MFA_EXIT_INTERVAL` | Bounds the post-2FA wait when relogin enabled | `60.0` |
230
+ | `auto_restart_time` | `IBC_AUTO_RESTART_TIME` | `"hh:mm AM/PM"` daily auto-restart time. Applied to Gateway/TWS automatically -- see "Declarative configuration" below | `None` |
231
+ | `auto_logoff_time` | `IBC_AUTO_LOGOFF_TIME` | `"hh:mm AM/PM"` daily auto-logoff time; same "Lock and Exit" radio-button pair as `auto_restart_time` -- if both are set, `auto_restart_time` wins | `None` |
232
+ | `cold_restart_time` | `IBC_COLD_RESTART_TIME` | TWS and Gateway. `"HH:MM"` 24-hour local time; every Sunday, ibcontroller closes the instance tidily and relaunches with a full fresh login, forcing IBKR's weekly Sunday 01:00 US/Eastern token-invalidation reauth -- not a GUI setting, see "Scheduled shutdown" below | `None` |
233
+ | `closedown_at` | `IBC_CLOSEDOWN_AT` | TWS and Gateway. `"HH:MM"` (daily) or `"<Weekday> HH:MM"` (weekly); closes the instance tidily at that time, no relaunch -- not a GUI setting, see "Scheduled shutdown" below | `None` |
234
+
235
+ #### `tws_version` auto-detection
236
+
237
+ When `tws_version` is unset, `launcher._detect_tws_version` scans `tws_path` for every
238
+ installed version (a directory with its own `jars/` subfolder) and picks the greatest
239
+ one — optionally narrowed to one update channel first, if `tws_channel` is set. A real
240
+ install can carry more than one version side by side, each on its own channel: e.g.
241
+ `IB Gateway 10.45` (channel `stable`) and `IB Gateway 10.50` (channel `latest`) installed
242
+ at once — `tws_channel = "stable"` picks `10.45`; leaving it unset picks `10.50`, the
243
+ greatest overall. The channel itself is read per-install from its own
244
+ `.install4j/i4jparams.conf`, not guessed from the version number.
245
+
246
+ TWS requests fall back to Gateway installs when `program = "tws"` and no TWS install matches — no
247
+ `Trader Workstation *` directory at all, or none on the requested `tws_channel` — the
248
+ Gateway installs (`IB Gateway *`) are used instead, launching the TWS front end
249
+ (`jclient.LoginFrame`) from the Gateway jars. `tws_channel` still applies to the
250
+ fallback pool strictly: a Gateway install on the *wrong* channel never satisfies a TWS
251
+ request (that raises a clear error instead, naming `tws_channel`). The reverse
252
+ (Gateway falling back to TWS installs) is deliberately not supported. Applies to a
253
+ pinned `tws_version` too — a pinned version absent from TWS installs resolves against
254
+ Gateway installs of the same version.
255
+
256
+ #### Scheduled shutdown: `cold_restart_time` / `closedown_at`
257
+
258
+ Unlike `read_only_api`/`auto_restart_time`/`auto_logoff_time` (real Global Configuration
259
+ GUI settings), these two aren't written to TWS/Gateway at all -- ibcontroller schedules
260
+ the action itself, ported from IBC's own `IbcTws.java` (the shared base class both
261
+ programs use, so both apply here too). If both are set, whichever occurs first wins; a
262
+ malformed value is logged and skipped rather than aborting the other one.
263
+
264
+ - `cold_restart_time` -- `"HH:MM"`, 24-hour, local time. Every **Sunday** at this time,
265
+ ibcontroller closes the instance tidily and relaunches with a full fresh login forcing the weekly
266
+ reauth IBKR requires around Sunday 01:00 US/Eastern token invalidation.
267
+ - `closedown_at` -- `"HH:MM"` (every day) or `"<Weekday> HH:MM"` (one day a week,
268
+ `Weekday` a full English name, `Monday`..`Sunday`). Closes the instance tidily at that
269
+ time, with no relaunch -- you're responsible for restarting it yourself.
270
+
271
+ Both use the local system clock, not
272
+ converted to US/Eastern, so pick a local time that lands after the Sunday 01:00
273
+ US/Eastern invalidation window if that's the intent.
274
+
275
+ ## Declarative configuration
276
+
277
+ Beyond `ibcontroller.toml`, three things let you shape ibcontroller's behavior without
278
+ writing any Python: **`labels.json`** (window/button text, plus simple pop-up dismissal
279
+ rules) and **`ibkr_settings.toml`** (Global Configuration settings applied to Gateway/TWS
280
+ at startup). Each is covered below.
281
+
282
+ ### `labels.json` — window/button text and pop-up dismissal
283
+
284
+ `{config_dir}/labels.json` (rename `labels.json.example` to activate) overrides one or
285
+ more fields of the bundled default, field by field — you only need to include what
286
+ you're changing, not the whole file. Use it when a TWS/Gateway release renames a button
287
+ or dialog title ibcontroller matches against.
288
+
289
+ The one part of `labels.json` that isn't just text is `dismiss_rules` — a list of
290
+ "if a window matches, click this button" entries, checked on every window that opens.
291
+ Each entry needs at least one of `match_title`/`match_text` (both, if given, must
292
+ match); `click` is the button's label.
293
+
294
+ ```json
295
+ {
296
+ "dismiss_rules": [
297
+ {
298
+ "name": "my_custom_popup",
299
+ "match_title": "Some Notice",
300
+ "click": "OK"
301
+ }
302
+ ]
303
+ }
304
+ ```
305
+
306
+ `dismiss_rules` is a list, not a map — an override *replaces the whole list*, so if you
307
+ want to keep the bundled entries (the non-brokerage-account warning, the auto-restart
308
+ confirmation notice) alongside your own, copy them into your override file too rather
309
+ than starting from an empty list. Good for any pop-up that's always handled the same
310
+ simple way; a dialog needing real logic (parsing a value out of its text, retrying,
311
+ raising an error) isn't expressible this way and needs actual code.
312
+
313
+ ### `ibkr_settings.toml` — Global Configuration settings
314
+
315
+ Global Configuration settings (the "Configure" / "Settings" dialog inside Gateway/TWS)
316
+ are applied once, right after login, from two layers combined into one list:
317
+
318
+ - **Built-in** — `read_only_api`, `auto_restart_time`, and `auto_logoff_time`, applied
319
+ straight from `ibcontroller.toml`/the environment. Set `read_only_api = false` or
320
+ `auto_restart_time = "04:00 AM"` in your config and it's applied on the next run —
321
+ no separate file needed. `auto_restart_time`/`auto_logoff_time` share one
322
+ radio-button pair; if you set both, `auto_restart_time` wins (the built-in file
323
+ applies it last, matching IBC's own stated precedence).
324
+ - **Your own `ibkr_settings.toml`** — anything else: any other Global Configuration
325
+ checkbox or text field, activated by pointing `[settings] file = "..."` (or
326
+ `IBC_SETTINGS_FILE`) at a real file (rename `ibkr_settings.toml.example` to
327
+ start from the bundled worked examples, e.g. the `api_precautions_*` toggles).
328
+
329
+ An entry looks like this:
330
+
331
+ ```toml
332
+ [[settings]]
333
+ tree_path = "API/Precautions" # Global Configuration's own tree, section by section
334
+ action = "toggle" # "toggle" | "type_text" | "type_text_near_label" | "auto_restart_time" | "auto_logoff_time"
335
+ label_ref = "api_precautions_bypass_bond_warning" # a name from labels.json's settings.controls map
336
+ # label = "Some Checkbox Label" # ...or a literal label, for anything not named there yet
337
+ value = true # a literal value...
338
+ # value_from_config = "read_only_api" # ...or pulled from ibcontroller.toml/env at apply time
339
+ ```
340
+
341
+ - `label_ref` looks the label up in `labels.json`'s `settings.controls` map (version-proofed
342
+ against a future rename); `label` is a literal string for anything not worth naming there.
343
+ - `value` is a literal; `value_from_config` instead reads an `ibcontroller.toml` field by
344
+ name at apply time — this is how the built-in entries stay driven purely by config. A
345
+ value that resolves to nothing (unset `value`, or a `value_from_config` field left at its
346
+ default `None`) skips that entry entirely — "leave unchanged," not "set to blank." This
347
+ is the actual enable/disable mechanism for `Config`-backed settings (see the `### Settings`
348
+ table above) — `value_from_config` only works for the fixed set of fields `settings.py`
349
+ already knows about (`read_only_api`/`auto_restart_time`/`auto_logoff_time` today);
350
+ `Config`'s own schema is closed, so pointing it at anything else logs a warning and never
351
+ applies — use a literal `value` for anything not in that set.
352
+ - `type_text_near_label`, for a field with no accessible label of its own, also takes
353
+ `field_index` (0-based position within the container below the matched label).
354
+
355
+ **Your file adds to the built-ins, and can also override one.** Every entry you declare
356
+ with a *different* `value_from_config` (or none at all, e.g. a literal-`value` entry) is
357
+ simply added to what gets applied. An entry with the *same* `value_from_config` as a
358
+ built-in *replaces* that built-in entry outright — the two are never both applied. Use
359
+ this if a future Gateway/TWS release moves `read_only_api` (or `auto_restart_time`) to a
360
+ different screen or renames its control: point your own entry at the new location, keep
361
+ `value_from_config = "read_only_api"`, and it takes over from the bundled one.
362
+
363
+ ```toml
364
+ # Overrides the built-in read_only_api entry with a new location/label, still driven
365
+ # by the same ibcontroller.toml field:
366
+ [[settings]]
367
+ tree_path = "API/Settings/Read-Only"
368
+ action = "toggle"
369
+ label = "Read-Only API Access"
370
+ value_from_config = "read_only_api"
371
+ ```
372
+
373
+ ## Logging
374
+
375
+ ibcontroller writes all of its log/trace files into one shared log directory
376
+ (resolved by `app_dirs.py`, or overridden with `IBC_APP_DIR` for
377
+ container mode). Every file is named with the `{instance}` name, so two instances
378
+ (e.g. `paper` and `live`) can run in parallel in the same directory without ever
379
+ interleaving — matched by `tail -f` on the right filename, no subdirectory per
380
+ instance.
381
+
382
+ **`IBC_LOG_SINK`** (`"std"`/`"file"`, default `"std"`) controls where
383
+ ibcontroller's own log and TWS/Gateway's own stdout/stderr go — console
384
+ (`"std"`) or file (`"file"`), exclusive, not both. This is the setting that
385
+ matters for Docker: with the default `"std"`, both streams show up in
386
+ `docker logs` and no file is written inside the container. It does **not**
387
+ cover the Java agent log or the wire trace below — those two are always file,
388
+ regardless of `log_sink`, the same way `trace_enabled`'s files are always
389
+ file when it's on: the Java agent log is JVM-side diagnostic detail (not
390
+ something you'd want interleaved into `docker logs` at `debug`), and the wire
391
+ trace is deliberately verbose NDJSON meant to be tailed, not read as console
392
+ scrollback.
393
+
394
+ | File | Layer | Enabled by | Level |
395
+ | --- | --- | --- | --- |
396
+ | `ibcontroller-{instance}.log` | ibcontroller app control flow | `log_sink=file` (else console) | `log_level` (default `info`) |
397
+ | `gateway-{instance}.log` | TWS/Gateway own stdout/stderr | `log_sink=file` (else console) | TWS/Gateway's own |
398
+ | `ibcontroller-java-agent-{instance}.log` | Java agent (in-process JUL) | always file, not affected by `log_sink` | `log_level` (see mapping below) |
399
+ | `cmd-{instance}.jsonl` | raw wire: commands sent + results, NDJSON | `trace_enabled`; always file, not affected by `log_sink` | always (DEBUG emit, gated separately) |
400
+ | `events-{instance}.jsonl` | raw wire: every event message received, NDJSON | `trace_enabled`; always file, not affected by `log_sink` | always (DEBUG emit, gated separately) |
401
+
402
+ ### Log levels
403
+
404
+ `IBC_LOG_LEVEL` (`debug`/`info`/`warning`/`error`, default `info`) controls
405
+ both the Python app log and (via `-Dibcontroller.log.level`) the Java agent log:
406
+
407
+ | `IBC_LOG_LEVEL` | Python `logging` | Java JUL |
408
+ | --- | --- | --- |
409
+ | `debug` | `DEBUG` | `FINE` |
410
+ | `info` | `INFO` | `INFO` |
411
+ | `warning` | `WARNING` | `WARNING` |
412
+ | `error` | `ERROR` | `SEVERE` |
413
+
414
+ The default `info` is quiet — it records the *actions* taken and problems found,
415
+ not the raw event stream. `debug` additionally logs every event message that
416
+ arrives on the Python side (below).
417
+
418
+ `trace_enabled` is a separate, independent toggle: it turns on the structured
419
+ raw-wire files and is not coupled to `log_level` — you can trace at `info`, or
420
+ read the human-readable arrival lines at `debug` without writing the NDJSON files,
421
+ or both.
422
+
423
+ ### What gets logged, per stream
424
+
425
+ - **`ibcontroller-{instance}.log`** — only written when `log_sink=file`; with
426
+ the default `log_sink=std` this same content goes to the console instead, no
427
+ file is created. The app's own control flow: launching an
428
+ instance, each login state transition (credentials submitted, 2FA in progress, login
429
+ completed/skipped on restart), declarative settings applied (and which entries were
430
+ skipped), and every recogniser action (e.g. `dismissed non-brokerage account
431
+ warning`). Credential *values* are never logged: `Config.userid`/`password` are
432
+ `typed_settings.types.Secret`-wrapped, and a `Secret`'s `__str__`/`__repr__` both
433
+ mask the real value (`*******`), so even a careless `logger.info("login as %s",
434
+ password)` stays safe. At `debug` this same file gets one line per arriving
435
+ event message (IBC's always-on
436
+ `logWindow` posture, mapped to our DEBUG level here) — e.g. `event: window_opened
437
+ class=ibgateway.ax title='IBKR Gateway' seq=1`. Event content is window metadata
438
+ only, never field values.
439
+ - **`gateway-{instance}.log`** — same `log_sink=file`/`std` split as above; a plain
440
+ drain of TWS/Gateway's own stdout/stderr, captured for the process's whole life
441
+ either way. Used to diagnose issues *inside* Gateway itself (JWPRINT prints, JVM
442
+ warnings, the occasional `InaccessibleObjectException` and similar). Level/content
443
+ are entirely TWS/Gateway's own; ibcontroller only pipes it to a file or the console.
444
+ - **`ibcontroller-java-agent-{instance}.log`** — **always a file, regardless of
445
+ `log_sink`** — the Java agent's `java.util.logging` output, from the JVM that
446
+ actually hosts TWS/Gateway, never routed to the console. At `debug` this is the
447
+ noisiest stream by far (every processed command, logged from inside the JVM
448
+ itself); console output at that level would drown out ibcontroller's own log
449
+ lines, so it stays file-only the same way the wire trace does. It logs connection
450
+ accepts/disconnects, each processed command's name + target/label/path/window_id
451
+ (**never a value** — `set_text`/`set_checkbox` values and therefore credentials can
452
+ never land here, mirroring Python's `Secret` redaction), window register/unregister,
453
+ and an event-queue overflow warning. Its level follows `log_level` via the table
454
+ above — note `debug` maps to JUL `FINE`.
455
+ - **`cmd-{instance}.jsonl` / `events-{instance}.jsonl`** — the raw wire protocol,
456
+ one JSON object per line, no formatter prefix (pure NDJSON), primarily for
457
+ debugging what was actually sent and received at the socket boundary.
458
+ `cmd-{instance}.jsonl` records each command as sent, then its result or error,
459
+ correlated by a `seq`. `events-{instance}.jsonl` mirrors **every** event message the
460
+ agent pushes, in order. Files are truncated at startup (per-session only) and are
461
+ deliberately a separate, opt-in stream — they are verbose by design and not meant
462
+ for steady-state operation.
463
+
464
+ ### Ordering / correctness notes
465
+
466
+ - File writes happen on dedicated listener threads (Python side) and a background
467
+ writer thread (Java side); a slow log volume never blocks the control loop or the
468
+ AWT event dispatch.
469
+ - All five files carry the instance name and share one flat directory, so two
470
+ instances (e.g. `paper` and `live`) can run in parallel and each tail its own files
471
+ without interleaving, even if copied or globbed out of their directory.
472
+
473
+ ## License
474
+
475
+ ```text
476
+
477
+ Copyright 2023 gnzsnz
478
+
479
+ Licensed under the Apache License, Version 2.0 (the "License");
480
+ you may not use this file except in compliance with the License.
481
+ You may obtain a copy of the License at
482
+
483
+ http://www.apache.org/licenses/LICENSE-2.0
484
+
485
+ Unless required by applicable law or agreed to in writing, software
486
+ distributed under the License is distributed on an "AS IS" BASIS,
487
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
488
+ See the License for the specific language governing permissions and
489
+ limitations under the License.
490
+ ```
@@ -0,0 +1,30 @@
1
+ ibcontroller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ibcontroller/actions.py,sha256=x_vmWq7v4DdI8d9rnqGiHgIof3ts2ZxVWkQPjD0YtN8,10327
3
+ ibcontroller/agent_client.py,sha256=1d4qt3-aNeZahF31Hb2RafgY8V2UTHcUhjPpseoWbd4,18064
4
+ ibcontroller/app_dirs.py,sha256=qB5MR9t8MTDbWAMhusYGZt5otKH0iSmyCOkcYDdUol0,1631
5
+ ibcontroller/cli.py,sha256=j8a-W0TtZctNjbnqj2tueMKiZV0L1zjFBpkZ6yTg_3E,7901
6
+ ibcontroller/config.py,sha256=toO69CFqsUnEXXXTlFkS3kGHGTzMjgSKmU3K392De5c,18305
7
+ ibcontroller/control_loop.py,sha256=2UkC9D6HLffLScmb7GPj5TkhWGvAORvK0QrvN7wGa7c,27080
8
+ ibcontroller/diagnostics.py,sha256=zgnvKJ-Y8qCeI6MtaPTg9-W9nNFlNnqbqGmOQFHeqgI,4684
9
+ ibcontroller/dispatch.py,sha256=D7Y_hC1A8Wh26EbCd69-tnPl1sr6Xg9JYtDLnvvF6Ao,12528
10
+ ibcontroller/ibcontroller-agent.jar,sha256=R3ejhCqzby4eqp7cf-VTqY1KfBxaUb6090xUv1MeCbc,31328
11
+ ibcontroller/labels.py,sha256=e-Thvi3Efl0mrXo-OXKHTHoje8izafgcCZcFwyp3Ud0,9353
12
+ ibcontroller/launcher.py,sha256=67mwD6fF3t0dFB88xBYX1s4DGPaAIOnWnXeKydJYogY,37787
13
+ ibcontroller/logging_setup.py,sha256=LaatDyOoM3q_ViE9YVo6MhdrI_oj-MM6RsIqsZDO5cs,13236
14
+ ibcontroller/login.py,sha256=zo4W_lUzRLcvXYeNh6GNbDsMkkij23mTNNQ4PTXQ_HU,26825
15
+ ibcontroller/main.py,sha256=9zRClBQ2C5HW93SXF15hXMRG7As-LDrUakxlSP23yWA,6988
16
+ ibcontroller/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ ibcontroller/recognisers.py,sha256=-2vS5sIo9k7AFo1chDhkUzd80k6KUjeVPCGd0RQObZ8,17476
18
+ ibcontroller/schedule.py,sha256=H5qCz6xLGUik6_pIprHlZP5P52NxOSN4dnM7hGRA3-4,6428
19
+ ibcontroller/settings.py,sha256=Z8RDPk9OLPL87moqreIXUEtWskHEOVgluArUz76akTk,24927
20
+ ibcontroller/data/builtin_settings.toml,sha256=dE3UN-RaKWwWvu0bH6hAPyiN2h-VTjcydKve2hqxFAo,1797
21
+ ibcontroller/data/ibcontroller.toml.example,sha256=DsK2DxTo-Jp_wwOUFbIqt8jbK3aBWAZnjLXbbvuUS64,8687
22
+ ibcontroller/data/ibkr_settings.toml.example,sha256=g36cG8Y8bhz9TQT-Z_j2eGZpzzytK2dMFMAhSODee0E,6662
23
+ ibcontroller/data/labels.json,sha256=ivPbxGkFMawwoUz4O6M0I5C8Ib0P3ytEi7hZSvPZc0o,3369
24
+ ibcontroller/data/labels.json.example,sha256=GDUEA9C3YHYDM340s76Qa43jWQRQg0KUZX3aQBfb6co,1879
25
+ py_ib_controller-0.0.0.dist-info/licenses/LICENSE.txt,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
26
+ py_ib_controller-0.0.0.dist-info/METADATA,sha256=A0jihHoaSm65U5t4lpTS-rUVOqaRoe4lbHKTxQCiglw,31665
27
+ py_ib_controller-0.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
28
+ py_ib_controller-0.0.0.dist-info/entry_points.txt,sha256=UanWNovn4ihngttyB0ihY3mDKQHikPHe5B8SWZcR6WA,54
29
+ py_ib_controller-0.0.0.dist-info/top_level.txt,sha256=Crs8viMOKB5BDWE1JdOnTswdhmtxqanP7t1RCctDP8o,13
30
+ py_ib_controller-0.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ibcontroller = ibcontroller.cli:app