第二十五章:套利策略的自动化部署

说实话,很多做套利的朋友,策略逻辑写得漂亮,回测曲线也好看。但一到实盘,就栽在「部署」这两个字上。

我见过最夸张的一次——有个团队写了三个月的跨品种套利策略,上线第一天晚上服务器宕机了,没人知道。第二天早上起来一看,亏了二十多万。你说冤不冤?

所以这一章,咱们就聊聊怎么把套利策略稳稳当当地跑起来。不是那种「装个Python就能跑」的简单教程,而是真正生产级别的部署方案。

25.1 服务器环境配置

25.1.1 选型建议

我个人习惯用Linux服务器跑套利策略。为什么?稳定、资源占用低、Cron原生支持。

配置方面,我建议至少:

  • CPU:4核以上(跨品种套利计算量大)
  • 内存:8GB起步(行情数据缓存很吃内存)
  • 磁盘:SSD 100GB以上(日志和数据库写入频繁)
  • 网络:BGP多线,延迟越低越好
我的经验:别用Windows服务器跑量化策略。我在项目中遇到过Windows自动更新重启导致策略中断的情况,那次损失虽然不大,但教训深刻。

25.1.2 基础环境搭建

嗯,这里我直接给一套我常用的初始化脚本:

# 更新系统
sudo apt update && sudo apt upgrade -y

# 安装Python 3.10
sudo apt install python3.10 python3.10-venv -y

# 安装系统依赖
sudo apt install build-essential libssl-dev libffi-dev -y

# 创建虚拟环境
python3.10 -m venv /opt/arbitrage_env
source /opt/arbitrage_env/bin/activate

# 安装核心库
pip install pandas numpy ccxt sqlalchemy psycopg2-binary
pip install requests websocket-client schedule

你想想看,为什么一定要用虚拟环境?因为不同策略可能依赖不同版本的库。我曾经因为升级了pandas版本,导致一个老策略的数据处理逻辑全崩了。从那以后,每个策略我都单独建虚拟环境。

25.2 定时任务(Cron)

25.2.1 为什么用Cron

套利策略的触发时机很关键。有的策略需要每分钟检查一次价差,有的只需要每天收盘后做一次调仓。Cron就是干这个的——定时执行你的Python脚本。

说白了,Cron就是Linux系统自带的「闹钟」。你告诉它「每天下午3点跑这个脚本」,它就会准时执行。

25.2.2 实战配置

先看看我的Cron配置模板:

# 编辑crontab
crontab -e

# 每分钟检查一次价差(高频套利)
* * * * * /opt/arbitrage_env/bin/python /opt/strategies/spread_monitor.py >> /var/log/arbitrage/spread.log 2>&1

# 每小时做一次持仓检查
0 * * * * /opt/arbitrage_env/bin/python /opt/strategies/position_check.py >> /var/log/arbitrage/position.log 2>&1

# 每天收盘后做一次结算(下午3点)
0 15 * * 1-5 /opt/arbitrage_env/bin/python /opt/strategies/settlement.py >> /var/log/arbitrage/settlement.log 2>&1
注意:Cron的环境变量和登录shell不一样。很多新手直接在Cron里写 python script.py,结果发现找不到模块。一定要用绝对路径指定Python解释器!

我曾经犯过一个低级错误——在Cron里用了相对路径,结果脚本一直没跑起来。排查了整整两天才发现是路径问题。嗯,从那以后我所有Cron任务都用绝对路径。

25.2.3 日志重定向

你看上面的配置,每个任务后面都跟了 >> /var/log/arbitrage/xxx.log 2>&1。这行代码的意思是:

  • >>:把输出追加到日志文件(不覆盖)
  • 2>&1:把错误信息也重定向到同一个文件

为什么要这么做?因为Cron默认不会保存输出。如果脚本报错了,你连错误信息都看不到。我建议所有生产环境的Cron任务都加上日志重定向。

25.3 日志监控

25.3.1 日志分级

日志不是随便写的。我一般分三个级别:

级别 用途 示例
INFO 记录正常操作 「价差检查完成,当前价差0.23%」
WARNING 记录异常但不影响运行 「交易所API响应延迟超过500ms」
ERROR 记录需要人工干预的错误 「下单失败:余额不足」

