日本株システムトレードプラットフォーム QuantX 上で RSI による売買アルゴリズムを開発する

# QuantX 上で RSI による売買アルゴリズムを開発する

## はじめに

株式投資のシステムトレードプラットフォームである QuantX 上で RSI による売買アルゴリズムを開発するハンズオンをどこかで行いたい（希望）と考えているのでその内容を基に Qiita にまとめます。スライドは [こちら](https://goo.gl/rb6VuP)。

また、この記事は

* Python知識、金融知識の有無を問わず、QuantX に興味がある方

を対象に、

* QuantX 利用の第一歩を踏みだす

ことを目的に書いたものです。

* @shinseitaro さんの記事 [RSIのアルゴリズム #QuantX](https://qiita.com/shinseitaro/items/6a699cf795c7254b0c84) に内容被っちゃいました、、pandas の扱い方とか勉強になるのでこちらも参照ください :relaxed:

## QuantX とは

* SmartTrade さんが提供する株式投資のシステムトレードプラットフォームです。Python でシステムトレードアルゴリズムを実装することができます。（現在はアルゴリズムに基づいた売買通知のみ）
* 詳細については @hiroshimodaさん のさんの記事 [Pythonで投資アルゴリズムを開発してみる](https://qiita.com/hiroshimoda/items/e89444729fe7d25c0275) や [公式ページ](https://quantx.io/top) を参照下さい

## RSI とは

> RSI(Relative Strength Index)は、日本語で「相対力指数」と呼ばれ、一定期間における値動きの強弱を数値で表した指標です。

> [RSI（Relative Strength Index） | アイネット証券](https://inet-sec.co.jp/study/technical-manual/rsi/ )

つまり、RSI とは「どれくらい値動きの勢いがあるか」の指標であると言えます。この RSI 下記の式によって算出されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/70342/a74a67a3-2371-e49a-bcab-abf8a31204aa.png)

RS = 期間中の平均上昇幅 / 期間中の平均下落幅

* この RSI の値が 80%(or 70%) を超えると買われすぎなので、売り時
* この RSI の値が 20%(or 30%) を下回ると売られすぎなので、買い時

と、一般的には言われているようです。

なので、

* RSI の値が 20% を下回った時、買い
* RSI の値が 80% を超えた時、売り

と、いうアルゴリズムを実装することを目標としてみたいと思います。

## 実装してみる

下記の様に実装してみました

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

def initialize(ctx):
    # 設定
    ctx.logger.debug("initialize() called")
    ctx.configure(
      channels={          # 利用チャンネル
        "jp.stock": {
          "symbols": [
            "jp.stock.1305",
            "jp.stock.9984",
            "jp.stock.9983",
            "jp.stock.7201",
            "jp.stock.9201",
            "jp.stock.9202",
            "jp.stock.7203"
          ],
          "columns": [
            #"open_price_adj",    # 始値(株式分割調整後)
            #"high_price_adj",    # 高値(株式分割調整後)
            #"low_price_adj",     # 安値(株式分割調整後)
            #"volume_adj",         # 出来高
            #"txn_volume",         # 売買代金
            "close_price",        # 終値
            "close_price_adj",    # 終値(株式分割調整後) 
          ]
        }
      }
    )

    def _my_signal(data):
      cp = data["close_price_adj"].fillna(method="ffill")
      rsi9 = pd.DataFrame(data=0,columns=[], index=cp.index)
      for (sym,val) in cp.items():
        rsi9[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod=9)
        
      return {
        "rsi9": rsi9,
        "buy:sig": rsi9 < 20,
        "sell:sig": rsi9 > 80,
      }

    # シグナル登録
    ctx.regist_signal("my_signal", _my_signal)

def handle_signals(ctx, date, current):
    '''
    current: pd.DataFrame
    '''

    done_syms = set([])
    buy = current["buy:sig"].dropna()
    sell = current["sell:sig"].dropna()
    df_buy = buy[buy > 0]
    for (sym,val) in df_buy.items():
        sec = ctx.getSecurity(sym)
        sec.order(sec.unit() * 1, comment="SIGNAL BUY")
        #ctx.logger.debug("BUY: %s,  %f" % (sec.code(), val))
    
    df_sell = sell[sell > 0]
    for (sym,val) in df_sell.items():
        sec = ctx.getSecurity(sym)
        sec.order_target_percent(0, comment="SIGNAL SELL")
        #ctx.logger.debug("SELL: %s,  %f" % (sec.code(), val))

```

気になった箇所を簡単に解説すると、

```python
cp = data["close_price_adj"].fillna(method="ffill")
```

* `close_price_adj` : 使用する株価（株式分割調整後終値）を指す
* `.fillna(method="fill")` : NA（欠損値）があった場合に補完する


```python
for (sym,val) in cp.items():
  rsi9[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod=9)
```
* `ta.RSI` : TA-lib の RSI 算出メソッド
* `cp[sym].values.astype(np.double)` : TA-lib が計算できるよう、numpy の double 型に数値を変換する
* `timeperiod=` : RSI の対象となる期間（日）


```python
return {
  "rsi9": rsi9,
}
```

* シグナルの定義を行い、`handle_signals`メソッド内でシグナルを受け取れるようにする

```python
rsi9 = current["rsi9"].dropna()
```

* initialize メソッド内で定義した `"rsi9"` シグナルを取得する


```python
df_buy = rsi9[rsi9 < 20]
df_sell = rsi9[rsi9 > 80]
```

* `rsi9[true]` ならば `rsi9` の値が `df_buy(df_sell)` に代入される。`rsi9[false]` なら `None` となる


```python
for (sym,val) in df_buy.items():
  sec = ctx.getSecurity(sym)
  sec.order(sec.unit() * 1, comment="SIGNAL BUY")
  # ctx.logger.debug("BUY: %s,  %f" % (sec.code(), val))
```

* `ctx.getSecurity(sym)` : 銘柄のオブジェクトを取得する
* `set.order` : 買い処理を発出 （注文処理について詳細は 公式APIマニュアル 参照）
* `ctx.logger.dubug` : コンソールに情報（ここでは銘柄コード、注文量）を出力できる

以上となります。

## 応用例

このアルゴリズムの応用例を簡単に挙げておきます。

* 1. パラメーターを変更する
    * RSI 売買の基準となる値
    * 売買量
    * 銘柄
* 2. RSI と似たような別の指標を利用する
    * MFI (Money Flow Index)
    * Stochastic RSI 
    * William’s %R  etc..

などなど、これ以外にも色んな要素が考えられるので奥が深いです :rolling_eyes:

## 最後に
* QuantX 開発元の SmartTrade さんで毎週水曜日にpythonアルゴリズムもくもく会が開催されております。興味がある方は参加してみては如何でしょうか :relaxed:
    * https://python-algo.connpass.com/
    * 自分も最近は毎週参加しております

## 免責注意事項
* このアルゴリズムに基づいて投資し、損害が発生した場合、一切責任を持ちません
* 今回のコードが正しく機能する保証は致しません
* この記事をきかっけに株式投資に興味を持ち、日本企業を応援する方が増えれば幸いなのですが、投資は自己判断にてお願い致します

## 外部リンク
QuantX
* https://quantx.io/top

公式API Doc
* https://factory.quantx.io/handbook/ja/api.html