Tech Whims

[58] Paper Trading 自动化方案

2026-03-26


执行体系 · 共 12 篇

Paper Trading 自动化方案与 IBKR Paper 实操指南

tradeSys #58 | 作者:娃彩 | 创建:2026-03-26 关联:#31 IBKR API 指南 | #32 启动清单 | #42 Phase 2 路线图 | #43 运维手册


一句话摘要

从 Phase 2 监控代码到无人值守 Paper Trading 30 天的完整方案:IBKR Paper Trading 端口 4002 与 Live 端口 4001 的隔离机制、macOS 自动化调度(cron vs launchd)、告警通知通道(飞书 webhook)、以及 Paper → Live 切换决策标准。


目录

  1. IBKR Paper Trading 核心机制
  2. 自动化调度方案
  3. 告警通知与每日摘要
  4. 30天 Paper Trading 验证方案
  5. Day 0 实操指南
  6. 故障排查与数据备份

第1章 IBKR Paper Trading 核心机制

1.1 Paper vs Live:关键差异

维度Paper TradingLive Trading影响评估
API 端口4002 (Gateway) / 7497 (TWS)4001 (Gateway) / 7496 (TWS)⚠️ 搞反会下真单
成交模拟基于实时报价模拟成交真实市场成交⚠️ Paper 成交价可能过于乐观
滑点模拟无真实滑点真实滑点Paper 表现可能优于实盘
市场数据延迟 10-15 分钟(免费)实时(需订阅)日内策略需订阅实时数据
资金$1M 虚拟(可调整)真实资金Paper 心理无压力
API 功能与 Live 完全一致完全一致✅ 代码无需修改
交易时间与真实市场一致完全一致✅ 包含盘前盘后

核心洞察:Paper Trading 的最大陷阱不是功能差异,而是成交质量幻觉——Paper 的模拟成交通常以 bid/ask 中间价成交,而实盘要考虑冲击成本和流动性。对于 Plan E3-AW 这种低频 ETF 再平衡策略,影响较小(月频、限价单、高流动性 ETF),但仍需在 Paper → Live 切换时预留 20-30% 滑点缓冲。

1.2 Paper Trading 账户初始化

Step 1:开通 Paper Trading 账户

Step 2:配置初始资金

Step 3:API 权限启用

1.3 ib_async 连接代码模板

from ib_async import *

# Paper Trading 连接(端口 4002)
ib = IB()
ib.connect('127.0.0.1', 4002, clientId=1)

# 验证连接:获取账户信息
account = ib.managedAccounts()[0]  # 应返回类似 'DU1234567' (Paper)
print(f"Connected to Paper Account: {account}")

# 安全校验:确保不是 Live 账户
if not account.startswith('DU'):
    raise RuntimeError(f"WARNING: Connected to Live account {account}! Aborting.")

安全红线:每次连接后必须验证账户前缀。Paper 账户以 DU 开头,Live 账户以 U 开头(无 D)。这是防止 Paper → Live 端口配置错误导致真单的最后防线。


第2章 自动化调度方案

2.1 调度需求分析

任务频率执行时间执行时长依赖
宏观仪表盘检查每日06:30 UTC+8~30sIB Gateway 运行
行为纪律引擎每日06:35 UTC+8~10s
DCA 定投执行每月每月1日 09:30~2minIB Gateway + Paper 连接
季度再平衡季度季末检查~5minIB Gateway + Paper 连接
数据备份每日07:00 UTC+8~5s

时区选择:06:30 UTC+8(美东 18:30)是美股收盘后 1.5 小时,此时 IBKR 日终结算完成,数据最准确。

2.2 cron vs launchd 对比

维度cronlaunchd (推荐)
Mac 原生支持✅ Apple 推荐
睡眠唤醒❌ 睡眠期间跳过✅ 可配置唤醒执行
依赖管理手动处理✅ 可声明依赖
日志邮件或文件✅ 统一 system.log
用户感知无 GUI可配置用户通知
复杂度简单中等

决策:使用 launchd 作为主力调度,原因:

  1. MacBook 可能睡眠,launchd 可配置 StartCalendarInterval + StandardOutPath 确保任务不丢
  2. 与 macOS 通知中心集成更好
  3. 可声明 IB Gateway 为依赖(虽然仍需手动启动 Gateway)

