R quantstrat 代码中的 While 循环 - 如何使其更快?

Posted

技术标签:

【中文标题】R quantstrat 代码中的 While 循环 - 如何使其更快?【英文标题】:While loop in R quantstrat code - how to make it faster? 【发布时间】:2011-12-04 11:50:07 【问题描述】:

在 quantstrat 包中,我找到了导致 applyRule 函数运行缓慢的主要原因之一,并想知道编写 while 循环是否更有效率。任何反馈都会有所帮助。对于任何有将此部分包装到 Parallel R 中的经验的人。

作为一个选项 apply 会起作用吗?或者我应该把这部分重新写成新的函数,比如ruleProc和nextIndex?我也在研究 Rcpp,但这可能是一个问题。非常感谢任何帮助和建设性建议?

   while (curIndex) 
    timestamp = Dates[curIndex]
    if (isTRUE(hold) & holdtill < timestamp) 
        hold = FALSE
        holdtill = NULL
    
    types <- sort(factor(names(strategy$rules), levels = c("pre",
        "risk", "order", "rebalance", "exit", "enter", "entry",
        "post")))
    for (type in types) 
        switch(type, pre = 
            if (length(strategy$rules[[type]]) >= 1) 
              ruleProc(strategy$rules$pre, timestamp = timestamp,
                path.dep = path.dep, mktdata = mktdata, portfolio = portfolio,
                symbol = symbol, ruletype = type, mktinstr = mktinstr)
            
        , risk = 
            if (length(strategy$rules$risk) >= 1) 
              ruleProc(strategy$rules$risk, timestamp = timestamp,
                path.dep = path.dep, mktdata = mktdata, portfolio = portfolio,
                symbol = symbol, ruletype = type, mktinstr = mktinstr)
            
        , order = 
            if (length(strategy$rules[[type]]) >= 1) 
              ruleProc(strategy$rules[[type]], timestamp = timestamp,
                path.dep = path.dep, mktdata = mktdata, portfolio = portfolio,
                symbol = symbol, ruletype = type, mktinstr = mktinstr,)
             else 
              if (isTRUE(path.dep)) 
                timespan <- paste("::", timestamp, sep = "")
               else timespan = NULL
              ruleOrderProc(portfolio = portfolio, symbol = symbol,
                mktdata = mktdata, timespan = timespan)
            
        , rebalance = , exit = , enter = , entry = 
            if (isTRUE(hold)) next()
            if (type == "exit") 
              if (getPosQty(Portfolio = portfolio, Symbol = symbol,
                Date = timestamp) == 0) next()
            
            if (length(strategy$rules[[type]]) >= 1) 
              ruleProc(strategy$rules[[type]], timestamp = timestamp,
                path.dep = path.dep, mktdata = mktdata, portfolio = portfolio,
                symbol = symbol, ruletype = type, mktinstr = mktinstr)
            
            if (isTRUE(path.dep) && length(getOrders(portfolio = portfolio,
              symbol = symbol, status = "open", timespan = timestamp,
              which.i = TRUE))) 
            
        , post = 
            if (length(strategy$rules$post) >= 1) 
              ruleProc(strategy$rules$post, timestamp = timestamp,
                path.dep = path.dep, mktdata = mktdata, portfolio = portfolio,
                symbol = symbol, ruletype = type, mktinstr = mktinstr)
            
        )
    
    if (isTRUE(path.dep))
        curIndex <- nextIndex(curIndex)
    else curIndex = FALSE

【问题讨论】:

