【Python量化系统工程实战 #06】从本地脚本到云端部署:量化系统的最小可运行架构

写了一大堆 .py 文件,换个电脑就跑不通;配置文件写死在代码里,改个参数要翻三页;别人接手你的项目,三天没看懂目录结构。本篇给你一个「拿到就能用」的量化项目骨架,含目录规范、配置分离、CLI 入口、环境自检。

一、为什么需要「项目骨架」

个人研究阶段,一个 main.py 解决所有问题。但当你想:

  • 换台机器复现回测结果
  • 让同事协作开发策略
  • 把脚本部署到服务器自动跑
  • 三个月后再回来看自己的代码

就必须有统一的目录结构、配置管理、依赖清单、启动方式。否则每次迁移都是灾难。

二、本文你将得到什么

  1. 标准目录结构:data / config / logs / strategies 各司其职
  2. 配置与代码分离:JSON 配置外置,改参数不动代码
  3. CLI 一键入口 :python main.py init 生成骨架,python main.py run 执行流水线
  4. 环境自检脚本:启动前自动检查 Python 版本和依赖包

三、标准目录结构

推荐的最小可运行结构:

bash 复制代码
my_quant_project/
├── config/
│   ├── settings.json       # 业务配置(标的、数据源、风控阈值)
│   └── logging.json        # 日志配置
├── data/
│   ├── raw/                # 原始 K 线(从 API 拉取的)
│   └── processed/          # 清洗后数据(因子、信号)
├── logs/                   # 运行时日志
├── strategies/             # 策略脚本
├── requirements.txt        # 依赖清单
├── .gitignore
└── README.md

设计原则:

  • config/ 与代码分离:改参数不动 Python
  • data/raw/ 与 data/processed/ 分离:原始数据不可覆盖,加工数据可重新生成
  • logs/ 独立:策略跑了一个月,日志文件可能上 GB,单独目录方便清理和备份
  • requirements.txt 必须存在:没有它,换台机器就是「 import 地狱」

四、CLI 入口:三个命令搞定全部操作

用 argparse 做子命令入口,比一堆独立脚本好维护:

python 复制代码
import argparse
import json
import os

# 项目骨架模板
SCAFFOLD = {
    "dirs": ["data/raw", "data/processed", "logs", "strategies", "config"],
    "files": {
        "config/settings.json": {
            "data_source": "mairui_api",
            "stocks": ["600519", "000001", "300750"],
            "risk_limits": {"max_drawdown": -0.05, "position_pct": 0.8},
        },
        "requirements.txt": "mairui\npandas\nnumpy\nAPScheduler\n",
        ".gitignore": "__pycache__/\n*.pyc\n.env\nlogs/\n",
        "README.md": "# 量化系统\n\n## 启动\n```bash\npython main.py run --config config/settings.json\n```\n",
    },
}

def ensure_project(root="my_quant_project"):
    for d in SCAFFOLD["dirs"]:
        os.makedirs(os.path.join(root, d), exist_ok=True)
    for fname, content in SCAFFOLD["files"].items():
        path = os.path.join(root, fname)
        os.makedirs(os.path.dirname(path), exist_ok=True)
        text = json.dumps(content, indent=2) if isinstance(content, dict) else content
        with open(path, "w", encoding="utf-8") as f:
            f.write(text)
    print(f"[ok] 项目初始化完成: {root}")

def check_env():
    import sys
    py_ok = sys.version_info >= (3, 9)
    print(f"Python {sys.version.split()[0]}: {'✅' if py_ok else '❌'}")
    for pkg in ["mairui", "pandas", "numpy"]:
        try:
            __import__(pkg)
            print(f"{pkg}: ✅")
        except ImportError:
            print(f"{pkg}: ❌ (pip install {pkg})")
    return py_ok

def run_pipeline(config_path):
    with open(config_path, "r", encoding="utf-8") as f:
        cfg = json.load(f)
    print(f"数据源: {cfg['data_source']}")
    print(f"监控标的: {cfg['stocks']}")
    for code in cfg["stocks"]:
        print(f"[{code}] 拉取... 校验... PASS")
    print(f"[ok] 流水线完成,共处理 {len(cfg['stocks'])} 只标的")

