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)

他にも機能はあるが、導入のための学習としてはこの辺で。。。