user@elrise.io:~/market-data-emulator
· [active]

market-data-emulator — генератор синтетических OHLCV-данных

→ репозиторий
Python-эмулятор рыночных данных для тестирования, бэктестинга и ML: Ornstein-Uhlenbeck, 71 встроенный сценарий, multi-pair с Cholesky-корреляцией, per-bar orderbook L2, funding-профиль, truth-dataset контракт с SHA256.

market-data-emulator — это Python-эмулятор рыночных данных, который генерирует реалистичные воспроизводимые OHLCV-серии для одного символа или для пакета из 1..8 скоррелированных символов. Это инструмент для тестирования бэкендов, бэктестинга торговых стратегий, нагрузочного тестирования риск-движков и подготовки обучающих датасетов для ML-моделей. Здесь нет подключения к реальным биржам и нет попыток строить правдоподобный микроструктурный рынок — это контролируемый синтетический источник с известной математической моделью.

Базовая ценовая модель — Ornstein-Uhlenbeck с mean-reversion, drift и волатильностью, которые меняются по сценарному расписанию. На 1-минутном базовом таймфрейме собирается OHLCV, потом он детерминированно ресемплится в любой целевой таймфрейм (1m, 5m, 1h, 1d, 1w, 1mo, плюс произвольный <N><unit>), под конец применяются рыночные инварианты (tick/lot, OHLC-инварианты, лимиты).

Текущий релиз даёт четыре крупные поверхности, которые нужны для бэктестинга в реалистичных условиях:

Все четыре поверхности в проде (см. README и regression/expected_hashes.json — single-pair путь остаётся byte-identical, метки DE-029..DE-032 в Issues/проде закрыты).

Библиотека работает в трёх режимах: standalone CLI (market-emulator), Python API (from market_emulator import Emulator) и HTTP API (FastAPI + uvicorn). Все три режима проходят через один и тот же core/pipeline_orchestrator.py — единый путь гарантии, что сегодняшний pytest и завтрашний --out data.json дают одинаковый результат.

Быстрый старт

Минимальный сценарий: установить эмулятор, сгенерировать 1000 1-минутных свечей, проверить детерминизм.

pip install -r requirements.txt
pip install -e .

market-emulator generate --timeframe 1m --count 1000

market-emulator generate --timeframe 5m --duration 30d --seed 42

market-emulator generate --timeframe 1h --count 720 \
    --format csv --out data.csv

Альтернативные способы (через pyproject.toml, dev-зависимости, Docker) — в §Альтернативная установка.

from market_emulator import Emulator, GenerateRequestV1, to_dataframe
from market_emulator.contracts.enums import Timeframe
from market_emulator.contracts.horizon import HorizonCount

emulator = Emulator()
request = GenerateRequestV1(
    timeframe=Timeframe.FIVE_MINUTES,
    horizon=HorizonCount(count=1000),
    seed=42,
)
result = emulator.generate(request)
df = to_dataframe(result)

print(df.head())
print(f"Свечей: {result.meta.normalized.count}")
print(f"Fingerprint: {result.meta.fingerprint}")

Эмулятор рассчитан на Python 3.11+. Контракт GenerateRequestV1 стабильный между минорными релизами в рамках серии 0.4.x.

Что это и чем не является

Эмулятор делает:

Эмулятор не делает:

Требования

Компонент Версия
Python 3.11 (минимум; pyproject.toml объявляет requires-python = ">=3.11")
pydantic >= 2.0
numpy >= 1.24
pandas >= 2.0
fastapi >= 0.100
typer >= 0.9
pyyaml >= 6.0
uvicorn >= 0.20 (опционально для HTTP API)

Dev-зависимости (pip install -e ".[dev]"): pytest, pytest-xdist, pytest-cov, hypothesis, httpx, jsonschema, scikit-learn, ruff, mypy, bandit, pip-audit, build.

CI-gate: pytest --cov-fail-under=90 (минимум 90% покрытия), ruff (E/F/I — игнор только E501), mypy (advisory-baseline, не валит билд), bandit (со skip B101).

Архитектура

Эмулятор построен как pipeline с явными стадиями и изолированными RNG-потоками. Оркестратор (core/pipeline_orchestrator.py) диспатчит на req.multi_pair: single-pair путь запускается byte-identical, multi-pair путь — per-symbol OU с общими Cholesky-инновациями.

Input Request
    ↓
