问题描述
解决方案
在PTrade量化交易平台中,获取历史行情(如使用 get_history 或 get_price)时,针对停牌或数据缺失的情况,系统有一套默认的处理机制。
以下是关于PTrade如何处理停牌及缺失数据的详细说明和代码示例。
1. 停牌数据的默认处理机制
PTrade在获取历史数据时,不会跳过停牌日期。时间轴严格按照二级市场的交易日历排列。如果某只股票在某个交易日停牌,系统会使用停牌前一笔有效数据进行填充(Forward Fill)。
具体表现如下:
- 价格字段(open, high, low, close, price): 使用停牌前最后一个交易日的收盘价进行填充。即:
今日开盘价 = 今日收盘价 = 昨日收盘价。 - 成交量字段(volume): 填充为 0。
- 成交额字段(money): 填充为 0。
策略启示:
在编写策略时,如果直接使用均线等指标,停牌期间的一字横盘数据会影响指标计算(例如导致波动率降低)。通常建议在开仓前过滤掉停牌的股票。
2. 分钟级缺失数据的处理(fill 参数)
在获取分钟级别数据时(特别是交易模式下),可能会遇到某一分钟没有成交导致的数据缺失。get_history 函数提供了一个 fill 参数来控制填充方式:
fill='pre':用上一分钟的数据填充当前时刻(前值填充)。fill='nan'(默认):使用NaN(Not a Number)进行填充。
3. 如何在代码中处理停牌
为了避免策略在停牌期间发出错误的交易信号,通常有以下两种处理方式:
方法一:使用 get_stock_status 主动过滤(推荐)
在交易逻辑执行前,先查询股票的 HALT 状态。
方法二:通过成交量判断
在获取历史数据后,判断 volume 是否为 0。
4. 代码示例
以下代码展示了如何在策略中识别并过滤停牌股票,以及获取历史数据后的验证逻辑。
def initialize(context):
# 初始化股票池
g.security = ['600570.SS', '000001.SZ', '600000.SS']
set_universe(g.security)
def handle_data(context, data):
# --- 方法一:使用 get_stock_status 过滤停牌股 (推荐) ---
# 获取当前股票池中所有股票的停牌状态
# 返回一个字典,Key为股票代码,Value为布尔值(True表示停牌,False表示未停牌)
halt_status = get_stock_status(g.security, 'HALT')
# 创建一个列表存储今日可交易的股票
tradable_stocks = []
for stock in g.security:
if halt_status.get(stock, False):
log.info("股票 %s 当前处于停牌状态,跳过处理。" % stock)
continue
else:
tradable_stocks.append(stock)
if len(tradable_stocks) == 0:
return
# --- 方法二:获取历史数据并观察停牌表现 ---
# 获取过去5天的日线数据
# 注意:PTrade不会跳过停牌日,停牌日会有数据,但Volume为0
history_data = get_history(5, '1d', ['close', 'volume'], security_list=tradable_stocks)
for stock in tradable_stocks:
# 获取该股票的收盘价和成交量序列
# 注意:get_history返回的数据结构在不同Python版本或参数下可能不同
# 这里假设返回的是DataFrame或可以通过query查询
# 针对多只股票返回的DataFrame进行查询
stock_df = history_data.query('code == "%s"' % stock)
if len(stock_df) > 0:
# 获取最近一个交易日的数据
last_close = stock_df['close'].iloc[-1]
last_volume = stock_df['volume'].iloc[-1]
# 再次确认:如果成交量为0,通常意味着停牌(或极端无流动性)
if last_volume == 0:
log.info("股票 %s 历史数据成交量为0,疑似停牌或无成交。" % stock)
else:
# 正常的交易逻辑
# 例如:简单的均线策略
avg_price = stock_df['close'].mean()
current_price = data[stock]['close']
if current_price > avg_price:
log.info("股票 %s 价格高于5日均线,且未停牌,执行买入检查..." % stock)
# order(stock, 100) # 示例下单
5. 总结
- 数据连续性:PTrade 历史数据在时间轴上是连续的,停牌日不会被剔除。
- 填充规则:停牌日价格沿用前值,成交量为 0。
- 最佳实践:不要依赖
get_history返回的数据来被动发现停牌,而应在handle_data或before_trading_start中使用get_stock_status(security_list, 'HALT')主动过滤停牌股票,以提高策略效率和准确性。
Q&A
Q1: 如果我在回测中使用了 get_history 获取了包含停牌日的数据计算均线,会有什么影响?
A1: 停牌期间价格为一条直线(前值填充),这会导致移动平均线(MA)在停牌期间走平,且波动率指标(如ATR、StdDev)会迅速下降。如果策略依赖波动率突破,可能会在复牌瞬间产生错误的信号。建议在计算指标前剔除成交量为0的行,或者使用 get_stock_status 预先过滤。
Q2: get_price 和 get_history 在处理停牌数据上有什么区别吗?
A2: 两者在处理停牌数据的逻辑上是一致的。都会返回停牌日的记录,价格为前收盘价,成交量为0。区别主要在于参数和返回格式,get_price 支持指定具体的时间段(start_date, end_date),而 get_history 指定的是数量(count)。
Q3: 分钟线数据缺失时,fill='nan' 和 fill='pre' 哪个更好?
A3: 这取决于策略类型。如果是基于价格连续性的趋势策略,使用 fill='pre'(前值填充)可以保持序列完整,避免计算报错。如果是对数据质量要求极高的统计套利策略,使用 fill='nan' 可以让你明确知道哪些时间点没有真实成交,从而避免基于虚假数据下单。注意 fill 参数主要在交易模式下生效。