2020-05-02 投資組合及量化策略分析R包blotter

blotter用于定義股票交易系統(tǒng)和模擬的工具,包括交易、投資組合和賬戶激活。為多資產(chǎn)類別和多貨幣投資組合提供投資組合支持。作者積極維護(hù)和開發(fā)該軟件包。

參考學(xué)習(xí)資料:

https://github.com/braverock/blotter

安裝

目前還是一個(gè)開發(fā)版本,需要從github安裝,所以要先安裝好devtools.

install.packages("devtools")

然后:

require(devtools)
install_github("braverock/blotter")

如果可以成功運(yùn)行demo files, 則提示已經(jīng)成功安裝了blotter.

library(blotter)
demo('longtrend', ask=FALSE)

運(yùn)行成功有如下信息:

> library(blotter)
Loading required package: xts
Loading required package: zoo

Attaching package: ‘zoo’

The following objects are masked from ‘package:base’:

    as.Date, as.Date.numeric

Registered S3 method overwritten by 'xts':
  method     from
  as.zoo.xts zoo 
Loading required package: FinancialInstrument
Loading required package: quantmod
Loading required package: TTR
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
Version 0.4-0 included new data defaults. See ?getSymbols.
Loading required package: PerformanceAnalytics

Attaching package: ‘PerformanceAnalytics’

The following object is masked from ‘package:graphics’:

    legend

> demo('longtrend', ask=FALSE)


    demo(longtrend)
    ---- ~~~~~~~~~

> # This is a very simple trend following strategy for testing the results of:
> # Faber, Mebane T., "A Quantitative Approach to Tactical Asset Allocation." 
> # Journal of Risk Management (Spring 2007).
> # The article proposes a very simple quantitative market-timing model.  They 
> # test the model in sample on the US stock market since 1900 before testing
> # it out-of-sample in twenty other markets.
> 
> # The article discusses a 200-day simple moving average, which is proposed
> # in Jeremy Seigel's book "Stocks for the Long Run" for timing the DJIA.  He 
> # concludes that a simple market timing strategy improves the absolute and
> # risk adjusted returns over a buy-and-hold strategy.  After all transaction
> # costs are included, the timing strategy falls short on the absolute return,
> # but still provides a better risk-adjusted return.  Siegel also tests timing on  
> # the Nasdaq composite since 1972 and finds better absolute and risk adjusted
> # returns.
> 
> # The article implements a simpler version of the 200-day SMA, opting for a
> # 10-month SMA.  Monthly data is more easily available for long periods of time,
> # and the lower granularity should translate to lower transaction costs.  
> 
> # The rules of the system are relatively simple:
> # - Buy when monthly price > 10-month SMA
> # - Sell and move to cash when monthly price < 10-month SMA
> 
> # 1. All entry and exit prices are on the day of the signal at the close.
> # 2. All data series are total return series including dividends, updated monthly. 
> #    For the purposes of this demo, we only use price returns.
> # 3. Cash returns are estimated with 90-day commercial paper.  Margin rates for
> #    leveraged models are estimated with the broker call rate.  Again, for the
> #    purposes of this demo, we ignore interest and leverage.
> # 4. Taxes, commissions, and slippage are excluded.
> 
> # This simple strategy is different from well-known trend-following systems in
> # three respects.  First, there's no shorting.  Positions are converted to cash on
> # a 'sell' signal, rather than taking a short position. Second, the entire position
> # is put on at trade inception.  No assumptions are made about increasing position
> # size as the trend progresses.  Third, there are no stops.  If the trend reverts
> # quickly, this system will wait for a sell signal before selling the position.
> 
> # Data
> # Instead of using total returns data, this demo uses monthly data for the SP500
> # downloaded from Yahoo Finance.  We'll use about 10 years of data, starting at 
> # the beginning of 1998.
> 
> # Load required libraries
> require(quantmod)

> require(TTR)

> require(blotter)

> Sys.setenv(TZ="UTC")

> # Try to clean up in case the demo was run previously
> try(rm("account.longtrend","portfolio.longtrend",pos=.blotter),silent=TRUE)

> try(rm("ltaccount","ltportfolio","ClosePrice","CurrentDate","equity","GSPC","i","initDate","initEq","Posn","UnitSize","verbose"),silent=TRUE)

> # Set initial values
> initDate='1997-12-31'

> initEq=100000

> # Load data with quantmod
> print("Loading data")
[1] "Loading data"

