12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ブル・ベアファンド自動裁定 〜はじめの一歩〜

Last updated at Posted at 2019-02-15

115 [更新済み]-01.jpg

導入

お前だれ

都内某T大学部4年、機械学習・統計手法でバイオインフォマ、ケモインフォマでケモケモしたりFinTechしようとしている株式会社Smart Tradeインターンの小林(Twitterアカウント)です。

神田のランチリポーターもやってます。

今回何やるの?

当社インターン5ヶ月目ということでいつまでも初心者とは言って居られなくなったので販売できそうなアルゴの実装を本気で目指して行きたいと思います。

その**"はじめの一歩"**として今回は以下を達成目標とします。

  • ブル・ベアファンドの仕組みを理解する
  • QuantX Factoryでブル・ベアファンドをの自動裁定を実装する

QuantX Factoryって何?

株式会社Smart Tradeが

" 金融の民主化 "

を理念に、これまで一部の有識者だけの物であったトレードアルゴリズムをより簡易にコード上で開発するためのプラットフォームです。

何が強いの?

本題

ブル・ベアファンドの仕組みを理解する

###ブル・ベアファンドとは
日経平均株価などの対応する株価指数の短期の値上がり・値下がりを利用して利益を出すことを狙いとした投資信託のことです。

上手くブル・ベアファンドの売買を行えば上昇相場でも下落相場でも利益を出すことができます。

ブル、ベアと二つの投資信託はそれぞれ対応する株価指数に対して特有の"動き方"と"倍率"を持っています。

それぞれの動き方

まず、それぞれの**"動き方"**について見て行きましょう。

ブル型ファンド (Bull fund) の動き

スクリーンショット 2019-02-15 11.13.49.png

その名からもわかる通り(レバレッジ型とも呼ばれます)
株価指数の値上がりに対し "牛の角が突き上げる" ように値上がりする投資信託のことで基本的には株価指数の値動きに対応するものと考えます。

上の図は黒線が対応する株価指数の動きであり赤線がブルファンドの値動きです。(イメージ)

この図からも

"対応する株価指数が上昇する時はブルファンドを買う"

という結論に至るかと思います。

ベア型ファンドの (Bear Fund) の動き

スクリーンショット 2019-02-15 11.14.11.png

これまたその名からわかる通り(インバース型とも呼ばれます)
株価指数の値上がりに対し "熊が爪を振り下ろす" ように値下がりする投資信託のことで基本的には株価指数の値動きに対して逆転するものと考えます。

上の図は黒線が対応する株価指数の動きであり青線がベアファンドの値動きです。(イメージ)

この図からも

"対応する株価指数が下落する時はベアファンドを買う"

という結論に至るかと思います。

倍率について

ブル・ベアファンドは対応する株価指標に対応して値動きすると説明しましたが、単純に等倍で値動きしている用では短期で大きな利益を出すことはできません。

そこで登場するのが値動きの倍率です。

少し例を見てみましょう

 
銘柄コード 銘柄名 概要
1367 ダイワ上場投信 TOPIXレバレッジ TOPIXの動きの2倍の値動きをする
1350 日経平均レバレッジ・インデックス連動型上場投信 日経平均の動きの2倍の値動きをする
1356 TOPIXベア2倍上場投信 TOPIXの動きの2倍の逆転した値動きをする
1357 日経ダブルインバース上場投信日経平均の動きの2倍の逆転した値動きをする

大体2倍の銘柄が多いですがトリプルと言った3倍などもあります。
高い倍率のブルベアファンドのメリットとして短期の株価指数の少しの動きで大きな利益を得ることができることが挙げられますが、裏を返せばリスクも倍になるという事なので注意が必要です。

より詳細に値動きと損益に関して知りたいかたはコチラをご覧ください。

ブル・ベアファンドをの自動裁定を実装する

選択銘柄

  • 1321 : 日経225連動型上場投資信託
    • これを今回のブルベアの対応する株価指標とします。
  • 1570 : 日経平均レバレッジ上場投信 (ブル型)
  • 1357 : 日経ダブルインバース上場投信 (ベア型)

以下各銘柄はそれぞれ株価指標・ブル・ベアと呼称します。

指標

