3.5 V3 内置策略一览

适用版本 V3 WindowsmacOSLinux

本篇重点:本篇逐个导读 V3 自带的 13 个案例策略:各自演示什么写法、怎么跑、跑之前要准备什么。

V3 专享 · 专栏持续更新

除了下面这些软件自带的案例策略,更多、更新的策略案例会在专栏里持续更新(第一期是 T0 策略专栏),这是 V3 用户特有的。

运行前填写自己的股票

加载示例 .kh 后,在「股票池设置」自行添加标的、保存配置,并下载对应周期及预热期的数据,再运行。单股票案例填一只,多股票案例可填多只;网格案例填自己选择的 T+0 ETF。旧视频中的股票是演示时手工设置,不是新版随包默认值。

1 先读这里:这些是演示案例

只演示写法,不构成任何投资建议本篇的 13 个案例只用来演示 khQuant 的功能和策略写法:随包 .kh 的股票池为空,加载后必须自行填写研究标的,策略案例的关键参数用随机数生成(写库脚本 4-1、4-2 除外),每次回测结果都不一样。不要把案例的回测结果当成选股或交易依据;回测结果只反映历史数据,不代表未来收益。

1.1 案例在哪里

第一次打开软件时会复制到 文档\KhQuant\strategies(Mac 在 ~/KhQuant/strategies,Linux 没有图形界面,用 kh init 复制)。运行过 kh init 的,也会复制一份到你填的策略目录。每个案例是一对同名文件:.py 是策略代码,.kh 是回测配置(股票池、区间、周期等),在主界面「加载配置」选 .kh 即可。4-1、4-2 是写数据库的脚本,只有 .py,不能当策略加载。

1.2 随机参数

每个案例的关键参数都用随机数生成,比如双均线案例:

params = {
    'ma_short': random.randint(3, 10),
    'ma_long': random.randint(15, 30),
}

所以同一个配置跑两次,结果会不一样(实测 RSI 案例跑 4 次,收益从 -0.70% 到 +0.34%)。日志开头会打印这次抽到的参数。自己研究时,把随机函数换成固定数值:

params = {
    'ma_short': 5,
    'ma_long': 20,
}

2 13 个案例一览

案例演示什么周期V2 有
【1-MA】双均线精简_使用khMA函数最小的策略骨架:一只股票、均线比大小、全仓进出日线有
【1-MA】双均线多股票_使用khMA函数遍历股票池、半仓买入日线有
【1-MA】双均线多股票_使用MA函数自己取历史数据,再用 MyTT 指标函数计算日线有
【2-RSI】RSI策略指标「穿越」判断、小仓位日线有
【3-T0】T0、【3-1】固定网格、【3-2】浮动网格、【3-3】跨层补单网格T+0 ETF 网格交易1 分钟无
【4-1】【4-2】写入数据库计算指标并写进本地数据库(脚本,不是策略)—无
【4-3】【4-4】读取数据库指标回测时读取自己写进去的指标日线无
【5-日内多次触发】日线多次触发验证同一天先卖后买日线无

3 双均线(3 个)

三个案例的交易规则相同:短均线在长均线之上、没有持仓就买;短均线在长均线之下、有持仓就卖。策略以当天开盘价下单,实际成交价再按配置计滑点:本案例总滑点 1%,买入上浮 0.5%、卖出下浮 0.5%,另计佣金和印花税。均线只用到前一天为止的收盘价,没有用到未来数据。

提示

这里判断的是「短均线在不在长均线之上」这个状态,不是严格的「上穿」那一刻。所以回测第一天只要短均线在上方,就会直接买入。

3.1 双均线精简_使用khMA函数

只交易你自行填写的股票池里的第 1 只,买卖都是全仓。核心只有几行(节选,ma_s、ma_l 等定义见案例文件):

stock_code = khGet(data, "first_stock")              # 股票池第 1 只
current_price = khPrice(data, stock_code, "open")    # 当天开盘价
current_date_str = khGet(data, "date_num")           # 当天日期
ma_short = khMA(stock_code, ma_s, end_time=current_date_str)
ma_long = khMA(stock_code, ma_l, end_time=current_date_str)
has_position = khHas(data, stock_code)
if ma_short > ma_long and not has_position:
    signals = generate_signal(data, stock_code, current_price, 1.0, 'buy', "金叉买入")
