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.
- ibcontroller/__init__.py +0 -0
- ibcontroller/actions.py +273 -0
- ibcontroller/agent_client.py +466 -0
- ibcontroller/app_dirs.py +44 -0
- ibcontroller/cli.py +211 -0
- ibcontroller/config.py +410 -0
- ibcontroller/control_loop.py +636 -0
- ibcontroller/data/builtin_settings.toml +37 -0
- ibcontroller/data/ibcontroller.toml.example +178 -0
- ibcontroller/data/ibkr_settings.toml.example +135 -0
- ibcontroller/data/labels.json +81 -0
- ibcontroller/data/labels.json.example +44 -0
- ibcontroller/diagnostics.py +119 -0
- ibcontroller/dispatch.py +288 -0
- ibcontroller/ibcontroller-agent.jar +0 -0
- ibcontroller/labels.py +242 -0
- ibcontroller/launcher.py +909 -0
- ibcontroller/logging_setup.py +277 -0
- ibcontroller/login.py +578 -0
- ibcontroller/main.py +156 -0
- ibcontroller/py.typed +0 -0
- ibcontroller/recognisers.py +448 -0
- ibcontroller/schedule.py +157 -0
- ibcontroller/settings.py +554 -0
- py_ib_controller-0.0.0.dist-info/METADATA +490 -0
- py_ib_controller-0.0.0.dist-info/RECORD +30 -0
- py_ib_controller-0.0.0.dist-info/WHEEL +5 -0
- py_ib_controller-0.0.0.dist-info/entry_points.txt +2 -0
- py_ib_controller-0.0.0.dist-info/licenses/LICENSE.txt +176 -0
- py_ib_controller-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -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,,
|