ib-connect 0.2.0__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.
- ib_connect-0.2.0/LICENSE +21 -0
- ib_connect-0.2.0/PKG-INFO +151 -0
- ib_connect-0.2.0/README.md +134 -0
- ib_connect-0.2.0/ib_connect/__init__.py +1 -0
- ib_connect-0.2.0/ib_connect/gui/__init__.py +1 -0
- ib_connect-0.2.0/ib_connect/gui/app.py +153 -0
- ib_connect-0.2.0/ib_connect/shared/__init__.py +1 -0
- ib_connect-0.2.0/ib_connect/shared/ib_connection.py +34 -0
- ib_connect-0.2.0/ib_connect/skills/__init__.py +1 -0
- ib_connect-0.2.0/ib_connect/skills/data_upload/__init__.py +1 -0
- ib_connect-0.2.0/ib_connect/skills/data_upload/data_upload.py +371 -0
- ib_connect-0.2.0/ib_connect/skills/ib_download/__init__.py +1 -0
- ib_connect-0.2.0/ib_connect/skills/ib_download/download.py +63 -0
- ib_connect-0.2.0/ib_connect/skills/ib_download/download_service.py +308 -0
- ib_connect-0.2.0/ib_connect/skills/ib_download/ib_download.py +188 -0
- ib_connect-0.2.0/ib_connect/skills/ib_download/job_queue.py +88 -0
- ib_connect-0.2.0/ib_connect/skills/ib_query/__init__.py +1 -0
- ib_connect-0.2.0/ib_connect/skills/ib_query/ib_query.py +62 -0
- ib_connect-0.2.0/ib_connect/skills/ib_query/query.py +160 -0
- ib_connect-0.2.0/ib_connect.egg-info/PKG-INFO +151 -0
- ib_connect-0.2.0/ib_connect.egg-info/SOURCES.txt +26 -0
- ib_connect-0.2.0/ib_connect.egg-info/dependency_links.txt +1 -0
- ib_connect-0.2.0/ib_connect.egg-info/entry_points.txt +3 -0
- ib_connect-0.2.0/ib_connect.egg-info/requires.txt +5 -0
- ib_connect-0.2.0/ib_connect.egg-info/top_level.txt +1 -0
- ib_connect-0.2.0/pyproject.toml +26 -0
- ib_connect-0.2.0/setup.cfg +4 -0
- ib_connect-0.2.0/setup.py +2 -0
ib_connect-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mats Bengtsson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ib_connect
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Modular Interactive Brokers integration toolkit — query contracts, download historical data, and upload to TimescaleDB.
|
|
5
|
+
Author-email: Mats Bengtsson <mats.bengtsson@outlook.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/alacazar/ib-connect
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: ib_insync
|
|
12
|
+
Requires-Dist: pandas
|
|
13
|
+
Requires-Dist: psycopg2-binary
|
|
14
|
+
Requires-Dist: watchdog
|
|
15
|
+
Requires-Dist: flask
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# IB Connect
|
|
19
|
+
|
|
20
|
+
A modular Python toolkit for Interactive Brokers integration — query contracts, download historical market data, and upload to a TimescaleDB database.
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
ib_connect/
|
|
26
|
+
├── shared/ # IB connection management (shared by all skills)
|
|
27
|
+
├── skills/
|
|
28
|
+
│ ├── ib_query/ # CLI: qualify and inspect contracts
|
|
29
|
+
│ ├── ib_download/ # CLI + service: async historical data downloader
|
|
30
|
+
│ └── data_upload/ # Service: watch folder → PostgreSQL/TimescaleDB
|
|
31
|
+
└── gui/ # Flask web UI
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Prerequisites
|
|
35
|
+
|
|
36
|
+
- Python 3.9+
|
|
37
|
+
- [IB Gateway](https://www.interactivebrokers.com/en/trading/ibgateway-stable.php) or TWS running with API connections enabled
|
|
38
|
+
- Paper trading port: `7497` (default)
|
|
39
|
+
- Live trading port: `7496`
|
|
40
|
+
- For `data_upload` and `gui`: PostgreSQL with [TimescaleDB](https://www.timescale.com/)
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/alacazar/ib-connect.git
|
|
46
|
+
cd ib_connect
|
|
47
|
+
pip install -e .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The `ib-query` and `ib-download` CLI commands are registered as entry points after installation.
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
The `ib_download` and `gui` skills require a `config.json`. Copy the provided examples and customize:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cp ib_connect/skills/ib_download/config.example.json ib_connect/skills/ib_download/config.json
|
|
58
|
+
cp ib_connect/gui/config.example.json ib_connect/gui/config.json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
For `data_upload`, set the `PG_URI` environment variable:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
export PG_URI="postgresql://user:password@localhost:5432/dbname"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Skills
|
|
68
|
+
|
|
69
|
+
### ib-query
|
|
70
|
+
|
|
71
|
+
Query and qualify contract details from IB.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
ib-query --symbol AAPL --sec-type STK
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Output:
|
|
78
|
+
```json
|
|
79
|
+
{"conId": 265598, "symbol": "AAPL", "secType": "STK", "exchange": "SMART", "currency": "USD", "fullName": "APPLE INC"}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
See [ib_connect/skills/ib_query/SKILL.md](ib_connect/skills/ib_query/SKILL.md) for all options.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### ib-download
|
|
87
|
+
|
|
88
|
+
Downloads historical OHLCV data from IB and writes CSV files. Consists of two parts:
|
|
89
|
+
|
|
90
|
+
**`download_service.py` — the background service**
|
|
91
|
+
|
|
92
|
+
IB enforces strict pacing limits: it throttles connections that issue too many data requests too quickly, and it only allows one active connection per client ID. Running a dedicated background service solves both problems — it owns a single, persistent IB connection and processes jobs one at a time, sleeping 20 seconds between data chunks to stay within IB's limits. Any number of callers can submit jobs without worrying about pacing or connection conflicts.
|
|
93
|
+
|
|
94
|
+
Start it once and keep it running (e.g. in `screen` or `tmux`, or as a system service):
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
python ib_connect/skills/ib_download/download_service.py
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Long date ranges are split automatically into chunks sized to IB's per-request limits. Progress and errors are logged to `download_service.log` alongside the script. A PID lock file prevents duplicate instances.
|
|
101
|
+
|
|
102
|
+
**`ib-download` — the CLI client**
|
|
103
|
+
|
|
104
|
+
Submits a job to the service's SQLite queue and returns a `job_key` immediately. The caller can check status later without staying connected.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Submit a job
|
|
108
|
+
ib-download -c 265598 -s 2023-01-01 -e 2023-12-31 -b "1 min"
|
|
109
|
+
|
|
110
|
+
# Check status
|
|
111
|
+
ib-download --status -k <job_key>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
On completion the service can notify an agent via webhook (configure `webhook_url` and `token` in `config.json`).
|
|
115
|
+
|
|
116
|
+
See [ib_connect/skills/ib_download/SKILL.md](ib_connect/skills/ib_download/SKILL.md) for all options including batch mode and output format.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### data-upload
|
|
121
|
+
|
|
122
|
+
A file-watching service that validates and uploads contract JSON files and OHLCV CSV files to PostgreSQL/TimescaleDB. Drop files into the configured `input_folder` and they are processed automatically — contracts go to `finance.contracts`, OHLCV data to `finance.ohlcv_1m` or `finance.ohlcv_1d`. Processed files are moved to `processed/`; failures go to `errors/`.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
python ib_connect/skills/data_upload/data_upload.py
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Expected filename pattern for OHLCV: `<symbol>.<conid>.<barsize>.csv`.
|
|
129
|
+
|
|
130
|
+
See [ib_connect/skills/data_upload/SKILL.md](ib_connect/skills/data_upload/SKILL.md) for schema details.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### GUI
|
|
135
|
+
|
|
136
|
+
A Flask web interface that ties the toolkit together. From the browser you can:
|
|
137
|
+
|
|
138
|
+
- **Query contracts** — search by symbol and security type, inspect the qualified contract details returned by IB
|
|
139
|
+
- **Save contracts** — export selected contracts as JSON to the `data_upload` input folder, so they are picked up and stored in PostgreSQL automatically
|
|
140
|
+
- **Submit download jobs** — configure date range and bar size per contract and enqueue jobs with the download service
|
|
141
|
+
- **Monitor jobs** — check status and cancel pending jobs
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
python ib_connect/gui/app.py
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Opens on `http://127.0.0.1:5000` by default (configurable in `gui/config.json`).
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# IB Connect
|
|
2
|
+
|
|
3
|
+
A modular Python toolkit for Interactive Brokers integration — query contracts, download historical market data, and upload to a TimescaleDB database.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
ib_connect/
|
|
9
|
+
├── shared/ # IB connection management (shared by all skills)
|
|
10
|
+
├── skills/
|
|
11
|
+
│ ├── ib_query/ # CLI: qualify and inspect contracts
|
|
12
|
+
│ ├── ib_download/ # CLI + service: async historical data downloader
|
|
13
|
+
│ └── data_upload/ # Service: watch folder → PostgreSQL/TimescaleDB
|
|
14
|
+
└── gui/ # Flask web UI
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- Python 3.9+
|
|
20
|
+
- [IB Gateway](https://www.interactivebrokers.com/en/trading/ibgateway-stable.php) or TWS running with API connections enabled
|
|
21
|
+
- Paper trading port: `7497` (default)
|
|
22
|
+
- Live trading port: `7496`
|
|
23
|
+
- For `data_upload` and `gui`: PostgreSQL with [TimescaleDB](https://www.timescale.com/)
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git clone https://github.com/alacazar/ib-connect.git
|
|
29
|
+
cd ib_connect
|
|
30
|
+
pip install -e .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The `ib-query` and `ib-download` CLI commands are registered as entry points after installation.
|
|
34
|
+
|
|
35
|
+
## Configuration
|
|
36
|
+
|
|
37
|
+
The `ib_download` and `gui` skills require a `config.json`. Copy the provided examples and customize:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cp ib_connect/skills/ib_download/config.example.json ib_connect/skills/ib_download/config.json
|
|
41
|
+
cp ib_connect/gui/config.example.json ib_connect/gui/config.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For `data_upload`, set the `PG_URI` environment variable:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
export PG_URI="postgresql://user:password@localhost:5432/dbname"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Skills
|
|
51
|
+
|
|
52
|
+
### ib-query
|
|
53
|
+
|
|
54
|
+
Query and qualify contract details from IB.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
ib-query --symbol AAPL --sec-type STK
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Output:
|
|
61
|
+
```json
|
|
62
|
+
{"conId": 265598, "symbol": "AAPL", "secType": "STK", "exchange": "SMART", "currency": "USD", "fullName": "APPLE INC"}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
See [ib_connect/skills/ib_query/SKILL.md](ib_connect/skills/ib_query/SKILL.md) for all options.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### ib-download
|
|
70
|
+
|
|
71
|
+
Downloads historical OHLCV data from IB and writes CSV files. Consists of two parts:
|
|
72
|
+
|
|
73
|
+
**`download_service.py` — the background service**
|
|
74
|
+
|
|
75
|
+
IB enforces strict pacing limits: it throttles connections that issue too many data requests too quickly, and it only allows one active connection per client ID. Running a dedicated background service solves both problems — it owns a single, persistent IB connection and processes jobs one at a time, sleeping 20 seconds between data chunks to stay within IB's limits. Any number of callers can submit jobs without worrying about pacing or connection conflicts.
|
|
76
|
+
|
|
77
|
+
Start it once and keep it running (e.g. in `screen` or `tmux`, or as a system service):
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python ib_connect/skills/ib_download/download_service.py
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Long date ranges are split automatically into chunks sized to IB's per-request limits. Progress and errors are logged to `download_service.log` alongside the script. A PID lock file prevents duplicate instances.
|
|
84
|
+
|
|
85
|
+
**`ib-download` — the CLI client**
|
|
86
|
+
|
|
87
|
+
Submits a job to the service's SQLite queue and returns a `job_key` immediately. The caller can check status later without staying connected.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Submit a job
|
|
91
|
+
ib-download -c 265598 -s 2023-01-01 -e 2023-12-31 -b "1 min"
|
|
92
|
+
|
|
93
|
+
# Check status
|
|
94
|
+
ib-download --status -k <job_key>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
On completion the service can notify an agent via webhook (configure `webhook_url` and `token` in `config.json`).
|
|
98
|
+
|
|
99
|
+
See [ib_connect/skills/ib_download/SKILL.md](ib_connect/skills/ib_download/SKILL.md) for all options including batch mode and output format.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### data-upload
|
|
104
|
+
|
|
105
|
+
A file-watching service that validates and uploads contract JSON files and OHLCV CSV files to PostgreSQL/TimescaleDB. Drop files into the configured `input_folder` and they are processed automatically — contracts go to `finance.contracts`, OHLCV data to `finance.ohlcv_1m` or `finance.ohlcv_1d`. Processed files are moved to `processed/`; failures go to `errors/`.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
python ib_connect/skills/data_upload/data_upload.py
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Expected filename pattern for OHLCV: `<symbol>.<conid>.<barsize>.csv`.
|
|
112
|
+
|
|
113
|
+
See [ib_connect/skills/data_upload/SKILL.md](ib_connect/skills/data_upload/SKILL.md) for schema details.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### GUI
|
|
118
|
+
|
|
119
|
+
A Flask web interface that ties the toolkit together. From the browser you can:
|
|
120
|
+
|
|
121
|
+
- **Query contracts** — search by symbol and security type, inspect the qualified contract details returned by IB
|
|
122
|
+
- **Save contracts** — export selected contracts as JSON to the `data_upload` input folder, so they are picked up and stored in PostgreSQL automatically
|
|
123
|
+
- **Submit download jobs** — configure date range and bar size per contract and enqueue jobs with the download service
|
|
124
|
+
- **Monitor jobs** — check status and cancel pending jobs
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
python ib_connect/gui/app.py
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Opens on `http://127.0.0.1:5000` by default (configurable in `gui/config.json`).
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# IB Connect Package
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# GUI Package
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
IB Data Downloader GUI - Flask Web App
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from flask import Flask, render_template, request, jsonify
|
|
7
|
+
import subprocess
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
import asyncio
|
|
12
|
+
import logging
|
|
13
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from ib_connect.skills.ib_query.query import query_ib
|
|
18
|
+
from ib_connect.skills.ib_download.job_queue import JobQueue
|
|
19
|
+
|
|
20
|
+
app = Flask(__name__)
|
|
21
|
+
logging.getLogger('werkzeug').setLevel(logging.ERROR)
|
|
22
|
+
|
|
23
|
+
# Config
|
|
24
|
+
CONFIG_FILE = 'config.json'
|
|
25
|
+
DEFAULT_CONFIG = {
|
|
26
|
+
'contracts_folder': './data/contracts',
|
|
27
|
+
'ib_host': '127.0.0.1',
|
|
28
|
+
'ib_port': 7497,
|
|
29
|
+
'ib_client_id': 77
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def load_config():
|
|
33
|
+
if os.path.exists(CONFIG_FILE):
|
|
34
|
+
with open(CONFIG_FILE, 'r') as f:
|
|
35
|
+
return json.load(f)
|
|
36
|
+
return DEFAULT_CONFIG
|
|
37
|
+
|
|
38
|
+
def save_config(config):
|
|
39
|
+
with open(CONFIG_FILE, 'w') as f:
|
|
40
|
+
json.dump(config, f, indent=2)
|
|
41
|
+
|
|
42
|
+
config = load_config()
|
|
43
|
+
|
|
44
|
+
@app.route('/')
|
|
45
|
+
def index():
|
|
46
|
+
return render_template('index.html')
|
|
47
|
+
|
|
48
|
+
@app.route('/query', methods=['POST'])
|
|
49
|
+
def query_contracts():
|
|
50
|
+
data = request.json
|
|
51
|
+
# Include configured IB params to avoid conflicts
|
|
52
|
+
data['host'] = config.get('ib_host', '127.0.0.1')
|
|
53
|
+
data['port'] = config.get('ib_port', 7497)
|
|
54
|
+
data['client_id'] = config.get('ib_client_id', 1)
|
|
55
|
+
def run_query():
|
|
56
|
+
return asyncio.run(query_ib(data))
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
with ThreadPoolExecutor() as executor:
|
|
60
|
+
future = executor.submit(run_query)
|
|
61
|
+
result = future.result()
|
|
62
|
+
if isinstance(result, dict) and 'error' in result:
|
|
63
|
+
return jsonify({'success': False, 'error': result['error']})
|
|
64
|
+
contracts = result if isinstance(result, list) else [result] if result else []
|
|
65
|
+
contracts = [c for c in contracts if c is not None] # Filter out nulls
|
|
66
|
+
return jsonify({'success': True, 'contracts': contracts})
|
|
67
|
+
except Exception as e:
|
|
68
|
+
return jsonify({'success': False, 'error': str(e)})
|
|
69
|
+
|
|
70
|
+
@app.route('/save_contracts', methods=['POST'])
|
|
71
|
+
def save_contracts():
|
|
72
|
+
data = request.json
|
|
73
|
+
selected = data.get('selected', [])
|
|
74
|
+
# Add defaults for missing required fields
|
|
75
|
+
for contract in selected:
|
|
76
|
+
if 'time_zone_id' not in contract or not contract['time_zone_id']:
|
|
77
|
+
contract['time_zone_id'] = 'US/Eastern'
|
|
78
|
+
if 'min_tick' not in contract or not contract['min_tick']:
|
|
79
|
+
contract['min_tick'] = 0.01
|
|
80
|
+
if 'tick_value' not in contract or not contract['tick_value']:
|
|
81
|
+
contract['tick_value'] = contract.get('min_tick', 0.01) * contract.get('multiplier', 1)
|
|
82
|
+
if 'multiplier' not in contract or not contract['multiplier']:
|
|
83
|
+
contract['multiplier'] = 1
|
|
84
|
+
# Omit empty strings and zero values, keep None for db defaults
|
|
85
|
+
cleaned_selected = []
|
|
86
|
+
for contract in selected:
|
|
87
|
+
cleaned = {k: v for k, v in contract.items() if v not in ('', 0)}
|
|
88
|
+
cleaned_selected.append(cleaned)
|
|
89
|
+
folder = config['contracts_folder']
|
|
90
|
+
os.makedirs(folder, exist_ok=True)
|
|
91
|
+
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
92
|
+
file_path = os.path.join(folder, f'selected_contracts_{timestamp}.json')
|
|
93
|
+
with open(file_path, 'w') as f:
|
|
94
|
+
json.dump(cleaned_selected, f, indent=2)
|
|
95
|
+
return jsonify({'success': True, 'file': file_path})
|
|
96
|
+
|
|
97
|
+
@app.route('/download', methods=['POST'])
|
|
98
|
+
def download_data():
|
|
99
|
+
data = request.json
|
|
100
|
+
contracts = data.get('contracts', [])
|
|
101
|
+
downloads_folder = config.get('downloads_folder', './data/downloads')
|
|
102
|
+
job_queue_db = config.get('job_queue_db', './jobs.db')
|
|
103
|
+
queue = JobQueue(job_queue_db)
|
|
104
|
+
jobs = []
|
|
105
|
+
for contract in contracts:
|
|
106
|
+
params = {
|
|
107
|
+
'conid': contract['conid'],
|
|
108
|
+
'start': contract['start_date'],
|
|
109
|
+
'end': contract['end_date'],
|
|
110
|
+
'bar_size': contract['bar_size'],
|
|
111
|
+
'show': contract.get('show', 'TRADES'),
|
|
112
|
+
'msg': f"Download {contract['symbol']} data ({contract.get('show', 'TRADES')})"
|
|
113
|
+
}
|
|
114
|
+
try:
|
|
115
|
+
job_key = queue.submit_job(params)
|
|
116
|
+
jobs.append({'contract': contract['symbol'], 'job_key': job_key, 'status': 'submitted'})
|
|
117
|
+
except Exception as e:
|
|
118
|
+
jobs.append({'contract': contract['symbol'], 'error': str(e)})
|
|
119
|
+
return jsonify({'jobs': jobs})
|
|
120
|
+
|
|
121
|
+
@app.route('/job_status/<job_key>')
|
|
122
|
+
def job_status(job_key):
|
|
123
|
+
try:
|
|
124
|
+
job_queue_db = config['job_queue_db']
|
|
125
|
+
queue = JobQueue(job_queue_db)
|
|
126
|
+
status_data = queue.get_status(job_key)
|
|
127
|
+
if status_data['status'] == 'not_found':
|
|
128
|
+
return jsonify({'status': 'error', 'details': 'Job not found'})
|
|
129
|
+
details = f"Status: {status_data.get('status', 'unknown')}"
|
|
130
|
+
if status_data.get('message'):
|
|
131
|
+
details += f" - {status_data['message']}"
|
|
132
|
+
if status_data.get('error'):
|
|
133
|
+
details += f" - Error: {status_data['error']}"
|
|
134
|
+
return jsonify({'status': 'ok', 'details': details})
|
|
135
|
+
except Exception as e:
|
|
136
|
+
return jsonify({'status': 'error', 'details': str(e)})
|
|
137
|
+
|
|
138
|
+
@app.route('/cancel_job/<job_key>', methods=['POST'])
|
|
139
|
+
def cancel_job(job_key):
|
|
140
|
+
try:
|
|
141
|
+
job_queue_db = config['job_queue_db']
|
|
142
|
+
queue = JobQueue(job_queue_db)
|
|
143
|
+
status_data = queue.get_status(job_key)
|
|
144
|
+
if status_data['status'] == 'pending':
|
|
145
|
+
queue.remove_job(job_key)
|
|
146
|
+
return jsonify({'success': True})
|
|
147
|
+
else:
|
|
148
|
+
return jsonify({'success': False, 'error': 'Job not pending'})
|
|
149
|
+
except Exception as e:
|
|
150
|
+
return jsonify({'success': False, 'error': str(e)})
|
|
151
|
+
|
|
152
|
+
if __name__ == '__main__':
|
|
153
|
+
app.run(debug=True, host=config.get('host', '127.0.0.1'), port=config.get('port', 5000))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Shared utilities for IB Connect
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from ib_insync import IB
|
|
2
|
+
|
|
3
|
+
class IBConnection:
|
|
4
|
+
"""
|
|
5
|
+
Connection management for Interactive Brokers using ib_insync.
|
|
6
|
+
Provides a connected IB instance that can be used as a context manager.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def connect(host='127.0.0.1', port=7497, clientId=1, timeout=4.0, readonly=False, account=''):
|
|
11
|
+
"""
|
|
12
|
+
Establish a connection to IB and return the connected IB instance (which is a context manager).
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
host (str): IB Gateway/TWS host. Default: '127.0.0.1'
|
|
16
|
+
port (int): IB Gateway/TWS port. Default: 7497
|
|
17
|
+
clientId (int): Client ID for the connection. Default: 1
|
|
18
|
+
timeout (float): Connection timeout in seconds. Default: 4.0
|
|
19
|
+
readonly (bool): Read-only connection. Default: False
|
|
20
|
+
account (str): Account to use (optional). Default: ''
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
IB: Connected IB instance (supports context manager: with ib: ...)
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
ConnectionError: If connection fails.
|
|
27
|
+
"""
|
|
28
|
+
ib = IB()
|
|
29
|
+
try:
|
|
30
|
+
ib.connect(host=host, port=port, clientId=clientId, timeout=timeout, readonly=readonly, account=account)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
raise ConnectionError(f"Failed to connect to Interactive Brokers: {e}") from e
|
|
33
|
+
|
|
34
|
+
return ib
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Skills for IB Connect
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Data Upload skill
|