feat: add Quant OS A-share baseline
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Quant OS / quant60 Baseline:Colab 与本地可复现实验\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
|
||||
}
|
||||
Reference in New Issue
Block a user