{ "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 }