量化系统定时任务实战:Cron + APScheduler + 企业微信通知

「数据会自己跑」不是口号,是刚需。本文给出两种定时方案 + 微信推送,让你的量化系统真正无人值守。

背景

Docker 把系统跑起来了,但数据不会自己更新。你不可能每天 15:30 手动打开终端:

bash 复制代码
python fetch_data.py   # 拉数据
python strategy.py     # 跑策略
python alert.py        # 发通知

定时任务搞定这最后一步。


方案速览

方案 一句话 推荐场景
Cron Linux 自带,定时执行命令 简单可靠,单机首选
APScheduler Python 库,代码管理调度 复杂调度、团队协作

新手直接用 Cron。零依赖,改不坏。


方案一:Cron(推荐)

1. 写执行脚本

run_daily.sh

bash 复制代码
#!/bin/bash
set -e

WORKDIR="/opt/quant-system"
cd "$WORKDIR"

log() {
    echo "[$(date '+%H:%M:%S')] $*"
}

log "===== 开始 ====="

# 1. 拉行情
log "拉取数据..."
docker compose exec -T backend python /app/fetch_data.py

# 2. 跑策略
log "执行策略..."
docker compose exec -T backend python /app/run_strategy.py

# 3. 推微信
log "推送通知..."
docker compose exec -T backend python /app/send_alert.py

log "===== 完成 ====="

-T 参数:Cron 环境没有 tty,不加会报 the input device is not a TTY

2. 配置 Cron

bash 复制代码
crontab -e

添加:

cron 复制代码
# 每个交易日 15:30 执行
30 15 * * 1-5 /opt/quant-system/run_daily.sh >> /var/log/quant/cron.log 2>&1

3. Cron 表达式速查

text 复制代码
* * * * * command
│ │ │ │ │
│ │ │ │ └─ 星期 (0=周日, 1-5=周一到周五)
│ │ │ └─── 月 (1-12)
│ │ └───── 日 (1-31)
│ └─────── 时 (0-23)
└───────── 分 (0-59)
需求 表达式
每个交易日 15:30 30 15 * * 1-5
每天 9:00 0 9 * * *
每 6 小时 0 */6 * * *
每周日 20:30 30 20 * * 0

4. Cron 避坑

bash 复制代码
# ❌ 坑1:环境变量缺失,找不到 docker
* * * * * docker compose exec backend python app.py

# ✅ 用绝对路径
* * * * * /usr/bin/docker compose -f /opt/quant/docker-compose.yml exec -T backend python /app/fetch_data.py

# ❌ 坑2:stderr 丢失
* * * * * script.sh >> log.txt

# ✅ stdout 和 stderr 都记录
* * * * * script.sh >> log.txt 2>&1

# ❌ 坑3:上次任务没跑完,新任务又启动 → 数据库写入冲突

# ✅ 加文件锁
* * * * * flock -n /tmp/quant.lock /opt/quant/run_daily.sh

方案二:APScheduler

适合想把调度逻辑写在代码里、享受 Git 版本管理的场景。

安装

bash 复制代码
pip install apscheduler

基础示例

python 复制代码
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def daily_job():
    """每日任务"""
    logger.info("开始拉数据...")
    # fetch_data()
    # run_strategies()
    logger.info("完成 ✅")

scheduler = BlockingScheduler()

# 周一到周五 15:30
scheduler.add_job(
    daily_job,
    'cron',
    id='daily_job',
    day_of_week='mon-fri',
    hour=15,
    minute=30,
    misfire_grace_time=600  # 错过 10 分钟内补执行
)

scheduler.start()

多任务 + 防冲突

python 复制代码
from apscheduler.executors.pool import ThreadPoolExecutor

executors = {
    'default': ThreadPoolExecutor(max_workers=3)
}

job_defaults = {
    'coalesce': True,       # 堆积合并
    'max_instances': 1,     # 不并发
}

scheduler = BlockingScheduler(
    executors=executors,
    job_defaults=job_defaults,
    timezone='Asia/Shanghai'
)

