exness-data-preprocess 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- exness_data_preprocess/__init__.py +51 -0
- exness_data_preprocess/api.py +216 -0
- exness_data_preprocess/cli.py +199 -0
- exness_data_preprocess/processor.py +899 -0
- exness_data_preprocess-0.1.0.dist-info/METADATA +506 -0
- exness_data_preprocess-0.1.0.dist-info/RECORD +10 -0
- exness_data_preprocess-0.1.0.dist-info/WHEEL +4 -0
- exness_data_preprocess-0.1.0.dist-info/entry_points.txt +2 -0
- exness_data_preprocess-0.1.0.dist-info/licenses/AUTHORS.md +24 -0
- exness_data_preprocess-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: exness-data-preprocess
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Professional Exness forex tick data preprocessing with optimal compression (Parquet Zstd-22) and DuckDB OHLC generation. Provides efficient storage (9% smaller than ZIP) with lossless precision and direct queryability.
|
|
5
|
+
Project-URL: Homepage, https://github.com/terrylica/exness-data-preprocess
|
|
6
|
+
Project-URL: Documentation, https://github.com/terrylica/exness-data-preprocess#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/terrylica/exness-data-preprocess.git
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/terrylica/exness-data-preprocess/issues
|
|
9
|
+
Project-URL: Source Code, https://github.com/terrylica/exness-data-preprocess
|
|
10
|
+
Project-URL: Changelog, https://github.com/terrylica/exness-data-preprocess/blob/main/CHANGELOG.md
|
|
11
|
+
Author-email: Eon Labs <terry@eonlabs.com>
|
|
12
|
+
Maintainer-email: Terry Li <terry@eonlabs.com>
|
|
13
|
+
License: MIT
|
|
14
|
+
License-File: AUTHORS.md
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Keywords: backtesting,compression,data-engineering,duckdb,efficient-storage,eurusd,exness,financial-data,forex,forex-data,lossless,microstructure,ohlc,pandas,parquet,preprocessing,pyarrow,quantitative-finance,queryable,tick-data,time-series,zstd
|
|
17
|
+
Classifier: Development Status :: 3 - Alpha
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Classifier: Programming Language :: Python :: 3
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
28
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
29
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
30
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
31
|
+
Requires-Python: >=3.9
|
|
32
|
+
Requires-Dist: duckdb>=0.9.0
|
|
33
|
+
Requires-Dist: httpx>=0.25.0
|
|
34
|
+
Requires-Dist: pandas>=2.0.0
|
|
35
|
+
Requires-Dist: pyarrow>=16.0.0
|
|
36
|
+
Requires-Dist: tqdm>=4.65.0
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: mypy>=1.5.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
43
|
+
Provides-Extra: test
|
|
44
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
|
|
45
|
+
Requires-Dist: pytest>=7.0.0; extra == 'test'
|
|
46
|
+
Description-Content-Type: text/markdown
|
|
47
|
+
|
|
48
|
+
# Exness Data Preprocess v2.0.0
|
|
49
|
+
|
|
50
|
+
[](https://pypi.org/project/exness-data-preprocess/)
|
|
51
|
+
[](https://pypi.org/project/exness-data-preprocess/)
|
|
52
|
+
[](https://github.com/terrylica/exness-data-preprocess/blob/main/LICENSE)
|
|
53
|
+
[](https://github.com/terrylica/exness-data-preprocess/actions)
|
|
54
|
+
[](https://pypi.org/project/exness-data-preprocess/)
|
|
55
|
+
[](https://github.com/astral-sh/ruff)
|
|
56
|
+
|
|
57
|
+
Professional forex tick data preprocessing with unified single-file DuckDB storage. Provides incremental updates, dual-variant storage (Raw_Spread + Standard), and Phase7 9-column OHLC schema with sub-15ms query performance.
|
|
58
|
+
|
|
59
|
+
## Features
|
|
60
|
+
|
|
61
|
+
- **Unified Single-File Architecture**: One DuckDB file per instrument (eurusd.duckdb)
|
|
62
|
+
- **Incremental Updates**: Automatic gap detection and download only missing months
|
|
63
|
+
- **Dual-Variant Storage**: Raw_Spread (primary) + Standard (reference) in same database
|
|
64
|
+
- **Phase7 OHLC Schema**: 9-column bars with dual spreads and dual tick counts
|
|
65
|
+
- **Fast Queries**: Date range queries with sub-15ms performance
|
|
66
|
+
- **On-Demand Resampling**: Any timeframe (5m, 1h, 1d) resampled in <15ms
|
|
67
|
+
- **PRIMARY KEY Constraints**: Prevents duplicate data during incremental updates
|
|
68
|
+
- **Simple API**: Clean Python API for all operations
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# From PyPI (when published)
|
|
74
|
+
pip install exness-data-preprocess
|
|
75
|
+
|
|
76
|
+
# From source
|
|
77
|
+
git clone https://github.com/Eon-Labs/exness-data-preprocess.git
|
|
78
|
+
cd exness-data-preprocess
|
|
79
|
+
pip install -e .
|
|
80
|
+
|
|
81
|
+
# Using uv (recommended)
|
|
82
|
+
uv pip install exness-data-preprocess
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Quick Start
|
|
86
|
+
|
|
87
|
+
### Python API
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
import exness_data_preprocess as edp
|
|
91
|
+
|
|
92
|
+
# Initialize processor
|
|
93
|
+
processor = edp.ExnessDataProcessor(base_dir="~/eon/exness-data")
|
|
94
|
+
|
|
95
|
+
# Download 3 years of EURUSD data (automatic gap detection)
|
|
96
|
+
result = processor.update_data(
|
|
97
|
+
pair="EURUSD",
|
|
98
|
+
start_date="2022-01-01",
|
|
99
|
+
delete_zip=True,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
print(f"Months added: {result['months_added']}")
|
|
103
|
+
print(f"Raw ticks: {result['raw_ticks_added']:,}")
|
|
104
|
+
print(f"Standard ticks: {result['standard_ticks_added']:,}")
|
|
105
|
+
print(f"OHLC bars: {result['ohlc_bars']:,}")
|
|
106
|
+
print(f"Database size: {result['duckdb_size_mb']:.2f} MB")
|
|
107
|
+
|
|
108
|
+
# Query 1-minute OHLC bars for January 2024
|
|
109
|
+
df_1m = processor.query_ohlc(
|
|
110
|
+
pair="EURUSD",
|
|
111
|
+
timeframe="1m",
|
|
112
|
+
start_date="2024-01-01",
|
|
113
|
+
end_date="2024-01-31",
|
|
114
|
+
)
|
|
115
|
+
print(df_1m.head())
|
|
116
|
+
|
|
117
|
+
# Query raw tick data for September 2024
|
|
118
|
+
df_ticks = processor.query_ticks(
|
|
119
|
+
pair="EURUSD",
|
|
120
|
+
variant="raw_spread",
|
|
121
|
+
start_date="2024-09-01",
|
|
122
|
+
end_date="2024-09-30",
|
|
123
|
+
)
|
|
124
|
+
print(f"Ticks: {len(df_ticks):,}")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Architecture v2.0.0
|
|
128
|
+
|
|
129
|
+
### Data Flow
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
Exness Public Repository (monthly ZIPs, both variants)
|
|
133
|
+
↓
|
|
134
|
+
Automatic Gap Detection
|
|
135
|
+
↓
|
|
136
|
+
Download Only Missing Months (Raw_Spread + Standard)
|
|
137
|
+
↓
|
|
138
|
+
DuckDB Single-File Storage (PRIMARY KEY prevents duplicates)
|
|
139
|
+
↓
|
|
140
|
+
Phase7 9-Column OHLC Generation (dual spreads + dual tick counts)
|
|
141
|
+
↓
|
|
142
|
+
Query Interface (date ranges, SQL filters, on-demand resampling)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Storage Format
|
|
146
|
+
|
|
147
|
+
**Single File Per Instrument**: `~/eon/exness-data/eurusd.duckdb`
|
|
148
|
+
|
|
149
|
+
**Schema**:
|
|
150
|
+
- `raw_spread_ticks` table: Timestamp (PK), Bid, Ask
|
|
151
|
+
- `standard_ticks` table: Timestamp (PK), Bid, Ask
|
|
152
|
+
- `ohlc_1m` table: Phase7 9-column schema
|
|
153
|
+
- `metadata` table: Coverage tracking
|
|
154
|
+
|
|
155
|
+
**Phase7 9-Column OHLC**:
|
|
156
|
+
1. `Timestamp` - Bar timestamp
|
|
157
|
+
2. `Open` - Opening price
|
|
158
|
+
3. `High` - High price
|
|
159
|
+
4. `Low` - Low price
|
|
160
|
+
5. `Close` - Closing price
|
|
161
|
+
6. `raw_spread_avg` - Average spread from Raw_Spread variant
|
|
162
|
+
7. `standard_spread_avg` - Average spread from Standard variant
|
|
163
|
+
8. `tick_count_raw_spread` - Tick count from Raw_Spread variant
|
|
164
|
+
9. `tick_count_standard` - Tick count from Standard variant
|
|
165
|
+
|
|
166
|
+
### Directory Structure
|
|
167
|
+
|
|
168
|
+
**Default Location**: `~/eon/exness-data/` (outside project workspace)
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
~/eon/exness-data/
|
|
172
|
+
├── eurusd.duckdb # Single file for all EURUSD data
|
|
173
|
+
├── gbpusd.duckdb # Single file for all GBPUSD data
|
|
174
|
+
├── xauusd.duckdb # Single file for all XAUUSD data
|
|
175
|
+
└── temp/
|
|
176
|
+
└── (temporary ZIP files)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Why Single-File Per Instrument?**
|
|
180
|
+
- **Unified Storage**: All years in one database
|
|
181
|
+
- **Incremental Updates**: Automatic gap detection and download only missing months
|
|
182
|
+
- **No Duplicates**: PRIMARY KEY constraints prevent duplicate data
|
|
183
|
+
- **Fast Queries**: Date range queries with sub-15ms performance
|
|
184
|
+
- **Scalability**: Multi-year data in ~2 GB per instrument (3 years)
|
|
185
|
+
|
|
186
|
+
## Usage Examples
|
|
187
|
+
|
|
188
|
+
### Example 1: Initial Download and Incremental Updates
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
import exness_data_preprocess as edp
|
|
192
|
+
|
|
193
|
+
processor = edp.ExnessDataProcessor()
|
|
194
|
+
|
|
195
|
+
# Initial download (3-year history)
|
|
196
|
+
result = processor.update_data(
|
|
197
|
+
pair="EURUSD",
|
|
198
|
+
start_date="2022-01-01",
|
|
199
|
+
delete_zip=True,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Run again - only downloads new months since last update
|
|
203
|
+
result = processor.update_data(
|
|
204
|
+
pair="EURUSD",
|
|
205
|
+
start_date="2022-01-01",
|
|
206
|
+
)
|
|
207
|
+
print(f"Months added: {result['months_added']} (0 if up to date)")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Example 2: Check Data Coverage
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
coverage = processor.get_data_coverage("EURUSD")
|
|
214
|
+
|
|
215
|
+
print(f"Database exists: {coverage['database_exists']}")
|
|
216
|
+
print(f"Raw_Spread ticks: {coverage['raw_spread_ticks']:,}")
|
|
217
|
+
print(f"Standard ticks: {coverage['standard_ticks']:,}")
|
|
218
|
+
print(f"OHLC bars: {coverage['ohlc_bars']:,}")
|
|
219
|
+
print(f"Date range: {coverage['earliest_date']} to {coverage['latest_date']}")
|
|
220
|
+
print(f"Days covered: {coverage['date_range_days']}")
|
|
221
|
+
print(f"Database size: {coverage['duckdb_size_mb']:.2f} MB")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Example 3: Query OHLC with Date Ranges
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
# Query 1-minute bars for January 2024
|
|
228
|
+
df_1m = processor.query_ohlc(
|
|
229
|
+
pair="EURUSD",
|
|
230
|
+
timeframe="1m",
|
|
231
|
+
start_date="2024-01-01",
|
|
232
|
+
end_date="2024-01-31",
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Query 1-hour bars for Q1 2024 (resampled on-demand)
|
|
236
|
+
df_1h = processor.query_ohlc(
|
|
237
|
+
pair="EURUSD",
|
|
238
|
+
timeframe="1h",
|
|
239
|
+
start_date="2024-01-01",
|
|
240
|
+
end_date="2024-03-31",
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Query daily bars for entire 2024
|
|
244
|
+
df_1d = processor.query_ohlc(
|
|
245
|
+
pair="EURUSD",
|
|
246
|
+
timeframe="1d",
|
|
247
|
+
start_date="2024-01-01",
|
|
248
|
+
end_date="2024-12-31",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
print(f"1m bars: {len(df_1m):,}")
|
|
252
|
+
print(f"1h bars: {len(df_1h):,}")
|
|
253
|
+
print(f"1d bars: {len(df_1d):,}")
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Example 4: Query Ticks with Date Ranges
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
# Query Raw_Spread ticks for September 2024
|
|
260
|
+
df_raw = processor.query_ticks(
|
|
261
|
+
pair="EURUSD",
|
|
262
|
+
variant="raw_spread",
|
|
263
|
+
start_date="2024-09-01",
|
|
264
|
+
end_date="2024-09-30",
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
print(f"Raw_Spread ticks: {len(df_raw):,}")
|
|
268
|
+
print(f"Columns: {list(df_raw.columns)}")
|
|
269
|
+
|
|
270
|
+
# Calculate spread statistics
|
|
271
|
+
df_raw['Spread'] = df_raw['Ask'] - df_raw['Bid']
|
|
272
|
+
print(f"Mean spread: {df_raw['Spread'].mean() * 10000:.4f} pips")
|
|
273
|
+
print(f"Zero-spreads: {((df_raw['Spread'] == 0).sum() / len(df_raw) * 100):.2f}%")
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Example 5: Query with SQL Filters
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
# Query only zero-spread ticks
|
|
280
|
+
df_zero = processor.query_ticks(
|
|
281
|
+
pair="EURUSD",
|
|
282
|
+
variant="raw_spread",
|
|
283
|
+
start_date="2024-09-01",
|
|
284
|
+
end_date="2024-09-01",
|
|
285
|
+
filter_sql="Bid = Ask",
|
|
286
|
+
)
|
|
287
|
+
print(f"Zero-spread ticks: {len(df_zero):,}")
|
|
288
|
+
|
|
289
|
+
# Query high-price ticks
|
|
290
|
+
df_high = processor.query_ticks(
|
|
291
|
+
pair="EURUSD",
|
|
292
|
+
variant="raw_spread",
|
|
293
|
+
start_date="2024-09-01",
|
|
294
|
+
end_date="2024-09-30",
|
|
295
|
+
filter_sql="Bid > 1.11",
|
|
296
|
+
)
|
|
297
|
+
print(f"High-price ticks: {len(df_high):,}")
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Example 6: Process Multiple Instruments
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
processor = edp.ExnessDataProcessor()
|
|
304
|
+
|
|
305
|
+
# Process multiple pairs
|
|
306
|
+
pairs = ["EURUSD", "GBPUSD", "XAUUSD"]
|
|
307
|
+
|
|
308
|
+
for pair in pairs:
|
|
309
|
+
print(f"Processing {pair}...")
|
|
310
|
+
result = processor.update_data(
|
|
311
|
+
pair=pair,
|
|
312
|
+
start_date="2023-01-01",
|
|
313
|
+
delete_zip=True,
|
|
314
|
+
)
|
|
315
|
+
print(f" Months added: {result['months_added']}")
|
|
316
|
+
print(f" Database size: {result['duckdb_size_mb']:.2f} MB")
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Example 7: Parallel Processing
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
323
|
+
|
|
324
|
+
def process_instrument(pair, start_date):
|
|
325
|
+
processor = edp.ExnessDataProcessor()
|
|
326
|
+
return processor.update_data(pair=pair, start_date=start_date, delete_zip=True)
|
|
327
|
+
|
|
328
|
+
instruments = [
|
|
329
|
+
("EURUSD", "2023-01-01"),
|
|
330
|
+
("GBPUSD", "2023-01-01"),
|
|
331
|
+
("XAUUSD", "2023-01-01"),
|
|
332
|
+
("USDJPY", "2023-01-01"),
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
with ThreadPoolExecutor(max_workers=4) as executor:
|
|
336
|
+
futures = {
|
|
337
|
+
executor.submit(process_instrument, pair, start_date): pair
|
|
338
|
+
for pair, start_date in instruments
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
for future in as_completed(futures):
|
|
342
|
+
pair = futures[future]
|
|
343
|
+
result = future.result()
|
|
344
|
+
print(f"{pair}: {result['months_added']} months added")
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Development
|
|
348
|
+
|
|
349
|
+
### Setup
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# Clone repository
|
|
353
|
+
git clone https://github.com/Eon-Labs/exness-data-preprocess.git
|
|
354
|
+
cd exness-data-preprocess
|
|
355
|
+
|
|
356
|
+
# Install with development dependencies (using uv)
|
|
357
|
+
uv sync --dev
|
|
358
|
+
|
|
359
|
+
# Or with pip
|
|
360
|
+
pip install -e ".[dev]"
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Testing
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
# Run all tests
|
|
367
|
+
uv run pytest
|
|
368
|
+
|
|
369
|
+
# Run with coverage
|
|
370
|
+
uv run pytest --cov=exness_data_preprocess --cov-report=html
|
|
371
|
+
|
|
372
|
+
# Run specific test
|
|
373
|
+
uv run pytest tests/test_processor.py -v
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Code Quality
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
# Format code
|
|
380
|
+
uv run ruff format .
|
|
381
|
+
|
|
382
|
+
# Lint
|
|
383
|
+
uv run ruff check --fix .
|
|
384
|
+
|
|
385
|
+
# Type checking
|
|
386
|
+
uv run mypy src/
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Building
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
# Build package
|
|
393
|
+
uv build
|
|
394
|
+
|
|
395
|
+
# Test installation locally
|
|
396
|
+
uv tool install --editable .
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Data Source
|
|
400
|
+
|
|
401
|
+
Data is sourced from Exness's public tick data repository:
|
|
402
|
+
- **URL**: https://ticks.ex2archive.com/
|
|
403
|
+
- **Format**: Monthly ZIP files with CSV tick data
|
|
404
|
+
- **Variants**: Raw_Spread (zero-spreads) + Standard (market spreads)
|
|
405
|
+
- **Content**: Timestamp, Bid, Ask prices for major forex pairs
|
|
406
|
+
- **Quality**: Institutional ECN/STP data with microsecond precision
|
|
407
|
+
|
|
408
|
+
## Technical Specifications
|
|
409
|
+
|
|
410
|
+
### Database Size (3-Year History, EURUSD)
|
|
411
|
+
|
|
412
|
+
| Metric | Value |
|
|
413
|
+
|--------|-------|
|
|
414
|
+
| Raw_Spread ticks | ~18.6M |
|
|
415
|
+
| Standard ticks | ~19.6M |
|
|
416
|
+
| OHLC bars (1m) | ~413K |
|
|
417
|
+
| Database size | ~2.08 GB |
|
|
418
|
+
| Date range | 2022-01-01 to 2025-01-10 |
|
|
419
|
+
|
|
420
|
+
### Query Performance
|
|
421
|
+
|
|
422
|
+
| Operation | Time |
|
|
423
|
+
|-----------|------|
|
|
424
|
+
| Query 880K ticks (1 month) | <15ms |
|
|
425
|
+
| Query 1m OHLC (1 month) | <10ms |
|
|
426
|
+
| Resample to 1h (1 month) | <15ms |
|
|
427
|
+
| Resample to 1d (1 year) | <20ms |
|
|
428
|
+
|
|
429
|
+
### Architecture Benefits
|
|
430
|
+
|
|
431
|
+
| Feature | Benefit |
|
|
432
|
+
|---------|---------|
|
|
433
|
+
| Single file per instrument | Unified storage, no file fragmentation |
|
|
434
|
+
| PRIMARY KEY constraints | Prevents duplicates during incremental updates |
|
|
435
|
+
| Automatic gap detection | Download only missing months |
|
|
436
|
+
| Dual-variant storage | Raw_Spread + Standard in same database |
|
|
437
|
+
| Phase7 OHLC schema | Dual spreads + dual tick counts |
|
|
438
|
+
| Date range queries | Efficient filtering without loading entire dataset |
|
|
439
|
+
| On-demand resampling | Any timeframe in <15ms |
|
|
440
|
+
| SQL filter support | Direct SQL WHERE clauses on ticks |
|
|
441
|
+
|
|
442
|
+
## API Reference
|
|
443
|
+
|
|
444
|
+
### ExnessDataProcessor
|
|
445
|
+
|
|
446
|
+
```python
|
|
447
|
+
processor = edp.ExnessDataProcessor(base_dir="~/eon/exness-data")
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**Methods**:
|
|
451
|
+
|
|
452
|
+
- `update_data(pair, start_date, force_redownload=False, delete_zip=True)` - Update database with latest data
|
|
453
|
+
- `query_ohlc(pair, timeframe, start_date=None, end_date=None)` - Query OHLC bars
|
|
454
|
+
- `query_ticks(pair, variant, start_date=None, end_date=None, filter_sql=None)` - Query tick data
|
|
455
|
+
- `get_data_coverage(pair)` - Get coverage information
|
|
456
|
+
|
|
457
|
+
**Parameters**:
|
|
458
|
+
|
|
459
|
+
- `pair` (str): Currency pair (e.g., "EURUSD", "GBPUSD", "XAUUSD")
|
|
460
|
+
- `timeframe` (str): OHLC timeframe ("1m", "5m", "15m", "1h", "4h", "1d")
|
|
461
|
+
- `variant` (str): Tick variant ("raw_spread" or "standard")
|
|
462
|
+
- `start_date` (str): Start date in "YYYY-MM-DD" format
|
|
463
|
+
- `end_date` (str): End date in "YYYY-MM-DD" format
|
|
464
|
+
- `filter_sql` (str): SQL WHERE clause (e.g., "Bid > 1.11 AND Ask < 1.12")
|
|
465
|
+
|
|
466
|
+
## Migration from v1.0.0
|
|
467
|
+
|
|
468
|
+
**v1.0.0 (Legacy)**:
|
|
469
|
+
- Monthly DuckDB files: `eurusd_ohlc_2024_08.duckdb`
|
|
470
|
+
- Parquet tick storage: `eurusd_ticks_2024_08.parquet`
|
|
471
|
+
- Functions: `process_month()`, `process_date_range()`, `analyze_ticks()`
|
|
472
|
+
|
|
473
|
+
**v2.0.0 (Current)**:
|
|
474
|
+
- Single DuckDB file: `eurusd.duckdb`
|
|
475
|
+
- No Parquet files (everything in DuckDB)
|
|
476
|
+
- Unified API: `processor.update_data()`, `processor.query_ohlc()`, `processor.query_ticks()`
|
|
477
|
+
|
|
478
|
+
**Migration Steps**:
|
|
479
|
+
1. Run `processor.update_data(pair, start_date)` to create new unified database
|
|
480
|
+
2. Delete old monthly files: `rm eurusd_ohlc_2024_*.duckdb eurusd_ticks_2024_*.parquet`
|
|
481
|
+
3. Update code to use new API methods
|
|
482
|
+
|
|
483
|
+
## License
|
|
484
|
+
|
|
485
|
+
MIT License - see LICENSE file for details.
|
|
486
|
+
|
|
487
|
+
## Authors
|
|
488
|
+
|
|
489
|
+
- Terry Li <terry@eonlabs.com>
|
|
490
|
+
- Eon Labs
|
|
491
|
+
|
|
492
|
+
## Contributing
|
|
493
|
+
|
|
494
|
+
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
|
|
495
|
+
|
|
496
|
+
## Acknowledgments
|
|
497
|
+
|
|
498
|
+
- Exness for providing high-quality public tick data
|
|
499
|
+
- DuckDB for embedded OLAP capabilities with sub-15ms query performance
|
|
500
|
+
|
|
501
|
+
## Additional Documentation
|
|
502
|
+
|
|
503
|
+
- **Basic Usage Examples**: See `examples/basic_usage.py`
|
|
504
|
+
- **Batch Processing**: See `examples/batch_processing.py`
|
|
505
|
+
- **Architecture Details**: See `docs/UNIFIED_DUCKDB_PLAN_v2.md`
|
|
506
|
+
- **Unit Tests**: See `tests/` directory
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
exness_data_preprocess/__init__.py,sha256=W2z4slZAQLWNrj83QNCiUg-z83vyasEI0DXFqjgxr0s,1342
|
|
2
|
+
exness_data_preprocess/api.py,sha256=gb1HUatHE4SqQ62uXM8j55oEDH_0lN7AqaoxSjuhAck,6764
|
|
3
|
+
exness_data_preprocess/cli.py,sha256=b3XOKF2cUO5E9D08rVRE3JRrxZr5UkbXsxijaaWR8A4,7059
|
|
4
|
+
exness_data_preprocess/processor.py,sha256=jSQ-cij56fZALOTAddsrtpJGOsBYYwJz_Ei6NsIa2P0,35653
|
|
5
|
+
exness_data_preprocess-0.1.0.dist-info/METADATA,sha256=DHG8t5T6kEAZVRb_13fxhOLNSuLabKVNtKFkCrpWR5M,15707
|
|
6
|
+
exness_data_preprocess-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
+
exness_data_preprocess-0.1.0.dist-info/entry_points.txt,sha256=2IoifK7DgeskCBuW-jmOaSMrcJQsmvRjxf6BK77hxk8,70
|
|
8
|
+
exness_data_preprocess-0.1.0.dist-info/licenses/AUTHORS.md,sha256=X8dzQz7qXtMBgCSynqdbiVqGU43nYswudSSW8njUXvA,511
|
|
9
|
+
exness_data_preprocess-0.1.0.dist-info/licenses/LICENSE,sha256=Tw9a0HzGThMjIXRkAR-0zL5azFsvGBaXZlornJKIXfE,1065
|
|
10
|
+
exness_data_preprocess-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Authors
|
|
2
|
+
|
|
3
|
+
## Project Lead
|
|
4
|
+
|
|
5
|
+
- **Terry Li** <terry@eonlabs.com>
|
|
6
|
+
- Original author and maintainer
|
|
7
|
+
- Architecture and core implementation
|
|
8
|
+
|
|
9
|
+
## Organization
|
|
10
|
+
|
|
11
|
+
**Eon Labs**
|
|
12
|
+
- Website: https://eonlabs.com
|
|
13
|
+
- GitHub: https://github.com/Eon-Labs
|
|
14
|
+
|
|
15
|
+
## Contributors
|
|
16
|
+
|
|
17
|
+
We welcome contributions! See CONTRIBUTING.md for guidelines.
|
|
18
|
+
|
|
19
|
+
## Acknowledgments
|
|
20
|
+
|
|
21
|
+
- Exness for providing high-quality public tick data
|
|
22
|
+
- DuckDB team for embedded OLAP database
|
|
23
|
+
- Apache Arrow and Parquet communities
|
|
24
|
+
- Facebook's Zstd compression library
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Eon Labs
|
|
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.
|