diff --git a/CHANGELOG.md b/CHANGELOG.md index f398380..95e35ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,22 @@ All notable changes to this project will be documented in this file. -## [Unreleased] - 2026-05-20 +## [Unreleased] - 2026-05-21 + +### Added +- Docs/Tooling: add `scripts/capture_frontend_previews.py` — a Playwright + Chromium driver that opens the live Docker frontend (`docker compose up -d api frontend`), runs a real `600519.SH` backtest in the Backtest Workbench, and writes `docs/assets/web-console-dashboard.png` and `docs/assets/web-console-backtest.png` straight from the running Vue console. Trailing blank padding is auto-cropped via Pillow. + +### Changed +- Docs: the README "Platform at a Glance" previews now come from **real sources only** — the two Web Console screenshots are captured from the live Vue frontend running under `docker compose`, and the backtest chart is the actual matplotlib output of `python unified_backtest_framework.py run --strategy macd --symbols 600519.SH --plot` (taken from `report//backtest_result.png`). No synthesized/mock images. +- Docs: rewrote the "Platform at a Glance" intro and image captions to name the real capture commands; redrew `docs/assets/architecture-overview.svg` to match the V6 layered architecture (Plugin SDK → SPI → Engines/Adapters → Runtime contexts → Kernel). +- Docs: rename the README "5-Second Preview" section to "Platform at a Glance" so the heading matches what the panel actually shows. + +### Fixed +- Docs: repair the README "CI Status" badge URL — switched from the legacy `workflows/CI/badge.svg` shortcut (which never matched the workflow name `CI/CD Pipeline`) to the canonical `actions/workflows/ci.yml/badge.svg?branch=main` form and pointed the click target at the workflow page. + +### Removed +- Docs/Tooling: delete `scripts/render_readme_preview.py` — the synthetic matplotlib renderer is superseded by real captures from the live frontend and CLI plot. +- Docs: drop obsolete preview assets `docs/assets/web-console-dashboard.svg`, `docs/assets/web-console-backtest.svg`, and `docs/assets/demo-workflow.gif` (superseded by the real PNG captures). ### Added - V6 Phase 7 (distribution split): add packaging manifests for `quant-platform-core`, `quant-platform-sdk`, `quant-platform-adapters-cn`, `quant-platform-ml`, `quant-platform-web`, and `quant-platform-cli` under `packages/`, with matching `quant_platform_*` import facades. diff --git a/README.md b/README.md index 6407654..0ba66a9 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,26 @@ [![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/) [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE) -[![CI Status](https://github.com/magic-alt/stock/workflows/CI/badge.svg)](https://github.com/magic-alt/stock/actions) +[![CI Status](https://github.com/magic-alt/stock/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/magic-alt/stock/actions/workflows/ci.yml) [![Code Coverage](https://codecov.io/gh/magic-alt/stock/branch/main/graph/badge.svg)](https://codecov.io/gh/magic-alt/stock) 面向 A 股的开源量化研究与回测平台:策略准入、回测报告、仿真交易、Web 控制台和实盘网关适配器一体化。 A-share quant research platform with strategy admission gates, backtesting, paper trading, a web console, and live gateway adapters. -**Current version**: V5.0.0 | **Updated**: 2026-05-19 | **Status**: local and container workflows ready +**Current version**: V5.0.0 | **Updated**: 2026-05-21 | **Status**: local and container workflows ready -## 5-Second Preview +## Platform at a Glance -![Architecture overview](docs/assets/architecture-overview.svg) +The two web-console previews below are **real screenshots of the running Vue frontend** (`docker compose up -d api frontend`), captured by [scripts/capture_frontend_previews.py](scripts/capture_frontend_previews.py) (Playwright + Chromium) after running an actual backtest on `600519.SH` through the API. The bottom chart is the **real CLI plot** produced by `python unified_backtest_framework.py run --strategy macd --symbols 600519.SH --plot`, taken straight from the run's `report//backtest_result.png`. -| Dashboard | Backtest workbench | +![V6 open-platform architecture](docs/assets/architecture-overview.svg) + +| Dashboard view (live frontend) | Backtest workbench view (live frontend, real backtest) | |---|---| -| ![Web console dashboard preview](docs/assets/web-console-dashboard.svg) | ![Web console backtest preview](docs/assets/web-console-backtest.svg) | +| ![Quant Platform dashboard — captured from the running Vue console](docs/assets/web-console-dashboard.png) | ![Backtest workbench — captured from the running Vue console after a real 600519.SH backtest](docs/assets/web-console-backtest.png) | -![Five-minute demo workflow](docs/assets/demo-workflow.gif) +![CLI backtest plot — 600519.SH with MA / Bollinger / RSI / MACD / KDJ panels and buy/sell markers, generated by `unified_backtest_framework.py run --plot`](docs/assets/backtest-preview.png) ## Why This Project diff --git a/docs/assets/architecture-overview.svg b/docs/assets/architecture-overview.svg index 800f1bf..175c9bb 100644 --- a/docs/assets/architecture-overview.svg +++ b/docs/assets/architecture-overview.svg @@ -1,72 +1,154 @@ - - Unified Quant Platform Architecture - Architecture overview showing CLI, web console, API, backtesting, admission, gateways, risk, audit, and reports. + + Unified Quant Platform — V6 Open-Platform Architecture + Layered view of the V6 open platform: plugin SDK, SPI/registry, engines and adapters, runtime contexts, and the shared kernel services. - - - + + + - + - - + + - - - - A-share Quant Research Platform - Strategy admission, backtesting, paper trading, web console, and live gateway adapters. - - - CLI / GUI - backtest, grid, admission - - - Web Console - Dashboard, Backtest, Trading - - - FastAPI v2 - jobs, reports, monitor, API - - - Backtest - A-share rules + reports - - - Admission - baseline + stage gates - - - Gateways - paper, stub, live adapters - - - Risk - OMS + audit - - - Reports - JSON, MD, charts - - - - - - - - - - - First run: python examples/one_click_demo.py --out-dir report/open_source_demo - \ No newline at end of file + + + + + + Unified Quant Platform — V6 Open Platform + Plugin SDK · SPI registry · canonical engines & adapters · runtime contexts · shared kernel. + + + PLUGIN SURFACE + + Plugin SDK + BaseStrategy · BaseAdapter · BasePlugin + pip install · cookiecutter template + + + Plugins (3rd-party + built-in) + strategies · data sources · brokers · ML + examples/plugins/ · entry_points + + + CLI · GUI · Web Console + unified_backtest_framework · backtest_gui + FastAPI v2 + Vue3/Vite console + + + API v2 + /api/v2/jobs · reports · monitor + /api/v2/info → contract_version + + + SPI / REGISTRY + + PluginRegistry · PluginSPI · Contract version gate + Discover · validate · test · load — single entry point for every plugin and runtime. + src/core/plugin_registry.py · src/sdk/ + + + ENGINES & ADAPTERS (canonical re-exports of V5 modules) + + + + Engines + Per-domain composition ring over V5 implementations. + + + engines.data + + engines.execution + + engines.risk · portfolio + + engines.backtest + + engines.research + + engines.report · attribution + + + + + Adapters + Stable namespace for IO + integrations. + + + adapters.data + + adapters.realtime + + adapters.broker + + adapters.storage + + adapters.ml + + adapters.messaging + + + + RUNTIME CONTEXTS + + BacktestRuntime + Deterministic replay over historical bars. + policy: replay · clock=virtual + + + SandboxRuntime + Paper trading on live data with stubbed broker. + policy: paper · clock=wall + + + LiveRuntime + Live gateways + admission gate enforcement. + policy: live · clock=wall + + + KERNEL (shared services) + + + Config · ConfigPort + MetricCollector · MetricsPort + Tracer · TracerPort + Audit · EventBus + JobQueue · JobStore + Secrets · RBAC + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/backtest-preview.png b/docs/assets/backtest-preview.png new file mode 100644 index 0000000..ade66ad Binary files /dev/null and b/docs/assets/backtest-preview.png differ diff --git a/docs/assets/demo-workflow.gif b/docs/assets/demo-workflow.gif deleted file mode 100644 index e03f398..0000000 Binary files a/docs/assets/demo-workflow.gif and /dev/null differ diff --git a/docs/assets/web-console-backtest.png b/docs/assets/web-console-backtest.png new file mode 100644 index 0000000..9840709 Binary files /dev/null and b/docs/assets/web-console-backtest.png differ diff --git a/docs/assets/web-console-backtest.svg b/docs/assets/web-console-backtest.svg deleted file mode 100644 index 1933568..0000000 --- a/docs/assets/web-console-backtest.svg +++ /dev/null @@ -1,47 +0,0 @@ - - Web Console Backtest Preview - Backtest screenshot-style preview with run form, task table, candlestick chart, and metric cards. - - - - - - Backtest Workbench - Run a strategy, inspect chart data, and keep reproducible artifacts. - - - Run configuration - Strategymacd - Symbols600519.SH, 000001.SZ - Date range2023-01-01 to 2024-12-31 - Run Backtest - - - Candlestick and volume - - - - - - - - - - - - - - - - Metrics - Sharpe1.42 - Max Drawdown-8.6% - AdmissionPASS - Artifacts4 - \ No newline at end of file diff --git a/docs/assets/web-console-dashboard.png b/docs/assets/web-console-dashboard.png new file mode 100644 index 0000000..005c726 Binary files /dev/null and b/docs/assets/web-console-dashboard.png differ diff --git a/docs/assets/web-console-dashboard.svg b/docs/assets/web-console-dashboard.svg deleted file mode 100644 index 7b617fd..0000000 --- a/docs/assets/web-console-dashboard.svg +++ /dev/null @@ -1,67 +0,0 @@ - - Web Console Dashboard Preview - Dashboard screenshot-style preview with status cards, equity curve, jobs, and gateway health. - - - - - - - - - - - Unified Quant - Dashboard - Backtest - Trading - Strategies - Data - Monitor - Dashboard - Demo workspace: paper gateway, admission status, and recent runs. - - - - Gateway - Connected - - - - - Admission - Passed - - - - - Open Jobs - 3 - - - - PnL - +1.7% - - - - Equity curve - - - - - - - Recent workflow - baseline registered - admission passed - paper validated - live candidate - \ No newline at end of file diff --git a/scripts/capture_frontend_previews.py b/scripts/capture_frontend_previews.py new file mode 100644 index 0000000..6064dcf --- /dev/null +++ b/scripts/capture_frontend_previews.py @@ -0,0 +1,125 @@ +"""Capture real screenshots of the running Vue frontend for README previews. + +Prerequisite: the Docker stack must be running (`docker compose up -d api frontend`) +so that the frontend is reachable on http://localhost:3000 and the API on +http://localhost:8000. + +Usage: + python -m playwright install chromium # one-off + python scripts/capture_frontend_previews.py + +Outputs: + docs/assets/web-console-dashboard.png — Dashboard view (real frontend) + docs/assets/web-console-backtest.png — Backtest workbench view (real frontend) +""" +from __future__ import annotations + +import pathlib +import sys +import time + +ROOT = pathlib.Path(__file__).resolve().parents[1] +OUT_DIR = ROOT / "docs" / "assets" +OUT_DIR.mkdir(parents=True, exist_ok=True) + +FRONTEND_URL = "http://localhost:3000" +VIEWPORT = {"width": 1440, "height": 900} + + +def _try_run_backtest(page) -> None: + """Best-effort: trigger a backtest from the Backtest page so the chart panel + becomes populated. If anything fails we still screenshot the page as-is.""" + try: + symbol_input = page.get_by_placeholder("600519.SH,000001.SZ") + if symbol_input.count() > 0: + symbol_input.first.fill("600519.SH") + run_btn = page.get_by_role("button", name="Run Backtest") + if run_btn.count() == 0: + run_btn = page.get_by_role("button", name="Submit Backtest Job") + if run_btn.count() > 0: + run_btn.first.click() + page.wait_for_selector(".backtest-chart", timeout=120_000) + # Wait for the ECharts canvas to actually have data. + time.sleep(5.0) + except Exception as exc: # noqa: BLE001 + print(f"[warn] backtest trigger failed (continuing with current state): {exc}") + + +def main() -> int: + try: + from playwright.sync_api import sync_playwright + except ImportError: + print("playwright not installed. Run: python -m pip install playwright && python -m playwright install chromium") + return 1 + + with sync_playwright() as pw: + browser = pw.chromium.launch(headless=True) + try: + context = browser.new_context(viewport=VIEWPORT, device_scale_factor=1.5) + page = context.new_page() + + # 1) Dashboard — viewport screenshot so the framing matches the README thumbnail. + dashboard_path = OUT_DIR / "web-console-dashboard.png" + print(f"[capture] {FRONTEND_URL}/ -> {dashboard_path.relative_to(ROOT)}") + page.goto(f"{FRONTEND_URL}/", wait_until="networkidle", timeout=60_000) + time.sleep(2.5) + page.evaluate("window.scrollTo(0, 0)") + page.screenshot(path=str(dashboard_path), full_page=False) + + # 2) Backtest — run a real backtest, then screenshot the result card + # (chart + KPIs) directly so the preview is compact and meaningful. + backtest_path = OUT_DIR / "web-console-backtest.png" + print(f"[capture] {FRONTEND_URL}/backtest -> {backtest_path.relative_to(ROOT)}") + page.goto(f"{FRONTEND_URL}/backtest", wait_until="networkidle", timeout=60_000) + time.sleep(1.5) + _try_run_backtest(page) + + # Prefer the result card (Results: ...) so the screenshot is focused. + chart_card = page.locator(".dark-card", has=page.locator(".backtest-chart")) + if chart_card.count() > 0: + chart_card.first.scroll_into_view_if_needed() + time.sleep(1.0) + chart_card.first.screenshot(path=str(backtest_path)) + _trim_trailing_blank(backtest_path) + else: + page.screenshot(path=str(backtest_path), full_page=False) + finally: + browser.close() + + print("[done] previews saved under docs/assets/") + return 0 + + +def _trim_trailing_blank(path: pathlib.Path) -> None: + """Crop a screenshot's trailing rows of near-uniform background colour + (the result card often extends past its visible content).""" + try: + from PIL import Image + except ImportError: + return + with Image.open(path) as im: + rgb = im.convert("RGB") + w, h = rgb.size + # Background colour sampled from the bottom-right (almost always the + # blank card body padding beyond the last rendered child). + bg = rgb.getpixel((w - 4, h - 4)) + + def _row_is_blank(y: int) -> bool: + sample_xs = range(0, w, max(1, w // 60)) + for x in sample_xs: + r, g, b = rgb.getpixel((x, y)) + if abs(r - bg[0]) + abs(g - bg[1]) + abs(b - bg[2]) > 36: + return False + return True + + last_content_row = h - 1 + while last_content_row > 0 and _row_is_blank(last_content_row): + last_content_row -= 1 + new_h = min(h, last_content_row + 24) + if new_h < h - 8: + rgb.crop((0, 0, w, new_h)).save(path) + print(f"[trim] {path.name}: {h} -> {new_h} px") + + +if __name__ == "__main__": + sys.exit(main())