第十四章:从零到一:构建你的第一个量化策略

在前面的章节中,我们已经熟悉了看海量化(KHQuant)的界面操作和核心功能。现在,是时候进入最激动人心的部分了——亲手编写一个属于自己的量化交易策略。

本章将作为一份详尽的实战教程,以经典的“双均线交叉”策略为蓝本,从最基础的概念开始,一步步引导您完成从策略思想到代码实现的全过程。您将学到:

  • 如何将一个交易想法转化为具体的策略逻辑。
  • 如何利用KHQuant框架和工具函数编写一个完整的单股票策略。
  • 策略编写中的关键注意事项和“避坑”指南。
  • 如何将单股票策略“升级”为能够同时处理多只股票的股票池策略。
  • 单/多股票策略在回测设置上的核心区别。

即使您是编程新手,也不必担心。本章将以最直观易懂的方式进行讲解,结合我们之前在第十三章介绍的便捷函数,您会发现编写一个量化策略比想象中要简单得多。


14.1 策略思想:双均线交叉

在开始编写代码之前,我们首先要明确策略的“思想”或“逻辑”。这是所有量化策略的起点。

双均线交叉策略是技术分析中最广为人知的策略之一,它的核心思想是利用两条不同周期的移动平均线(Moving Average, MA)来判断市场趋势的转换,并产生交易信号。

  • 短期均线 (Short MA):通常选择较短的周期,如5日、10日。它对价格变动更敏感,能快速反映近期的价格趋势。
  • 长期均线 (Long MA):通常选择较长的周期,如20日、60日。它对价格变动较为平滑,能反映长期的市场趋势。

交易规则:

  1. 买入信号(金叉):当短期均线从下方上穿长期均线时,被视为一个看涨信号,表明短期趋势开始强于长期趋势,可能预示着一轮上涨行情的开始。此时,我们执行买入操作。
  2. 卖出信号(死叉):当短期均线从上方下穿长期均线时,被视为一个看跌信号,表明短期趋势开始弱于长期趋势,可能预示着一轮下跌行情的开始。此时,我们执行卖出操作。

仓位管理规则:

  • 买入:当出现金叉信号且我们当前没有持仓时,全仓买入。
  • 卖出:当出现死叉信号且我们当前持有仓位时,全仓卖出。

💡 思考与简化:
这个策略非常纯粹,它只关心一件事:两条均线的相对位置。它不考虑成交量、基本面或其他任何指标。这种简单性使其成为学习策略编写的绝佳范例。


14.2 运行前的准备:回测参数设置

在深入代码细节之前,让我们先在看海量化的主界面上,为即将运行的 双均线精简.py 策略配置好正确的“土壤”。一个正确的配置是策略能否按预期运行的前提。

对于我们的日线级别双均线策略,推荐的核心配置如下:

  1. 选择策略文件:
  • 在主界面的“策略文件”下拉菜单中,找到并选择 strategies/双均线精简.py
  1. 设置回测时段:
  • 选择一个您感兴趣的回测开始和结束日期,例如 2025-01-012025-07-03,以观察策略在过去一整年的表现。
  1. 配置数据与触发方式 (核心):
  • 数据设置:
    • 周期: 选择 1d。因为我们的策略是基于“日”均线,所以必须使用日线级别的数据作为计算基础。
  • 触发方式:
    • 类型: 选择 日K线触发。这个设置确保了我们的策略主逻辑函数 khHandlebar 每天只在开盘时被调用一次。这完美匹配了我们日线级别的交易频率。
  1. 设置股票池:
  • 因为 双均线精简.py 是一个单股票策略,所以我们只需要在“股票池”区域添加一只股票作为回测标的即可。
  • 点击“添加股票”按钮,输入您想回测的股票代码,例如 000001.SZ (平安银行)。

单股票策略回测设置

⚠️ 重要提示1:
数据周期和触发方式的匹配至关重要。对于日线策略,使用 1d 数据和 日K线触发 是最合理、最高效的组合。如果错误地使用了 tick 数据或 tick触发,会导致策略在每个tick都进行一次不必要的日线计算,极大地拖慢回测速度且没有意义。

⚠️ 重要提示2:

在运行策略前,务!必!确!认!所选股票在指定的时间段内成功补充了相应周期的数据。

完成以上设置后,我们就有了一个随时可以运行回测的正确环境。现在,让我们带着这些设置,去深入理解策略代码是如何实现我们的交易思想的。


14.3 单股票策略实现 (以 双均线精简.py 为例)

现在,我们将上述思想翻译成代码。我们将以 双均线精简.py 文件为例,逐行进行讲解。

# coding: utf-8
# 1. 导入工具包
from khQuantImport import *

# 2. 初始化函数 (本次策略未使用)
def init(stocks=None, data=None):
    """策略初始化"""
    pass