Dispatch on req.multi_pair
    ↓   ┌──────────────────────────────────────────────┐
    ├──>│ single-pair path (req.multi_pair is None)    │
    │   │   Time Grid (1m base)                       │
    │   │       ↓                                     │
    │   │   Price Process (OU)                        │
    │   │       ↓                                     │
    │   │   OHLCV Construction                       │
    │   │       ↓                                     │
    │   │   Resampling (1m → target)                  │
    │   └──────────────────────────────────────────────┘
    │   ┌──────────────────────────────────────────────┐
    └──>│ multi-pair path (req.multi_pair is set)      │
        │   Time Grid (1m base, shared)               │
        │       ↓                                     │
        │   Cholesky innovations (shared)             │
        │       ↓                                     │
        │   Per-symbol OU (shared regime schedule)    │
        │       ↓                                     │
        │   Per-symbol OHLCV Construction (rows       │
        │     carry `symbol`)                          │
        │       ↓                                     │
        │   Resampling (1m → target)                  │
        └──────────────────────────────────────────────┘
    ↓
Optional per-bar orderbook (когда задан liquidity_profile)
    ↓
Optional per-bar funding    (когда задан funding_profile)
    ↓
Constraints (tick/lot, OHLC-инварианты)
    ↓
Export (JSON / CSV / JSONL / jsonset)
    ↓
Output

Ключевые решения:

Канонический источник архитектуры — docs/architecture.md и docs/architecture/ARCHITECTURE_MAP.md. Дизайн-решения по multi-pair — docs/adr/0006-multi-pair-derivatives-design.md.

Установка

Базовый сценарий (через requirements.txt) уже покрыт в §Быстрый старт. Здесь — альтернативные варианты и полный dev-окружение.

Через pyproject.toml

# Базовый install
pip install -e .

# Dev-зависимости (pytest, ruff, mypy, bandit, hypothesis, ...)
pip install -e ".[dev]"

Dev-окружение через requirements-dev.txt

pip install -r requirements.txt -r requirements-dev.txt
pip install -e .

Docker

Multi-stage Dockerfile на python:3.12-slim:

docker build -t market-data-emulator:local .
docker run --rm market-data-emulator:local \
    market-emulator generate --timeframe 5m --count 1000 --seed 42

docker-compose.yml поднимает сервис с монтированием data/ и pre-built сценариями.

CLI

# 1000 1-минутных свечей
market-emulator generate --timeframe 1m --count 1000

# 30 дней 5-минутных
market-emulator generate --timeframe 5m --duration 30d --seed 42

# Экспорт в CSV
market-emulator generate --timeframe 1h --count 720 \
    --format csv --out data.csv

# Произвольный таймфрейм <N><unit>
market-emulator generate --timeframe 90m --count 100
market-emulator generate --timeframe 2h  --count 24
market-emulator generate --timeframe 3mo --count 12

# Конкретный сценарий
market-emulator generate --timeframe 5m --count 2000 \
    --scenario crash_then_recover --seed 42

# Funding profile (perpetual-swap метаданные)
market-emulator generate --timeframe 1m --count 240 --seed 42 \
    --scenario baseline_mix \
    --config funding_profile='{"pattern": "extreme_positive", "magnitude": 0.001}'

# Liquidity profile (orderbook L2 на каждый бар)
market-emulator generate --timeframe 1m --count 240 --seed 42 \
  --scenario low_liquidity_silent \
  --config liquidity_profile='{"levels_per_side": 10, "base_depth_usd": 500.0, "depth_shape": "power_law", "spread_bps_mean": 20.0, "spread_bps_std": 4.0}'

# Список зарегистрированных сценариев
market-emulator generate --list-scenarios

# Batch-план (несколько датасетов из YAML)
market-emulator batch --plan examples/batch_plan.yaml \
    --now 2026-01-31T00:00:00Z

funding_extreme_positive_240, funding_extreme_negative_240, correlated_xrp_btc_eth — preset-профили, зарегистрированные в src/market_emulator/scenarios/profile_registry.py. Compose через MarketProfileRegistry.get_overlay(name) или --config funding_profile='{...}' — оба пути дают идентичный результат.

Python API

from market_emulator import Emulator, GenerateRequestV1, to_dataframe
from market_emulator.contracts.enums import Timeframe
from market_emulator.contracts.horizon import HorizonCount

