LoginSignup
2
2

More than 5 years have passed since last update.

CCIを別の指標と掛け合わせてみた

Last updated at Posted at 2018-12-31

はじめに

今回はQuantXでCCIを使った簡単な逆張りアルゴリズムを作ってみた。の続きの記事です。

今回やったこと

金融指標を二つだけでなく、3つ4つ組み合わせてみると、トレンド転換など精度の高いアルゴリズムが組めるのではないか? 実際にトレーダーが使用しているインジゲーターのようなものがQuantX上で実装できるのではないかと思い、今回作りました。

MT4インジゲーター的なアルゴリズムを作ってみる

銘柄はROEが高いもの、また成長率が高いものを入れています。
今回は銘柄は固定です。
特に凝ったことはしてないのでコードの解説は今回割愛させていただきます。

その1 CCI,RSI,Bollinger bandを組み合わせてみた

シグナル条件

CCI

・-100%以下から-100%以上に突き抜けたら買いシグナル
・100%以上から100%以下に突き抜けたら売りシグナル

RSI

・30%以下で買いシグナル
・70%以上で売りシグナル

Bollinger band

・-2σ線が終値にタッチしたら買いシグナル
・2σ線が終値にタッチしたら売りシグナル

コード

cci-rsi-bol.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.6942",
            "jp.stock.2721",
            "jp.stock.9263",
            "jp.stock.6556",
            "jp.stock.3092",
            "jp.stock.4380",
            "jp.stock.3856",
            "jp.stock.2930",
            "jp.stock.2146",
            "jp.stock.3445",
            "jp.stock.6065",
            "jp.stock.8927",
            "jp.stock.2371",
            "jp.stock.3765",
            "jp.stock.3932",
            "jp.stock.5542",
            "jp.stock.3779",
            "jp.stock.2127",
            "jp.stock.2211",
            "jp.stock.8167",
            "jp.stock.3064",
            "jp.stock.3276",
            "jp.stock.8918",
            "jp.stock.2928",
            "jp.stock.6071",
            "jp.stock.1434",
            "jp.stock.3676",
            "jp.stock.3825",
            "jp.stock.6067",
            "jp.stock.1333", 
            "jp.stock.3267",
            "jp.stock.1814", 
            "jp.stock.4521", 
            "jp.stock.4503", 
            "jp.stock.5194", 
            "jp.stock.5391", 
            "jp.stock.5940", 
            "jp.stock.7226", 
            "jp.stock.7956", 
            "jp.stock.3092", 
            "jp.stock.7148", 
            "jp.stock.9115", 
            "jp.stock.9508", 
            "jp.stock.2371", 
            "jp.stock.3765", 
            "jp.stock.6533", 
            "jp.stock.4587", 
            "jp.stock.4536", 
            "jp.stock.3064", 
            "jp.stock.1808", 
          ],
          "columns": [
            "open_price_adj",    # 始値(株式分割調整後)
            "high_price_adj",    # 高値(株式分割調整後)
            "low_price_adj",     # 安値(株式分割調整後)
            "close_price",        # 終値
            "close_price_adj",    # 終値(株式分割調整後) 
          ] 
        }
      }
    )

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


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

      df_cci_buy_sig = (df_cci > -100.0 ) & (df_cci.shift(1) < -100.0)
      df_cci_sell_sig = (df_cci < 100.0) & (df_cci.shift(1) > 100.0) 

      #RSIの設定
      cp = data["close_price_adj"].fillna(method="ffill")
      rsi = pd.DataFrame(data=0,columns=[], index=cp.index)
      for (sym,val) in cp.items():
        rsi[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod=9)
      df_rsi_buy_sig = rsi < 30
      df_rsi_sell_sig = rsi > 70

      #Bollinger bandの設定
      cp = data["close_price_adj"].fillna(method="ffill")

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

      bb_buy_sig = pd.DataFrame(data=0,columns=[], index=cp.index)
      bb_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=14,
          nbdevup=2,
          nbdevdn=2,
          matype=0)

        lband[sym] = lowerband[sym]
        uband[sym] = upperband[sym]
        bb_buy_sig[sym] = lowerband[sym] / cp[sym]
        bb_sell_sig[sym] = cp[sym] / upperband[sym]

        df_bb_buy_sig = bb_buy_sig >= 1.002
        df_bb_sell_sig = bb_sell_sig >= 0.99

      # #売買のサインの定義
      buy_sig = ((df_cci_buy_sig) 
       & (df_rsi_buy_sig)
       | (df_bb_buy_sig)
      )

      sell_sig = ((df_cci_sell_sig)
       & (df_rsi_sell_sig)
       | (df_bb_sell_sig)
      )

      return {
        "CCI": df_cci,
        "rsi": rsi,
        "upperband:price": uband,
        "lowerband:price": lband,
        "buy:sig":buy_sig,
        "sell:sig":sell_sig, 
        }


    # シグナル登録 memo 参照
    ctx.regist_signal("my_signal", _mysignal)


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

    done_syms = set([])
    for (sym,val) in ctx.portfolio.positions.items():
        returns = val["returns"]
        #ctx.logger.debug("%s %f" % (sym, returns))
        if returns < -0.10:
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="損切り(%f)" % returns)
          done_syms.add(sym)
        elif returns > 0.10:
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="利益確定売(%f)" % returns)
          done_syms.add(sym)

    # 買いシグナル
    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)