elif ma_short < ma_long and has_position:
    signals = generate_signal(data, stock_code, current_price, 1.0, 'sell', "死叉卖出")

khMA 一行就完成「取历史收盘价 + 求均线」。配置:2025-01-01 到 2025-07-03,日线,前复权。

3.2 双均线多股票_使用khMA函数

用 khGet(data, "stocks") 遍历你自行填写的股票池,每次买入用当时可用资金的一半,卖出全部。历史数据不够算均线的股票当天跳过。

3.3 双均线多股票_使用MA函数

先用 khHistory 取一段收盘价,再用 MyTT 的 MA() 计算均线。和本节第 2 个案例对照着看,能理解 khMA 帮你省掉了哪一步;需要其他指标(MACD、KDJ 等)时,就用这种「取数据 + MyTT 函数」的写法。

注意

随包配置的股票池为空。加载后先自行添加研究标的,再下载对应数据;可以先选少量标的熟悉流程。CLI 可在 .kh 的 data.stock_list 中填写自己的代码,或使用 –stocks 参数指定。

4 RSI(1 个)

用 khHistory 取收盘价,MyTT 的 RSI() 计算,比较前两个交易日的 RSI(khHistory 不含当天):

  • RSI 由下往上穿过买入线,并且没有持仓 → 用 10% 的资金买入;
  • RSI 由上往下穿过卖出线,并且有持仓 → 全部卖出。

周期 10~20、买入线 25~45、卖出线 55~75 都是随机的。这是真正的「穿越」判断,加上仓位只有 10%,所以成交很少(整个回测区间约 5 个月只有两三笔),收益通常在 ±1% 附近,这是正常的。RSI 用的是较短的一段历史算的,数值和看盘软件可能有少许差别。

5 T+0 网格与日内多次触发(5 个)

▶ 本节视频:3.5(二)· T0 与日内多次触发

5.1 哪些品种能 T+0,软件怎么知道

5.1.1 内置的 T+0 清单

软件判断一个标的能不能 T+0,只看它在不在内置清单 T0型ETF.csv 里,不按代码前缀推断。清单共 333 条,以 ETF 为主,也有部分 LOF,分 5 类:

类别条数举例
股票型(跨境、港股通)119513900、159726
QDII117513050、513100
债券型53511010、511090
货币型27511690、511860
另类投资(黄金、商品)17518880、159985

清单是 2025 年 11 月整理的,软件不会自动更新。交易所的 T+0 品种有增减时,清单可能滞后,回测里的 T+0 资格按当前清单算,不考虑某个品种历史上什么时候开始支持 T+0。实际交易前请以交易所最新公告为准。

5.1.2 两个预设股票池

股票池设置里有「T0型ETF」(整份清单)和「T0股票型ETF」(其中的股票型部分)两个预设池,勾选时会先弹出「数据滞后提示」,提醒清单可能滞后。用这两个池子回测,池里全是 T+0 标的,会进入 T+0 模式。

提示

T+0 网格这类策略一般只盯一两只流动性好的品种。不建议直接拿整个「T0型ETF」池去跑:数据量大、回测慢,案例策略也只交易池里的第 1 只。

5.1.3 在策略里判断

策略代码里可以调用这几个函数(从 khQuantImport 导入):

  • is_t0_etf(code):这只标的在不在 T+0 清单里。
  • check_t0_support(codes):一组标的的情况,返回两个值:类型 all_t0(全部是)、mixed(部分是)或 no_t0(都不是),以及是否按 T+0 运行(只有 all_t0 时为 True)。

另有一个 is_etf(code),它只按代码前缀判断是不是 ETF,和能不能 T+0 没有关系,不要拿它判断 T+0。

5.2 股票池决定交易模式

每次开始运行时,软件检查一遍股票池:

股票池交易模式你会看到
全部在 T+0 清单里T+0日志「T+0交易模式已启用」;股票池区域标题变成「股票池设置 - T+0交易模式」,底色变成深棕、标题变橙色,回测结束后恢复
部分在清单里整池 T+1弹出「T+0模式提醒」,写明支持和不支持 T+0 的各有几只
都不在清单里T+1日志「交易模式: T+1(标准A股交易规则)」
全 T+0 股票池开始回测后,股票池区域变成 T+0 样式
全 T+0 股票池开始回测后,股票池区域变成 T+0 样式

