braverock / quantstrat

283 stars 114 forks source link

[R-Forge #1146] MOO/MOC ordertypes? #36

Closed joshuaulrich closed 8 years ago

joshuaulrich commented 8 years ago

Submitted by: Mark Breman Assigned to: Brian Peterson R-Forge link

Hi,

I can't find a way to specify in my strategy that orders should be executed at the Open or at the Close.

The ruleSignal() function has these ordertypes (according to the docs): ordertype: one of 'market','limit','stoplimit', or 'stoptrailing'.

Would it be possible to add Market On Open (MOO) and Market On Close (MOC) ordertypes?

Thanks,

-Mark-

Followups:

Date: 2010-10-26 19:00 Sender: Brian Peterson traceback is:> applyStrategy(strategy=strat, portfolios=portfolio)Error in [.xts(x, , loc) : 'i' or 'j' out of rangeIn addition: Warning message:In applyIndicators(strategy = strategy, mktdata = mktdata, parameters = parameters, : some arguments stored for myindicator do not match> traceback()7: .Call('_do_subset_xts', x, as.integer(i), as.integer(j), drop, PACKAGE = 'xts')6: [.xts(x, , loc)5: x[, loc]4: getPrice(last(mktdata[txntime]), prefer = 'close')3: ruleOrderProc(portfolio = portfolio, symbol = symbol, mktdata = mktdata, timespan = timespan, ...)2: applyRules(portfolio = portfolio, symbol = symbol, strategy = strategy, mktdata = mktdata, Dates = NULL, indicators = sret$indicators, signals = sret$signals, parameters = parameters, ..., path.dep = FALSE)1: applyStrategy(strategy = strat, portfolios = portfolio)definitely a bug, I'll try to stomp it. - Brian

Date: 2010-10-26 18:15 Sender: Brian Peterson I've replicated your error, I'll try to sort it out.Thanks, - Brian

Date: 2010-10-26 14:09 Sender: Mark Breman Hi Brian,As a follow-up, and after re-reading your last explanation I'm beginning to doubt if my modification to orders.R is correct. Is it?Anyway, if I run my testscript without the modification I get an error on applyStrategy():....> strat <- add.rule(strat, name='ruleSignal', arguments = list(sigcol='ind.gt.50', sigval=TRUE, orderqty=1, ordertype='market', orderside='long', pricemethod='market', prefer='Open', delay=86400), type='enter', path.dep=TRUE)> > > > out<-try(applyStrategy(strategy=strat, portfolios=portfolio))Error in [.xts(x, , loc) : 'i' or 'j' out of rangeIn addition: Warning message:In applyIndicators(strategy = strategy, mktdata = mktdata, parameters = parameters, : some arguments stored for myindicator do not match> ...Thought to let you know just in case.-Mark-

Date: 2010-10-26 13:58 Sender: Mark Breman Hi Brian,Ok I made a simplified testscript to reproduce the transactions on non-existing price periods.Before running it you should modify ruleOrderProc() in orders.R as I did to get the price from the order-book instead of always the close price from the pricedata o execution of the order:replace line 423 in orders.R:txnprice=as.numeric(getPrice(last(mktdata[txntime]), prefer='close')) withtxnprice = as.numeric(ordersubset[ii,]$Order.Price) This replacement is correct right?here is the script to run:####################### start script #####################library(blotter)library(quantstrat)try(rm('account.myacct','portfolio.myportf',pos=.blotter),silent=TRUE)try(rm('MYSTOCK','USD',pos=.instrument),silent=TRUE)try(rm('order_book.myportf',pos=.strategy),silent=TRUE)Sys.setenv(TZ='GMT')open = c(23.0, 23.01, 23.02, 23.04, 23.04, 23.05)high = c(23.10, 23.11, 23.12, 23.13, 23.14, 23.15)low = c(22.0, 22.01, 22.02, 22.03, 22.04, 22.05)close = c(22.5, 22.51, 22.52, 22.53, 22.54, 22.55)dates = as.POSIXct(strptime(c('2010-01-01', '2010-01-02', '2010-01-05', '2010-01-06', '2010-01-07', '2010-01-08'), '%Y-%m-%d'))MYSTOCK = xts(data.frame(Open=open, High=high, Low=low, Close=close), dates)myindicator <-function(mktdata) { s = xts(c(F,T,F,F,F,F), dates) colnames(s) = 'myindicator' return(s)}initDate='2009-12-31'currency('USD')stock(primary_id='MYSTOCK', currency='USD', multiplier=1)portfolio='myportf'account='myacct'verbose=TRUEinitPortf(name=portfolio, symbols='MYSTOCK', initDate=initDate)initAcct(name=account, portfolios=portfolio, initDate=initDate, initEq=100)initOrders(portfolio=portfolio, initDate=initDate)strat <- strategy('mystrat')strat <- add.indicator(strategy = strat, name = 'myindicator', arguments = list(x = quote(mktdata)), label='myindicator')strat <- add.signal(strat, name='sigThreshold',arguments = list(threshold=0, column='myindicator' ,relationship='gt', cross=TRUE), label='ind.gt.50')strat <- add.rule(strat, name='ruleSignal', arguments = list(sigcol='ind.gt.50', sigval=TRUE, orderqty=1, ordertype='market', orderside='long', pricemethod='market', prefer='Open', delay=86400), type='enter', path.dep=TRUE)out<-try(applyStrategy(strategy=strat, portfolios=portfolio))#print(getOrderBook(portfolio))updatePortf(Portfolio=portfolio,Dates=paste('::',as.Date(Sys.time()),sep=''))p = getPortfolio(Portfolio=portfolio)p$symbols$MYSTOCK$txnp$symbols$MYSTOCK$posPL################################ end script ###################################The output I get from the last lines is:> p$symbols$MYSTOCK$txn Txn.Qty Txn.Price Txn.Value Txn.Avg.Cost Pos.Qty Pos.Avg.Cost Gross.Txn.Realized.PL Txn.Fees Net.Txn.Realized.PL Con.Mult2009-12-31 0 0.00 0.00 0.00 0 0.00 0 0 0 02010-01-03 1 23.01 23.01 23.01 1 23.01 0 0 0 1> > p$symbols$MYSTOCK$posPL Pos.Qty Con.Mult Ccy.Mult Pos.Value Pos.Avg.Cost Txn.Value Period.Realized.PL Period.Unrealized.PL Gross.Trading.PL Txn.Fees2009-12-31 0 1 1 0.00 0.00 0.00 0 0.00 0.00 02010-01-01 0 1 1 0.00 0.00 0.00 0 0.00 0.00 02010-01-02 0 1 1 0.00 0.00 0.00 0 0.00 0.00 02010-01-03 1 1 1 22.51 23.01 23.01 0 -0.50 -0.50 02010-01-05 1 1 1 22.52 0.00 0.00 0 0.01 0.01 02010-01-06 1 1 1 22.53 0.00 0.00 0 0.01 0.01 02010-01-07 1 1 1 22.54 0.00 0.00 0 0.01 0.01 02010-01-08 1 1 1 22.55 0.00 0.00 0 0.01 0.01 0 Net.Trading.PL2009-12-31 0.002010-01-01 0.002010-01-02 0.002010-01-03 -0.502010-01-05 0.012010-01-06 0.012010-01-07 0.012010-01-08 0.01> As you can see the transaction takes place on 2010-01-03, which is not in the price data.I hope this clarifies the issue.-Mark-

Date: 2010-10-26 12:18 Sender: Brian Peterson Mark, I don't think that specifying the next day's price will take any other code changes. You will specify delay=86400'delay' has been modeled in quantstrat orders since inception. I think it should do what you want. In high frequency data, you are correct that delay models 'processing and transmission latency', but I think that it serves to model what you want as well.Let me try to provide a little more information. A market order with delay will be entered as a 'market' order at the price that is present when the order is entered. This provides information that may be used to model such things as slippage and model accuracy, questions like 'How good are we at detecting turning points?'If you add a one-day delay, and there is no price data (as for weekends and holidays), it will still execute on Monday's open, for example, since that will be the next bar, and you will have an open order. I think adding more options obfuscates the usage more than utilizing already provided options, no?Could you provide an example of 2) ? Execution should happen on the next bar, so this would be a bug. We have seen in illiquid markets with things like gaps that limit orders may be executed at prices 'in the gap', but this is correct, you limit order would have to be passed through for a market to reach a new level. I haven't seen transaction executed in time periods where there are no bars.I'm also a little concerned with options that are explicitly forward-looking. At least in the things I do, almost all orders are sent to the market as limit orders, not market orders, to prevent slippage. Forward looking options break the explicit 'only information available at the time of the decision' nature that quantstrat tries to achieve, and also mess with the ability to enter orders at decision time, potentially in a live trading environment.I am concerned about the possible bug described by your point 2), so let me know please, and we'll see if we can sort it out.Regards, - Brian

Date: 2010-10-26 07:11 Sender: Mark Breman Hi Brian,Thanks for fixing the prefer parameter, I have verified that it's now available in ruleSignal() (in traderules.R).I have also verified that if I replace:txnprice=as.numeric(getPrice(last(mktdata[txntime]), prefer='close'))with txnprice = as.numeric(ordersubset[ii,]$Order.Price) in ruleOrderProc() (orders.R), that it will indeed use the price from the order-book, as expected.Now there is one thing left to fix: How to specify in add.rule() that you want the next day's Open or Close instead of todays.You suggested earlier to use the delay parameter for this (using a one day delay in seconds).I had a good look in the code and did some testing, but I'm afraid this might not be a good idea because:1) it looks like the delay parameter is meant to model the latency of an order traveling over the network (the time it takes for it to get on the order-book after submitting), and not the preferred period of execution. Mixing these two in the same parameter will obfuscate the code I think... 2) adding one day (in seconds) as a delay will only work if the periodicity in the price data is consecutive, but most OHLC data series have gaps (only trading days are present). Testing this gave me some funny results like execution of orders on none trading days (days not present in the price series) etc. I think there is no easy way to fix this.I would suggest three other options for implementing this (in order of my preference I think):1) add another parameter 'period.prefer'. This parameter could hold the values: 'current' and 'next' ('current' would be default). In ruleSignal() (traderules.R) and/or addOrder() (orders.R) we could than test for the 'next' value, and if found, we could select the period and price from the next row in mktdata and add these to the order-book. For instance we could change ruleSignal() (line 74 in traderules.R) from: orderprice <- try(getPrice(x=data, prefer=prefer))[as.character(timestamp)]to if(period.prefer == 'next') orderprice <- try(last(first(getPrice(x=data, prefer=prefer))[paste(as.character(timestamp), '::', sep='')], 2)) else orderprice <- try(getPrice(x=data, prefer=prefer))[as.character(timestamp)]2) allow the prefer parameter to hold two new values: 'next.open' and 'next.close', and test for these in ruleSignal() and/or addOrder()...If they are found the value should also be restored to 'open' or 'close' as it's directly used by getPrice().3) We could also use option 2 and change getPrice() to also accept the new values and return the correct period data. In this case the prefer parameter does not have to be restored.Please let me know what you think.-Mark-

Date: 2010-10-25 14:44 Sender: Brian Peterson Found a bug, 'prefer' still wasn't always being passed. We've explicitly added prefer as an argument to ruleSignal now. r431 always passes it, so nested matching shouldn't be a problem anymore.Regards, - Brian

Date: 2010-10-25 12:56 Sender: Brian Peterson SVN r430 contains argument handling for 'prefer' explicitly lower down in the chain. I also updated documentation so a few things should be a little easier to sort through.r429 also contains performance enhancements from Josh Ulrich, potentially 40% or more.Looking forward to the results of your testing, then we'll close this request.Regards, - Brian

Date: 2010-10-25 11:27 Sender: Brian Peterson Mark, thanks.I'm looking at this now, and hopefully will have it passing through the 'prefer' argument soon. - Brian

Date: 2010-10-21 07:33 Sender: Mark Breman Ok I had a good look at the code after Brian's remark. My conclusions:I think the way to go here would be the 'prefer' parameter to specify 'Open' or 'Close' (the latter is the default) or some other price column which is available in the price data. There is no need for adding an other order type, as they are both 'market' orders.So the add.rule call in the strategy definition would then look like:...strat <- add.rule(strat, name='ruleSignal', arguments = list(sigcol='DVI.gt.50', sigval=TRUE, orderqty=1, ordertype='market', orderside='long', pricemethod='market', prefer='Open', delay=.0001), type='enter', path.dep=TRUE)...This 'prefer' parameter is already handled in the code but it's somewhere lost in all the formal parameter juggling that's taking place. I have verified that it's not set/available in ruleSignal() (in tradeRules.R), while I think it should be because here the order is added to the orderbook (with the actual price to be used). I can't find where the parameter is lost though...ruleOrderProc() (in orders.R) executes the orders. I think there is a mistake in the function when it's handling a market order on a low-frequency time scale (daily, monthly etc): it's not getting the price for the order from the orderbook (as for the other order types), but directly from the mktdata, and always with the close price:...txnprice=as.numeric(getPrice(last(mktdata[txntime]), prefer='close'))...I think this line should be something like:...txnprice = as.numeric(ordersubset[ii,]$Order.Price) ... Please let me know if my findings are correct.-Mark-

Date: 2010-10-14 15:21 Sender: Brian Peterson On daily OHLC data, the current 'market' order is a MOC order if you don't have any delay set in the order entry. This presumes either that you would have your signal before the close, or that you have access to the intraday cross through your broker.I work in intraday/tick/BBO data at the office, so this isn't a high priority for me personally, but we would certainly entertain/apply a patch to ruleOrderProc for daily OHLC data that wqould support MOC/MOO orders. For MOO orders, you might want to add a 1-day delay (86400) from the current timestamp, to force it to the next bar. If you look at ruleOrderProc in the traderules.R file, you'll see the switch statement that I'm talking about.Regards, - Brian

braverock commented 8 years ago

closing, this has been fixed for years.