開発コードのリンクはコチラ

結果

スクリーンショット 2018-12-28 18.46.19.png

感想

銘柄選別したから結果がよかったと思います。
2018年の10月くらいまですごく成績がいいのにそこから下落気味。
MaxDrawdownを比較的高めであるのが気になります。

その2 CCI,ADX,MACD,RSIを組み合わせてみた

シグナル条件

CCI

・-100%以下から-100%以上に突き抜けたら買いシグナル
・100%以上から100%以下に突き抜けたら売りシグナル

MACD

・MACDがシグナルを下から上へクロスしたポイント(ゴールデンクロス)で買いサイン
・MACDがシグナルを上から下へクロスしたポイント(デッドクロス)で売りサイン

RSI

・25%以下で買いシグナル
・75%以上で売りシグナル

ADX

・ADXが45%を超えたら売買シグナル

コード

cci-adx-macd-rsi.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.6942",
            "jp.stock.2721",
            "jp.stock.9263",
            "jp.stock.6556",
            "jp.stock.3092",
            "jp.stock.4380",
            "jp.stock.3856",
            "jp.stock.2930",
            "jp.stock.2146",
            "jp.stock.3445",
            "jp.stock.6065",
            "jp.stock.8927",
            "jp.stock.2371",
            "jp.stock.3765",
            "jp.stock.3932",
            "jp.stock.5542",
            "jp.stock.3779",
            "jp.stock.2127",
            "jp.stock.2211",
            "jp.stock.8167",
            "jp.stock.3064",
            "jp.stock.3276",
            "jp.stock.8918",
            "jp.stock.2928",
            "jp.stock.6071",
            "jp.stock.1434",
            "jp.stock.3676",
            "jp.stock.3825",
            "jp.stock.6067",
            "jp.stock.1333", 
            "jp.stock.3267",
            "jp.stock.1814", 
            "jp.stock.4521", 
            "jp.stock.4503", 
            "jp.stock.5194", 
            "jp.stock.5391", 
            "jp.stock.5940", 
            "jp.stock.7226", 
            "jp.stock.7956", 
            "jp.stock.3092", 
            "jp.stock.7148", 
            "jp.stock.9115", 
            "jp.stock.9508", 
            "jp.stock.2371", 
            "jp.stock.3765", 
            "jp.stock.6533", 
            "jp.stock.4587", 
            "jp.stock.4536", 
            "jp.stock.3064", 
            "jp.stock.1808", 

          ],
          "columns": [
            "open_price_adj",    # 始値(株式分割調整後)
            "high_price_adj",    # 高値(株式分割調整後)
            "low_price_adj",     # 安値(株式分割調整後)
            "close_price",        # 終値
            "close_price_adj",    # 終値(株式分割調整後) 
          ] 
        }
      }
    )

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

      #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=14)

        d_cci[symbol] = cci

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

      df_cci_buy_sig = (df_cci > -100.0 ) & (df_cci.shift(1) < -100.0)
      df_cci_sell_sig = (df_cci < 100.0) & (df_cci.shift(1) > 100.0) 

      #MACDの設定
      d_macd = dict() 
      d_macdsignal = dict()
      d_macdhist = dict() 

      for symbol in data.minor_axis:
        macd, macdsignal, macdhist = ta.MACD(df_close[symbol].values.astype(np.double), fastperiod=12 , slowperiod=26, signalperiod=9)

        d_macd[symbol] = macd
        d_macdsignal[symbol] = macdsignal
        d_macdhist[symbol] = macdhist

      df_macd = pd.DataFrame(d_macd, index=data.major_axis)        
      df_macdsignal = pd.DataFrame(d_macdsignal, index=data.major_axis)        
      df_macdhist = pd.DataFrame(d_macdhist, index=data.major_axis)

      df_goldencross = (df_macd > df_macdsignal) & (df_macd.shift(1) < df_macdsignal.shift(1)) 
      df_deadcross = (df_macd < df_macdsignal) & (df_macd.shift(1) > df_macdsignal.shift(1))

       #ADXの設定
      d_adx = dict()

      for symbol in data.minor_axis:
        adx = ta.ADX(df_high[symbol].values.astype(np.double), 
                                df_low[symbol].values.astype(np.double), 
                                df_close[symbol].values.astype(np.double), 
                                timeperiod=14)

        d_adx[symbol] = adx

      df_adx = pd.DataFrame(d_adx, index=data.major_axis)

      df_adx_sig = df_adx > 45


      #RSI
      cp = data["close_price_adj"].fillna(method="ffill")
      rsi = pd.DataFrame(data=0,columns=[], index=cp.index)
      for (sym,val) in cp.items():
        rsi[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod=14)
      df_rsi_buy_sig = rsi < 25
      df_rsi_sell_sig = rsi > 75

      # #売買のサインの定義
      buy_sig = ((df_cci_buy_sig) 
      & (df_rsi_buy_sig)
      & (df_adx_sig)
      | (df_goldencross)
      )

      sell_sig = ((df_cci_sell_sig)
      & (df_rsi_sell_sig)
      & (df_adx_sig)
      | (df_deadcross)
      )

      return {
        "ADX": df_adx,
        "MACD": df_macd, 
        "MACDSignal": df_macdsignal, 
        "MACDHist": df_macdhist, 
        "CCI": df_cci,
        "buy:sig":buy_sig,
        "sell:sig":sell_sig,        
        }


    # シグナル登録 memo 参照
    ctx.regist_signal("my_signal", _mysignal)


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

    done_syms = set([])
    for (sym,val) in ctx.portfolio.positions.items():
        returns = val["returns"]
        #ctx.logger.debug("%s %f" % (sym, returns))
        if returns < -0.10:
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="損切り(%f)" % returns)
          done_syms.add(sym)
        elif returns > 0.10:
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="利益確定売(%f)" % returns)
          done_syms.add(sym)



    # 買いシグナル
    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)

