2.6 自定义指标的写入与读取

在量化策略开发中,随着策略复杂度的提升,实时计算所有指标(如复杂的因子、机器学习预测值或非量价数据)往往会成为回测效率的瓶颈。看海量化回测系统 V3.2 版本引入了基于 DuckDB 的指标预计算与持久化机制,允许用户将计算好的指标写入本地数据库,并在策略中以极低的延迟读取,从而实现“一次计算,多次复用”。

前置阅读建议

本章内容涉及策略框架的高级应用。为了更好地理解相关概念,建议您在阅读本章之前,先充分理解看海量化回测系统的基础框架。请务必先行阅读官方教程的 3.1 – 3.3 节

只有在充分掌握了策略文件的基本结构(如 init, khHandlebar)以及常用 API(如 khGet, khPrice)的使用后,您才能更顺畅地运用本章介绍的高级数据管理功能。

本章将详细介绍相关的核心函数定义及使用流程。


一、 核心工具函数详解

系统提供了两个核心函数用于数据库的读写操作:khDuckWrite(写入)和 khDuckDB(读取)。它们位于 khQuantImport 模块中,可直接调用。

1.1 khDuckWrite:写入数据

khDuckWrite 是将数据持久化到本地 DuckDB 数据库的核心函数。它具备智能的 Schema 管理能力,当写入新字段时,会自动在数据库表中添加对应列,无需手动维护表结构。

函数定义

def khDuckWrite(
    stock_list: Union[str, List[str]], 
    period: str, 
    data: Union[Dict, pd.DataFrame], 
    fields: List[str] = None, 
    time_col: str = "time", 
    duckdb_path: str = None, 
    field_types: Dict = None, 
    insert_missing: bool = False, 
    update_time: bool = True
) -> Dict:

参数说明

  • stock_list: 股票代码或列表(如 '000001.SZ'['000001.SZ', '600000.SH'])。
  • period: 数据周期类型,支持 '1d' (日线), '1m' (分钟线), '5m' (5分钟线), 'tick' (Tick数据)。
  • data: 数据源,支持两种格式:
    • Dict: {stock_code: DataFrame} 字典格式。
    • DataFrame: 单只股票的 DataFrame,或包含 stock_code 列的多股票 DataFrame。
  • fields: (可选) 指定要写入的字段名列表。如果为 None,则自动推断 data 中除时间和股票代码外的所有列。
  • time_col: (可选) 数据源中的时间列名,默认为 'time'。系统会自动将其标准化为 DuckDB 的时间格式。
  • duckdb_path: (可选) 指定 DuckDB 数据根目录。默认为空,自动使用系统配置的路径。
  • field_types: (可选) 字段类型映射字典,如 {'ma5': 'DOUBLE'}。通常不需要指定,系统会自动推断。
  • insert_missing: (可选) 是否插入数据库中不存在的时间点记录。默认为 False(仅更新已有时间点的记录)。
  • update_time: (可选) 是否自动更新 update_time 列,记录最后修改时间。默认为 True

返回值

返回一个字典,包含每个股票的写入结果统计:

{
    '000001.SZ': {
        'updated': 100,  # 更新的行数
        'inserted': 0,   # 插入的新行数
        'rows': 100,     # 总处理行数
        'fields': ['ma5', 'ma10'] # 写入的字段
    }
}

使用示例

import pandas as pd
from khQuantImport import *

# 假设我们计算好了 000001.SZ 的 MA5 数据
df = pd.DataFrame({
    'time': ['2024-01-01', '2024-01-02'],
    'MA5': [10.5, 10.6]
})

# 写入数据库
khDuckWrite(
    stock_list='000001.SZ',
    period='1d',
    data=df,
    fields=['MA5']
)

1.2 khDuckDB:读取数据

khDuckDB 是通用的数据读取接口,支持跨周期、跨标的获取历史数据,并支持自动复权处理。

函数定义

def khDuckDB(
    stock_list: Union[str, List[str]], 
    period: str, 
    fields: List[str] = None, 
    start_time: str = None, 
    end_time: str = None, 
    dividend_type: str = 'none', 
    duckdb_path: str = None, 
    return_format: str = 'dict'
) -> Dict[str, pd.DataFrame]:

