| name | qka |
| description | QKA 框架使用技能。当用户需要基于 QKA 框架编写 A 股量化策略、运行回测、 处理股票数据、生成报告时使用。涵盖 QKA 全部核心 API 的使用方法。
|
QKA 框架技能
概述
QKA 是一个 A 股量化交易回测框架。核心流程:
Data(symbols) → Strategy(策略类) → Backtest(回测) → Report(报告)
↑
indicators(预计算指标)
类名约束: 自定义策略类必须命名为 MyStrategy,继承 Strategy
能力边界
能做的策略类型:
- 趋势跟踪(双均线、海龟突破、MACD)
- 均值回归(RSI、Bollinger Bands)
- 多因子选股(PE/ROE/动量/波动率打分选股,周期 rebalance)
- 等权/市值加权组合
- 定投(固定间隔买入固定金额)
- 大盘 MA 择时、股债轮动
做不了的:
- 分钟级/高频(无分钟数据)
- 期权、期货
- 机器学习选股(无特征工程管道)
- 实盘交易
- 事件驱动(无财报/公告订阅)
- 多周期策略(仅单周期)
A 股交易规则
- 买入股数必须是 100 的整数倍(一手)
- 价格必须 > 0 且不是 NaN
- 资金不足时不买入
sizing.percent() 和 sizing.fixed() 已自动按手取整
Data
Data(**symbols** \Optional[List[str]]` = None, period `str` = '1d', adjust `str` = 'qfq', source `str` = 'baostock', pool_size `int` = 10, datadir `Optional[Path]` = None, indicators `Optional[dict]` = None)`
初始化数据对象
Data.get(**lazy** \bool` = False) → `lazy=False: pd.DataFrame,列名格式 {symbol}|{factor}``
获取历史数据。 并发下载所有股票数据,应用因子计算,并返回合并后的数据。
Data 模块
数据获取、缓存和指标预计算。
Data 构造函数
from qka import Data
data = Data(
symbols=['000001.SZ', '600000.SH'],
period='1d',
adjust='qfq',
source='baostock',
pool_size=10,
datadir=None,
indicators=None,
)
symbols: A 股代码 000001.SZ(深市)或 600000.SH(沪市)
period: 目前仅支持 '1d'(日线)
adjust: 'qfq'(前复权,默认)、'hfq'(后复权)、'bfq'(不复权)
source: 'baostock'(默认,串行下载)、'akshare'(HTTP 并发)、'qmt'(QMT)
datadir: 默认当前目录的 datadir/,缓存 parquet 文件
indicators 参数
两种格式:
格式一:字典(推荐)
data = Data(symbols=['000001.SZ'], indicators={
'sma_5': ('sma', 5),
'sma_20': ('sma', 20),
'rsi_14': ('rsi', 14),
'macd': ('macd', 12, 26, 9),
'bbands': ('bbands', 20, 2),
'atr_14': ('atr', 14),
'ma5_custom': lambda df: df['close'].rolling(5).mean(),
'sma_on_high': ('sma', 'high', 20),
})
支持的 TA 指标名:sma, ema, wma, rsi, macd, bbands, atr
macd 产生 3 列:macd, macd_signal, macd_diff
bbands 产生 3 列:bbands_upper, bbands_middle, bbands_lower
格式二:函数
data = Data(symbols=['000001.SZ'], indicators=lambda df:
df.assign(
ma5=df['close'].rolling(5).mean(),
ma20=df['close'].rolling(20).mean()
)
)
函数接收单只股票的 DataFrame,返回添加了额外列的 DataFrame。
获取数据
data = Data(symbols=['000001.SZ', '600000.SH'], indicators={'sma_5': ('sma', 5)})
df = data.get()
ddf = data.get(lazy=True)
get() 返回 pd.DataFrame,列名格式 {symbol}|{factor}
- 示例列名:
000001.SZ|close, 000001.SZ|sma_5, 600000.SH|volume
get(lazy=True) 返回 dask.DataFrame,计算前操作延迟执行
常见错误
Data(symbols=['000001.SZ'], indicators={'sma_5': ('sma', 5)})
data = Data(['000001.SZ'])
data.indicators = {...}
Strategy
Strategy(**cash** \float` = 100000.0)`
初始化策略
Strategy.get(**factor** \str`) → `pd.Series,index=股票代码,values=最新值``
获取当前 bar 的横截面数据。 替代旧的 on_bar(date, get) 中的 get 参数。 仅当 on_bar 通过 self._data 注入数据后才能使用。
Strategy.history(**factor** \str`, window `int` = 20) → `pd.DataFrame,行=日期,列=股票代码``
获取因子的历史窗口数据。
Strategy.on_bar(**date**)\
每个 bar 的处理逻辑,必须由子类实现。 使用 self.get(factor) / self.history(factor, window) 获取数据。 --- 用法 --- class MyStrategy(Strategy): def on_bar(self, date): # 横截面数据(当前 bar 所有股票) close = self.get('close') # 历史序列(过去 N...
Strategy 模块
策略编写核心。
策略类结构
from qka import Strategy
class MyStrategy(Strategy):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def on_bar(self, date):
"""每个交易日回调一次"""
pass
规则:
- 类名必须是
MyStrategy
__init__ 必须用 **kwargs 透传,不能固定参数
- ❌ 禁止写
on_bar(self, date, get)——没有 get 参数
self.get(factor) -> pd.Series
当前 bar 所有股票的横截面数据。
close = self.get('close')
volume = self.get('volume')
high = self.get('high')
for sym in close.index:
price = float(close[sym])
if price > 0:
...
factor: 因子名,如 'close', 'open', 'high', 'low', 'volume'
- 也可以取预计算指标:
self.get('sma_5'), self.get('rsi_14')
- 返回
pd.Series, index=股票代码
- 如果某股票当前值缺失,会从 Series 中排除
close = self.get('close')
for sym in close.index:
...
self.get('close', count=20)
get('close')
self.history(factor, window) -> pd.DataFrame
因子的历史窗口数据。
hist = self.history('close', 20)
ma5 = hist.iloc[-5:].mean()
today_close = hist.iloc[-1]
yesterday_close = hist.iloc[-2]
series = hist[sym].dropna()
- 返回
pd.DataFrame, 行=日期(倒序), 列=股票代码
- 如果
history 的数据不够 window 天,前几行会有 NaN
- 用
.dropna() 清理后再计算
hist.iloc[-N:] 取最近 N 天
self.broker
交易接口。详见下方 Broker 模块。
self.broker.buy('000001.SZ', price, 100)
self.broker.sell('000001.SZ', price, 100)
self.sizing
仓位计算。详见下方 Sizing 模块。
size = self.sizing.percent(0.1, price)
self.broker.buy(sym, price, size)
Broker
Broker(**initial_cash** = 100000.0, **commission_rate** = DEFAULT_COMMISSION_RATE, **stamp_duty_rate** = DEFAULT_STAMP_DUTY_RATE, **slippage** = DEFAULT_SLIPPAGE)\
初始化Broker
Broker.on_bar(**date**, **get**)\
Bar结束时记录当前状态。
Broker.buy(**symbol** \str`, price `float`, size `int`) → `bool``
买入操作 考虑滑点(买入价上移)和佣金(最低 5 元)。
Broker.sell(**symbol** \str`, price `float`, size `int`) → `bool``
卖出操作 考虑滑点(卖出价下移)、佣金(最低 5 元)和印花税。
Broker.get(**factor** \str`, timestamp = None) → `Any``
从trades DataFrame中获取数据
Broker 模块
虚拟交易经纪商,管理资金、持仓和费用。
初始化
Broker 由 Strategy 自动创建,用户在策略中通过 self.broker 访问。
strategy = MyStrategy(cash=100000)
交易接口
self.broker.buy(symbol, price, size)
self.broker.sell(symbol, price, size)
状态属性
self.broker.cash
self.broker.positions
symbol in self.broker.positions
费用设置
from qka import Broker
broker = Broker(
initial_cash=100000,
commission_rate=0.00025,
stamp_duty_rate=0.0005,
slippage=0.001,
)
回测结果数据
回测执行后,以下属性保存完整记录:
self.broker.trades — 逐日净值记录(pd.DataFrame)
equity = self.broker.trades['total']
cash = self.broker.trades['cash']
value = self.broker.trades['value']
用于构造净值曲线:
import pandas as pd
eq = pd.Series(self.broker.trades['total'].values,
index=self.broker.trades.index)
self.broker.trade_history — 逐笔交易记录(list[dict])
每笔 dict 的字段:
| 字段 | 类型 | 说明 |
|---|
action | str | 'buy' 或 'sell' |
symbol | str | 股票代码 |
price | float | 市价 |
exec_price | float | 滑点后成交价 |
size | int | 股数 |
amount | float | 成交金额 |
commission | float | 佣金 |
timestamp | 时间戳 | 交易日期 |
卖出额外含 net_proceeds(扣除费用后净收入)。
正确/错误用法
if '000001.SZ' in self.broker.positions:
size = self.broker.positions['000001.SZ']['size']
self.broker.sell('000001.SZ', price, size)
self.broker.cash -= 1000
self.broker.positions['000001.SZ'] = {'size': 100, ...}
SizingAccessor
SizingAccessor(**broker**)\
SizingAccessor.fixed_shares(**n** \int`) → `int``
固定股数。 如果 n 不足一手(100股),返回 0。
SizingAccessor.fixed_amount(**amount** \float`, price `float`) → `int``
固定金额。 计算 amount 能买多少股,向下按手取整。
SizingAccessor.percent(**ratio** \float`, price `float`) → `int``
资金百分比。 使用可用现金的 ratio 比例买入,按手取整。
SizingAccessor.atr_risk(**risk_ratio** \float`, price `float`, atr_value `float`, multiplier `float` = 2.0) → `int``
ATR 风险仓位。 基于 ATR(平均真实波幅)计算仓位,确保单笔亏损不超过 risk_ratio 比例。 公式:股数 = (cash * risk_ratio) / (atr_value * multiplier)
SizingAccessor.kelly(**win_rate** \float`, win_loss_ratio `float`, price `float`) → `int``
凯利公式。 f* = (p * b - q) / b 其中: - p = 胜率 - b = 赔率(盈利/亏损) - q = 1 - p(败率) 当 f* ≤ 0 时返回 0(不建议下注)。
Sizing 模块
仓位计算工具。在策略中通过 self.sizing 访问。
方法
self.sizing.percent(ratio, price) -> int
按可用资金的百分比计算买入股数,自动向下取整到 100 的倍数。
price = float(self.get('close')['000001.SZ'])
size = self.sizing.percent(0.1, price)
self.broker.buy('000001.SZ', price, size)
ratio: 资金比例,如 0.1 = 10%, 0.5 = 50%
price: 当前价格
- 返回:股数(int),已按手取整
- 如果计算出的股数 < 100,返回 0
self.sizing.fixed_shares(n) -> int
买入固定股数。
size = self.sizing.fixed_shares(1000)
self.broker.buy('000001.SZ', price, size)
self.sizing.fixed_amount(amount, price) -> int
买入固定金额。
size = self.sizing.fixed_amount(5000, price)
self.broker.buy('000001.SZ', price, size)
注意事项
price = float(self.get('close')['000001.SZ'])
size = self.sizing.percent(0.1, price)
if size >= 100:
self.broker.buy(sym, price, size)
self.broker.buy(sym, price, self.sizing.percent(0.1, price))
Backtest
Backtest(**data**, **strategy**)\
初始化回测引擎
Backtest.run(**benchmark** \Optional[str]` = None) → `None。回测结果保存在 self.results 中,可通过``
执行回测 遍历所有时间点,在每个时间点调用策略的on_bar方法进行交易决策, 并记录交易后的状态。 大规模回测(>500 bar)时自动使用分区迭代,分块加载数据到内存, 避免一次性加载全量数据。
Backtest.summary() → \dict``
计算并打印回测绩效指标 返回包含以下指标的字典: - 总收益率、年化收益率、年化波动率 - 夏普比率、最大回撤、Calmar比率 - 胜率、盈亏比、交易次数 - 最终资产、总手续费
Backtest 模块
回测引擎,管理策略生命周期和数据加载流程。
用法
from qka import Data, Backtest
data = Data(symbols=['000001.SZ'], indicators={'sma_5': ('sma', 5)})
bt = Backtest(data, MyStrategy(cash=100000))
bt.run(benchmark='000300.SH')
data: Data 实例(已配置 symbol 和 indicators)
MyStrategy(cash=...): 策略实例,cash 为初始资金
benchmark: 基准指数代码,如 '000300.SH'(沪深300)、'000001.SH'(上证)
Backtest 参数
bt = Backtest(
data,
strategy,
start_date='2023-01-01',
end_date='2024-12-31',
)
回测结果
summary() -> dict
metrics = bt.summary()
返回的字典包含:
总收益率 — 策略总收益百分比
年化收益率 — 年化收益率
最大回撤 — 最大回撤百分比(负值)
夏普比率 — 年化夏普比率
胜率 — 盈利交易占比
交易次数 — 总交易笔数
benchmark_收益 — 基准总收益百分比
benchmark_年化 — 基准年化收益率
完整回测流程
from qka import Data, Strategy, Backtest
class MyStrategy(Strategy):
def on_bar(self, date):
close = self.get('close')
for sym in close.index:
price = float(close[sym])
if price <= 0:
continue
if sym not in self.broker.positions:
size = self.sizing.percent(0.1, price)
if size >= 100:
self.broker.buy(sym, price, size)
data = Data(symbols=['000001.SZ', '600000.SH'])
bt = Backtest(data, MyStrategy(cash=100000))
bt.run(benchmark='000300.SH')
print(bt.summary())