3. 策略代码工程化:项目目录结构设计、配置文件管理(YAML/JSON)、日志系统搭建(logging模块)
好,咱们进入第三个核心话题。代码能跑了,不代表能上线赚钱了。
实盘和回测最大的区别是什么?回测挂了可以重来,实盘挂了,亏的是真金白银。所以,代码必须工程化。说白了,就是让你的策略代码像一台精密的仪器,而不是一堆实验台上的电线。
我个人习惯,在写第一行策略逻辑之前,先把架子搭好。这个架子,就是项目目录结构、配置文件管理和日志系统。这三样东西,决定了你未来运维的幸福感。
3.1 项目目录结构设计:别把鸡蛋放在一个篮子里
很多新手喜欢把所有代码塞进一个文件夹,甚至一个文件里。刚开始觉得方便,但策略一多,或者需要多人协作时,就彻底乱套了。
我见过最夸张的项目,一个 main.py 文件有 3000 行。你想改个参数,得在 3000 行里 Ctrl+F 找半天。这哪是交易,这是考古。
一个合理的目录结构,应该像这样:
my_quant_strategy/
├── config/ # 配置文件目录
│ ├── settings.yaml # 主配置
│ └── instruments.yaml # 交易标的配置
├── data/ # 数据相关
│ ├── fetcher.py # 数据获取
│ └── processor.py # 数据清洗
├── strategy/ # 策略逻辑
│ ├── base.py # 策略基类
│ ├── ma_cross.py # 均线策略
│ └── arbitrage.py # 套利策略
├── engine/ # 引擎核心
│ ├── backtest.py # 回测引擎
│ └── live.py # 实盘引擎
├── risk/ # 风控模块
│ └── manager.py # 风控管理器
├── utils/ # 工具函数
│ ├── logger.py # 日志配置
│ └── helpers.py # 辅助函数
├── logs/ # 日志输出目录
├── tests/ # 单元测试
│ └── test_strategy.py
├── requirements.txt # 依赖清单
└── main.py # 程序入口
核心原则:高内聚,低耦合。每个模块只做一件事,并且做好一件事。数据模块别去管策略逻辑,策略模块也别直接写文件。
嗯,这里要注意,logs/ 目录不要提交到 Git 仓库。记得在 .gitignore 里加上它。我曾经有一次不小心把几十 GB 的日志文件 push 到了远程仓库,结果同事拉代码拉了半小时,差点被骂死。
3.2 配置文件管理:把参数从代码里「赶」出去
为什么要把参数写进配置文件?因为实盘和回测的参数往往不一样。比如回测时手续费是万二,实盘时券商给你万一点五。你总不能每次改参数都去改代码吧?
我个人强烈推荐使用 YAML 格式。它比 JSON 更易读,支持注释,写起来也舒服。
一个典型的 settings.yaml 长这样:
# 主配置
exchange:
name: "binance"
api_key: "${BINANCE_API_KEY}" # 从环境变量读取,别硬编码!
api_secret: "${BINANCE_SECRET}"
strategy:
name: "ma_cross"
params:
fast_ma: 5
slow_ma: 20
position_size: 0.1 # 每次开仓比例
risk:
max_drawdown: 0.15 # 最大回撤 15%
max_position: 0.5 # 最大仓位 50%
logging:
level: "INFO"
file: "logs/trade.log"
避坑指南:API Key 和 Secret 绝对不要直接写在 YAML 文件里!用环境变量或者专门的密钥管理服务。我曾经见过有人把密钥提交到 GitHub 公开仓库,结果几分钟内账户就被盗了,损失惨重。
Python 读取 YAML 的代码示例:
import yaml
import os
def load_config(path="config/settings.yaml"):
with open(path, 'r') as f:
config = yaml.safe_load(f)
# 替换环境变量占位符
for key, value in config.items():
if isinstance(value, str) and value.startswith("${"):
env_var = value[2:-1]
config[key] = os.environ.get(env_var)
return config
# 使用
config = load_config()
print(config['strategy']['params']['fast_ma']) # 输出 5
你看,这样改参数只需要改 YAML 文件,代码完全不用动。而且 YAML 支持嵌套结构,比扁平化的 JSON 清晰多了。
3.3 日志系统搭建:让程序「开口说话」
实盘交易时,程序在后台跑,你不可能一直盯着屏幕。那怎么知道它有没有正常执行?有没有报错?有没有成交?
答案就是日志。日志是程序的「黑匣子」。出了问题,第一件事就是翻日志。
Python 自带的 logging 模块足够强大,别用 print。为什么?因为 print 只能输出到控制台,而 logging 可以同时输出到控制台、文件、甚至网络。
一个标准的日志配置:
import logging
import logging.handlers
import os
def setup_logger(name="trading", log_file="logs/trade.log"):
# 创建日志目录
os.makedirs(os.path.dirname(log_file), exist_ok=True)
# 创建 logger
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
# 格式:时间 - 级别 - 模块 - 消息
formatter = logging.Formatter(
'%(asctime)s - %(levelname)s - %(name)s - %(message)s'
)
# 控制台处理器(输出 INFO 及以上)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
# 文件处理器(输出 DEBUG 及以上,按天轮转)
file_handler = logging.handlers.TimedRotatingFileHandler(
log_file, when='midnight', backupCount=30
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
# 添加处理器
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger
# 使用
logger = setup_logger()
logger.info("策略启动成功")
logger.warning("连接交易所超时,正在重试...")
logger.error("下单失败:余额不足")
注意:日志文件一定要做轮转(Rotating)。否则跑个一年半载,日志文件能大到几百 GB,磁盘爆了程序就挂了。我建议按天轮转,保留 30 天。
日志级别怎么选?
| 级别 | 使用场景 | 示例 |
|---|---|---|
| DEBUG | 调试信息,开发时用 | 「收到 ticker: BTC/USDT, price=50000」 |
| INFO | 正常流程信息 | 「策略启动成功」「订单已提交」 |
| WARNING | 可能有问题,但不影响运行 | 「网络延迟较高」「API 限频接近阈值」 |
| ERROR | 发生了错误,但程序还能跑 | 「下单失败:价格超出限制」 |
| CRITICAL | 严重错误,程序即将退出 | 「数据库连接丢失,无法恢复」 |
实盘时,我一般把控制台设为 INFO 级别,文件设为 DEBUG 级别。这样平时看控制台只看到关键信息,出问题时再去翻详细的 DEBUG 日志。
3.4 知识体系总览
下面这张图,帮你把这三块内容串起来。你想想看,一个策略从启动到运行,数据是怎么流动的?
说白了,目录结构是骨架,配置文件是血肉,日志系统是神经。三者缺一不可。
我的个人习惯:每次新建一个策略项目,先花 10 分钟把目录结构、配置文件和日志系统搭好。这 10 分钟,能为你未来省下 10 个小时的排查时间。
好了,这一章的内容就到这里。记住,工程化不是炫技,是为了让你睡得安稳。实盘时,日志里每一行输出,都是你判断系统是否健康的依据。
公众号:蓝海资料掘金营,微信deep3321