问题描述
解决方案
在PTrade量化交易平台中,策略的运行是基于事件驱动的。这意味着策略代码并非从头到尾线性执行,而是根据市场的时间状态(盘前、盘中、盘后)触发特定的函数。
以下是PTrade策略在不同时间点的运行机制、主要功能及区别的详细解释:
1. 盘前阶段 (Pre-Market)
盘前阶段主要用于每日交易开始前的准备工作,如数据更新、股票池调整和变量重置。
- 对应函数:
before_trading_start(context, data) - 运行时间:
- 回测模式:每个交易日的 08:30。
- 实盘/模拟交易:每个交易日的 09:10 (默认,具体视券商配置而定)。
- 核心机制与用途:
- 设置股票池:通常在此阶段调用
set_universe或get_index_stocks来确定当天要关注或交易的股票列表。 - 获取基础数据:适合调用
get_fundamentals(财务数据) 或get_history(历史行情) 来计算当天的静态指标(如均线、因子值)。 - 重置每日变量:如果策略中有需要每日清零的计数器或标志位(例如
g.buy_count = 0),应在此处重置。 - 注意事项:
- 在实盘的9:10分,交易所的集合竞价尚未开始或刚刚开始,此时调用
get_snapshot获取的实时行情(如开盘价、成交量)可能为0或无效数据。 - 不可下单:此阶段通常不进行委托下单操作(虽然API未完全禁止,但因无最新行情,下单极易失败或价格偏离)。
- 在实盘的9:10分,交易所的集合竞价尚未开始或刚刚开始,此时调用
- 设置股票池:通常在此阶段调用
2. 盘中阶段 (Intra-Day)
这是策略最核心的阶段,负责实时监控行情、生成交易信号并执行买卖操作。
- 对应函数:
handle_data(context, data):核心驱动函数。tick_data(context, data):Tick级高频驱动(仅实盘)。run_daily(context, func, time='...'):定时任务。run_interval(context, func, seconds=...):间隔任务(仅实盘)。
- 运行时间:
- 股票:09:30 ~ 11:30 和 13:00 ~ 15:00。
- 回测差异:分钟回测中,
handle_data每分钟运行一次;日线回测中,handle_data每天 15:00 运行一次(即收盘时)。
- 核心机制与用途:
- 行情监控:通过
data对象或get_snapshot获取当前的最新价格、成交量、五档盘口等。 - 逻辑判断:计算技术指标(如MACD、RSI),判断是否满足买入或卖出条件。
- 委托下单:调用
order、order_target、order_value等接口发送交易指令。 - 注意事项:
- 频率区别:
handle_data在分钟回测是每分钟K线结束时触发;tick_data是每3秒(或有新Tick时)触发,适合高频策略。 - 集合竞价:PTrade支持通过
run_daily设置在 9:15~9:25 之间运行函数,用于处理集合竞价逻辑(需券商支持)。
- 频率区别:
- 行情监控:通过
3. 盘后阶段 (Post-Market)
盘后阶段用于当天的总结、数据整理、文件导出或盘后固定价格交易。
- 对应函数:
after_trading_end(context, data) - 运行时间:
- 通常为交易日的 15:30 (具体视券商配置而定)。
- 核心机制与用途:
- 数据统计:统计当天的成交情况、持仓变化或计算当日收益率。
- 文件导出:使用
get_trades_file导出对账单,或将自定义数据保存到本地(如使用pickle持久化全局变量g)。 - 盘后业务:虽然大部分交易已结束,但科创板/创业板等支持盘后固定价格交易 (15:05-15:30)。如果策略涉及此类交易,需使用专门的
after_trading_order接口,且通常在盘中或盘后特定时间段调用。 - 注意事项:
- 此函数每天只运行一次。
- 在此阶段调用普通
order函数通常无效,因为连续竞价市场已关闭。
总结对比表
| 特性 | 盘前 (Before Market) | 盘中 (Intra-Day) | 盘后 (After Market) |
|---|---|---|---|
| 核心函数 | before_trading_start |
handle_data, tick_data |
after_trading_end |
| 典型时间 | 08:30 (回测) / 09:10 (实盘) | 09:30 ~ 15:00 | 15:30 |
| 主要任务 | 选股、取财务数据、重置变量 | 择时、计算信号、下单交易 | 总结、归档、持久化数据 |
| 行情数据 | 仅历史数据,实时数据可能无效 | 实时推送 (Tick/Bar) | 当日收盘数据已确定 |
| 能否交易 | 否 (通常) | 是 | 否 (除盘后固定价业务外) |
Q&A: 常见问题解答
Q1: initialize 函数属于哪个阶段?
A: initialize 不属于上述任何一个每日循环阶段。它只在策略启动时运行一次。用于初始化全局变量(如 g.security)和设置运行参数(如 set_commission)。它是在第一天的 before_trading_start 之前执行的。
Q2: 我可以在盘前阶段使用 get_snapshot 吗?
A: 可以调用,但返回值可能没有意义。在实盘9:10分时,交易所尚未产生当天的开盘价或成交量,返回的可能是0或者昨日收盘数据。建议在9:25集合竞价结束后或9:30开盘后获取实时快照。
Q3: 日线回测中,handle_data 是在什么时候运行的?
A: 在日线级别回测中,handle_data 默认在 15:00 运行。这意味着策略是基于当天的收盘价(Close)进行判断的。如果你的策略逻辑是“基于昨日收盘价在今日开盘买入”,在日线回测中可能无法精确模拟(因为代码执行时今天已经收盘了),这种情况下建议使用分钟级别回测。
Q4: 盘后固定价格交易(如科创板)应该写在哪里?
A: 盘后固定价格交易(15:05-15:30)通常建议通过 run_daily 定时任务在 15:05 之后触发,或者在 handle_data 中判断时间触发。虽然它发生在连续竞价结束后,但通常不直接写在 after_trading_end 中,因为 after_trading_end 往往在 15:30 之后才运行,此时盘后交易也已经结束了。