VeighNa量化社区
你的开源社区量化交易平台 | vn.py | vnpy
Member
avatar
加入于:
帖子: 2
声望: 0

老师好
在学习vnpy过程中,遇到组合策略出现创建策略失败,报找不到策略(策略是vnpy自带的),但是用cta策略(vnpy自带的)调试正常的。不知道组合策略哪里出问题了,麻烦有空帮忙解答下,谢谢!
description

Member
avatar
加入于:
帖子: 6109
声望: 374
  1. “用cta策略(vnpy自带的)调试正常的”是什么意思呢?
  2. TrendFollowingStrategy是有一点小问题,等待修复,可以先试试别的策略吧;
  3. 建议使用vn.py官方打包的Python发行版VN Studio作为Python环境,同时使用Visual Studio Code(安装Python和Pylance插件)作为IDE工具。
    对于新手来说,尤其不推荐使用PyCharm,作为针对大型Python项目的PyCharm,使用时需要用户对运行环境进行大量细节配置,不熟悉的人很容易在各种地方出错,浪费大量时间去爬坑。
Member
avatar
加入于:
帖子: 2
声望: 0

老师好
1.用cta策略(vnpy自带的)调试正常的,意思就是用CTA引擎加载策略正常的

description

2.我换了个组合策略加载还是不行,这里得到空,导致找不到策略

description

Member
avatar
加入于:
帖子: 6109
声望: 374

从你图上看,portfolio_engine.init_engine后面好像没加括号

Member
avatar
加入于:
帖子: 14
声望: 0

在使用组合策略vnpy_portfoliostrategy 也遇到了类似的问题,执行了init_engine 之后 self.strategies 仍然为空,策略添加一直失败:

def add_strategy(
self, class_name: str, strategy_name: str, vt_symbols: list, setting: dict
) -> None:
"""添加策略实例"""
if strategy_name in self.strategies:
self.writelog(("创建策略失败,存在重名{}").format(strategy_name))
return

    strategy_class: type[StrategyTemplate] | None = self.classes.get(class_name, None)
    if not strategy_class:
        self.write_log(_("创建策略失败,找不到策略类{}").format(class_name))     **《==============这里找不到strategy_class**
        return

反复排查之后发现,问题原因是策略文件中引入了一个自定义kline.py(和策略文件在同一目录下)文件:

from kline import TrendFlag, KLineType, KLine

该文件在回测时一直在使用,不存在语法或者编译问题,其中只定义了几个枚举类和K线类,对外部的依赖只有这行:from enum import IntEnum,把 kline.py 的代码复制到策略文件中就好了,暂时没想明白是什么原因。

恳请大佬解惑,也希望能帮到遇到类似问题的朋友们。

Super Moderator
avatar
加入于:
帖子: 118
声望: 13

我来帮你分析这个问题。这涉及到 VeighNa 组合策略模块的策略加载机制。让我先查询相关文档和源代码。

这是一个非常典型且容易踩坑的问题!让我详细解释一下根本原因:

问题根源分析

vnpy_portfoliostrategy 的引擎源代码(engine.py)可以看到策略加载的核心逻辑:

def load_strategy_class_from_module(self, module_name: str) -> None:
    """通过策略文件加载策略类"""
    try:
        module: ModuleType = importlib.import_module(module_name)
        # ... 查找策略类并注册到 self.classes
    except:  # noqa
        msg: str = _("策略文件{}加载失败,触发异常:\n{}").format(module_name, traceback.format_exc())
        self.write_log(msg)

关键问题在这里:

1. 模块导入路径问题

当引擎使用 importlib.import_module() 加载策略文件时,Python 的模块搜索路径(sys.path)决定了能否找到依赖的模块。

你的目录结构大概是:

strategies/
    ├── your_strategy.py    # 策略文件
    └── kline.py            # 自定义模块

在策略文件中你使用了:

from kline import TrendFlag, KLineType, KLine

问题场景:

场景 结果
回测时直接运行策略文件 ✅ 正常,因为当前工作目录包含 strategies/
通过 VeighNa 引擎加载 ❌ 失败,因为 importlib.import_module 的上下文不同

2. 异常被静默捕获

注意代码中的 except: # noqa,这会导致:

  • 导入 kline 模块失败
  • 抛出 ModuleNotFoundErrorImportError
  • 异常被捕获,只输出日志,策略类不会被注册到 self.classes
  • 你看到的只是 "找不到策略类" 的提示,而非真正的导入错误

3. 为什么复制代码能解决问题

kline.py 的内容复制到策略文件中后:

  • 不再需要从外部导入 kline 模块
  • 策略文件可以独立被 importlib 加载
  • 策略类成功注册到 self.classes

解决方案

方案 1:修改导入方式(推荐)

将相对导入改为绝对导入,并确保 strategies 目录在 Python 路径中:

在策略文件顶部添加:

import sys
from pathlib import Path
# 将当前目录添加到 Python 路径
sys.path.insert(0, str(Path(__file__).parent))

from kline import TrendFlag, KLineType, KLine

方案 2:使用包结构

strategies 目录变为 Python 包:

strategies/
    ├── __init__.py         # 空文件,标记为包
    ├── your_strategy.py
    └── kline.py

然后在策略文件中使用相对导入:

from .kline import TrendFlag, KLineType, KLine

注意: 需要修改引擎代码支持从包中加载策略,或者使用 -m 参数运行。

方案 3:将 kline.py 放到 Python 路径中

kline.py 放到 VeighNa 能识别的位置,例如:

  • vnpy_portfoliostrategy/ 目录下
  • 或者创建一个独立的 pip 包

方案 4:动态导入(最灵活)

在策略类内部延迟导入:

class YourStrategy(StrategyTemplate):
    def on_init(self):
        # 在初始化时才导入
        from kline import TrendFlag, KLineType, KLine
        # ...

调试建议

如果你想确认具体的导入错误,可以临时修改引擎代码来查看详细错误信息:

# 在 engine.py 的 load_strategy_class_from_module 方法中
def load_strategy_class_from_module(self, module_name: str) -> None:
    try:
        module: ModuleType = importlib.import_module(module_name)
        # ...
    except Exception as e:
        import traceback
        print(f"=== 策略加载失败: {module_name} ===")
        print(traceback.format_exc())  # 打印完整堆栈
        msg: str = _("策略文件{}加载失败,触发异常:\n{}").format(module_name, traceback.format_exc())
        self.write_log(msg)

总结

问题原因 说明
模块搜索路径 importlib.import_module 的上下文与直接运行不同
异常静默捕获 导入错误被 except: 捕获,只记录日志
策略类未注册 导入失败后,策略类无法被添加到 self.classes

最佳实践建议:

  • 策略文件尽量保持自包含,减少外部依赖
  • 如需共享代码,建议创建独立的工具包并安装到 Python 环境中
  • 或使用 sys.path 动态添加路径的方式

希望这个分析能帮助你理解问题本质!

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】