T+0 是整个股票池一起开或关的。混合池里那几只 T+0 ETF 也按 T+1 处理,这样不会因为个别标的当天买卖而算出虚高的收益。「T+0模式提醒」窗口里勾上「这个策略不再提醒」,本次打开软件期间同一个策略不再弹出。

用命令行或 Web 工作台运行时判定规则相同,只是没有弹窗和变色,从运行日志里看交易模式。

5.2.1 可卖数量怎么变

每只持仓有两个数:持仓数量和可卖数量。卖出时只看可卖数量,不够就拒单,日志写「可用持仓不足」。

时点T+0 模式T+1 模式
买入成交后可卖数量立即加上这次买入的数量可卖数量不变
卖出成交后持仓数量和可卖数量都减去卖出量
新交易日开始不变可卖数量 = 持仓数量(昨天买的解冻)

所以在 T+0 模式下,上一根分钟线买入的份额,下一根分钟线就能卖。

5.2.2 价格精度和费用

  • 价格精度按单只证券算:场内基金(ETF、LOF)3 位小数,股票 2 位,和是否 T+0 无关。
  • ETF、基金卖出免印花税,也免过户费,网格策略的成本主要是佣金和滑点。
  • 滑点比例填的是买卖双边合计,每边各算一半。网格案例填 0.001,即单边 0.05%;日线多次触发案例为 0.01,即单边 0.5%。

5.3 日内多次触发的实战写法

1.8 讲过:触发方式选「日K线触发」时,可以把「日内触发上限」调到 2~100,在 khHandlebar 里调用 khRequestNextDailyTrigger() 申请当天再运行一轮。这里补充三点。

5.3.1 返回值

khRequestNextDailyTrigger() 申请成功返回 True,以下情况返回 False,不会多出一轮:

  • 触发方式不是日K线触发(分钟、Tick 触发下不起作用);
  • 今天已经运行到了上限(上限默认 1,要先在界面或 .kh 里调大)。

只有在 khHandlebar 里申请才会多跑一轮。在盘前、盘后回调里调用可能也返回 True,但不会多出一轮。

5.3.2 和 T+0 配合

  • T+0 池:第 1 轮买入,第 2 轮就能卖出同一批份额。
  • T+1 池:第 1 轮买入的,第 2 轮卖不出。多轮触发在这里的用途是「先卖后买」:第 1 轮卖出,第 2 轮用卖出回笼的资金买入。第 2 轮能看到第 1 轮成交后的资金和持仓。

5.3.3 同一根日线,同一个价格

多轮触发每一轮拿到的都是同一根日 K 线的数据,价格完全相同。想在盘中不同时间点看价格、做决定,用分钟或 Tick 触发,或者自定义定时触发(见 1.8)。4 个 T+0 网格案例用的都是 1 分钟触发。

5.4 自带案例

这 5 个案例在策略目录里(Windows 是 文档\KhQuant\strategies,其他平台见本篇第 1 节),每个都是 .py 策略加 .kh 配置。经典网格和 3-1~3-3 这 4 个网格案例只交易池中第 1 只;「5-日内多次触发」对池中每只分别判断。参数用随机数生成,每次运行结果都不一样,研究时请把随机函数换成你想测试的固定值。

5.4.1 【3-T0的ETF交易策略】经典网格

文件名:【3-T0的ETF交易策略】T0

第一次触发时按比例建底仓,记下锚点价。之后价格跌到锚点下方一个间距就买入一笔,涨到上方一个间距就卖出一笔,每次成交后锚点移到现价。

  • 配置:513050.SH,1 分钟触发,1 分钟前复权数据,初始资金 100 万,回测区间约 6 年。
  • 需要的数据:513050.SH 的 1 分钟前复权;复盘用的个股分析还需要日线。区间很长、数据量大,第一次跑建议先把区间缩短到几个月。

5.4.2 【3-1】固定网格

文件名:【3-1T0的ETF交易策略】固定网格策略

※ 股票池默认空,加载后请自行添加一只符合 T+0 规则的 ETF。