# 3. 策略主逻辑函数
def khHandlebar(data: Dict) -> List[Dict]:
    """策略主逻辑,在每个K线或Tick数据到来时执行"""

    # 4. 初始化信号列表
    signals = []

    # 5. 获取数据
    stock_code = khGet(data, "first_stock")
    current_price = khPrice(data, stock_code, "open")
    current_date_num = khGet(data, "date_num")

    # 6. 计算指标
    ma_short = MA(stock_code, 5, end_time=current_date_num)
    ma_long = MA(stock_code, 20, end_time=current_date_num)

    # (可选) 打印日志用于调试
    logging.info(f"计算结果 - 短期均线: {ma_short:.2f}, 长期均线: {ma_long:.2f}")

    # 7. 获取持仓状态
    has_position = khHas(data, stock_code)

    # 8. 编写交易逻辑
    if ma_short > ma_long and not has_position:
        # 9. 生成买入信号
        signals = generate_signal(data, stock_code, current_price, 1.0, 'buy', 
                                  f"5日线({ma_short:.2f})上穿20日线({ma_long:.2f})")

    elif ma_short < ma_long and has_position:
        # 10. 生成卖出信号
        signals = generate_signal(data, stock_code, current_price, 1.0, 'sell', 
                                  f"5日线({ma_short:.2f})下穿20日线({ma_long:.2f})")

    # 11. 返回信号
    return signals

代码分步详解

  1. 导入工具包: from khQuantImport import *
  • 这是编写所有策略的“魔法咒语”。它将第十三章中介绍的所有便捷函数(如 khGet, khPrice, MA, generate_signal 等)全部导入,让我们可以直接使用。
  1. 初始化函数 init:
  • 这个函数在整个回测任务开始时只执行一次。对于一些复杂的策略,可以在这里定义全局变量、加载外部数据等。但对于我们这个简单的双均线策略,不需要任何初始化操作,所以函数体只有一行 pass,表示“什么都不做”。
  1. 策略主逻辑函数 khHandlebar:
  • 这是策略的“心脏”。框架会根据你在主界面设置的触发方式(如“日K线触发”),在每个交易日开盘时调用一次这个函数。所有的判断和交易逻辑都在这里完成。
  • 它接收一个名为 data 的字典作为参数,这个字典就是我们之前提过的 context,包含了当前时间点所有需要的信息。
  • 它必须返回一个列表(List[Dict]),这个列表里装着交易信号。
  1. 初始化信号列表: signals = []
  • 我们首先创建一个空的列表,用来存放本轮可能产生的交易信号。这是一个好习惯。
  1. 获取数据:
  • stock_code = khGet(data, "first_stock"): 对于单股票策略,我们通常只关心股票池里的第一只(也是唯一一只)股票。khGet 函数可以轻松地帮我们拿到它的代码。
  • current_price = khPrice(data, stock_code, "open"): 我们获取该股票当天的开盘价,并将其作为我们买卖的参考价格。
  • current_date_num = khGet(data, "date_num"): 我们获取 "YYYYMMDD" 格式的当前日期,这是为了在下一步计算历史均线时,告诉函数数据应该截止到哪一天,以避免使用未来数据
  1. 计算指标:
  • ma_short = MA(stock_code, 5, end_time=current_date_num): 调用 MA 函数计算5日短期均线。
  • ma_long = MA(stock_code, 20, end_time=current_date_num): 计算20日长期均线。
  • ⚠️ 注意:这里传入 end_time 参数至关重要!它确保了在计算均线时,只使用截止到 current_date_num 之前的数据,完美地避免了未来函数问题,保证了回测的公平性。
  1. 获取持仓状态: has_position = khHas(data, stock_code)
  • 使用 khHas 函数判断我们当前是否持有这只股票的仓位。
  1. 编写交易逻辑:
  • if ma_short > ma_long and not has_position:: 这是金叉买入的判断条件。ma_short > ma_long 表示短期均线在长期均线上方(即金叉状态),not has_position 表示我们当前没有持仓。两个条件同时满足,才会触发买入。
  • elif ma_short < ma_long and has_position:: 这是死叉卖出的判断条件。ma_short < ma_long 表示死叉状态,has_position 表示我们当前持有仓位。
  1. 生成买入信号:
  • 我们调用 generate_signal 函数。
  • data: 传入完整的 context
  • stock_code, current_price: 传入股票代码和计划交易的价格。
  • 1.0: 资金使用比例。1.0 表示使用全部可用资金。函数会自动计算能买多少股。
  • 'buy': 交易方向。
  • f"...": 交易原因,使用f-string可以动态地将当前的均线值记录下来,方便复盘。
  1. 生成卖出信号:
    • 同样调用 generate_signal 函数。
    • 1.0: 持仓卖出比例。1.0 表示卖出全部持仓
    • 'sell': 交易方向。
  2. 返回信号: return signals
    • 将包含交易信号的列表返回给框架。框架收到后会自动执行下单。如果整个过程没有满足任何条件,返回的就是一个空列表,表示本轮无操作。

