【第7回】テクニカル指標による銘柄スクリーニング
December 18, 2020
株価サーバー テクニカル分析 python無一物中Project記事一覧
- 【第1回】株価サーバーの構築の概要
- 【第2回】無尽蔵から株価データの一括取得
- 【第3回】無尽蔵株価データから銘柄毎の時系列データを生成
- 【第4回】無尽蔵から自動で株価データを取得し株価データベースを更新
- 【第5回】出来ず株価の補填と株式分割を自動調整してチャート表示
- 【第6回】mplfinanceを使用してテクニカルチャート表示
- 【第7回】テクニカル指標による銘柄スクリーニング(本記事)
- 【第8回】株価チャート配信サーバー
- 【第9回】チャート形状の認識
はじめに
無一物中Project第1回~第4回の連載で、無尽蔵サイトで公開されている株価データを利用して株価データベースを構築しました。第5回~第6回の連載では、テクニカルチャートを表示してみました。
今回は、構築した株価データベースを使用して銘柄スクリーニングをしてみます。巷のサイトのようにWebベースで操作できる訳でなく、GUIがある訳でもないので、使い勝手は悪いですが、pythonでプログラミングすることにより、相当柔軟性が高いスクリーニングが可能になります。
全銘柄を舐めるスクリーニングフレームワーク
まずは、株価データベース中の全銘柄を舐めまわすコードを考えます。 株価データベースは以下のように特定の名前のディレクトリに銘柄毎の時系列csvファイルが置かれた構成を想定しています。 dataディレクトリには、証券コードの1000の桁毎のサブディレクトリを切り、その中に、銘柄毎の株価日足時系列データのcsvファイルが置かれているものとします。
working_root/
data/
1000/
1001.csv
1002.csv
...
2000/
...
9000/
9001.csv
...
以下のコードは、株価データベース中の全銘柄を舐めまわすための簡単なスクリーニングフレームワークです。関数process()が全銘柄に対して呼び出されます。この例では何もしていませんが、process()には、時系列株価のDataFrameオブジェクトが引数で渡ってくるので、ここにテクニカル分析するなりのコードを書けば、銘柄スクリーニングができる訳です。
import os
import glob
import muzinzo.adjust_close_value as mzl
def process(code, df):
result = False
# ここにテクニカル分析かなにかのコードを入れる
# 抽出した銘柄ならTrueを返す
return result
def walk_around():
for i in range(1, 10):
csv_files = glob.glob('./data/{}/*.csv'.format(i*1000))
for csv_file in csv_files:
filename = os.path.basename(csv_file)
code = filename[0:4]
df = mzl.load_stock_price_csv(csv_file, term=100)
if process(code, df):
print('buy: '+code)
def main():
walk_around()
if __name__ == '__main__':
main()
テクニカル分析
筆者が参考にするテクニカル指標は、スローストキャスティクス、macdあたりでしょうか。あとは古典的な移動平均線とか。テクニカルは絶対視はしていないので、まぁどうでもよいというか、刺身のつまというか。
ストキャスティクスは相場の過熱感をあらわす指標で、80%以上で買われすぎ、20%以下なら売られすぎ(閾値を80%, 20%固定ではなくお好みで)をあらわします。ストキャスティクスには、%K, %D, %SDの3つの指標があり、
- %Dと%SDが売られすぎ領域(20%以下)でゴールデンクロスならば買い
- %Dと%SDが買われすぎ領域(80%以上)でデッドデンクロスならば売り
という判断がよく使われます。%Dと%SDではなく、%Kと%Dを使うほうがより敏感に反応するらしいのですが、感度が良すぎると弊害として虚報の多くなるので一長一短です。%Dと%SDを使う方法を「スロー」と呼んでいるようです。
macdには、macdとsignalの2つの指標があり、
- macdとsignalがゴールデンクロスならば買い
- macdとsignalがデッドデンクロスならば売り
という判断がよく使われます。これもmacdとsignalが互いに絡み合うようなときは虚報が多数発生することになるので、短期間でクロスを繰り返すようなケースは取り除く必要もあるでしょう。
あとは、古典的なテクニカル分析の王道である、移動平均線mavのゴールデンクロスもいれときましょう。
なわけで、銘柄毎にmav, stochastic, macdを算出し、
直近日に、
- 中期移動平均線と長期移動平均線がゴールデンクロス
- %Dと%SDが売られすぎ領域でゴールデンクロス
- macdとsignalがゴールデンクロス
のいずれかのシグナルが点灯した銘柄を抽出するコードを書いてみます。
テクニカルスクリーニングの実装
ストキャスティクスとmacdを計算するコードは、本連載【第6回】mplfinanceを使用してテクニカルチャート表示 で紹介したコードをそのまま利用します。再掲すると、以下の関数です。
import pandas as pd
def calc_mav(df, s, m, l):
mav = pd.DataFrame()
mav["mav_s"] = df["Close"].rolling(s).mean()
mav["mav_m"] = df["Close"].rolling(m).mean()
mav["mav_l"] = df["Close"].rolling(l).mean()
return mav
def calc_stochastic(df, term):
stochastic = pd.DataFrame()
stochastic['%K'] = ((df['Close'] - df['Low'].rolling(term).min()) \
/ (df['High'].rolling(term).max() - df['Low'].rolling(term).min())) * 100
stochastic['%D'] = stochastic['%K'].rolling(3).mean()
stochastic['%SD'] = stochastic['%D'].rolling(3).mean()
stochastic['UL'] = 80
stochastic['DL'] = 20
return stochastic
def calc_macd(df, es, el, sg):
macd = pd.DataFrame()
macd['ema_s'] = df['Close'].ewm(span=es).mean()
macd['ema_l'] = df['Close'].ewm(span=el).mean()
macd['macd'] = macd['ema_s'] - macd['ema_l']
macd['signal'] = macd['macd'].ewm(span=sg).mean()
macd['diff'] = macd['macd'] - macd['signal']
f_plus = lambda x: x if x > 0 else 0
f_minus = lambda x: x if x < 0 else 0
macd['diff+'] = macd['diff'].map(f_plus)
macd['diff-'] = macd['diff'].map(f_minus)
return macd
直近日のゴールデンクロス、デッドクロスの判定関数は次のようにしました。やや強引なコードですが。
def calc_todays_gc(shorts, longs):
# 直近日がゴールデンクロスならTrueを返す
if len(shorts)<75:
return False
today = len(shorts) - 1
yesterday = len(shorts) - 2
short0 = shorts[yesterday]
long0 = longs[yesterday]
short = shorts[today]
long = longs[today]
if not math.isnan(short0) and not math.isnan(long0) and not math.isnan(short) and not math.isnan(long):
if (short0-long0)<0 and (short-long)>0:
return True
return False
データ蓄積日数が75日未満なら判定を諦めていますが、使用する指標がストキャスティクスとmacdだけならもっと短い期間でもよいのですが、後々75日移動平均線なんかも使おうかと考えており、あとあと面倒がないようにざっくり75日で足切りしました。
期間内での過去のゴールデンクロスを全て求める場合は以下のようなコードになります。
def calc_gc(shorts, longs):
# ゴールデンクロス検出
gcs = []
short0 = shorts[0]
long0 = longs[0]
for i in range(len(shorts)):
short = shorts[i]
long = longs[i]
if not math.isnan(short0) and not math.isnan(long0) and not math.isnan(short) and not math.isnan(long):
if (short0-long0)<0 and (short-long)>0:
gcs.append(shorts.index[i])
short0 = short
long0 = long
return gcs
ここまで準備した状態で、先に紹介した全銘柄を舐めるフレームワークのprocess()関数を書いてやれば、スクリーニングコードが完成です。
移動平均線のゴールデンクロス
例えば、5日移動平均線と25日移動平均線のゴールデンクロス銘柄を見つけたいなら次のようになります。
def process(code, df):
mav = calc_mav(df, 5, 25, 75)
mav_buy = calc_todays_gc(mav['mav_s'], mav['mav_m'])
if calc_todays_gc(mav['mav_s'], mav['mav_m']):
return True
else:
return False
macdのゴールデンクロス
macdとsignalのゴールデンクロス銘柄を見つけたいなら次のようになります。
def process(code, df):
macd = calc_macd(df, 12, 26, 9)
macd_buy = calc_todays_gc(macd['macd'], macd['signal'])
days = calc_macd_accuracy(macd)
if macd_buy and days>10:
return True
else:
return False
短期間のうちにmacdとsignalが交差をくりかえすようなダマしを回避するため、macdとsignalの直近の1つ前の交点までの期間を以下のコードで算出し、その期間が10日以下だったらダマしと判断するようにしています。見逃しが発生するリスクもありますが。。。
def calc_macd_accuracy(macd):
gc1 = calc_gc(macd['macd'], macd['signal'])
gc2 = calc_gc(macd['signal'], macd['macd'])
gc1.extend(gc2)
gc = sorted(gc1)
if len(gc)>=2:
term = gc[len(gc)-1] - gc[len(gc)-2]
return term.days
return 0
ストキャスティクスのゴールデンクロス
ストキャスティクス%Dと%SDが売られすぎ領域(20%以下)でゴールデンクロスとなった銘柄を見つけたいなら次のようになります。
def process(code, df):
stochastic = calc_stochastic(df, 14)
stochastic_buy = calc_todays_gc(stochastic['%D'], stochastic['%SD'])
if stochastic_buy and stochastic['%D'][today]<20 and stochastic['%SD'][today]<20:
return True
else:
return False
次のステップ
あとは複数の指標を組み合わせてand/orをとったりして、自分なりの複雑なスクリーニングを実装しましょう。
デッドクロスを算出する場合は、ゴールデンクロス算出関数 calc_todays_gc(), calc_gc()関数の引数を入れ替えるだけで、使いまわしできます。
ここで紹介したコードは、スクリーニングで抽出した銘柄の証券コードを表示するだけなのですが、これではあまりにも不親切なので、抽出した銘柄のチャートをファイルに出力するようにしておくとよいでしょう。
チャートのファイル出力例
mpf.plot(df, type='candle', volume=True,
addplot=apd_oscilator,
savefig=dict(fname=code+'.png',dpi=100))
まとめ
無一物中Project第7回では、これまでに構築した株価データベースを使って、テクニカル指標により銘柄スクリーニングする方法を紹介しました。