QuantXで基本的なアルゴリズムを0.0.5エンジンに対応させる

#はじめに
QuantXでは、2019年5月末に0.0.1から0.0.5へエンジンの改装が行われました。
これまでSmart Trade社インターン生がアルゴリズムに関して様々な記事を書いてきましたが、その多くは旧エンジンの0.0.1についての記事になります。
そこで、今回は[QuantXアルゴリズム作成者に向けて](https://qiita.com/Miku_F/items/ffa208d373f7d1c4f942)にある基本的なアルゴリズムを0.0.5に対応させたコードを紹介したいと思います。
#コードの紹介
##基本的な変更点
①ライブラリの読み込みに必要に応じて、以下の3つを追加する。

```python
import maron
import maron.signalfunc as sf
import maron.execfunc as ef
```
②buy_sigとsell_sigについて以下を追加する。

```python
market_sig = pd.DataFrame(data=0.0, columns=cp.columns, index=cp.index)
market_sig[buy_sig == True] = 1.0
market_sig[sell_sig == True] = -1.0
market_sig[(buy_sig ==True) & (sell_sig == True)] =0.0
```
③returnの部分について、buy_sig とsell_sig を消去して、代わりにmarket_sigを追加する。

```python
"market:sig": market_sig
```


##[ブル・ベアファンド自動裁定 〜はじめの一歩〜](https://qiita.com/Ric418/items/ac539b72f6368079b400)
```python
#必要なライブラリの読み込み
import numpy as np
import pandas as pd
import talib as ta
import maron
import maron.signalfunc as sf
import maron.execfunc as ef

#RCIを計算する関数
def get_rci(close, period):
      rank_period = np.arange(period, 0, -1)
      length = len(close)
      rci = np.zeros(length)
      for i in range(length):
          if i < period - 1:
              rci[i] = 0
          else :
              rank_price = close[i - period + 1: i + 1].rank(method='min', ascending = False).values
              rci[i] = (1 - (6 * sum((rank_period - rank_price)**2)) / (period**3 - period)) * 100
      return rci

def initialize(ctx):
    # 設定
    ctx.logger.debug("initialize() called")
    ctx.configure(
      channels={          # 利用チャンネル
        	"jp.stock": {
                "symbols": [
                    "jp.stock.1321", #日経225連動型上場投資信託
                    "jp.stock.1357", #日経ダブルインバース上場投信 bear
                    "jp.stock.1570", #日経平均レバレッジ上場投信 bull
                ],
                "columns": [
                  "close_price_adj"
                ]
            }
          }
        )

    def _my_signal(data):
      cp = data["close_price_adj"].fillna(method='ffill')
      # ctx.logger.debug(cp)
      rci9 = pd.DataFrame(0, index=cp.index, columns=cp.columns)
      rsi7 = pd.DataFrame(0,index=cp.index, columns=cp.columns)

      for (sym,val) in cp.items():
        rci9[sym]=get_rci(cp[sym], 9)

      for (sym,val) in cp.items():
        rsi7[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod=7)
      # ctx.logger.debug(rci9)
      # ctx.logger.debug(rsi7)
      buy_sig= (rci9 < -80) | (rsi7 < 30)
      sell_sig= (rci9 > 80) | (rsi7 > 70)
      
      market_sig = pd.DataFrame(data=0.0, columns=cp.columns, index=cp.index)
      market_sig[buy_sig == True] = 1.0
      market_sig[sell_sig == True] = -1.0
      market_sig[(buy_sig ==True) & (sell_sig == True)] =0.0      
      return {
        "rci9:g2":rci9,
        "rsi7:g2":rsi7,
        "market:sig": market_sig,        
      }

    # シグナル登録
    ctx.regist_signal("my_signal", _my_signal)
def handle_signals(ctx, date, current):
    '''
    current: pd.DataFrame
    '''
    # ctx.logger.debug(current)
    bear = ctx.getSecurity("jp.stock.1357")
    bull = ctx.getSecurity("jp.stock.1570")
    # 買いシグナル
    df_buy = current["buy:sig"][0]
    if df_buy:
      bull.order_target_percent(0.10, comment="BULL BUY")
      bear.order_target_percent(0, comment="BEAR SELL")
    # 売りシグナル
    df_sell = current["sell:sig"][0]
    if df_sell:
      bear.order_target_percent(0.10, comment="BEAR BUY")
      bull.order_target_percent(0, comment="BULL SELL")  

```
上記の基本的な変更点通りに、
4-7行目に①を追加。
56-59に②を追加。
63行目を③のように変更。

##[QuantX FactoryでMACDのアルゴリズムを作ってみよう。](https://qiita.com/katakyo/items/63e4c6461a0c8ac861d6)

```python
#ライブラリーの設定
import pandas as pd
import talib as ta
import numpy as np
import maron
import maron.signalfunc as sf
import maron.execfunc as ef

  #関数の初期化
def initialize(ctx):
  #設定
    ctx.logger.debug("initialize() called")
    ctx.configure(
      target="jp.stock.daily",
      channels={          #銘柄選択
        "jp.stock": {
          "symbols": [
            "jp.stock.1414", "jp.stock.2269",
            "jp.stock.2292", "jp.stock.2371",
            "jp.stock.2379", "jp.stock.2413",
            "jp.stock.2427", "jp.stock.2702",
            "jp.stock.2782", "jp.stock.2809",
            "jp.stock.2930", "jp.stock.3003",
            "jp.stock.3053", "jp.stock.3064",
            "jp.stock.3092", "jp.stock.3141",
            "jp.stock.3382", "jp.stock.3407",
            "jp.stock.3665", "jp.stock.3676",
            "jp.stock.4063", "jp.stock.4343",
            "jp.stock.4452", "jp.stock.4519",
            "jp.stock.4543", "jp.stock.4704",
            "jp.stock.4751", "jp.stock.4911",
            "jp.stock.4974", "jp.stock.5108",
            "jp.stock.6028", "jp.stock.6098",
            "jp.stock.6194", "jp.stock.6503",
            "jp.stock.6645", "jp.stock.6758",
            "jp.stock.6866", "jp.stock.7269",
            "jp.stock.7272", "jp.stock.7606",
            "jp.stock.7936", "jp.stock.8020",
            "jp.stock.8252", "jp.stock.8591",
            "jp.stock.8929", "jp.stock.9007",
            "jp.stock.9086", "jp.stock.9433",
            "jp.stock.9474", "jp.stock.9613"
          ],
          "columns": [
            #"open_price_adj",    # 始値(株式分割調整後)
            #"high_price_adj",    # 高値(株式分割調整後)
            #"low_price_adj",     # 安値(株式分割調整後)
            #"close_price",        # 終値
            "close_price_adj",    # 終値(株式分割調整後) 
          ] 
        }
      }
    )
      #売買シグナル生成部分
    def _my_signal(data):
      cp = data["close_price_adj"].fillna(method="ffill")

      #各銘柄の終値(株式分割調整後)を取得、欠損データの補完 
      df_close = data["close_price_adj"].fillna(method='ffill') 

      #MACDの設定
      d_macd = dict() 
      d_macdsignal = dict()
      d_macdhist = dict() 

      for symbol in data.minor_axis:
        macd, macdsignal, macdhist = ta.MACD(df_close[symbol].values.astype(np.double), fastperiod=12 , slowperiod=26, signalperiod=9)

        d_macd[symbol] = macd
        d_macdsignal[symbol] = macdsignal
        d_macdhist[symbol] = macdhist

      df_macd = pd.DataFrame(d_macd, index=data.major_axis)        
      df_macdsignal = pd.DataFrame(d_macdsignal, index=data.major_axis)        
      df_macdhist = pd.DataFrame(d_macdhist, index=data.major_axis)

      #macdの売り買いのサインを定義
      df_goldencross = (df_macd > df_macdsignal) &  (df_macd.shift(1) < df_macdsignal.shift(1))
      df_deadcross = (df_macd < df_macdsignal) &  (df_macd.shift(1) > df_macdsignal.shift(1))

      #売買シグナルの設定
      buy_sig = df_goldencross 
      sell_sig = df_deadcross 
      market_sig = pd.DataFrame(data=0.0, columns=cp.columns, index=cp.index)
      market_sig[buy_sig == True] = 1.0
      market_sig[sell_sig == True] = -1.0
      market_sig[(buy_sig ==True) & (sell_sig == True)] =0.0

      return{
          "MACD": df_macd, 
          "MACDSignal": df_macdsignal, 
          "MACDHist": df_macdhist, 
          "market:sig": market_sig
        }


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

def handle_signals(ctx, date, current):

    df = current.copy()
    market_sig = current["market:sig"]
    # 買いシグナル
    buy = market_sig[market_sig > 0.0]
    if not buy.empty:
      for (sym, val) in buy.items(): 
        #ctx.logger.info(val)
        sec = ctx.getSecurity(sym)
        msg = "買いシグナル"
        sec.order_target_percent(0.10, comment= msg)

    # 売りシグナル
    sell = market_sig[market_sig < 0.0]
    if not sell.empty:
      for (sym, val) in sell.items(): 
        sec = ctx.getSecurity(sym)
        msg = "売りシグナル"
        sec.order_target_percent(0, comment= msg)
```
上記の基本的な変更点通りに、
5-7行目に①を追加。
85-87行目に②を追加。
93行目を③のように変更。

またそれに加えて、
handle_signal部分において、
103行目に下記を追加する。

```python
market_sig = current["market:sig"]
```
また、

```python
df_long = df[df["buy:sig"]]
```
のところを105行目の下記のように変更させる。

```python
buy = market_sig[market_sig > 0.0]
```
その際、その名前の変更に伴う必要な変更箇所を変更させて対応させる。
更に、

```python
for (sym, val) in buy.itterows():
```
の部分を

```python
for (sym, val) in buy.items():
```

107行目上記のようにiterrowsからitemsに変更させる。

sellについても同様の変更を行う。（sellのときは、market_sig が market_sig < 0.0になることを注意する)