emulator = Emulator()
request = GenerateRequestV1(
    timeframe=Timeframe.FIVE_MINUTES,
    horizon=HorizonCount(count=1000),
    seed=42,
)
result = emulator.generate(request)

df = to_dataframe(result)
print(df.head())

print(f"Свечей: {result.meta.normalized.count}")
print(f"Сценарий: {result.meta.scenario_name}")
print(f"Fingerprint: {result.meta.fingerprint}")

Emulator — основной entry point. GenerateRequestV1 — каноническая Pydantic v2-схема входа. to_dataframe — адаптер к pandas. Метаданные результата (result.meta.*) дают доступ к fingerprint, sanity-чекам, сценарным флагам и truth-dataset-артефактам.

Multi-pair generation

1..8 скоррелированных символов в одном запросе через MultiPairSpec. Под капотом req.multi_pair маршрутизирует multi-pair-путь с Cholesky-инновациями; при None запускается single-pair байт-в-байт.

from market_emulator import Emulator, GenerateRequestV1, to_dataframe
from market_emulator.contracts.enums import Timeframe
from market_emulator.contracts.horizon import HorizonCount
from market_emulator.contracts.spec import (
    CorrelationMatrix,
    MultiPairSpec,
    SymbolSpec,
)

emulator = Emulator()

# BTC, ETH, XRP с каноническими корреляциями (ADR-0006)
request = GenerateRequestV1(
    timeframe=Timeframe.ONE_MINUTE,
    horizon=HorizonCount(count=240),
    seed=42,
    multi_pair=MultiPairSpec(
        symbols=[
            SymbolSpec(symbol="BTCUSDT"),
            SymbolSpec(symbol="ETHUSDT"),
            SymbolSpec(symbol="XRPUSDT"),
        ],
        correlation_matrix=CorrelationMatrix(
            symbols=["BTCUSDT", "ETHUSDT", "XRPUSDT"],
            matrix=[
                [1.00, 0.85, 0.55],
                [0.85, 1.00, 0.50],
                [0.55, 0.50, 1.00],
            ],
        ),
        shared_regime_schedule=True,
    ),
)

result = emulator.generate(request)
df = to_dataframe(result)  # строки несут колонку `symbol`

mp = result.meta.multi_pair
print(f"symbols: {mp.symbols}")
print(f"bundle fingerprint: {mp.bundle_fingerprint}")

Preset correlated_xrp_btc_eth зарегистрирован как overlay-профиль в src/market_emulator/scenarios/profile_registry.py. Compose поверх любого base-сценария через MarketProfileRegistry.get_overlay("correlated_xrp_btc_eth") или инлайн через MultiPairSpec — оба пути эквивалентны.

Truth Dataset Contract

Контракт «верифицируемых датасетов»: SHA256 сырых данных, sanity-чеки, сценарные инварианты.

from market_emulator import Emulator, GenerateRequestV1
from market_emulator.contracts.enums import Timeframe
from market_emulator.contracts.horizon import HorizonCount

emulator = Emulator()
request = GenerateRequestV1(
    timeframe=Timeframe.ONE_MINUTE,
    horizon=HorizonCount(count=1000),
    seed=42,
    scenario='baseline_mix',  # triggers artifacts
)

result = emulator.generate(request)

print(f"Raw hash (SHA256): {result.meta.raw_data_hash}")
print(f"Sanity checks: {result.meta.dataset_sanity_checks}")
print(f"Truth version: {result.meta.truth_dataset_version}")

# Верификация детерминизма
result2 = emulator.generate(request)
assert result.meta.raw_data_hash == result2.meta.raw_data_hash
print("✓ Determinism verified")

Канонический контракт — docs/contracts/truth-dataset.md. Экспортёры truth — src/market_emulator/exporters/truth_exporter.py (export_truth_json, export_truth_meta_json, export_truth_jsonl, export_truth_csv). Все четыре writers для одного (seed, scenario, version) дают байт-идентичный output.

Форматы вывода

Формат CLI Что в файлах
json --format json --out data.json Self-contained: данные + метаданные в одном файле
csv --format csv --out data.csv data.csv + data.meta.json (sidecar)
jsonl --format jsonl --out data.jsonl Streaming-friendly: data.jsonl + data.meta.json
jsonset --format jsonset --out-dir <dir> Multi-file: meta.json + manifest.json + (topic, symbol)/<symbol>.json

jsonset (канонический layout, shipped):

