py-ib-controller 0.0.0__tar.gz → 0.1.2__tar.gz
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.
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/.github/workflows/ci.yml +7 -2
- py_ib_controller-0.1.2/.github/workflows/publish.yml +148 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/PKG-INFO +27 -13
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/README.md +26 -12
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/docs/development.md +8 -1
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/cli.py +1 -1
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/config.py +23 -4
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/ibcontroller-agent.jar +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/launcher.py +24 -64
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/py_ib_controller.egg-info/PKG-INFO +27 -13
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/py_ib_controller.egg-info/SOURCES.txt +2 -0
- py_ib_controller-0.1.2/py_ib_controller.egg-info/scm_file_list.json +69 -0
- py_ib_controller-0.1.2/py_ib_controller.egg-info/scm_version.json +8 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/pyproject.toml +2 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_config.py +38 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_launcher.py +74 -69
- py_ib_controller-0.0.0/.github/workflows/publish.yml +0 -79
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/.github/ISSUE_TEMPLATE.md +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/.github/dependabot.yml +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/.gitignore +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/.pre-commit-config.yaml +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/.sdkmanrc +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/CONTRIBUTING.md +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/LICENSE.txt +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/Makefile +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/agent/src/main/java/ibcontroller/agent/AgentMain.java +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/agent/src/main/java/ibcontroller/agent/ComponentLookup.java +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/agent/src/main/java/ibcontroller/agent/EventBridge.java +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/agent/src/main/java/ibcontroller/agent/Protocol.java +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/agent/src/main/java/ibcontroller/agent/Security.java +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/agent/src/main/java/ibcontroller/agent/WriteOps.java +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/docs/Architecture.svg +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/docs/Configuration.md +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/__init__.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/actions.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/agent_client.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/app_dirs.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/control_loop.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/data/builtin_settings.toml +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/data/ibcontroller.toml.example +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/data/ibkr_settings.toml.example +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/data/labels.json +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/data/labels.json.example +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/diagnostics.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/dispatch.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/labels.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/logging_setup.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/login.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/main.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/py.typed +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/recognisers.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/schedule.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller/settings.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/ibcontroller.env +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/py_ib_controller.egg-info/dependency_links.txt +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/py_ib_controller.egg-info/entry_points.txt +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/py_ib_controller.egg-info/requires.txt +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/py_ib_controller.egg-info/top_level.txt +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/setup.cfg +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/__init__.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/conftest.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/fakes.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_actions.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_agent_client.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_app_dirs.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_cli.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_control_loop.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_diagnostics.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_dispatch.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_labels.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_logging_setup.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_login.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_main.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_recognisers.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_schedule.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_settings.py +0 -0
- {py_ib_controller-0.0.0 → py_ib_controller-0.1.2}/tests/test_trace_integration.py +0 -0
|
@@ -5,6 +5,11 @@ on:
|
|
|
5
5
|
branches: [master]
|
|
6
6
|
pull_request:
|
|
7
7
|
workflow_dispatch:
|
|
8
|
+
workflow_call:
|
|
9
|
+
|
|
10
|
+
env:
|
|
11
|
+
JAVA_VERSION: "17"
|
|
12
|
+
JAVA_DISTRIBUTION: zulu
|
|
8
13
|
|
|
9
14
|
jobs:
|
|
10
15
|
check-build-test:
|
|
@@ -29,8 +34,8 @@ jobs:
|
|
|
29
34
|
- name: Set up JDK
|
|
30
35
|
uses: actions/setup-java@v6
|
|
31
36
|
with:
|
|
32
|
-
distribution:
|
|
33
|
-
java-version:
|
|
37
|
+
distribution: ${{ env.JAVA_DISTRIBUTION }}
|
|
38
|
+
java-version: ${{ env.JAVA_VERSION }}
|
|
34
39
|
|
|
35
40
|
- name: Build Java agent jar
|
|
36
41
|
run: make build
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
env:
|
|
8
|
+
PYTHON_VERSION: "3.12"
|
|
9
|
+
JAVA_VERSION: "17"
|
|
10
|
+
JAVA_DISTRIBUTION: zulu
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
check:
|
|
14
|
+
uses: ./.github/workflows/ci.yml
|
|
15
|
+
|
|
16
|
+
build:
|
|
17
|
+
needs: check
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout full history
|
|
21
|
+
uses: actions/checkout@v7
|
|
22
|
+
with:
|
|
23
|
+
# setuptools-scm derives the package version from git tags
|
|
24
|
+
# (pyproject.toml: dynamic = ["version"]); a shallow clone
|
|
25
|
+
# would silently produce a fallback/dev version.
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
|
|
28
|
+
- name: Install uv and Python
|
|
29
|
+
uses: astral-sh/setup-uv@v10.1.0
|
|
30
|
+
with:
|
|
31
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
32
|
+
|
|
33
|
+
- name: Restore environment
|
|
34
|
+
run: uv sync --all-groups
|
|
35
|
+
|
|
36
|
+
- name: Set up JDK
|
|
37
|
+
uses: actions/setup-java@v6
|
|
38
|
+
with:
|
|
39
|
+
distribution: ${{ env.JAVA_DISTRIBUTION }}
|
|
40
|
+
java-version: ${{ env.JAVA_VERSION }}
|
|
41
|
+
|
|
42
|
+
- name: Build jar and Python distribution
|
|
43
|
+
run: make dist
|
|
44
|
+
|
|
45
|
+
- name: Store the distribution packages
|
|
46
|
+
uses: actions/upload-artifact@v7
|
|
47
|
+
with:
|
|
48
|
+
name: python-package-distributions
|
|
49
|
+
path: dist/
|
|
50
|
+
|
|
51
|
+
publish-to-pypi:
|
|
52
|
+
needs: build
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
environment: pypi
|
|
55
|
+
permissions:
|
|
56
|
+
contents: read
|
|
57
|
+
id-token: write # trusted publishing to PyPI -- no API token in secrets
|
|
58
|
+
steps:
|
|
59
|
+
- name: Download all the dists
|
|
60
|
+
uses: actions/download-artifact@v8
|
|
61
|
+
with:
|
|
62
|
+
name: python-package-distributions
|
|
63
|
+
path: dist/
|
|
64
|
+
|
|
65
|
+
- name: Publish to PyPI
|
|
66
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
67
|
+
|
|
68
|
+
github-release:
|
|
69
|
+
name: Sign distributions with Sigstore, upload to GitHub Release
|
|
70
|
+
needs: publish-to-pypi
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
permissions:
|
|
73
|
+
contents: write # mandatory for making GitHub Releases
|
|
74
|
+
id-token: write # mandatory for sigstore
|
|
75
|
+
steps:
|
|
76
|
+
- name: Checkout full history
|
|
77
|
+
uses: actions/checkout@v7
|
|
78
|
+
with:
|
|
79
|
+
# git-cliff needs the full commit/tag log to build the changelog
|
|
80
|
+
fetch-depth: 0
|
|
81
|
+
|
|
82
|
+
- name: Download all the dists
|
|
83
|
+
uses: actions/download-artifact@v8
|
|
84
|
+
with:
|
|
85
|
+
name: python-package-distributions
|
|
86
|
+
path: dist/
|
|
87
|
+
|
|
88
|
+
- name: Generate checksums
|
|
89
|
+
run: sha256sum dist/* > dist/SHA256SUMS
|
|
90
|
+
|
|
91
|
+
- name: Sign the dists with Sigstore
|
|
92
|
+
uses: sigstore/gh-action-sigstore-python@v3.5.0
|
|
93
|
+
with:
|
|
94
|
+
inputs: >-
|
|
95
|
+
./dist/*.tar.gz ./dist/*.whl
|
|
96
|
+
|
|
97
|
+
- name: Generate changelog
|
|
98
|
+
uses: orhun/git-cliff-action@v4
|
|
99
|
+
with:
|
|
100
|
+
# no cliff.toml -- git-cliff's built-in default config already
|
|
101
|
+
# groups Conventional Commits (feat/fix/docs/...) correctly
|
|
102
|
+
args: --latest --tag ${{ github.ref_name }} --strip header
|
|
103
|
+
output: CHANGELOG_release.md
|
|
104
|
+
|
|
105
|
+
- name: Assemble release notes
|
|
106
|
+
env:
|
|
107
|
+
TAG: ${{ github.ref_name }}
|
|
108
|
+
REPO: ${{ github.repository }}
|
|
109
|
+
run: |
|
|
110
|
+
PREV_TAG=$(git describe --tags --abbrev=0 "$TAG^" 2>/dev/null || true)
|
|
111
|
+
{
|
|
112
|
+
cat CHANGELOG_release.md
|
|
113
|
+
echo
|
|
114
|
+
echo "## Installation"
|
|
115
|
+
echo
|
|
116
|
+
echo " pip install py-ib-controller==${TAG#v}"
|
|
117
|
+
echo
|
|
118
|
+
echo "## Verifying release artifacts"
|
|
119
|
+
echo
|
|
120
|
+
echo "Assets are signed with [Sigstore](https://www.sigstore.dev/). To verify:"
|
|
121
|
+
echo
|
|
122
|
+
echo " pip install sigstore"
|
|
123
|
+
echo " sigstore verify identity dist/*.whl dist/*.tar.gz \\"
|
|
124
|
+
echo " --cert-identity https://github.com/$REPO/.github/workflows/publish.yml@refs/tags/$TAG \\"
|
|
125
|
+
echo " --cert-oidc-issuer https://token.actions.githubusercontent.com"
|
|
126
|
+
echo
|
|
127
|
+
echo "\`SHA256SUMS\` is attached for checksum verification."
|
|
128
|
+
if [ -n "$PREV_TAG" ]; then
|
|
129
|
+
echo
|
|
130
|
+
echo "**Full Changelog**: https://github.com/$REPO/compare/$PREV_TAG...$TAG"
|
|
131
|
+
fi
|
|
132
|
+
} > notes.md
|
|
133
|
+
|
|
134
|
+
- name: Create GitHub Release
|
|
135
|
+
env:
|
|
136
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
137
|
+
run: >-
|
|
138
|
+
gh release create '${{ github.ref_name }}'
|
|
139
|
+
--repo '${{ github.repository }}' --notes-file notes.md
|
|
140
|
+
|
|
141
|
+
- name: Upload artifacts and signatures to GitHub Release
|
|
142
|
+
env:
|
|
143
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
144
|
+
# dist/ contains the built packages, SHA256SUMS, plus the
|
|
145
|
+
# sigstore-produced signatures and certificates.
|
|
146
|
+
run: >-
|
|
147
|
+
gh release upload '${{ github.ref_name }}' dist/**
|
|
148
|
+
--repo '${{ github.repository }}'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-ib-controller
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Login/launch controller for TWS and IBKR Gateway, via an in-process Java agent
|
|
5
5
|
Author-email: gnzsnz <8376642+gnzsnz@users.noreply.github.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -28,18 +28,36 @@ Heavily inspired by [IBC](https://github.com/IbcAlpha/IBC) project, now archived
|
|
|
28
28
|
|
|
29
29
|
## What it does?
|
|
30
30
|
|
|
31
|
-
- Finds ibgateway/TWS installation, JRE and
|
|
31
|
+
- Finds ibgateway/TWS installation, JRE and launch it.
|
|
32
32
|
- Automates user/password entry, waits for MFA.
|
|
33
|
-
- Automates
|
|
33
|
+
- Automates ibgateway/TWS settings.
|
|
34
34
|
- Manages pop-ups and automatically accept.
|
|
35
|
-
|
|
36
35
|
- Manages ibgateway/TWS restarts.
|
|
37
36
|
|
|
38
37
|
It provides a "**declarative**" engine, so settings and pop-ups can be "declared" by configuration entries.
|
|
39
38
|
|
|
40
39
|
## How to use it
|
|
41
40
|
|
|
41
|
+
To use `ibcontroller` the recommended way is to install with uv/pipx or pip:
|
|
42
|
+
|
|
42
43
|
```bash
|
|
44
|
+
# install with uv/pipx
|
|
45
|
+
uv tool install py-ib-controller
|
|
46
|
+
|
|
47
|
+
# with pip
|
|
48
|
+
uv pip install py-ib-controller
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Give it a spin, `ib-controller run --dotenv=.env-paper --trading-mode=paper --program=gateway --tws-channel latest`
|
|
52
|
+
|
|
53
|
+
https://github.com/user-attachments/assets/b97f7180-29d9-4e09-b32f-8be838dff0e2
|
|
54
|
+
|
|
55
|
+
To build from source, this is the short version, for a detailed guide please check the [development guide](./docs/development.md).
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# 0. Fetch the code
|
|
59
|
+
git clone https://github.com/gnzsnz/ibcontroller.git
|
|
60
|
+
cd ibcontroller
|
|
43
61
|
# 1. Build the Java agent jar and install the package (editable, for a source checkout)
|
|
44
62
|
make dist
|
|
45
63
|
uv sync
|
|
@@ -47,11 +65,7 @@ uv sync
|
|
|
47
65
|
# 2. Scaffold a starter config directory (platformdirs default, or $IBC_APP_DIR)
|
|
48
66
|
uv run ibcontroller init
|
|
49
67
|
|
|
50
|
-
# 3. Export IBC_USERID / IBC_PASSWORD (see Credentials below)
|
|
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
|
-
|
|
68
|
+
# 3. Export IBC_USERID / IBC_PASSWORD or create an .env file (see Credentials below).
|
|
55
69
|
# 4. Run it -- Ctrl-C for a graceful shutdown
|
|
56
70
|
uv run ibcontroller run
|
|
57
71
|
```
|
|
@@ -61,8 +75,6 @@ uv run ibcontroller run
|
|
|
61
75
|
run directly on a fresh install -- it will still fail with a clear error naming whichever
|
|
62
76
|
setting is actually missing (in practice, just the two credential env vars).
|
|
63
77
|
|
|
64
|
-
`ibcontroller version` prints the installed package version.
|
|
65
|
-
|
|
66
78
|
### CLI parameters
|
|
67
79
|
|
|
68
80
|
You can use the following cli parameters:
|
|
@@ -138,7 +150,9 @@ environment variables, with precedence `defaults < TOML file < .env < environmen
|
|
|
138
150
|
can be loaded from a `.env` file. Credentials are environment-variable-only and must
|
|
139
151
|
never appear in the config file.
|
|
140
152
|
|
|
141
|
-
A sample file with all settings is provided as an [example](./ibcontroller.env).
|
|
153
|
+
A sample file with all environment settings is provided as an [example](./ibcontroller.env).
|
|
154
|
+
In the same way a sample `.toml` file with all config is
|
|
155
|
+
[available](./ibcontroller/data/ibcontroller.toml.example).
|
|
142
156
|
|
|
143
157
|
`ibcontroller init` (or the first `ibcontroller run`) copies starter templates into the
|
|
144
158
|
config directory: `ibcontroller.toml` (every key commented out -- safe to run as-is,
|
|
@@ -263,7 +277,7 @@ malformed value is logged and skipped rather than aborting the other one.
|
|
|
263
277
|
|
|
264
278
|
- `cold_restart_time` -- `"HH:MM"`, 24-hour, local time. Every **Sunday** at this time,
|
|
265
279
|
ibcontroller closes the instance tidily and relaunches with a full fresh login forcing the weekly
|
|
266
|
-
|
|
280
|
+
re-auth IBKR requires around Sunday 01:00 US/Eastern token invalidation.
|
|
267
281
|
- `closedown_at` -- `"HH:MM"` (every day) or `"<Weekday> HH:MM"` (one day a week,
|
|
268
282
|
`Weekday` a full English name, `Monday`..`Sunday`). Closes the instance tidily at that
|
|
269
283
|
time, with no relaunch -- you're responsible for restarting it yourself.
|
|
@@ -6,18 +6,36 @@ Heavily inspired by [IBC](https://github.com/IbcAlpha/IBC) project, now archived
|
|
|
6
6
|
|
|
7
7
|
## What it does?
|
|
8
8
|
|
|
9
|
-
- Finds ibgateway/TWS installation, JRE and
|
|
9
|
+
- Finds ibgateway/TWS installation, JRE and launch it.
|
|
10
10
|
- Automates user/password entry, waits for MFA.
|
|
11
|
-
- Automates
|
|
11
|
+
- Automates ibgateway/TWS settings.
|
|
12
12
|
- Manages pop-ups and automatically accept.
|
|
13
|
-
|
|
14
13
|
- Manages ibgateway/TWS restarts.
|
|
15
14
|
|
|
16
15
|
It provides a "**declarative**" engine, so settings and pop-ups can be "declared" by configuration entries.
|
|
17
16
|
|
|
18
17
|
## How to use it
|
|
19
18
|
|
|
19
|
+
To use `ibcontroller` the recommended way is to install with uv/pipx or pip:
|
|
20
|
+
|
|
20
21
|
```bash
|
|
22
|
+
# install with uv/pipx
|
|
23
|
+
uv tool install py-ib-controller
|
|
24
|
+
|
|
25
|
+
# with pip
|
|
26
|
+
uv pip install py-ib-controller
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Give it a spin, `ib-controller run --dotenv=.env-paper --trading-mode=paper --program=gateway --tws-channel latest`
|
|
30
|
+
|
|
31
|
+
https://github.com/user-attachments/assets/b97f7180-29d9-4e09-b32f-8be838dff0e2
|
|
32
|
+
|
|
33
|
+
To build from source, this is the short version, for a detailed guide please check the [development guide](./docs/development.md).
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# 0. Fetch the code
|
|
37
|
+
git clone https://github.com/gnzsnz/ibcontroller.git
|
|
38
|
+
cd ibcontroller
|
|
21
39
|
# 1. Build the Java agent jar and install the package (editable, for a source checkout)
|
|
22
40
|
make dist
|
|
23
41
|
uv sync
|
|
@@ -25,11 +43,7 @@ uv sync
|
|
|
25
43
|
# 2. Scaffold a starter config directory (platformdirs default, or $IBC_APP_DIR)
|
|
26
44
|
uv run ibcontroller init
|
|
27
45
|
|
|
28
|
-
# 3. Export IBC_USERID / IBC_PASSWORD (see Credentials below)
|
|
29
|
-
# only settings actually required. Everything else in the base ibcontroller.toml
|
|
30
|
-
# is commented out and already safe to run as-is: trading_mode defaults to "paper",
|
|
31
|
-
# and tws_version is auto-detected from standard tws_path.
|
|
32
|
-
|
|
46
|
+
# 3. Export IBC_USERID / IBC_PASSWORD or create an .env file (see Credentials below).
|
|
33
47
|
# 4. Run it -- Ctrl-C for a graceful shutdown
|
|
34
48
|
uv run ibcontroller run
|
|
35
49
|
```
|
|
@@ -39,8 +53,6 @@ uv run ibcontroller run
|
|
|
39
53
|
run directly on a fresh install -- it will still fail with a clear error naming whichever
|
|
40
54
|
setting is actually missing (in practice, just the two credential env vars).
|
|
41
55
|
|
|
42
|
-
`ibcontroller version` prints the installed package version.
|
|
43
|
-
|
|
44
56
|
### CLI parameters
|
|
45
57
|
|
|
46
58
|
You can use the following cli parameters:
|
|
@@ -116,7 +128,9 @@ environment variables, with precedence `defaults < TOML file < .env < environmen
|
|
|
116
128
|
can be loaded from a `.env` file. Credentials are environment-variable-only and must
|
|
117
129
|
never appear in the config file.
|
|
118
130
|
|
|
119
|
-
A sample file with all settings is provided as an [example](./ibcontroller.env).
|
|
131
|
+
A sample file with all environment settings is provided as an [example](./ibcontroller.env).
|
|
132
|
+
In the same way a sample `.toml` file with all config is
|
|
133
|
+
[available](./ibcontroller/data/ibcontroller.toml.example).
|
|
120
134
|
|
|
121
135
|
`ibcontroller init` (or the first `ibcontroller run`) copies starter templates into the
|
|
122
136
|
config directory: `ibcontroller.toml` (every key commented out -- safe to run as-is,
|
|
@@ -241,7 +255,7 @@ malformed value is logged and skipped rather than aborting the other one.
|
|
|
241
255
|
|
|
242
256
|
- `cold_restart_time` -- `"HH:MM"`, 24-hour, local time. Every **Sunday** at this time,
|
|
243
257
|
ibcontroller closes the instance tidily and relaunches with a full fresh login forcing the weekly
|
|
244
|
-
|
|
258
|
+
re-auth IBKR requires around Sunday 01:00 US/Eastern token invalidation.
|
|
245
259
|
- `closedown_at` -- `"HH:MM"` (every day) or `"<Weekday> HH:MM"` (one day a week,
|
|
246
260
|
`Weekday` a full English name, `Monday`..`Sunday`). Closes the instance tidily at that
|
|
247
261
|
time, with no relaunch -- you're responsible for restarting it yourself.
|
|
@@ -87,7 +87,7 @@ as it can be: `javac` + `jar`, wired through a small `Makefile`.
|
|
|
87
87
|
|
|
88
88
|
## TWS / IBKR Gateway itself
|
|
89
89
|
|
|
90
|
-
Download from IBKR directly [gateway](
|
|
90
|
+
Download from IBKR directly [gateway](https://www.interactivebrokers.com/en/trading/ibgateway-latest.php) or [TWS](https://www.interactivebrokers.com/en/trading/download-tws.php). Then install.
|
|
91
91
|
|
|
92
92
|
After the first run, `ibcontroller` renames the gateway/TWS script. This is required to manage restarts.
|
|
93
93
|
|
|
@@ -123,3 +123,10 @@ Day-to-day cycle: `make build` after any agent-side change, `uv run pytest` for
|
|
|
123
123
|
source .venv/bin/activate
|
|
124
124
|
ibcontroller run --dotenv=.env-paper --trading-mode=paper --program=gateway
|
|
125
125
|
```
|
|
126
|
+
|
|
127
|
+
## Architecture
|
|
128
|
+
|
|
129
|
+
`ibcontroller` follows a layered architecture with separation of concerns between layers.
|
|
130
|
+
Any contribution must follow this separation of concerns.
|
|
131
|
+
|
|
132
|
+

|
|
@@ -198,7 +198,7 @@ def run( # noqa: PLR0913, PLR0917 -- one typer.Option per Config field, not
|
|
|
198
198
|
def version() -> None:
|
|
199
199
|
"""Print the installed ibcontroller version."""
|
|
200
200
|
try:
|
|
201
|
-
typer.echo(_pkg_version("
|
|
201
|
+
typer.echo(_pkg_version("py-ib-controller"))
|
|
202
202
|
except PackageNotFoundError:
|
|
203
203
|
typer.echo("unknown (not installed)")
|
|
204
204
|
|
|
@@ -131,6 +131,19 @@ def _log_level_converter(raw: object) -> int:
|
|
|
131
131
|
return _LOG_LEVELS[name]
|
|
132
132
|
|
|
133
133
|
|
|
134
|
+
def _expand_user(raw: object) -> str:
|
|
135
|
+
"""Expands `~` regardless of source (factory default, TOML file, env var, CLI) --
|
|
136
|
+
without this, a bare `Path(...)` downstream treats a literal `~` as a relative path
|
|
137
|
+
component and creates a `~` directory instead of expanding to the home directory
|
|
138
|
+
(gitea #57). Single point of truth for every path-typed Config field, so no
|
|
139
|
+
consumption site needs its own `.expanduser()` call."""
|
|
140
|
+
return str(Path(str(raw)).expanduser())
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _optional_path_converter(raw: object) -> str | None:
|
|
144
|
+
return None if raw is None else _expand_user(raw)
|
|
145
|
+
|
|
146
|
+
|
|
134
147
|
class TradingMode(StrEnum):
|
|
135
148
|
LIVE = "live"
|
|
136
149
|
PAPER = "paper"
|
|
@@ -208,7 +221,9 @@ class Config:
|
|
|
208
221
|
# The IBKR settings file to load (if any) -- see `ibkr_settings.toml.example`.
|
|
209
222
|
# Defaults to `None` (inert) until a user points `[settings] file` at a renamed
|
|
210
223
|
# copy of the example file. See module docstring for why this is deliberate.
|
|
211
|
-
settings_file: str | None =
|
|
224
|
+
settings_file: str | None = ts.option(
|
|
225
|
+
default=None, converter=_optional_path_converter
|
|
226
|
+
)
|
|
212
227
|
# instance default: f"{program}-{trading_mode.value}".
|
|
213
228
|
instance: str = "{program}-{trading_mode}"
|
|
214
229
|
program: str = "gateway" # "gateway" or "tws"
|
|
@@ -220,11 +235,13 @@ class Config:
|
|
|
220
235
|
# installs (not a strict enum). Defaults to "stable", the safer pick when several
|
|
221
236
|
# channels are installed side by side.
|
|
222
237
|
tws_channel: str = "stable"
|
|
223
|
-
tws_path: str | None = None
|
|
238
|
+
tws_path: str | None = ts.option(default=None, converter=_optional_path_converter)
|
|
224
239
|
# Where TWS/Gateway itself stores its settings (IBC's own TWS_SETTINGS_PATH) --
|
|
225
240
|
# deliberately NOT the same as tws_path (the install location). None defaults,
|
|
226
241
|
# per instance, in launcher.py's resolve_tws_settings_path.
|
|
227
|
-
tws_settings_path: str | None =
|
|
242
|
+
tws_settings_path: str | None = ts.option(
|
|
243
|
+
default=None, converter=_optional_path_converter
|
|
244
|
+
)
|
|
228
245
|
# IBC-key-compatible settings (file or env, never a secret).
|
|
229
246
|
trading_mode: TradingMode = TradingMode.PAPER
|
|
230
247
|
# IBC: ReadOnlyLogin -- loaded but not yet wired to any behavior.
|
|
@@ -280,7 +297,9 @@ class Config:
|
|
|
280
297
|
# (DictLoader), so the config file's `log_dir` and the IBC_LOG_DIR env var
|
|
281
298
|
# genuinely override it. See load_config for the loader order.
|
|
282
299
|
trace_enabled: bool = False
|
|
283
|
-
log_dir: str = ts.option(
|
|
300
|
+
log_dir: str = ts.option(
|
|
301
|
+
factory=lambda: str(resolve_app_dirs()[1]), converter=_expand_user
|
|
302
|
+
)
|
|
284
303
|
# Logging level for ibcontroller's own log file (not the raw wire trace).
|
|
285
304
|
# Was previously unannotated (`log_level = logging.INFO`), which meant attrs
|
|
286
305
|
# never turned it into a real field at all -- not configurable, silently fixed
|
|
Binary file
|
|
@@ -16,7 +16,6 @@ import asyncio
|
|
|
16
16
|
import functools
|
|
17
17
|
import logging
|
|
18
18
|
import platform
|
|
19
|
-
import plistlib
|
|
20
19
|
import re
|
|
21
20
|
from dataclasses import dataclass
|
|
22
21
|
from pathlib import Path
|
|
@@ -95,7 +94,7 @@ def _os_name() -> str:
|
|
|
95
94
|
|
|
96
95
|
def _resolve_tws_path(config: Config, os_name: str) -> Path:
|
|
97
96
|
if config.tws_path:
|
|
98
|
-
return Path(config.tws_path)
|
|
97
|
+
return Path(config.tws_path)
|
|
99
98
|
return Path.home() / ("Applications" if os_name == "macos" else "Jts")
|
|
100
99
|
|
|
101
100
|
|
|
@@ -113,7 +112,7 @@ def resolve_tws_settings_path(config: Config) -> Path:
|
|
|
113
112
|
running live and paper concurrently is this project's normal use case,
|
|
114
113
|
not an edge case to opt into safety for."""
|
|
115
114
|
if config.tws_settings_path:
|
|
116
|
-
return Path(config.tws_settings_path)
|
|
115
|
+
return Path(config.tws_settings_path)
|
|
117
116
|
return Path.home() / "Jts" / config.instance
|
|
118
117
|
|
|
119
118
|
|
|
@@ -311,11 +310,10 @@ def _version_sort_key(version: str) -> tuple[int, ...]:
|
|
|
311
310
|
|
|
312
311
|
def _read_i4j_variable(install4j_dir: Path, name: str) -> str | None:
|
|
313
312
|
"""A plain install4j `<variable name="{name}" value="..." />` entry --
|
|
314
|
-
distinct from `
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
label."""
|
|
313
|
+
distinct from `_read_i4j_vmoptions`'s own handling of the much larger
|
|
314
|
+
`javaOptions` variable's own value blob (two different shapes in the same
|
|
315
|
+
`i4jparams.conf` file). Used for `channel`, which each installed version
|
|
316
|
+
carries its own real value for, not a fixed label."""
|
|
319
317
|
conf = install4j_dir / "i4jparams.conf"
|
|
320
318
|
if not conf.is_file():
|
|
321
319
|
return None
|
|
@@ -441,48 +439,17 @@ def _read_vmoptions_file(path: Path) -> list[str]:
|
|
|
441
439
|
return options
|
|
442
440
|
|
|
443
441
|
|
|
444
|
-
def
|
|
445
|
-
"""
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
conf
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
def _read_macos_vmoptions(app_bundle: Path) -> list[str]:
|
|
456
|
-
"""`--add-opens`/`--add-exports` (JPMS strong encapsulation) live in the
|
|
457
|
-
`.app` bundle's `Info.plist` (install4j's `JavaVM`/`VMOptionArray`), not
|
|
458
|
-
in `ibgateway.vmoptions`. Read live from the installed app, not
|
|
459
|
-
hardcoded, since it can change per version.
|
|
460
|
-
|
|
461
|
-
Takes the `.app` bundle's own path directly (not `program_path`): the
|
|
462
|
-
bundle may already have been renamed by `_prevent_native_restart`, so
|
|
463
|
-
re-deriving `f"{program_path.name}.app"` here would silently miss it on
|
|
464
|
-
every relaunch after the first."""
|
|
465
|
-
app_plist = app_bundle / "Contents" / "Info.plist"
|
|
466
|
-
if not app_plist.is_file():
|
|
467
|
-
return []
|
|
468
|
-
with app_plist.open("rb") as f:
|
|
469
|
-
data = plistlib.load(f)
|
|
470
|
-
raw_options = data.get("JavaVM", {}).get("VMOptionArray", [])
|
|
471
|
-
return [opt for opt in raw_options if "${" not in opt and not opt.startswith("-D")]
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
def _read_linux_vmoptions(install4j_dir: Path) -> list[str]:
|
|
475
|
-
"""`--add-opens`/`--add-exports` (JPMS strong encapsulation) live in
|
|
476
|
-
install4j's own `javaOptions` variable inside `i4jparams.conf` on Linux,
|
|
477
|
-
not in `tws.vmoptions`/`ibgateway.vmoptions`. Mirrors
|
|
478
|
-
`_read_macos_vmoptions`'s role for `Info.plist` on macOS, just a
|
|
479
|
-
different source file for the same install4j mechanism.
|
|
480
|
-
|
|
481
|
-
Unlike the macOS version, `-D` tokens are kept here rather than filtered:
|
|
482
|
-
on Linux this same variable is also the only place some installs carry
|
|
483
|
-
real `-D` fixes (e.g. Gateway's `-Djdk.xml.elementAttributeLimit`) and a
|
|
484
|
-
`-DjxBrowserKey`. A harmless duplicate of `_read_jxbrowser_key`'s own
|
|
485
|
-
value when both fire -- the JVM just takes the last one."""
|
|
442
|
+
def _read_i4j_vmoptions(install4j_dir: Path) -> list[str]:
|
|
443
|
+
"""`--add-opens`/`--add-exports` (JPMS strong encapsulation), the real
|
|
444
|
+
XML-parser `-D` fixes (e.g. Gateway's `-Djdk.xml.elementAttributeLimit`),
|
|
445
|
+
and `-DjxBrowserKey` all live in install4j's own `javaOptions` variable
|
|
446
|
+
inside `i4jparams.conf`, not in `tws.vmoptions`/`ibgateway.vmoptions` --
|
|
447
|
+
true on both Linux and macOS (checked directly against IBC's own
|
|
448
|
+
`ibcstart.sh`, which reads this same variable, from this same
|
|
449
|
+
`{program_path}/.install4j` location, on both platforms uniformly, and
|
|
450
|
+
never touches macOS's `Info.plist` at all). `-D` tokens are kept here
|
|
451
|
+
rather than filtered -- this is the only place some installs carry them,
|
|
452
|
+
so filtering would silently drop real options."""
|
|
486
453
|
raw = _read_i4j_variable(install4j_dir, "javaOptions")
|
|
487
454
|
if not raw:
|
|
488
455
|
return []
|
|
@@ -501,9 +468,7 @@ def _prevent_native_restart(program_path: Path) -> Path:
|
|
|
501
468
|
it back.
|
|
502
469
|
|
|
503
470
|
Idempotent: a second call after the rename already happened is a no-op,
|
|
504
|
-
returning the same renamed path.
|
|
505
|
-
path either way, since `_read_macos_vmoptions` needs to read
|
|
506
|
-
`Info.plist` from wherever it actually lives now, renamed or not.
|
|
471
|
+
returning the same renamed path.
|
|
507
472
|
|
|
508
473
|
macOS only, called only under `os_name == "macos"` in `build_launch_plan`
|
|
509
474
|
-- see `_prevent_native_restart_linux` for the Linux equivalent, called
|
|
@@ -538,10 +503,10 @@ def _prevent_native_restart_linux(program_path: Path, script_name: str) -> Path:
|
|
|
538
503
|
|
|
539
504
|
Idempotent, matching `_prevent_native_restart`: a second call after the
|
|
540
505
|
rename already happened is a no-op, returning the same renamed path.
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
506
|
+
The return value doesn't need to be read back by a vmoptions reader --
|
|
507
|
+
`_read_i4j_vmoptions` reads from `.install4j/i4jparams.conf`, which
|
|
508
|
+
doesn't move when this script is renamed -- so callers only need this
|
|
509
|
+
for its side effect."""
|
|
545
510
|
original = program_path / script_name
|
|
546
511
|
renamed = program_path / f"{script_name}-1"
|
|
547
512
|
if renamed.exists():
|
|
@@ -624,17 +589,12 @@ def build_launch_plan(
|
|
|
624
589
|
channel = _read_i4j_variable(install4j_dir, "channel") or config.tws_channel
|
|
625
590
|
vm_options.append(f"-Dchannel={channel}")
|
|
626
591
|
|
|
627
|
-
jxbrowser_key = _read_jxbrowser_key(install4j_dir)
|
|
628
|
-
if jxbrowser_key:
|
|
629
|
-
vm_options.append(f"-DjxBrowserKey={jxbrowser_key}")
|
|
630
|
-
|
|
631
592
|
if os_name == "macos":
|
|
632
|
-
|
|
633
|
-
vm_options.extend(_read_macos_vmoptions(app_bundle))
|
|
593
|
+
_prevent_native_restart(program_path)
|
|
634
594
|
else:
|
|
635
595
|
script_name = "ibgateway" if resolved_program == "gateway" else "tws"
|
|
636
596
|
_prevent_native_restart_linux(program_path, script_name)
|
|
637
|
-
|
|
597
|
+
vm_options.extend(_read_i4j_vmoptions(install4j_dir))
|
|
638
598
|
|
|
639
599
|
# Agent-side logging (java.util.logging, AgentMain.configureLogging, 2026-09-08):
|
|
640
600
|
# its own per-instance file under the same shared log dir Python uses, so the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-ib-controller
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Login/launch controller for TWS and IBKR Gateway, via an in-process Java agent
|
|
5
5
|
Author-email: gnzsnz <8376642+gnzsnz@users.noreply.github.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -28,18 +28,36 @@ Heavily inspired by [IBC](https://github.com/IbcAlpha/IBC) project, now archived
|
|
|
28
28
|
|
|
29
29
|
## What it does?
|
|
30
30
|
|
|
31
|
-
- Finds ibgateway/TWS installation, JRE and
|
|
31
|
+
- Finds ibgateway/TWS installation, JRE and launch it.
|
|
32
32
|
- Automates user/password entry, waits for MFA.
|
|
33
|
-
- Automates
|
|
33
|
+
- Automates ibgateway/TWS settings.
|
|
34
34
|
- Manages pop-ups and automatically accept.
|
|
35
|
-
|
|
36
35
|
- Manages ibgateway/TWS restarts.
|
|
37
36
|
|
|
38
37
|
It provides a "**declarative**" engine, so settings and pop-ups can be "declared" by configuration entries.
|
|
39
38
|
|
|
40
39
|
## How to use it
|
|
41
40
|
|
|
41
|
+
To use `ibcontroller` the recommended way is to install with uv/pipx or pip:
|
|
42
|
+
|
|
42
43
|
```bash
|
|
44
|
+
# install with uv/pipx
|
|
45
|
+
uv tool install py-ib-controller
|
|
46
|
+
|
|
47
|
+
# with pip
|
|
48
|
+
uv pip install py-ib-controller
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Give it a spin, `ib-controller run --dotenv=.env-paper --trading-mode=paper --program=gateway --tws-channel latest`
|
|
52
|
+
|
|
53
|
+
https://github.com/user-attachments/assets/b97f7180-29d9-4e09-b32f-8be838dff0e2
|
|
54
|
+
|
|
55
|
+
To build from source, this is the short version, for a detailed guide please check the [development guide](./docs/development.md).
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# 0. Fetch the code
|
|
59
|
+
git clone https://github.com/gnzsnz/ibcontroller.git
|
|
60
|
+
cd ibcontroller
|
|
43
61
|
# 1. Build the Java agent jar and install the package (editable, for a source checkout)
|
|
44
62
|
make dist
|
|
45
63
|
uv sync
|
|
@@ -47,11 +65,7 @@ uv sync
|
|
|
47
65
|
# 2. Scaffold a starter config directory (platformdirs default, or $IBC_APP_DIR)
|
|
48
66
|
uv run ibcontroller init
|
|
49
67
|
|
|
50
|
-
# 3. Export IBC_USERID / IBC_PASSWORD (see Credentials below)
|
|
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
|
-
|
|
68
|
+
# 3. Export IBC_USERID / IBC_PASSWORD or create an .env file (see Credentials below).
|
|
55
69
|
# 4. Run it -- Ctrl-C for a graceful shutdown
|
|
56
70
|
uv run ibcontroller run
|
|
57
71
|
```
|
|
@@ -61,8 +75,6 @@ uv run ibcontroller run
|
|
|
61
75
|
run directly on a fresh install -- it will still fail with a clear error naming whichever
|
|
62
76
|
setting is actually missing (in practice, just the two credential env vars).
|
|
63
77
|
|
|
64
|
-
`ibcontroller version` prints the installed package version.
|
|
65
|
-
|
|
66
78
|
### CLI parameters
|
|
67
79
|
|
|
68
80
|
You can use the following cli parameters:
|
|
@@ -138,7 +150,9 @@ environment variables, with precedence `defaults < TOML file < .env < environmen
|
|
|
138
150
|
can be loaded from a `.env` file. Credentials are environment-variable-only and must
|
|
139
151
|
never appear in the config file.
|
|
140
152
|
|
|
141
|
-
A sample file with all settings is provided as an [example](./ibcontroller.env).
|
|
153
|
+
A sample file with all environment settings is provided as an [example](./ibcontroller.env).
|
|
154
|
+
In the same way a sample `.toml` file with all config is
|
|
155
|
+
[available](./ibcontroller/data/ibcontroller.toml.example).
|
|
142
156
|
|
|
143
157
|
`ibcontroller init` (or the first `ibcontroller run`) copies starter templates into the
|
|
144
158
|
config directory: `ibcontroller.toml` (every key commented out -- safe to run as-is,
|
|
@@ -263,7 +277,7 @@ malformed value is logged and skipped rather than aborting the other one.
|
|
|
263
277
|
|
|
264
278
|
- `cold_restart_time` -- `"HH:MM"`, 24-hour, local time. Every **Sunday** at this time,
|
|
265
279
|
ibcontroller closes the instance tidily and relaunches with a full fresh login forcing the weekly
|
|
266
|
-
|
|
280
|
+
re-auth IBKR requires around Sunday 01:00 US/Eastern token invalidation.
|
|
267
281
|
- `closedown_at` -- `"HH:MM"` (every day) or `"<Weekday> HH:MM"` (one day a week,
|
|
268
282
|
`Weekday` a full English name, `Monday`..`Sunday`). Closes the instance tidily at that
|
|
269
283
|
time, with no relaunch -- you're responsible for restarting it yourself.
|