Submitted by: Mark Breman
Assigned to: Brian Peterson
R-Forge link
After running the following scenario and calling updateAcct() I get warnings (NA's introduced by coercion) and end up with double starting rows in the portfolio$myportf section of the account:
rm(list=ls(envir=.blotter),envir=.blotter)
rm('portfolio.myportfolio', 'account.myaccount', pos=.blotter)
Warning messages:
1: In rm('portfolio.myportfolio', 'account.myaccount', pos = .blotter) :
object 'portfolio.myportfolio' not found
2: In rm('portfolio.myportfolio', 'account.myaccount', pos = .blotter) :
object 'account.myaccount' not found
Sys.setenv(TZ='GMT')
prices = c(23.07, 23.05, 23.05, 23.09, 23.28, 23.36)
MYSTOCK = xts(prices, as.POSIXct(strptime(paste('2009-01-', seq(1:length(prices)), sep=''),'%Y-%m-%d')))
colnames(MYSTOCK) = 'Close'
initDate='2009-01-01'
currency('USD')
stock(primary_id='MYSTOCK', currency='USD', multiplier=1)
portfolio='myportf'
account='myacct'
initPortf(name=portfolio, symbols='MYSTOCK', initDate=initDate)
[1] 'myportf'
initAcct(name=account, portfolios=portfolio, initDate=initDate, initEq=1000)
[1] 'myacct'
verbose=TRUE
addTxn(Portfolio=portfolio, Symbol='MYSTOCK', TxnDate='2009-01-02', TxnQty=36, TxnPrice=23.05, TxnFees=-1.0)
[1] '2009-01-02 MYSTOCK 36 @ 23.05'
addTxn(Portfolio=portfolio, Symbol='MYSTOCK', TxnDate='2009-01-04', TxnQty=-36, TxnPrice=23.09, TxnFees=-1.0)
[1] '2009-01-04 MYSTOCK -36 @ 23.09'
updatePortf(Portfolio=portfolio, Dates='2009::')
[1] 'myportf'
getPortfolio(portfolio)
$MYSTOCK
$MYSTOCK$txn
Txn.Qty Txn.Price Txn.Fees Txn.Value Txn.Avg.Cost Pos.Qty Pos.Avg.Cost Realized.PL Con.Mult
2009-01-01 0 0.00 0 0.00 0.00000 0 0.00000 0.00 0
2009-01-02 36 23.05 -1 830.80 23.07778 36 23.07778 0.00 1
2009-01-04 -36 23.09 -1 -830.24 23.06222 0 0.00000 -0.56 1
$MYSTOCK$posPL
Pos.Qty Con.Mult Ccy.Mult Pos.Value Txn.Value Txn.Fees Realized.PL Unrealized.PL Trading.PL
2009-01-01 0 1 1 0.0 0.00 0 0.00 0 0.00
2009-01-02 36 1 1 829.8 830.80 -1 0.00 -1 -1.00
2009-01-03 36 1 1 829.8 0.00 0 0.00 0 0.00
2009-01-04 0 1 1 0.0 -830.24 -1 -0.56 1 0.44
2009-01-05 0 1 1 0.0 0.00 0 0.00 0 0.00
2009-01-06 0 1 1 0.0 0.00 0 0.00 0 0.00
attr(,'class')
[1] 'blotter_portfolio' 'portfolio'
2-1.44
[1] 0.56
36*0.04
[1] 1.44
MYSTOCK
Close
2009-01-01 23.07
2009-01-02 23.05
2009-01-03 23.05
2009-01-04 23.09
2009-01-05 23.28
2009-01-06 23.36
getAccount(account)
$TOTAL
Additions Withdrawals Txn.Fees Realized.PL Unrealized.PL Int.Income Trading.PL Advisory.Fees Net.Performance End.Eq
2009-01-01 0 0 0 0 0 0 0 0 0 1000
$portfolio.myportf
Long.Value Short.Value Net.Value Gross.Value Txn.Fees Realized.PL Unrealized.PL Trading.PL
2009-01-01 0 0 0 0 0 0 0 0
attr(,'class')
[1] 'portfolio_account' 'account'
updateAcct(name=account, Dates='2009::')
[1] 'myacct'
There were 12 warnings (use warnings() to see them)
warnings()
Warning messages:
1: In as_numeric(YYYY) : NAs introduced by coercion
2: In as_numeric(YYYY) : NAs introduced by coercion
3: In as_numeric(YYYY) : NAs introduced by coercion
4: In as_numeric(YYYY) : NAs introduced by coercion
5: In as_numeric(YYYY) : NAs introduced by coercion
6: In as_numeric(YYYY) : NAs introduced by coercion
7: In as_numeric(YYYY) : NAs introduced by coercion
8: In as_numeric(YYYY) : NAs introduced by coercion
9: In as_numeric(YYYY) : NAs introduced by coercion
10: In as_numeric(YYYY) : NAs introduced by coercion
11: In as_numeric(YYYY) : NAs introduced by coercion
12: In as_numeric(YYYY) : NAs introduced by coercion
getPortfolio(portfolio)
$MYSTOCK
$MYSTOCK$txn
Txn.Qty Txn.Price Txn.Fees Txn.Value Txn.Avg.Cost Pos.Qty Pos.Avg.Cost Realized.PL Con.Mult
2009-01-01 0 0.00 0 0.00 0.00000 0 0.00000 0.00 0
2009-01-02 36 23.05 -1 830.80 23.07778 36 23.07778 0.00 1
2009-01-04 -36 23.09 -1 -830.24 23.06222 0 0.00000 -0.56 1
$MYSTOCK$posPL
Pos.Qty Con.Mult Ccy.Mult Pos.Value Txn.Value Txn.Fees Realized.PL Unrealized.PL Trading.PL
2009-01-01 0 1 1 0.0 0.00 0 0.00 0 0.00
2009-01-02 36 1 1 829.8 830.80 -1 0.00 -1 -1.00
2009-01-03 36 1 1 829.8 0.00 0 0.00 0 0.00
2009-01-04 0 1 1 0.0 -830.24 -1 -0.56 1 0.44
2009-01-05 0 1 1 0.0 0.00 0 0.00 0 0.00
2009-01-06 0 1 1 0.0 0.00 0 0.00 0 0.00
attr(,'class')
[1] 'blotter_portfolio' 'portfolio'
getAccount(account)
$TOTAL
Additions Withdrawals Txn.Fees Realized.PL Unrealized.PL Int.Income Trading.PL Advisory.Fees Net.Performance End.Eq
2009-01-01 0 0 0 0.00 0 0 0.00 0 0 1000
2009-01-01 0 0 0 0.00 0 0 0.00 0 0 0
2009-01-01 0 0 0 0.00 0 0 0.00 0 0 0
2009-01-02 0 0 -1 0.00 -1 0 -1.00 0 0 0
2009-01-03 0 0 0 0.00 0 0 0.00 0 0 0
2009-01-04 0 0 -1 -0.56 1 0 0.44 0 0 0
2009-01-05 0 0 0 0.00 0 0 0.00 0 0 0
2009-01-06 0 0 0 0.00 0 0 0.00 0 0 0
$portfolio.myportf
Long.Value Short.Value Net.Value Gross.Value Txn.Fees Realized.PL Unrealized.PL Trading.PL
2009-01-01 0.0 0 0.0 0.0 0 0.00 0 0.00 <<=============== FIRST ROW
2009-01-01 0.0 0 0.0 0.0 0 0.00 0 0.00 <<=============== DOUBLE ROW!!
2009-01-02 829.8 0 829.8 829.8 -1 0.00 -1 -1.00
2009-01-03 829.8 0 829.8 829.8 0 0.00 0 0.00
2009-01-04 0.0 0 0.0 0.0 -1 -0.56 1 0.44
2009-01-05 0.0 0 0.0 0.0 0 0.00 0 0.00
2009-01-06 0.0 0 0.0 0.0 0 0.00 0 0.00
attr(,'class')
[1] 'portfolio_account' 'account'
Also note the triple starting row in the $TOTAL section (already reported as another bug with ID: 831)
Followups:
Date: 2010-10-07 16:57
Sender: Brian Peterson
Resolved. Closed. Fixed. Account will not include double rows anymore. - Brian
Date: 2010-08-17 21:01
Sender: Brian Peterson
This has been fixed in SVN rev 374 for updatePortf.UpdateAcct still to come.
Date: 2010-03-04 03:40
Sender: Peter Carl
I've picked Brian's comments about his proposed solution out of the comments below and added them to Feature Request 289.Although I think we should fix the problem (at some point soon), I'll propose that we insert a stop() for now.Does that seem like a reasonable temporary approach?
Date: 2010-03-03 18:36
Sender: Mark Breman
Brian,Just a remark about your message regarding the initDate should be a date prior to the first close price given.If I retry my test scenario with an initDate set to a date prior to the first price date, the problems I reported persist:> rm(list=ls())> rm(list=ls(envir=.blotter),envir=.blotter)> rm('portfolio.myportfolio', 'account.myaccount', pos=.blotter)Warning messages:1: In rm('portfolio.myportfolio', 'account.myaccount', pos = .blotter) : object 'portfolio.myportfolio' not found2: In rm('portfolio.myportfolio', 'account.myaccount', pos = .blotter) : object 'account.myaccount' not found> > Sys.setenv(TZ='GMT')> > prices = c(23.07, 23.05, 23.05, 23.09, 23.28, 23.36)> MYSTOCK = xts(prices, as.POSIXct(strptime(paste('2009-01-', seq(1:length(prices)), sep=''),'%Y-%m-%d')))> colnames(MYSTOCK) = 'Close'> > initDate='2008-12-31'> currency('USD')> stock(primary_id='MYSTOCK', currency='USD', multiplier=1)> > portfolio='myportf'> account='myacct'> > initPortf(name=portfolio, symbols='MYSTOCK', initDate=initDate)[1] 'myportf'> initAcct(name=account, portfolios=portfolio, initDate=initDate, initEq=1000)[1] 'myacct'> > verbose=TRUE> > addTxn(Portfolio=portfolio, Symbol='MYSTOCK', TxnDate='2009-01-02', TxnQty=36, TxnPrice=23.05, TxnFees=-1.0)[1] '2009-01-02 MYSTOCK 36 @ 23.05'> addTxn(Portfolio=portfolio, Symbol='MYSTOCK', TxnDate='2009-01-04', TxnQty=-36, TxnPrice=23.09, TxnFees=-1.0)[1] '2009-01-04 MYSTOCK -36 @ 23.09'> > updatePortf(Portfolio=portfolio, Dates='2009::')[1] 'myportf'> > #CurrentSpan='2010-01-05::2010-03-01'> #updateAcct(name=account, Dates='2009-01-01:2009-01-06')> updateAcct(name=account, Dates='2009::')[1] 'myacct'There were 12 warnings (use warnings() to see them)> > #updateEndEq(Account=account)> > #getTxns(Portfolio=portfolio, Symbol='PLW', Date='2010::')> > getPortfolio(Portfolio=portfolio)$MYSTOCK$MYSTOCK$txn Txn.Qty Txn.Price Txn.Fees Txn.Value Txn.Avg.Cost Pos.Qty Pos.Avg.Cost Realized.PL Con.Mult2008-12-31 0 0.00 0 0.00 0.00000 0 0.00000 0.00 02009-01-02 36 23.05 -1 830.80 23.07778 36 23.07778 0.00 12009-01-04 -36 23.09 -1 -830.24 23.06222 0 0.00000 -0.56 1$MYSTOCK$posPL Pos.Qty Con.Mult Ccy.Mult Pos.Value Txn.Value Txn.Fees Realized.PL Unrealized.PL Trading.PL2008-12-31 0 1 1 0.0 0.00 0 0.00 0 0.002009-01-02 36 1 1 829.8 830.80 -1 0.00 -1 -1.002009-01-03 36 1 1 829.8 0.00 0 0.00 0 0.002009-01-04 0 1 1 0.0 -830.24 -1 -0.56 1 0.442009-01-05 0 1 1 0.0 0.00 0 0.00 0 0.002009-01-06 0 1 1 0.0 0.00 0 0.00 0 0.00attr(,'class')[1] 'blotter_portfolio' 'portfolio' > getAccount(Account=account)$TOTAL Additions Withdrawals Txn.Fees Realized.PL Unrealized.PL Int.Income Trading.PL Advisory.Fees Net.Performance End.Eq2008-12-31 0 0 0 0.00 0 0 0.00 0 0 10002008-12-31 0 0 0 0.00 0 0 0.00 0 0 02008-12-31 0 0 0 0.00 0 0 0.00 0 0 02009-01-02 0 0 -1 0.00 -1 0 -1.00 0 0 02009-01-03 0 0 0 0.00 0 0 0.00 0 0 02009-01-04 0 0 -1 -0.56 1 0 0.44 0 0 02009-01-05 0 0 0 0.00 0 0 0.00 0 0 02009-01-06 0 0 0 0.00 0 0 0.00 0 0 0$portfolio.myportf Long.Value Short.Value Net.Value Gross.Value Txn.Fees Realized.PL Unrealized.PL Trading.PL2008-12-31 0.0 0 0.0 0.0 0 0.00 0 0.002008-12-31 0.0 0 0.0 0.0 0 0.00 0 0.002009-01-02 829.8 0 829.8 829.8 -1 0.00 -1 -1.002009-01-03 829.8 0 829.8 829.8 0 0.00 0 0.002009-01-04 0.0 0 0.0 0.0 -1 -0.56 1 0.442009-01-05 0.0 0 0.0 0.0 0 0.00 0 0.002009-01-06 0.0 0 0.0 0.0 0 0.00 0 0.00attr(,'class')[1] 'portfolio_account' 'account' > Regarding your remarks about handling out-of-order transactions, multiple calls to update* functions etc. I think that if you do accept the parameters of a user you should always produce a correct result or die with an (clear) error. Recalculation of the entire portfolio/account should be possible at all times I think.Is it not possible to sort the user provided transactions according to date before storing them in the blotter administration? I'm not sure about this at all and have to think about it a bit.Regards,-Mark-
Date: 2010-03-03 15:30
Sender: Brian Peterson
The issue in the transactions is that Pos.Qty Pos.Avg.Cost Realized.PLare all calculated, and path dependent.So if things come in out of order, everything later is wrong.I agree that if we decide to throw a stop() error it should be with a clear error. The question is whether we should put in the stop() error for now, or try to fix the problem.Clearly the initial assumption was overly restrictive. Now we need to deal with it. - Brian
Date: 2010-03-03 15:24
Sender: Mark Breman
Brian,Just a remark about your message regarding the initDate should be a date prior to the first close price given.If I retry my test scenario with an initDate set to a date prior to the first price date, the problems I reported persist:> rm(list=ls())> rm(list=ls(envir=.blotter),envir=.blotter)> rm('portfolio.myportfolio', 'account.myaccount', pos=.blotter)Warning messages:1: In rm('portfolio.myportfolio', 'account.myaccount', pos = .blotter) : object 'portfolio.myportfolio' not found2: In rm('portfolio.myportfolio', 'account.myaccount', pos = .blotter) : object 'account.myaccount' not found> > Sys.setenv(TZ='GMT')> > prices = c(23.07, 23.05, 23.05, 23.09, 23.28, 23.36)> MYSTOCK = xts(prices, as.POSIXct(strptime(paste('2009-01-', seq(1:length(prices)), sep=''),'%Y-%m-%d')))> colnames(MYSTOCK) = 'Close'> > initDate='2008-12-31'> currency('USD')> stock(primary_id='MYSTOCK', currency='USD', multiplier=1)> > portfolio='myportf'> account='myacct'> > initPortf(name=portfolio, symbols='MYSTOCK', initDate=initDate)[1] 'myportf'> initAcct(name=account, portfolios=portfolio, initDate=initDate, initEq=1000)[1] 'myacct'> > verbose=TRUE> > addTxn(Portfolio=portfolio, Symbol='MYSTOCK', TxnDate='2009-01-02', TxnQty=36, TxnPrice=23.05, TxnFees=-1.0)[1] '2009-01-02 MYSTOCK 36 @ 23.05'> addTxn(Portfolio=portfolio, Symbol='MYSTOCK', TxnDate='2009-01-04', TxnQty=-36, TxnPrice=23.09, TxnFees=-1.0)[1] '2009-01-04 MYSTOCK -36 @ 23.09'> > updatePortf(Portfolio=portfolio, Dates='2009::')[1] 'myportf'> > #CurrentSpan='2010-01-05::2010-03-01'> #updateAcct(name=account, Dates='2009-01-01:2009-01-06')> updateAcct(name=account, Dates='2009::')[1] 'myacct'There were 12 warnings (use warnings() to see them)> > #updateEndEq(Account=account)> > #getTxns(Portfolio=portfolio, Symbol='PLW', Date='2010::')> > getPortfolio(Portfolio=portfolio)$MYSTOCK$MYSTOCK$txn Txn.Qty Txn.Price Txn.Fees Txn.Value Txn.Avg.Cost Pos.Qty Pos.Avg.Cost Realized.PL Con.Mult2008-12-31 0 0.00 0 0.00 0.00000 0 0.00000 0.00 02009-01-02 36 23.05 -1 830.80 23.07778 36 23.07778 0.00 12009-01-04 -36 23.09 -1 -830.24 23.06222 0 0.00000 -0.56 1$MYSTOCK$posPL Pos.Qty Con.Mult Ccy.Mult Pos.Value Txn.Value Txn.Fees Realized.PL Unrealized.PL Trading.PL2008-12-31 0 1 1 0.0 0.00 0 0.00 0 0.002009-01-02 36 1 1 829.8 830.80 -1 0.00 -1 -1.002009-01-03 36 1 1 829.8 0.00 0 0.00 0 0.002009-01-04 0 1 1 0.0 -830.24 -1 -0.56 1 0.442009-01-05 0 1 1 0.0 0.00 0 0.00 0 0.002009-01-06 0 1 1 0.0 0.00 0 0.00 0 0.00attr(,'class')[1] 'blotter_portfolio' 'portfolio' > getAccount(Account=account)$TOTAL Additions Withdrawals Txn.Fees Realized.PL Unrealized.PL Int.Income Trading.PL Advisory.Fees Net.Performance End.Eq2008-12-31 0 0 0 0.00 0 0 0.00 0 0 10002008-12-31 0 0 0 0.00 0 0 0.00 0 0 02008-12-31 0 0 0 0.00 0 0 0.00 0 0 02009-01-02 0 0 -1 0.00 -1 0 -1.00 0 0 02009-01-03 0 0 0 0.00 0 0 0.00 0 0 02009-01-04 0 0 -1 -0.56 1 0 0.44 0 0 02009-01-05 0 0 0 0.00 0 0 0.00 0 0 02009-01-06 0 0 0 0.00 0 0 0.00 0 0 0$portfolio.myportf Long.Value Short.Value Net.Value Gross.Value Txn.Fees Realized.PL Unrealized.PL Trading.PL2008-12-31 0.0 0 0.0 0.0 0 0.00 0 0.002008-12-31 0.0 0 0.0 0.0 0 0.00 0 0.002009-01-02 829.8 0 829.8 829.8 -1 0.00 -1 -1.002009-01-03 829.8 0 829.8 829.8 0 0.00 0 0.002009-01-04 0.0 0 0.0 0.0 -1 -0.56 1 0.442009-01-05 0.0 0 0.0 0.0 0 0.00 0 0.002009-01-06 0.0 0 0.0 0.0 0 0.00 0 0.00attr(,'class')[1] 'portfolio_account' 'account' > Regarding your remarks about handling out-of-order transactions, multiple calls to update* functions etc. I think that if you do accept the parameters of a user you should always produce a correct result or die with an (clear) error. Recalculation of the entire portfolio/account should be possible at all times I think.Is it not possible to sort the user provided transactions according to date before storing them in the blotter administration? I'm not sure about this at all and have to think about it a bit.Regards,-Mark-
Date: 2010-03-03 14:32
Sender: Brian Peterson
one of the design assumptions we made to get blotter off the ground was that we weren't going to support things happening 'out of order'As a 'trade blotter', the design assumption went, the user will have control over where and how it is run, and will run things in linear time without overlaps.The documentation supports this assumption, stating that initDate should be a date prior to the first close price given.Now, all that aside, users will run update* more than once on the same date. What do we do about it?Today, we 'rbind' the new calculations to the time series, which can result in double and triple rows, and definitely results in incorrect calculations.I think we have a few options. - die with an error if you've already calc'd over those timestamps- throw out any rows after currentDate and recalc- insert rows on currentDate by index, overwriting previously calculated rows- apply adjustments to bring all future rows into complianceIn an accounting system, the last option (adjustments) would be correct. 'blotter' is not a portfolio accounting stystem, so I think we can toss that option for portfolio and account updates. However, I think we do need to apply adjustments, of a sort, to transactions. I'll come to that in a bit.If we wanted to hold to our design assumption, we'd pick the first option, and insert a stop() error and make the user sort it out. I don't think anyone would prefer this option.That leaves the second and third options.For updatePortf and LupdateAcct, I think we should go with the second option. eave any rows prior to currentDate, throw out any later rows, and calc over the Dates range sent to the update* function. This should be minimal code to add, and will work.The third option, to insert rows directly, is very fragile if the index of updates changes, so I'd rather not go down that path, though it is also only a small code change.For transactions, I don't think the solution is nearly so clear. If a transaction comes in out of order, I'd be inclined for now to try to trap that (if txnDate is before last() txnDate) abd throw a stop error. If we want to handle them, then I think we'd need to retrieve all transactions after the current txnDate (using getTxns with a date range), strip the calculated values, leaving only the reported values (time, qty, price, fees), and remove those rows from the table. Then you'd insert the new transaction as normal, and insert the other out of order transactions using addTxns(). You'd also need to clear out or recalc any portfolio or account updates after txnDate, since they're now wrong.We need to be very careful in balancing robustness against speed. The more we recalc, the longer it takes. all of the add* functions are currently quite fast, but the update* functions are slow (painfully so in some cases). Supporting out or order and duplicate updates may exacerbate these performance issues. - Brian