<dir>/
├── meta.json                  # общие метаданные
├── manifest.json              # topic → file index
├── ohlcv/<symbol>.json        # per-symbol OHLCV
├── funding/<symbol>.json      # per-symbol funding history
├── orderbook/<symbol>.json    # per-symbol orderbook L2
└── truth/<symbol>.json        # truth target (только для truth сценариев)

Реализация — src/market_emulator/exporters/jsonset_exporter.py. Тест-матрица — tests/test_jsonset_cli.py, tests/test_jsonset_exporter.py, tests/test_jsonset_schema_conformance.py.

Примеры ответов

Реальные ответы на команды из examples/. Все примеры — на seed=42, --now 2026-01-31T00:00:00Z, BTC-like профиль.

JSON (--format json)

Команда:

market-emulator generate \
    --timeframe 1m --count 5 --seed 42 \
    --now 2026-01-31T00:00:00Z --format json \
    --out data.json

data.json (один self-contained файл с метаданными и свечами):

{
  "meta": {
    "generation_id": "b81d494e-25da-4909-a3ac-0e54926d2a8a",
    "timeframe": "1m",
    "normalized": {
      "start": "2026-01-30T23:55:00Z",
      "end": "2026-01-31T00:00:00Z",
      "count": 5,
      "step_seconds": 60,
      "step_kind": "fixed"
    },
    "seed": 42,
    "profile_used": {
      "symbol_profile": "btc_like",
      "price_start": null,
      "price_precision": null,
      "tick_size": null,
      "lot_size": null
    },
    "market_used": {
      "market_regime": "mix",
      "regime_schedule": [],
      "market_session": "us"
    },
    "format": "json",
    "schema": "ohlcv",
    "include": ["ohlcv"],
    "warnings": [],
    "debug": {
      "regimes_summary": {"trend_up": 5},
      "price_preview": {
        "first": 50000.00000000001,
        "last": 50888.80646166614,
        "count": 5
      },
      "constraints": {
        "tick_size": 0.01,
        "lot_size": 0.0001,
        "precision": 2,
        "min_notional": 5.0,
        "repair": {
          "high_adjusted": 0,
          "low_adjusted": 0,
          "low_clamped_to_tick": 0,
          "high_low_swapped": 0
        }
      }
    },
    "base_timeframe": null,
    "target_timeframe": null,
    "resampled": false,
    "dropped_partial_windows": 0,
    "scenario_name": null,
    "scenario_version": null,
    "scenario_hash": null,
    "engine_version": null,
    "meta_hash": "4186f70a87513395ce4aaaf7032cd47002adc228e3e706a21ca76bc6f2afa26d",
    "data_hash": "9e7479605c9fe81dbcd5e9295d9a52b9f4513a8310076b6534f70ca2db517252",
    "fingerprint": "36e4dc7fd7c4435d442aa76540117858f63de1aa72b569ebc88ab77c81d3019d",
    "truth_dataset_version": null,
    "raw_data_hash": null,
    "dataset_sanity_checks": null,
    "emulator_artifacts": null,
    "funding_history": null,
    "orderbook_context": null,
    "multi_pair": null,
    "partial_last_bar": null,
    "analyzer_meta_hash": null
  },
  "data": [
    {
      "ts_open": "2026-01-30T23:55:00Z",
      "ts_close": "2026-01-30T23:56:00Z",
      "open": 50180.28,
      "high": 51138.92,
      "low": 48548.44,
      "close": 50000.0,
      "volume": 273.9469
    },
    {
      "ts_open": "2026-01-30T23:56:00Z",
      "ts_close": "2026-01-30T23:57:00Z",
      "open": 50000.0,
      "high": 50792.6,
      "low": 49130.56,
      "close": 50524.22,
      "volume": 251.9564
    },
    {
      "ts_open": "2026-01-30T23:57:00Z",
      "ts_close": "2026-01-30T23:58:00Z",
      "open": 50524.22,
      "high": 51719.0,
      "low": 48550.17,
      "close": 51405.81,
      "volume": 376.1741
    },
    {
      "ts_open": "2026-01-30T23:58:00Z",
      "ts_close": "2026-01-30T23:59:00Z",
      "open": 51405.81,
      "high": 54167.23,
      "low": 49056.3,
      "close": 52610.45,
      "volume": 513.139
    },
    {
      "ts_open": "2026-01-30T23:59:00Z",
      "ts_close": "2026-01-31T00:00:00Z",
      "open": 52610.45,
      "high": 55316.8,
      "low": 50184.69,
      "close": 50888.81,
      "volume": 558.4578
    }
  ]
}