参数说明

  • stock_list: 股票代码或列表。
  • period: 周期类型 ('1d', '1m', '5m', 'tick')。
  • fields: (可选) 需要读取的字段列表。None 表示读取所有字段。
  • start_time: (可选) 开始时间,格式 'YYYYMMDD''YYYYMMDDHHmmss'
  • end_time: (可选) 结束时间。
  • dividend_type: (可选) 复权类型,支持 'none' (不复权), 'front' (前复权), 'back' (后复权) 等。
  • duckdb_path: (可选) 自定义数据库路径。
  • return_format: (可选) 返回格式,目前仅支持 'dict'

返回值

返回字典 {股票代码: DataFrame}


二、 策略集成:如何在回测中使用自定义指标

在策略中使用自定义指标主要有两种方式。推荐使用 方式 A,因为它利用了内存预加载机制,效率极高。

方式 A:高性能内存读取(推荐)

这套方案通过 khAddExtraFields 在初始化阶段预加载数据,然后在 khHandlebar 中通过 khIndex 极速读取。

步骤 1:在 init 中声明字段

使用 khAddExtraFields 告诉回测引擎需要预加载哪些额外列。

def init(context, data):
    # 声明需要使用 'MA_5' 和 'RSI_14' 这两个自定义指标
    # 系统会在回测开始前,自动将这些列从 DuckDB 加载到内存
    khAddExtraFields(data, ['MA_5', 'RSI_14'])

步骤 2:在 khHandlebar 中读取

使用 khIndex 函数获取当前时间步的指标值。此操作直接访问内存,零 IO 开销。

def khHandlebar(data):
    # 获取当前股票代码
    stock = "000001.SZ"

    # 直接读取指标值
    ma5 = khIndex(data, stock, 'MA_5')
    rsi = khIndex(data, stock, 'RSI_14')

    # 使用指标进行逻辑判断
    if rsi > 80:
        # 执行卖出逻辑...
        pass

函数说明:khIndex(data, stock_code, field)
* 功能: 获取指定股票、指定字段在当前时间点的值。
* 特点: 实际上是 khPrice 的语义化别名,共享其高效的内存访问机制。如果字段不存在或值为 NaN,返回 0.0


方式 B:灵活动态读取

如果需要在策略中获取过去一段历史区间的数据(例如“获取过去100天的 MA5 序列”),或者需要跨标的读取数据(例如“在交易 A 股时读取 B 股的数据”),可以使用 khDuckDB

注意: khDuckDB 涉及磁盘 IO 和数据库查询,频繁调用会降低回测速度。建议仅在低频场景(如每日收盘后)使用。

def khHandlebar(data):
    # 示例:获取 000001.SZ 过去 20 天的 close 和 MA_5 数据
    data_map = khDuckDB(
        stock_list=['000001.SZ'],
        period='1d',
        fields=['close', 'MA_5'],
        start_time='20230101', # 示例时间,实际应根据 khGet(data, 'date') 动态计算
        end_time='20230120'
    )

    df = data_map.get('000001.SZ')
    if df is not None and not df.empty:
        # 进行复杂的历史数据分析...
        pass

三、 配套案例代码详解

为了方便大家上手,系统在 strategies 目录下提供了 4 个完整的代码案例,覆盖了从“数据计算写入”到“策略读取回测”的全流程。这些案例按序号 4-14-4 排列,建议按照顺序阅读和运行。

3.1 数据写入案例

这两份代码不是回测策略,而是独立运行的 Python 脚本。你需要直接在 IDE 中运行它们,将计算好的指标写入到本地 DuckDB 数据库中。

【4-1】基础入门:khDuckWrite_simple.py

  • 文件路径: strategies/【4-1写入和读取数据库指标-写入数据库案例】khDuckWrite_simple.py
  • 功能: 演示最基础的“读取-计算-写入”流程。
  • 重要提示: 运行前请务必打开代码文件,将 duckdb_path 变量修改为您本地实际的 DuckDB 数据存放目录(例如 D:\stock_data),否则无法正确写入。
  • 逻辑:
    1. 从 DuckDB 读取 000001.SZ 的历史收盘价 (close)。
    2. 计算 5 日均线 (ma5)。
    3. 使用 khDuckWritema5 字段写回数据库。
  • 适用场景: 适合初学者理解 khDuckDBkhDuckWrite 的基本用法。

