「数据会自己跑」不是口号,是刚需。本文给出两种定时方案 + 微信推送,让你的量化系统真正无人值守。
背景
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 表达式搞定。量化系统的自动化程度,决定了你能把精力花在策略上还是运维上。