#はじめに
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になることを注意する)
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に対応させたコードになります。
アルゴリズムの詳しい内容はそれぞれの記事をご覧ください。