meta.fingerprint — SHA256-хеш конфигурации (без generation_id и generated_at), служит ключом для кеширования и регрессионных проверок. meta.data_hash — SHA256 самих данных. meta.regimes_summary — распределение баров по режимам (trend_up: 5 означает, что все 5 баров внутри трендовой фазы).

CSV (--format csv)

Команда:

market-emulator generate \
    --timeframe 5m --count 3 --seed 42 \
    --now 2026-01-31T00:00:00Z --format csv \
    --out data.csv

data.csv:

ts_open,ts_close,open,high,low,close,volume
2026-01-30T23:45:00Z,2026-01-30T23:50:00Z,50180.28,55316.8,48548.44,50888.81,1973.6744
2026-01-30T23:50:00Z,2026-01-30T23:55:00Z,50888.81,53366.41,47545.55,51518.33,1886.6701
2026-01-30T23:55:00Z,2026-01-31T00:00:00Z,51518.33,58363.88,49798.85,54907.27,2010.3284

data.meta.json (sidecar, та же meta структура, что и в JSON-выводе):

{
  "generation_id": "65b257a0-5642-4d65-981c-51688fec8fc5",
  "timeframe": "5m",
  "normalized": {
    "start": "2026-01-30T23:45:00Z",
    "end": "2026-01-31T00:00:00Z",
    "count": 3,
    "step_seconds": 300,
    "step_kind": "fixed"
  },
  "seed": 42,
  "profile_used": {"symbol_profile": "btc_like"},
  "market_used": {"market_regime": "mix", "market_session": "us"},
  "format": "csv",
  "schema": "ohlcv",
  "resampled": true,
  "base_timeframe": "1m",
  "target_timeframe": "5m",
  "data_hash": "a654cc13c7603f35784d74ddb2faf17a4e2c1703c1699eb79091ef31ecaa1746",
  "meta_hash": "8505f335dbceb16517ce7f67fd743fb23e97fb6cc24b82a8ee20826dd00afb39",
  "fingerprint": "09bcff37290b76955e51b5318a83ba6e16066..."
}

resampled: true означает, что 1-минутный базовый pipeline был ресемплирован в 5-минутный таймфрейм; base_timeframe / target_timeframe фиксируют это.

JSONL (--format jsonl)

Команда:

market-emulator generate \
    --timeframe 5m --count 3 --seed 42 \
    --now 2026-01-31T00:00:00Z --format jsonl \
    --out data.jsonl

data.jsonl — по одному JSON-объекту на строку, без обёртки-массива:

{"ts_open": "2026-01-30T23:45:00Z", "ts_close": "2026-01-30T23:50:00Z", "open": 50180.28, "high": 55316.8, "low": 48548.44, "close": 50888.81, "volume": 1973.6744}
{"ts_open": "2026-01-30T23:50:00Z", "ts_close": "2026-01-30T23:55:00Z", "open": 50888.81, "high": 53366.41, "low": 47545.55, "close": 51518.33, "volume": 1886.6701}
{"ts_open": "2026-01-30T23:55:00Z", "ts_close": "2026-01-31T00:00:00Z", "open": 51518.33, "high": 58363.88, "low": 49798.85, "close": 54907.27, "volume": 2010.3284}

data.meta.json создаётся рядом. JSONL формат — для стриминга: jq -c '.close' data.jsonl проходит 100k+ баров без загрузки в память.

Python API: to_dataframe

>>> from market_emulator import Emulator, GenerateRequestV1, to_dataframe
>>> from market_emulator.contracts.enums import Timeframe
>>> from market_emulator.contracts.horizon import HorizonCount
>>> 
>>> emulator = Emulator()
>>> request = GenerateRequestV1(
...     timeframe=Timeframe.ONE_MINUTE,
...     horizon=HorizonCount(count=5),
...     seed=42,
... )
>>> result = emulator.generate(request)
>>> result.meta.normalized.model_dump()
{'start': datetime.datetime(2026, 8, 5, 11, 1, tzinfo=datetime.timezone.utc),
 'end': datetime.datetime(2026, 8, 5, 11, 6, tzinfo=datetime.timezone.utc),
 'count': 5,
 'step_seconds': 60,
 'step_kind': 'fixed'}