今回は特に選定せず実装が容易い二つを選ぶました。
ここではブルベアの実装をメインとするので各指標の実装と理論についてはリンクの方を参照してください。
RCIに関してはインターン生の佐々木さん(@akihirosasaki)の実装を借りさせていただきました。

  • RCI : (売られすぎ)-100 〜 100(買われすぎ)で判断。実装とその理論についてはコチラ
  • RSI : (売られすぎ)0 〜 100(買われすぎ)で判断。実装とその理論についてはコチラ

アルゴリズム

とりあえずははじめの一歩なのでアルゴリズムは単純にしました。

  1. 上記の各指標を株価指標のみで計算し株価指標が上昇するか下落するかを予測
  2. 予測結果の元
    1. 上昇する場合ブルを買い、ベアを売る
    2. 下落する場合ブルを売り、ベアを買う

実装

今回ははじめの一歩ですのでQuantX Factoryの使い方も含めて説明していきます、既に理解されている方は読み飛ばしつつでお願いします。

コードはここをスタートとして書いていきましょう。

スクリーンショット 2019-02-15 13.33.02.png

cloneボタンを押して好きな名前をつけてコピーを作成して下さい。

ここからも直接コピーできます
#必要なライブラリの読み込み
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": {
        		"columns": [
        		],
        		"symbols": [
        			
        		]
        	}
        
      }
    )

    def _my_signal(data):
      buy_sig = 
      sell_sig = 
      return {
        "buy:sig":buy_sig,
        "sell:sig":sell_sig,
      }

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

def handle_signals(ctx, date, current):
    '''
    current: pd.DataFrame
    '''
    
    # ctx.logger.debug(df)
    
    # ctx.logger.debug(df["buy:sig"])
    # ctx.logger.debug(df["sell:sig"])
    # 買いシグナル
   
    # 売りシグナル
    

銘柄と終値などの読み込み

まずは今回使う銘柄と終値などのデータを読み込めるようにしましょう。

24行目から

"jp.stock": {
        		"symbols": [
        			
        		],
        		"columns": [
        		  
        		]
        	}

の様なコードがあると思います、このコードを以下のコードにしましょう。(コピペを推奨します)

"jp.stock": {
        		"symbols": [
        			"jp.stock.1321", #日経225連動型上場投資信託
        			"jp.stock.1357", #日経ダブルインバース上場投信 bear
        			"jp.stock.1570", #日経平均レバレッジ上場投信 bull
        		],
        		"columns": [
        		  "close_price_adj"
        		]
        	}

このコードは

  • "jp.stock" : 日本株
  • "symbols" : 銘柄は指定した三つの銘柄コードのもの
  • "columns" : 値は今回終値の調整値だけを用いるので "close_price_adj"
    となります。

指標の計算とシグナル出し

次はRCI、RSIを用いた指標計算に移ります。

38行目あたりの def _my_signal(data) から、ここは買い売りシグナル(今回は日経平均の上昇下落シグナル)を生成する部分となります。

def _my_signal(data):
      buy_sig = 
      sell_sig = 
      return {
        "buy:sig":buy_sig,
        "sell:sig":sell_sig,
      }

このコードを以下の様に書き換えましょう

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)
      return {
        "buy:sig":buy_sig,
        "sell:sig":sell_sig,
        "rci9:g2":rci9,
        "rsi7:g2":rsi7,
      }

順を追って説明すると

cp = data["close_price_adj"].fillna(method='ffill')
# データ"close_price_adj"の欠損を無くし使えるデータに整形
# ctx.logger.debug(cp) #データの中身が気になる方はこの行の先頭の#を外して実行してみて下さい。
rci9 = pd.DataFrame(0, index=cp.index, columns=cp.columns)
# 指標RCIの計算結果を保存するための容れ物を用意します、容れ物の形状はcpと同じにしています。
rsi7 = pd.DataFrame(0,index=cp.index, columns=cp.columns)
# 指標RSIの計算結果を保存するための容れ物を用意します、容れ物の形状はcpと同じにしています。

となり...

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)
#指標RCI,RSIを計算する関数get_rci(),ta.RSI()を呼び出し各銘柄においてfor分により計算しています。
# ctx.logger.debug(rci9)
# ctx.logger.debug(rsi7) #各指標RSI, RCIがどの様な値になっているか確認したい場合先頭の#を外して実行してみて下さい。

