exness-data-preprocess 0.2.0__tar.gz → 0.3.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.
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.cz.toml +1 -1
- exness_data_preprocess-0.3.0/CHANGELOG.md +53 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/CLAUDE.md +14 -11
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/PKG-INFO +9 -15
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/README.md +8 -14
- exness_data_preprocess-0.3.0/RELEASE_NOTES.md +22 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/DATABASE_SCHEMA.md +135 -44
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/README.md +4 -8
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/UNIFIED_DUCKDB_PLAN_v2.md +2 -2
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/examples/basic_usage.py +3 -3
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/examples/batch_processing.py +1 -1
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/pyproject.toml +1 -1
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/src/exness_data_preprocess/__init__.py +1 -1
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/src/exness_data_preprocess/processor.py +46 -180
- exness_data_preprocess-0.3.0/src/exness_data_preprocess/schema.py +256 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/README.md +1 -1
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/test_functional_regression.py +13 -17
- exness_data_preprocess-0.2.0/CHANGELOG.md +0 -35
- exness_data_preprocess-0.2.0/examples/add_schema_comments.py +0 -54
- exness_data_preprocess-0.2.0/tests/PYDANTIC_TEST_STRATEGY.md +0 -430
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.github/workflows/ci.yml +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.github/workflows/pre-commit-autoupdate.yml +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.github/workflows/publish.yml +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.gitignore +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.pre-commit-config.yaml +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/AUTHORS.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/CONTRIBUTING.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/GITHUB_PYPI_SETUP.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/LICENSE +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/PYDANTIC_REFACTORING_PLAN.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/PYDANTIC_REFACTORING_STATUS.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/cliff.toml +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/EXNESS_DATA_SOURCES.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/archive/UNIFIED_DUCKDB_PLAN_v1.0.0_LEGACY.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/implementation-plan.yaml +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/planning-index.yaml +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/compression-benchmarks/README.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/compression-benchmarks/test_all_compression_methods.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/compression-benchmarks/test_delta_encoding_properly.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/compression-benchmarks/test_parquet_compression_methods.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/01-methodology.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/02-mode-truncated-analysis.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/03-hurdle-model-analysis.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/04-temporal-patterns.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/05-final-recommendations.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/README.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/scripts/reproduction_guide.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/visualizations/hurdle-model/hurdle_decomposition.png +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/visualizations/mode-truncated/cv_mode_comparison.png +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/visualizations/mode-truncated/distribution_overlay.png +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/visualizations/mode-truncated/signal_quality_comparison.png +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/visualizations/mode-truncated/variance_decomposition.png +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/visualizations/temporal/concentration_curve.png +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/visualizations/temporal/percentile_analysis.png +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/01-methodology.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/02-baseline-sep2024.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/03-multiperiod-validation.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/04-discoveries-and-plan-evolution.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/README.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/findings/baseline-sep2024-comprehensive-summary.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/findings/phase2-mean-reversion-report.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/findings/phase3-volatility-model-report.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/findings/phase4-5-statistical-tests-survivorship-report.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/baseline-sep2024/01_mean_reversion_analysis.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/baseline-sep2024/02_volatility_model_simple.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/baseline-sep2024/03_liquidity_crisis_detection.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/baseline-sep2024/04_regime_detection_analysis.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/multiperiod-validation/phase1_data_validation.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/multiperiod-validation/phase2_mean_reversion.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/multiperiod-validation/phase3_volatility_model.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/multiperiod-validation/phase4_statistical_tests.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/multiperiod-validation/phase5_survivorship_bias.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/multiperiod-validation/phase5_survivorship_bias_v1.0.1.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/multiperiod-validation/phase6a_interval_analysis.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/multiperiod-validation/phase6b_price_clustering.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/multiperiod-validation/phase6c_combined_pattern.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/reproduction_guide.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/visualization/diagnostic_burst_decay.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/visualization/generate_interactive_dashboard.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/visualization/generate_lightweight_dashboard.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/visualization/prepare_visualization_data.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/src/exness_data_preprocess/cli.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/src/exness_data_preprocess/models.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/__init__.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/archive/E2E_TESTING_PLAN_v1.0.0.md +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/conftest.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/test_basic.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/test_models.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/test_processor_pydantic.py +0 -0
- {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/test_types.py +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to exness-data-preprocess will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### ✨ Features
|
|
10
|
+
|
|
11
|
+
- Implement pydantic v2 models with dual-variant e2e testing - Add Pydantic v2 models (UpdateResult, CoverageInfo) for type-safe API - Update processor to use Pydantic types (PairType, TimeframeType, VariantType) - Remove deprecated api.py module (broken functionality) - Implement comprehensive test suite (48 tests, 100% passing): * test_models.py - Pydantic model validation (13 tests) * test_types.py - Type safety and helpers (15 tests) * test_processor_pydantic.py - Integration tests (6 tests) * test_functional_regression.py - v2.0.0 regression tests (10 tests) - Fix Standard variant downloads (variant="" not "Standard") - Add true end-to-end testing with real Exness downloads - Test data: EURUSD August 2024 (815K Raw_Spread + 877K Standard ticks) - Update documentation and add refactoring status tracking - Coverage: models.py 100%, __init__.py 100%, processor.py 45% BREAKING CHANGE: api.py removed (use ExnessDataProcessor directly)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### 🔧 Continuous Integration
|
|
15
|
+
|
|
16
|
+
- Modernize publish workflow with uv and validation pipeline Update GitHub Actions publish workflow to match gapless-crypto-data standards: Build System: - Replace pip+build with uv for consistency with local development - Add UV_SYSTEM_PYTHON=1 environment variable - Use uv sync --dev for dependency installation Validation Pipeline: - Add file encoding validation (UTF-8/ASCII enforcement) - Add ruff linting and format checking - Add pytest test suite execution before build Security: - Add Sigstore artifact signing for published packages - Maintain PyPI Trusted Publishing (OIDC) authentication Configuration: - Change environment name: release → pypi (semantic clarity) - Change artifact name: dist → python-package-distributions (consistency) - Add verbose output to PyPI publish step Security Note: - Added .token-info.md to .gitignore to prevent accidental token documentation commits - Workflow uses OIDC (no API token needed)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### 🧰 Maintenance
|
|
20
|
+
|
|
21
|
+
- **release**: Bump version 0.1.0 → 0.2.0 [skip ci]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### ✨ Features
|
|
25
|
+
|
|
26
|
+
- Initial commit with v2.0.0 unified DuckDB architecture - Unified single-file DuckDB storage per instrument (eurusd.duckdb) - Phase7 9-column OHLC schema with dual-variant spreads - Sub-15ms query performance with date range filtering - PRIMARY KEY constraints prevent duplicates - Automatic gap detection for incremental updates - DuckDB self-documentation with COMMENT ON statements - GitHub Actions CI/CD with PyPI Trusted Publishing - Pre-commit hooks (ruff, commitizen, basic checks) - Professional README with shields.io badges
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### 🐛 Bug Fixes
|
|
30
|
+
|
|
31
|
+
- Force add .pre-commit-config.yaml (was incorrectly gitignored)
|
|
32
|
+
|
|
33
|
+
- Remove .pre-commit-config.yaml from .gitignore Pre-commit config should be version controlled, not ignored.
|
|
34
|
+
|
|
35
|
+
- **pre-commit**: Enable unsafe fixes for ruff to resolve remaining errors
|
|
36
|
+
|
|
37
|
+
- **research**: Resolve remaining linting errors in research scripts - Fix bare except clause in 02_volatility_model_simple.py (E722) - Move math.erf imports to top in 03_liquidity_crisis_detection.py (E402) - Move math.erf imports to top in 04_regime_detection_analysis.py (E402)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### 💅 Code Style
|
|
41
|
+
|
|
42
|
+
- Apply pre-commit formatting fixes - Fix end-of-file newlines in 2 MD files - Apply ruff quote style fixes (200 errors fixed) - Reformat 26 files with ruff-format
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### 🔧 CI/CD Improvements
|
|
46
|
+
|
|
47
|
+
- **ci**: Add pre-commit config and basic tests - Add .pre-commit-config.yaml to repository - Create basic test suite (test_basic.py) for CI - Add GITHUB_PYPI_SETUP.md with complete setup instructions Fixes CI failures: - Pre-commit hook now finds config file - Pytest now finds and runs tests successfully
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### 🧰 Maintenance
|
|
51
|
+
|
|
52
|
+
- Prepare v0.1.0 release
|
|
53
|
+
|
|
@@ -80,7 +80,7 @@ doppler run --project claude-config --config dev -- uv publish --token "$PYPI_TO
|
|
|
80
80
|
- **Lines 247-275**: `_append_ticks_to_db()` - Tick insertion with PRIMARY KEY duplicate prevention
|
|
81
81
|
- **Lines 277-377**: `_discover_missing_months()` - Gap detection for incremental updates
|
|
82
82
|
- **Lines 379-516**: `update_data()` - Main entry point for downloading and updating data
|
|
83
|
-
- **Lines 518-549**: `_regenerate_ohlc()` - Phase7
|
|
83
|
+
- **Lines 518-549**: `_regenerate_ohlc()` - Phase7 13-column OHLC (v1.2.0) generation with LEFT JOIN
|
|
84
84
|
- **Lines 551-603**: `query_ticks()` - Tick queries with date range and SQL filters
|
|
85
85
|
- **Lines 605-706**: `query_ohlc()` - OHLC queries with on-demand resampling (1m/5m/15m/1h/4h/1d)
|
|
86
86
|
- **Lines 708-821**: `add_schema_comments()` / `add_schema_comments_all()` - Retrofit self-documentation
|
|
@@ -102,10 +102,11 @@ doppler run --project claude-config --config dev -- uv publish --token "$PYPI_TO
|
|
|
102
102
|
- Incremental updates with automatic gap detection
|
|
103
103
|
- PRIMARY KEY constraints prevent duplicates during updates
|
|
104
104
|
|
|
105
|
-
**Phase7
|
|
105
|
+
**Phase7 13-Column OHLC Schema** (v1.2.0):
|
|
106
106
|
- BID-only OHLC from Raw_Spread variant
|
|
107
107
|
- Dual spreads: `raw_spread_avg` and `standard_spread_avg`
|
|
108
108
|
- Dual tick counts: `tick_count_raw_spread` and `tick_count_standard`
|
|
109
|
+
- Normalized metrics: `range_per_spread`, `range_per_tick`, `body_per_spread`, `body_per_tick`
|
|
109
110
|
- Generated via LEFT JOIN between Raw_Spread and Standard variants
|
|
110
111
|
|
|
111
112
|
**Self-Documentation**:
|
|
@@ -138,9 +139,9 @@ Each `.duckdb` file contains:
|
|
|
138
139
|
- Reference data for spread comparison
|
|
139
140
|
|
|
140
141
|
3. **`ohlc_1m`** table:
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
142
|
+
- **Schema**: Phase7 13-column (v1.2.0) - See [`schema.py`](src/exness_data_preprocess/schema.py)
|
|
143
|
+
- **Details**: BID-only OHLC with dual-variant spreads, tick counts, and normalized metrics
|
|
144
|
+
- **Reference**: [`DATABASE_SCHEMA.md`](docs/DATABASE_SCHEMA.md)
|
|
144
145
|
|
|
145
146
|
4. **`metadata`** table:
|
|
146
147
|
- Columns: `key` (PK), `value`, `updated_at`
|
|
@@ -179,7 +180,7 @@ Each `.duckdb` file contains:
|
|
|
179
180
|
- ✅ **Dual-variant storage**: Raw_Spread + Standard in same database
|
|
180
181
|
- ✅ **PRIMARY KEY constraints**: Prevents duplicates during incremental updates
|
|
181
182
|
- ✅ **Automatic gap detection**: Downloads only missing months
|
|
182
|
-
- ✅ **Phase7
|
|
183
|
+
- ✅ **Phase7 13-column OHLC (v1.2.0)**: Dual spreads + dual tick counts
|
|
183
184
|
- ✅ **Date range queries**: Sub-15ms query performance
|
|
184
185
|
- ✅ **On-demand resampling**: Any timeframe (5m, 1h, 1d) in <15ms
|
|
185
186
|
|
|
@@ -206,11 +207,13 @@ Each `.duckdb` file contains:
|
|
|
206
207
|
- `/tmp/exness-duckdb-test/test_refactored_processor.py` - Validation test
|
|
207
208
|
- `/tmp/exness-duckdb-test/test_queries_only.py` - Query validation test
|
|
208
209
|
|
|
209
|
-
### Phase7
|
|
210
|
+
### Phase7 13-Column OHLC Schema v1.2.0 (✅ Implemented)
|
|
210
211
|
|
|
211
|
-
**Decision**: Dual-variant BID-only OHLC with
|
|
212
|
+
**Decision**: Dual-variant BID-only OHLC with 13 columns capturing both Raw_Spread and Standard characteristics plus normalized metrics
|
|
212
213
|
|
|
213
|
-
**Schema**:
|
|
214
|
+
**Schema**: Phase7 13-column (v1.2.0)
|
|
215
|
+
- **Definition**: See [`schema.py`](src/exness_data_preprocess/schema.py)
|
|
216
|
+
- **Documentation**: See [`DATABASE_SCHEMA.md`](docs/DATABASE_SCHEMA.md)
|
|
214
217
|
|
|
215
218
|
**Key Features**:
|
|
216
219
|
- ✅ **BID-only OHLC**: Uses Raw_Spread Bid prices (execution prices)
|
|
@@ -311,7 +314,7 @@ SELECT table_name, column_name, data_type, comment FROM duckdb_columns();
|
|
|
311
314
|
- ✅ **Dual-variant storage** - Raw_Spread + Standard in same database
|
|
312
315
|
- ✅ **PRIMARY KEY constraints** - Prevents duplicates during incremental updates
|
|
313
316
|
- ✅ **Automatic gap detection** - Downloads only missing months
|
|
314
|
-
- ✅ **Phase7
|
|
317
|
+
- ✅ **Phase7 13-column OHLC (v1.2.0)** - Dual spreads + dual tick counts
|
|
315
318
|
- ✅ **Date range queries** - Sub-15ms query performance
|
|
316
319
|
- ✅ **On-demand resampling** - Any timeframe in <15ms
|
|
317
320
|
- ✅ **SQL filter support** - Direct SQL WHERE clauses on ticks
|
|
@@ -356,7 +359,7 @@ SELECT table_name, column_name, data_type, comment FROM duckdb_columns();
|
|
|
356
359
|
eurusd.duckdb:
|
|
357
360
|
├── raw_spread_ticks # Timestamp (PK), Bid, Ask
|
|
358
361
|
├── standard_ticks # Timestamp (PK), Bid, Ask
|
|
359
|
-
├── ohlc_1m # Phase7
|
|
362
|
+
├── ohlc_1m # Phase7 13-column schema (v1.2.0)
|
|
360
363
|
└── metadata # Coverage tracking
|
|
361
364
|
```
|
|
362
365
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: exness-data-preprocess
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
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
5
|
Project-URL: Homepage, https://github.com/terrylica/exness-data-preprocess
|
|
6
6
|
Project-URL: Documentation, https://github.com/terrylica/exness-data-preprocess#readme
|
|
@@ -55,14 +55,14 @@ Description-Content-Type: text/markdown
|
|
|
55
55
|
[](https://pypi.org/project/exness-data-preprocess/)
|
|
56
56
|
[](https://github.com/astral-sh/ruff)
|
|
57
57
|
|
|
58
|
-
Professional forex tick data preprocessing with unified single-file DuckDB storage. Provides incremental updates, dual-variant storage (Raw_Spread + Standard), and Phase7
|
|
58
|
+
Professional forex tick data preprocessing with unified single-file DuckDB storage. Provides incremental updates, dual-variant storage (Raw_Spread + Standard), and Phase7 13-column OHLC schema (v1.2.0) with sub-15ms query performance.
|
|
59
59
|
|
|
60
60
|
## Features
|
|
61
61
|
|
|
62
62
|
- **Unified Single-File Architecture**: One DuckDB file per instrument (eurusd.duckdb)
|
|
63
63
|
- **Incremental Updates**: Automatic gap detection and download only missing months
|
|
64
64
|
- **Dual-Variant Storage**: Raw_Spread (primary) + Standard (reference) in same database
|
|
65
|
-
- **Phase7 OHLC Schema**:
|
|
65
|
+
- **Phase7 OHLC Schema**: 13-column bars (v1.2.0) with dual spreads, tick counts, and normalized metrics
|
|
66
66
|
- **Fast Queries**: Date range queries with sub-15ms performance
|
|
67
67
|
- **On-Demand Resampling**: Any timeframe (5m, 1h, 1d) resampled in <15ms
|
|
68
68
|
- **PRIMARY KEY Constraints**: Prevents duplicate data during incremental updates
|
|
@@ -138,7 +138,7 @@ Download Only Missing Months (Raw_Spread + Standard)
|
|
|
138
138
|
↓
|
|
139
139
|
DuckDB Single-File Storage (PRIMARY KEY prevents duplicates)
|
|
140
140
|
↓
|
|
141
|
-
Phase7
|
|
141
|
+
Phase7 13-Column OHLC Generation (v1.2.0 - dual spreads, tick counts, normalized metrics)
|
|
142
142
|
↓
|
|
143
143
|
Query Interface (date ranges, SQL filters, on-demand resampling)
|
|
144
144
|
```
|
|
@@ -150,19 +150,13 @@ Query Interface (date ranges, SQL filters, on-demand resampling)
|
|
|
150
150
|
**Schema**:
|
|
151
151
|
- `raw_spread_ticks` table: Timestamp (PK), Bid, Ask
|
|
152
152
|
- `standard_ticks` table: Timestamp (PK), Bid, Ask
|
|
153
|
-
- `ohlc_1m` table: Phase7
|
|
153
|
+
- `ohlc_1m` table: Phase7 13-column schema (v1.2.0)
|
|
154
154
|
- `metadata` table: Coverage tracking
|
|
155
155
|
|
|
156
|
-
**Phase7
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
4. `Low` - Low price
|
|
161
|
-
5. `Close` - Closing price
|
|
162
|
-
6. `raw_spread_avg` - Average spread from Raw_Spread variant
|
|
163
|
-
7. `standard_spread_avg` - Average spread from Standard variant
|
|
164
|
-
8. `tick_count_raw_spread` - Tick count from Raw_Spread variant
|
|
165
|
-
9. `tick_count_standard` - Tick count from Standard variant
|
|
156
|
+
**Phase7 13-Column OHLC (v1.2.0)**:
|
|
157
|
+
- **Column Definitions**: See [`schema.py`](src/exness_data_preprocess/schema.py) - Single source of truth
|
|
158
|
+
- **Comprehensive Reference**: See [`DATABASE_SCHEMA.md`](docs/DATABASE_SCHEMA.md) - Query examples and usage patterns
|
|
159
|
+
- **Key Features**: BID-only OHLC with dual spreads (Raw_Spread + Standard) and normalized spread metrics
|
|
166
160
|
|
|
167
161
|
### Directory Structure
|
|
168
162
|
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
[](https://pypi.org/project/exness-data-preprocess/)
|
|
8
8
|
[](https://github.com/astral-sh/ruff)
|
|
9
9
|
|
|
10
|
-
Professional forex tick data preprocessing with unified single-file DuckDB storage. Provides incremental updates, dual-variant storage (Raw_Spread + Standard), and Phase7
|
|
10
|
+
Professional forex tick data preprocessing with unified single-file DuckDB storage. Provides incremental updates, dual-variant storage (Raw_Spread + Standard), and Phase7 13-column OHLC schema (v1.2.0) with sub-15ms query performance.
|
|
11
11
|
|
|
12
12
|
## Features
|
|
13
13
|
|
|
14
14
|
- **Unified Single-File Architecture**: One DuckDB file per instrument (eurusd.duckdb)
|
|
15
15
|
- **Incremental Updates**: Automatic gap detection and download only missing months
|
|
16
16
|
- **Dual-Variant Storage**: Raw_Spread (primary) + Standard (reference) in same database
|
|
17
|
-
- **Phase7 OHLC Schema**:
|
|
17
|
+
- **Phase7 OHLC Schema**: 13-column bars (v1.2.0) with dual spreads, tick counts, and normalized metrics
|
|
18
18
|
- **Fast Queries**: Date range queries with sub-15ms performance
|
|
19
19
|
- **On-Demand Resampling**: Any timeframe (5m, 1h, 1d) resampled in <15ms
|
|
20
20
|
- **PRIMARY KEY Constraints**: Prevents duplicate data during incremental updates
|
|
@@ -90,7 +90,7 @@ Download Only Missing Months (Raw_Spread + Standard)
|
|
|
90
90
|
↓
|
|
91
91
|
DuckDB Single-File Storage (PRIMARY KEY prevents duplicates)
|
|
92
92
|
↓
|
|
93
|
-
Phase7
|
|
93
|
+
Phase7 13-Column OHLC Generation (v1.2.0 - dual spreads, tick counts, normalized metrics)
|
|
94
94
|
↓
|
|
95
95
|
Query Interface (date ranges, SQL filters, on-demand resampling)
|
|
96
96
|
```
|
|
@@ -102,19 +102,13 @@ Query Interface (date ranges, SQL filters, on-demand resampling)
|
|
|
102
102
|
**Schema**:
|
|
103
103
|
- `raw_spread_ticks` table: Timestamp (PK), Bid, Ask
|
|
104
104
|
- `standard_ticks` table: Timestamp (PK), Bid, Ask
|
|
105
|
-
- `ohlc_1m` table: Phase7
|
|
105
|
+
- `ohlc_1m` table: Phase7 13-column schema (v1.2.0)
|
|
106
106
|
- `metadata` table: Coverage tracking
|
|
107
107
|
|
|
108
|
-
**Phase7
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
4. `Low` - Low price
|
|
113
|
-
5. `Close` - Closing price
|
|
114
|
-
6. `raw_spread_avg` - Average spread from Raw_Spread variant
|
|
115
|
-
7. `standard_spread_avg` - Average spread from Standard variant
|
|
116
|
-
8. `tick_count_raw_spread` - Tick count from Raw_Spread variant
|
|
117
|
-
9. `tick_count_standard` - Tick count from Standard variant
|
|
108
|
+
**Phase7 13-Column OHLC (v1.2.0)**:
|
|
109
|
+
- **Column Definitions**: See [`schema.py`](src/exness_data_preprocess/schema.py) - Single source of truth
|
|
110
|
+
- **Comprehensive Reference**: See [`DATABASE_SCHEMA.md`](docs/DATABASE_SCHEMA.md) - Query examples and usage patterns
|
|
111
|
+
- **Key Features**: BID-only OHLC with dual spreads (Raw_Spread + Standard) and normalized spread metrics
|
|
118
112
|
|
|
119
113
|
### Directory Structure
|
|
120
114
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to exness-data-preprocess will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### ✨ Features
|
|
10
|
+
|
|
11
|
+
- Implement pydantic v2 models with dual-variant e2e testing - Add Pydantic v2 models (UpdateResult, CoverageInfo) for type-safe API - Update processor to use Pydantic types (PairType, TimeframeType, VariantType) - Remove deprecated api.py module (broken functionality) - Implement comprehensive test suite (48 tests, 100% passing): * test_models.py - Pydantic model validation (13 tests) * test_types.py - Type safety and helpers (15 tests) * test_processor_pydantic.py - Integration tests (6 tests) * test_functional_regression.py - v2.0.0 regression tests (10 tests) - Fix Standard variant downloads (variant="" not "Standard") - Add true end-to-end testing with real Exness downloads - Test data: EURUSD August 2024 (815K Raw_Spread + 877K Standard ticks) - Update documentation and add refactoring status tracking - Coverage: models.py 100%, __init__.py 100%, processor.py 45% BREAKING CHANGE: api.py removed (use ExnessDataProcessor directly)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### 🔧 Continuous Integration
|
|
15
|
+
|
|
16
|
+
- Modernize publish workflow with uv and validation pipeline Update GitHub Actions publish workflow to match gapless-crypto-data standards: Build System: - Replace pip+build with uv for consistency with local development - Add UV_SYSTEM_PYTHON=1 environment variable - Use uv sync --dev for dependency installation Validation Pipeline: - Add file encoding validation (UTF-8/ASCII enforcement) - Add ruff linting and format checking - Add pytest test suite execution before build Security: - Add Sigstore artifact signing for published packages - Maintain PyPI Trusted Publishing (OIDC) authentication Configuration: - Change environment name: release → pypi (semantic clarity) - Change artifact name: dist → python-package-distributions (consistency) - Add verbose output to PyPI publish step Security Note: - Added .token-info.md to .gitignore to prevent accidental token documentation commits - Workflow uses OIDC (no API token needed)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### 🧰 Maintenance
|
|
20
|
+
|
|
21
|
+
- **release**: Bump version 0.1.0 → 0.2.0 [skip ci]
|
|
22
|
+
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# Database Schema Documentation - exness-data-preprocess
|
|
1
|
+
# Database Schema Documentation - exness-data-preprocess v1.2.0
|
|
2
2
|
|
|
3
3
|
**Database Type**: DuckDB (embedded OLAP database)
|
|
4
4
|
**Architecture**: Single-file per instrument (unified multi-year storage)
|
|
5
|
-
**Schema Version**: 2.0
|
|
6
|
-
**Last Updated**: 2025-10-
|
|
5
|
+
**Schema Version**: v1.2.0 (Phase7 13-column OHLC with normalized metrics)
|
|
6
|
+
**Last Updated**: 2025-10-14
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
Each currency pair is stored in a **single DuckDB file** containing:
|
|
13
13
|
- **All historical tick data** from both variants (Raw_Spread + Standard)
|
|
14
|
-
- **Pre-computed 1-minute OHLC bars** with Phase7
|
|
14
|
+
- **Pre-computed 1-minute OHLC bars** with Phase7 13-column schema (includes normalized spread metrics)
|
|
15
15
|
- **Metadata** tracking coverage and update history
|
|
16
16
|
|
|
17
17
|
**File Naming Convention**: `{instrument_lowercase}.duckdb`
|
|
@@ -229,25 +229,23 @@ ORDER BY hour;
|
|
|
229
229
|
|
|
230
230
|
## Table 3: `ohlc_1m`
|
|
231
231
|
|
|
232
|
-
**Purpose**: Pre-computed 1-minute OHLC bars with Phase7
|
|
232
|
+
**Purpose**: Pre-computed 1-minute OHLC bars with Phase7 13-column schema
|
|
233
233
|
|
|
234
234
|
**Use Case**: Primary data source for backtesting and technical analysis
|
|
235
235
|
|
|
236
236
|
**Generation Method**: Aggregated from `raw_spread_ticks` and `standard_ticks` tables
|
|
237
237
|
|
|
238
|
-
### Schema (Phase7
|
|
238
|
+
### Schema (Phase7 13-Column - v1.2.0)
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
| `tick_count_raw_spread` | BIGINT | NULL | Number of ticks from Raw_Spread variant (NULL if no ticks) |
|
|
250
|
-
| `tick_count_standard` | BIGINT | NULL | Number of ticks from Standard variant (NULL if no Standard ticks for that minute) |
|
|
240
|
+
**Column Definitions**: See [`../src/exness_data_preprocess/schema.py`](../src/exness_data_preprocess/schema.py) for complete column definitions with types and descriptions.
|
|
241
|
+
|
|
242
|
+
**Quick Summary**: 13 columns comprising:
|
|
243
|
+
- **Core OHLC** (5): Timestamp, Open, High, Low, Close
|
|
244
|
+
- **Dual Spreads** (2): raw_spread_avg, standard_spread_avg
|
|
245
|
+
- **Dual Tick Counts** (2): tick_count_raw_spread, tick_count_standard
|
|
246
|
+
- **Normalized Metrics** (4): range_per_spread, range_per_tick, body_per_spread, body_per_tick
|
|
247
|
+
|
|
248
|
+
**Schema Version**: v1.2.0
|
|
251
249
|
|
|
252
250
|
### Indexes
|
|
253
251
|
|
|
@@ -258,27 +256,25 @@ ORDER BY hour;
|
|
|
258
256
|
|
|
259
257
|
- **PRIMARY KEY on Timestamp**: Ensures unique 1-minute bars
|
|
260
258
|
- **NOT NULL constraints**: Only on OHLC price columns (Open, High, Low, Close) to ensure price data integrity
|
|
261
|
-
- **NULLABLE columns**:
|
|
259
|
+
- **NULLABLE columns**:
|
|
260
|
+
- Spread and tick count columns (raw_spread_avg, standard_spread_avg, tick_count_raw_spread, tick_count_standard) can be NULL when LEFT JOIN with Standard ticks yields no matches for that minute
|
|
261
|
+
- Normalized metrics (range_per_spread, range_per_tick, body_per_spread, body_per_tick) are NULL when Standard data is missing or when division by zero would occur
|
|
262
262
|
|
|
263
263
|
### Table Comments
|
|
264
264
|
|
|
265
|
-
**Stored in Database**:
|
|
265
|
+
**Stored in Database**: All 13 column comments are automatically added when the database is created, dynamically generated from `schema.py` (see `OHLCSchema.get_column_comment_sqls()`).
|
|
266
266
|
|
|
267
267
|
```sql
|
|
268
268
|
COMMENT ON TABLE ohlc_1m IS
|
|
269
|
-
'Phase7 v1.
|
|
269
|
+
'Phase7 v1.2.0 1-minute OHLC bars (BID-only from Raw_Spread, dual-variant spreads and tick counts, normalized metrics).
|
|
270
270
|
OHLC Source: Raw_Spread BID prices. Spreads: Dual-variant (Raw_Spread + Standard).';
|
|
271
271
|
|
|
272
|
-
--
|
|
272
|
+
-- All 13 column comments (see schema.py for complete definitions)
|
|
273
|
+
-- Examples:
|
|
273
274
|
COMMENT ON COLUMN ohlc_1m.Timestamp IS 'Minute-aligned bar timestamp';
|
|
274
275
|
COMMENT ON COLUMN ohlc_1m.Open IS 'Opening price (first Raw_Spread Bid)';
|
|
275
|
-
COMMENT ON COLUMN ohlc_1m.
|
|
276
|
-
|
|
277
|
-
COMMENT ON COLUMN ohlc_1m.Close IS 'Closing price (last Raw_Spread Bid)';
|
|
278
|
-
COMMENT ON COLUMN ohlc_1m.raw_spread_avg IS 'Average spread from Raw_Spread variant (NULL if no ticks)';
|
|
279
|
-
COMMENT ON COLUMN ohlc_1m.standard_spread_avg IS 'Average spread from Standard variant (NULL if no Standard ticks for that minute)';
|
|
280
|
-
COMMENT ON COLUMN ohlc_1m.tick_count_raw_spread IS 'Number of ticks from Raw_Spread variant (NULL if no ticks)';
|
|
281
|
-
COMMENT ON COLUMN ohlc_1m.tick_count_standard IS 'Number of ticks from Standard variant (NULL if no Standard ticks for that minute)';
|
|
276
|
+
COMMENT ON COLUMN ohlc_1m.range_per_spread IS '(High-Low)/standard_spread_avg - Range normalized by spread (NULL if no Standard ticks)';
|
|
277
|
+
-- ... (see schema.py OHLCSchema.COLUMNS for all 13 column comments)
|
|
282
278
|
```
|
|
283
279
|
|
|
284
280
|
**Retrieve Comments**:
|
|
@@ -301,7 +297,28 @@ SELECT
|
|
|
301
297
|
AVG(r.Ask - r.Bid) as raw_spread_avg,
|
|
302
298
|
AVG(s.Ask - s.Bid) as standard_spread_avg,
|
|
303
299
|
COUNT(r.Timestamp) as tick_count_raw_spread,
|
|
304
|
-
COUNT(s.Timestamp) as tick_count_standard
|
|
300
|
+
COUNT(s.Timestamp) as tick_count_standard,
|
|
301
|
+
-- Normalized spread metrics (v1.2.0) - NULL-safe calculations
|
|
302
|
+
CASE
|
|
303
|
+
WHEN AVG(s.Ask - s.Bid) > 0
|
|
304
|
+
THEN (MAX(r.Bid) - MIN(r.Bid)) / AVG(s.Ask - s.Bid)
|
|
305
|
+
ELSE NULL
|
|
306
|
+
END as range_per_spread,
|
|
307
|
+
CASE
|
|
308
|
+
WHEN COUNT(s.Timestamp) > 0
|
|
309
|
+
THEN (MAX(r.Bid) - MIN(r.Bid)) / COUNT(s.Timestamp)
|
|
310
|
+
ELSE NULL
|
|
311
|
+
END as range_per_tick,
|
|
312
|
+
CASE
|
|
313
|
+
WHEN AVG(s.Ask - s.Bid) > 0
|
|
314
|
+
THEN ABS(LAST(r.Bid ORDER BY r.Timestamp) - FIRST(r.Bid ORDER BY r.Timestamp)) / AVG(s.Ask - s.Bid)
|
|
315
|
+
ELSE NULL
|
|
316
|
+
END as body_per_spread,
|
|
317
|
+
CASE
|
|
318
|
+
WHEN COUNT(s.Timestamp) > 0
|
|
319
|
+
THEN ABS(LAST(r.Bid ORDER BY r.Timestamp) - FIRST(r.Bid ORDER BY r.Timestamp)) / COUNT(s.Timestamp)
|
|
320
|
+
ELSE NULL
|
|
321
|
+
END as body_per_tick
|
|
305
322
|
FROM raw_spread_ticks r
|
|
306
323
|
LEFT JOIN standard_ticks s
|
|
307
324
|
ON DATE_TRUNC('minute', r.Timestamp) = DATE_TRUNC('minute', s.Timestamp)
|
|
@@ -309,6 +326,40 @@ GROUP BY DATE_TRUNC('minute', r.Timestamp)
|
|
|
309
326
|
ORDER BY Timestamp;
|
|
310
327
|
```
|
|
311
328
|
|
|
329
|
+
### Normalized Spread Metrics (v1.2.0+)
|
|
330
|
+
|
|
331
|
+
**Purpose**: Normalize OHLC movements by market activity (spread and tick count) to enable comparison across different market conditions.
|
|
332
|
+
|
|
333
|
+
**Calculations** (NULL-safe to handle missing Standard data):
|
|
334
|
+
|
|
335
|
+
1. **`range_per_spread`** = (High - Low) / standard_spread_avg
|
|
336
|
+
- **Interpretation**: How many spreads wide is the bar's range?
|
|
337
|
+
- **High values**: Large price movement relative to typical spread
|
|
338
|
+
- **Example**: If range = 0.0002 and avg_spread = 0.00004, then range_per_spread = 5.0 spreads
|
|
339
|
+
|
|
340
|
+
2. **`range_per_tick`** = (High - Low) / tick_count_standard
|
|
341
|
+
- **Interpretation**: Average price movement per tick
|
|
342
|
+
- **High values**: High volatility with few ticks (fast moves)
|
|
343
|
+
- **Low values**: Choppy price action with many ticks
|
|
344
|
+
|
|
345
|
+
3. **`body_per_spread`** = abs(Close - Open) / standard_spread_avg
|
|
346
|
+
- **Interpretation**: How many spreads is the directional movement?
|
|
347
|
+
- **High values**: Strong directional movement relative to spread
|
|
348
|
+
- **Low values**: Indecision/ranging behavior
|
|
349
|
+
|
|
350
|
+
4. **`body_per_tick`** = abs(Close - Open) / tick_count_standard
|
|
351
|
+
- **Interpretation**: Directional efficiency (movement per tick)
|
|
352
|
+
- **High values**: Strong directional movement per tick
|
|
353
|
+
- **Low values**: Many ticks but little net movement
|
|
354
|
+
|
|
355
|
+
**NULL Handling**: All normalized metrics are NULL when `standard_spread_avg` or `tick_count_standard` are 0 or NULL (occurs when LEFT JOIN yields no Standard ticks for that minute).
|
|
356
|
+
|
|
357
|
+
**Use Cases**:
|
|
358
|
+
- Identify high-volatility bars independent of absolute price levels
|
|
359
|
+
- Compare market activity across different sessions
|
|
360
|
+
- Filter for significant directional moves
|
|
361
|
+
- Detect ranging vs trending market conditions
|
|
362
|
+
|
|
312
363
|
### Characteristics
|
|
313
364
|
|
|
314
365
|
- **Timeframe**: 1-minute bars (minute-aligned)
|
|
@@ -320,22 +371,29 @@ ORDER BY Timestamp;
|
|
|
320
371
|
### Example Data
|
|
321
372
|
|
|
322
373
|
```
|
|
323
|
-
Timestamp | Open | High | Low | Close | raw_spread_avg | standard_spread_avg | tick_count_raw_spread | tick_count_standard
|
|
324
|
-
|
|
325
|
-
2024-09-01 14:05:00 | 1.10500 | 1.10520 | 1.10495 | 1.10515 | 0.00001 | 0.00004 | 25 | 28
|
|
326
|
-
2024-09-01 14:06:00 | 1.10515 | 1.10525 | 1.10510 | 1.10520 | 0.00001 | 0.00004 | 23 | 26
|
|
327
|
-
2024-09-01 14:07:00 | 1.10520 | 1.10530 | 1.10515 | 1.10525 | 0.00001 | 0.00004 | 27 | 30
|
|
374
|
+
Timestamp | Open | High | Low | Close | raw_spread_avg | standard_spread_avg | tick_count_raw_spread | tick_count_standard | range_per_spread | range_per_tick | body_per_spread | body_per_tick
|
|
375
|
+
---------------------|---------|---------|---------|---------|----------------|---------------------|-----------------------|---------------------|------------------|----------------|-----------------|---------------
|
|
376
|
+
2024-09-01 14:05:00 | 1.10500 | 1.10520 | 1.10495 | 1.10515 | 0.00001 | 0.00004 | 25 | 28 | 6.25 | 0.00089 | 3.75 | 0.00054
|
|
377
|
+
2024-09-01 14:06:00 | 1.10515 | 1.10525 | 1.10510 | 1.10520 | 0.00001 | 0.00004 | 23 | 26 | 3.75 | 0.00058 | 1.25 | 0.00019
|
|
378
|
+
2024-09-01 14:07:00 | 1.10520 | 1.10530 | 1.10515 | 1.10525 | 0.00001 | 0.00004 | 27 | 30 | 3.75 | 0.00050 | 1.25 | 0.00017
|
|
328
379
|
```
|
|
329
380
|
|
|
381
|
+
**Notes**:
|
|
382
|
+
- All 13 columns shown including normalized metrics (v1.2.0+)
|
|
383
|
+
- `range_per_spread = (High-Low) / standard_spread_avg` (e.g., 0.00025 / 0.00004 = 6.25 spreads)
|
|
384
|
+
- `range_per_tick = (High-Low) / tick_count_standard` (e.g., 0.00025 / 28 = 0.00089)
|
|
385
|
+
- `body_per_spread = abs(Close-Open) / standard_spread_avg` (e.g., 0.00015 / 0.00004 = 3.75 spreads)
|
|
386
|
+
- `body_per_tick = abs(Close-Open) / tick_count_standard` (e.g., 0.00015 / 28 = 0.00054)
|
|
387
|
+
|
|
330
388
|
### Query Examples
|
|
331
389
|
|
|
332
390
|
```sql
|
|
333
|
-
-- Get 1-minute bars for September 2024
|
|
391
|
+
-- Get 1-minute bars for September 2024 (all 13 columns)
|
|
334
392
|
SELECT * FROM ohlc_1m
|
|
335
393
|
WHERE Timestamp >= '2024-09-01' AND Timestamp < '2024-10-01'
|
|
336
394
|
ORDER BY Timestamp;
|
|
337
395
|
|
|
338
|
-
-- Resample to 1-hour bars
|
|
396
|
+
-- Resample to 1-hour bars (all 13 columns)
|
|
339
397
|
SELECT
|
|
340
398
|
DATE_TRUNC('hour', Timestamp) as hour,
|
|
341
399
|
FIRST(Open ORDER BY Timestamp) as Open,
|
|
@@ -345,21 +403,41 @@ SELECT
|
|
|
345
403
|
AVG(raw_spread_avg) as raw_spread_avg,
|
|
346
404
|
AVG(standard_spread_avg) as standard_spread_avg,
|
|
347
405
|
SUM(tick_count_raw_spread) as tick_count_raw_spread,
|
|
348
|
-
SUM(tick_count_standard) as tick_count_standard
|
|
406
|
+
SUM(tick_count_standard) as tick_count_standard,
|
|
407
|
+
AVG(range_per_spread) as range_per_spread,
|
|
408
|
+
AVG(range_per_tick) as range_per_tick,
|
|
409
|
+
AVG(body_per_spread) as body_per_spread,
|
|
410
|
+
AVG(body_per_tick) as body_per_tick
|
|
349
411
|
FROM ohlc_1m
|
|
350
412
|
WHERE Timestamp >= '2024-09-01'
|
|
351
413
|
GROUP BY hour
|
|
352
414
|
ORDER BY hour;
|
|
353
415
|
|
|
354
|
-
--
|
|
416
|
+
-- Filter high-volatility bars (range > 5 spreads)
|
|
417
|
+
SELECT * FROM ohlc_1m
|
|
418
|
+
WHERE Timestamp >= '2024-09-01'
|
|
419
|
+
AND range_per_spread > 5.0
|
|
420
|
+
ORDER BY range_per_spread DESC
|
|
421
|
+
LIMIT 10;
|
|
422
|
+
|
|
423
|
+
-- Find strong directional bars (body > 3 spreads, high body efficiency)
|
|
424
|
+
SELECT * FROM ohlc_1m
|
|
425
|
+
WHERE Timestamp >= '2024-09-01'
|
|
426
|
+
AND body_per_spread > 3.0
|
|
427
|
+
AND body_per_tick > 0.0005
|
|
428
|
+
ORDER BY body_per_spread DESC
|
|
429
|
+
LIMIT 10;
|
|
430
|
+
|
|
431
|
+
-- Compare normalized metrics across time periods
|
|
355
432
|
SELECT
|
|
356
433
|
DATE_TRUNC('day', Timestamp) as day,
|
|
357
434
|
COUNT(*) as bar_count,
|
|
358
|
-
|
|
359
|
-
|
|
435
|
+
AVG(range_per_spread) as avg_range_per_spread,
|
|
436
|
+
AVG(body_per_spread) as avg_body_per_spread,
|
|
360
437
|
AVG(raw_spread_avg) * 10000 as avg_raw_spread_pips
|
|
361
438
|
FROM ohlc_1m
|
|
362
439
|
WHERE Timestamp >= '2024-09-01'
|
|
440
|
+
AND range_per_spread IS NOT NULL -- Ensure Standard data exists
|
|
363
441
|
GROUP BY day
|
|
364
442
|
ORDER BY day;
|
|
365
443
|
```
|
|
@@ -703,10 +781,22 @@ print(f"Months added: {result['months_added']}")
|
|
|
703
781
|
|
|
704
782
|
## Version History
|
|
705
783
|
|
|
706
|
-
###
|
|
784
|
+
### v1.2.0 (2025-10-14) - Current
|
|
785
|
+
|
|
786
|
+
- **Added**: 4 normalized spread metrics to OHLC schema (range_per_spread, range_per_tick, body_per_spread, body_per_tick)
|
|
787
|
+
- **Refactored**: Centralized schema definition in `schema.py` module
|
|
788
|
+
- **OHLC Schema**: 13 columns (Phase7 with normalized metrics)
|
|
789
|
+
- **Architecture**: Single source of truth for all schema definitions
|
|
790
|
+
- **Breaking Change**: None - additive only (existing 9 columns unchanged)
|
|
791
|
+
|
|
792
|
+
### v1.1.0 (2025-10-12) - Phase7 Initial
|
|
793
|
+
|
|
794
|
+
- **Added**: Phase7 9-column OHLC schema (dual spreads + dual tick counts)
|
|
795
|
+
- **OHLC Schema**: BID-only OHLC from Raw_Spread with dual-variant metrics
|
|
796
|
+
|
|
797
|
+
### v2.0.0 (2025-10-12) - Unified Architecture
|
|
707
798
|
|
|
708
799
|
- **Change**: Single-file per instrument (not monthly files)
|
|
709
|
-
- **Added**: Phase7 9-column OHLC schema
|
|
710
800
|
- **Added**: Dual-variant storage (Raw_Spread + Standard)
|
|
711
801
|
- **Added**: PRIMARY KEY constraints for duplicate prevention
|
|
712
802
|
- **Added**: Metadata table for coverage tracking
|
|
@@ -723,6 +813,7 @@ print(f"Months added: {result['months_added']}")
|
|
|
723
813
|
|
|
724
814
|
## Related Documentation
|
|
725
815
|
|
|
816
|
+
- **Schema Definition**: [`../src/exness_data_preprocess/schema.py`](../src/exness_data_preprocess/schema.py) - Single source of truth for all schema definitions
|
|
726
817
|
- **Architecture Plan**: [`UNIFIED_DUCKDB_PLAN_v2.md`](UNIFIED_DUCKDB_PLAN_v2.md)
|
|
727
818
|
- **Phase7 OHLC Spec**: [`research/eurusd-zero-spread-deviations/data/plan/phase7_bid_ohlc_construction_v1.1.0.md`](research/eurusd-zero-spread-deviations/data/plan/phase7_bid_ohlc_construction_v1.1.0.md)
|
|
728
819
|
- **Data Sources**: [`EXNESS_DATA_SOURCES.md`](EXNESS_DATA_SOURCES.md)
|
|
@@ -731,6 +822,6 @@ print(f"Months added: {result['months_added']}")
|
|
|
731
822
|
|
|
732
823
|
---
|
|
733
824
|
|
|
734
|
-
**Last Updated**: 2025-10-
|
|
825
|
+
**Last Updated**: 2025-10-14
|
|
735
826
|
**Maintainer**: Terry Li <terry@eonlabs.com>
|
|
736
|
-
**Schema Version**: 2.0
|
|
827
|
+
**Schema Version**: v1.2.0 (Phase7 13-column OHLC with normalized metrics)
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
- ✅ **Incremental updates**: Automatic gap detection, download only missing months
|
|
25
25
|
- ✅ **PRIMARY KEY constraints**: Prevents duplicates during incremental updates
|
|
26
26
|
- ✅ **Performance**: <15ms queries for all timeframes
|
|
27
|
-
- ✅ **Phase7 Compliant**: Dual-variant
|
|
27
|
+
- ✅ **Phase7 Compliant**: Dual-variant 13-column (v1.2.0) schema
|
|
28
28
|
- ✅ **SQL on Ticks**: Direct tick analysis with date range filtering
|
|
29
29
|
- ✅ **Scalability**: 2.08 GB for 13 months, ~4.8 GB for 3 years
|
|
30
30
|
|
|
@@ -98,13 +98,9 @@ curl -s "https://ticks.ex2archive.com/ticks/" | jq -r '.[] | .name' | grep -i "E
|
|
|
98
98
|
- [`04-discoveries-and-plan-evolution.md`](research/eurusd-zero-spread-deviations/04-discoveries-and-plan-evolution.md) - Version-tracked findings
|
|
99
99
|
- [`data/plan/phase7_bid_ohlc_construction_v1.1.0.md`](research/eurusd-zero-spread-deviations/data/plan/phase7_bid_ohlc_construction_v1.1.0.md) - OHLC specification
|
|
100
100
|
|
|
101
|
-
**Phase7 Schema** (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Timestamp, Open, High, Low, Close,
|
|
105
|
-
raw_spread_avg, standard_spread_avg,
|
|
106
|
-
tick_count_raw_spread, tick_count_standard
|
|
107
|
-
```
|
|
101
|
+
**Phase7 Schema** (13 columns, v1.2.0):
|
|
102
|
+
- **Definition**: [`../src/exness_data_preprocess/schema.py`](../src/exness_data_preprocess/schema.py)
|
|
103
|
+
- **Comprehensive Guide**: [`DATABASE_SCHEMA.md`](DATABASE_SCHEMA.md)
|
|
108
104
|
|
|
109
105
|
---
|
|
110
106
|
|
{exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/UNIFIED_DUCKDB_PLAN_v2.md
RENAMED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
- ✅ **Maximum Simplicity**: ONE file = ONE instrument (eurusd.duckdb, xauusd.duckdb)
|
|
23
23
|
- ✅ **Continuous Time Series**: No gaps between months, seamless queries across years
|
|
24
24
|
- ✅ **Incremental Updates**: Check for new data on Exness, append automatically
|
|
25
|
-
- ✅ **Phase7 Compliant**: Dual-variant
|
|
25
|
+
- ✅ **Phase7 Compliant**: Dual-variant 13-column (v1.2.0) schema
|
|
26
26
|
- ✅ **Scalable**: ~400-500 MB per instrument for 3 years (manageable)
|
|
27
27
|
- ✅ **Simple Backups**: Copy one file to backup entire instrument history
|
|
28
28
|
|
|
@@ -587,7 +587,7 @@ Based on real data validation:
|
|
|
587
587
|
- ✅ **Multi-year storage** (minimum 3 years)
|
|
588
588
|
- ✅ **Incremental updates** (automatic gap detection)
|
|
589
589
|
- ✅ **Duplicate prevention** (PRIMARY KEY constraints)
|
|
590
|
-
- ✅ **Phase7 compliance** (
|
|
590
|
+
- ✅ **Phase7 compliance** (13-column (v1.2.0) OHLC schema)
|
|
591
591
|
- ✅ **Time-range queries** (start_date/end_date support)
|
|
592
592
|
|
|
593
593
|
### Performance
|
|
@@ -11,7 +11,7 @@ Architecture v2.0.0:
|
|
|
11
11
|
- One DuckDB file per instrument (eurusd.duckdb, not monthly files)
|
|
12
12
|
- Dual-variant storage (Raw_Spread + Standard) for Phase7
|
|
13
13
|
- Automatic gap detection and incremental updates
|
|
14
|
-
-
|
|
14
|
+
- 13-column (v1.2.0) OHLC schema with dual spreads
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
from pathlib import Path
|
|
@@ -33,7 +33,7 @@ print("=" * 80)
|
|
|
33
33
|
# 1. Create eurusd.duckdb (single file)
|
|
34
34
|
# 2. Download missing months from start_date to present
|
|
35
35
|
# 3. Store dual variants (Raw_Spread + Standard)
|
|
36
|
-
# 4. Generate Phase7
|
|
36
|
+
# 4. Generate Phase7 13-column (v1.2.0) OHLC
|
|
37
37
|
processor = edp.ExnessDataProcessor(base_dir=BASE_DIR)
|
|
38
38
|
|
|
39
39
|
result = processor.update_data(
|
|
@@ -206,7 +206,7 @@ print("\nKey Features of v2.0.0:")
|
|
|
206
206
|
print(" ✅ Single file per instrument (eurusd.duckdb)")
|
|
207
207
|
print(" ✅ Automatic incremental updates")
|
|
208
208
|
print(" ✅ Dual-variant storage (Phase7 compliant)")
|
|
209
|
-
print(" ✅
|
|
209
|
+
print(" ✅ 13-column (v1.2.0) OHLC schema")
|
|
210
210
|
print(" ✅ Date range queries")
|
|
211
211
|
print(" ✅ On-demand resampling (<15ms)")
|
|
212
212
|
print(" ✅ SQL filters on ticks")
|