VeighNa量化社区
你的开源社区量化交易平台
Member
avatar
加入于:
帖子: 420
声望: 173

问题:

用户实现了自己的CTA策略,可能会放在多个合约上跑。用户策略里会声母一系列的策略成员变量,这些策略成员变量应该是每个策略实例是不同的。可是我发现事实不是这样的!——不同的合约竟然共用着一个了的策略成员变量!

策略代码

下面代码保存在用户策略文件夹下的test_strategy.py文件中

from typing import Any,List,Dict,Tuple
import copy

from vnpy.app.cta_strategy import (
    CtaTemplate,
    BarGenerator,
    ArrayManager,
    StopOrder,
    Direction
)

from vnpy.trader.engine import MainEngine,EventEngine
from vnpy.app.cta_strategy.engine import CtaEngine
from vnpy.event.engine import Event

from vnpy.trader.object import (
    LogData,
    TickData,
    BarData,
    TradeData,
    OrderData,
)


class test_strategy(CtaTemplate):
    """"""

    author = "hxxjava"
    kx_interval = 1
    parameters = [
        "kx_interval"
    ]

    kx_count:int = 0
    all_bars:List[BarData] =[]

    variables = ["kx_count"]

    relate_names:List[str] = []

    def __init__(
        self,
        cta_engine: Any,
        strategy_name: str,
        vt_symbol: str,
        setting: dict,
    ):
        super().__init__(cta_engine,strategy_name,vt_symbol,setting)
        self.bg = BarGenerator(self.on_bar,self.kx_interval,self.on_Nmin_bar)
        self.am = ArrayManager()

        self.relate_names.append(vt_symbol)

    def on_init(self):
        """
        Callback when strategy is inited.
        """
        self.write_log("test_strategy 初始化")
        self.load_bar(20)

        self.write_log(f"relate_names={self.relate_names} !!!")

    def on_start(self):
        """ """
        self.write_log(f"test_strategy 已开始 self.kx_interval={self.kx_interval}",)

    def on_stop(self):
        """"""
        self.write_log("test_strategy 已停止")

    def on_tick(self, tick: TickData):
        """
        Callback of new tick data update.
        """
        self.bg.update_tick(tick)

    def on_bar(self, bar: BarData):
        """
        Callback of new bar data update.
        """
        if self.inited:
            self.write_log(f"I got a 1min BarData")
        self.bg.update_bar(bar)

    def on_Nmin_bar(self, bar: BarData):
        """
        Callback of new bar data update.
        """
        self.all_bars.append(bar)
        self.kx_count = len(self.all_bars)

        if self.inited:
            self.write_log(f"I got a {self.kx_interval}min BarData {self.kx_count}")

    def on_trade(self, trade: TradeData):
        """
        Callback of new trade data update.
        """

    def on_order(self, order: OrderData):
        """
        Callback of new order data update.
        """

    def on_stop_order(self, stop_order: StopOrder):
        """
        Callback of stop order update.
        """

观察到的问题:

创建rb2010.SHFE合约的test_strategy的实例,kx_interval为5

description

创建ag2012.SHFE合约的test_strategy的实例,kx_interval为10

description

init函数中的日志输出尽然是这样的

用来存放该策略里20日的 all_bars数组中保存有ag2012.SHFE的所有20日里的所有10分钟K线数据
description

再次初始化test-rb2010策略

用来存放该策略里20日的 all_bars数组中,不只是保存有rb2010的所有5分钟K线数据,也保存有ag2012的10分钟K线数据。

description

问题的原因:

不同的用户策略应该拥有各自不同的成员变量:
all_bars
relate_names
可是从实际的测试结果看,它们却是相同的,这是不应该的!
原因发生在cta_engine里的这个函数中:

    def add_strategy(
        self, class_name: str, strategy_name: str, vt_symbol: str, setting: dict
    ):
        """
        Add a new strategy.
        """
        if strategy_name in self.strategies:
            self.write_log(f"创建策略失败,存在重名{strategy_name}")
            return

        strategy_class = self.classes.get(class_name, None)
        if not strategy_class:
            self.write_log(f"创建策略失败,找不到策略类{class_name}")
            return

        strategy = strategy_class(self, strategy_name, vt_symbol, setting)
        self.strategies[strategy_name] = strategy

        # Add vt_symbol to strategy map.
        strategies = self.symbol_strategy_map[vt_symbol]
        strategies.append(strategy)

        # Update to setting file.
        self.update_strategy_setting(strategy_name, setting)

        self.put_strategy_event(strategy)