>>> result.meta.fingerprint
'38c4736fb1e75de585630001f1ffdaf613a6125546bed13938235d9d48841580'
>>> df = to_dataframe(result)
>>> df.head()
                    ts_open                  ts_close      open      high       low     close    volume symbol orderbook funding universe  is_partial_last_bar regime_label quality_flags context
0 2026-08-05 11:01:00+00:00 2026-08-05 11:02:00+00:00  50180.28  51138.92  48548.44  50000.00  273.9469   None      None    None     None                False         None            []    None
1 2026-08-05 11:02:00+00:00 2026-08-05 11:03:00+00:00  50000.00  50792.60  49130.56  50524.22  251.9564   None      None    None     None                False         None            []    None
2 2026-08-05 11:03:00+00:00 2026-08-05 11:04:00+00:00  50524.22  51719.00  48550.17  51405.81  376.1741   None      None    None     None                False         None            []    None
3 2026-08-05 11:04:00+00:00 2026-08-05 11:05:00+00:00  51405.81  54167.23  49056.30  52610.45  513.1390   None      None    None     None                False         None            []    None
4 2026-08-05 11:05:00+00:00 2026-08-05 11:06:00+00:00  52610.45  55316.80  50184.69  50888.81  558.4578   None      None    None     None                False         None            []    None

to_dataframe возвращает pandas DataFrame с колонками: ts_open, ts_close, open, high, low, close, volume (базовые OHLCV) плюс symbol, orderbook, funding, universe, is_partial_last_bar, regime_label, quality_flags, context (опциональные, появляются при соответствующих overlays). Колонки orderbook / funding и universe заполняются per-bar при liquidity_profile / funding_profile / multi-pair пути.

Что означают ключевые поля в meta

Детерминизм

Одни и те же входы всегда дают одинаковый выход:

market-emulator generate --timeframe 1m --count 1000 --seed 42 \
    --now 2026-01-31T00:00:00Z --format json > run1.json
market-emulator generate --timeframe 1m --count 1000 --seed 42 \
    --now 2026-01-31T00:00:00Z --format json > run2.json

# Байт-идентично (за исключением generation_id и generated_at)
diff run1.json run2.json

Важно: --now обязателен для count и duration горизонтов — без него время отсчитывается от текущего момента, и rerun в разные секунды даёт разные timestamps. Для горизонта HorizonRangestart/end) --now не нужен.

Регрессионный пакет tests/test_regression_pack.py фиксирует SHA256-хеши для 10 канонических сценариев. Это позволяет ловить любой непреднамеренный drift в пайплайне на CI.

Каталог сценариев

Подробный каталог — docs/scenarios.md. Здесь — компактный индекс с разбивкой по принципу деления.

Принцип деления

Все 71 встроенных сценария организованы по двум осям:

  1. RegimeType (src/market_emulator/contracts/enums.py::RegimeType) — какая рыночная фаза моделируется: mix, trend_up, trend_down, range, crash, technical_pattern, wyckoff, event_driven, behavioral. Это enum, который попадает в meta.scenario_regime каждого результата и читается downstream-анализаторами.
  2. Механизм — что именно делает сценарий с ценовым процессом: чистый drift + σ, переключение фаз, multi-segment drift_segments, session-aware окна, gap (open_jump), открытые funding/liquidity overlays, attachment к TA-паттерну, attachment к Wyckoff-фазе, attachment к крипто-эвенту.

Built-in сценарии делятся на 6 исторических волн (раздел §24-profile catalogue в docs/scenarios.md):

Волна Регион Сценариев Что добавляет
Historical 18 MIX/TREND/RANGE 18 Базовая линейка: baseline, trend, range, crash, stress, high-vol
Profile (Categories 2, 3, 5) Всех типов 14 Покрытие trend × vol × liquidity × session матрицы
Crash-spike CRASH-подкатегория 7 Liquidation, squeeze, blow-off, capitulation
Technical-pattern TECHNICAL_PATTERN 7 H&S, треугольники, double/триple top/bottom, cup+handle
Wyckoff WYCKOFF 6 Accumulation, distribution, spring, UTAD, SOS/SOW
Crypto-event EVENT_DRIVEN 8 Token unlocks, listings, exploits, governance, depeg
Calendar + behavioral MIX, BEHAVIORAL 11 Halving, options expiry, daily open/close, FOMO, panic

