feat: add Quant OS A-share baseline

This commit is contained in:
2026-07-26 12:54:04 +08:00
commit 48c5f64bbd
98 changed files with 31874 additions and 0 deletions
+235
View File
@@ -0,0 +1,235 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Quant OS / quant60 BaselineColab 与本地可复现实验\n",
"\n",
"这份 notebook 不保存账号、密码或 token。先在下面设置可选运行开关;默认流程不需要账号。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"optional"
]
},
"outputs": [],
"source": [
"RUN_JQDATA = False\n",
"JQDATA_START = \"2024-01-02\"\n",
"JQDATA_AS_OF = \"2024-06-28\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 默认工程验证\n",
"\n",
"这份 notebook 不保存账号、密码或 token。它会定位已有源码,或在 Colab 的空运行时从 `git.gomars.fun` 克隆代码,然后运行与 CI 相同的测试、bundle、合成回测和证据校验。合成数据只验证工程语义,不证明投资收益。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pathlib import Path\n",
"import json\n",
"import os\n",
"import shutil\n",
"import subprocess\n",
"import sys\n",
"\n",
"REPO_URL = \"https://git.gomars.fun/boat/quants-strategies.git\"\n",
"REPO_BRANCH = \"master\"\n",
"\n",
"def run(command, *, env=None):\n",
" print(\"+\", \" \".join(str(item) for item in command))\n",
" subprocess.run(command, check=True, env=env)\n",
"\n",
"cwd = Path.cwd().resolve()\n",
"if cwd.name == \"quant-os\" and (cwd / \"pyproject.toml\").is_file():\n",
" project = cwd\n",
"elif (cwd / \"quant-os\" / \"pyproject.toml\").is_file():\n",
" project = cwd / \"quant-os\"\n",
"else:\n",
" checkout = Path(\"/content/quants-strategies\")\n",
" if not checkout.exists():\n",
" run([\"git\", \"clone\", \"--depth\", \"1\", \"--branch\", REPO_BRANCH, REPO_URL, str(checkout)])\n",
" project = checkout / \"quant-os\"\n",
"\n",
"if not (project / \"pyproject.toml\").is_file():\n",
" raise RuntimeError(f\"Quant OS project not found at {project}\")\n",
"os.chdir(project)\n",
"print({\"project\": str(project), \"python\": sys.version})"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pythonpath = os.pathsep.join([\"src\", \".\"])\n",
"child_env = dict(os.environ, PYTHONPATH=pythonpath)\n",
"smoke_dir = Path(\"artifacts/colab-smoke\")\n",
"research_dir = Path(\"artifacts/colab-research\")\n",
"for generated_dir in (smoke_dir, research_dir):\n",
" if generated_dir.is_symlink():\n",
" raise RuntimeError(f\"refusing to replace symlinked output: {generated_dir}\")\n",
" if generated_dir.exists():\n",
" shutil.rmtree(generated_dir)\n",
"run([sys.executable, \"-m\", \"unittest\", \"discover\", \"-s\", \"tests\", \"-p\", \"*.py\", \"-q\"], env=child_env)\n",
"run([sys.executable, \"tools/bundle_platforms.py\"])\n",
"run([sys.executable, \"-m\", \"quant60\", \"smoke\", \"--config\", \"configs/baseline.json\", \"--output\", str(smoke_dir)], env=child_env)\n",
"run([sys.executable, \"-m\", \"quant60\", \"verify-manifest\", str(smoke_dir / \"manifest.json\")], env=child_env)\n",
"run([sys.executable, \"-m\", \"quant60\", \"research-smoke\", \"--output\", str(research_dir)], env=child_env)\n",
"run([sys.executable, \"-m\", \"quant60\", \"verify-research-manifest\", str(research_dir / \"manifest.json\")], env=child_env)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"report = json.loads(Path(\"artifacts/colab-smoke/report.json\").read_text(encoding=\"utf-8\"))\n",
"summary = {key: report[key] for key in [\"run_id\", \"trading_days\", \"order_count\", \"fill_count\", \"total_return\", \"max_drawdown\", \"investment_value_claim\"]}\n",
"print(json.dumps(summary, ensure_ascii=False, indent=2))\n",
"assert summary[\"investment_value_claim\"] is False\n",
"assert summary[\"fill_count\"] > 0\n",
"research_report = json.loads(Path(\"artifacts/colab-research/report.json\").read_text(encoding=\"utf-8\"))\n",
"research_summary = {key: research_report[key] for key in [\"run_id\", \"sample_count\", \"fold_count\", \"mean_oos_rank_ic\", \"target_gross_weight\", \"investment_value_claim\"]}\n",
"print(json.dumps(research_summary, ensure_ascii=False, indent=2))\n",
"assert research_summary[\"investment_value_claim\"] is False\n",
"assert research_summary[\"fold_count\"] > 0"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"equity = json.loads(Path(\"artifacts/colab-smoke/equity.json\").read_text(encoding=\"utf-8\"))\n",
"try:\n",
" import matplotlib.pyplot as plt\n",
"except ImportError:\n",
" print(\"matplotlib is optional; equity artifact is still available\")\n",
"else:\n",
" plt.figure(figsize=(10, 4))\n",
" plt.plot([row[\"date\"] for row in equity], [row[\"equity\"] for row in equity])\n",
" plt.title(\"Quant60 synthetic engineering smoke (not an investment result)\")\n",
" plt.xticks(rotation=45)\n",
" plt.tight_layout()\n",
" chart_path = Path(\"artifacts/colab-smoke/equity.png\")\n",
" plt.savefig(chart_path, dpi=140)\n",
" plt.close()\n",
" print({\"equity_chart\": str(chart_path.resolve())})"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 可选:JQData 授权快照 → PIT 回测与决策\n",
"\n",
"把首个代码 cell 的 `RUN_JQDATA` 改为 `True` 后,账号和密码只通过无回显交互输入,不写入 cell、artifact 或 Git。`JQDATA_AS_OF` 必须是区间内的真实交易日。该路径生成 provider snapshot、同一 `portable-momentum-v1` 的本地历史回测和单日信号/目标,并逐层验证;它不是聚宽 hosted 回测。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"optional"
]
},
"outputs": [],
"source": [
"if RUN_JQDATA:\n",
" run([sys.executable, \"-m\", \"pip\", \"install\", \"-r\", \"requirements/jqdata.txt\"])\n",
" jq_snapshot = Path(\"artifacts/jqdata-csi500\")\n",
" jq_decision = Path(\"artifacts/jqdata-csi500-decision\")\n",
" jq_backtest = Path(\"artifacts/jqdata-csi500-backtest\")\n",
" run([sys.executable, \"tools/jqdata_snapshot.py\", \"--index\", \"000905.XSHG\", \"--start\", JQDATA_START, \"--end\", JQDATA_AS_OF, \"--output\", str(jq_snapshot)], env=child_env)\n",
" run([sys.executable, \"-m\", \"quant60\", \"snapshot-backtest\", \"--snapshot\", str(jq_snapshot / \"manifest.json\"), \"--config\", \"configs/baseline.json\", \"--output\", str(jq_backtest)], env=child_env)\n",
" run([sys.executable, \"-m\", \"quant60\", \"verify-manifest\", str(jq_backtest / \"manifest.json\")], env=child_env)\n",
" run([sys.executable, \"-m\", \"quant60\", \"snapshot-decision\", \"--snapshot\", str(jq_snapshot / \"manifest.json\"), \"--config\", \"configs/baseline.json\", \"--as-of\", JQDATA_AS_OF, \"--equity\", \"1000000\", \"--output\", str(jq_decision)], env=child_env)\n",
" run([sys.executable, \"-m\", \"quant60\", \"verify-snapshot-decision\", str(jq_decision / \"manifest.json\")], env=child_env)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 可选:Qlib 0.9.7 原生 smoke\n",
"\n",
"只在 Python 3.12 环境把下面的 `RUN_QLIB` 改为 `True`。安装和运行结果必须单独保存;未执行时不得写成已验证。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"optional"
]
},
"outputs": [],
"source": [
"RUN_QLIB = False\n",
"if RUN_QLIB:\n",
" if sys.version_info[:2] != (3, 12):\n",
" raise RuntimeError(\"Qlib adapter is pinned to CPython 3.12\")\n",
" run([sys.executable, \"-m\", \"pip\", \"install\", \"-r\", \"requirements/research-py312.txt\"])\n",
" fixture = Path(\"artifacts/qlib-tiny-provider\")\n",
" run([sys.executable, \"tools/build_qlib_tiny_fixture.py\", str(fixture), \"--days\", \"80\"])\n",
" run([sys.executable, \"-m\", \"platforms.qlib_runner\", \"--provider-uri\", str(fixture), \"--market\", \"csi300\", \"--benchmark\", \"SH000300\", \"--start\", \"2024-02-01\", \"--end\", \"2024-04-19\", \"--lookback\", \"20\", \"--topk\", \"1\", \"--n-drop\", \"1\", \"--rebalance\", \"weekly\", \"--output-json\", \"artifacts/qlib-native/result.json\"], env=child_env)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"smoke_archive = shutil.make_archive(\"artifacts/quant-os-colab-smoke\", \"zip\", \"artifacts/colab-smoke\")\n",
"research_archive = shutil.make_archive(\"artifacts/quant-os-colab-research\", \"zip\", \"artifacts/colab-research\")\n",
"download_artifacts = [smoke_archive, research_archive]\n",
"if Path(\"artifacts/jqdata-csi500\").is_dir():\n",
" download_artifacts.append(shutil.make_archive(\"artifacts/quant-os-jqdata-snapshot\", \"zip\", \"artifacts/jqdata-csi500\"))\n",
"if Path(\"artifacts/jqdata-csi500-decision\").is_dir():\n",
" download_artifacts.append(shutil.make_archive(\"artifacts/quant-os-jqdata-decision\", \"zip\", \"artifacts/jqdata-csi500-decision\"))\n",
"if Path(\"artifacts/jqdata-csi500-backtest\").is_dir():\n",
" download_artifacts.append(shutil.make_archive(\"artifacts/quant-os-jqdata-backtest\", \"zip\", \"artifacts/jqdata-csi500-backtest\"))\n",
"if Path(\"artifacts/qlib-native\").is_dir():\n",
" download_artifacts.append(shutil.make_archive(\"artifacts/quant-os-qlib-native\", \"zip\", \"artifacts/qlib-native\"))\n",
"print({\"download_artifacts\": download_artifacts})"
]
}
],
"metadata": {
"colab": {
"name": "quant_os_colab.ipynb",
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}