開発コードのリンクはコチラ

結果

スクリーンショット 2018-12-28 19.01.19.png

感想

1年程全く利益を出せていないのが非常に問題である。
上昇トレンドにはかなり強い?

その3 CCI,RSI,SMA(単純移動平均線)を組み合わせてみた

シグナル条件

CCI

・-100%以下から-100%以上に突き抜けたら買いシグナル
・100%以上から100%以下に突き抜けたら売りシグナル

RSI

・30%以下で買いシグナル
・70%以上で売りシグナル

SMA(単純移動平均線)

・終値の25日移動平均を75日移動平均をratioとし±1.05のところで売買シグナルをだす。

コード

cci-rsi-sma.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.6942",
            "jp.stock.2721",
            "jp.stock.9263",
            "jp.stock.6556",
            "jp.stock.3092",
            "jp.stock.4380",
            "jp.stock.3856",
            "jp.stock.2930",
            "jp.stock.2146",
            "jp.stock.3445",
            "jp.stock.6065",
            "jp.stock.8927",
            "jp.stock.2371",
            "jp.stock.3765",
            "jp.stock.3932",
            "jp.stock.5542",
            "jp.stock.3779",
            "jp.stock.2127",
            "jp.stock.2211",
            "jp.stock.8167",
            "jp.stock.3064",
            "jp.stock.3276",
            "jp.stock.8918",
            "jp.stock.2928",
            "jp.stock.6071",
            "jp.stock.1434",
            "jp.stock.3676",
            "jp.stock.3825",
            "jp.stock.6067",
            "jp.stock.1333", 
            "jp.stock.3267",
            "jp.stock.1814", 
            "jp.stock.4521", 
            "jp.stock.4503", 
            "jp.stock.5194", 
            "jp.stock.5391", 
            "jp.stock.5940", 
            "jp.stock.7226", 
            "jp.stock.7956", 
            "jp.stock.3092", 
            "jp.stock.7148", 
            "jp.stock.9115", 
            "jp.stock.9508", 
            "jp.stock.2371", 
            "jp.stock.3765", 
            "jp.stock.6533", 
            "jp.stock.4587", 
            "jp.stock.4536", 
            "jp.stock.3064", 
            "jp.stock.1808", 
          ],
          "columns": [
            "open_price_adj",    # 始値(株式分割調整後)
            "high_price_adj",    # 高値(株式分割調整後)
            "low_price_adj",     # 安値(株式分割調整後)
            "close_price",        # 終値
            "close_price_adj",    # 終値(株式分割調整後) 
          ] 
        }
      }
    )

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


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

      df_cci_buy_sig = (df_cci > -100.0 ) & (df_cci.shift(1) < -100.0)
      df_cci_sell_sig = (df_cci < 100.0) & (df_cci.shift(1) > 100.0) 

      #RSIの設定
      cp = data["close_price_adj"].fillna(method="ffill")
      rsi = pd.DataFrame(data=0,columns=[], index=cp.index)
      for (sym,val) in cp.items():
        rsi[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod=9)
      df_rsi_buy_sig = rsi < 30
      df_rsi_sell_sig = rsi > 70

      #移動平均線の設定
      m25 = data["close_price_adj"].fillna(method='ffill').rolling(window=25,center=False).mean()
      m75 = data["close_price_adj"].fillna(method='ffill').rolling(window=25,center=False).mean()
      ratio = m25 / m75

      df_sma_buy_sig = ratio < -1.05 
      df_sma_sell_sig = ratio > 1.05

      # #売買のサインの定義
      buy_sig = ((df_cci_buy_sig) 
       & (df_rsi_buy_sig)
       | (df_sma_buy_sig)
      )

      sell_sig = ((df_cci_sell_sig)
       & (df_rsi_sell_sig)
       | (df_sma_sell_sig)
      )

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


    # シグナル登録 memo 参照
    ctx.regist_signal("my_signal", _mysignal)


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

    done_syms = set([])
    for (sym,val) in ctx.portfolio.positions.items():
        returns = val["returns"]
        #ctx.logger.debug("%s %f" % (sym, returns))
        if returns < -0.10:
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="損切り(%f)" % returns)
          done_syms.add(sym)
        elif returns > 0.10:
          sec = ctx.getSecurity(sym)
          sec.order(-val["amount"], comment="利益確定売(%f)" % returns)
          done_syms.add(sym)

    # 買いシグナル
    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)

開発コードのリンクはコチラ

結果

スクリーンショット 2018-12-31 20.05.33.png

感想

結構きれいな右肩上がりでいいアルゴリズムだと思います。
やはり2018年10月に下落したのが大きい。

終わりに

2018年も終わりですね。
年末は日経平均、ダウ平均株価が下がったりと苦労された方も多いかと思います。
エンジニアのみなさんQuantX Factoryでいいアルゴリズムを作りましょう!!
良いお年を〜!!

宣伝

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

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

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

免責注意事項

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

2
2
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
2
2