我建议在Python脚本里用logging模块,而不是print。为什么?因为logging可以控制输出级别、格式化时间戳、还能同时输出到文件和终端。

import logging
import logging.handlers

# 配置日志
logger = logging.getLogger('arbitrage')
logger.setLevel(logging.INFO)

# 文件日志(每天轮转)
file_handler = logging.handlers.TimedRotatingFileHandler(
    '/var/log/arbitrage/strategy.log',
    when='midnight',
    backupCount=30
)
file_handler.setLevel(logging.INFO)

# 终端日志
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)

# 格式化
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

logger.addHandler(file_handler)
logger.addHandler(console_handler)

# 使用示例
logger.info('策略启动成功')
logger.warning('交易所A响应延迟偏高')
logger.error('下单失败:余额不足')

25.3.2 日志轮转

日志文件会越来越大。如果不做轮转,一个月下来可能几十个GB。上面代码里的 TimedRotatingFileHandler 就是干这个的——每天生成一个新日志文件,保留最近30天的。

我建议保留至少30天的日志。为什么?因为有些问题可能过了两周才暴露出来,你需要回溯历史日志才能定位。

25.4 异常报警机制

25.4.1 报警分级

不是所有异常都需要报警。我一般分三级:

  • P0(紧急):策略停止运行、资金异常、连续亏损超过阈值 → 电话/短信通知
  • P1(重要):交易所API异常、网络波动、价差异常 → 微信/钉钉通知
  • P2(一般):日志轮转失败、非关键数据缺失 → 邮件通知,次日查看

25.4.2 实现方案

我常用的报警方式有两种:

方案一:钉钉/企业微信机器人

import requests
import json

def send_dingtalk_alert(message, level='INFO'):
    webhook_url = 'https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN'
    
    data = {
        "msgtype": "text",
        "text": {
            "content": f"[{level}] 套利策略报警\n{message}"
        }
    }
    
    headers = {'Content-Type': 'application/json'}
    response = requests.post(webhook_url, data=json.dumps(data), headers=headers)
    return response.ok

方案二:短信报警(重要报警用)

def send_sms_alert(message):
    # 使用阿里云短信服务
    import aliyunsdkcore.client
    import aliyunsdkdysmsapi.request.v20170525
    
    # 配置你的AccessKey
    client = AcsClient('your-access-key', 'your-access-secret', 'cn-hangzhou')
    
    request = SendSmsRequest.SendSmsRequest()
    request.set_PhoneNumbers('13800138000')
    request.set_SignName('量化报警')
    request.set_TemplateCode('SMS_123456789')
    request.set_TemplateParam(json.dumps({"message": message}))
    
    response = client.do_action_with_exception(request)
    return response
核心原则:报警不是越多越好。如果每个小错误都报警,团队很快就会「报警疲劳」,真正出大事时反而没人关注。我建议P0报警直接打电话,P1报警发到工作群,P2报警发邮件。

25.5 知识体系总览

下面这张图,是我对套利策略自动化部署的整体理解:

套利策略自动化部署体系 套利策略脚本 Cron 定时调度 执行环境 Python 3.10 虚拟环境 系统依赖 监控与报警 日志记录 日志轮转 报警通知 短信/钉钉 日志文件 / 数据库

你看这个结构,从策略脚本到Cron调度,再到执行环境、监控报警,最后落到存储。每一层都环环相扣。少了任何一层,整个系统都不够健壮。

25.6 避坑指南

最后,分享几个我踩过的坑:

  • 不要用root跑策略:我曾经用root跑策略,结果一个bug把系统文件删了。现在所有策略都用普通用户运行。
  • Cron的PATH问题:Cron的环境变量很少。我习惯在脚本开头手动设置PATH。
  • 日志磁盘写满:有一次日志文件把磁盘写满了,策略直接崩溃。从那以后我加了磁盘使用率监控。
  • 报警不要只依赖一种方式:钉钉可能宕机,短信可能延迟。我建议至少两种报警渠道。

嗯,自动化部署这件事,说白了就是「把人的不确定性降到最低」。机器不会累,不会忘,不会情绪化。但前提是——你得把环境配好、任务设好、日志管好、报警搭好。

这一套下来,你的套利策略才能真正做到「无人值守,安心睡觉」。


公众号:蓝海资料掘金营,微信deep3321