##[RSIっていう指標、長期短期で使ってみた](https://qiita.com/Ric418/items/81f6d5d7521305511e15)

```python
import pandas as pd
import talib as ta
import numpy as np
import maron
import maron.signalfunc as sf
import maron.execfunc as ef

def initialize(ctx):
    # 設定
    ctx.logger.debug("initialize() called")
    ctx.configure(
      channels={          # 利用チャンネル
        "jp.stock": {
          "symbols": [
            "jp.stock.8840", #大京
            "jp.stock.7897", #ホクシン
            "jp.stock.6138", #ダイジェト
            "jp.stock.8685", #AIG
            "jp.stock.3289", #東急不HD
            "jp.stock.1847", #イチケン
            "jp.stock.9739", #NSW
            "jp.stock.6345", #アイチコーポ
            "jp.stock.7554", #幸楽苑HD
            "jp.stock.2908", #フジッコ
            "jp.stock.8860", #フジ住
            "jp.stock.6436", #アマノ
            "jp.stock.8011", #三陽商
            "jp.stock.8848", #レオパレス
            "jp.stock.7433", #伯東
            "jp.stock.3724", #ベリサーブ
          ],
          "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")
      rsi_S = pd.DataFrame(data=0,columns=[], index=cp.index)
      rsi_L = pd.DataFrame(data=0,columns=[], index=cp.index)
      for (sym,val) in cp.items():
        rsi_S[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod=9)
        rsi_L[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod=22)
        df_buy = rsi_S[(rsi_S < 20) & (rsi_L < 30)]
        df_sell = rsi_S[(rsi_S > 80) & (rsi_L > 70)]
        #条件にrsi_L  < 30, rsi_L > 70を追加
        market_sig = pd.DataFrame(data=0.0, columns=cp.columns, index=cp.index)
        market_sig[df_buy == True] = 1.0
        market_sig[df_sell == True] = -1.0
        market_sig[(df_buy ==True) & (df_sell == True)] =0.0
        
      return {
        "rsi_S": rsi_S,
        "rsi_L": rsi_L,
        "market:sig": market_sig
      }

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

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

    done_syms = set([])
    df_buy = current["buy:sig"].dropna()
    df_sell = current["sell:sig"].dropna()
    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))
    
    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))
```
上記の基本的な変更点通りに、
4-6行目に①を追加。
55-58行目に②を追加。
63行目を③のように変更。

以上が、基本的なアルゴリズムにあるアルゴリズムを0.0.5に対応させたコードになります。
アルゴリズムの詳しい内容はそれぞれの記事をご覧ください。