dataquery-sdk 0.0.7__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.
- dataquery/__init__.py +215 -0
- dataquery/auth.py +380 -0
- dataquery/auto_download.py +448 -0
- dataquery/cli.py +244 -0
- dataquery/client.py +2825 -0
- dataquery/config.py +571 -0
- dataquery/connection_pool.py +538 -0
- dataquery/dataquery.py +2467 -0
- dataquery/exceptions.py +169 -0
- dataquery/logging_config.py +415 -0
- dataquery/models.py +1303 -0
- dataquery/py.typed +2 -0
- dataquery/rate_limiter.py +520 -0
- dataquery/retry.py +406 -0
- dataquery/utils.py +525 -0
- dataquery_sdk-0.0.7.dist-info/METADATA +996 -0
- dataquery_sdk-0.0.7.dist-info/RECORD +21 -0
- dataquery_sdk-0.0.7.dist-info/WHEEL +5 -0
- dataquery_sdk-0.0.7.dist-info/entry_points.txt +2 -0
- dataquery_sdk-0.0.7.dist-info/licenses/LICENSE +201 -0
- dataquery_sdk-0.0.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,996 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dataquery-sdk
|
|
3
|
+
Version: 0.0.7
|
|
4
|
+
Summary: Python SDK for DATAQUERY Data API - Query, download, and check availability of economic data files
|
|
5
|
+
Author-email: DATAQUERY SDK Team <support@dataquery.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/dataquery/dataquery-sdk
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/dataquery/dataquery-sdk/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/dataquery/dataquery-sdk/wiki
|
|
9
|
+
Project-URL: Source Code, https://github.com/dataquery/dataquery-sdk
|
|
10
|
+
Keywords: dataquery,data,api,economic,financial,download,async,oauth,rate-limiting,sdk
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: aiohttp<4.0.0,>=3.8.0
|
|
30
|
+
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
31
|
+
Requires-Dist: structlog>=23.0.0
|
|
32
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
38
|
+
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
39
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
40
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
42
|
+
Requires-Dist: pip-audit>=2.7.0; extra == "dev"
|
|
43
|
+
Requires-Dist: build>=1.3.0; extra == "dev"
|
|
44
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
45
|
+
Provides-Extra: docs
|
|
46
|
+
Requires-Dist: mkdocs>=1.5.0; extra == "docs"
|
|
47
|
+
Requires-Dist: mkdocs-material>=9.0.0; extra == "docs"
|
|
48
|
+
Requires-Dist: mkdocstrings[python]>=0.24.0; extra == "docs"
|
|
49
|
+
Requires-Dist: mike>=1.1.0; extra == "docs"
|
|
50
|
+
Provides-Extra: pandas
|
|
51
|
+
Requires-Dist: pandas>=2.0.0; extra == "pandas"
|
|
52
|
+
Provides-Extra: all
|
|
53
|
+
Requires-Dist: pytest>=7.0.0; extra == "all"
|
|
54
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
|
|
55
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "all"
|
|
56
|
+
Requires-Dist: black>=23.0.0; extra == "all"
|
|
57
|
+
Requires-Dist: flake8>=6.0.0; extra == "all"
|
|
58
|
+
Requires-Dist: mypy>=1.5.0; extra == "all"
|
|
59
|
+
Requires-Dist: isort>=5.12.0; extra == "all"
|
|
60
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "all"
|
|
61
|
+
Requires-Dist: mkdocs>=1.5.0; extra == "all"
|
|
62
|
+
Requires-Dist: mkdocs-material>=9.0.0; extra == "all"
|
|
63
|
+
Requires-Dist: mkdocstrings[python]>=0.24.0; extra == "all"
|
|
64
|
+
Requires-Dist: mike>=1.1.0; extra == "all"
|
|
65
|
+
Requires-Dist: pandas>=2.0.0; extra == "all"
|
|
66
|
+
Requires-Dist: pip-audit>=2.7.0; extra == "all"
|
|
67
|
+
Requires-Dist: build>=1.3.0; extra == "all"
|
|
68
|
+
Dynamic: license-file
|
|
69
|
+
|
|
70
|
+
# DataQuery SDK
|
|
71
|
+
|
|
72
|
+
Python SDK for J.P. Morgan DataQuery API - high-performance file downloads and time series data access.
|
|
73
|
+
|
|
74
|
+
**Key capabilities:**
|
|
75
|
+
- Parallel file downloads with progress tracking
|
|
76
|
+
- Time series queries (expressions, instruments, groups)
|
|
77
|
+
- OAuth 2.0 authentication with auto-refresh
|
|
78
|
+
- Connection pooling and rate limiting
|
|
79
|
+
- Pandas DataFrame integration
|
|
80
|
+
|
|
81
|
+
## Table of Contents
|
|
82
|
+
|
|
83
|
+
- [Quick Start](#quick-start)
|
|
84
|
+
- [Installation](#installation)
|
|
85
|
+
- [Set Credentials](#set-credentials)
|
|
86
|
+
- [Execution Modes](#execution-modes-async-and-sync)
|
|
87
|
+
- [Download Files](#download-jpmaqs-files-simplest)
|
|
88
|
+
- [Query Time Series](#query-time-series-and-convert-to-dataframe)
|
|
89
|
+
- [Discover Datasets](#discover-available-datasets)
|
|
90
|
+
- [Common Use Cases](#common-use-cases)
|
|
91
|
+
- [File Downloads](#file-downloads)
|
|
92
|
+
- [Time Series Queries](#time-series-queries)
|
|
93
|
+
- [Advanced Usage](#advanced-usage)
|
|
94
|
+
- [API Reference](#api-reference)
|
|
95
|
+
- [Configuration](#configuration)
|
|
96
|
+
- [Error Handling](#error-handling)
|
|
97
|
+
- [Examples & Recipes](#examples--recipes)
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Quick Start
|
|
102
|
+
|
|
103
|
+
### Installation
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pip install dataquery-sdk
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Set Credentials
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
export DATAQUERY_CLIENT_ID="your_client_id"
|
|
113
|
+
export DATAQUERY_CLIENT_SECRET="your_client_secret"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Execution Modes: Async and Sync
|
|
117
|
+
|
|
118
|
+
**Python scripts** can use either async or sync methods:
|
|
119
|
+
- **Sync methods** (simpler): Use `with DataQuery()` and call methods without `await`
|
|
120
|
+
- **Async methods** (requires `asyncio.run()`): Use `async with DataQuery()` and `await` method calls
|
|
121
|
+
|
|
122
|
+
**Jupyter notebooks** must use async methods with `await` (event loop already running)
|
|
123
|
+
|
|
124
|
+
> Performance is identical - sync methods are lightweight wrappers around async implementations.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### Download JPMAQS Files (Simplest)
|
|
129
|
+
|
|
130
|
+
**Jupyter Notebooks:**
|
|
131
|
+
```python
|
|
132
|
+
from dataquery import DataQuery
|
|
133
|
+
|
|
134
|
+
# Download all files for January 2025
|
|
135
|
+
async with DataQuery() as dq:
|
|
136
|
+
await dq.run_group_download_async(
|
|
137
|
+
group_id="JPMAQS_GENERIC_RETURNS",
|
|
138
|
+
start_date="20250101",
|
|
139
|
+
end_date="20250131",
|
|
140
|
+
destination_dir="./data"
|
|
141
|
+
)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Python Scripts (Sync):**
|
|
145
|
+
```python
|
|
146
|
+
from dataquery import DataQuery
|
|
147
|
+
|
|
148
|
+
# Download all files for January 2025
|
|
149
|
+
with DataQuery() as dq:
|
|
150
|
+
dq.run_group_download(
|
|
151
|
+
group_id="JPMAQS_GENERIC_RETURNS",
|
|
152
|
+
start_date="20250101",
|
|
153
|
+
end_date="20250131",
|
|
154
|
+
destination_dir="./data"
|
|
155
|
+
)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Python Scripts (Async):**
|
|
159
|
+
```python
|
|
160
|
+
import asyncio
|
|
161
|
+
from dataquery import DataQuery
|
|
162
|
+
|
|
163
|
+
async def main():
|
|
164
|
+
async with DataQuery() as dq:
|
|
165
|
+
await dq.run_group_download_async(
|
|
166
|
+
group_id="JPMAQS_GENERIC_RETURNS",
|
|
167
|
+
start_date="20250101",
|
|
168
|
+
end_date="20250131",
|
|
169
|
+
destination_dir="./data"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
asyncio.run(main())
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
All JPMAQS Generic Returns files for January are now in `./data/`.
|
|
176
|
+
|
|
177
|
+
### Query Time Series and Convert to DataFrame
|
|
178
|
+
|
|
179
|
+
**Jupyter Notebooks:**
|
|
180
|
+
```python
|
|
181
|
+
from dataquery import DataQuery
|
|
182
|
+
|
|
183
|
+
# Get time series data and convert to pandas DataFrame
|
|
184
|
+
async with DataQuery() as dq:
|
|
185
|
+
result = await dq.get_expressions_time_series_async(
|
|
186
|
+
expressions=["DB(MTE,IRISH EUR 1.100 15-May-2029 LON,,IE00BH3SQ895,MIDPRC)"],
|
|
187
|
+
start_date="20240101",
|
|
188
|
+
end_date="20240131"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Convert to DataFrame for analysis
|
|
192
|
+
df = dq.time_series_to_dataframe(result)
|
|
193
|
+
print(df.head())
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Python Scripts:**
|
|
197
|
+
```python
|
|
198
|
+
import asyncio
|
|
199
|
+
from dataquery import DataQuery
|
|
200
|
+
|
|
201
|
+
async def main():
|
|
202
|
+
async with DataQuery() as dq:
|
|
203
|
+
result = await dq.get_expressions_time_series_async(
|
|
204
|
+
expressions=["DB(MTE,IRISH EUR 1.100 15-May-2029 LON,,IE00BH3SQ895,MIDPRC)"],
|
|
205
|
+
start_date="20240101",
|
|
206
|
+
end_date="20240131"
|
|
207
|
+
)
|
|
208
|
+
df = dq.time_series_to_dataframe(result)
|
|
209
|
+
return df
|
|
210
|
+
|
|
211
|
+
df = asyncio.run(main())
|
|
212
|
+
print(df.head())
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Discover Available Datasets
|
|
216
|
+
|
|
217
|
+
**Jupyter Notebooks:**
|
|
218
|
+
```python
|
|
219
|
+
from dataquery import DataQuery
|
|
220
|
+
|
|
221
|
+
# List all available groups and convert to DataFrame
|
|
222
|
+
async with DataQuery() as dq:
|
|
223
|
+
groups = await dq.list_groups_async(limit=100)
|
|
224
|
+
|
|
225
|
+
# Convert to DataFrame - generic method works with any response
|
|
226
|
+
df = dq.to_dataframe(groups)
|
|
227
|
+
print(df[['group_id', 'group_name', 'description']].head())
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Python Scripts:**
|
|
231
|
+
```python
|
|
232
|
+
from dataquery import DataQuery
|
|
233
|
+
|
|
234
|
+
# Sync version
|
|
235
|
+
with DataQuery() as dq:
|
|
236
|
+
groups = dq.list_groups(limit=100)
|
|
237
|
+
|
|
238
|
+
# Convert to DataFrame - generic method works with any response
|
|
239
|
+
df = dq.to_dataframe(groups)
|
|
240
|
+
print(df[['group_id', 'group_name', 'description']].head())
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Common Use Cases
|
|
246
|
+
|
|
247
|
+
### File Downloads
|
|
248
|
+
|
|
249
|
+
#### Download All Files for Date Range (Recommended)
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
# Jupyter notebooks - use async/await
|
|
253
|
+
async with DataQuery() as dq:
|
|
254
|
+
results = await dq.run_group_download_async(
|
|
255
|
+
group_id="JPMAQS_GENERIC_RETURNS",
|
|
256
|
+
start_date="20250101",
|
|
257
|
+
end_date="20250131",
|
|
258
|
+
destination_dir="./data",
|
|
259
|
+
max_concurrent=3 # Download 3 files at once
|
|
260
|
+
)
|
|
261
|
+
print(f"Downloaded {results['successful']} files")
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
# Python scripts - use sync methods
|
|
266
|
+
with DataQuery() as dq:
|
|
267
|
+
results = dq.run_group_download(
|
|
268
|
+
group_id="JPMAQS_GENERIC_RETURNS",
|
|
269
|
+
start_date="20250101",
|
|
270
|
+
end_date="20250131",
|
|
271
|
+
destination_dir="./data"
|
|
272
|
+
)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
# Python scripts - async requires asyncio.run()
|
|
277
|
+
import asyncio
|
|
278
|
+
from dataquery import DataQuery
|
|
279
|
+
|
|
280
|
+
async def download():
|
|
281
|
+
async with DataQuery() as dq:
|
|
282
|
+
return await dq.run_group_download_async(
|
|
283
|
+
group_id="JPMAQS_GENERIC_RETURNS",
|
|
284
|
+
start_date="20250101",
|
|
285
|
+
end_date="20250131",
|
|
286
|
+
destination_dir="./data",
|
|
287
|
+
max_concurrent=3
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
results = asyncio.run(download())
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**When to use:** You want all files for a file group within a date range.
|
|
294
|
+
|
|
295
|
+
#### Download Single File
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
async with DataQuery() as dq:
|
|
299
|
+
result = await dq.download_file_async(
|
|
300
|
+
file_group_id="JPMAQS_GENERIC_RETURNS",
|
|
301
|
+
file_datetime="20250115", # Specific date
|
|
302
|
+
destination_path="./downloads"
|
|
303
|
+
)
|
|
304
|
+
print(f"Downloaded: {result.local_path}")
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**When to use:** You need just one specific file.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
### Time Series Queries
|
|
312
|
+
|
|
313
|
+
#### Query by Expression (Fastest for known data)
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
async with DataQuery() as dq:
|
|
317
|
+
result = await dq.get_expressions_time_series_async(
|
|
318
|
+
expressions=[
|
|
319
|
+
"DB(MTE,IRISH EUR 1.100 15-May-2029 LON,,IE00BH3SQ895,MIDPRC)",
|
|
320
|
+
"DB(MTE,IRISH EUR 2.400 15-May-2030 LON,,IE00BJ38CR43,MIDPRC)"
|
|
321
|
+
],
|
|
322
|
+
start_date="20240101",
|
|
323
|
+
end_date="20240131"
|
|
324
|
+
)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**When to use:** You know the exact expression syntax (from DataQuery Web or documentation).
|
|
328
|
+
|
|
329
|
+
#### Query by Instrument ID
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
async with DataQuery() as dq:
|
|
333
|
+
result = await dq.get_instrument_time_series_async(
|
|
334
|
+
instruments=[
|
|
335
|
+
"477f892d3cc8745578887a92d35c2a3e-DQGNMTBNDFIM",
|
|
336
|
+
"67e1bfca56bdee7a0fd5fe4c62a1e0dc-DQGNMTBNDFIM"
|
|
337
|
+
],
|
|
338
|
+
attributes=["MIDPRC", "REPO_1M"],
|
|
339
|
+
start_date="20240101",
|
|
340
|
+
end_date="20240131"
|
|
341
|
+
)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**When to use:** You have instrument IDs from a previous search or list.
|
|
345
|
+
|
|
346
|
+
#### Query Entire Group with Filter
|
|
347
|
+
|
|
348
|
+
```python
|
|
349
|
+
async with DataQuery() as dq:
|
|
350
|
+
result = await dq.get_group_time_series_async(
|
|
351
|
+
group_id="FI_GO_BO_EA",
|
|
352
|
+
attributes=["MIDPRC", "REPO_1M"],
|
|
353
|
+
filter="country(IRL)", # Ireland bonds only
|
|
354
|
+
start_date="20240101",
|
|
355
|
+
end_date="20240131"
|
|
356
|
+
)
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**When to use:** You want all instruments in a dataset, optionally filtered by country/currency.
|
|
360
|
+
|
|
361
|
+
#### Search for Instruments
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
async with DataQuery() as dq:
|
|
365
|
+
# Find instruments matching keywords
|
|
366
|
+
results = await dq.search_instruments_async(
|
|
367
|
+
group_id="FI_GO_BO_EA",
|
|
368
|
+
keywords="irish"
|
|
369
|
+
)
|
|
370
|
+
print(f"Found {results.items} instruments")
|
|
371
|
+
|
|
372
|
+
# Use the results to query time series
|
|
373
|
+
instrument_ids = [inst.instrument_id for inst in results.instruments]
|
|
374
|
+
data = await dq.get_instrument_time_series_async(
|
|
375
|
+
instruments=instrument_ids[:5], # First 5 results
|
|
376
|
+
attributes=["MIDPRC"],
|
|
377
|
+
start_date="20240101",
|
|
378
|
+
end_date="20240131"
|
|
379
|
+
)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**When to use:** You're discovering data and don't know instrument IDs yet.
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Advanced Usage
|
|
387
|
+
|
|
388
|
+
### High-Performance File Downloads
|
|
389
|
+
|
|
390
|
+
Use parallel HTTP range requests to download large files faster:
|
|
391
|
+
|
|
392
|
+
```python
|
|
393
|
+
async with DataQuery() as dq:
|
|
394
|
+
result = await dq.download_file_async(
|
|
395
|
+
file_group_id="JPMAQS_GENERIC_RETURNS",
|
|
396
|
+
file_datetime="20250115",
|
|
397
|
+
destination_path="./downloads",
|
|
398
|
+
num_parts=8, # Split into 8 parallel chunks
|
|
399
|
+
progress_callback=lambda fid, p: print(f"{p.bytes_downloaded:,} bytes")
|
|
400
|
+
)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Performance tuning:**
|
|
404
|
+
```python
|
|
405
|
+
# Download multiple files concurrently
|
|
406
|
+
await dq.run_group_download_async(
|
|
407
|
+
group_id="JPMAQS_GENERIC_RETURNS",
|
|
408
|
+
start_date="20250101",
|
|
409
|
+
end_date="20250131",
|
|
410
|
+
destination_dir="./data",
|
|
411
|
+
max_concurrent=5, # 5 files at once
|
|
412
|
+
num_parts=4 # Each file in 4 chunks = 20 total parallel requests
|
|
413
|
+
)
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
**Optimal settings:**
|
|
417
|
+
- `num_parts`: 2-8 (higher = faster on good connections)
|
|
418
|
+
- `max_concurrent`: 3-5 (too high may hit rate limits)
|
|
419
|
+
|
|
420
|
+
**Note:** Jupyter notebooks use `await` directly. Python scripts use sync methods or wrap async code in `asyncio.run()`.
|
|
421
|
+
|
|
422
|
+
### Grid Data (Premium Datasets)
|
|
423
|
+
|
|
424
|
+
```python
|
|
425
|
+
async with DataQuery() as dq:
|
|
426
|
+
# Query grid data by expression
|
|
427
|
+
grid = await dq.get_grid_data_async(
|
|
428
|
+
expr="DBGRID(FXOVOL,FXO,CW,AUD,USD)",
|
|
429
|
+
date="20240216"
|
|
430
|
+
)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### DataFrame Conversion
|
|
434
|
+
|
|
435
|
+
The SDK provides a universal `to_dataframe()` method that automatically handles any API response type:
|
|
436
|
+
|
|
437
|
+
```python
|
|
438
|
+
async with DataQuery() as dq:
|
|
439
|
+
# Generic to_dataframe() works with all response types
|
|
440
|
+
groups = await dq.list_groups_async(limit=100)
|
|
441
|
+
groups_df = dq.to_dataframe(groups)
|
|
442
|
+
|
|
443
|
+
# Time series
|
|
444
|
+
ts_data = await dq.get_expressions_time_series_async(
|
|
445
|
+
expressions=["DB(MTE,IRISH EUR 1.100 15-May-2029 LON,,IE00BH3SQ895,MIDPRC)"],
|
|
446
|
+
start_date="20240101",
|
|
447
|
+
end_date="20240131"
|
|
448
|
+
)
|
|
449
|
+
ts_df = dq.to_dataframe(ts_data)
|
|
450
|
+
|
|
451
|
+
# Instruments
|
|
452
|
+
instruments = await dq.list_instruments_async(group_id="FI_GO_BO_EA")
|
|
453
|
+
instruments_df = dq.to_dataframe(instruments.instruments)
|
|
454
|
+
|
|
455
|
+
# Files
|
|
456
|
+
files = await dq.list_available_files_async(
|
|
457
|
+
group_id="JPMAQS_GENERIC_RETURNS",
|
|
458
|
+
start_date="20250101",
|
|
459
|
+
end_date="20250131"
|
|
460
|
+
)
|
|
461
|
+
files_df = dq.to_dataframe(files)
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
**Advanced options:**
|
|
465
|
+
```python
|
|
466
|
+
# Include metadata fields
|
|
467
|
+
df = dq.to_dataframe(groups, include_metadata=True)
|
|
468
|
+
|
|
469
|
+
# Parse specific columns as dates
|
|
470
|
+
df = dq.to_dataframe(files, date_columns=['last_modified', 'created_date'])
|
|
471
|
+
|
|
472
|
+
# Convert specific columns to numeric
|
|
473
|
+
df = dq.to_dataframe(files, numeric_columns=['file_size'])
|
|
474
|
+
|
|
475
|
+
# Apply custom transformations
|
|
476
|
+
df = dq.to_dataframe(
|
|
477
|
+
data,
|
|
478
|
+
custom_transformations={
|
|
479
|
+
'price': lambda x: float(x) if x else 0.0
|
|
480
|
+
}
|
|
481
|
+
)
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
**Type-specific convenience methods** (same functionality as generic method):
|
|
485
|
+
- `groups_to_dataframe(groups, include_metadata=False)`
|
|
486
|
+
- `time_series_to_dataframe(time_series, include_metadata=False)`
|
|
487
|
+
- `instruments_to_dataframe(instruments, include_metadata=False)`
|
|
488
|
+
- `files_to_dataframe(files, include_metadata=False)`
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## API Reference
|
|
493
|
+
|
|
494
|
+
### File Download Methods
|
|
495
|
+
|
|
496
|
+
#### `download_file_async()`
|
|
497
|
+
|
|
498
|
+
Download a single file with optional parallel chunks.
|
|
499
|
+
|
|
500
|
+
```python
|
|
501
|
+
result = await dq.download_file_async(
|
|
502
|
+
file_group_id: str, # Required: File group identifier
|
|
503
|
+
file_datetime: str, # Required: File date (YYYY-MM-DD)
|
|
504
|
+
destination_path: Path, # Required: Where to save
|
|
505
|
+
num_parts: int = 1, # Optional: Parallel chunks (1-10)
|
|
506
|
+
part_size_mb: int = 100, # Optional: Chunk size in MB
|
|
507
|
+
progress_callback: Callable = None, # Optional: Progress updates
|
|
508
|
+
overwrite: bool = False # Optional: Overwrite existing
|
|
509
|
+
) -> DownloadResult
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
**Returns:** `DownloadResult` with status, file size, download time, and local path.
|
|
513
|
+
|
|
514
|
+
#### `download_files_by_date_range_async()`
|
|
515
|
+
|
|
516
|
+
Download all files in a date range.
|
|
517
|
+
|
|
518
|
+
```python
|
|
519
|
+
results = await dq.download_files_by_date_range_async(
|
|
520
|
+
file_group_id: str, # Required: File group identifier
|
|
521
|
+
start_date: str, # Required: Start date (YYYY-MM-DD)
|
|
522
|
+
end_date: str, # Required: End date (YYYY-MM-DD)
|
|
523
|
+
destination_path: Path, # Required: Where to save
|
|
524
|
+
num_parts: int = 1, # Optional: Parallel chunks per file
|
|
525
|
+
max_concurrent: int = 3, # Optional: Concurrent downloads
|
|
526
|
+
progress_callback: Callable = None # Optional: Progress updates
|
|
527
|
+
) -> List[DownloadResult]
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
**Returns:** List of `DownloadResult` for each file.
|
|
531
|
+
|
|
532
|
+
#### `list_available_files_async()`
|
|
533
|
+
|
|
534
|
+
Check what files are available for a group and date range.
|
|
535
|
+
|
|
536
|
+
```python
|
|
537
|
+
files = await dq.list_available_files_async(
|
|
538
|
+
group_id: str, # Required: Group identifier
|
|
539
|
+
start_date: str, # Required: Start date (YYYY-MM-DD)
|
|
540
|
+
end_date: str # Required: End date (YYYY-MM-DD)
|
|
541
|
+
) -> List[dict]
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**Returns:** List of dictionaries with `file_group_id`, `file_datetime`, and `file_size`.
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
### Time Series Query Methods
|
|
549
|
+
|
|
550
|
+
#### `get_expressions_time_series_async()`
|
|
551
|
+
|
|
552
|
+
Query using DataQuery expressions (fastest when you know the expression).
|
|
553
|
+
|
|
554
|
+
**Minimal usage:**
|
|
555
|
+
```python
|
|
556
|
+
result = await dq.get_expressions_time_series_async(
|
|
557
|
+
expressions=["DB(MTE,IRISH EUR 1.100 15-May-2029 LON,,IE00BH3SQ895,MIDPRC)"],
|
|
558
|
+
start_date="20240101",
|
|
559
|
+
end_date="20240131"
|
|
560
|
+
)
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
**Complete signature:**
|
|
564
|
+
```python
|
|
565
|
+
result = await dq.get_expressions_time_series_async(
|
|
566
|
+
expressions: List[str], # Required: List of expressions (max 20)
|
|
567
|
+
format: str = "JSON", # Optional: Response format
|
|
568
|
+
start_date: str = None, # Optional: YYYYMMDD or TODAY-1M
|
|
569
|
+
end_date: str = None, # Optional: YYYYMMDD or TODAY
|
|
570
|
+
calendar: str = "CAL_USBANK", # Optional: Calendar convention
|
|
571
|
+
frequency: str = "FREQ_DAY", # Optional: FREQ_DAY, FREQ_WEEK, etc.
|
|
572
|
+
conversion: str = "CONV_LASTBUS_ABS", # Optional: Conversion method
|
|
573
|
+
nan_treatment: str = "NA_NOTHING", # Optional: NA_NOTHING, NA_LAST, etc.
|
|
574
|
+
data: str = "REFERENCE_DATA", # Optional: Data domain
|
|
575
|
+
page: str = None # Optional: Pagination token
|
|
576
|
+
) -> TimeSeriesResponse
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
**Recommended parameters:**
|
|
580
|
+
- `calendar="CAL_WEEKDAYS"` - For international coverage
|
|
581
|
+
- `data="ALL"` - To include market data
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
#### `get_instrument_time_series_async()`
|
|
586
|
+
|
|
587
|
+
Query specific instruments by their IDs.
|
|
588
|
+
|
|
589
|
+
**Minimal usage:**
|
|
590
|
+
```python
|
|
591
|
+
result = await dq.get_instrument_time_series_async(
|
|
592
|
+
instruments=["477f892d3cc8745578887a92d35c2a3e-DQGNMTBNDFIM"],
|
|
593
|
+
attributes=["MIDPRC"],
|
|
594
|
+
start_date="20240101",
|
|
595
|
+
end_date="20240131"
|
|
596
|
+
)
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
**Complete signature:**
|
|
600
|
+
```python
|
|
601
|
+
result = await dq.get_instrument_time_series_async(
|
|
602
|
+
instruments: List[str], # Required: List of instrument IDs (max 20)
|
|
603
|
+
attributes: List[str], # Required: List of attributes
|
|
604
|
+
data: str = "REFERENCE_DATA", # Optional: Data domain
|
|
605
|
+
format: str = "JSON", # Optional: Response format
|
|
606
|
+
start_date: str = None, # Optional: YYYYMMDD or TODAY-2W
|
|
607
|
+
end_date: str = None, # Optional: YYYYMMDD or TODAY
|
|
608
|
+
calendar: str = "CAL_USBANK", # Optional: Calendar convention
|
|
609
|
+
frequency: str = "FREQ_DAY", # Optional: Frequency
|
|
610
|
+
conversion: str = "CONV_LASTBUS_ABS", # Optional: Conversion
|
|
611
|
+
nan_treatment: str = "NA_NOTHING", # Optional: Missing data
|
|
612
|
+
page: str = None # Optional: Pagination token
|
|
613
|
+
) -> TimeSeriesResponse
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**When to use:** You have instrument IDs from search or list operations.
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
#### `get_group_time_series_async()`
|
|
621
|
+
|
|
622
|
+
Query all instruments in a group, optionally with filters.
|
|
623
|
+
|
|
624
|
+
**Minimal usage:**
|
|
625
|
+
```python
|
|
626
|
+
result = await dq.get_group_time_series_async(
|
|
627
|
+
group_id="FI_GO_BO_EA",
|
|
628
|
+
attributes=["MIDPRC"],
|
|
629
|
+
start_date="20240101",
|
|
630
|
+
end_date="20240131"
|
|
631
|
+
)
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
**With filter:**
|
|
635
|
+
```python
|
|
636
|
+
result = await dq.get_group_time_series_async(
|
|
637
|
+
group_id="FI_GO_BO_EA",
|
|
638
|
+
attributes=["MIDPRC", "REPO_1M"],
|
|
639
|
+
filter="country(IRL)", # Ireland bonds only
|
|
640
|
+
start_date="20240101",
|
|
641
|
+
end_date="20240131",
|
|
642
|
+
calendar="CAL_WEEKDAYS"
|
|
643
|
+
)
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
**Complete signature:**
|
|
647
|
+
```python
|
|
648
|
+
result = await dq.get_group_time_series_async(
|
|
649
|
+
group_id: str, # Required: Group identifier
|
|
650
|
+
attributes: List[str], # Required: List of attributes
|
|
651
|
+
filter: str = None, # Optional: Filter expression
|
|
652
|
+
data: str = "REFERENCE_DATA", # Optional: Data domain
|
|
653
|
+
format: str = "JSON", # Optional: Response format
|
|
654
|
+
start_date: str = None, # Optional: YYYYMMDD or TODAY-1M
|
|
655
|
+
end_date: str = None, # Optional: YYYYMMDD or TODAY
|
|
656
|
+
calendar: str = "CAL_USBANK", # Optional: Calendar
|
|
657
|
+
frequency: str = "FREQ_DAY", # Optional: Frequency
|
|
658
|
+
conversion: str = "CONV_LASTBUS_ABS", # Optional: Conversion
|
|
659
|
+
nan_treatment: str = "NA_NOTHING", # Optional: Missing data
|
|
660
|
+
page: str = None # Optional: Pagination
|
|
661
|
+
) -> TimeSeriesResponse
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
**Available filters:** Check with `get_group_filters_async()` - typically `country(CODE)` or `currency(CODE)`.
|
|
665
|
+
|
|
666
|
+
---
|
|
667
|
+
|
|
668
|
+
### Discovery Methods
|
|
669
|
+
|
|
670
|
+
#### `search_instruments_async()`
|
|
671
|
+
|
|
672
|
+
Search for instruments within a group by keywords.
|
|
673
|
+
|
|
674
|
+
```python
|
|
675
|
+
results = await dq.search_instruments_async(
|
|
676
|
+
group_id: str, # Required: Group to search
|
|
677
|
+
keywords: str, # Required: Search terms
|
|
678
|
+
page: str = None # Optional: Pagination token
|
|
679
|
+
) -> InstrumentsResponse
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
#### `list_instruments_async()`
|
|
683
|
+
|
|
684
|
+
List all instruments in a group (paginated).
|
|
685
|
+
|
|
686
|
+
```python
|
|
687
|
+
instruments = await dq.list_instruments_async(
|
|
688
|
+
group_id: str, # Required: Group identifier
|
|
689
|
+
instrument_id: str = None, # Optional: Specific instrument
|
|
690
|
+
page: str = None # Optional: Pagination token
|
|
691
|
+
) -> InstrumentsResponse
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
#### `list_groups_async()`
|
|
695
|
+
|
|
696
|
+
List available data groups/datasets.
|
|
697
|
+
|
|
698
|
+
```python
|
|
699
|
+
groups = await dq.list_groups_async(
|
|
700
|
+
limit: int = 100 # Optional: Results per page (100-1000)
|
|
701
|
+
) -> List[Group]
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
**Note:** Minimum limit is 100 per API specification.
|
|
705
|
+
|
|
706
|
+
#### `get_group_attributes_async()`
|
|
707
|
+
|
|
708
|
+
Get available attributes for a group.
|
|
709
|
+
|
|
710
|
+
```python
|
|
711
|
+
attrs = await dq.get_group_attributes_async(
|
|
712
|
+
group_id: str, # Required: Group identifier
|
|
713
|
+
instrument_id: str = None, # Optional: Specific instrument
|
|
714
|
+
page: str = None # Optional: Pagination token
|
|
715
|
+
) -> AttributesResponse
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
#### `get_group_filters_async()`
|
|
719
|
+
|
|
720
|
+
Get available filter dimensions for a group.
|
|
721
|
+
|
|
722
|
+
```python
|
|
723
|
+
filters = await dq.get_group_filters_async(
|
|
724
|
+
group_id: str, # Required: Group identifier
|
|
725
|
+
page: str = None # Optional: Pagination token
|
|
726
|
+
) -> FiltersResponse
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
---
|
|
730
|
+
|
|
731
|
+
## Configuration
|
|
732
|
+
|
|
733
|
+
### Environment Variables
|
|
734
|
+
|
|
735
|
+
```bash
|
|
736
|
+
# Required
|
|
737
|
+
export DATAQUERY_CLIENT_ID="your_client_id"
|
|
738
|
+
export DATAQUERY_CLIENT_SECRET="your_client_secret"
|
|
739
|
+
|
|
740
|
+
# Optional - API endpoints (defaults shown)
|
|
741
|
+
export DATAQUERY_BASE_URL="https://api-developer.jpmorgan.com"
|
|
742
|
+
export DATAQUERY_CONTEXT_PATH="/research/dataquery-authe/api/v2"
|
|
743
|
+
export DATAQUERY_FILES_BASE_URL="https://api-strm-gw01.jpmchase.com"
|
|
744
|
+
|
|
745
|
+
# Optional - OAuth
|
|
746
|
+
export DATAQUERY_OAUTH_TOKEN_URL="https://authe.jpmorgan.com/as/token.oauth2"
|
|
747
|
+
export DATAQUERY_OAUTH_RESOURCE_ID="JPMC:URI:RS-06785-DataQueryExternalApi-PROD"
|
|
748
|
+
|
|
749
|
+
# Optional - Performance
|
|
750
|
+
export DATAQUERY_MAX_RETRIES="3"
|
|
751
|
+
export DATAQUERY_TIMEOUT="60"
|
|
752
|
+
export DATAQUERY_RATE_LIMIT_RPM="300" # Requests per minute
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
### Programmatic Configuration
|
|
756
|
+
|
|
757
|
+
```python
|
|
758
|
+
from dataquery import DataQuery, ClientConfig
|
|
759
|
+
|
|
760
|
+
config = ClientConfig(
|
|
761
|
+
client_id="your_client_id",
|
|
762
|
+
client_secret="your_client_secret",
|
|
763
|
+
max_retries=3,
|
|
764
|
+
timeout=60.0,
|
|
765
|
+
rate_limit_rpm=300
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
async with DataQuery(config=config) as dq:
|
|
769
|
+
# Your code here
|
|
770
|
+
pass
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### Performance Tuning
|
|
774
|
+
|
|
775
|
+
**For file downloads:**
|
|
776
|
+
```python
|
|
777
|
+
# Optimal settings for most use cases
|
|
778
|
+
num_parts=4 # 2-8 parallel chunks per file
|
|
779
|
+
part_size_mb=50 # 25-100 MB chunks
|
|
780
|
+
max_concurrent=3 # 1-5 concurrent file downloads
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
**For time series:**
|
|
784
|
+
```python
|
|
785
|
+
# Recommended date range
|
|
786
|
+
start_date="TODAY-1Y" # Max 1 year per request
|
|
787
|
+
calendar="CAL_WEEKDAYS" # International coverage
|
|
788
|
+
data="ALL" # Include market data
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
## Date and Calendar Reference
|
|
794
|
+
|
|
795
|
+
### Date Formats
|
|
796
|
+
|
|
797
|
+
**Absolute dates:**
|
|
798
|
+
```python
|
|
799
|
+
start_date="20240101" # YYYYMMDD format
|
|
800
|
+
end_date="20241231"
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
**Relative dates:**
|
|
804
|
+
```python
|
|
805
|
+
start_date="TODAY" # Today
|
|
806
|
+
start_date="TODAY-1D" # Yesterday
|
|
807
|
+
start_date="TODAY-1W" # 1 week ago
|
|
808
|
+
start_date="TODAY-1M" # 1 month ago
|
|
809
|
+
start_date="TODAY-1Y" # 1 year ago
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### Calendar Conventions
|
|
813
|
+
|
|
814
|
+
Choose based on your use case:
|
|
815
|
+
|
|
816
|
+
| Calendar | Description | Use Case |
|
|
817
|
+
|----------|-------------|----------|
|
|
818
|
+
| `CAL_WEEKDAYS` | Monday-Friday (recommended for international) | Multi-country data |
|
|
819
|
+
| `CAL_USBANK` | US banking days (default) | US-only data |
|
|
820
|
+
| `CAL_WEEKDAY_NOHOLIDAY` | All weekdays | Generic business days |
|
|
821
|
+
| `CAL_DEFAULT` | Calendar day | Include weekends |
|
|
822
|
+
|
|
823
|
+
**30+ calendars supported.** See API documentation for complete list.
|
|
824
|
+
|
|
825
|
+
### Frequency Conventions
|
|
826
|
+
|
|
827
|
+
| Frequency | Description |
|
|
828
|
+
|-----------|-------------|
|
|
829
|
+
| `FREQ_DAY` | Daily (default) |
|
|
830
|
+
| `FREQ_WEEK` | Weekly |
|
|
831
|
+
| `FREQ_MONTH` | Monthly |
|
|
832
|
+
| `FREQ_QUARTER` | Quarterly |
|
|
833
|
+
| `FREQ_YEAR` | Annual |
|
|
834
|
+
|
|
835
|
+
---
|
|
836
|
+
|
|
837
|
+
## Error Handling
|
|
838
|
+
|
|
839
|
+
### Common Patterns
|
|
840
|
+
|
|
841
|
+
```python
|
|
842
|
+
from dataquery import DataQuery, DataQueryError
|
|
843
|
+
|
|
844
|
+
async def safe_query():
|
|
845
|
+
try:
|
|
846
|
+
async with DataQuery() as dq:
|
|
847
|
+
result = await dq.get_expressions_time_series_async(
|
|
848
|
+
expressions=["DB(...)"],
|
|
849
|
+
start_date="20240101",
|
|
850
|
+
end_date="20240131"
|
|
851
|
+
)
|
|
852
|
+
return result
|
|
853
|
+
except DataQueryError as e:
|
|
854
|
+
print(f"DataQuery API error: {e}")
|
|
855
|
+
except Exception as e:
|
|
856
|
+
print(f"Unexpected error: {e}")
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
### Check Health
|
|
860
|
+
|
|
861
|
+
```python
|
|
862
|
+
async with DataQuery() as dq:
|
|
863
|
+
if await dq.health_check_async():
|
|
864
|
+
print("API is healthy")
|
|
865
|
+
else:
|
|
866
|
+
print("API health check failed")
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
### Monitor Statistics
|
|
870
|
+
|
|
871
|
+
```python
|
|
872
|
+
async with DataQuery() as dq:
|
|
873
|
+
# Make some requests...
|
|
874
|
+
await dq.list_groups_async(limit=100)
|
|
875
|
+
|
|
876
|
+
# Get statistics
|
|
877
|
+
stats = dq.get_stats()
|
|
878
|
+
print(f"Total requests: {stats.get('total_requests', 0)}")
|
|
879
|
+
print(f"Success rate: {stats.get('success_rate', 0):.1f}%")
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
---
|
|
883
|
+
|
|
884
|
+
## Examples & Recipes
|
|
885
|
+
|
|
886
|
+
### Download and Process JPMAQS Data
|
|
887
|
+
|
|
888
|
+
```python
|
|
889
|
+
import asyncio
|
|
890
|
+
from dataquery import DataQuery
|
|
891
|
+
from pathlib import Path
|
|
892
|
+
|
|
893
|
+
async def download_jpmaqs_month():
|
|
894
|
+
"""Download and process a month of JPMAQS data"""
|
|
895
|
+
async with DataQuery() as dq:
|
|
896
|
+
results = await dq.download_files_by_date_range_async(
|
|
897
|
+
file_group_id="JPMAQS",
|
|
898
|
+
start_date="2025-01-01",
|
|
899
|
+
end_date="2025-01-31",
|
|
900
|
+
destination_path="./jpmaqs_data",
|
|
901
|
+
num_parts=4
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
for result in results:
|
|
905
|
+
if result.status.value == "completed":
|
|
906
|
+
print(f"Downloaded {result.local_path.name}: {result.file_size:,} bytes")
|
|
907
|
+
# Process the file here
|
|
908
|
+
else:
|
|
909
|
+
print(f"Failed {result.file_id}: {result.error_message}")
|
|
910
|
+
|
|
911
|
+
asyncio.run(download_jpmaqs_month())
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
### Build Time Series for Portfolio
|
|
915
|
+
|
|
916
|
+
```python
|
|
917
|
+
async def get_portfolio_data(instrument_ids: list):
|
|
918
|
+
"""Get time series data for a portfolio of instruments"""
|
|
919
|
+
async with DataQuery() as dq:
|
|
920
|
+
# Get 1 year of daily data
|
|
921
|
+
ts_data = await dq.get_instrument_time_series_async(
|
|
922
|
+
instruments=instrument_ids,
|
|
923
|
+
attributes=["MIDPRC", "REPO_1M"],
|
|
924
|
+
start_date="TODAY-1Y",
|
|
925
|
+
end_date="TODAY",
|
|
926
|
+
calendar="CAL_WEEKDAYS",
|
|
927
|
+
data="ALL"
|
|
928
|
+
)
|
|
929
|
+
|
|
930
|
+
# Convert to DataFrame for analysis
|
|
931
|
+
df = dq.time_series_to_dataframe(ts_data)
|
|
932
|
+
return df
|
|
933
|
+
|
|
934
|
+
# Usage
|
|
935
|
+
portfolio_ids = [
|
|
936
|
+
"477f892d3cc8745578887a92d35c2a3e-DQGNMTBNDFIM",
|
|
937
|
+
"67e1bfca56bdee7a0fd5fe4c62a1e0dc-DQGNMTBNDFIM"
|
|
938
|
+
]
|
|
939
|
+
df = asyncio.run(get_portfolio_data(portfolio_ids))
|
|
940
|
+
print(df.head())
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
### Discover and Query New Dataset
|
|
944
|
+
|
|
945
|
+
```python
|
|
946
|
+
async def explore_dataset(group_id: str, keywords: str):
|
|
947
|
+
"""Explore a dataset and get sample data"""
|
|
948
|
+
async with DataQuery() as dq:
|
|
949
|
+
# Search for instruments
|
|
950
|
+
search_results = await dq.search_instruments_async(
|
|
951
|
+
group_id=group_id,
|
|
952
|
+
keywords=keywords
|
|
953
|
+
)
|
|
954
|
+
print(f"Found {search_results.items} instruments matching '{keywords}'")
|
|
955
|
+
|
|
956
|
+
# Get first 5 instrument IDs
|
|
957
|
+
instrument_ids = [inst.instrument_id for inst in search_results.instruments[:5]]
|
|
958
|
+
|
|
959
|
+
# Get available attributes
|
|
960
|
+
attrs = await dq.get_group_attributes_async(group_id=group_id)
|
|
961
|
+
print(f"Available attributes: {len(attrs.instruments)} instruments")
|
|
962
|
+
|
|
963
|
+
# Query sample data
|
|
964
|
+
ts_data = await dq.get_instrument_time_series_async(
|
|
965
|
+
instruments=instrument_ids,
|
|
966
|
+
attributes=["MIDPRC"], # Common attribute
|
|
967
|
+
start_date="TODAY-1W",
|
|
968
|
+
end_date="TODAY",
|
|
969
|
+
calendar="CAL_WEEKDAYS"
|
|
970
|
+
)
|
|
971
|
+
|
|
972
|
+
return ts_data
|
|
973
|
+
|
|
974
|
+
# Usage
|
|
975
|
+
data = asyncio.run(explore_dataset("FI_GO_BO_EA", "irish"))
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
---
|
|
979
|
+
|
|
980
|
+
## Requirements
|
|
981
|
+
|
|
982
|
+
- **Python 3.10+** (Python 3.11+ recommended for better performance)
|
|
983
|
+
- **Dependencies:** aiohttp, pydantic, structlog (auto-installed)
|
|
984
|
+
- **Optional:** pandas (for DataFrame conversion)
|
|
985
|
+
|
|
986
|
+
---
|
|
987
|
+
|
|
988
|
+
## Support
|
|
989
|
+
|
|
990
|
+
For issues, questions, or feature requests, contact DataQuery support at DataQuery_Support@jpmorgan.com
|
|
991
|
+
|
|
992
|
+
---
|
|
993
|
+
|
|
994
|
+
## License
|
|
995
|
+
|
|
996
|
+
See LICENSE file for details.
|