LoginSignup
4
1

More than 5 years have passed since last update.

QuantXでCCIを使った簡単な逆張りアルゴリズムを作ってみた。

Last updated at Posted at 2018-12-26

まず自己紹介から

某理系大学で電気を専攻してる大学2年生のkatakyoです。
Pythonも金融の知識もガチ初心者です。
最近SmartTrade社の方でインターン始めました。

[宣伝] QuantX Factoryって何?

URL:QuantX Factory ホームページ
開いてみるとこんな画面が出てきます。
スクリーンショット 2018-11-28 18.14.48.png

QuantX Factoryとは
ブラウザ上で動かせる、株式や仮想通貨などの売買ルール(アルゴリズム)の作成、販売ができるシステムトレードプラットフォームです。pythonというプログラミング言語を用いて動かします。

開発デモを体験するというボタンをクリックするとログインしてくださいという画面がでます。
Facebook,Googleアカウントなどで簡単にログインできるのでログインしちゃいましょう!

ログインが終わると何やら難しそうなコードが出てきます。スクリーンショット 2018-11-29 9.22.17.png

これは移動平均線という金融指標を用いたアルゴリズムです。
デフォルトですでに完成されています!

移動平均線について知りたい方はコチラ

Pythonでコードをいじり、バックテストをすることで株式売買のシミュレーションができちゃいます!

今回はCCIという指標を使ってみる。

今回の紹介するアルゴリズムはCCIというテクニカル手法を用いたアルゴリズムになります。

そもそもCCIって何?

CCI(Commodity Channel Index、コモディティ・チャネル・インデックス)は、1980年にアメリカの投資家「ドナルド・M・ランバート」が開発した、オシレーター系のテクニカル指標です。
オシレータ系?何それ?って人はコチラ
元々は商品市場向けに作られた指標です。
CCIは、ざっくり言うと「現在価格がその日の高値・安値・終値の平均値の移動平均から、どれくらい離れているのか」を表す指標です。

CCIの計算式

N日間のCCIの計算式は以下のようになります。

N日間のCCI計算式
中値=(高値+安値+終値)÷3
中値平均=N日間の中値の平均
絶対偏差=(中値-中値平均)の絶対値
CCI=(中値-中値平均)÷(0.015×(N日間の絶対偏差の平均))

正直複雑ですね。
ですが、TA-Libライブラリーでコチラも簡単に計算できます!!
ここで注意なのがCCIの指標には同じオシレータ系指標のRSIやストキャスティクスのように天井、底がありません!!
つまり、±200%、±300%のような大きな数値になってしまったりします。

CCIの基本的な売買サイン

CCIは順張りでも逆張りでも使えるテクニカル指標です。
今回はCCIでよく使われる基本的な逆張りのトレード方法に絞って説明していきます。
基本的にはCCIは0(ゼロライン)の交差でトレンドの転換となります。
0より上で上昇トレンド、0より下で下降トレンドと判断ができます。
そして、RSIのように±100%で売られすぎ、買われ過ぎを判定するのがCCIの逆張りの基本的な考え方です。
(株)カカクコムの直近三ヶ月の値を例にとって見てみます。

CCI 基本.png

図の赤い折れ線グラフがCCIの値を示しています。縦の赤のラインが買いシグナル、青のラインが売りシグナルを出している部分です。
図にも記述しているように、CCIが100%を超えていると市場で買われ過ぎているので、売りサインを出す。CCIが-100%以下だと市場で売られ過ぎているので、買いサインを出すようにします。

CCIのアルゴリズムを実装してみる

アルゴリズムを記述します。

cci.py
import talib as ta 
import pandas as pd 
import numpy as np 

  #関数の初期化
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": [
            "high_price_adj",    # 高値(株式分割調整後)
            "low_price_adj",     # 安値(株式分割調整後)
            "close_price_adj",    # 終値(株式分割調整後) 
          ] 
        }
      }
    )

    def _my_signal(data):

      df_high = data["high_price_adj"].fillna(method='ffill')
      df_low = data["low_price_adj"].fillna(method='ffill')
      df_close = data["close_price_adj"].fillna(method='ffill')

      #CCIの設定
      d_cci = dict()

      for symbol in data.minor_axis:
        cci = ta.CCI(df_high[symbol].values.astype(np.double), 
                                df_low[symbol].values.astype(np.double), 
                                df_close[symbol].values.astype(np.double), 
                                timeperiod=5)

        d_cci[symbol] = cci

      df_cci = pd.DataFrame(d_cci, index=data.major_axis)

      #売買のサインの定義
      buy_sig  = (df_cci > -100.0 ) & (df_cci.shift(1) < -100.0)
      sell_sig = (df_cci < 100.0) & (df_cci.shift(1) > 100.0) 

      return {
        "CCI": df_cci,
        "buy:sig":buy_sig,
        "sell:sig":sell_sig,        
        }


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


