Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

49 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Crypto Data Lake

A production-ready, local-first cryptocurrency market data pipeline for Windows that collects, transforms, validates, and stores real-time trading data from Binance.

Architecture

Data Flow: Exchange β†’ Collector β†’ Transformer β†’ Parquet Storage β†’ DuckDB β†’ Analysis

  • Collector: Real-time WebSocket connection to Binance for trade and quote data
  • Transformer: Converts raw events into 1-second OHLCV bars with L1 quotes
  • Validator: Ensures data quality, continuity, and schema integrity
  • Compactor: Merges partitioned files into daily Parquet archives with hash verification

Features

  • βœ… Live streaming data collection (trades + top-of-book quotes)
  • βœ… Historical backfill via Binance REST API (up to 6 months)
  • βœ… Unified orchestrator with auto-transform scheduling
  • βœ… Macro/FX data collection (1-minute from yfinance)
  • βœ… Auto-reconnect with exponential backoff
  • βœ… Rotating JSONL file output (60-second intervals)
  • βœ… 1-second OHLCV aggregation with VWAP and spread
  • βœ… Partitioned Parquet storage (year/month/day)
  • βœ… Data validation (gaps, duplicates, schema checks)
  • βœ… Daily file compaction with SHA256 hashing
  • βœ… Comprehensive logging with 14-day rotation
  • βœ… Full test coverage with pytest

Tech Stack

  • Python 3.12 (compatible with 3.11+)
  • aiohttp + websockets: Async WebSocket client
  • pandas + pyarrow: Data transformation and Parquet I/O
  • duckdb: Fast analytical queries on Parquet files
  • loguru: Structured logging
  • pytest: Unit and integration testing

Quick Start

1. Install Dependencies

python -m venv venv
venv\Scripts\activate  # On Windows
# source venv/bin/activate  # On Linux/Mac
pip install -r requirements.txt

2. Configure

# Copy the example config
cp config.yml.example config.yml

Edit config.yml to set:

  • base_path: Local data lake directory (default: ./data - relative, works anywhere)
  • symbols: Trading pairs to track (13 crypto pairs pre-configured)
  • macro_minute.tickers: Stock/FX/commodity tickers (17 pre-configured)

3. Run Pipeline

# Collect live data (run continuously)
python main.py --mode collector

# Transform raw data to Parquet (run every 5 min or on-demand)
python main.py --mode transformer --date 2025-10-20

# Validate data quality
python main.py --mode validate --date 2025-10-20

# Compact daily files
python main.py --mode compact --date 2025-10-20

Project Structure

crypto-lake/
β”œβ”€β”€ main.py                 # CLI entrypoint
β”œβ”€β”€ config.yml              # Runtime configuration
β”œβ”€β”€ collector/
β”‚   └── collector.py        # WebSocket data collection
β”œβ”€β”€ transformer/
β”‚   └── transformer.py      # Raw β†’ Parquet transformation
β”œβ”€β”€ storage/
β”‚   └── compactor.py        # Daily file compaction
β”œβ”€β”€ tools/
β”‚   β”œβ”€β”€ common.py           # Shared utilities
β”‚   β”œβ”€β”€ validator.py        # Data quality checks
β”‚   β”œβ”€β”€ backfill.py         # Historical data via REST API
β”‚   └── scheduler.py        # Windows Task Scheduler integration
β”œβ”€β”€ sql/
β”‚   └── query_duckdb.sql    # Example analytical queries
└── tests/
    β”œβ”€β”€ test_collector.py   # Collector unit tests
    └── test_transformer.py # Transformer unit tests

Data Output

Raw Data (JSONL)

D:/CryptoDataLake/raw/binance/SOLUSDT/2025-10-20/part_001.jsonl

Each line: {symbol, ts_event, ts_recv, price, qty, side, bid, ask, stream}

Transformed Data (Parquet)

