RSIのアルゴリズム #QuantX

## RSI 
今回はRSIを使ったアルゴリズムを [QuantX](https://quantx.io/)で書いてみたいと思います．
[RSI（相対力指数）](https://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AF%E3%83%8B%E3%82%AB%E3%83%AB%E6%8C%87%E6%A8%99%E4%B8%80%E8%A6%A7#RSI)は最も有名なオシレーター系の指標と言えるのではないでしょうか．

計算式は簡単で，

```math
RSI = \frac{過去ｎ日間で上昇した値幅}{過去ｎ日間で上昇した値幅+過去ｎ日間で下落した値幅} \times 100　\\
```
です．
過去何日分を見るか，というのがキモになりますが，よく使われるのは１４日間のようです．
今回は短めに５日間にしてみます．

## シグナル

RSIが30％以下になったら買い，７０％以上で売り

## 手仕舞い

売りシグナルがでたタイミング

## コード

```python
import numpy as np 
import talib as ta 
import pandas as pd 


def initialize(ctx):
    # 設定
    ctx.target = 0.10
    ctx.period = 5
    ctx.codes = [1605, 1925, 2503, 4519, 4911, 6301, 6752, 7741, 8001 ]
    ctx.symbol_list  = ["jp.stock.{}".format(code) for code in ctx.codes]
    
    
    ctx.configure(
      channels={          # 利用チャンネル
        "jp.stock": {
          "symbols":ctx.symbol_list,
          "columns": ["close_price_adj",    # 終値(株式分割調整後) 
          ]}})

    def _RSI(data):
      
      df_close = data["close_price_adj"].fillna(method='ffill')
      # memo 参照
      df_rsi = df_close.apply(lambda s: ta.RSI(s.values.astype(np.double), timeperiod=ctx.period),axis=0)

      buy_sig = df_rsi < 30.0 
      sell_sig = df_rsi > 70.0 
      
      return {
        "rsi": df_rsi,
        "buy:sig": buy_sig, 
        "sell:sig": sell_sig, 
          
        }

    # シグナル登録
    ctx.regist_signal("RSI", _RSI)

def handle_signals(ctx, date, current):
    df = current.copy()

    # 買いシグナル
    df_long = df[df["buy:sig"]]
    if not df_long.empty:
      for (sym, val) in df_long.iterrows(): 
        sec = ctx.getSecurity(sym)
        msg = "買いシグナル"
        sec.order_target_percent(ctx.target, comment= msg)

    # 売り（ポジションクローズ）シグナル
    df_sell = df[df["sell:sig"]]
    if not df_sell.empty:
      for (sym, val) in df_sell.iterrows(): 
        sec = ctx.getSecurity(sym)
        msg = "売りシグナル"
        sec.order_target_percent(0, comment= msg)
```

## 結果

![2018-08-02_19.jpg](https://qiita-image-store.s3.amazonaws.com/0/14019/251dc13c-ef8d-5b44-d2ff-64d00271f376.jpeg)



## memo 

#### apply 

今回は，[pandas.DataFrame.apply](https://pandas.pydata.org/pandas-docs/version/0.22/generated/pandas.DataFrame.apply.html#pandas-dataframe-apply)を使って各銘柄のRSIを算出しました．

どういう仕組みか，簡単に説明します．
まずは説明のために、いくつかの銘柄について、乱数で過去１００日分の株価を作ります．

```python
import pandas as pd 
import numpy as np 
import talib as ta 

inputs = {
    'Toyota': np.random.randint(80, 100, size=100), 
    'Nissan': np.random.randint(50, 100, size=100), 
    'JAL': np.random.randint(40, 60, size=100), 
    'ANA': np.random.randint(50, 100, size=100), 
    'JT': np.random.randint(150, 200, size=100), 
}

df_close = pd.DataFrame(inputs)
```
`df_close` のデータはこのような構造になります．QuantX上でも全く同じ構造です．（ただしコラム名は文字列ではなく銘柄オブジェクト）

![2018-08-02_16.jpg](https://qiita-image-store.s3.amazonaws.com/0/14019/50f23cad-dc05-7d60-b742-fa96665d8799.jpeg)

このデータフレームの各コラム，`ANA`, `JAL`, `JT`, `Nissan`, `Toyota` それぞれの `Series` データに `ta.RSI` を適用させて，その結果をDataFrameで返すのが apply です．

```python
df_rsi = df_close.apply(lambda s: ta.RSI(s.values.astype(np.double), timeperiod=5)) 
```
`df_rsi` は，このようなデータになります．

![2018-08-02_17.jpg](https://qiita-image-store.s3.amazonaws.com/0/14019/9a9bb90b-357e-ffa4-a4e1-7ec2bae610c7.jpeg)

#### lambda 

apply の引数に無名関数 `lambda` を使いました．
上記の `lambda s: ta.RSI(s.values.astype(np.double), timeperiod=5)` は

```python 
def hoge (s)
    return ta.RSI(s.values.astype(np.double), timeperiod=5)
```
と全く同じ意味になります．

#### apply を使用しない

`apply`を使わずに書くとすると，このようなコードが考えられます．

```python
d =dict()
for s in df_close.columns:
  d[s] = ta.RSI(df_close[s].values.astype(np.double), timeperiod=5)
#df_rsi = pd.DataFrame(d) 
df_rsi = pd.DataFrame(d, index = df_close.index) 
```
個人的な趣味ですが，１行で終わるところを４行も書くのは好みではないので， `apply` を使いました．

## 感想

+ 銘柄は [ストキャスティクスアルゴリズム](https://qiita.com/shinseitaro/items/ffcbb4bec6bb344c199d)で使用したものをそのまま使いました．

## 免責注意事項
+ このコードに基づき投資した結果、損害が発生しても，一切責任を持ちません．
+ このコードが正しく機能する保証は一切致しません．
+ このアルゴリズムを勧めているわけではありません．あくまで QuantX / Python のサンプルコードとして掲載しているだけです．