Расчёт: 18 + 14 + 7 + 7 + 6 + 8 + 11 = 71. Регрессионные хеши для первых 18 в regression/expected_hashes.json неизменны — multi-pair путь, overlays и новые волны byte-identical для прошлых сценариев.

Группа A: Каноническая линейка (18)

Базовая линейка, на которой собирались регрессионные хеши. Эти сценарии — валидационный baseline: если какая-то фича ломает их хеш, ловит tests/test_regression_pack.py.

Baseline. baseline_mix — MIX, σ=0.02, normal noise. Сбалансированная смесь режимов; основной «нормальный рынок» для regression-тестов.

Trend. trend_up_soft (drift +0.0003, σ=0.015), trend_up_hard (drift +0.0008, σ=0.025), trend_down_soft (drift -0.0003, σ=0.015), trend_down_hard (drift -0.0008, σ=0.025).

Range. range_tight (drift=0, σ=0.01), range_wide (drift=0, σ=0.025).

High-volatility. high_vol_spiky — MIX, σ=0.04, Student-t (df=5) для fat tails, vol clustering.

Market events. crash_then_recover (drift -0.0002, σ=0.035, Student-t df=4), pump_then_dump (drift +0.0002, σ=0.035, Student-t df=4).

Stress & edge cases. extreme_volatility_burst (σ ramp 0.02→0.15→0.02 за 60+180 бар, volume 10x), gap_event (open_jump +5% @ bar 100, -5% @ bar 200), flash_crash_recovery (-20% за 10 бар @ bar 80, восстановление за 20).

Funding profile (требует funding_profile). funding_extreme_positive_240, funding_extreme_negative_240 — sustained funding rates, registered как overlay-профили.

Low-liquidity (требует liquidity_profile). low_liquidity_silent — RANGE + tight OHLCV + low-liquidity orderbook.

Regime transition. regime_transition_smooth — 60 бар в A, 60 бар в плавном переходе, 60 бар в B, 60 бар обратно.

Pullback / breakout. trending_with_pullbacks (аптренд + 3-4 pullback 1-3% с восстановлением), ranging_with_breakout_attempts (100 бар range, failed breakout, 100 бар range, успешный breakout).

Volume distribution. high_volume_no_price_move (200 бар, volume 5x, drift=0).

Группа B: Profile gap-fill (14)

Закрывает QA-program audit для матрицы trend × vol × liquidity × session. Каждый сценарий — параметризованный сценарий из исторических 18 + дополнительные метаданные (market_session, liq, vol).

Breakout (3): breakout_clean (BREAKOUT, medium vol, normal liq), breakout_failed (range с fake breakout, medium vol), breakout_chaotic_vol (BREAKOUT, extreme vol, thin liq).

Mean reversion (2): mean_reversion_high_vol (mean reversion, high vol, normal liq), mean_reversion_low_vol_deep (mean reversion, low vol, deep liq, asia session).

Session diversity (5): asia_uptrend (uptrend, asia), europe_downtrend (downtrend, europe), cross_session_breakout (breakout, cross, high vol), cross_session_chaotic (chaotic, extreme vol, thin liq, cross), asia_low_vol_range (range, low vol, asia).

Mixed combos (4): breakout_low_vol_normal (breakout, low vol), mean_reversion_medium_vol_thin (mean reversion, medium vol, thin), chaotic_medium_vol_normal (chaotic, medium vol), chaotic_high_vol_deep (chaotic, high vol, deep liq, cross).

Группа C: Crash / spike sub-category (7)

Подкатегория CRASH — ликвидации, шорт-сквизы, blow-off, capitulation. Все multi-phase через drift_segments + open_jumps.

Группа D: Technical patterns (TECHNICAL_PATTERN, 7)

Все несут market_regime = TECHNICAL_PATTERN. Используют multi-segment drift_segments для построения TA-фигур.

Группа E: Wyckoff (WYCKOFF, 6)

market_regime = WYCKOFF. Классические Wyckoff-фазы.

Группа F: Crypto events (EVENT_DRIVEN, 8)

market_regime = EVENT_DRIVEN. Событийные сценарии для крипто-рынка.

Группа G: Calendar + behavioral (11)

Calendar (6) — MIX regime, фокус на time-of-day / day-of-week / cyclical.

Behavioral (5)market_regime = BEHAVIORAL. Возвращают новый enum для отличия от trend/range/crash.

Overlay-профили (11)

