# CLAUDE.md — A-Share Data Platform (Trading OS) ## Project Overview A-share trading operating system built on Parquet + DuckDB with REST/WebSocket APIs. Architecture follows a 5-subsystem design: Data Platform → Market Intelligence → Signal Intelligence → Execution Intelligence → Presentation. Dashboard at `/dashboard` renders a Trading Command Center. ## Tech Stack - **Data**: `akshare` (East Money / Sina), Parquet (Zstd), DuckDB (analytics) - **API**: FastAPI + uvicorn, single `/api/v1/dashboard/state` endpoint + K-line/stock/calendar REST - **CLI**: Typer (`ashare-dp backfill daily|minute|industry|signals ...`) - **Scheduler**: APScheduler (EOD 15:05, EMA52 15:10) - **Config**: pydantic-settings (`.env`) ## Project Structure (v10 — 5 Subsystems) ``` src/ashare_dp/ ├── config.py # Settings ├── core/ # Shared kernel │ ├── models.py # Freq enum, INDEX_CODES, INDEX_SINA_SYMBOLS │ ├── codes.py # ★ Single ts_code conversion module (IDEMPOTENT) │ ├── calendar.py # Trading calendar, market state, Beijing TZ │ └── exceptions.py ├── domain/ # Ontology — shared contracts │ ├── state.py # MarketState (7-dim continuous vector) │ ├── context.py # TradingContext, Playbook, Expectancy, Opportunity │ ├── events.py # RiskEvent │ ├── features.py # FeatureDefinition │ ├── leadership.py # LeaderState enum │ └── signal.py # SignalType, SignalInstance ├── data/ # ═══ DATA PLATFORM ═══ │ ├── sources/ # akshare_client, index, industry │ ├── pipelines/ # backfill, eod, realtime │ └── store/ # database (get_db, analytics_conn, kline_glob), repository, partitioning, schema ├── features/ # Feature Store (6 registered features, all use analytics_conn + kline_glob) ├── market/ # ═══ MARKET INTELLIGENCE ═══ │ ├── state.py # infer_market_state │ ├── leadership.py # assess_leaders (lifecycle per industry) │ ├── opportunity.py # rank_opportunities │ ├── flow.py # compute_flow (money flow graph) │ ├── sentiment.py # Phase 2 placeholder │ └── memory.py # StateStore (state_snapshot table) ├── signals/ # ═══ SIGNAL INTELLIGENCE ═══ (the moat) │ ├── detectors.py # EMA52 cross detection + shared screening logic │ ├── store.py # signal_instance CRUD (to be extracted from detectors) │ └── expectancy.py # get_expectancy(state) — single entry, fallback chain internal ├── execution/ # ═══ EXECUTION INTELLIGENCE ═══ │ ├── playbook.py # build_playbook (State → strategies/bias/holding) │ ├── risk.py # RiskRule engine + evaluate_risks │ └── brief.py # build_brief + brief_to_api_dict (single serialization point) └── apps/ # ═══ PRESENTATION ═══ ├── api/ # app.py, routers/, websocket/, dashboard/ ├── cli/ # main.py + backfill/serve/query/eod/screening commands └── scheduler/ # scheduler.py, jobs.py ``` ## Key Conventions ### ts_code conversion (CRITICAL) **Always use `from ashare_dp.core.codes import to_ts_code`** — the single idempotent implementation. Never write local `_code_to_ts_code()` copies. `to_ts_code()` is safe to call on any format: bare codes, already-formatted ts_codes, Sina symbols, even legacy corrupted `.SZ.SZ` values. ### Database connections - **DuckDB tables** (stock_info, trading_calendar, signal_instance): use `get_db()` context manager from `data.store.database` - **Analytics queries** (features, engines): use `analytics_conn()` for raw DuckDB connection — the single sanctioned way. Never hardcode `"data/duckdb/ashare.db"` - **Parquet globs**: use `kline_glob()` or `partition_glob(freq)` — never hardcode paths ### Architecture boundaries - `data/` knows nothing about trading - `features/` computes features, never classifies regimes - `market/` infers state, knows nothing about signals - `signals/` queries historical expectancy, knows nothing about execution - `execution/` maps state to strategies, assembles TradingBrief - `apps/` only renders, never reasons ### MarketState is a continuous vector (not enum) 7 dimensions: trend, fear, liquidity, rotation, participation, volatility, breadth. Each 0.0–1.0. Display labels derived downstream only. ### API Response Single endpoint produces all dashboard data: `GET /api/v1/dashboard/state`. Response versioned (`"version": "1.0"`). Serialization in `execution/brief.py::brief_to_api_dict()` — the single serialization point. Router only orchestrates engine calls. ## Running ```bash pip install -e ".[dev]" ashare-dp backfill init # Schema + stock list + trading calendar ashare-dp backfill daily # Daily/weekly/monthly + indices ashare-dp backfill industry # Industry classifications ashare-dp backfill signals # EMA52 signal detection + store ashare-dp serve start # API + scheduler + realtime open http://localhost:8000/dashboard ```