Quantstratを使う2 〜 簡単な実装
前回「Quantstratを使う1 〜 インストール」ではインストールまで終わったので、簡単な実装をしてみる。
■トレード戦略
・シンボルデータ
-GBPUSD
-30分足のOHLCデータ
・2つのSMAのデッドクロス/ゴールデンクロスを使用
-SMA10
-SMA30
・エントリーロジック
-買い:stop-limit at high of SMA crossing bar
-売り:stop-limit at low of SMA crossing bar
※)先に読み進めないと意味が分からん。
■FinancialInstrumentを使う
・通貨の決定
次のようにする。
> require('FinancialInstrument') > currency(c('GBP','USD')) [1] "GBP" "USD"
もしくは、次のようにする。
> exchange_rate(primary_id='GBPUSD', tick_size=0.0001) [1] "GBPUSD"
あるいは、次のようにしてもよい。
> exchange_rate(currency='USD', counter_currency='GBP', tick_size=0.0001) [1] "GBPUSD"
環境を保存するにはsaveInstruments()を使用し、ロードするにはloadInstruments()を使用する。
そうすると、ワーキングディレクトリに「MyInstruments.RData」というファイルができる。
> saveInstruments() > loadInstruments()
■symbolデータをyahooから取得してrdaファイルに格納する
元ネタのPDFでは、イキナリ下記のようなコードがあり、データを取得できるように読めたがこれはdata.dirで指定するディレクトリにGBPUSDのrdaデータが保存されていることが前提。
> data.dir<-'D:\\100_work\\data' > .from<-'2002-10-21' > .to<-'2008-07-04' > getSymbols.FI(Symbols='GBPUSD', dir=data.dir, from=.from, to=.to)
ここでは最初にrdaファイルへ株価データを保存する方法を記すため、GBPJPYデータはやめて、SPYデータをyahooから取得してrdaファイルに保存する。
# 株価の取得 > getSymbols("SPY", src='yahoo') [1] "SPY" # 取得した株価の確認 > head(SPY) SPY.Open SPY.High SPY.Low SPY.Close SPY.Volume SPY.Adjusted 2007-01-03 142.25 142.86 140.57 141.37 94807600 117.2748 2007-01-04 141.23 142.05 140.61 141.67 69620600 117.5237 2007-01-05 141.33 141.40 140.38 140.54 76645300 116.5863 2007-01-08 140.82 141.41 140.25 141.19 71655000 117.1255 2007-01-09 141.31 141.60 140.40 141.07 75680100 117.0260 2007-01-10 140.58 141.57 140.30 141.54 72428000 117.4159 # ワーキングディレクトリにデータファイル(.rda)格納用のディレクトリを作成する > dir.create("tmpdata") # SPY.rdaという名前でSPYオブジェクトをtmpdataファルダへ保存する > saveSymbols.common("SPY", base_dir="tmpdata")
これでファイルへ保存できたが、今度は読み込んで見る。
> head(SPY) SPY.Open SPY.High SPY.Low SPY.Close SPY.Volume SPY.Adjusted 2007-01-03 142.25 142.86 140.57 141.37 94807600 117.2748 2007-01-04 141.23 142.05 140.61 141.67 69620600 117.5237 2007-01-05 141.33 141.40 140.38 140.54 76645300 116.5863 2007-01-08 140.82 141.41 140.25 141.19 71655000 117.1255 2007-01-09 141.31 141.60 140.40 141.07 75680100 117.0260 2007-01-10 140.58 141.57 140.30 141.54 72428000 117.4159 # SPYオブジェクトを削除 > rm(SPY) > head(SPY) 以下にエラー head(SPY) : オブジェクト 'SPY' がありません # SPY.rdaファイルをtmpdataから読み込みSPYオブジェクトへ格納する > getSymbols("SPY", src='FI', dir="tmpdata", split_method='common') [1] "SPY" > head(SPY) SPY.Open SPY.High SPY.Low SPY.Close SPY.Volume SPY.Adjusted 2007-01-03 142.25 142.86 140.57 141.37 94807600 117.2748 2007-01-04 141.23 142.05 140.61 141.67 69620600 117.5237 2007-01-05 141.33 141.40 140.38 140.54 76645300 116.5863 2007-01-08 140.82 141.41 140.25 141.19 71655000 117.1255 2007-01-09 141.31 141.60 140.40 141.07 75680100 117.0260 2007-01-10 140.58 141.57 140.30 141.54 72428000 117.4159
さて、同様にoandaからGBPUSDを取得してみようとしたがエラーとなりうまくいかない。yahooからも取得できない。
> getSymbols('GBP/USD', src='oanda') In download.file(paste(oanda.URL, from.date, to.date, "exch=", currency.pair[1], : 開けません: HTTP ステータスは '404 Not Found' です > getSymbols('GBP/USD', src='yahoo') In download.file(paste(yahoo.URL, "s=", Symbols.name, "&a=", from.m, : 開けません: HTTP ステータスは '404 Not Found' です
そのため、TOPIXのデータを取得してGBPUSDの代わりに使用する。
> getSymbols('YJ998405', src='yahooj') [1] "YJ998405" > head( YJ998405.T) YJ998405.T.Open YJ998405.T.High YJ998405.T.Low YJ998405.T.Close 2007-01-04 1692.94 1701.45 1692.21 1698.95 2007-01-05 1697.06 1697.15 1670.35 1675.33 2007-01-09 1672.68 1696.02 1670.24 1692.12 2007-01-10 1690.59 1690.98 1657.44 1663.00 2007-01-11 1668.57 1675.94 1650.82 1656.72 2007-01-12 1670.33 1692.03 1667.65 1685.27
列名を変更して、TOPIXというオブジェクトに格納する。
※)便宜上なので、必須ではない。
> TOPIX<-YJ998405.T > head(TOPIX) YJ998405.T.Open YJ998405.T.High YJ998405.T.Low YJ998405.T.Close 2007-01-04 1692.94 1701.45 1692.21 1698.95 2007-01-05 1697.06 1697.15 1670.35 1675.33 2007-01-09 1672.68 1696.02 1670.24 1692.12 2007-01-10 1690.59 1690.98 1657.44 1663.00 2007-01-11 1668.57 1675.94 1650.82 1656.72 2007-01-12 1670.33 1692.03 1667.65 1685.27 > names(TOPIX)<-c("Open","High","Low","Close") > head(TOPIX) Open High Low Close 2007-01-04 1692.94 1701.45 1692.21 1698.95 2007-01-05 1697.06 1697.15 1670.35 1675.33 2007-01-09 1672.68 1696.02 1670.24 1692.12 2007-01-10 1690.59 1690.98 1657.44 1663.00 2007-01-11 1668.57 1675.94 1650.82 1656.72 2007-01-12 1670.33 1692.03 1667.65 1685.27 # RDAファイルに保存する > saveSymbols.common("TOPIX", base_dir="tmpdata")
作成したTOPIXのRDAファイルを読み込む。
# 設定 > data.dir<-'D:\\100_work\\data' > .from<-'2007-01-04' > .to<-'2016-01-12' # データ取得 > getSymbols.FI(Symbols='TOPIX', dir=data.dir, from=.from, to=.to) Reading 2016.01.11.TOPIX.rda ... failed. File not found in D:\100_work\data\tmpdata/TOPIX ... skipping Reading 2016.01.12.TOPIX.rda ... failed. File not found in D:\100_work\data\tmpdata/TOPIX ... skipping rbinding data ... done. NULL 警告メッセージ: In getSymbols.FI(Symbols = "TOPIX", dir = data.dir, from = .from, : No data found.
取得に失敗。次の方法で取得。
> getSymbols("TOPIX", src='FI', dir="tmpdata", split_method='common') [1] "TOPIX"
■戦略用オブジェクトの作成
> strategy(name='tes', store=TRUE)
■Indicators
●quantstrat::add.indicator()
add.indicator( strategy = 'tes', name = 'SMA', arguments = list(x = quote(Cl(mktdata)[,1]),n = 10), label="nFast" ) [1] "tes" add.indicator( strategy = 'tes', name = 'SMA', arguments = list(x = quote(Cl(mktdata)[,1]),n = 30), label="nSlow" ) [1] "tes"
■Signals
●quantstrat::add.signal()
add.signal( strategy='tes', name='sigCrossover', arguments = list(columns=c("nFast", "nSlow"),relationship="gt"), label='long' ) [1] "tes" add.signal( strategy='tes', name='sigCrossover', arguments = list( columns=c("nFast", "nSlow"),relationship="lt"), label='short' )
■Rules
●quantstrat::add.rules()
ここは使い方が最初良くわからなかったが、具体的には下記のようにすれば良いようだ。
第1引数には、strategyもしくはstrategyの名前を指定する。
第2引数には、使用する関数を指定する。今回はruleSignalという関数を使用している。
第3引数には、使用する関数の引数をリスト形式で指定する。
第4引数には、ルールの型を指定する。「enter」、「exit」、「risk」などがある。
第5引数は、オーダーリストに表示されるラベルを指定する。
>add.rule(strategy='tes', name='ruleSignal', arguments=list( sigcol='long' , sigval=TRUE, orderside='long' , ordertype='stoplimit', prefer='High', threshold=0.0005, orderqty=+100000, replace=FALSE ), type='enter', label='EnterLONG' ) [1] "tes"
add.ruleは一般的に次のような構造をしている。
functionには、ruleSignal()を使うのが普通。もしもruleSignal()を使わない場合は、同様の引数を使用する関数を使う。
add.rule(
strategy,
function to apply,
list of arguments for function to apply
type,
label
)
ruleSignal()は次のような構造を持つ。
ordertypeには、limit、stoplimit、stoptrading、market、icebergを指定できる。
ruleSignal( sigcol='long', sigval=TRUE, orderside='long', ordertype='stoplimit', prefer='High', threshold=0.0005, orderqty=100000, replace=FALSE )
ここまでで、Ruleの設定方法は終了なので、実際にEntryルール、Exitルールを設定する。
事前に下記変数を用意する(便宜上なので必須ではない)。
> .threshold<-0.0005 > .orderqty<-100000
●entry rules
Longエントリーのルール。
add.rule(strategy='tes', name='ruleSignal', arguments=list(sigcol='long' , sigval=TRUE, orderside='long' , ordertype='stoplimit', prefer='High', threshold=.threshold, orderqty=+.orderqty, replace=FALSE ), type='enter', label='EnterLONG' ) [1] "tes"
Shortエントリーのルール。
add.rule(strategy='tes', name='ruleSignal', arguments=list(sigcol='short', sigval=TRUE, orderside='short', ordertype='stoplimit', prefer='Low', threshold=-.threshold, orderqty=-.orderqty, replace=FALSE ), type='enter', label='EnterSHORT' ) [1] "tes"
●exit rules
Longのエグジットルール
add.rule(strategy='tes', name='ruleSignal', arguments=list(sigcol='long' , sigval=TRUE, orderside='short', ordertype='market', orderqty='all', replace=TRUE ), type='exit', label='Exit2LONG' ) [1] "tes"
Shortのエグジットルール
add.rule(strategy='tes', name='ruleSignal', arguments=list(sigcol='short', sigval=TRUE, orderside='long' , ordertype='market', orderqty='all', replace=TRUE ), type='exit', label='Exit2SHORT' ) [1] "tes"
■portfolioの用意
portfolioを作成するにはinitPortf関数を使用する。
この例の場合、事前にTOPIXというオブジェクトを用意して、そこにgetSymbols関数を使用して取得したデータを格納していることが前提。この前提があるから、symbols<-c("TOPIX")によってsymbolsを設定できる。
> symbols<-c("TOPIX") > currency("JPY") [1] "JPY" > stock(symbols, currency="JPY", multiplier=1) [1] "YJ99845" > initDate<-'2007-01-04' > initPortf(portfolio.st, symbols, initDate=initDate, currency='JPY') [1] "tes"
■order bookの用意
order bookを作成するにはinitOrders関数を使用する。
> initOrders(portfolio.st, initDate=initDate)
■戦略の実行
applyStrategy関数を使用して戦略を実行する。
> applyStrategy("tes", portfolio.st) [1] "2007-03-14 00:00:00 TOPIX -1e+05 @ 1684.8895" [1] "2007-04-12 00:00:00 TOPIX 1e+05 @ 1726.18" [1] "2007-05-23 00:00:00 TOPIX 1e+05 @ 1743.1205" [1] "2007-05-23 00:00:00 TOPIX 1e+05 @ 1743.1205" [1] "2007-07-25 00:00:00 TOPIX -2e+05 @ 1754.03" [1] "2007-07-25 00:00:00 TOPIX -1e+05 @ 1747.64" [1] "2007-10-01 00:00:00 TOPIX 1e+05 @ 1615.89" [1] "2007-10-01 00:00:00 TOPIX 1e+05 @ 1622.4905" [1] "2007-10-01 00:00:00 TOPIX 1e+05 @ 1622.4905" [1] "2007-10-29 00:00:00 TOPIX -2e+05 @ 1606.49" [1] "2007-11-08 00:00:00 TOPIX -1e+05 @ 1535.99" [1] "2007-12-11 00:00:00 TOPIX 1e+05 @ 1567.02" [1] "2007-12-11 00:00:00 TOPIX 1e+05 @ 1567.9405" ・・・・・・・・・・・
実行後にmktdataオブジェクトが生成されているので確認する。
> head(mktdata) Open High Low Close SMA.nFast SMA.nSlow long short 2007-01-04 1692.94 1701.45 1692.21 1698.95 NA NA NA NA 2007-01-05 1697.06 1697.15 1670.35 1675.33 NA NA NA NA 2007-01-09 1672.68 1696.02 1670.24 1692.12 NA NA NA NA 2007-01-10 1690.59 1690.98 1657.44 1663.00 NA NA NA NA 2007-01-11 1668.57 1675.94 1650.82 1656.72 NA NA NA NA 2007-01-12 1670.33 1692.03 1667.65 1685.27 NA NA NA NA > tail(mktdata) Open High Low Close SMA.nFast SMA.nSlow long short 2016-01-04 1532.53 1544.73 1506.27 1509.67 1533.608 1561.850 NA NA 2016-01-05 1504.69 1516.55 1498.21 1504.71 1527.608 1559.123 NA NA 2016-01-06 1508.78 1514.48 1478.78 1488.84 1522.782 1555.405 NA NA 2016-01-07 1483.90 1489.63 1456.61 1457.94 1515.448 1550.563 NA NA 2016-01-08 1442.85 1472.64 1441.05 1447.32 1506.820 1545.276 NA NA 2016-01-12 1429.52 1436.08 1401.95 1401.95 1494.653 1538.852 NA NA
●quantstrat::getOrderBook()
order bookを確認する。
> head(getOrderBook(portfolio.st)) $tes $tes$TOPIX Order.Qty Order.Price Order.Type Order.Side Order.Threshold Order.Status Order.StatusTime Prefer Order.Set Txn.Fees Rule Time.In.Force 2007-03-08 "-1e+05" "1684.8895" "stoplimit" "short" "-5e-04" "closed" "2007-03-14 00:00:00" "Low" NA "0" "EnterSHORT" "" 2007-04-11 "all" "1739.01" "market" "short" NA "closed" "2007-04-12 00:00:00" "" NA "0" "Exit2LONG" "" 2007-04-11 "1e+05" "1743.5005" "stoplimit" "long" "5e-04" "replaced" "2007-04-25" "High" NA "0" "EnterLONG" "" 2007-04-11 "1e+05" "1743.5005" "stoplimit" "long" "5e-04" "replaced" "2007-04-25" "High" NA "0" "EnterLONG" "" 2007-04-25 "-1e+05" "1683.1895" "stoplimit" "short" "-5e-04" "replaced" "2007-05-14" "Low" NA "0" "EnterSHORT" "" 2007-05-14 "1e+05" "1743.1205" "stoplimit" "long" "5e-04" "closed" "2007-05-23 00:00:00" "High" NA "0" "EnterLONG" "" 2007-05-14 "1e+05" "1743.1205" "stoplimit" "long" "5e-04" "closed" "2007-05-23 00:00:00" "High" NA "0" "EnterLONG" ""
●blotter::chart.Posn()
> chart.Posn(portfolio.st)
他にも機能はあるが、導入のための学習としてはこの辺で。。。