def handle_signals(ctx, date, current):
    df = current.copy()

    # 買いシグナル
    df_long = df[df["buy:sig"]]
    if not df_long.empty:
      for (sym, val) in df_long.iterrows(): 
        sec = ctx.getSecurity(sym)
        msg = "買いシグナル"
        sec.order_target_percent(0.10, comment= msg)

    # 売りシグナル
    df_sell = df[df["sell:sig"]]
    if not df_sell.empty:
      for (sym, val) in df_sell.iterrows(): 
        sec = ctx.getSecurity(sym)
        msg = "売りシグナル"
        sec.order_target_percent(0, comment= msg)

基本的なコードの記述方法は、以前自分が書いたMACDの記事と類似しているのでポイントだけ絞って説明していきます。

コードの説明

データ量の決定及び欠損値の補完

今回CCIの指標には、中値を計算する際、高値、安値、終値をデータ量として使いますので、

cci.py
"columns": [
            "high_price_adj",    # 高値(株式分割調整後)
            "low_price_adj",     # 安値(株式分割調整後)
            "close_price_adj",    # 終値(株式分割調整後) 
          ] 

とデータを引っ張ってきます。

欠損値が含まれているので、

cci.py
def _mysignal(data):

      df_high = data["high_price_adj"].fillna(method='ffill')
      df_low = data["low_price_adj"].fillna(method='ffill')
      df_close = data["close_price_adj"].fillna(method='ffill')

mysignal生成部分でfillna(method='ffill')を用いデータ量の欠損値の補完を行います。

CCIの設定

今回は設定期間を5日としてCCIの計算をTA-Libを用いて行います。

cci.py
#CCIの設定
      d_cci = dict()

      for symbol in data.minor_axis:
        cci = ta.CCI(df_high[symbol].values.astype(np.double), 
                                df_low[symbol].values.astype(np.double), 
                                df_close[symbol].values.astype(np.double), 
                                timeperiod=5)

        d_cci[symbol] = cci

      df_cci = pd.DataFrame(d_cci, index=data.major_axis)

CCIのデータの容れ物ができました。
他のTA-Libのライブラリーも同様の手順でできるので試してみてください!!

TA-Libライブラリーはコチラ

売買シグナル生成

cci.py
      #売買のサインの定義
      buy_sig  = (df_cci > -100.0 ) & (df_cci.shift(1) < -100.0)
      sell_sig = (df_cci < 100.0) & (df_cci.shift(1) > 100.0)    

今回CCIが-100%以下から-100%以上に突き抜けたとき買いシグナル、100%以上から-100%以下に突き抜けたとき売りシグナルとしました。
shiftメソットを使い、CCIの各データを前日のデータと比較させています。

データの可視化

返り値を設定し、データの可視化を行います。

cci.py
 return {
        "CCI": df_cci,
        "buy:sig":buy_sig,
        "sell:sig":sell_sig,        
        }

今まで解説してきた関数「_my_signal」を登録して使うことを明示します。

cci.py
    # シグナル登録 
    ctx.regist_signal("my_signal", _mysignal)

株の売買方法の設定

cci.py
def handle_signals(ctx, date, current):
    df = current.copy()

    # 買いシグナル
    df_long = df[df["buy:sig"]]
    if not df_long.empty:
      for (sym, val) in df_long.iterrows(): 
        sec = ctx.getSecurity(sym)
        msg = "買いシグナル"
        sec.order_target_percent(0.10, comment= msg)

    # 売りシグナル
    df_sell = df[df["sell:sig"]]
    if not df_sell.empty:
      for (sym, val) in df_sell.iterrows(): 
        sec = ctx.getSecurity(sym)
        msg = "売りシグナル"
        sec.order_target_percent(0, comment= msg)

今回はorder_target_percentを買いのシグナルで10%、売りシグナルの時0%としました。

バックテスト結果

さてバックテスト結果は
スクリーンショット 2018-12-26 13.22.55.png

3年で50%とまあまあですね〜
前日の日経平均株価の急落で15%程損してる気がします、、、

CCIのメリット・デメリット

CCIのメリット

CCIはシグナル発生がとても早く、トレンド転換をいち早く察知するのに向いています。そのため売られ過ぎ、買われ過ぎかをすぐに見分けることができます。
また、オシレータ系の指標ながら、トレンドの察知もできるので、逆張りだけでなく順張りのトレードにも使用できます。

CCIのデメリット

CCIは反応がとても早いのが特徴ですが、それ故にダマシが多いのが特徴です。
また数値に天井と底がないため、さらにダマシが増加する恐れがあります。

終わりに

他の指標と組み合わせダマシを回避し、逆張りだけでなく順張りのアルゴリズムを今後記述していきます!!(乞うご期待!!)

宣伝

勉強会やってます!
日時:毎週金曜日19時〜
場所:神田 千代田共同ビル4階 SmartTrade社オフィス
内容:初心者(プログラミングってものを知らなくてもOK)向けに初心者(私とか)がこんな内容をハンズオン(一緒にやる事)で解説しています
備考:猛者の方も是非御鞭撻にいらして下さい、そして開発・伝導者になりましょう!

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

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

免責注意事項

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

4
1
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
4
1