ブル・ベアファンド自動裁定 〜買い増し機能を柔軟に〜

![115 [更新済み]-01.jpg](https://qiita-image-store.s3.amazonaws.com/0/318424/2ddff4af-d160-958b-bdc1-f2930bb9a7c3.jpeg)
## 導入
人気のあるETFブルベア型の取引の自動裁定システムをQuantX Factory上で開発します。
シリーズ化していますので

「QuantXって何？」
「ブルベア取引って？」
「まず名を名乗れよ。」

って方は以下のURLの1を参照下さい。

1. [ブル・ベアファンド自動裁定 〜はじめの一歩〜](https://qiita.com/Ric418/items/ac539b72f6368079b400)
2. [ブル・ベアファンド自動裁定 〜利確損切り機能実装〜](https://qiita.com/Ric418/items/ac539b72f6368079b400)
3. [ブル・ベアファンド自動裁定 〜QuantX APIで買い増し機能実装〜](https://qiita.com/Ric418/items/3c73c6f8124e65cd8544)
4. [ブル・ベアファンド自動裁定 〜QuantX APIでリスク回避・保有日数編〜（前回）](https://qiita.com/Ric418/items/11c0607aafd47a0d83d8)

##今回の目標
買い増し機能が固定の数字・数量の指定であり柔軟性の乏しいものでした。
これを最終的な取引結果の良い悪いはともかく

**保有している銘柄の数量の何割かを買い増しする**
[前回のコードはこちら](https://factory.quantx.io/developer/a279f6509d3c44058d0fc1a63e49011c)

という機能に改善します。

具体的には以下のようなアルゴリズムです

- 各銘柄の保有数をAPIにより取得しそれに割合をかける事で買い増しを行う

###早速やっていく
一応コードの全体像を眺めておきましょう、今回はこのコードの

`handle_signals`

の部分をいじって行く形になります。

<details><summary>コードの全体像</summary><div>

```py
#必要なライブラリの読み込み
import numpy as np
import pandas as pd
import talib as ta

#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"
        		]
        	}
        
      }
    )
    ctx.flags = {
      "jp.stock.1570":0,
      "jp.stock.1357":0
    }
    
    ctx.counts = {
      "jp.stock.1570":0,
      "jp.stock.1357":0
    }
    
    ctx.count_border = 4

    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)
        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)
      
      # ctx.logger.debug(buy_sig)
      # ctx.logger.debug(buy_sig.shift(1))
      return {
        "incbull:sig":buy_sig.shift(1),
        "incbear:sig":sell_sig.shift(1),
        "buy:sig":buy_sig,
        "sell:sig":sell_sig,
        "rci9:g2":rci9,
        "rsi7:g2":rsi7,
      }

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

def handle_signals(ctx, date, current):
    '''
    current: pd.DataFrame
    '''
    done_syms = set([])
    
    # ctx.logger.debug(ctx.localStorage["bulldone"])
    bear = ctx.getSecurity("jp.stock.1357")
    bull = ctx.getSecurity("jp.stock.1570")
    df_buy = current["buy:sig"][0]
    df_sell = current["sell:sig"][0]
    df_inc_bull = current["incbull:sig"][0]
    df_inc_bear = current["incbear:sig"][0]
        
    #保有日数カウント・利確損切り
    for (sym,val) in ctx.portfolio.positions.items():
        if ctx.counts[sym] == ctx.count_border:
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="保有日数超過売り" )
          ctx.flags[sym] = 0
          done_syms.add(sym)
          ctx.counts[sym] = 0
          continue
        if (sym == 'jp.stock.1321') | ('jp.stock.1357' in done_syms) | ('jp.stock.1570' in done_syms):
          continue
        returns = val["returns"]
        if returns > 0.05:
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="利益確定売(%f)" % returns)
          ctx.flags[sym] = 0
          done_syms.add(sym)
          ctx.counts[sym] = 0
          # ctx.logger.debug(ctx.localStorage[sym])
        elif returns < -0.01:
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="損切り(%f)" % returns)
          ctx.flags[sym] = 0
          done_syms.add(sym)
          ctx.counts[sym] = 0
    # 買いシグナル
    # ctx.logger.debug(df_buy)
    # ctx.logger.debug(ctx.localStorage["jp.stock.1570"])
    if df_buy & df_inc_bull & (ctx.flags["jp.stock.1570"] == 1):
      if not ('jp.stock.1357' in done_syms) | ('jp.stock.1570' in done_syms):
        bull.order(bull.unit() * 10, comment="BULL買い増し")
        done_syms.add("jp.stock.1570")
    if df_buy & (ctx.flags["jp.stock.1570"] == 0):
      if not ('jp.stock.1357' in done_syms) | ('jp.stock.1570' in done_syms):
        bull.order_target_percent(0.50, comment="BULL BUY")
        bear.order_target_percent(0, comment="BEAR SELL")
        done_syms.add("jp.stock.1570")
        ctx.flags["jp.stock.1570"] = 1
        ctx.flags["jp.stock.1357"] = 0
        ctx.counts["jp.stock.1357"] = 0
    
    # 売りシグナル
    if df_sell & df_inc_bear & (ctx.flags["jp.stock.1357"] == 1):
      if not ('jp.stock.1357' in done_syms) | ('jp.stock.1570' in done_syms):
        bear.order(bear.unit() * 10, comment="BEAR買い増し")
        done_syms.add("jp.stock.1357")
    if df_sell & (ctx.flags["jp.stock.1357"] == 0):
      if not ('jp.stock.1357' in done_syms) | ('jp.stock.1570' in done_syms):
        bear.order_target_percent(0.50, comment="BEAR BUY")
        bull.order_target_percent(0, comment="BULL SELL") 
        done_syms.add("jp.stock.1357")
        ctx.flags["jp.stock.1357"] = 1
        ctx.flags["jp.stock.1570"] = 0
        ctx.counts["jp.stock.1570"] = 0
        
    for (sym,val) in ctx.portfolio.positions.items():
      if val["amount"] > 0:
        ctx.counts[sym] = ctx.counts[sym] + 1
    # ctx.logger.debug(ctx.counts["jp.stock.1357"])
    # ctx.logger.debug(ctx.counts["jp.stock.1570"])
        
    
```
</div></details>

####追加するコード
今回はそれほど追加するコードも少ないのでその変更点のみ説明します。

121行, 135行あたりの注文のコードを

```.py
# 121行目あたり
if 'jp.stock.1570' in ctx.portfolio.positions:
          bull.order(ctx.portfolio.positions['jp.stock.1570']['amount']*0.15, comment="BULL買い増し")
          done_syms.add("jp.stock.1570")

# 136行目あたり
if 'jp.stock.1357' in ctx.portfolio.positions:
          bear.order(ctx.portfolio.positions['jp.stock.1357']['amount']*0.15, comment="BEAR買い増し")
          done_syms.add("jp.stock.1357")
```

に変更します、ちなみにこれはそれぞれの銘柄を持っている場合、
`ctx.portfolio.positions['jp.stock.1357']['amount']*0.15`
によって保有している株式数の15%を買い増しを行っています。

これで改善完了です。

完成のコードは[こちら](https://factory.quantx.io/developer/a279f6509d3c44058d0fc1a63e49011c)

##実行・結果
###前回の結果（固定買い増し）
![スクリーンショット 2019-04-15 4.12.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318424/87a5afbd-e465-d915-8291-0940f190c5a7.png)

###今回の結果（割合買い増し）
![スクリーンショット 2019-04-15 4.12.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318424/6cffdd3d-49fe-e57f-f45f-7db6f9eac49a.png)


###結果・考察
成績としては下がることになった(もう少し数値の町営などすれば改善するかも)が買い増しを保有数に対する割合にしたことで長期取引や初期資金量の違いに柔軟に対応する事ができるようになったと考えられる。

###今後の展望
保有株数にたいする割合による買い増しを行った事で保有株数の増加の勢いが大きくなったためリスク抑制のために総資産に対する保有率でリスク管理を行う。

##宣伝

###もくもく会もやってるよ
**日時**：毎週水曜日18時〜
**場所**：神田　千代田共同ビル４階　SmartTrade社オフィス
**内容**：基本黙々と自習しながら猛者の方に質問して強くなっていく会
**備考**：お菓子と終わりにお酒が出るよ

###詳細はこちらだよ
Pythonアルゴリズム勉強会HP：[https://python-algo.connpass.com/](https://python-algo.connpass.com/)
(connpassって言うイベントサイトに飛びます)

##免責注意事項
このコード・知識を使った実際の取引で生じた損益に関しては一切の責任を負いかねますので御了承下さい。

## materials
bull-bear:<a href='https://jp.freepik.com/free-vector/ベクター画像_2461918.htm'>Designed by Rawpixel.com</a>