ローソク足とテクニカル指標を組み合わせたアルゴリズムを作る

#はじめに
Smart Trade社が提供するQuantX Factory上ではtalibを用いた、ローソク足を用いて株式の売買アルゴリズムを作ることが出来ます。
しかしながら、ローソク足を用いたサンプルアルゴリズムは数が乏しく、現状参考にできるものが、インターン生が書いた、[こちら](https://qiita.com/Mea/items/52be071da240440eca66)のまとめしかありません。
そこで、今回は、ローソク足（長い足のローソク（大陽線・大陰線））とテクニカル指標（移動平均乖離率）を組み合わせたコードを作ることで、テクニカル指標とローソク足の組み合わせのサンプルコードを作りたいと思います。

#移動平均乖離率とローソク足を組み合わせたコード
こちらのコードがコードの完全版となります。銘柄はTOPIX Core 30を使っています。

コピペするときにはこちらをお使いください。

```python
# 必要なライブラリーをimportする
import maron
import maron.signalfunc as sf
import maron.execfunc as ef
import pandas as pd
import talib as ta
import numpy as np

# オーダ方法を決定する
ot = maron.OrderType.MARKET_OPEN   # シグナルがでた翌日の始値のタイミングでオーダー

# 銘柄、columnsの取得を行う
# 初期化を行う
def initialize(ctx):
  ctx.configure(
    channels={               # 利用チャンネル
      "jp.stock": {
        "symbols": [
          "jp.stock.2914", #JT(日本たばこ産業)
          "jp.stock.8766", #東京海上ホールディングス
          "jp.stock.8031", #三井物産
          "jp.stock.8316", #三井住友フィナンシャルグループ
          "jp.stock.8411", #みずほフィナンシャルグループ
          "jp.stock.9437", #NTTドコモ
          "jp.stock.4502", #武田薬品工業
          "jp.stock.8058", #三菱商事
          "jp.stock.9433", #KDDI
          "jp.stock.9432", #日本電信電話
          "jp.stock.7267", #ホンダ（本田技研工業）
          "jp.stock.8306", #三菱ＵＦＪフィナンシャル・グループ
          "jp.stock.4503", #アステラス製薬
          "jp.stock.4063", #信越化学工業
          "jp.stock.7974", #任天堂
          "jp.stock.6981", #村田製作所
          "jp.stock.3382", #セブン＆アイ・ホールディングス
          "jp.stock.9020", #東日本旅客鉄道
          "jp.stock.8802", #三菱地所
          "jp.stock.9022", #東海旅客鉄道
          "jp.stock.9984", #ソフトバンクグループ
          "jp.stock.6861", #キーエンス
          "jp.stock.6501", #日立製作所
          "jp.stock.6752", #パナソニック
          "jp.stock.6758", #ソニー
          "jp.stock.6954", #ファナック
          "jp.stock.7203", #トヨタ自動車
          "jp.stock.7751", #キヤノン
          "jp.stock.4452", #花王
          "jp.stock.6098", #リクルートホールディングス
        ],
        "columns": [
          "open_price_adj",    # 始値(株式分割調整後)
          "high_price_adj",    # 高値(株式分割調整後)
          "low_price_adj",     # 安値(株式分割調整後)
          "close_price",     # 終値
          "close_price_adj", # 終値(株式分割調整後)
          #"volume_adj",     # 出来高
          #"txn_volume",     # 売買代金
        ]
      }
    }
  )
  
  # シグナル定義
  def _my_signal(data):
    # 終値等の取得を行い、欠損値補完を行う。
    cp =  data["close_price_adj"].fillna(method='ffill')
    op =  data["open_price_adj"].fillna(method= 'ffill')
    hp = data["high_price_adj"].fillna(method="ffill")
    lp = data["low_price_adj"].fillna(method="ffill")
    
    # 25日移動平均線と75日移動平均線の乖離率を計算
    m25 = data["close_price_adj"].fillna(method='ffill').rolling(window=25, center=False).mean()
    m75 = data["close_price_adj"].fillna(method='ffill').rolling(window=75, center=False).mean()
    ratio = m25 / m75
    
    # ローソク足についてを計算
    candle_1 = pd.DataFrame(data=0,columns=[], index=cp.index)
    for(sym,val) in cp.items():
      candle_1[sym] = ta.CDLLONGLINE(op[sym],hp[sym],lp[sym],cp[sym])
      
    # true,falseのbool型にする
    candle_1_buy = candle_1[(candle_1 == 100)]
    candle_1_sell = candle_1[(candle_1 == -100)]
    candle_1_buy[(candle_1 == 100)] = True
    candle_1_sell[(candle_1 == -100)] = False

    # 売買シグナルを定義(bool値で返す)
    buy_sig = (ratio > 1.05) & candle_1_buy
    sell_sig = (ratio < 0.95) & candle_1_sell

    # 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

    return {
      "mavg_25:price": m25,
      "mavg_75:price": m75,
      "ratio:g2":ratio,
      "market:sig": market_sig,
      "candle:1":candle_1
    }
      
  # シグナル登録
  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.03:
      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

```
#コードの解説
このアルゴリズムは、ローソク足の指標に対して売買シグナルが出ているときと、移動平均線の指標に対して、売買シグナルが出ているときの両方の「かつ」の条件で、シグナルが出るようになっています。

まず初めに、QuantXでアルゴリズムを作るのに必要な初期設定を行っていきます。
詳細につきましては、[公式ドキュメント](https://factory.quantx.io/handbook/ja/)やQunaX初心者に向けた[こちら](https://qiita.com/Miku_F/items/ffa208d373f7d1c4f942)の記事をご覧ください。
この記事では省略させていただきます。

```python
# 必要なライブラリーをimportする
import maron
import maron.signalfunc as sf
import maron.execfunc as ef
import pandas as pd
import talib as ta
import numpy as np

# オーダ方法を決定する
ot = maron.OrderType.MARKET_OPEN   # シグナルがでた翌日の始値のタイミングでオーダー

# 銘柄、columnsの取得を行う
# 初期化を行う
def initialize(ctx):
  ctx.configure(
    channels={               # 利用チャンネル
      "jp.stock": {
        "symbols": [
          "jp.stock.2914", #JT(日本たばこ産業)
          "jp.stock.8766", #東京海上ホールディングス
          "jp.stock.8031", #三井物産
          "jp.stock.8316", #三井住友フィナンシャルグループ
          "jp.stock.8411", #みずほフィナンシャルグループ
          "jp.stock.9437", #NTTドコモ
          "jp.stock.4502", #武田薬品工業
          "jp.stock.8058", #三菱商事
          "jp.stock.9433", #KDDI
          "jp.stock.9432", #日本電信電話
          "jp.stock.7267", #ホンダ（本田技研工業）
          "jp.stock.8306", #三菱ＵＦＪフィナンシャル・グループ
          "jp.stock.4503", #アステラス製薬
          "jp.stock.4063", #信越化学工業
          "jp.stock.7974", #任天堂
          "jp.stock.6981", #村田製作所
          "jp.stock.3382", #セブン＆アイ・ホールディングス
          "jp.stock.9020", #東日本旅客鉄道
          "jp.stock.8802", #三菱地所
          "jp.stock.9022", #東海旅客鉄道
          "jp.stock.9984", #ソフトバンクグループ
          "jp.stock.6861", #キーエンス
          "jp.stock.6501", #日立製作所
          "jp.stock.6752", #パナソニック
          "jp.stock.6758", #ソニー
          "jp.stock.6954", #ファナック
          "jp.stock.7203", #トヨタ自動車
          "jp.stock.7751", #キヤノン
          "jp.stock.4452", #花王
          "jp.stock.6098", #リクルートホールディングス
        ],
        "columns": [
          "open_price_adj",    # 始値(株式分割調整後)
          "high_price_adj",    # 高値(株式分割調整後)
          "low_price_adj",     # 安値(株式分割調整後)
          "close_price",     # 終値
          "close_price_adj", # 終値(株式分割調整後)
          #"volume_adj",     # 出来高
          #"txn_volume",     # 売買代金
        ]
      }
    }
  )
```

上記の初期設定が終わりましたら、売買シグナルを生成する部分である、my_signal部分を定義していきます。

```python
  # シグナル定義
  def _my_signal(data):
    # 終値等の取得を行い、欠損値補完を行う。
    cp =  data["close_price_adj"].fillna(method='ffill')
    op =  data["open_price_adj"].fillna(method= 'ffill')
    hp = data["high_price_adj"].fillna(method="ffill")
    lp = data["low_price_adj"].fillna(method="ffill")
```

終値等の値に対して、それぞれ欠損値を補完していきます。これらの値の詳細は[こちら](https://factory.quantx.io/handbook/ja/dataset.jp.stock.html)のcolumnsをご覧ください。

次に移動平均線の計算を行っていきます。

```python
    # 25日移動平均線と75日移動平均線の乖離率を計算
    m25 = data["close_price_adj"].fillna(method='ffill').rolling(window=25, center=False).mean()
    m75 = data["close_price_adj"].fillna(method='ffill').rolling(window=75, center=False).mean()
    ratio = m25 / m75
```

pandasのメソッドを使って、計算していきます。移動平均乖離率については[アルゴリズム開発入門](https://factory.quantx.io/handbook/ja/)に詳しい説明がありますので、そちらをご覧ください。

```python
    candle_1 = pd.DataFrame(data=0,columns=[], index=cp.index)
    for(sym,val) in cp.items():
      candle_1[sym] = ta.CDLLONGLINE(op[sym],hp[sym],lp[sym],cp[sym])
```

ローソク足の計算を行っていきます。
candle_1という空の箱をまず用意します。
そこに、talibの関数を使って、ローソク足の必要なデータを入れていきます。

```python
    #true,false型にする
    candle_1_buy = candle_1[(candle_1 == 100)]
    candle_1_sell = candle_1[(candle_1 == -100)]
    ctx.logger.debug(candle_1_buy)
    
    candle_1_buy[(candle_1 == 100)] = True
    candle_1_sell[(candle_1 == -100)] = False
```

次にローソク足のデータを必要な形に変形していきます。
今、ローソク足（candle_1）に入っているデータは、

| 2017-07-04 | 0 | 0 | -100 | 0    |
|------------|---|---|------|------|
| 2017-07-05 | 0 | 0 | 100  | 100  |
| 2017-07-06 | 0 | 0 | 0    | -100 |
のように、-100, 0, 100 のいずれかで表されています。


今回はテクニカル指標として、移動平均乖離率とローソク足を組み合わせたものを作ります。
そこで、今ここで、移動平均線の売買シグナルを考えます。
今回、乖離率の割合が1.05よりも大きかった時に買シグナルを出すとすると、これは、buy_sig = ratio > 1.05　というコードになります。この時のbuy_sigは、

| 2019-04-22 | False | False | True | False |   |
|------------|-------|-------|------|-------|---|
| 2019-04-23 | False | False | True | False |   |
| 2019-04-24 | False | False | True | False |   |
という形であらわされます。

今回は、ローソク足とテクニカル指標の両方でシグナルが出たときに買シグナルを出したいので、buy_sigで、（テクニカル指標で買シグナル）かつ（ローソク足で買シグナル）という条件にしたいと思います。
このままだと形が違うため、かつの条件にすることが出来ません。そこで、ローソク足の売買シグナルをTrue,False型に直したいと思います。

ローソク足で、買シグナルが出るときの名前をcandle_1_buyとします。ローソク足で買シグナルが出るとき、（すなわちcandle_1=100となるとき）candle_1_buyをTrueと定義します。

```python
    # 売買シグナルを定義(bool値で返す)
    buy_sig = (ratio > 1.05) & candle_1_buy
    sell_sig = (ratio < 0.95) & candle_1_sell
```
データの下処理が出来たところで、bool型の買シグナルと売シグナルを定義します。
上記のコードで直した結果のbuy_sigは以下になります。

| 2019-05-09 | NaN   | NaN   | NaN  | NaN   |   |
|------------|-------|-------|------|-------|---|
| 2019-05-10 | NaN   | NaN   | NaN  | NaN   |   |
| 2019-05-13 | NaN   | NaN   | NaN  | NaN   |   |
| 2019-05-14 | False | False | True | False |   |


```python
    # 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

    return {
      "mavg_25:price": m25,
      "mavg_75:price": m75,
      "ratio:g2":ratio,
      "market:sig": market_sig,
      "candle:1":candle_1
    }
      
  # シグナル登録
  ctx.regist_signal("my_signal", _my_signal)
```

ここまで来たら、後はQuantXの仕様にのっとってシグナル登録等をしていきます。ここら辺の操作も[ドキュメント](https://factory.quantx.io/handbook/ja/)をご覧ください。

```python
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.03:
      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
```
my_signalが定義し終わった後は利確、損切等の設定を行っていきます。handle_signalsもドキュメント等の普通のコードと同じになります。

#まとめ
ローソク足のみの損益率は以下になります。
![ローソク (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378059/20ba9e67-e00b-d321-3c3a-af4a1b183e52.png)

移動平均乖離率のみの損益率は以下になります。
![乖離率 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378059/4012fe81-ee93-1557-7ba6-54cefa485065.png)

ローソク足と移動平均乖離率は以下になります。
![ローソク足と移動平均線 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378059/a4ab3fd6-4260-7907-9c2a-2e11307486b4.png)

移動平均乖離率のみが一番成績がいいのですが...

それでも、ローソク足のみよりは、ローソク足と移動平均乖離率の組み合わせの方が成績が良くなりました。
今回テクニカル指標とローソク足のサンプルコードを作ることが目的で、成績は気にしませんでしたが、ローソク足は選び方と組み合わせが大事だと感じました。
組み合わせることによって成績は大きく変動するということが分かったので、今後は組み合わせ方や選び方を研究したいです！