锚点建仓时定下后不再移动,按固定间距分层:买在下一层、卖在上一层,避免在同一条线附近来回成交。每一来回跨 2 个间距,利润约为浮动网格的 2 倍,但单边行情会把网格用完。

注意:这个案例的股票池为空,加载后先自行添加一只 T+0 ETF,再下载对应区间(2025 年 8 月~2026 年 3 月)的 1 分钟和日线数据。

5.4.3 【3-2】浮动网格

文件名:【3-2T0的ETF交易策略】浮动网络策略(文件名里写的是「网络」,安装包里就叫这个)

※ 股票池默认空,加载后请自行添加一只符合 T+0 规则的 ETF。

每次成交后锚点移到成交价,每一来回只跨 1 个间距,利润约为固定网格的一半,但趋势行情里不容易被单边用完。配置同 3-1,股票池同样需要先自行填写。

5.4.4 【3-3】跨层补单网格

文件名:【3-3T0的ETF交易策略】跨层补单网格策略

※ 股票池默认空,加载后请自行添加一只符合 T+0 规则的 ETF。

在固定网格的基础上,一根 K 线跨过 N 层时,一次按 N 倍的单笔量成交,补上被跳过的网格线。交易记录里的原因会写「网格买入 跨N层补单」。配置同 3-1。

5.4.5 【5-日内多次触发】日线先卖后买

文件名:【5-日内多次触发】日线多次触发验证

两只普通股票,按 T+1 运行,日K线触发、日内触发上限 2:

  1. 第 1 轮:持仓中收盘价跌破长均线的全部卖出,然后调用 khRequestNextDailyTrigger()。
  2. 第 2 轮:对没有持仓、短均线上穿长均线的标的,用一定比例的现金买入。

这个案例和 T+0 无关,演示的是 T+1 下的标准用法。数据只要两只股票的日线前复权,BaoStock 就能下;策略要回看最多约 35 根日线,下载起点要比回测开始日期早 2 个月左右。

5.5 用个股分析复盘

回测结束后,在结果窗口点「个股分析」标签,会打开独立的个股分析窗口(通用功能见 1.10)。复盘 T+0 时重点看:

个股分析窗口:上方日K线标出买卖日,中间是每日盈亏,下方是交易明细
个股分析窗口:上方日K线标出买卖日,中间是每日盈亏,下方是交易明细
  • 日 K 线:有买入的日子在 K 线下方标橙色 B,有卖出的标青色 S。一天不论成交几笔,只标一个 B、一个 S。鼠标悬停显示当天的价格和全部买卖。框选放大,右键还原。
  • 点某一天:弹出这一天的 1 分钟走势图,每笔买卖都标在成交时间对应的分钟线上,能看清同一天里买卖的先后和价差。
  • 交易明细:每笔成交的时间、方向、价格、数量、金额、手续费、已实现盈亏、浮动盈亏和资金变化。
日K线的框选放大和悬浮窗
日K线的框选放大和悬浮窗
注意

个股分析的日 K 线要用日线数据,日内走势图周期固定 1 分钟,复权方式与回测配置的 dividend_type 一致(默认前复权),和回测用的周期无关。T+0 网格案例只下了 1 分钟线时,日 K 线会显示「日K线加载失败,仅显示成交点」,点击也打不开日内图,记得把日线一起下好。

5.6 风险提示

  • 案例只演示软件功能和写法,参数随机生成,不构成任何策略推荐或投资建议。
  • 网格案例只交易股票池第 1 只,没有止损,单边行情会把网格用完,需要自己加仓位控制。
  • .kh 里的 risk 段(仓位上限、单笔上限、亏损上限)目前不起作用,不要依赖它做风控。
  • 货币、纯债 ETF 日内波动很小,通常覆盖不了交易成本,做网格要选波动和流动性都合适的品种。
  • T+0 资格以交易所最新公告为准。
延伸阅读:两个专栏

6 自定义指标读写(4 个)

▶ 本节视频:3.5(三)· 自定义指标的存取

6.1 原理

SZ\000001.db
└─ kline_1d:time | open high low close volume … | ma5 | DIF | DEA | MACD
                    (行情,下载时写入)              (你写入的指标)