我怀疑这个问题是while 所固有的,但无论while 内部发生什么。您的代码似乎没有做任何事情 - 没有赋值或返回值。因此,由于ruleProc 的副作用,您必须运行它。如果副作用之一是您在其他地方分配值,我将首先重构它。根据我的经验,在结果列表中使用 lapplydo.call 通常比使用 [&lt;- 进行选择性分配更快。 如果您发布了每个底层数据结构的玩具示例,那么我认为 for 可以被矢量化。此外,nextIndexruleProc 等函数也需要公开。只有在那之后,才能有人对while 循环做出良好的评估。 好的,刚刚查看了这段代码...尝试先将 ruleProc 向量化。事实上,如果ruleProc 是这个代码非常慢的一个例子,那么你几乎不需要思考就可以轻松实现大幅加速。通过ruleProc 并将所有内容移出包含整个函数的巨大for 循环。很多。如果你不能自己做那部分,然后发布它进行矢量化。但请先行动。 我在下面提供了一个答案,如果 OP 能接受,那就太好了。我还将在这里提供一个评论,这实际上是一个我没有时间测试的假设。我怀疑 applyRules 中的状态机代码可以通过使用 R 2.13.0 引入的 compiler 包显着加快。这可用于对 quantstrat 中的部分/全部 applyRules 函数进行字节编译。 【参考方案1】:

Garrett 的回答确实指向了 R-SIG-Finance 列表上的最后一次主要讨论,其中讨论了相关问题。

quantstrat 中的 applyRules 函数绝对是花费最多时间的地方。

在这个问题中复制的 while 循环代码是 applyRules 执行的路径相关部分。我相信所有这些都包含在文档中,但我会简要回顾一下 SO 后代。

我们在 applyRules 内部构造了一个降维索引,这样我们就不必观察每个时间戳并检查它。我们只记录可以合理预期策略会在订单簿上起作用的特定时间点,或者可以合理预期订单会被执行的特定时间点。

这是状态相关路径相关的代码。在这种情况下,关于“矢量化”的闲谈没有任何意义。如果我需要了解当前的市场状态、订单簿和我的仓位,并且如果我的订单可能会被其他规则以与时间相关的方式修改,我看不出如何对这段代码进行矢量化。

从计算机科学的角度来看,这是一个状态机。我能想到的几乎所有语言的状态机通常都写成 while 循环。这不是真正可以协商或改变的。

问题询问使用 apply 是否会有所帮助。 R 中的 apply 语句被实现为循环,所以不,它没有帮助。即使是像 mclapplyforeach 这样的并行应用也无济于事,因为这是代码的状态相关部分。在不考虑状态的情况下评估不同的时间点没有任何意义。您会注意到 quantstrat 的非状态相关部分已尽可能矢量化,并且只占用很少的运行时间。

John 的评论建议删除 ruleProc 中的 for 循环。 for 循环所做的只是在此时检查与策略相关的每个规则。该循环的唯一计算密集型部分是调用规则函数的 do.call。 for 循环的其余部分只是为这些函数定位和匹配参数,并且从代码分析来看,根本不需要太多时间。在这里使用并行应用也没有多大意义,因为规则函数是按类型顺序应用的,因此可以在新的进入指令之前应用取消或风险指令。就像数学有一个操作顺序,或者银行有一个存款/取款处理顺序一样,quantstrat 也有一个规则类型的评估顺序,如文档中所述。

为了加快执行速度,可以做以下四件事:

    编写一个不依赖路径的策略:这是代码支持的,简单的策略可以这样建模。在此模型中,您将编写一个自定义规则函数,当您认为应该得到满足时直接调用 addTxn。它可以是一个对您的指标/信号进行操作的矢量化函数,并且应该非常快。 预处理您的信号:如果状态机需要评估订单簿/规则/投资组合的状态以查看它是否需要做某事的地方较少,则速度增加几乎与减少信号。这是大多数用户忽略的领域,编写信号函数并不会真正评估何时可能需要修改仓位或订单簿的操作。 显式并行化分析问题的各个部分:我通常编写显式并行包装器来分离不同的参数评估或符号评估,请参阅 applyParameter 以了解使用 foreach 的示例强> 用 C/C++ 重写 applyRules 中的状态机:欢迎使用补丁,但请参阅 Garrett 发布的链接以了解更多详细信息。

我可以向您保证,如果对信号生成功能稍加注意,大多数策略可以在每个核心每天每个交易品种每个核心分钟的几分之一内运行。不建议在笔记本电脑上运行大型回测。

参考:quantstrat - applyRules

【讨论】:

以上是关于R quantstrat 代码中的 While 循环 - 如何使其更快?的主要内容,如果未能解决你的问题,请参考以下文章

Quantitative Trading with R:两个简单的策略

如何安装R语言中的goseq包

while循

如何在 quantstrat 中使用 sigPeak() 函数

quantstrat:尽管信号值似乎有效,但策略不采取立场?

如何在 Python 中的另一个 while 循环中正确地创建一个 while 循环?