Help us understand the problem. What is going on with this article?

QuantXでボリンジャーバンドを用いたシステムトレード

More than 1 year has passed since last update.

自己紹介

この度Smart Tradeでインターンすることになったゴンです。

ゴンについて、知りたい方はこちら。

https://www.investor-gon.com/

初めての記事はこちら(QuantXを初めてみて)
https://qiita.com/investor-gon/items/2b1952ae5c2c88334c74

何を作るのか(完成目標)



日経平均寄与率上位10位の銘柄(2018年12月25日)を用いて、ボリンジャーバンドが-2σに近いときに買い、+2σに近いときに売り、を入れるアルゴリズムを作る!!!

とりあえず動けば良い。

数式で書くとこんな感じ



買い(buy)= \frac{lowerband(-2σ)}{価格} >1.002
\\
売り(sell) =\frac{価格}{upperband(+2σ)} >0.99




ボリンジャーバンドとは、移動平均を表す線と、その上下に値動きの幅を示す線を加えた指標のことをいいます。

1980年ころにジョン・ボリンジャー氏が考案した指標で、「価格の大半がこの帯(バンド)の中に収まる」という統計学を応用したテクニカル指標のひとつです。

1次標準偏差、1σ=約68.3%
2次標準偏差、2σ=約95.5%
3次標準偏差、3σ=約99.7%

まずは完成コード(コピペ、どうぞ)

############################################################################
# 日経225 ボリンジャーバンド

  #ライブラリーの設定
import pandas as pd
import talib as ta
import numpy as np


def initialize(ctx):
    # 設定
    ctx.logger.debug("initialize() called")
    ctx.configure(
      target="jp.stock.daily",
      channels={          # 利用チャンネル
        "jp.stock": {
          "symbols": [
            "jp.stock.9983", #ファーストリテイリング
            "jp.stock.9984", #ソフトバンク
            "jp.stock.6954", #ファナック
            "jp.stock.9433", #KDDI
            "jp.stock.8028", #ファミリーマート
            "jp.stock.8035", #東京エレクトロン
            "jp.stock.4543", #テルモ
            "jp.stock.6367", #ダイキン
            "jp.stock.6971", #京セラ
            "jp.stock.9735", #セコム
          ],
          "columns": [
            #"open_price_adj",    # 始値(株式分割調整後)
            #"high_price_adj",    # 高値(株式分割調整後)
            #"low_price_adj",     # 安値(株式分割調整後)
            "close_price",        # 終値
            "close_price_adj",    # 終値(株式分割調整後) 
            "volume_adj",         # 出来高
            "txn_volume",         # 売買代金
          ]
        }
      }
    )

    #売買シグナル生成部分
    def _TALIB_CALL(data):

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

      upperband = {}
      middleband = {}
      lowerband = {}

      buy_sig = pd.DataFrame(data=0,columns=[], index=cp.index)
      sell_sig = pd.DataFrame(data=0,columns=[], index=cp.index)
      uband = pd.DataFrame(data=0,columns=[], index=cp.index)
      lband = pd.DataFrame(data=0,columns=[], index=cp.index)

      for (sym,val) in cp.items():
        upperband[sym], middleband[sym], lowerband[sym] = ta.BBANDS(cp[sym].values.astype(np.double),
          timeperiod=25,
          nbdevup=2,
          nbdevdn=2,
          matype=0)

        lband[sym] = lowerband[sym]
        uband[sym] = upperband[sym]
        #-2σに近いときに買い
        buy_sig[sym] = lowerband[sym] / cp[sym]
        #+2σに近いときに売り
        sell_sig[sym] = cp[sym] / upperband[sym]


      return {
        "upperband:price": uband,
        "lowerband:price": lband,
        "bb_buy:sig": (buy_sig >= 1.002),
        "bb_sell:sig": (sell_sig >= 0.99),
      }


    # シグナル登録
    ctx.regist_signal("TALIB", _TALIB_CALL)

def handle_signals(ctx, date, current):

    done_syms = set([])

    ratio = current["bb_buy:sig"]
    df = ratio[ratio == 1.0]
    for (sym,val) in df.items():
        if sym in done_syms:
          continue

        sec = ctx.getSecurity(sym)
        sec.order_target_percent(0.15, comment="シグナル買(%f)" % val)
        pass

    ratio = current["bb_sell:sig"]
    df = ratio[ratio == 1.0]
    for (sym,val) in df.items():
        if sym in done_syms:
          continue

        sec = ctx.getSecurity(sym)
        sec.order_target_percent(0, comment="シグナル売(%f)" % val)
        pass

    pass


結果はこちら

スクリーンショット 2018-12-27 12.03.57.png


三年間で36%のプラスですね。うお。結構プラスで嬉しいって感じ。

ちなみに、

アルファ値は、アルゴリズムの収益率から市場全体の動き(ベンチマーク)に連動したリターンを差し引いたもので、本数値が高いほど、ベンチマークの収益率を上回り、それだけリターンが高いことを意味します。


ベータ値とは、アルゴリズムの収益が証券市場全体の動きに対してどの程度敏感に反応して変動するかを示す数値です。この数値が高いほど、証券市場全体の動きに連動した変動幅(増減幅)が大きいことを意味します。

解説

ライブラリーの選択

#ライブラリーの設定

import pandas as pd
import talib as ta
import numpy as np