14.4 多股票策略进阶 (以 双均线多股票.py 为例)

学会了单股票策略后,将其扩展为能同时处理一个股票池的多股票策略,其实非常简单。核心思想就是将原有的逻辑放入一个循环中,对股票池里的每一只股票都独立地执行一遍

在动手修改代码之前,请先在主界面上做好准备:

  • 选择策略文件:将策略文件切换为 双均线多股票.py
  • 修改股票池:这是关键一步。您需要清空之前只包含单只股票的股票池,然后通过“添加股票”或“导入股票池”功能,加入多只股票,例如导入“沪深300成分股”列表。

多股票策略回测设置

做完以上准备后,让我们看看 双均线多股票.py 是如何实现的。

# coding: utf-8
from khQuantImport import *

def init(stocks=None, data=None):
    """策略初始化"""
    pass

def khHandlebar(data: Dict) -> List[Dict]:
    """策略主逻辑,支持多只股票的双均线策略"""
    signals = []

    # 1. 获取整个股票池
    stock_list = khGet(data, "stocks")
    current_date_num = khGet(data, "date_num")

    # 2. 遍历股票池
    for stock_code in stock_list:
        # --- 循环内部的逻辑与单股票策略几乎完全相同 ---

        current_price = khPrice(data, stock_code, "open")
        ma_short = MA(stock_code, 5, end_time=current_date_num)
        ma_long = MA(stock_code, 20, end_time=current_date_num)

        has_position = khHas(data, stock_code)

        if ma_short > ma_long and not has_position:
            # 3. 调整仓位管理
            buy_signal = generate_signal(data, stock_code, current_price, 0.5, 'buy', 
                                       f"{stock_code[:6]} 金叉买入")
            # 4. 使用 extend 添加信号
            signals.extend(buy_signal)

        elif ma_short < ma_long and has_position:
            sell_signal = generate_signal(data, stock_code, current_price, 1, 'sell', 
                                        f"{stock_code[:6]} 死叉卖出")
            signals.extend(sell_signal)

    return signals

与单股票策略的核心区别

  1. 获取整个股票池:
  • 不再使用 khGet(data, "first_stock"),而是 stock_list = khGet(data, "stocks"),一次性获取你在主界面股票池中添加的所有股票代码。
  1. 遍历股票池:
  • 使用 for stock_code in stock_list: 循环,对列表中的每一只股票重复执行后续的逻辑。
  1. 调整仓位管理:
  • 在单股票策略中,我们可以全仓买入(ratio=1.0)。但在多股票策略中,如果对第一只满足条件的股票就全仓买入,后面即使有更好的机会也没有资金了。
  • 因此,我们将买入的资金比例调整为一个较小的值,例如 0.5 (即50%)。这意味着每只股票最多使用当前可用资金的50%来建仓,这样可以将资金分散投资到多只股票上,降低单一个股的风险。
  1. 使用 extend 添加信号:
  • generate_signal 返回的是一个列表。在循环中,我们需要将每次新生成的信号列表追加到总的 signals 列表中。这里使用 signals.extend(buy_signal) 而不是 signals.append(buy_signal)
  • 区别: append 会将整个列表作为一个元素添加进去,形成 [[signal1], [signal2]] 这样的嵌套结构,这是错误的。而 extend 会将新列表中的元素逐个取出,添加到现有列表中,形成 [signal1, signal2] 这样的扁平结构,这才是框架需要的格式。

当然了,使用多股票的策略是可以兼容单支股票的。

运行上述两个策略,得到的结果如下:

单股票策略回测结果

多股票策略回测结果


14.5 总结与展望

恭喜你!通过本章的学习,你已经掌握了:

  • ✅ 将一个交易思路(双均线交叉)转化为代码的全过程。
  • ✅ 编写一个简洁且功能完备的单股票策略。
  • ✅ 将单股票策略轻松扩展为支持股票池的多股票策略。
  • ✅ 理解了两种策略在回测设置上的根本区别。

这只是你量化交易之旅的开始。基于本章所学的知识,你可以尝试进行各种扩展:

  • 更换指标:将 MA 换成 EMA, BOLL, MACD 等其他你熟悉的技术指标。
  • 优化参数:尝试不同的均线周期组合(如10日 vs 30日),看看回测效果有何不同。
  • 改进仓位管理:尝试更复杂的仓位管理模型,例如根据市场波动率动态调整仓位大小。
  • 增加过滤条件:在买入前增加其他过滤条件,例如要求成交量放大,或者要求股票处于上涨趋势等。

现在,打开看海量化交易平台,开始构建属于你自己的第一个策略吧!

风险提示

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

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

官网:www.khsci.com/khQuant