这里的的代码可以看出,不同的策略实例使用相同的策略类时,不是立即创建新的策略类实例,而是从self.classes字典中,根据class_name查询得到的。

因此出现了本次测试中出现的,不同的策略实例使用同一个策略类实例,当然它们所引用的列表成员变量也是同一个的情况!

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

mark

Member
avatar
加入于:
帖子: 420
声望: 173

原因已经找到,把类变量变成实例变量就可以了

class test_strategy中的

all_bars
relate_name

都是类变量,它们在所有的子类中都是同一个。所以不同的策略在on_Nmin_bar()函数中的这条语句:

self.all_bars.append(bar)

其实是向同一个类变量all_bars列表中添加bar,所有导致错误!
把它们变成实例变量就可以了,方法是这样的
init()函数中

    self.all_bars:List[BarData] =[]
    self.relate_names:List[str] = []

请看一个例子

class CLanguage :
    name = "xxx"  #类变量
    addr = "http://"  #类变量
    def __init__(self):
        self.name = "C语言中文网"   #实例变量
        self.addr = "http://c.biancheng.net"   #实例变量
    # 下面定义了一个say实例方法
    def say(self):
        self.catalog = 13  #实例变量

def test():
    clang1 = CLanguage()
    #修改 clang 对象的实例变量
    print(clang1.name)
    print(clang1.addr)

    clang1.name = "python教程"
    clang1.addr = "http://c.biancheng.net/python"
    print(clang1.name)
    print(clang1.addr)
    clang2 = CLanguage()
    print(clang2.name)
    print(clang2.addr)
    #输出类变量的值
    print(CLanguage.name)
    print(CLanguage.addr)

if __name__ == "__main__":
    test()

输出结果:

xxx
http://
C语言中文网
http://c.biancheng.net
python教程
http://c.biancheng.net/python
C语言中文网
http://c.biancheng.net
xxx
http://

这对我们正确编写CTA策略非常重要

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

hxxjava wrote:

问题:

用户实现了自己的CTA策略,可能会放在多个合约上跑。用户策略里会声母一系列的策略成员变量,这些策略成员变量应该是每个策略实例是不同的。可是我发现事实不是这样的!——不同的合约竟然共用着一个了的策略成员变量!

策略代码

下面代码保存在用户策略文件夹下的test_strategy.py文件中

from typing import Any,List,Dict,Tuple
import copy

from vnpy.app.cta_strategy import (
    CtaTemplate,
    BarGenerator,
    ArrayManager,
    StopOrder,
    Direction
)

from vnpy.trader.engine import MainEngine,EventEngine
from vnpy.app.cta_strategy.engine import CtaEngine
from vnpy.event.engine import Event

from vnpy.trader.object import (
    LogData,
    TickData,
    BarData,
    TradeData,
    OrderData,
)


class test_strategy(CtaTemplate):
    """"""

    author = "hxxjava"
    kx_interval = 1
    parameters = [
        "kx_interval"
    ]

    kx_count:int = 0
    all_bars:List[BarData] =[]

    variables = ["kx_count"]

    relate_names:List[str] = []

    def __init__(
        self,
        cta_engine: Any,
        strategy_name: str,
        vt_symbol: str,
        setting: dict,
    ):
        super().__init__(cta_engine,strategy_name,vt_symbol,setting)
        self.bg = BarGenerator(self.on_bar,self.kx_interval,self.on_Nmin_bar)
        self.am = ArrayManager()
        
        self.relate_names.append(vt_symbol)

    def on_init(self):
        """
        Callback when strategy is inited.
        """
        self.write_log("test_strategy 初始化")
        self.load_bar(20)

        self.write_log(f"relate_names={self.relate_names} !!!")

    def on_start(self):
        """ """
        self.write_log(f"test_strategy 已开始 self.kx_interval={self.kx_interval}",)
        
    def on_stop(self):
        """"""
        self.write_log("test_strategy 已停止")

    def on_tick(self, tick: TickData):
        """
        Callback of new tick data update.
        """
        self.bg.update_tick(tick)

    def on_bar(self, bar: BarData):
        """
        Callback of new bar data update.
        """
        if self.inited:
            self.write_log(f"I got a 1min BarData")
        self.bg.update_bar(bar)

    def on_Nmin_bar(self, bar: BarData):
        """
        Callback of new bar data update.
        """
        self.all_bars.append(bar)
        self.kx_count = len(self.all_bars)

        if self.inited:
            self.write_log(f"I got a {self.kx_interval}min BarData {self.kx_count}")

    def on_trade(self, trade: TradeData):
        """
        Callback of new trade data update.
        """

    def on_order(self, order: OrderData):
        """
        Callback of new order data update.
        """

    def on_stop_order(self, stop_order: StopOrder):
        """
        Callback of stop order update.
        """

