问题描述
解决方案
在 QMT 中,ContextInfo.get_market_data 的返回值类型高度依赖于你传入的参数(主要是股票数量和时间范围/count)。
在大多数常用场景下,它已经返回了 pandas.DataFrame 或 pandas.Panel(旧版 Pandas 的三维结构)。
以下是针对不同情况将数据转换为标准 DataFrame 的方法:
1. 单只股票,多条数据(直接返回 DataFrame)
如果你只获取一只股票的一段时间序列数据,返回值本身就是 DataFrame,无需转换。
# -*- coding: gbk -*-
def handlebar(ContextInfo):
# 获取平安银行最近 10 天的开高低收
# stock_code 列表中只有一个元素
df = ContextInfo.get_market_data(
['open', 'high', 'low', 'close'],
stock_code=['000001.SZ'],
period='1d',
count=10
)
# 此时 df 已经是 DataFrame 类型
# index 是时间,columns 是 open, high, low, close
print(type(df))
print(df)
2. 多只股票,多条数据(返回 Panel,需转换)
如果你获取多只股票的时间序列数据,get_market_data 返回的是 pandas.Panel(三维数组:Items=股票, Major_axis=时间, Minor_axis=字段)。
由于 Panel 在新版 Pandas 中已被移除(虽然 QMT 内置环境支持),且操作不便,通常建议将其转换为 MultiIndex DataFrame。
转换方法:使用 .to_frame()
# -*- coding: gbk -*-
def handlebar(ContextInfo):
# 获取两只股票最近 5 天的数据
panel_data = ContextInfo.get_market_data(
['open', 'close'],
stock_code=['000001.SZ', '600000.SH'],
period='1d',
count=5
)
print("原始类型:", type(panel_data)) # <class 'pandas.core.panel.Panel'>
# 方法一:转换为 MultiIndex DataFrame (推荐)
# 转换后 index 变为 (时间, 股票代码) 的双重索引
df_multi = panel_data.to_frame()
print("转换后的 DataFrame:")
print(df_multi.head())
# 方法二:如果你只想要某个字段(例如 close)的 DataFrame
# 结果:index=时间,columns=股票代码
df_close = panel_data['close']
print("收盘价 DataFrame:")
print(df_close.head())
3. (推荐) 使用 get_market_data_ex
官方文档建议使用 ContextInfo.get_market_data_ex 替代 get_market_data。
get_market_data_ex 返回的是一个 字典 (Dict),Key 是股票代码,Value 是该股票的 DataFrame。
如果你需要将其合并为一个大的 DataFrame,可以使用 pd.concat。
# -*- coding: gbk -*-
import pandas as pd
def handlebar(ContextInfo):
stock_list = ['000001.SZ', '600000.SH']
# 获取数据,返回格式为 {code: DataFrame, code: DataFrame}
data_dict = ContextInfo.get_market_data_ex(
['open', 'high', 'low', 'close'],
stock_list,
period='1d',
count=5
)
# 遍历字典查看单独的 DataFrame
for code, df in data_dict.items():
print(f"股票 {code} 的数据类型: {type(df)}")
# 【转换技巧】将字典合并为一个大的 DataFrame
# 1. 给每个 DataFrame 添加一列 'code'
for code in data_dict:
data_dict[code]['code'] = code
# 2. 使用 concat 合并
full_df = pd.concat(data_dict.values())
# 3. (可选) 设置索引方便查询
# full_df.index.name = 'time'
# full_df = full_df.reset_index().set_index(['time', 'code'])
print("合并后的 DataFrame:")
print(full_df)
总结
- 单股:
get_market_data直接返回 DataFrame。 - 多股:
get_market_data返回 Panel,使用.to_frame()转为 DataFrame。 - 最佳实践:使用
get_market_data_ex,它返回字典包含 DataFrame,逻辑更清晰,然后使用pd.concat合并。
Q&A
Q: 为什么 get_market_data 返回多只股票时是 Panel 类型?
A: QMT 内置的 Python 环境版本较老(通常是 Python 3.6.8,Pandas 0.22.0),在那个版本的 Pandas 中,Panel 是用于存储三维数据(股票 x 时间 x 字段)的标准结构。虽然新版 Pandas 已废弃 Panel,但在 QMT 环境中它依然存在。
Q: get_market_data_ex 和 get_market_data 有什么主要区别?
A: get_market_data 的返回类型会根据参数变化(值、Series、DataFrame 或 Panel),容易导致代码出错。get_market_data_ex 返回结构统一为字典 {code: DataFrame},且支持订阅逻辑,更稳定且易于处理。
Q: 如何将 DataFrame 中的时间戳索引转换为可读日期?
A: QMT 返回的 DataFrame 索引通常是字符串格式的时间(如 '20231027' 或 '20231027093000')。你可以使用 pd.to_datetime(df.index) 将其转换为 datetime 对象,或者直接解析字符串。如果是毫秒时间戳,可以使用 ContextInfo 中的 timetag_to_datetime 函数处理。