写入脚本和回测读的是同一张表。重新下载行情时,已有的指标列不会被抹掉;但新下载的日期这几列是空的,需要再跑一次写入脚本补上。

6.2 写入:khDuckWrite

khDuckWrite(stock_list, period, data, fields=None, time_col="time",
            duckdb_path=None, field_types=None, insert_missing=False, update_time=True,
            allow_builtin_fields=False)
参数说明
stock_list股票代码,字符串或列表
period'1d'、'1m'、'5m' 或 'tick',写进对应周期的表
data{代码: DataFrame},或一个 DataFrame(多只股票时要带 stock_code 列)。必须有时间列
fields要写入的列;不填则写入除时间、代码外的全部列
duckdb_path数据目录。不填时依次使用:当前回测的数据目录、软件设置里的数据目录;都没有时报错
allow_builtin_fields默认 False:不允许改写 close、复权价等内置列。确实要导入外部行情时才设为 True
field_types指定列类型,如 {'ma5': 'DOUBLE'},一般不用填,会自动推断
insert_missing默认 False:库里没有的时间点直接丢弃;True 时新增行(新行只有时间和你的字段)

返回 {代码: {'updated': 更新行数, 'inserted': 新增行数, 'rows': …, 'fields': […]}}。三条规则:

  1. 按时间匹配:日线在库里是 2025-12-30 09:30:00 这样的时间,写入时只写日期('2025-12-30' 或 '20251230')也会自动对齐到当天 09:30。分钟线、tick 要和库里的时间完全一致。对不上的时间点不会写入,并打印警告。最稳的做法是像案例那样,先用 khDuckDB 把这只股票的 time 列读出来,在这张表上算指标,再写回去。
  2. 表里没有的列自动添加。
  3. 重跑会覆盖旧值,不会产生重复行。
提示

列名不区分大小写:已有 DIF 时再写 dif,写入的是同一列。默认不允许写 close、open_front 这类内置列,以免覆盖原始行情。自定义指标建议加前缀,如 MY_MACD。

6.3 读取

6.3.1 khDuckDB:按需查询

khDuckDB(stock_list, period, fields=None, start_time=None, end_time=None,
         dividend_type='none', duckdb_path=None)
  • 返回 {代码: DataFrame},fields 不填返回整张表。
  • end_time 写 8 位日期时包含当天这根 K 线。
  • dividend_type 默认 'none'(原始价),要前复权写 'front';自定义指标列原样返回,不做复权。
  • 不给 start_time 时,第一次查询会把这只股票的数据全部读进内存,之后在内存里取,速度不慢。给了 start_time 则每次都查库。
  • duckdb_path 不填时,和 khDuckWrite 一样默认使用回测或软件设置的数据目录。它以只读方式打开数据库,目录不存在时返回空结果,不会建出空库。
  • 数据层面出错时不抛异常:路径不对返回空字典,字段不存在返回空表,日志里有错误信息。

6.3.2 khAddExtraFields + khIndex:随行情读入(推荐)

def init(stock_codes, init_data):
    khAddExtraFields(init_data, ["DIF", "DEA", "MACD"])

def khHandlebar(data):
    dif = khIndex(data, stock_code, "DIF")

在 init 里声明要用的列,回测引擎加载行情时会把这些列和开高低收一起读进内存,日志显示「策略 init 注入自定义指标字段: [’DIF’, ‘DEA’, ‘MACD’]」。之后 khIndex 像取收盘价一样取当前这根 K 线的值(空值返回 0.0)。

  • 指标列不复权:前复权回测里,khPrice 返回前复权价,khIndex 返回库里存的原值。
  • 如果库里没有这一列:这只股票整只的行情都读不出来(日志里报一条错误「查询失败 … Referenced column “DIF” not found」):这只股票在回测里等于没有数据,khIndex 读到 0,也取不到价格、不会交易;其他股票照常回测。所有股票都缺这一列时,回测报「没有找到任何有效的时间点」并停止。请先写入。
  • 库里自带的列也能这样读,比如日线的换手率 turn(哪些列有值取决于数据源,见 1.4)。