> currency("USD")
[1] "USD"

> stock("GSPC",currency="USD",multiplier=1)
[1] "GSPC"

> getSymbols('^GSPC', src='yahoo', index.class=c("POSIXt","POSIXct"),from='1998-01-01')
‘getSymbols’ currently uses auto.assign=TRUE by default, but will
use auto.assign=FALSE in 0.5-0. You will still be able to use
‘loadSymbols’ to automatically load data. getOption("getSymbols.env")
and getOption("getSymbols.auto.assign") will still be checked for
alternate defaults.

This message is shown once per session and may be disabled by setting 
options("getSymbols.warning4.0"=FALSE). See ?getSymbols for details.

[1] "^GSPC"

> GSPC=to.monthly(GSPC, indexAt='endof', drop.time=FALSE)

> # Set up indicators with TTR
> print("Setting up indicators")
[1] "Setting up indicators"

> GSPC$SMA10m <- SMA(GSPC[,grep('Adj',colnames(GSPC))], 10)

> # Set up a portfolio object and an account object in blotter
> print("Initializing portfolio and account structure")
[1] "Initializing portfolio and account structure"

> ltportfolio='longtrend'

> ltaccount='longtrend'

> initPortf(ltportfolio,'GSPC', initDate=initDate)
[1] "longtrend"

> initAcct(ltaccount,portfolios='longtrend', initDate=initDate, initEq=initEq)
[1] "longtrend"

> verbose=TRUE

> # Create trades
> for( i in 10:NROW(GSPC) ) { 
+     # browser()
+     CurrentDate=time(GSPC)[i]
+     cat(".")
+     equity = getEndEq(ltaccount, CurrentDate)
+ 
+     ClosePrice = as.numeric(Ad(GSPC[i,]))
+     Posn = getPosQty(ltportfolio, Symbol='GSPC', Date=CurrentDate)
+     UnitSize = as.numeric(trunc(equity/ClosePrice))
+ 
+     # Position Entry (assume fill at close)
+     if( Posn == 0 ) { 
+     # No position, so test to initiate Long position
+         if( as.numeric(Ad(GSPC[i,])) > as.numeric(GSPC[i,'SMA10m']) ) { 
+             cat('\n')
+             # Store trade with blotter
+             addTxn(ltportfolio, Symbol='GSPC', TxnDate=CurrentDate, TxnPrice=ClosePrice, TxnQty = UnitSize , TxnFees=0, verbose=verbose)
+         } 
+     } else {
+     # Have a position, so check exit
+         if( as.numeric(Ad(GSPC[i,]))  <  as.numeric(GSPC[i,'SMA10m'])) { 
+             cat('\n')
+             # Store trade with blotter
+             addTxn(ltportfolio, Symbol='GSPC', TxnDate=CurrentDate, TxnPrice=ClosePrice, TxnQty = -Posn , TxnFees=0, verbose=verbose)
+         } 
+     }
+ 
+     # Calculate P&L and resulting equity with blotter
+     updatePortf(ltportfolio, Dates = CurrentDate)
+     updateAcct(ltaccount, Dates = CurrentDate)
+     updateEndEq(ltaccount, Dates = CurrentDate)
+ } # End dates loop
.
[1] "1998-10-30 00:00:00 GSPC 91 @ 1098.670044"
...........
[1] "1999-09-30 00:00:00 GSPC -91 @ 1282.709961"
.
[1] "1999-10-29 00:00:00 GSPC 85 @ 1362.930054"
...........
[1] "2000-09-29 00:00:00 GSPC -85 @ 1436.51001"
..................
[1] "2002-03-28 00:00:00 GSPC 107 @ 1147.390015"
.
[1] "2002-04-30 00:00:00 GSPC -107 @ 1076.920044"
............
[1] "2003-04-30 00:00:00 GSPC 125 @ 916.919983"
...............
[1] "2004-07-30 00:00:00 GSPC -125 @ 1101.719971"
...
[1] "2004-10-29 00:00:00 GSPC 122 @ 1130.199951"
.....................................
[1] "2007-11-30 00:00:00 GSPC -122 @ 1481.140015"
...................
[1] "2009-06-30 00:00:00 GSPC 197 @ 919.320007"
...........
[1] "2010-05-28 00:00:00 GSPC -197 @ 1089.410034"
..
[1] "2010-07-30 00:00:00 GSPC 195 @ 1101.599976"
.
[1] "2010-08-31 00:00:00 GSPC -195 @ 1049.329956"
.
[1] "2010-09-30 00:00:00 GSPC 179 @ 1141.199951"
...........
[1] "2011-08-31 00:00:00 GSPC -179 @ 1218.890015"
.....
[1] "2012-01-31 00:00:00 GSPC 166 @ 1312.410034"
...........................................
[1] "2015-08-31 00:00:00 GSPC -166 @ 1972.180054"
..
[1] "2015-10-30 00:00:00 GSPC 157 @ 2079.360107"
..
[1] "2015-12-31 00:00:00 GSPC -157 @ 2043.939941"
...
[1] "2016-03-31 00:00:00 GSPC 156 @ 2059.73999"
...............................
[1] "2018-10-31 00:00:00 GSPC -156 @ 2711.73999"
.
[1] "2018-11-30 00:00:00 GSPC 153 @ 2760.169922"
.
[1] "2018-12-31 00:00:00 GSPC -153 @ 2506.850098"
..
[1] "2019-02-28 00:00:00 GSPC 138 @ 2784.48999"
...
[1] "2019-05-31 00:00:00 GSPC -138 @ 2752.060059"
.
[1] "2019-06-28 00:00:00 GSPC 129 @ 2941.76001"
........
[1] "2020-02-28 00:00:00 GSPC -129 @ 2954.219971"
...
> cat('\n')