今回は株価指標の銘柄においてのみ格指標を計算すれば良いのですが今後拡張することを考え計算しておきます。ちなみにここではRCI, RSIそれぞれ9日、7日の期間で計算しています。

各指標の値で計算したのち...

buy_sig= (rci9 < -80) | (rsi7 < 30)
# rci9が-80より小さい、もしくはrsi7が30より小さい時買いシグナルを出す(上昇すると予測)
sell_sig= (rci9 > 80) | (rsi7 > 70)
# rci9が80より大きい、もしくはrsi7が70より大きい時売りシグナルを出す(下落すると予測)
return {
  "buy:sig":buy_sig,
  "sell:sig":sell_sig,
  "rci9:g2":rci9,
  "rsi7:g2":rsi7,
}
#デバッグや結果に描画などでも使える様にreturnで返す

ここまでで株価指標の上昇と下落の予測シグナルを出すことができる様になりました。

ちなみに61行目の ctx.regist_signal("my_signal", _my_signal) でこのシグナルを登録しています。これをしなければ後で使えないのでしっかり登録しましょう。

シグナルの処理

ここからが本題です、63行目から def handle_signals(ctx, date, current): 以降を書いていきます。ここはデータ構造が少し複雑になるので順を追ってコードを書いていきます。

まず、

'''
current: pd.DataFrame
'''

「 current: pd.DataFrame...? 」

となる方もいるかと思いますので説明いたします、先ほども41行目あたりで出てきましたpd.DataFrameですがこれは**"容れ物"です、が、ここの"容れ物"**少し41行目のものとは訳が違います。

というと、この current という容れ物には既に中に色々入っています。
試しに ctx.logger.debug(current) を追加、以下のコードで実行し current 中身を確認しましょう。

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

以下の様なログが出てきたと思います。

スクリーンショット 2019-02-15 15.25.27.png

ここでは上昇予測である買いシグナル(buy:sig)と下降予測である売りシグナル(sell:sig)
を用いたいので current["buy:sig"] , current["sell:sig"]で指定します。

ただし今回は株価指標である"jp.stock.1321"の各シグナルのみ用いるので
current["buy:sig"][0], current["sell:sig"][0]という指定になります。
ちなみにここでの [0] とは以下のDataFrameの0行目(株価指標のシグナル)を指定していることになります。

スクリーンショット 2019-02-15 15.38.47.png

これを踏まえてコードを以下の様に書きます。

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")      
    

それぞれ説明すると、

bear = ctx.getSecurity("jp.stock.1357")
#指定したベアの銘柄のオブジェクトをbearとして保存します、あとで注文に用います。
bull = ctx.getSecurity("jp.stock.1570")
#指定したブルの銘柄のオブジェクトをbullとして保存します、あとで注文に用います。

こうして取引する銘柄を指定した後

# 買いシグナル
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がTrueの時、つまりブルベアが対応する株式指標の銘柄が値上がりと予測
    • bull : 2倍値上がりするであろうブル銘柄の総保有額が総資産の10%となる様に買い
    • bear : 2倍値下がりするであろうベア銘柄を総保有額が総資産の0%となる様に売る(全売り)
# 売りシグナル
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")
  • df_buyがTrueの時、つまりブルベアが対応する株式指標の銘柄が値下がりと予測
    • bull : 2倍値下がりするであろうブル銘柄を総保有額が総資産の0%となる様に売る(全売り)
    • bear : 2倍値上がりするであろうベア銘柄の総保有額が総資産の10%となる様に買い

そのほかの注文方法は公式ドキュメント参考にしてください

やっと完成です!
一応完成コードはコチラ

結果

スクリーンショット 2019-02-15 16.19.42.png

評価・考察

  • 結果からわかる様に下落が連続的に続く場面に関して一定の利益を上げている
  • 日経平均が細かく上下を繰り返すと振り回されて損失を出すことが多くなる
  • 各種指標RSI・RCIの計算期間が短すぎると日経平均に過敏に反応してしまう?

今後の展望

  • ブルベアの特徴である短期トレードに強い指標を選別する。
  • 利確・損切り機能をつけて安定性をつける。

##終わりに
今回は販売可能なレベルを目指しブルベアアルゴリズムの実装を目指しました。
今後開発の進捗を報告する予定ですのでよろしくお願いいたします。

##宣伝

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

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

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

materials

bull-bear:Designed by Rawpixel.com

12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?