khAddExtraFields + khIndexkhDuckDB
取什么当前这根 K 线的值任意区间、任意股票的一段历史
速度随行情一次读入,最快首次读全量后走内存,也不慢
需要填路径不需要,用回测的数据目录一般不需要,默认用同一个数据目录
列不存在时缺列的股票整只没有行情;全部缺时回测报错返回空表,策略里要自己判断

6.4 写入时的注意事项

  • 运行写入脚本前,关掉回测、数据管理窗口、定时补充等使用同一个数据目录的程序;脚本运行期间它们也打不开数据库,反过来也一样。
  • khDuckWrite 每只股票写完会立即落盘。脚本里如果自己用别的方式打开了数据库,结束前请关闭连接;自带案例在结尾已经调用了 DuckDBManager.close_instances()。
旧版本用户注意

V3.4.1.4 及更早的版本,khDuckWrite 写入新列后不会自动落盘,脚本退出后可能留下无法读取的临时文件,之后回测这些股票会失败(提示「Failure while replaying WAL file」或「没有找到任何有效的时间点」)。请升级到最新版;已经出现这个报错的,在「数据管理」点工具栏「WAL修复」恢复行情数据,再用新版本重新写入指标。

6.5 案例 4-1:写入 5 日均线

※ 写库脚本没有随机数;股票列表是示例,请换成你下载过的代码。

【4-1写入和读取数据库指标-写入数据库案例】khDuckWrite_simple.py 的步骤:

  1. 用 khDuckDB 读出一只股票的 time 和 close;
  2. df["ma5"] = df["close"].rolling(5).mean(),去掉前 4 行空值;
  3. khDuckWrite(..., fields=["ma5"]) 写回;
  4. 再读出 time、ma5,打印最后 5 行验证。

数据目录默认用软件设置里的目录;要写到别的目录,设置环境变量 DUCKDB_DATA_ROOT,或改代码里的 duckdb_path。标的默认留空,请先填写代码里的 stock_list 或设置环境变量 DUCKDB_TEST_STOCK;未填写时只提示退出,不读写数据库。正常输出的关键几行类似下面这样(条数随你下载的数据而定):

成功读取到 N 条基础数据。
--- 2. 数据加工完毕,准备写入 N 条新数据 ---
写入操作返回结果: {'000001.SZ': {'updated': N, 'inserted': 0, 'rows': N, 'fields': ['ma5']}}
【000001.SZ】验证成功!获取到的最新 5 条数据如下:

写完在数据管理的「SQL查询」里执行 SELECT time, close, ma5 FROM kline_1d ORDER BY time DESC LIMIT 5,可以看到新的一列。

6.5.1 怎么运行写入脚本(Windows 安装版)

4-1、4-2 是普通的 Python 脚本,不是策略,不能用「加载配置」或 kh run 运行。安装版没有单独的 Python,需要自己装一个,再让它用上 khQuant 安装目录里的函数库。只需要准备一次:

  1. 安装 Python 3.11(64 位):从 Python 官网下载 3.11.x 的 Windows 64 位安装程序,安装时勾选「Add python.exe to PATH」。
  2. 安装依赖:打开命令提示符,运行(版本号和 khQuant 自带的保持一致):
    py -3.11 -m pip install pandas==2.3.1 numpy==2.2.6 duckdb==1.4.3 pytz holidays requests psutil
  3. 建一个工作文件夹,比如 C:\khwork,在里面新建 run_write.py,内容如下(khQuant 装在别处的,改第二行的路径):
    import os, sys, runpy
    KH = r"C:\Program Files (x86)\khQuant\_internal"
    os.add_dll_directory(KH)
    sys.path.append(KH)
    runpy.run_path(sys.argv[1], run_name="__main__")
    它的作用是让 Python 能找到 khQuant 的函数库和运行库,再去运行你指定的脚本。

之后每次写入:

  1. 把 4-1 从安装目录的 _internal\strategies(默认 C:\Program Files (x86)\khQuant\_internal\strategies)复制到 C:\khwork,改名为 write_ma5.py。从旧版本升级上来的,文档\KhQuant\strategies 里可能还是旧版的案例文件(软件不会覆盖你已有的文件),请以安装目录里的为准。
  2. 关掉 khQuant 的数据管理窗口和正在跑的回测,在 C:\khwork 运行:
    cd C:\khwork
    py -3.11 run_write.py write_ma5.py

