LoginSignup
0
2

More than 3 years have passed since last update.

QuantXで基本的なアルゴリズムを0.0.5エンジンに対応させる

Posted at

はじめに

QuantXでは、2019年5月末に0.0.1から0.0.5へエンジンの改装が行われました。
これまでSmart Trade社インターン生がアルゴリズムに関して様々な記事を書いてきましたが、その多くは旧エンジンの0.0.1についての記事になります。
そこで、今回はQuantXアルゴリズム作成者に向けてにある基本的なアルゴリズムを0.0.5に対応させたコードを紹介したいと思います。

コードの紹介

基本的な変更点

①ライブラリの読み込みに必要に応じて、以下の3つを追加する。

import maron
import maron.signalfunc as sf
import maron.execfunc as ef

②buy_sigとsell_sigについて以下を追加する。

market_sig = pd.DataFrame(data=0.0, columns=cp.columns, index=cp.index)
market_sig[buy_sig == True] = 1.0
market_sig[sell_sig == True] = -1.0
market_sig[(buy_sig ==True) & (sell_sig == True)] =0.0

③returnの部分について、buy_sig とsell_sig を消去して、代わりにmarket_sigを追加する。

"market:sig": market_sig

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

#必要なライブラリの読み込み
import numpy as np
import pandas as pd
import talib as ta
import maron
import maron.signalfunc as sf
import maron.execfunc as ef

#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"
                ]
            }
          }
        )

    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)

      market_sig = pd.DataFrame(data=0.0, columns=cp.columns, index=cp.index)
      market_sig[buy_sig == True] = 1.0
      market_sig[sell_sig == True] = -1.0
      market_sig[(buy_sig ==True) & (sell_sig == True)] =0.0      
      return {
        "rci9:g2":rci9,
        "rsi7:g2":rsi7,
        "market:sig": market_sig,        
      }

    # シグナル登録
    ctx.regist_signal("my_signal", _my_signal)
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")  

上記の基本的な変更点通りに、
4-7行目に①を追加。
56-59に②を追加。
63行目を③のように変更。

QuantX FactoryでMACDのアルゴリズムを作ってみよう。

#ライブラリーの設定
import pandas as pd
import talib as ta
import numpy as np
import maron
import maron.signalfunc as sf
import maron.execfunc as ef

  #関数の初期化
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": [
            #"open_price_adj",    # 始値(株式分割調整後)
            #"high_price_adj",    # 高値(株式分割調整後)
            #"low_price_adj",     # 安値(株式分割調整後)
            #"close_price",        # 終値
            "close_price_adj",    # 終値(株式分割調整後) 
          ] 
        }
      }
    )
      #売買シグナル生成部分
    def _my_signal(data):
      cp = data["close_price_adj"].fillna(method="ffill")

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

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

      #macdの売り買いのサインを定義
      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))

      #売買シグナルの設定
      buy_sig = df_goldencross 
      sell_sig = df_deadcross 
      market_sig = pd.DataFrame(data=0.0, columns=cp.columns, index=cp.index)
      market_sig[buy_sig == True] = 1.0
      market_sig[sell_sig == True] = -1.0
      market_sig[(buy_sig ==True) & (sell_sig == True)] =0.0

      return{
          "MACD": df_macd, 
          "MACDSignal": df_macdsignal, 
          "MACDHist": df_macdhist, 
          "market:sig": market_sig
        }


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

def handle_signals(ctx, date, current):

    df = current.copy()
    market_sig = current["market:sig"]
    # 買いシグナル
    buy = market_sig[market_sig > 0.0]
    if not buy.empty:
      for (sym, val) in buy.items(): 
        #ctx.logger.info(val)
        sec = ctx.getSecurity(sym)
        msg = "買いシグナル"
        sec.order_target_percent(0.10, comment= msg)

    # 売りシグナル
    sell = market_sig[market_sig < 0.0]
    if not sell.empty:
      for (sym, val) in sell.items(): 
        sec = ctx.getSecurity(sym)
        msg = "売りシグナル"
        sec.order_target_percent(0, comment= msg)

