QuantX～RSI&ボリンジャーバンド編～

#内容
今回はRSIとボリンジャーバンドを使ったアルゴリズムを紹介します。


#自己紹介

某理工学部に通う大学2年のfalcon_xです.
大学ではC言語を少しやっているぐらいで、Pythonはほぼ初心者です、、、
Smart Trade社でインターンを始め, QuantX Factoryを使ってアルゴリズムを開発してます。
##What is Smart Trade？

>Smart Trade 会社URL：https://smarttrade.co.jp/

ホームページを開くと、こんなページが出てきます。
<div align = "center">
<img width="994" alt="SmartTrade2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538875/a3775ccb-d643-0344-5350-a5d7387c3f73.png">
</div>
Smart Tradeとは、株式の売買タイミングを判断する投資アルゴリズムのプラットフォーム企業です。特別なお金持ちではなくても金融サービスを楽しめる、**金融の民主化**を実現するために、アルゴリズム開発プラットフォーム**QuantX Factory**やアルゴリズム売買プラットフォーム**QuantX Store**を提供する会社です。
　
**QuantX Factory**ではエンジニアを対象に、アルゴリズム開発環境を無償で公開しています。自分が作りたいアルゴリズムをPythonという言語を使ってWeb上で開発することができ、過去の株価の変動を用いて損益等々のバックテストが行えるというものです。またここで開発したアルゴリズムを**QuantX Store**で販売でき、投資家はアルゴリズムを購入することができます。

> QuantX Factory URL：https://factory.quantx.io/
>QuantX Store URL：https://quantx.io/store

またSmart Trade社では、毎週QuantXのアルゴリズムの勉強会をしています。興味のある方はぜひお越しください。

>Pythonアルゴリズム勉強会：https://python-algo.connpass.com/

#RSIとボリンジャーバンドを使ったアルゴリズム
RSIとボリンジャーバンドについては下記のサイトがわかりやすいので、詳しく知っりたい方は下記のサイトを参考までにご覧ください！
>RSI：https://info.monex.co.jp/technical-analysis/indicators/005.html
>ボリンジャーバンド：https://www.jibunbank.co.jp/products/foreign_deposit/chart/help/bollinger_band/

さて、本題に参ります。
今回の買い、売りのサインは以下のように出します。

**買いシグナル**
RSIが40未満で+2σ線を終値が下回ったとき。

**売りシグナル**
RSIが70を上回り、-2σを終値が下回ったとき。

##コード
公開コードは以下のリンクになります。
https://factory.quantx.io/developer/f5eec92829294c279499e42894575a18/coding