D:/CryptoDataLake/parquet/binance/SOLUSDT/year=2025/month=10/day=20/*.parquet

Columns: symbol, window_start, open, high, low, close, volume_base, volume_quote, trade_count, vwap, bid, ask, spread

Compacted Data (Daily)

D:/CryptoDataLake/parquet/binance/SOLUSDT/2025-10-20.parquet

Testing

Run the full test suite:

pytest -v

Current Status: βœ… All 4 tests passing

  • test_parse_event_trade_and_quote: Binance message parsing
  • test_rotating_writer: JSONL file rotation logic
  • test_transformer_integration: End-to-end pipeline test
  • test_aggregate_bars_1s_basic: OHLCV aggregation correctness

Validation Results (Latest)

Date: 2025-10-20 Data Quality:

  • βœ… 321 rows collected (SOLUSDT)
  • βœ… 0 missing seconds
  • βœ… 0 duplicates
  • βœ… All schema columns present
  • βœ… SHA256 hash: 49d27af23d4e64ddea78a63a30ed32064d742a1e9fcc2320fb29c9c4f5816458

Scheduling (Windows)

Set up automated pipeline execution:

python tools/scheduler.py --action setup_all

This creates Windows Task Scheduler jobs:

  • Collector: Runs on system startup
  • Transformer: Runs every 5 minutes
  • Compactor: Runs daily at 1:30 AM

Unified Orchestrator (Recommended)

The orchestrator is a single long-running process that manages both crypto and macro data collection, with built-in health monitoring.

What It Does

The orchestrator runs:

  1. Binance WebSocket Collector - Continuous crypto streaming (19 symbols)
  2. Macro/FX Data Fetcher - Scheduled 1-minute data collection every 15 minutes (SPY, UUP, ES=F, FX pairs)
  3. Transformer - Automatic transformation of raw data to Parquet every 60 minutes
  4. One-Time Startup Backfill - Fetches 7 days of historical macro data on launch
  5. Health Monitoring - Writes metrics every 60 seconds to JSON and Markdown files

How to Run

Using config.yml (recommended):

python main.py --mode orchestrate

With CLI arguments:

python main.py --mode orchestrate \
  --macro_tickers SPY,UUP,ES=F,EURUSD=X,GBPUSD=X,USDJPY=X \
  --macro_interval_min 15 \
  --macro_lookback_startup_days 7 \
  --macro_runtime_lookback_days 1

Configuration

Edit config.yml to set default orchestrator parameters:

transformer:
  resample_interval_sec: 1
  parquet_compression: "snappy"
  schedule_minutes: 60  # Run transformer every 60 minutes (0 to disable)

macro_minute:
  tickers:
    - SPY          # S&P 500 ETF
    - UUP          # US Dollar Index Fund
    - ES=F         # E-mini S&P 500 Futures
    - EURUSD=X     # EUR/USD
    - GBPUSD=X     # GBP/USD
    - USDJPY=X     # USD/JPY
    - USDCAD=X     # USD/CAD
    - USDCHF=X     # USD/CHF
  schedule_minutes: 15            # Fetch macro data every 15 minutes
  startup_backfill_days: 7        # One-time backfill on startup
  runtime_lookback_days: 1        # Fetch last 1 day on each run

Health Monitoring

The orchestrator writes health metrics to two files:

JSON Heartbeat (D:/CryptoDataLake/logs/health/heartbeat.json):

{
  "ts_utc": "2025-10-22T12:00:00Z",
  "collector": {
    "status": "running",
    "last_latency_p50_ms": 10.5,
    "last_latency_p95_ms": 25.3,
    "last_seen_ts": "2025-10-22T11:59:00Z"
  },
  "macro_minute": {
    "status": "idle",
    "last_run_start": "2025-10-22T11:45:00Z",
    "last_run_end": "2025-10-22T11:46:00Z",
    "last_run_rows_written": 1500,
    "last_error": null
  },
  "transformer": {
    "status": "idle",
    "last_run_start": "2025-10-22T12:00:00Z",
    "last_run_end": "2025-10-22T12:02:00Z",
    "last_error": null
  },
  "files": {
    "raw_count_today": 100,
    "parquet_1s_rows_today": 50000,
    "macro_min_rows_today": 2000
  }
}

Markdown Report (D:/CryptoDataLake/reports/health.md):

  • Overall system status (HEALTHY / ERROR / STOPPED)
  • Real-time crypto collector metrics
  • Macro/FX fetcher metrics
  • Today's data volume statistics

How to Stop

Press Ctrl+C to gracefully stop the orchestrator. All threads will shut down cleanly within 10 seconds, and a final heartbeat will be written.

Data Collection Schedule

  • Crypto Data: Continuous streaming, files rotate every 60 seconds
  • Macro Data:
    • Startup: 7-day backfill (happens once when orchestrator starts)
    • Runtime: Fetches every 15 minutes (or as configured)
    • Each run: Fetches last 1 day with automatic deduplication

Notes

  • Macro data is fetched via yfinance REST API (1-minute granularity, max 7 days)
  • Crypto data is collected via Binance WebSocket (real-time streaming)
  • Both data types are stored in partitioned Parquet format (year/month/day)
  • Health metrics update every 60 seconds (configurable in code)
  • All timestamps are UTC timezone-aware

Testing Mode

Testing mode allows you to validate the entire Crypto Lake system in minutes instead of hours by compressing time-based intervals while maintaining all production logic, data integrity checks, and warnings.

When to Use Testing Mode

  • πŸ” Validate end-to-end pipeline after code changes
  • πŸš€ Quick smoke test before production deployment
  • πŸ§ͺ Development and debugging with accelerated cycles
  • πŸ“Š Verify all components work together correctly

How Testing Mode Works

Testing mode applies time compression to scheduled operations:

Operation Production Testing Speedup
Transform Interval 60 min 2 min 30x faster
Macro Fetch Interval 15 min 1 min 15x faster
Macro Startup Backfill 7 days 1 day 7x less data
Backfill Lookback 180 days 3 days 60x less data

What Stays the Same:

  • βœ… All business logic and data processing
  • βœ… All validation rules and integrity checks
  • βœ… All warnings and error handling
  • βœ… Full 19-symbol collection (no shortcuts)
  • βœ… Real-time WebSocket streaming
  • βœ… Parquet schema and partitioning

What Changes:

  • πŸ“ Output directory: D:/CryptoDataLake/test/ (isolated)
  • πŸ“ Log files: D:/CryptoDataLake/test/logs/test/ (isolated)
  • ⏱️ Intervals compressed for speed
  • 🏷️ All health reports labeled "MODE: TEST"
  • ⚑ Forced initial transform after 2-minute warmup

Running in Testing Mode

Method 1: Shorthand flag

python main.py --mode test

Method 2: Orchestrate with testing flag

python main.py --mode orchestrate --testing

Method 3: Enable in config.yml

testing:
  enabled: true  # Activates testing mode globally

Test Workflow Example

A typical 5-minute test run validates:

  1. Startup (0:00-0:30): Orchestrator initializes, starts WebSocket collector
  2. Macro Backfill (0:30-1:00): One-time 1-day macro data fetch
  3. Forced Transform (2:00): Guaranteed transform cycle runs
  4. Scheduled Transform (4:00): Second transform cycle completes
  5. Macro Fetch (3:00): Scheduled macro data fetch
  6. Shutdown (5:00): User presses Ctrl+C, test summary prints

Test Summary Output

When you stop the orchestrator with Ctrl+C, you'll see:

================================================================================
TEST COMPLETE
================================================================================
Duration: 300.5s
Transform cycles run: 2
Macro fetches: 2
Files written: 16
Warnings: None
================================================================================

Configuration

All testing parameters are defined in config.yml:

testing:
  enabled: false                  # Can be overridden by CLI flags
  transform_interval_min: 2       # Transform every 2 minutes
  macro_interval_min: 1           # Fetch macro data every 1 minute
  macro_lookback_startup_days: 1  # Backfill 1 day on startup
  macro_runtime_lookback_days: 1  # Fetch 1 day on each run
  backfill_days: 3                # Backfill 3 days for testing
  base_path: "D:/CryptoDataLake/test"  # Isolated test directory

Validating Test Results

After running in test mode, validate the output:

# Run final validation on test directory
python -m tools.final_validation

# Check health report
cat D:/CryptoDataLake/test/reports/health.md

# Inspect test logs
cat D:/CryptoDataLake/test/logs/test/orchestrator.log

The validation script automatically detects test mode from config.yml and validates the test directory.

Notes

  • Isolation: Test mode uses completely separate directories - production data is never touched
  • No Shortcuts: All 19 symbols are collected, all validations run, all warnings fire
  • Realistic Testing: Only time intervals are compressed - everything else is production-grade
  • Clean State: Delete D:/CryptoDataLake/test/ between test runs for clean testing
  • Performance: Expect ~50-100 MB of test data after a 5-minute run

Test GUI (Streamlit)

Launch the interactive dashboard for data visualization and quality monitoring:

streamlit run gui/app.py

Features:

  • Real-time Candlestick Charts: View OHLC data across multiple timeframes (1s, 1m, 5m, 15m, 1h)
  • Volume Analysis: Secondary volume bars color-coded by price direction
  • Spread Visualization: Track bid-ask spread over time
  • Data Quality Metrics: Monitor gaps, completeness, and continuity
  • Auto-Refresh: Enable overnight monitoring with configurable intervals (15s - 5m)
  • Export: Download filtered data as CSV

Access: http://localhost:8501 after launch

Tips:

  • Use auto-refresh with 60s interval for overnight monitoring
  • All timestamps displayed in UTC
  • Efficiently queries large datasets using DuckDB with time-based filtering

Analysis-Ready Layer

The crypto-lake includes an analysis-ready layer with research views, data slicing tools, and comprehensive validation:

Research Views (DuckDB)

Pre-built views make analysis "one SELECT away". All views use window_start as the timestamp field (UTC timezone-aware).

Available Views:

  1. bars_1s - Base 1-second bars from collector (with exchange literal)
  2. bars_1m - 1-minute rollup from 1s bars
  3. klines_1m - Official Binance 1m klines (if available)
  4. compare_our_vs_kline_1m - Comparison view for data quality checks
  5. funding_oi_hourly - Funding rates and open interest (if derivatives data exists)
  6. macro_minute - 1-minute macro data (SPY, UUP, ES=F, etc.)
  7. dxy_synthetic_minute - DXY synthetic index (if available)

Location: sql/views.sql (uses @@BASE@@ placeholder for base path)

Note: The views use a @@BASE@@ placeholder that is replaced at runtime with the actual base_path from config.yml. This allows the SQL views to be portable across different installations.

Data Slice Tool

Export timeboxed datasets for analysis in Parquet or CSV format:

# Export 1-minute bars for specific symbols and time range
python main.py --mode slice \
    --symbols SOLUSDT,SUIUSDT \
    --start 2025-10-21T00:00:00Z \
    --end 2025-10-21T23:59:00Z \
    --tf 1m \
    --source bars \
    --out data/extracts/sol_1m_2025-10-21.parquet \
    --format parquet

# Export to CSV instead
python main.py --mode slice \
    --symbols SOLUSDT \
    --start 2025-10-21T00:00:00Z \
    --end 2025-10-21T01:00:00Z \
    --tf 1s \
    --source bars \
    --out data/extracts/sol_1s_sample.csv \
    --format csv

Parameters:

  • --symbols: Comma-separated list of symbols (e.g., SOLUSDT,SUIUSDT)
  • --start / --end: Time range in ISO format with Z suffix
  • --tf: Timeframe (1s or 1m)
  • --source: Data source (bars or klines)
  • --out: Output file path
  • --format: Output format (parquet or csv)

Data Quality Validation

Run comprehensive data quality checks and generate Markdown reports:

# Run validation rulepack
python main.py --mode validate_rules \
    --symbols SOLUSDT,SUIUSDT,ADAUSDT \
    --start 2025-10-21T00:00:00Z \
    --end 2025-10-21T14:40:00Z \
    --tf 1s \
    --source bars \
    --report reports/sanity_2025-10-21.md

Validation Rules:

  • R1: OHLC ordering (low ≀ open,close ≀ high)
  • R2: Non-negative prices (OHLC > 0)
  • R3: Ask β‰₯ Bid (bars only, ignores NaN)
  • R4: No NaNs in OHLC (allows NaN in bid/ask/spread)
  • R5: Timestamp continuity and UTC minute alignment
  • R6: Spread sanity (spread β‰₯ 0, < 5% mid for >99.9% of rows)
  • R7: Parity check with official klines (optional, if available)

Report Output:

  • Summary table with violation counts per rule
  • Top 25 offending rows per rule
  • Overall assessment: PASS / INVESTIGATE / FAIL
  • Detailed statistics (spread outliers, volume differences, etc.)

Example Report Structure:

# Data Quality Validation Report

## Run Metadata
- Symbols: SOLUSDT, SUIUSDT, ADAUSDT
- Time Range: 2025-10-21T00:00:00Z to 2025-10-21T14:40:00Z
- Timeframe: 1s
- Total Rows Checked: 157,680

## Validation Summary
| Rule | Description | Total Checked | Violations | % Violated |
|------|-------------|---------------|------------|------------|
| R1   | OHLC ordering | 157,680 | 0 | 0.00% |
| R2   | Positive prices | 157,680 | 0 | 0.00% |
...

## Overall Assessment
βœ… PASS - All validation rules passed successfully.

Binance Historical Backfill

Fetch historical 1-minute OHLCV data from Binance REST API to backfill up to 6 months of data:

# Backfill 180 days of historical data
python main.py --mode backfill_binance \
    --symbols SOLUSDT,BTCUSDT,ETHUSDT \
    --lookback_days 180 \
    --interval 1m

Features

  • REST API Integration: Uses Binance /api/v3/klines endpoint
  • Daily Chunking: Fetches data in daily chunks to respect API limits
  • Automatic Deduplication: Checks existing data and only writes new timestamps
  • Rate Limit Handling: Exponential backoff with retry logic (up to 5 attempts)
  • Partitioned Storage: Writes to D:/CryptoDataLake/backfill/binance/{SYMBOL}/year=YYYY/month=MM/day=DD/
  • UTC Timestamps: All timestamps normalized to UTC timezone

Schema

  • open_time: Candle open timestamp (UTC)
  • open, high, low, close: Price data
  • volume: Base asset volume
  • close_time: Candle close timestamp (UTC)
  • quote_volume: Quote asset volume
  • trades: Number of trades in candle
  • taker_base_vol, taker_quote_vol: Taker volume metrics

Use Cases

  1. Historical Analysis: Backfill 6 months for backtesting strategies
  2. Gap Filling: Fill gaps from collector downtime
  3. Bootstrap: Initialize database before starting real-time collection

Notes

  • Binance limits: 1000 klines per request, 1200 requests/minute
  • Rate limiting: 0.1s sleep between requests (10 req/s max)
  • Deduplication: Existing timestamps are automatically skipped
  • Storage: Separate from real-time data (backfill/ vs parquet/)

Macro Data Collection

Collect 1-minute macro data (stocks, ETFs, futures) using yfinance:

# Fetch SPY and UUP data for last 7 days
python main.py --mode macro_minute \
    --tickers SPY,UUP,ES=F \
    --lookback_days 7

Storage: D:/CryptoDataLake/macro/minute/{ticker}/year=2025/month=10/day=21/

Schema: ticker, ts, open, high, low, close, volume (all UTC timezone-aware)

DuckDB Queries

Load and analyze data:

-- Load 1-second bars
SELECT * FROM read_parquet('D:/CryptoDataLake/parquet/**/*.parquet');

-- Aggregate to 1-minute bars
SELECT
    symbol,
    date_trunc('minute', window_start) AS ts,
    first(open) AS open,
    max(high) AS high,
    min(low) AS low,
    last(close) AS close,
    sum(volume_base) AS volume
FROM read_parquet('D:/CryptoDataLake/parquet/**/*.parquet')
GROUP BY symbol, ts
ORDER BY ts;

Build Verification

The codebase has been fully tested and verified:

  • βœ… Environment: Python 3.12.6, all dependencies installed
  • βœ… Static Analysis: All modules import successfully, no syntax errors
  • βœ… Unit Tests: 4/4 passing
  • βœ… Functional Tests: All 4 pipeline modes verified with live data
  • βœ… Data Quality: Zero gaps, zero duplicates, full schema compliance

See docs/ for the complete build verification report.

Notes

  • High latency warnings during collection are normal (caused by Binance event timestamp metadata)
  • The collector auto-reconnects on network failures with exponential backoff
  • Logs rotate daily and are retained for 14 days
  • Parquet files use Snappy compression for optimal size/speed tradeoff

License

MIT

About

Production-ready cryptocurrency market data pipeline for real-time collection, transformation, and storage of Binance trading data

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages