#はじめに
今回はQuantXでCCIを使った簡単な逆張りアルゴリズムを作ってみた。の続きの記事です。
#今回やったこと
金融指標を二つだけでなく、3つ4つ組み合わせてみると、トレンド転換など精度の高いアルゴリズムが組めるのではないか? 実際にトレーダーが使用しているインジゲーターのようなものがQuantX上で実装できるのではないかと思い、今回作りました。
#MT4インジゲーター的なアルゴリズムを作ってみる
銘柄はROEが高いもの、また成長率が高いものを入れています。
今回は銘柄は固定です。
特に凝ったことはしてないのでコードの解説は今回割愛させていただきます。
##その1 CCI,RSI,Bollinger bandを組み合わせてみた
###シグナル条件
####CCI
・-100%以下から-100%以上に突き抜けたら買いシグナル
・100%以上から100%以下に突き抜けたら売りシグナル
####RSI
・30%以下で買いシグナル
・70%以上で売りシグナル
####Bollinger band
・-2σ線が終値にタッチしたら買いシグナル
・2σ線が終値にタッチしたら売りシグナル
###コード
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年の10月くらいまですごく成績がいいのにそこから下落気味。
MaxDrawdownを比較的高めであるのが気になります。
##その2 CCI,ADX,MACD,RSIを組み合わせてみた
###シグナル条件
####CCI
・-100%以下から-100%以上に突き抜けたら買いシグナル
・100%以上から100%以下に突き抜けたら売りシグナル
####MACD
・MACDがシグナルを下から上へクロスしたポイント(ゴールデンクロス)で買いサイン
・MACDがシグナルを上から下へクロスしたポイント(デッドクロス)で売りサイン
####RSI
・25%以下で買いシグナル
・75%以上で売りシグナル
####ADX
・ADXが45%を超えたら売買シグナル
###コード
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)
###感想
1年程全く利益を出せていないのが非常に問題である。
上昇トレンドにはかなり強い?
##その3 CCI,RSI,SMA(単純移動平均線)を組み合わせてみた
###シグナル条件
####CCI
・-100%以下から-100%以上に突き抜けたら買いシグナル
・100%以上から100%以下に突き抜けたら売りシグナル
####RSI
・30%以下で買いシグナル
・70%以上で売りシグナル
####SMA(単純移動平均線)
・終値の25日移動平均を75日移動平均をratioとし±1.05のところで売買シグナルをだす。
###コード
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年10月に下落したのが大きい。
#終わりに
2018年も終わりですね。
年末は日経平均、ダウ平均株価が下がったりと苦労された方も多いかと思います。
エンジニアのみなさんQuantX Factoryでいいアルゴリズムを作りましょう!!
良いお年を〜!!
#宣伝
勉強会やってます! 新年は1/9(水)から!!
日時:毎週金曜日19時〜
場所:神田 千代田共同ビル4階 SmartTrade社オフィス
内容:初心者(プログラミングってものを知らなくてもOK)向けに初心者(私とか)がこんな内容をハンズオン(一緒にやる事)で解説しています
備考:猛者の方も是非御鞭撻にいらして下さい、そして開発・伝導者になりましょう!
もくもく会もやってます!
日時:毎週水曜日18時〜
場所:神田 千代田共同ビル4階 SmartTrade社オフィス
内容:基本黙々と自習しながら猛者の方に質問して強くなっていく会
備考:お菓子と終わりにお酒を飲みながら参加者と歓談できます!
詳細はこちらだよ
Pythonアルゴリズム勉強会HP:https://python-algo.connpass.com/
(connpassって言うイベントサイトに飛びます)
#免責注意事項
このコードや知識を使った実際の取引で生じた損益に関しては一切の責任を負いかねますので御了承下さい