使うライブラリーをあらかじめ設定しておきます。

日本株式の選択

def initialize(ctx):
    # 設定
    ctx.logger.debug("initialize() called")
    ctx.configure(
      target="jp.stock.daily",
      channels={          # 利用チャンネル
        "jp.stock": {
          "symbols": [
            "jp.stock.9983", #ファーストリテイリング
            "jp.stock.9984", #ソフトバンク
            "jp.stock.6954", #ファナック
            "jp.stock.9433", #KDDI
            "jp.stock.8028", #ファミリーマート
            "jp.stock.8035", #東京エレクトロン
            "jp.stock.4543", #テルモ
            "jp.stock.6367", #ダイキン
            "jp.stock.6971", #京セラ
            "jp.stock.9735", #セコム
          ],
          "columns": [
            #"open_price_adj",    # 始値(株式分割調整後)
            #"high_price_adj",    # 高値(株式分割調整後)
            #"low_price_adj",     # 安値(株式分割調整後)
            #"close_price",        # 終値
            "close_price_adj",    # 終値(株式分割調整後) 
            #"volume_adj",         # 出来高
            #"txn_volume",         # 売買代金
          ]
        }
      }
    )




今回使用する日経平均寄与率上位10銘柄を持ってきます。


また、使う値は、日足の終値(株式分割調整後) です。

取引手法

 #売買シグナル生成部分
    def _TALIB_CALL(data):

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

      upperband = {}
      middleband = {}
      lowerband = {}

      buy_sig = pd.DataFrame(data=0,columns=[], index=cp.index)
      sell_sig = pd.DataFrame(data=0,columns=[], index=cp.index)
      uband = pd.DataFrame(data=0,columns=[], index=cp.index)
      lband = pd.DataFrame(data=0,columns=[], index=cp.index)

      for (sym,val) in cp.items():
        upperband[sym], middleband[sym], lowerband[sym] = ta.BBANDS(cp[sym].values.astype(np.double),
          timeperiod=25,
          nbdevup=2,
          nbdevdn=2,
          matype=0)

        lband[sym] = lowerband[sym]
        uband[sym] = upperband[sym]
        #-2σに近いときに買い
        buy_sig[sym] = lowerband[sym] / cp[sym]
        #+2σに近いときに売り
        sell_sig[sym] = cp[sym] / upperband[sym]


      return {
        "upperband:price": uband,
        "lowerband:price": lband,
        "bb_buy:sig": (buy_sig >= 1.002),
        "bb_sell:sig": (sell_sig >= 0.99),
      }


    # シグナル登録
    ctx.regist_signal("TALIB", _TALIB_CALL)

今回使用する値をcpに代入して、ボリンジャーバンド(+2σと-2σ)を求めます。
今回は、日足は25日、+-2σで指定しています。
(コード的にはここ。timeperiod=25,nbdevup=2,nbdevdn=2,)



そして、株価とボリンジャーバンドの比を求めて売買を決定しています。


返り値としてボリンジャーバンドの価格と買いシグナルと売りシグナルを設定します。


そしてシグナルを登録しておく。

ボリンジャーバンドとは、移動平均を表す線と、その上下に値動きの幅を示す線を加えた指標のことをいいます。

1980年ころにジョン・ボリンジャー氏が考案した指標で、「価格の大半がこの帯(バンド)の中に収まる」という統計学を応用したテクニカル指標のひとつです。

1次標準偏差、1σ=約68.3%
2次標準偏差、2σ=約95.5%
3次標準偏差、3σ=約99.7%

数量選択

def handle_signals(ctx, date, current):

    done_syms = set([])

    ratio = current["bb_buy:sig"]
    df = ratio[ratio == 1.0]
    for (sym,val) in df.items():
        if sym in done_syms:
          continue

        sec = ctx.getSecurity(sym)
        sec.order_target_percent(0.15, comment="シグナル買(%f)" % val)
        pass

    ratio = current["bb_sell:sig"]
    df = ratio[ratio == 1.0]
    for (sym,val) in df.items():
        if sym in done_syms:
          continue

        sec = ctx.getSecurity(sym)
        sec.order_target_percent(0, comment="シグナル売(%f)" % val)
        pass

    pass

あとはさっき設定した買いシグナル売りシグナルで売買をするのですが、
詳しくはこちらなのですが、今回使用したやつだけ載せておきます。

https://determined-feynman-fb5a05.netlify.com/ja/api.html#Security




 
order_target_percent(amount, comment): void

この銘柄の総保有額が総資産評価額(現金+保有ポジション評価額)に対して指定の割合となるように注文を行ないます。 amountには割合(例:5%なら0.05)を指定します。


つまり、今回の場合は、総資産の15%の買いを入れていて、資金がなくなったらおしまいという感じで資金管理しています。

結構攻めたトレードで成果を上げた感じですね。

それでは今回はこの辺で。

investor-gon
トレーダー /株/為替/仮想通貨 /投資歴3年/トレードペディア運営 累計15万PV /慶應義塾大学/ わかりやすく株や為替・仮想通貨・自動運用などの情報を発信しています。
https://www.investor-gon.com/
quantxtrade_inc
株式会社Smart TradeはQuantXをはじめ、AIxITを活用した金融サービスを次々に世の中に打ち出していきます。
https://smarttrade.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away