# 按先后顺序拆成三个任务
scheduler.add_job(fetch_data,    'cron', id='1_fetch',   day_of_week='mon-fri', hour=15, minute=30)
scheduler.add_job(run_strategy,  'cron', id='2_strategy',day_of_week='mon-fri', hour=15, minute=35)
scheduler.add_job(send_alert,    'cron', id='3_alert',   day_of_week='mon-fri', hour=15, minute=36)

scheduler.start()

max_instances=1:同个任务只能有一个实例在跑。上一次没跑完,这次自动跳过。


企业微信通知

获取 Webhook

企业微信 → 群聊 → 群机器人 → 添加 → 复制 Webhook URL

ini 复制代码
https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx

发送信号

python 复制代码
import requests
from datetime import datetime

WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY"

def send_signal(symbol, signal_type, price):
    """推送交易信号"""
    emoji = "📈" if signal_type == "BUY" else "📉"
    
    payload = {
        "msgtype": "markdown",
        "markdown": {
            "content": (
                f"## {emoji} 量化信号提醒\n"
                f"> 标的:**{symbol}**\n"
                f"> 信号:<font color=\"warning\">{signal_type}</font>\n"
                f"> 价格:{price}\n"
                f"> 时间:{datetime.now().strftime('%Y-%m-%d %H:%M')}"
            )
        }
    }
    
    resp = requests.post(WEBHOOK_URL, json=payload, timeout=10)
    return resp.json().get("errcode") == 0

def send_error(msg):
    """异常告警"""
    payload = {
        "msgtype": "text",
        "text": {
            "content": f"⚠️ 系统异常\n{msg}"
        }
    }
    requests.post(WEBHOOK_URL, json=payload, timeout=10)

异常捕获示例

python 复制代码
def fetch_data():
    try:
        # 拉数据...
        logger.info("拉取完成")
    except Exception as e:
        logger.error(f"拉取失败: {e}")
        send_error(str(e))  # 微信告警

scheduler.add_job(fetch_data, 'cron', ...)

Docker 环境下运行

docker-compose.yml 中让 backend 启动 APScheduler:

yaml 复制代码
backend:
  # ...
  command: python scheduler.py   # 替换原来的 app.py

或用宿主机 Cron 调用容器:

bash 复制代码
30 15 * * 1-5 docker compose -f /opt/quant/docker-compose.yml exec -T backend python /app/scheduler.py

最终效果

makefile 复制代码
15:30  自动拉取全市场日线数据
15:35  策略计算,生成买卖信号
15:36  微信推送 → 手机响 → 看一眼信号 → 决策

全程零手动。你只需要看手机。


总结

text 复制代码
定时触发 ── Cron / APScheduler
    │
    ├── 拉数据 ── akshare / tushare
    ├── 跑策略 ── 你的量化逻辑
    └── 发通知 ── 企业微信 Webhook

三件事,一个 cron 表达式搞定。量化系统的自动化程度,决定了你能把精力花在策略上还是运维上。


相关推荐
用户559135782631 天前
量化系统 Docker 部署实战:docker-compose 一键拉起 PostgreSQL + 策略引擎
产品
修己xj2 天前
项目代号:吞金兽1.0 —— 从立项到半岁,一个家庭的项目管理实战纪实
产品
极客三刀流2 天前
idea执行maven里的生命周期,报错信息'powershell' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 Cannot start maven from wrapper
产品
用户559135782632 天前
量化交易 PostgreSQL 建表指南:K 线数据 + 信号记录 + 性能优化
产品
极客三刀流3 天前
windows版jdk版本管理工具
产品
用户559135782636 天前
四大免费数据源代码实测
产品
用户559135782637 天前
Python + PostgreSQL + Docker 技术选型实战
产品
AskHarries8 天前
我如何从1000个产品里筛出方向
人工智能·产品·全栈
华洛9 天前
讲讲如何在传统产品中挖掘AI需求
javascript·产品经理·产品