> # Chart results with quantmod
> chart.Posn(ltportfolio, Symbol = 'GSPC', Dates = '1998::')

> plot(add_SMA(n=10,col='darkgreen', on=1))

> #look at a transaction summary
> getTxns(Portfolio="longtrend", Symbol="GSPC")
           Txn.Qty Txn.Price Txn.Fees  Txn.Value Txn.Avg.Cost Net.Txn.Realized.PL
1997-12-31       0      0.00        0       0.00         0.00               0.000
1998-10-30      91   1098.67        0   99978.97      1098.67               0.000
1999-09-30     -91   1282.71        0 -116726.61      1282.71           16747.632
1999-10-29      85   1362.93        0  115849.05      1362.93               0.000
2000-09-29     -85   1436.51        0 -122103.35      1436.51            6254.296
2002-03-28     107   1147.39        0  122770.73      1147.39               0.000
2002-04-30    -107   1076.92        0 -115230.44      1076.92           -7540.287
2003-04-30     125    916.92        0  114615.00       916.92               0.000
2004-07-30    -125   1101.72        0 -137715.00      1101.72           23099.998
2004-10-29     122   1130.20        0  137884.39      1130.20               0.000
2007-11-30    -122   1481.14        0 -180699.08      1481.14           42814.688
2009-06-30     197    919.32        0  181106.04       919.32               0.000
2010-05-28    -197   1089.41        0 -214613.78      1089.41           33507.735
2010-07-30     195   1101.60        0  214812.00      1101.60               0.000
2010-08-31    -195   1049.33        0 -204619.34      1049.33          -10192.654
2010-09-30     179   1141.20        0  204274.79      1141.20               0.000
2011-08-31    -179   1218.89        0 -218181.31      1218.89           13906.521
2012-01-31     166   1312.41        0  217860.07      1312.41               0.000
2015-08-31    -166   1972.18        0 -327381.89      1972.18          109521.823
2015-10-30     157   2079.36        0  326459.54      2079.36               0.000
2015-12-31    -157   2043.94        0 -320898.57      2043.94           -5560.966
2016-03-31     156   2059.74        0  321319.44      2059.74               0.000
2018-10-31    -156   2711.74        0 -423031.44      2711.74          101712.000
2018-11-30     153   2760.17        0  422306.00      2760.17               0.000
2018-12-31    -153   2506.85        0 -383548.06      2506.85          -38757.933
2019-02-28     138   2784.49        0  384259.62      2784.49               0.000
2019-05-31    -138   2752.06        0 -379784.29      2752.06           -4475.330
2019-06-28     129   2941.76        0  379487.04      2941.76               0.000
2020-02-28    -129   2954.22        0 -381094.38      2954.22            1607.335

> # Copy the results into the local environment
> print("Retrieving resulting portfolio and account")
[1] "Retrieving resulting portfolio and account"

> ltportfolio = getPortfolio("longtrend")

> ltaccount = getAccount("longtrend")