2.3 launchd plist 配置

文件: ~/Library/LaunchAgents/com.tradesys.daily-check.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.tradesys.daily-check</string>
    
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/python3</string>
        <string>/Users/mac/workspace/wacai/self/research/tradeSys/code/macro_dashboard.py</string>
        <string>--json</string>
    </array>
    
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>6</integer>
        <key>Minute</key>
        <integer>30</integer>
    </dict>
    
    <key>StandardOutPath</key>
    <string>/Users/mac/workspace/wacai/self/research/tradeSys/logs/daily-check.log</string>
    
    <key>StandardErrorPath</key>
    <string>/Users/mac/workspace/wacai/self/research/tradeSys/logs/daily-check.error.log</string>
    
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
    </dict>
</dict>
</plist>

加载命令

launchctl load ~/Library/LaunchAgents/com.tradesys.daily-check.plist
launchctl enable user/$(id - u)/com.tradesys.daily-check
launchctl start com.tradesys.daily-check  # 立即测试

第3章 告警通知与每日摘要

3.1 告警级别到通知方式的映射

告警级别触发条件通知方式响应时效
🔴 RED再平衡触发 / GLD 超限 / sUSDe 退出飞书消息 + 邮件 + 短信立即
🟠 ORANGEVIX >30 / DXY 强趋势飞书消息4小时内
🟡 YELLOW偏离度接近阈值 / 行为警告飞书消息24小时内
🟢 GREEN一切正常每日摘要(仅一次)

3.2 飞书 webhook 集成