命令注册:

python 复制代码
parser = argparse.ArgumentParser(description="量化系统")
sub = parser.add_subparsers(dest="cmd")

sub.add_parser("init", help="初始化项目骨架").add_argument("--name", default="my_quant_project")
sub.add_parser("env", help="环境自检")
run_p = sub.add_parser("run", help="运行流水线")
run_p.add_argument("--config", default="config/settings.json")

args = parser.parse_args()
if args.cmd == "init":
    ensure_project(args.name)
elif args.cmd == "env":
    check_env()
elif args.cmd == "run":
    run_pipeline(args.config)

实测运行:

bash 复制代码
$ python main.py init --name my_quant_project
[ok] 项目初始化完成: my_quant_project

$ python main.py env
Python 3.9.22: ✅
mairui: ✅
pandas: ✅
numpy: ✅

$ python main.py run --config my_quant_project/config/settings.json
数据源: mairui_api
监控标的: ['600519', '000001', '300750']
[600519] 拉取... 校验... PASS
[000001] 拉取... 校验... PASS
[300750] 拉取... 校验... PASS
[ok] 流水线完成,共处理 3 只标的

五、配置分离:JSON 外置 + 环境变量兜底

业务配置放 config/settings.json:

json 复制代码
{
  "data_source": "mairui_api",
  "licence_env": "MAIRUI_LICENCE",
  "stocks": ["600519", "000001", "300750"],
  "schedule": {"hour": 15, "minute": 35},
  "risk_limits": {"max_drawdown": -0.05, "position_pct": 0.8}
}

证书路径走环境变量,不硬编码:

python 复制代码
import os

licence = os.environ.get("MAIRUI_LICENCE", "")
if not licence:
    raise RuntimeError("请设置环境变量 MAIRUI_LICENCE")

好处:

  • 敏感信息(证书)不进 Git
  • 不同环境(开发/测试/生产)用不同的环境变量,同一套代码到处跑
  • 配置文件可版本化,证书由运维单独管理

六、常见坑

  1. .gitignore 漏掉日志和数据 :logs/ 和 data/raw/*.csv 一定要写进 .gitignore,否则 Git 仓库会膨胀到几百 MB。
  2. requirements.txt 不写版本号 :生产环境建议写 pandas==2.2.3,否则下次安装可能拿到不兼容的新版本。
  3. 路径用硬编码的 \\ :Windows 下 \\ 在 Linux 上跑不通。统一用 pathlib.Path 或 os.path.join。
  4. 忽略日志轮转 :长期运行的系统,logs/app.log 会无限增长。生产环境建议用 logging.handlers.RotatingFileHandler。

七、小结

本文给了一个「拿到就能用」的量化项目骨架:

  • 目录结构:data / config / logs / strategies 分离,职责清晰
  • CLI 入口:init / env / run 三个命令覆盖日常操作
  • 配置分离:业务参数 JSON 外置,证书走环境变量
  • 环境自检:启动前自动检查 Python 版本和依赖,失败即停

下一篇(#07)我们将解决「历史数据补采与回填」------断网、API 超时、新上市股票导致的数据缺口,怎么用断点续传 + 幂等写入补回来?


免责声明:本文仅供技术学习交流,不构成任何投资建议。量化策略回测表现不代表未来收益,投资有风险,决策需谨慎。

代码与文档 :github.com/MaiRuiApi

相关推荐
136096757232 小时前
页面打不开不是 Nginx 的错
前端·后端
ALONE阿龙太原微码2 小时前
RBAC 以及主流权限模型
后端
斑鸠喳喳2 小时前
线程本地存储 ThreadLocal
java·后端
你顶住我先撤2 小时前
RocketMQ 消息类型
后端
高频因子挖掘机3 小时前
批量行情返回后,怎样把请求失败的股票单独挑出来?
后端·github·api
小蒜学长4 小时前
在线保险服务与管理平台的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·服务平台·在线保险
我的div丢了肿么办5 小时前
go语言中管道 channel的使用
后端·go
Harvil_5 小时前
模型点三道菜,回喂被拒 400
后端
松就是我902985 小时前
如何设计多Agent
后端