con un clic
qka
QKA 框架使用技能。当用户需要基于 QKA 框架编写 A 股量化策略、运行回测、 处理股票数据、生成报告时使用。涵盖 QKA 全部核心 API 的使用方法。
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
QKA 框架使用技能。当用户需要基于 QKA 框架编写 A 股量化策略、运行回测、 处理股票数据、生成报告时使用。涵盖 QKA 全部核心 API 的使用方法。
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
| name | qka |
| description | QKA 框架使用技能。当用户需要基于 QKA 框架编写 A 股量化策略、运行回测、 处理股票数据、生成报告时使用。涵盖 QKA 全部核心 API 的使用方法。 |
QKA 是一个 A 股量化交易回测框架。核心流程:
Data(symbols) → Strategy(策略类) → Backtest(回测) → Report(报告)
↑
indicators(预计算指标)
类名约束: 自定义策略类必须命名为 MyStrategy,继承 Strategy
能做的策略类型:
做不了的:
sizing.percent() 和 sizing.fixed() 已自动按手取整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}``获取历史数据。 并发下载所有股票数据,应用因子计算,并返回合并后的数据。
数据获取、缓存和指标预计算。
from qka import Data
data = Data(
symbols=['000001.SZ', '600000.SH'], # 股票代码
period='1d', # 周期:'1d'
adjust='qfq', # 复权:'qfq'/'hfq'/'bfq'
source='baostock', # 数据源:'baostock'/'akshare'/'qmt'
pool_size=10, # 并发线程数(仅 akshare)
datadir=None, # 缓存目录,默认 ./datadir
indicators=None, # 预计算指标
)
symbols: A 股代码 000001.SZ(深市)或 600000.SH(沪市)period: 目前仅支持 '1d'(日线)adjust: 'qfq'(前复权,默认)、'hfq'(后复权)、'bfq'(不复权)source: 'baostock'(默认,串行下载)、'akshare'(HTTP 并发)、'qmt'(QMT)datadir: 默认当前目录的 datadir/,缓存 parquet 文件两种格式:
data = Data(symbols=['000001.SZ'], indicators={
'sma_5': ('sma', 5), # (指标名, 参数...)
'sma_20': ('sma', 20), # 默认用 close 计算
'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), # 指定用 high 列计算
})
支持的 TA 指标名:sma, ema, wma, rsi, macd, bbands, atr
macd 产生 3 列:macd, macd_signal, macd_diffbbands 产生 3 列:bbands_upper, bbands_middle, bbands_lowerdata = 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()
# 懒加载模式(大数据用 dask,分块迭代)
ddf = data.get(lazy=True)
get() 返回 pd.DataFrame,列名格式 {symbol}|{factor}000001.SZ|close, 000001.SZ|sma_5, 600000.SH|volumeget(lazy=True) 返回 dask.DataFrame,计算前操作延迟执行# ✅ 正确:传 indicators 到 Data 构造函数
Data(symbols=['000001.SZ'], indicators={'sma_5': ('sma', 5)})
# ❌ 错误:Data() 后不能动态加指标
data = Data(['000001.SZ'])
data.indicators = {...} # 没用
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...
策略编写核心。
from qka import Strategy
class MyStrategy(Strategy):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 初始化自定义状态(可选)
# 不写 __init__ 也行,用父类默认值
def on_bar(self, date):
"""每个交易日回调一次"""
# date: pd.Timestamp
# self.get() / self.history() 获取数据
# self.broker 交易
# self.sizing 计算仓位
pass
规则:
MyStrategy__init__ 必须用 **kwargs 透传,不能固定参数on_bar(self, date, get)——没有 get 参数当前 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=股票代码# ✅ 正确用法
close = self.get('close')
for sym in close.index:
...
# ❌ 错误用法
self.get('close', count=20) # 没有 count 参数
get('close') # get 不是全局函数
因子的历史窗口数据。
hist = self.history('close', 20) # 过去 20 天收盘价
ma5 = hist.iloc[-5:].mean() # 最近 5 天均值,Series(index=股票代码)
today_close = hist.iloc[-1] # 今天收盘价
yesterday_close = hist.iloc[-2] # 昨天收盘价
series = hist[sym].dropna() # 某只股票的历史序列
pd.DataFrame, 行=日期(倒序), 列=股票代码history 的数据不够 window 天,前几行会有 NaN.dropna() 清理后再计算hist.iloc[-N:] 取最近 N 天交易接口。详见下方 Broker 模块。
self.broker.buy('000001.SZ', price, 100) # 买入 100 股
self.broker.sell('000001.SZ', price, 100) # 卖出 100 股
仓位计算。详见下方 Sizing 模块。
# 10% 资金买入,自动按手取整
size = self.sizing.percent(0.1, price)
self.broker.buy(sym, price, size)
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 由 Strategy 自动创建,用户在策略中通过 self.broker 访问。
# Strategy 内部自动创建
strategy = MyStrategy(cash=100000) # 初始资金 10 万
# 买入
self.broker.buy(symbol, price, size)
# symbol: 股票代码,如 '000001.SZ'
# price: 成交价(float)
# size: 股数(int,必须 100 的倍数)
# 卖出
self.broker.sell(symbol, price, size)
self.broker.cash # 可用资金(float)
self.broker.positions # 持仓 dict
# 持仓格式:
# {symbol: {'size': int, 'avg_price': float, 'cost': float}}
symbol in self.broker.positions # 判断是否持仓
from qka import Broker
broker = Broker(
initial_cash=100000,
commission_rate=0.00025, # 万2.5 佣金(默认)
stamp_duty_rate=0.0005, # 万5 印花税,仅卖出(默认)
slippage=0.001, # 0.1% 滑点(默认)
)
回测执行后,以下属性保存完整记录:
equity = self.broker.trades['total'] # 净值序列(Series, index=日期)
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)
每笔 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(**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(不建议下注)。
仓位计算工具。在策略中通过 self.sizing 访问。
按可用资金的百分比计算买入股数,自动向下取整到 100 的倍数。
# 用 10%(10000 元)资金买入
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: 当前价格买入固定股数。
size = self.sizing.fixed_shares(1000) # 买入 1000 股
self.broker.buy('000001.SZ', price, size)
买入固定金额。
size = self.sizing.fixed_amount(5000, price) # 投入 5000 元
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)
# ❌ 错误:不校验 size 直接买
self.broker.buy(sym, price, self.sizing.percent(0.1, price))
# 如果 percent 返回 0,buy 会报错
Backtest(**data**, **strategy**)\初始化回测引擎
Backtest.run(**benchmark** \Optional[str]` = None) → `None。回测结果保存在 self.results 中,可通过``执行回测 遍历所有时间点,在每个时间点调用策略的on_bar方法进行交易决策, 并记录交易后的状态。 大规模回测(>500 bar)时自动使用分区迭代,分块加载数据到内存, 避免一次性加载全量数据。
Backtest.summary() → \dict``计算并打印回测绩效指标 返回包含以下指标的字典: - 总收益率、年化收益率、年化波动率 - 夏普比率、最大回撤、Calmar比率 - 胜率、盈亏比、交易次数 - 最终资产、总手续费
回测引擎,管理策略生命周期和数据加载流程。
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') # 沪深300 为基准
data: Data 实例(已配置 symbol 和 indicators)MyStrategy(cash=...): 策略实例,cash 为初始资金benchmark: 基准指数代码,如 '000300.SH'(沪深300)、'000001.SH'(上证)bt = Backtest(
data, # Data 实例
strategy, # Strategy 实例
start_date='2023-01-01', # 可选,数据过滤起始
end_date='2024-12-31', # 可选,数据过滤结束
)
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())