看到「验证成功」和最后 5 行数据,就写好了。4-2 同样处理,也需要先填写自己的标的。股票池模式默认关闭;需要时按脚本注释切换,并把 CSV 路径改成自己的文件。

提示

直接把脚本放在别处用 Python 运行,会提示「No module named ‘khQTTools’」,这就是没通过 run_write.py 运行。依赖装错版本时,常见报错是 pandas 或 duckdb 导入失败,按第 2 步的版本重新安装即可。

6.6 案例 4-2:批量写入 MACD

※ 写库脚本没有随机数;股票列表是示例,请换成你下载过的代码。

【4-2】khDuckWrite_macd.py 读沪深 300 成分股列表,逐只计算:

EMA12 = close.ewm(span=12, adjust=False).mean()
EMA26 = close.ewm(span=26, adjust=False).mean()
DIF  = EMA12 - EMA26
DEA  = DIF.ewm(span=9, adjust=False).mean()
MACD = (DIF - DEA) * 2

然后把 DIF、DEA、MACD 三列写回。注意:

  • 代码默认处理全部 300 只(注释说切前 3 只,但代码没切),第一次练习建议把 stock_list[:] 改成 stock_list[:3],或直接写成你下载过的几只。
  • 它用原始价(不复权)计算 MACD,除权日前后指标会跳。想和前复权回测口径一致,可以在读数据的 khDuckDB 里加 dividend_type='front'。

6.7 案例 4-4:随行情读入 MACD

※ 只有买入比例是随机的。

【4-4直接读取底层指标-自定义指标策略案例】DirectIndex_MACD_Strategy:

  1. init 里 khAddExtraFields(data, ["DIF", "DEA", "MACD"]);
  2. 每根 K 线用 khIndex 取当天的 DIF、DEA,用一个全局字典记住昨天的值(第一根只记录不交易);
  3. 昨天 DIF < DEA、今天 DIF > DEA 视为金叉,没有持仓就买;反过来死叉、有持仓就全部卖出。

配置:股票池留空,请自行填写已写入 MACD 的股票;回测区间 2024-01-01 到 2025-12-31,日线前复权。先按 4-2 给所选股票写入 MACD 再运行。

6.8 案例 4-3:用 khDuckDB 查询

※ 只有买入比例是随机的。

【4-3写入和读取数据库指标-读取数据库指标的策略案例】DuckDB_MACD_Strategy 实现同样的信号,只是每根 K 线用 khDuckDB(..., end_time=当天) 查询到当天为止的 DIF、DEA,取最后两行当「今天」和「昨天」。同一份数据下,4-3 和 4-4 的买卖日期基本相同(4-4 首根 K 线只记录不交易),股数因随机的买入比例不同而不同。

4-3 读的是和回测相同的数据目录,不需要额外设置(除非你设置了环境变量 DUCKDB_DATA_ROOT,那样它会改读那个目录)。

6.9 关于「当天收盘值」

4-3、4-4 用的是当天收盘后算出的 DIF、DEA,并按当天收盘价成交,相当于「收盘那一刻判断、立刻按收盘价成交」,这是理想化的假设。想更贴近实际,可以改成用昨天的指标判断、今天开盘价成交。

7 准备数据

案例需要的数据建议下载起点
双均线 3 个、RSI对应股票的日线,前复权2024-09-01(回测从 2025 年初开始,均线和 RSI 最多要往前看约 60 个交易日)
4-3、4-4日线前复权,并先用 4-2 写入 MACD2023 年初(MACD 需要较长的预热)
5-日内多次触发自行填写研究标的,使用日线前复权2023-11 起
T+0 网格ETF 的 1 分钟线(加日线供复盘)见第 5 节

日线可以用任何数据源,用 tx 最简单(免费,记得加 --adj front):

kh data download --source tx --stocks 000001.SZ,000002.SZ,600000.SH --period 1d --adj front --start 20230101

界面里用「数据管理 → tx数据导入」,手动输入这几只代码、勾选前复权即可。