Overlay-профили — это конфигурации существующих сценариев с заполненными overlay-полями. Регистрируются в MarketProfileRegistry через MarketProfileRegistry.get_overlay(name).

Профиль Базовый сценарий Overlay-поле
deep_book_uptrend trend_up_hard liquidity_class=deep
deep_book_downtrend trend_down_hard liquidity_class=deep
deep_book_breakout ranging_with_breakout_attempts liquidity_class=deep
deep_book_chaotic extreme_volatility_burst liquidity_class=deep
deep_book_range high_volume_no_price_move liquidity_class=deep
funding_extreme_positive_240 baseline_mix FundingProfile(pattern="extreme_positive")
funding_extreme_negative_240 baseline_mix FundingProfile(pattern="extreme_negative")
funding_periodic_oscillating_240 baseline_mix FundingProfile(pattern="regime_dependent")
multi_pair_btc_eth_correlated_up trend_up_hard MultiPairSpec(BTCUSDT, ETHUSDT, SOLUSDT)
correlated_xrp_btc_eth baseline_mix MultiPairSpec(XRPUSDT, BTCUSDT, ETHUSDT)
alt_rotation_jan_feb baseline_mix MultiPairSpec(8 alts)

Composability-паттерны (20)

Это поля LiquidityProfile, FundingProfile, MultiPairSpec, которые compose-ятся поверх любого базового сценария. Не отдельные сценарии — overlay knobs.

LiquidityProfile (10): levels_per_side, base_depth_usd, spread_bps_mean, spread_bps_std, depth_shape; overlay knobs: liquidity_dry_up, orderbook_imbalance, stop_cascade, iceberg_absorption, v2 overlays: spoofing, layering, stop_hunt, twap_execution, vwap_execution, iceberg_variation.

FundingProfile (5): базовые 5 patterns (extreme_positive, extreme_negative, regime_dependent, periodic, oscillating); overlay knobs (B5-B7): funding_flip, funding_squeeze, stable_coin_depeg_shock; volatility-surface annotations: volatility_regime, term_structure, funding_rate_cap, premium_index.

MultiPairSpec (3): lead_lag, correlation_breakdown, cross_asset_contagion.

Truth-сценарии (8)

Регистрируются в TRUTH_SCENARIO_SUMMARIES, инвокация через Emulator.generate_truth(TruthGenerateRequestV1(...)). Возвращают TruthDatasetResultV1 с target массивами и truth_meta.target для QA-ассертов.

Примеры

В examples/ собраны рабочие скрипты:

# Python quickstart
python examples/quickstart_module.py

# CLI-скрипты
bash examples/generate_csv_10d_1m.sh
bash examples/generate_json_30d_5m.sh

# Комплексный проход по всем задокументированным CLI-поверхностям
bash examples/demo_cli.sh

demo_cli.sh — это single end-to-end скрипт, который проходит каждую документированную CLI-поверхность (basic generation, date range, duration, scenarios, custom profile, export formats, multi-timeframe, batch, determinism, --now override, JSONL streaming с jq) и пишет результаты в data/.

# Batch-план
mkdir -p data
market-emulator batch --plan examples/batch_plan.yaml \
    --now 2026-01-31T00:00:00Z

batch_plan.yaml документирует четыре кейса: 1m baseline, 5m trending, 1d по диапазону дат, micro-5m за неделю. Все четыре с фиксированными seed'ами дают байт-идентичный output.

Карта документации

docs/ разбит на три уровня:

User-facing references (консьюмерская документация):

Architecture meta (внутренний дизайн):

Operational:

Тестирование

# Полный прогон
pytest

# Без slow/perf
pytest -m "not slow and not perf"

# Регрессионный пакет
pytest tests/test_regression_pack.py

# Сценарии + jsonset
pytest tests/test_jsonset_cli.py tests/test_jsonset_exporter.py \
    tests/test_jsonset_schema_conformance.py

pytest настроен на --cov-fail-under=90 (минимум 90% покрытия). pytest-xdist запускает тесты в 8 потоках. Маркеры slow и perf отсекаются через -m "not slow and not perf".

Регрессионный пакет проверяет SHA256-хеши для 10 канонических сценариев. Любой непреднамеренный drift в пайплайне (от рефакторинга до изменения формата) ловится этим пакетом на CI.

CONTRIBUTING.md документирует dev-окружение, code style, процесс отправки изменений.

Источники