[R-SIG-Finance] Bug in ruleOrderProc - txnfees is not a numeric?

Gei Lin gmonaie at gmail.com
Wed Dec 10 18:52:34 CET 2014


I ran into this same problem and have a proposed patch. As Ivan proposed,
it appears to be related to the splitting transactions to cross position
values over 0. Any strategy with rules that create orders crossing
positions through 0 will fail on its eventual call to addTxn, even if you
define TxnFees = 0 on the ruleSignal (because it is carried around
internally as.character to support txnFUNs)

It looks like the problem lies in blotter::addTxn rather than
quantstrat::ruleOrderProc. If we move the txnfee evaluation before
splitting the transactions we will have a valid numeric rather than a
character in the '/' operation. I made this change in my dev version and it
looks like it works.

addTxn <- function(Portfolio, Symbol, TxnDate, TxnQty, TxnPrice, ...,
TxnFees=0, allowRebates=FALSE, ConMult=NULL, verbose=TRUE, eps=1e-06)

<snip>

    # FUNCTION
    # Coerce the transaction fees to a function if a string was supplied

    if(is.character(TxnFees)) {
        TF <- try(match.fun(TxnFees), silent=TRUE)
        if (!inherits(TF,"try-error")) TxnFees<-TF
    }
    # Compute transaction fees if a function was supplied
    if (is.function(TxnFees)) {
      txnfees <- TxnFees(TxnQty, TxnPrice, Symbol)
    } else {
      txnfees<- as.numeric(TxnFees)
    }

    if(is.null(txnfees) | is.na(txnfees)) txnfees = 0
    if(txnfees>0 && !isTRUE(allowRebates)) stop('Positive Transaction
Fees should only be used in the case of broker/exchange rebates for
TxnFees ',TxnFees,'. See Documentation.')

addTxn has a block of code that evaluates the transaction fees if a
function was passed in, or casts it as as.numeric. Quantstrat will pass in
argument TxnFees as an xts of characters. Before TxnFees can be evaluated,
an attempt to split the transaction into two pieces is made.

   # split transactions that would cross through zero
    if(PrevPosQty!=0 && sign(PrevPosQty+TxnQty)!=sign(PrevPosQty) &&
PrevPosQty!=-TxnQty){*        txnFeeQty=TxnFees/abs(TxnQty) #
calculate fees pro-rata by quantity*
        addTxn(Portfolio=pname, Symbol=Symbol, TxnDate=TxnDate,
TxnQty=-PrevPosQty, TxnPrice=TxnPrice, ...,
                TxnFees = txnFeeQty*abs(PrevPosQty), ConMult =
ConMult, verbose = verbose, eps=eps)
        TxnDate=TxnDate+2*eps #transactions need unique timestamps, so
increment a bit
        TxnQty=TxnQty+PrevPosQty
        PrevPosQty=0
        TxnFees=txnFeeQty*abs(TxnQty+PrevPosQty)
    }

At this stage TxnFees is still a character. This / operator is what is
creating the error:

Error in `/.default`(TxnFees, abs(TxnQty)) :
  non-numeric argument to binary operator

Switching the order of these two blocks of code and changing

*txnFeeQty=TxnFees/abs(TxnQty)*

To

*txnFeeQty=txnfees/abs(TxnQty)*

should fix it.

2014-01-14 10:45 GMT-05:00 Brian G. Peterson <brian at braverock.com>:

> Ivan,
>
> Thanks for the reproducible example.  We'll investigate.
>
> Brian
>
> On 01/14/2014 09:28 AM, Ivan Popivanov wrote:
>
>> Hi Brian,
>>
>> return(as.numeric(res)) - same thing, I tried it before posting here.
>> There is no problem if I use return(100*sign(orderqty)), which indicates
>> the path dependency I was trying to imply. The problem doesn't seem to
>> occur for long only positions - it gotta be switching. I believe, the
>> problem is outside os.all.equity. Notice, it has a lot of debug output
>> and it clearly indicates that the function returns, and returns a
>> numeric before the problem is hit. For me it's easy to repro it
>> (although it takes longer) with the attached script (it needs some
>> adjustments). The problem is hit for 2001-04-08. I don't touch txnFees
>> anywhere in my code, so I have no control over this behaviour - it needs
>> to be type casted somewhere - from what you are saying - taking the type
>> into account.
>>
>> Thanks for the comments about the testing.
>>
>> Can you point me to code which computes the portfolio size as you
>> suggested?
>>
>>  From my experience, it's often needed to be able to show the strategy
>> performance over years, with all the system on. That's an area quanstrat
>> lacks - both in terms of examples and testing (I am pretty sure this is
>> a regression since the last time I looked at it).
>>
>> Thanks,
>> Ivan
>>
>>
>>
>>
>> On Tue, Jan 14, 2014 at 9:26 AM, Brian G. Peterson <brian at braverock.com
>> <mailto:brian at braverock.com>> wrote:
>>
>>     You don't actually include how you're calculating txnfees, and your
>>     example is not reproducible, so I'm forced to guess.
>>
>>     try
>>
>>       return(as.numeric(res))
>>
>>     in your function.
>>
>>     As noted in the documentation, txnFees can be a string name of a
>>     function, so forcing it to as.numeric is a really bad idea.
>>
>>     I'll also note that you don't need to call updatePortf every time.
>>     That's extremely expensive by comparison to just adding up the
>>     realized P&L (from closed transaction) and Unrealized P&L (from your
>>     open position) yourself.  You should also be aware that this
>>     approach (or yours) will only capture equity changes in a single
>>     instrument (since instruments are processed one at a time).
>>
>>     Most literature on evaluating trading strategies (see e.g. Aronson
>>     or Tomasini and Jaekle) tends to do system design on fixed trade
>>     sizes because it is much easier to evaluate whether or not your
>>     system really has any edge, or is just participating in market
>>     drift.  I tend to separate the order sizing decisions till very late
>>     in the strategy development process.
>>
>>     Regards,
>>
>>     Brian
>>
>>
>>     On 01/13/2014 07:15 PM, Ivan Popivanov wrote:
>>
>>         It only happens when I use a custom function to compute the
>>         position size,
>>         but it won't happen with a constant size. So it may have
>>         something to do
>>         with the splitting of orders. My function uses all available
>> equity
>>         rounding up - it's at the end. I get the following error:
>>
>>         Error in `/.default`(TxnFees, abs(TxnQty)) :
>>             non-numeric argument to binary operator
>>
>>         It seems that the fault is in ruleOrderProc, the caller of addTxn,
>>         whereabouts txnfees is indeed an xts object. To me the fix is to
>>         pass
>>         as.numeric(txnfees) to addTxn.
>>
>>         Regards,
>>         Ivan
>>
>>         PS: Is my osAllEquity a good way to implement the compounding
>>         growth or is
>>         there a better alternative? I think it's worth having an example
>> to
>>         illustrate this functionality.
>>
>>         osAllEquity = function(
>>                 timestamp,
>>                 orderqty,
>>         portfolio.name <http://portfolio.name>,
>>                 symbol,
>>                 ...)
>>         {
>>              verbose = TRUE
>>              # verbose = FALSE
>>
>>              if(verbose) cat("\n=====")
>>              if(verbose) cat(paste( "\n", timestamp, sep=""))
>>
>>              if(verbose) cat(paste( "\n   orderqty=", orderqty, sep=""))
>>              portfolio = updatePortf(Portfolio=portfoli__o.name
>>         <http://portfolio.name>, Dates=paste('::',
>>         as.Date(timestamp), sep=''))
>>              # end.eq = getEndEq(portfolio.name <http://portfolio.name>,
>>         as.Date(timestamp))
>>              # print( end.eq )
>>              pnl = sum(getPortfolio(portfolio.__name
>>         <http://portfolio.name>)$summary$Net.Trading.PL
>>         <http://Net.Trading.PL>)
>>              end.eq = initEq + pnl
>>              if(verbose) cat(paste( "\n   pnl=", pnl, sep=""))
>>              if(verbose) cat(paste( "\n   equity=", end.eq, sep=""))
>>              close.price = as.numeric(Cl(mktdata[__timestamp,]))
>>              if(verbose) cat(paste("\n   close price=", close.price,
>>         sep=""))
>>              res = as.numeric( ceiling( end.eq / close.price ) ) * sign(
>>         orderqty )
>>              if(verbose) cat(paste("\n   qty=", res, sep=""))
>>              if(verbose) cat("\n=====\n")
>>              # return(sign(orderqty*1000))
>>              # res = 1000*sign(orderqty)
>>              return(res)
>>         }
>>
>>                  [[alternative HTML version deleted]]
>>
>>         _________________________________________________
>>         R-SIG-Finance at r-project.org <mailto:R-SIG-Finance at r-project.org>
>>         mailing list
>>         https://stat.ethz.ch/mailman/__listinfo/r-sig-finance
>>         <https://stat.ethz.ch/mailman/listinfo/r-sig-finance>
>>         -- Subscriber-posting only. If you want to post, subscribe first.
>>         -- Also note that this is not the r-help list where general R
>>         questions should go.
>>
>>
>>     --
>>     Brian G. Peterson
>>     http://braverock.com/brian/
>>     Ph: 773-459-4973 <tel:773-459-4973>
>>     IM: bgpbraverock
>>
>>     _________________________________________________
>>     R-SIG-Finance at r-project.org <mailto:R-SIG-Finance at r-project.org>
>>     mailing list
>>     https://stat.ethz.ch/mailman/__listinfo/r-sig-finance
>>     <https://stat.ethz.ch/mailman/listinfo/r-sig-finance>
>>     -- Subscriber-posting only. If you want to post, subscribe first.
>>     -- Also note that this is not the r-help list where general R
>>     questions should go.
>>
>>
>>
>>
>> _______________________________________________
>> R-SIG-Finance at r-project.org mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-sig-finance
>> -- Subscriber-posting only. If you want to post, subscribe first.
>> -- Also note that this is not the r-help list where general R questions
>> should go.
>>
>>
> --
> Brian G. Peterson
> http://braverock.com/brian/
> Ph: 773-459-4973
> IM: bgpbraverock
>
> _______________________________________________
> R-SIG-Finance at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-sig-finance
> -- Subscriber-posting only. If you want to post, subscribe first.
> -- Also note that this is not the r-help list where general R questions
> should go.
>



-- 

“*Don't quit. Suffer now and live the rest of your life as a champion.*”

                                                -
Muhammad Ali*​*
*​*

“*The world is full of obvious things which nobody by any chance observes.*”

                                                -
Sir Arthur Conan Doyle*​*
*​*

	[[alternative HTML version deleted]]



More information about the R-SIG-Finance mailing list