创建 webhook

  1. 飞书群 → 设置 → 群机器人 → 添加机器人 → 自定义 webhook
  2. 复制 webhook URL(格式:https://open.feishu.cn/open-apis/bot/v2/hook/xxxx
  3. 保存到配置文件(不硬编码)

通知代码

import json
import urllib.request
from datetime import datetime

FEISHU_WEBHOOK = "https://open.feishu.cn/open-apis/bot/v2/hook/YOUR_TOKEN"

def send_feishu_alert(level: str, title: str, content: str):
    """发送飞书告警"""
    color_map = {
        "RED": "red",
        "ORANGE": "orange",
        "YELLOW": "yellow",
        "GREEN": "green"
    }
    
    payload = {
        "msg_type": "interactive",
        "card": {
            "config": {"wide_screen_mode": True},
            "header": {
                "title": {"tag": "plain_text", "content": f"[{level}] {title}"},
                "template": color_map.get(level, "blue")
            },
            "elements": [
                {"tag": "div", "text": {"tag": "lark_md", "content": content}}
            ]
        }
    }
    
    req = urllib.request.Request(
        FEISHU_WEBHOOK,
        data=json.dumps(payload).encode(),
        headers={"Content-Type": "application/json"},
        method="POST"
    )
    
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            return json.loads(resp.read())
    except Exception as e:
        print(f"Failed to send Feishu alert: {e}")
        return None

3.3 每日摘要邮件

对于 GREEN 状态,发送简洁摘要而非逐条通知:

Subject: [tradeSys] Daily Summary 2026-03-26 | 🟢 All Systems Healthy

Portfolio: $52,847 (+0.3%)
Allocation: GLD 25.1% | DBMF 24.8% | sUSDe 25.2% | BIL 24.9%
Drift: Max +0.3% (within ±5% threshold)
VIX: 18.2 (NORMAL regime)
DXY: 103.4 (neutral)
CTS: 0.85 (weak trend)

Actions: None required
Next Check: 2026-03-27 06:30

第4章 30天 Paper Trading 验证方案

4.1 验证目标

目标成功标准测量方式
连接稳定性30 天无中断每日连接成功率 100%
数据准确性持仓/价格与 IBKR 一致每日交叉验证
执行正确性订单类型、数量、价格正确订单日志审查
告警及时性RED 告警 < 1 分钟送达模拟触发测试
再平衡流程季度再平衡完整执行模拟再平衡测试

4.2 每日运行清单

自动化部分(launchd 执行):

人工确认部分(每天 2 分钟):

4.3 每周深度检查

检查项频率工具
净值曲线对比每周Paper 账户 vs 本地计算
订单历史审查每周IBKR 交易确认 vs 本地日志
偏离度计算验证每周手动计算 1-2 个标的
告警通道测试每周手动触发测试告警

4.4 Paper → Live 切换决策标准

必须同时满足以下条件

条件标准验证方式
运行时长≥ 30 天无中断日志审查
连接成功率≥ 99%每日连接日志
数据一致性持仓/价格误差 < 0.1%每日交叉验证
再平衡测试至少完成 1 次完整再平衡模拟或真实触发
告警测试RED/ORANGE 告警均成功送达手动触发测试
心理就绪老板确认对策略有信心主观评估

切换步骤

  1. 备份所有 Paper Trading 数据和配置
  2. 修改端口:4002 → 4001
  3. 修改账户验证:检查 Live 账户前缀 U(非 DU
  4. 首次连接:仅查询,不下单,验证持仓
  5. 首次下单:小额测试单(如 $100 GLD)
  6. 观察 1 周确认正常后,全量执行

第5章 Day 0 实操指南

5.1 准备工作清单

硬件/软件

账户配置

5.2 首次连接测试

Step 1:启动 IB Gateway

# 打开 IB Gateway 应用
open -a "IB Gateway"

# 登录选择:
# - Trading Mode: Paper Trading
# - Username: yourusername_paper
# - Password: your_password

Step 2:验证 API 连接

cd /Users/mac/workspace/wacai/self/research/tradeSys/code
python3 -c "
from ib_async import *
ib = IB()
try:
    ib.connect('127.0.0.1', 4002, clientId=1)
    account = ib.managedAccounts()[0]
    print(f'Connected to Paper Account: {account}')
    if not account.startswith('DU'):
        print('WARNING: Not a Paper account!')
    else:
        print('Paper account verified.')
    ib.disconnect()
except Exception as e:
    print(f'Connection failed: {e}')
"

Step 3:运行宏观仪表盘

python3 macro_dashboard.py --brief

5.3 首笔建仓(Paper Trading)

Plan E3-AW 初始配比

标的目标权重$50K 配额ETF 选择
GLD25%$12,500GLD (SPDR Gold Shares)
DBMF25%$12,500DBMF (iMGP DBi Managed Futures)
sUSDe25%$12,500sUSDe (链上,暂用 USDT 代理)
BIL25%$12,500BIL (SPDR Bloomberg 1-3 Month T-Bill)

建仓代码(仅 Paper Trading):

from ib_async import *

ib = IB()
ib.connect('127.0.0.1', 4002, clientId=1)

# 安全校验
account = ib.managedAccounts()[0]
assert account.startswith('DU'), f"Not Paper account: {account}"

# 定义合约
contracts = {
    'GLD': Stock('GLD', 'SMART', 'USD'),
    'DBMF': Stock('DBMF', 'SMART', 'USD'),
    'BIL': Stock('BIL', 'SMART', 'USD'),
}

# 获取当前价格并下单
orders = []
for symbol, contract in contracts.items():
    ib.qualifyContracts(contract)
    ticker = ib.reqMktData(contract, '', False, False)
    ib.sleep(2)  # 等待数据
    
    if ticker.marketPrice():
        shares = int(12500 / ticker.marketPrice())  # $12,500 per position
        order = MarketOrder('BUY', shares)
        trade = ib.placeOrder(contract, order)
        orders.append((symbol, trade))
        print(f"{symbol}: Buying {shares} shares @ ~{ticker.marketPrice()}")

# 等待成交
for symbol, trade in orders:
    while not trade.isDone():
        ib.sleep(1)
    print(f"{symbol}: {trade.orderStatus.status}")

ib.disconnect()

第6章 故障排查与数据备份

6.1 常见问题排查

问题可能原因解决方案
连接被拒绝Gateway 未运行 / 端口错误确认 Gateway 已登录,端口 4002
账户前缀非 DU连接到 Live 账户检查端口,4002 是 Paper
市场数据无返回未订阅实时数据使用延迟数据 reqMarketDataType(3)
订单被拒绝保证金不足 / 非交易时间检查 Paper 账户余额和交易时间
Gateway 崩溃内存不足增加内存配置到 4096 MB
连接断开超时 / 网络问题实现重连逻辑

6.2 重连逻辑模板

from ib_async import *
import time

MAX_RETRIES = 3
RETRY_DELAY = 5

def connect_with_retry(port=4002, client_id=1):
    ib = IB()
    for attempt in range(MAX_RETRIES):
        try:
            ib.connect('127.0.0.1', port, clientId=client_id)
            account = ib.managedAccounts()[0]
            if not account.startswith('DU'):
                raise RuntimeError(f"Connected to non-Paper account: {account}")
            print(f"Connected to {account}")
            return ib
        except Exception as e:
            print(f"Connection attempt {attempt + 1} failed: {e}")
            if attempt < MAX_RETRIES - 1:
                time.sleep(RETRY_DELAY)
    raise RuntimeError("Max connection retries exceeded")

6.3 数据备份方案

备份内容

数据路径备份频率
持仓状态data/portfolio_state.json每日
交易日志data/trade_log.json每日
监控历史data/*_history.json每日
配置文件code/config/变更时

备份脚本(每日 07:00 执行):

#!/bin/bash
BACKUP_DIR="/Users/mac/workspace/wacai/self/research/tradeSys/backups/$(date +%Y-%m-%d)"
DATA_DIR="/Users/mac/workspace/wacai/self/research/tradeSys/data"

mkdir -p "$BACKUP_DIR"
cp -r "$DATA_DIR"/*.json "$BACKUP_DIR/" 2>/dev/null

# 保留最近 90 天
find /Users/mac/workspace/wacai/self/research/tradeSys/backups -type d -mtime +90 -exec rm -rf {} \; 2>/dev/null

echo "Backup completed: $BACKUP_DIR"

6.4 监控健康检查

每日检查

# 检查 IB Gateway 是否运行
lsof -i :4002 | grep LISTEN

# 检查日志目录是否更新
ls -la /Users/mac/workspace/wacai/self/research/tradeSys/logs/

# 检查数据文件是否更新
ls -la /Users/mac/workspace/wacai/self/research/tradeSys/data/*.json

附录:独到见解与来源

独到见解(非 Common Sense)

  1. Paper 成交质量幻觉:Paper Trading 以 bid/ask 中间价成交,实盘要考虑冲击成本。对于 Plan E3-AW 低频 ETF 策略,建议预留 20-30% 滑点缓冲。

  2. 端口是最后一道防线:Paper 端口 4002 vs Live 端口 4001 的差异是防止真单的最后防线。代码中必须验证账户前缀(DU vs U)。

  3. launchd 优于 cron:MacBook 睡眠时 cron 会跳过任务,launchd 可以在唤醒后补执行。对于交易日检查,这是关键差异。

  4. Paper 账户验证必须自动化:手动检查账户类型容易疏忽。每次连接后自动验证 account.startswith('DU') 是必要的安全措施。

  5. 30 天验证的隐性价值:Paper Trading 30 天不只是测试代码,更重要的是测试心理——对策略的信心需要在无压力环境中建立。

可查证来源

  1. IBKR TWS API Documentation - https://ibkrcampus.com/ibkr-api-page/twsapi-doc/
  2. ib_async GitHub - https://github.com/ib-api-reloaded/ib_async
  3. IBKR Free Trial 说明 - https://www.interactivebrokers.com/en/trading/free-trial.php
  4. 已有研究 #31 IBKR API 指南 - tradeSys-ibkr-api-guide.md
  5. 已有研究 #43 运维手册 - tradeSys-year1-ops-manual.md

检查线自检

检查项状态
事实准确✅ 端口、账户前缀、API 调用均基于 IBKR 官方文档和 ib_async 官方示例
独到见解✅ 5 条非 Common Sense 洞察,包括成交质量幻觉、端口安全机制、launchd vs cron
收件人视角✅ 老板视角:Day 0 可直接操作,无需额外研究
风险评估✅ Paper → Live 切换需 6 个条件同时满足,包含心理就绪评估
可执行性✅ 完整代码模板、launchd 配置、备份脚本,可直接部署

文档完成于 2026-03-26 | 字数:约 3,200 字