8 建议的顺序

  1. 【1-MA】双均线精简_使用khMA函数:单股、全仓、代码最短,最容易看懂资金曲线和每笔成交。
  2. 【1-MA】双均线多股票_使用khMA函数:学会遍历股票池和部分仓位。
  3. 【2-RSI】RSI策略:学会 khHistory 加 MyTT 指标、穿越判断。
  4. 【1-MA】双均线多股票_使用MA函数:对照第 2 个,理解两种算指标的写法(先自行填写股票池)。
  5. 【5-日内多次触发】:日线上的先卖后买(第 5 节)。
  6. 【3-】T+0 网格系列:需要 ETF 分钟数据(第 5 节)。
  7. 【4-2】→【4-4】→【4-3】:先写指标再读(第 6 节)。

常见问题

问:回测跑完没有任何交易?
答:先看日志里是不是提示历史数据不够。往前多下 3 个月的数据;用 tx 下载时要加 --adj front。

问:「双均线多股票_使用MA函数」跑了很久?
答:随包股票池为空;自行选择大量标的时会耗时较长,可先用少量标的验证。

问:3-1、3-2、3-3 提示找不到 xxxxxx.SH?
答:旧配置可能带占位符;新随包配置股票池为空,都需要自行填写一只符合 T+0 规则的 ETF。

问:加载 4-1、4-2 报错?
答:它们是写数据库的脚本,不是策略,用法见第 6 节。

问:股票池里都是 ETF,为什么还是 T+1?
答:ETF 不一定都能 T+0。只有在 T+0 清单里的才算,池里只要有一只不在清单里,整池就按 T+1。看运行开始时有没有弹「T+0模式提醒」。

问:卖出被拒,提示「可用持仓不足」?
答:T+1 模式下当天买入的不能当天卖。确认股票池是否全部在 T+0 清单里。

问:khRequestNextDailyTrigger() 一直返回 False?
答:检查触发方式是不是「日K线触发」、「日内触发上限」是否大于 1。另外,只有在 khHandlebar 里申请才会多跑一轮。

问:个股分析里点日 K 线没有弹出日内图?
答:缺日线或 1 分钟数据。缺日线时日 K 线根本画不出来;缺 1 分钟数据时会提示「数据缺失」。把对应日期的数据下好即可。

问:同一个案例跑两次,结果不一样?
答:案例参数是随机生成的。把策略开头的随机函数改成固定数值,结果就稳定了。

问:写入返回 updated: 0?
答:时间对不上(分钟线要和库里的时间完全一致),或者这只股票在这些日期本来就没有行情。看脚本打印的警告;最稳的做法是先用 khDuckDB 读出 time 列再计算。

问:回测报「Failure while replaying WAL file」或「没有找到任何有效的时间点」?
答:有两种可能。一是用 V3.4.1.4 或更早版本写入后留下的问题,见第 6.4 节:用「数据管理 → WAL修复」修复,升级后重新写入。二是策略用 khAddExtraFields 登记了自定义列,而股票池里所有股票都还没写入这一列(只缺几只时,缺的那几只没有行情,其余照常),见第 6.3 节。

问:提示「另一个程序正在使用此文件」?
答:写入脚本和回测、下载、数据管理窗口同时在用同一个数据目录。关掉其他程序再运行。

问:4-4 报 Referenced column “DIF” not found?
答:这只股票还没写入 DIF,先运行 4-2。

问:写入时报不允许改写内置列?
答:列名和 close 等内置列重名了,换一个名字;确实要导入外部行情才加 allow_builtin_fields=True。

问:运行写入脚本提示 No module named ‘khQTTools’?
答:要通过 run_write.py 运行,见第 6.5 节。

各个函数(khGet、khPrice、khHistory、khMA、generate_signal 等)的完整说明见 3.2 策略工具箱;回调和信号的规则见 3.1。

注意

所有案例只用于演示软件功能和写法,参数随机生成,不构成任何投资建议,回测结果不代表未来收益。

本篇基于 V3.4.1.10 编写。

×
没有账号?注册  忘记密码?

风险提示

投资有风险,开户需谨慎。本系统仅为投资者提供量化交易相关的数据处理与分析工具,不构成任何投资建议。 请您在审慎思考后作出选择。特别声明:本系统对您与券商之间的交易、合作不承担任何法律责任。 市场有风险,投资需谨慎。

© 2024 khQuant看海量化回测平台 版权所有

官网:www.khsci.com/khQuant