> ###############################################################################
> # Blotter: Tools for transaction-oriented trading systems development
> # for R (see http://r-project.org/) 
> # Copyright (c) 2008 Peter Carl and Brian G. Peterson
> #
> # This library is distributed under the terms of the GNU Public License (GPL)
> # for full details see the file COPYING
> #
> # $Id$
> #
> ###############################################################################
There were 14 warnings (use warnings() to see them)

出圖一張如下所示:


image-20200502202126984.png

依賴包及版本需求

很有意思的一個(gè)設(shè)定:

Sys.setenv(TZ="UTC")設(shè)置時(shí)區(qū),作者考慮的非常全面,之前學(xué)習(xí)Python金融數(shù)據(jù)管包的過程中遇到過這類問題,我當(dāng)時(shí)沒找到這種設(shè)定,只能手動修改日期,太不智能了,估計(jì)也是有方法設(shè)置的,我對Python也不熟,當(dāng)時(shí)也沒找到相應(yīng)的設(shè)置。但是R里的這個(gè)設(shè)置還是讓我很驚喜的。有些教程不能完整復(fù)現(xiàn),有的時(shí)候就是因?yàn)闀r(shí)差的原因,特別是時(shí)間序列數(shù)據(jù)的處理!

示例數(shù)據(jù)解讀

該數(shù)據(jù)發(fā)表于Faber, Mebane T., "A Quantitative Approach to Tactical Asset Allocation." Journal of Risk Management (Spring 2007).

本文討論了在Jeremy Seigel的“長期的股票(Stocks for the Long Run)”一書中提出的200日均線來計(jì)算DJIA的時(shí)間。他的結(jié)論是,與買入并持有策略相比,簡單的市場擇時(shí)策略可以提高絕對收益和風(fēng)險(xiǎn)調(diào)整后的收益。在計(jì)入所有交易成本后,計(jì)時(shí)策略低于絕對收益,但仍提供了更好的風(fēng)險(xiǎn)調(diào)整后收益。Siegel還測試了自1972年以來納斯達(dá)克綜合指數(shù)的時(shí)機(jī),發(fā)現(xiàn)了更好的絕對回報(bào)率和風(fēng)險(xiǎn)調(diào)整后的回報(bào)率。

本文實(shí)現(xiàn)了200天SMA的更簡單版本,選擇了10個(gè)月的SMA。月度數(shù)據(jù)在很長一段時(shí)間內(nèi)更容易獲得,較低的粒度應(yīng)該會轉(zhuǎn)化為較低的交易成本。

該系統(tǒng)的規(guī)則相對簡單:

  • 當(dāng)月價(jià)>10個(gè)月SMA時(shí)買入
  • 當(dāng)月價(jià)<10個(gè)月SMA時(shí)賣出,并轉(zhuǎn)移到現(xiàn)金
    • 1.所有進(jìn)場和出場價(jià)格都是在收盤信號發(fā)出的當(dāng)天。
    • 2.所有數(shù)據(jù)序列均為含股息的總收益序列,按月更新。出于本演示的目的,我們僅使用價(jià)格返還。
    • 3.現(xiàn)金回報(bào)以90天期商業(yè)票據(jù)估算。杠桿模型的保證金費(fèi)率是用經(jīng)紀(jì)人看漲費(fèi)率估算的。同樣,出于本演示的目的,我們忽略了利息和杠桿。
    • 4.不包括稅、傭金和拖期。

這種簡單的策略與知名的趨勢跟蹤系統(tǒng)有三個(gè)不同之處。首先,沒有做空(shorting)。Positions根據(jù)“賣出”信號轉(zhuǎn)換為現(xiàn)金,而不是空頭short position。其次,整個(gè)position是在交易開始時(shí)建立的。沒有假設(shè)隨著趨勢的發(fā)展而增加position。第三,沒有??? stops)。如果趨勢迅速恢復(fù),該系統(tǒng)將等待賣出信號,然后再賣出倉位。

數(shù)據(jù)使用的不是總returns數(shù)據(jù),此演示使用從Yahoo Finance下載的SP500的月度數(shù)據(jù)。我們將使用大約10年的數(shù)據(jù),從1998年初開始。

blotter依賴環(huán)境:

  • R (>= 3.0.0)
  • xts (>= 0.10-0)
  • FinancialInstrument(>= 0.6.3)
  • PerformanceAnalytics
  • quantmod

Imports:

  • zoo
  • TTR
  • graphics
  • methods
  • stats
  • utils
  • boot
  • foreach

Suggests:

  • Hmisc
  • RUnit
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容