【4-2】进阶实战:khDuckWrite_macd.py

  • 文件路径: strategies/【4-2写入和读取数据库指标-写入数据库案例】khDuckWrite_macd.py
  • 功能: 演示批量处理股票池及多字段写入。
  • 重要提示: 同样,运行前请务必修改代码中的 duckdb_path 为您本地的真实路径。
  • 逻辑:
    1. 读取 沪深300成分股 列表。
    2. 遍历每只股票,读取历史行情。
    3. 计算 MACD 指标的三个分量:DIF, DEA, MACD
    4. 批量将这三个字段写入数据库。
  • 适用场景: 适合需要大规模预计算复杂因子(如 MACD, KDJ, Bollinger 等)的用户。

3.2 策略读取案例

这两份代码是真正的回测策略,需要在看海量化回测系统的回测界面中加载运行。它们演示了如何在回测中利用刚才写入的数据。

【4-3】动态读取策略:DuckDB_MACD_Strategy.py

  • 文件路径: strategies/【4-3写入和读取数据库指标-读取数据库指标的策略案例】DuckDB_MACD_Strategy.py
  • 核心方法: 使用 khDuckDB 函数在 khHandlebar 中动态查询。
  • 逻辑:
    • 在每一天,针对每一只股票,调用 khDuckDB 查询过去几天的 DIFDEA 数据。
    • 根据查询到的 DataFrame 判断金叉/死叉,发出买卖信号。
  • 特点: 代码逻辑直观,适合低频交易或需要回溯长周期历史数据的场景。但在全市场回测时速度较慢(因为涉及频繁的磁盘 IO)。

【4-4】高性能策略:DirectIndex_MACD_Strategy.py(强烈推荐)

  • 文件路径: strategies/【4-4直接读取底层指标-自定义指标策略案例】DirectIndex_MACD_Strategy.py
  • 核心方法: 使用 khAddExtraFields + khIndex 组合拳。
  • 逻辑:
    • Init 阶段: 调用 khAddExtraFields(data, ["DIF", "DEA", "MACD"]),通知引擎预加载这三个字段。
    • Handlebar 阶段: 调用 khIndex(data, stock, "DIF") 直接从内存获取当日指标值。
    • 状态维护: 使用全局字典 g_yesterday_indicators 记录上一日的指标值,配合当日值判断金叉/死叉。
  • 特点: 极速回测。由于数据预先加载到内存,读取操作零 IO 开销,非常适合全市场高频或中频策略的回测。

3.3 推荐的学习路径

  1. 运行 4-1: 确保环境配置正确,成功写入简单的 ma5 数据。
  2. 运行 4-2: 尝试计算并写入 MACD 数据(建议先用少量股票测试)。
  3. 回测 4-4: 加载 DirectIndex_MACD_Strategy.py,体验基于预计算数据的极速回测。
  4. 参考 4-3: 如果你的策略逻辑非常复杂(例如需要跨品种套利),再参考此案例学习如何灵活读取 DuckDB。

四、 最佳实践总结

  1. 预计算优于实时计算: 凡是逻辑复杂、输入固定的指标,都应优先考虑预计算并存入 DuckDB。
  2. 内存读取优于磁盘读取: 在策略主循环中,尽量使用 khIndex 读取预加载的数据。khDuckDB 仅用于复杂的历史回溯或跨标的查询。
  3. 字段命名规范: 自定义指标字段名建议具备可读性且避免与内置字段(open, close, volume 等)冲突,例如使用 MY_MACD, FACTOR_MOMENTUM 等。
  4. 数据对齐: 写入数据时,khDuckWrite 默认会根据 time 列自动对齐。建议确保时间戳格式标准(YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS)。

通过掌握这套自定义指标管理机制,您可以构建出更高效、更复杂的量化回测系统,将数据工程与策略逻辑解耦,专注于策略本身的价值发现。

显示验证码
没有账号?注册  忘记密码?

风险提示

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

© 2024 看海量化交易系统 版权所有

官网:www.khsci.com/khQuant