```c
# ライブラリーのimport
# 必要ライブラリー
import maron
import maron.signalfunc as sf
import maron.execfunc as ef
import pandas as pd
import numpy as np
import talib as ta

ot = maron.OrderType.MARKET_OPEN   # シグナルがでた翌日の始値のタイミングでオーダー


# 銘柄、columnsの取得
def initialize(ctx):
  # 設定
  ctx.logger.debug("initialize() called")
  ctx.configure(
    channels={               # 銘柄選択
      "jp.stock": {
        "symbols": [
          "jp.stock.3382", 
          "jp.stock.4063", 
          "jp.stock.4452",
          "jp.stock.4502",
          "jp.stock.4503",
          "jp.stock.4568",
          "jp.stock.6094",
          "jp.stock.6501",
          "jp.stock.6758",
          "jp.stock.6861",
          "jp.stock.6954",
          "jp.stock.6981",
          "jp.stock.7203",
          "jp.stock.7267",
          "jp.stock.7751",
          "jp.stock.7974",
          "jp.stock.8031",
          "jp.stock.8058",
          "jp.stock.8306",
          "jp.stock.8316",
          "jp.stock.8411",
          "jp.stock.8766",
          "jp.stock.8802",
          "jp.stock.9020",
          "jp.stock.9022",
          "jp.stock.9432",
          "jp.stock.9433",
          "jp.stock.9437",
          "jp.stock.9984",
        ],
        "columns": [
          "close_price",     # 終値
          "close_price_adj", # 終値(株式分割調整後)
          "volume_adj",     # 出来高
          "txn_volume",     # 売買代金
        ]
      }
    }
  )
  
  # シグナル定義
  def _my_signal(data):
    cp =  data["close_price_adj"].fillna(method='ffill')     # 終値の取得
    
    #75日移動平均線の設定
    m75 = data["close_price_adj"].fillna(method='ffill').rolling(window=75, center=False).mean()
    
    #rsiの空の入れ物を用意
    rsi_S = pd.DataFrame(data=0,columns=[], index=cp.index)
    #rsi計算
    for (sym,val) in cp.items():
        rsi_S[sym] = ta.RSI(cp[sym], timeperiod=26)
    
    #ボリンジャーバンドの空の入れ物を用意
    upperband = pd.DataFrame(data=0,columns=[], index=cp.index)
    lowerband = pd.DataFrame(data=0,columns=[], index=cp.index)
    middleband = pd.DataFrame(data=0,columns=[], index=cp.index)
    
    #ボリンジャーバンドの計算
    for (sym,val) in cp.items():
      upperband[sym], middleband[sym], lowerband[sym] = ta.BBANDS(cp[sym],
        timeperiod=21,
        nbdevup=2,
        nbdevdn=2,
        matype=0)
        
    
    # 売買シグナルを定義
    buy_sig = (rsi_S < 40) & (cp < lowerband)
    sell_sig = (rsi_S > 70) & (cp > upperband)

    # market_sigという全て0が格納されているデータフレームを作成
    market_sig = pd.DataFrame(data=0.0, columns=cp.columns, index=cp.index)
    
    # buy_sigがTrueのとき1.0、sell_sigがTrueのとき-1.0とおく
    market_sig[buy_sig == True] = 1.0
    market_sig[sell_sig == True] = -1.0
    market_sig[(buy_sig == True) & (sell_sig == True)] = 0.0
    
    # ctx.logger.debug(market_sig)

    return {
      "mavg_75:price": m75,
      "rsi_S:g2": rsi_S,
      "uband": upperband,
      "mband": middleband,
      "lband": lowerband,
      "market:sig": market_sig,
    }
      
  # シグナル登録
  ctx.regist_signal("my_signal", _my_signal)

def handle_signals(ctx, date, current):
  '''
  current: pd.DataFrame
  '''
  market_sig = current["market:sig"]
  
  done_syms = set([])
  
  # 損切り、利確の設定
  for (sym, val) in ctx.portfolio.positions.items():
    returns = val["returns"]
    if returns < -0.05:
      sec = ctx.getSecurity(sym)
      sec.order(-val["amount"], comment="損切り(%f)" % returns)
      done_syms.add(sym)
    elif returns > 0.05:
      sec = ctx.getSecurity(sym)
      sec.order(-val["amount"], comment="利益確定売(%f)" % returns)
      done_syms.add(sym)

  # 買いシグナル
  buy = market_sig[market_sig > 0.0]
  for (sym, val) in buy.items():
    if sym in done_syms:
      continue
    
    sec = ctx.getSecurity(sym)
    sec.order(sec.unit() * 1 ,orderType=ot, comment="SIGNAL BUY")
    # ctx.logger.debug("BUY: %s,  %f" % (sec.code(), val))
    pass
    
  # 売りシグナル
  sell = market_sig[market_sig < 0.0]
  for (sym, val) in sell.items():
    if sym in done_syms:
      continue
    sec = ctx.getSecurity(sym)
    sec.order(sec.unit() * -1,orderType=ot, comment="SIGNAL SELL")
    #ctx.logger.debug("SELL: %s,  %f" % (sec.code(), val))
    pass
```
結果↓
<img width="592" alt="rsi&amp;BB.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538875/e2a40b65-4e6b-713d-91dc-2a7a14fc7db4.png">

α値が低いのが気になりますね、、、



##コード解説

勉強会用に作成した資料が下記のリンク先にあるので、基本的な解説はここでは省略。

>勉強会資料URL : https://python-algo.connpass.com/event/161848/

今回のコードの重要なところだけ説明していきます。
**RSIの計算**

```c
    rsi_S = pd.DataFrame(data=0,columns=[], index=cp.index)
    #rsi計算
    for (sym,val) in cp.items():
        rsi_S[sym] = ta.RSI(cp[sym], timeperiod=26)
```

はじめの行は空のデータフレーム設定。
for以下はRSIの計算になっていて、timeperiodには計算に用いる期間の設定。

**ボリンジャーバンドの計算**

```c
#ボリンジャーバンドの空の入れ物を用意
    upperband = pd.DataFrame(data=0,columns=[], index=cp.index)
    lowerband = pd.DataFrame(data=0,columns=[], index=cp.index)
    middleband = pd.DataFrame(data=0,columns=[], index=cp.index)
    
    #ボリンジャーバンドの計算
    for (sym,val) in cp.items():
      upperband[sym], middleband[sym], lowerband[sym] = ta.BBANDS(cp[sym],
        timeperiod=21,
        nbdevup=2,
        nbdevdn=2,
        matype=0)
```

上3行は空のデータフレーム設定。
for以下はボリンジャーバンドの計算。
timeperiodは期間、nbdevup、nbdevdnはの値はσ線の値（今回の場合は+2σ, -2σ）。

**売買シグナル**

```c
# 売買シグナルを定義
    buy_sig = (rsi_S < 40) & (cp < lowerband)
    sell_sig = (rsi_S > 70) & (cp > upperband)
```

買いシグナル：
RSIを用いて、売られすぎを判断＆ボリンジャーバンドの-2σを株価が下回っている状態

売りシグナル：
RSIを用いて、買われすぎを判断＆ボリンジャーバンドの+2σを株価が下回っている状態

今回はここらへんで。

#免責注意事項
本コードを用いた実際のトレードで生じる損害は一切保証しかねますので、ご注意ください。