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.
Files changed (90) hide show
  1. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.cz.toml +1 -1
  2. exness_data_preprocess-0.3.0/CHANGELOG.md +53 -0
  3. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/CLAUDE.md +14 -11
  4. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/PKG-INFO +9 -15
  5. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/README.md +8 -14
  6. exness_data_preprocess-0.3.0/RELEASE_NOTES.md +22 -0
  7. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/DATABASE_SCHEMA.md +135 -44
  8. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/README.md +4 -8
  9. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/UNIFIED_DUCKDB_PLAN_v2.md +2 -2
  10. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/examples/basic_usage.py +3 -3
  11. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/examples/batch_processing.py +1 -1
  12. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/pyproject.toml +1 -1
  13. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/src/exness_data_preprocess/__init__.py +1 -1
  14. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/src/exness_data_preprocess/processor.py +46 -180
  15. exness_data_preprocess-0.3.0/src/exness_data_preprocess/schema.py +256 -0
  16. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/README.md +1 -1
  17. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/test_functional_regression.py +13 -17
  18. exness_data_preprocess-0.2.0/CHANGELOG.md +0 -35
  19. exness_data_preprocess-0.2.0/examples/add_schema_comments.py +0 -54
  20. exness_data_preprocess-0.2.0/tests/PYDANTIC_TEST_STRATEGY.md +0 -430
  21. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.github/workflows/ci.yml +0 -0
  22. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.github/workflows/pre-commit-autoupdate.yml +0 -0
  23. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.github/workflows/publish.yml +0 -0
  24. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.gitignore +0 -0
  25. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/.pre-commit-config.yaml +0 -0
  26. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/AUTHORS.md +0 -0
  27. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/CONTRIBUTING.md +0 -0
  28. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/GITHUB_PYPI_SETUP.md +0 -0
  29. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/LICENSE +0 -0
  30. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/PYDANTIC_REFACTORING_PLAN.md +0 -0
  31. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/PYDANTIC_REFACTORING_STATUS.md +0 -0
  32. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/cliff.toml +0 -0
  33. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/EXNESS_DATA_SOURCES.md +0 -0
  34. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/archive/UNIFIED_DUCKDB_PLAN_v1.0.0_LEGACY.md +0 -0
  35. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/implementation-plan.yaml +0 -0
  36. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/planning-index.yaml +0 -0
  37. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/compression-benchmarks/README.md +0 -0
  38. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/compression-benchmarks/test_all_compression_methods.py +0 -0
  39. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/compression-benchmarks/test_delta_encoding_properly.py +0 -0
  40. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/compression-benchmarks/test_parquet_compression_methods.py +0 -0
  41. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/01-methodology.md +0 -0
  42. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/02-mode-truncated-analysis.md +0 -0
  43. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/03-hurdle-model-analysis.md +0 -0
  44. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/04-temporal-patterns.md +0 -0
  45. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/05-final-recommendations.md +0 -0
  46. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/README.md +0 -0
  47. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/scripts/reproduction_guide.md +0 -0
  48. {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
  49. {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
  50. {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
  51. {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
  52. {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
  53. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/visualizations/temporal/concentration_curve.png +0 -0
  54. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-spread-analysis/visualizations/temporal/percentile_analysis.png +0 -0
  55. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/01-methodology.md +0 -0
  56. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/02-baseline-sep2024.md +0 -0
  57. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/03-multiperiod-validation.md +0 -0
  58. {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
  59. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/README.md +0 -0
  60. {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
  61. {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
  62. {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
  63. {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
  64. {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
  65. {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
  66. {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
  67. {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
  68. {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
  69. {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
  70. {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
  71. {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
  72. {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
  73. {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
  74. {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
  75. {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
  76. {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
  77. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/docs/research/eurusd-zero-spread-deviations/scripts/reproduction_guide.md +0 -0
  78. {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
  79. {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
  80. {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
  81. {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
  82. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/src/exness_data_preprocess/cli.py +0 -0
  83. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/src/exness_data_preprocess/models.py +0 -0
  84. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/__init__.py +0 -0
  85. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/archive/E2E_TESTING_PLAN_v1.0.0.md +0 -0
  86. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/conftest.py +0 -0
  87. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/test_basic.py +0 -0
  88. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/test_models.py +0 -0
  89. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/test_processor_pydantic.py +0 -0
  90. {exness_data_preprocess-0.2.0 → exness_data_preprocess-0.3.0}/tests/test_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  [tool.commitizen]
2
2
  name = "cz_conventional_commits"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  tag_format = "v$major.$minor.$patch"
5
5
  update_changelog_on_bump = true
6
6
  version_files = [
@@ -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 9-column OHLC generation with LEFT JOIN
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 9-Column OHLC Schema**:
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
- - Columns: `Timestamp` (PK), `Open`, `High`, `Low`, `Close`, `raw_spread_avg`, `standard_spread_avg`, `tick_count_raw_spread`, `tick_count_standard`
142
- - Phase7 9-column schema
143
- - Generated from Raw_Spread BID prices with dual-variant spreads
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 9-column OHLC**: Dual spreads + dual tick counts
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 9-Column OHLC Schema (✅ Implemented)
210
+ ### Phase7 13-Column OHLC Schema v1.2.0 (✅ Implemented)
210
211
 
211
- **Decision**: Dual-variant BID-only OHLC with 9 columns capturing both Raw_Spread and Standard characteristics
212
+ **Decision**: Dual-variant BID-only OHLC with 13 columns capturing both Raw_Spread and Standard characteristics plus normalized metrics
212
213
 
213
- **Schema**: `Timestamp | Open | High | Low | Close | raw_spread_avg | standard_spread_avg | tick_count_raw_spread | tick_count_standard`
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 9-column OHLC** - Dual spreads + dual tick counts
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 9-column schema
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.2.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
  [![Downloads](https://img.shields.io/pypi/dm/exness-data-preprocess.svg)](https://pypi.org/project/exness-data-preprocess/)
56
56
  [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](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 9-column OHLC schema with sub-15ms query performance.
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**: 9-column bars with dual spreads and dual tick counts
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 9-Column OHLC Generation (dual spreads + dual tick counts)
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 9-column schema
153
+ - `ohlc_1m` table: Phase7 13-column schema (v1.2.0)
154
154
  - `metadata` table: Coverage tracking
155
155
 
156
- **Phase7 9-Column OHLC**:
157
- 1. `Timestamp` - Bar timestamp
158
- 2. `Open` - Opening price
159
- 3. `High` - High price
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
  [![Downloads](https://img.shields.io/pypi/dm/exness-data-preprocess.svg)](https://pypi.org/project/exness-data-preprocess/)
8
8
  [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](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 9-column OHLC schema with sub-15ms query performance.
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**: 9-column bars with dual spreads and dual tick counts
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 9-Column OHLC Generation (dual spreads + dual tick counts)
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 9-column schema
105
+ - `ohlc_1m` table: Phase7 13-column schema (v1.2.0)
106
106
  - `metadata` table: Coverage tracking
107
107
 
108
- **Phase7 9-Column OHLC**:
109
- 1. `Timestamp` - Bar timestamp
110
- 2. `Open` - Opening price
111
- 3. `High` - High price
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 v2.0.0
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.0 (Phase7 9-column OHLC)
6
- **Last Updated**: 2025-10-12
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 9-column schema
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 9-column schema
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 9-Column)
238
+ ### Schema (Phase7 13-Column - v1.2.0)
239
239
 
240
- | Column | Type | Constraints | Description |
241
- |--------------------------|----------------------------|---------------|-------------------------------------------------------|
242
- | `Timestamp` | TIMESTAMP WITH TIME ZONE | PRIMARY KEY | Minute-aligned bar timestamp |
243
- | `Open` | DOUBLE | NOT NULL | Opening price (first Raw_Spread Bid) |
244
- | `High` | DOUBLE | NOT NULL | High price (max Raw_Spread Bid) |
245
- | `Low` | DOUBLE | NOT NULL | Low price (min Raw_Spread Bid) |
246
- | `Close` | DOUBLE | NOT NULL | Closing price (last Raw_Spread Bid) |
247
- | `raw_spread_avg` | DOUBLE | NULL | Average spread from Raw_Spread variant (NULL if no ticks) |
248
- | `standard_spread_avg` | DOUBLE | NULL | Average spread from Standard variant (NULL if no Standard ticks for that minute) |
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**: 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
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**: The following comments are automatically added when the database is created (see `processor.py` lines 201-215).
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.1.0 1-minute OHLC bars (BID-only from Raw_Spread, dual-variant spreads and tick counts).
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
- -- Column comments
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.High IS 'High price (max Raw_Spread Bid)';
276
- COMMENT ON COLUMN ohlc_1m.Low IS 'Low price (min Raw_Spread Bid)';
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
- -- Get daily statistics
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
- MIN(Low) as day_low,
359
- MAX(High) as day_high,
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
- ### v2.0.0 (2025-10-12)
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-12
825
+ **Last Updated**: 2025-10-14
735
826
  **Maintainer**: Terry Li <terry@eonlabs.com>
736
- **Schema Version**: 2.0.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 9-column schema
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** (9 columns):
102
-
103
- ```sql
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
 
@@ -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 9-column schema
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** (9-column OHLC schema)
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
- - 9-column OHLC schema with dual spreads
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 9-column OHLC
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(" ✅ 9-column OHLC schema")
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")