上記の基本的な変更点通りに、
5-7行目に①を追加。
85-87行目に②を追加。
93行目を③のように変更。

またそれに加えて、
handle_signal部分において、
103行目に下記を追加する。

market_sig = current["market:sig"]

また、

df_long = df[df["buy:sig"]]

のところを105行目の下記のように変更させる。

buy = market_sig[market_sig > 0.0]

その際、その名前の変更に伴う必要な変更箇所を変更させて対応させる。
更に、

for (sym, val) in buy.itterows():

の部分を

for (sym, val) in buy.items():

107行目上記のようにiterrowsからitemsに変更させる。

sellについても同様の変更を行う。(sellのときは、market_sig が market_sig < 0.0になることを注意する)

RSIっていう指標、長期短期で使ってみた

import pandas as pd
import talib as ta
import numpy as np
import maron
import maron.signalfunc as sf
import maron.execfunc as ef

def initialize(ctx):
    # 設定
    ctx.logger.debug("initialize() called")
    ctx.configure(
      channels={          # 利用チャンネル
        "jp.stock": {
          "symbols": [
            "jp.stock.8840", #大京
            "jp.stock.7897", #ホクシン
            "jp.stock.6138", #ダイジェト
            "jp.stock.8685", #AIG
            "jp.stock.3289", #東急不HD
            "jp.stock.1847", #イチケン
            "jp.stock.9739", #NSW
            "jp.stock.6345", #アイチコーポ
            "jp.stock.7554", #幸楽苑HD
            "jp.stock.2908", #フジッコ
            "jp.stock.8860", #フジ住
            "jp.stock.6436", #アマノ
            "jp.stock.8011", #三陽商
            "jp.stock.8848", #レオパレス
            "jp.stock.7433", #伯東
            "jp.stock.3724", #ベリサーブ
          ],
          "columns": [
            #"open_price_adj",    # 始値(株式分割調整後)
            #"high_price_adj",    # 高値(株式分割調整後)
            #"low_price_adj",     # 安値(株式分割調整後)
            #"volume_adj",         # 出来高
            #"txn_volume",         # 売買代金
            "close_price",        # 終値
            "close_price_adj",    # 終値(株式分割調整後) 
          ]
        }
      }
    )

    def _my_signal(data):
      cp = data["close_price_adj"].fillna(method="ffill")
      rsi_S = pd.DataFrame(data=0,columns=[], index=cp.index)
      rsi_L = pd.DataFrame(data=0,columns=[], index=cp.index)
      for (sym,val) in cp.items():
        rsi_S[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod=9)
        rsi_L[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod=22)
        df_buy = rsi_S[(rsi_S < 20) & (rsi_L < 30)]
        df_sell = rsi_S[(rsi_S > 80) & (rsi_L > 70)]
        #条件にrsi_L  < 30, rsi_L > 70を追加
        market_sig = pd.DataFrame(data=0.0, columns=cp.columns, index=cp.index)
        market_sig[df_buy == True] = 1.0
        market_sig[df_sell == True] = -1.0
        market_sig[(df_buy ==True) & (df_sell == True)] =0.0

      return {
        "rsi_S": rsi_S,
        "rsi_L": rsi_L,
        "market:sig": market_sig
      }

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

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

    done_syms = set([])
    df_buy = current["buy:sig"].dropna()
    df_sell = current["sell:sig"].dropna()
    for (sym,val) in df_buy.items():
        sec = ctx.getSecurity(sym)
        sec.order(sec.unit() * 1, comment="SIGNAL BUY")
        #ctx.logger.debug("BUY: %s,  %f" % (sec.code(), val))

    for (sym,val) in df_sell.items():
        sec = ctx.getSecurity(sym)
        sec.order_target_percent(0, comment="SIGNAL SELL")
        #ctx.logger.debug("SELL: %s,  %f" % (sec.code(), val))

上記の基本的な変更点通りに、
4-6行目に①を追加。
55-58行目に②を追加。
63行目を③のように変更。

以上が、基本的なアルゴリズムにあるアルゴリズムを0.0.5に対応させたコードになります。
アルゴリズムの詳しい内容はそれぞれの記事をご覧ください。

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