5

quantstrat パッケージで、applyRule 関数の遅さの主な原因の 1 つを特定し、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
}
4

1 に答える 1

7

ギャレットの回答は、関連する質問が議論されたR-SIG-Financeリストの最後の主要な議論を示しています。

quantstratのapplyRules関数は、ほとんどの時間が費やされる場所です。

この質問でコピーされたwhileループコードは、applyRules実行のパスに依存する部分です。これらすべてがドキュメントでカバーされていると思いますが、SOの子孫について簡単に確認します。

applyRules内に次元削減インデックスを作成して、すべてのタイムスタンプを監視してチェックする必要がないようにします。戦略がオーダーブックに基づいて行動することが合理的に期待される特定の時点、または注文が履行されることが合理的に期待される特定の時点にのみ注意します。

これは、状態依存およびパス依存のコードです。「ベクトル化」のアイドルトークは、このコンテキストでは意味がありません。市場の現在の状態、注文書、および自分の位置を知る必要があり、注文が他のルールによって時間依存的に変更される可能性がある場合、このコードをどのようにベクトル化できるかわかりません。

コンピュータサイエンスの観点からは、これはステートマシンです。私が考えることができるほとんどすべての言語のステートマシンは、通常、whileループとして記述されます。これは実際には交渉可能でも変更可能でもありません。

質問は、applyの使用が役立つかどうかを尋ねます。Rのapplyステートメントはループとして実装されているため、役に立ちません。mclapplyforeachなどの並列適用でさえ、これはコードの状態に依存する部分の内部にあるため、役に立ちません。状態に関係なく異なる時点を評価することは意味がありません。quantstratの状態に依存しない部分は可能な限りベクトル化されており、実行時間のほとんどを占めていないことに注意してください。

Johnによるコメントは、ruleProcのforループを削除することを提案しています。forループが行うのは、この時点で戦略に関連付けられている各ルールをチェックすることだけです。そのループの唯一の計算集約的な部分は、ルール関数を呼び出すためのdo.callです。forループの残りの部分は、これらの関数の引数を見つけて照合するだけであり、コードプロファイリングからはそれほど時間はかかりません。ルール関数は型順に適用されるため、ここでも並列適用を使用することはあまり意味がありません。そのため、キャンセルまたはリスクディレクティブを新しいエントリディレクティブの前に適用できます。数学に操作の順序がある場合、または銀行に預金/引き出し処理の順序がある場合と同様に、ドキュメントに記載されているように、quantstratにはルールタイプの評価順序があります。

実行を高速化するために実行できる主なことは4つあります。

  1. パスに依存しない戦略を記述します。これはコードによってサポートされており、単純な戦略をこのようにモデル化できます。このモデルでは、塗りつぶしを取得する必要があると思われるときにaddTxnを直接呼び出すカスタムルール関数を記述します。これは、インジケーター/シグナルを操作するベクトル化された関数である可能性があり、非常に高速である必要があります。
  2. 信号の前処理:ステートマシンがオーダーブック/ルール/ポートフォリオの状態を評価して何かを行う必要があるかどうかを確認する必要がある場所が少ない場合、速度の増加は信号の減少とほぼ直線的です。これはほとんどのユーザーが無視している領域であり、ポジションやオーダーブックを変更するアクションが必要になる可能性がある場合の評価を実際には行わないシグナル関数を記述します。
  3. 分析問題の一部を明示的に並列化する:私は通常、明示的に並列ラッパーを記述して、さまざまなパラメーター評価またはシンボル評価を分離します。foreachを使用した例については、applyParameterを参照ください。
  4. C / C ++のapplyRules内のステートマシンを書き直します。パッチは大歓迎ですが、詳細については、Garrettが投稿したリンクを参照してください。

信号生成機能に少し注意を払えば、ほとんどの戦略は、ティックデータのコアごとに1日あたり1シンボルあたり1コア分で実行できることを保証できます。ラップトップで大規模なバックテストを実行することはお勧めしません。

参照:quantstrat-applyRules

于 2011-10-12T12:48:47.627 に答える