数据准备与加载指南 (Data Guide)¶
数据是量化回测的基石。AKQuant 作为一个高性能回测框架,对数据的格式和质量有一定的要求。本文档将详细介绍如何准备、清洗和加载数据,以确保回测的顺利进行。
1. 数据格式标准 (Data Format)¶
AKQuant 的核心引擎(Rust)和 Python 接口层主要通过 pandas.DataFrame 或 List[Bar] 进行交互。最推荐的方式是使用 Pandas DataFrame。
1.1 必需列 (Required Columns)¶
你的 DataFrame 必须 包含以下列(列名不区分大小写,但在内部会被转换为小写):
| 列名 (Column) | 类型 (Type) | 说明 |
|---|---|---|
date / time / datetime |
datetime64[ns] |
时间戳索引。必须是 Pandas 的 datetime 类型。 |
open |
float |
开盘价 |
high |
float |
最高价 |
low |
float |
最低价 |
close |
float |
收盘价 |
volume |
float |
成交量 |
symbol |
str |
标的代码 (如 "000001", "AAPL") |
注意:
- 列名标准化:建议在传入前将列名统一重命名为英文小写(如
open,close)。 - Symbol 列:即使只回测一支股票,也必须包含
symbol列,以便引擎识别数据所属标的。
1.2 索引 (Index)¶
- DataFrame 的索引可以是默认的整数索引,也可以是
DatetimeIndex。 - 如果使用
DatetimeIndex,AKQuant 会自动将其作为时间列。 - 排序:数据必须按时间升序排列(旧 -> 新)。
2. 数据获取与加载示例¶
2.1 从 CSV 加载¶
这是最常见的方式。假设你有一个 data.csv 文件。
import pandas as pd
from akquant import run_backtest
# 1. 读取 CSV
df = pd.read_csv("data.csv")
# 2. 转换时间列
# 必须确保时间列是 datetime 类型,而不是字符串
df['date'] = pd.to_datetime(df['date'])
# 3. 确保列名正确
# 假设 CSV 列名是 "Date", "Open", ...
df.columns = [c.lower() for c in df.columns]
# 4. 添加 symbol 列 (如果 CSV 中没有)
if 'symbol' not in df.columns:
df['symbol'] = "DEMO_TICKER"
# 5. 排序
df = df.sort_values('date').reset_index(drop=True)
# 6. 传入回测
# result = run_backtest(data=df, ...)
2.2 使用 AKShare (A股数据)¶
AKShare 是一个非常强大的开源财经数据接口库。
import akshare as ak
import pandas as pd
# 1. 下载数据 (以前复权为例)
# period="daily" 日线; adjust="qfq" 前复权
df = ak.stock_zh_a_hist(symbol="000001", period="daily", start_date="20200101", end_date="20231231", adjust="qfq")
# 2. 重命名列 (AKShare 返回中文列名)
df = df.rename(columns={
"日期": "date",
"开盘": "open",
"最高": "high",
"最低": "low",
"收盘": "close",
"成交量": "volume"
})
# 3. 类型转换
df['date'] = pd.to_datetime(df['date'])
df['symbol'] = "000001"
# 4. 筛选列
df = df[["date", "open", "high", "low", "close", "volume", "symbol"]]
2.3 使用 yfinance (美股数据)¶
import yfinance as yf
# 1. 下载数据
df = yf.download("AAPL", start="2020-01-01", end="2023-12-31")
# yfinance 返回 MultiIndex 列 (如果下载多股) 或大写列名
# 这里简化处理单股情况
df.columns = [c.lower() for c in df.columns]
df.reset_index(inplace=True) # 将 Date 索引变成列
df = df.rename(columns={"date": "date"}) # 确保是 date
df['symbol'] = "AAPL"
3. 多标的数据 (Multi-Symbol Data)¶
如果你需要同时回测多只股票(例如全市场选股策略),有两种方式传入数据:
方式 A:单一 DataFrame (推荐)¶
将所有股票的数据拼接成一个巨大的 DataFrame。
# 假设 df_a, df_b 是两只股票的数据
df_all = pd.concat([df_a, df_b])
# 必须按时间排序!AKQuant 是事件驱动引擎,按时间流推送数据
df_all = df_all.sort_values(['date', 'symbol'])
# run_backtest(data=df_all, ...)
方式 B:字典 (Dict of DataFrames)¶
4. 高级话题¶
4.1 预热期数据 (Warmup Period)¶
在计算技术指标(如 MA60, MACD)时,通过 warmup_period 机制,AKQuant 允许策略在正式交易前先“消化”一段历史数据。
- 问题:如果策略第一天就要计算 MA60,但只传入了从回测开始日期的数据,前 59 天是无法计算指标的。
- 解决:确保传入的数据比
start_time(回测开始时间) 更早一些。 - 配置:在策略中设置
warmup_period = 60,引擎会自动从数据流的开头截取 60 个 Bar 仅用于更新指标,而不触发on_bar交易逻辑。
4.2 历史数据获取 (get_history)¶
在策略中,你可以随时获取过去 N 天的行情数据。
self.get_history(count, symbol, field): 返回numpy.ndarray,性能极高(零拷贝)。self.get_history_df(count, symbol): 返回pd.DataFrame,方便使用 Pandas 计算。
注意:get_history 获取的是当前时刻之前的数据,不包含当前 Bar(为了避免未来函数)。如果需要包含当前 Bar 的数据参与计算,可以手动 append。
4.3 时区 (Timezone)¶
AKQuant 内部统一使用 UTC 时间戳。
如果你的数据是本地时间(如北京时间),请在 run_backtest 中指定 timezone="Asia/Shanghai"。
更多详情请参考 时区处理指南。