观察到的问题:

创建rb2010.SHFE合约的test_strategy的实例,kx_interval为5

description

创建ag2012.SHFE合约的test_strategy的实例,kx_interval为10

description

init函数中的日志输出尽然是这样的

用来存放该策略里20日的 all_bars数组中保存有ag2012.SHFE的所有20日里的所有10分钟K线数据
description

再次初始化test-rb2010策略

用来存放该策略里20日的 all_bars数组中,不只是保存有rb2010的所有5分钟K线数据,也保存有ag2012的10分钟K线数据。

description

问题的原因:

不同的用户策略应该拥有各自不同的成员变量:
all_bars
relate_names
可是从实际的测试结果看,它们却是相同的,这是不应该的!
原因发生在cta_engine里的这个函数中:

    def add_strategy(
        self, class_name: str, strategy_name: str, vt_symbol: str, setting: dict
    ):
        """
        Add a new strategy.
        """
        if strategy_name in self.strategies:
            self.write_log(f"创建策略失败,存在重名{strategy_name}")
            return

        strategy_class = self.classes.get(class_name, None)
        if not strategy_class:
            self.write_log(f"创建策略失败,找不到策略类{class_name}")
            return

        strategy = strategy_class(self, strategy_name, vt_symbol, setting)
        self.strategies[strategy_name] = strategy

        # Add vt_symbol to strategy map.
        strategies = self.symbol_strategy_map[vt_symbol]
        strategies.append(strategy)

        # Update to setting file.
        self.update_strategy_setting(strategy_name, setting)

        self.put_strategy_event(strategy)

这里的的代码可以看出,不同的策略实例使用相同的策略类时,不是立即创建新的策略类实例,而是从self.classes字典中,根据class_name查询得到的。

因此出现了本次测试中出现的,不同的策略实例使用同一个策略类实例,当然它们所引用的列表成员变量也是同一个的情况!

请教一下,那个设置setting不需要实例化吗

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

这个问题现在应该不存在了吧,直接对单个策略做不同合约的实例化就可以吧。
现在去实现数据还会相互干扰吗?
现在好像没有这个问题,是因为源代码做了修订吗?

Administrator
avatar
加入于:
帖子: 4502
声望: 321

mengrong wrote:

这个问题现在应该不存在了吧,直接对单个策略做不同合约的实例化就可以吧。
现在去实现数据还会相互干扰吗?
现在好像没有这个问题,是因为源代码做了修订吗?

只要不在策略类下定义共享的可变数据对象(比如dict/list/set),而是把这些对象定义放到类的init函数下,就不会有影响。

Member
avatar
加入于:
帖子: 257
声望: 3

用Python的交易员 wrote:

mengrong wrote:

这个问题现在应该不存在了吧,直接对单个策略做不同合约的实例化就可以吧。
现在去实现数据还会相互干扰吗?
现在好像没有这个问题,是因为源代码做了修订吗?

只要不在策略类下定义共享的可变数据对象(比如dict/list/set),而是把这些对象定义放到类的init函数下,就不会有影响。

陈老师,考虑一下把这些代码合并到官方去哈,vnpy粉丝的这个期盼很强烈,论坛一搜索就能反映出来。

社区中就不断出现实时K线图表的需求,加上前段时间的文华事件,开发一套100%完全开源的高性能图表工具,可能也变成了挺有价值的工作。
后续会根据社区用户的反馈情况,来决定是否要进一步增加图表功能,例如:

多周期K线图表
任意时间范围
技术指标
画线分析
等等(图上能玩的太多了~)

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

沪公网安备 31011502017034号

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