问题描述
解决方案
在 PTrade 量化交易平台中,get_history 函数是获取历史行情数据的核心接口。根据 PTrade 的 API 文档,该函数返回的数据通常已经是 pandas.DataFrame(单只股票或 Python 3.11 多只股票)或 pandas.Panel(Python 3.5 多只股票多字段)格式,这使得直接利用 Pandas 库进行数据分析变得非常便捷。
以下是如何使用 Pandas 对 get_history 返回的数据进行清洗、计算技术指标以及生成交易信号的详细指南。
1. 获取数据与基础查看
首先,我们需要调用 get_history 获取数据。通常我们获取的是一个 DataFrame,其中行索引(Index)是时间(datetime),列(Columns)是行情字段(如 open, close, high, low, volume)。
# 获取单只股票过去 100 天的日线数据
# 返回的是 DataFrame,索引为 datetime 对象
df = get_history(100, frequency='1d', field=['open', 'high', 'low', 'close', 'volume'], security_list='600570.SS')
# 打印前 5 行查看数据结构
log.info("前5行数据:\n%s" % df.head())
# 查看数据的基础统计信息(均值、标准差、最大最小值等)
log.info("数据统计描述:\n%s" % df.describe())
2. 使用 Pandas 计算技术指标
Pandas 的强大之处在于其向量化运算和滚动窗口函数(Rolling Window),这对于计算移动平均线(MA)、波动率等指标非常高效。
计算移动平均线 (MA)
使用 rolling().mean() 方法:
# 计算 5 日均线
df['MA5'] = df['close'].rolling(window=5).mean()
# 计算 10 日均线
df['MA10'] = df['close'].rolling(window=10).mean()
计算涨跌幅 (Returns)
使用 pct_change() 方法:
# 计算每日涨跌幅
df['pct_change'] = df['close'].pct_change()
计算历史波动率 (Volatility)
使用 rolling().std() 方法:
# 计算 20 日的历史波动率
df['volatility'] = df['close'].rolling(window=20).std()
3. 数据清洗与筛选
在计算指标后,通常前几行数据会因为窗口期不足而产生 NaN(空值)。
# 删除包含空值的行
df.dropna(inplace=True)
# 筛选出收盘价大于 5 日均线的日期数据
bullish_days = df[df['close'] > df['MA5']]
4. 完整策略代码示例
下面是一个完整的 PTrade 策略示例。该策略在 handle_data 中获取历史数据,使用 Pandas 计算双均线(MA5 和 MA10),并根据金叉(MA5 上穿 MA10)和死叉(MA5 下穿 MA10)进行买卖操作。
def initialize(context):
# 设置股票池:恒生电子
g.security = '600570.SS'
set_universe(g.security)
# 设置滑点和佣金(可选,为了回测更真实)
set_commission(commission_ratio=0.0003, min_commission=5.0)
set_slippage(slippage=0.002)
def handle_data(context, data):
security = g.security
# 1. 获取过去 20 天的收盘价数据
# 注意:为了计算 MA10,我们需要至少 10 根 K 线,取 20 根是为了确保数据充足
# include=False 表示不包含当前周期(即只取历史数据,避免未来函数)
df = get_history(20, frequency='1d', field=['close'], security_list=security, include=False)
# 检查数据是否获取成功且长度足够
if df is None or len(df) < 10:
log.info("数据不足,跳过本次计算")
return
# 2. 使用 Pandas 计算均线
# 计算 5 日均线
ma5_series = df['close'].rolling(window=5).mean()
# 计算 10 日均线
ma10_series = df['close'].rolling(window=10).mean()
# 获取最新的均线值(序列的最后一个值)
current_ma5 = ma5_series.iloc[-1]
current_ma10 = ma10_series.iloc[-1]
# 获取前一天的均线值(用于判断交叉)
prev_ma5 = ma5_series.iloc[-2]
prev_ma10 = ma10_series.iloc[-2]
# 获取当前持仓
position = get_position(security)
# 3. 交易逻辑判断
# 金叉:昨天 MA5 < MA10,且今天 MA5 > MA10
if prev_ma5 < prev_ma10 and current_ma5 > current_ma10:
# 记录日志
log.info("金叉出现:MA5=%.2f, MA10=%.2f" % (current_ma5, current_ma10))
# 满仓买入
cash = context.portfolio.cash
if cash > 0:
order_value(security, cash)
log.info("买入 %s" % security)
# 死叉:昨天 MA5 > MA10,且今天 MA5 < MA10
elif prev_ma5 > prev_ma10 and current_ma5 < current_ma10:
# 记录日志
log.info("死叉出现:MA5=%.2f, MA10=%.2f" % (current_ma5, current_ma10))
# 如果有持仓,清仓卖出
if position.amount > 0:
order_target(security, 0)
log.info("卖出 %s" % security)
5. 处理多只股票的数据
当 security_list 包含多只股票时,get_history 的返回格式在不同 Python 版本下可能不同。
- Python 3.11 环境:返回 MultiIndex DataFrame(多级索引),列索引包含股票代码。
- Python 3.5 环境:如果查询多个字段,可能返回
pandas.Panel(已弃用的数据结构)。
推荐的处理方式(兼容性写法):
如果需要处理多只股票,建议使用 query 方法或者循环处理,或者明确指定 is_dict=True(如果 API 支持)来获取字典格式,然后自行转换为 DataFrame。
在 Python 3.11 中处理多只股票 DataFrame 的示例:
# 假设 df 是包含多只股票数据的 DataFrame
# 使用 query 提取特定股票的数据
df_stock_a = df.query('code == "600570.SS"')
# 接下来可以像处理单只股票一样处理 df_stock_a
总结
使用 Pandas 分析 PTrade 数据的主要步骤:
- 获取:
get_history拿到 DataFrame。 - 计算:利用
rolling(),mean(),std(),pct_change()等向量化函数计算指标。 - 切片:利用
.iloc[-1]获取最新值用于交易判断。 - 清洗:利用
dropna()处理计算产生的空值。
Q&A: PTrade 数据分析常见问题
Q1: 为什么计算出的 MA(均线)前几行是 NaN?
A: 这是因为滚动窗口计算需要足够的数据。例如计算 5 日均线,前 4 天的数据不足以计算平均值,因此 Pandas 会填充为 NaN。在使用数据前,建议使用 df.dropna() 去除这些无效行,或者在获取数据时多获取一些历史长度(如需要 10 日均线,至少获取 20 天数据)。
Q2: get_history 中的 include 参数应该设为 True 还是 False?
A: 在编写策略逻辑(尤其是 handle_data 中)时,通常建议设为 False。因为 handle_data 运行时,当天的 K 线可能尚未走完(如果是盘中运行),包含当前数据可能导致“未来函数”问题(即利用了收盘后才能确定的价格在盘中交易)。如果是在盘后分析或 after_trading_end 中,可以设为 True。
Q3: 如何将 Pandas 计算出的信号转换为交易指令?
A: Pandas 计算出的通常是整个序列(Series)。你需要提取序列的最后一行(iloc[-1])作为“当前时刻”的指标值,将其与阈值进行比较(if 语句),然后调用 PTrade 的 order 系列函数(如 order, order_target, order_value)进行下单。