Хотя цикл в коде R Quantstrat - как сделать это быстрее? - PullRequest
5 голосов
/ 10 октября 2011

В пакете quantstrat я обнаружил одну из главных причин медлительности функции applyRule и задаюсь вопросом, не эффективнее ли написать цикл while. Любая обратная связь будет полезна. Для всех, кто испытал необходимость обернуть эту часть в Parallel R.

Как вариант применить будет работать, а пока? Или я должен переписать эту часть в новую функцию, такую ​​как 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
}

1 Ответ

7 голосов
/ 12 октября 2011

Ответ Гарретта указывает на последнее серьезное обсуждение в списке R-SIG-Finance, где обсуждался связанный с этим вопрос.

Функция applyRules в quantstrat абсолютно необходима для того, чтобы проводить большую часть времени.

Код цикла while, скопированный в этом вопросе, является зависимой от пути частью выполнения applyRules. Я полагаю, что все это описано в документации, но я кратко расскажу о SO потомстве.

Мы создаем индекс уменьшения размера внутри applyRules, чтобы нам не приходилось наблюдать каждую временную метку и проверять ее. Мы принимаем к сведению только определенные моменты времени, когда разумно ожидать, что стратегия будет действовать в книге заказов, или когда заказы могут обоснованно ожидаться, что они будут выполнены.

Это код состояния и код пути . В этом контексте пустые разговоры о векторизации не имеют никакого смысла. Если мне нужно знать текущее состояние рынка, книгу заказов и мою позицию, и если мои ордера могут изменяться в зависимости от времени с помощью других правил, я не вижу, как этот код может быть векторизован.

С точки зрения информатики, это конечный автомат. Конечные автоматы почти на каждом языке, о котором я могу думать, обычно пишутся как циклы while. Это не подлежит обсуждению или изменению.

Вопрос спрашивает, поможет ли применение apply . операторы apply в R реализованы как циклы, так что нет, это не поможет. Даже параллельное применение, такое как mclapply или foreach , не может помочь, потому что это находится внутри зависимой от состояния части кода. Оценивать разные моменты времени без учета состояния не имеет никакого смысла. Вы заметите, что не зависящие от состояния части quantstrat по возможности векторизованы, и на них приходится очень мало времени работы.

Комментарий, сделанный Джоном, предлагает удалить цикл for в ruleProc . Все, что делает цикл for, проверяет каждое правило, связанное со стратегией, на данный момент. Единственная вычислительная часть этого цикла - do.call для вызова функции правила. Остальная часть цикла for просто находит и сопоставляет аргументы для этих функций, и из профилирования кода совсем не занимает много времени. Здесь также не имеет смысла использовать параллельное применение, поскольку функции правила применяются в порядке типов, так что директивы отмены или риска можно применять перед новыми директивами ввода. Как в математике есть порядок операций, так и в банке есть порядок обработки ввода / вывода, так и в quantstrat есть порядок оценки типа правила, как изложено в документации.

Чтобы ускорить выполнение, можно сделать четыре основных вещи:

  1. написать стратегию, не зависящую от пути : это поддерживается кодом, и простые стратегии могут быть смоделированы таким образом. В этой модели вы написали бы пользовательскую функцию правила, которая вызывает addTxn напрямую, когда вы думаете, что должны получить свои заливки. Это может быть векторизованная функция, работающая с вашими индикаторами / сигналами, и она должна быть очень быстрой.
  2. предварительная обработка ваших сигналов : если есть меньше мест, где конечный автомат должен оценить состояние книги заказов / правил / портфеля, чтобы увидеть, нужно ли ему что-то делать, увеличение скорости почти линейно с уменьшением сигналов. Это та область, которой большинство пользователей пренебрегают, записывая функции сигналов, которые на самом деле не выполняют оценку, когда могут потребоваться действия, которые могли бы изменить позиции или книгу заказов.
  3. явное распараллеливание частей вашей задачи анализа : Я обычно пишу явно параллельные обертки для выделения различных оценок параметров или вычислений символов, см. applyParameter для примера использования foreach
  4. переписать конечный автомат внутри applyRules в C / C ++ : исправления приветствуются, но для получения дополнительной информации см. Ссылку, опубликованную Гарреттом.

Я могу заверить вас, что большинство стратегий могут выполняться за доли такта в минуту на символ в день на ядро ​​в данных тиков, если немного позаботиться о функциях генерации сигналов.Запускать большие тесты на ноутбуке не рекомендуется.

Ссылка: quantstrat - applyRules

...