【第9回】チャート形状の認識

March 30, 2021

株価サーバーテクニカル分析python

無一物中Project記事一覧


はじめに

筆者が利用する楽天証券のトレーディングツール「MARKET SPEED II」には、チャート形状検索の機能があります。これは、

  • 上昇ストップ
  • 上昇
  • 急上昇
  • 調整
  • もみ合い
  • リバウンド
  • 急落
  • 下落
  • 下げ止まり

のチャートの形状を認識し、該当する銘柄を教えてくれる機能です。

marketspeed.png

なかなか便利な機能なのですが、これをどうやって実現しているのだろうと気になって、自分でも試してみることにしました。これを自前で用意できれば、この機能とテクニカル分析を組み合わせて、強力なスクリーニングができるのではないかと思った訳です。

実際の投資に役立ちそうな形状は、筆者の場合「下げ止まり」、「リバウンド」あたりかな。「下げ止まり」、「リバウンド」銘柄をウォッチリストに入れて、重点監視するとか。

「急上昇」は最重要なのだけど、「MARKET SPEED II」でも、今回紹介する抽出方法でも、けっこう遅れて抽出されるので、あまり役に立ちません。

チャート形状認識実験

「MARKET SPEED II」のチャート形状認識アルゴリズムは非公開なので、知る由はありませんが、けっこうあやしい結果が得られる場合もあり、そんなにたいそうなことはやってないんじゃないかな。AIで形状を学習してとかも真っ先に思いついたのですが、もっとシンプルに、「データ同士がどの程度似ているか」を定量的にあらわしている正規化相関を利用してやってみることにします。

以下のコードでは、データ列aとb、データ列aとcの相関係数を計算しています。

import numpy as np

a = [10, 11, 12, 13, 14]
b = [10, 12, 11, 11, 12]
c = [6, 9, -8, 0, 1]

corr1 = np.corrcoef(a, b)
print(corr1[1,0])
corr2 = np.corrcoef(a, c)
print(corr2[1,0])

相関係数の値は-1.0~1.0の間の値をとり、この値が1.0に近いほど似ていることを意味します。 コードの実行結果は、aとbの相関係数は0.5669467095138409、aとcの相関係数は-0.46190580640999945だから、cよりもbのほうが、aに似ているということになります。

そこで、次の図のような形状毎のデータ列(形状テンプレート)を用意し、株価のデータとの相関係数を算出し、相関係数の値が1.0に近ければ、その形状であると判定します。

reference_functions.png

データの期間は30日間とします。例として2021/3/27時点での以下の3銘柄で試してみます。

corr_coeff_1.png

株価が異なっても比較できるように期間の株価を0~1で正規化します。

corr_coeff_2.png

形状テンプレートと株価の相関係数と形状の判定結果は以下のようになりました。

1802 大林組 相関係数 判定結果
上昇ストップ 0.7396801593636323
上昇 0.9302408236379394
急上昇 0.9169569543912299
調整 -0.17099452448064192
もみ合い 0.23862822925360594
リバウンド 0.17099452448064195
急落 -0.8987018370356069
下落 -0.9302408236379393
下げ止まり -0.7701760966773804
1939 四電工 相関係数 判定結果
上昇ストップ 0.6610759242186979
上昇 0.8931602044435897
急上昇 0.9257444402842008
調整 -0.2694073532155971
もみ合い 0.1571958709317137
リバウンド 0.2694073532155971
急落 -0.9116196735909801
下落 -0.8931602044435897
下げ止まり -0.6944864950399954
4062 イビデン 相関係数 判定結果
上昇ストップ -0.24637125495374249
上昇 0.17806110554600332
急上昇 0.5336115201980216
調整 -0.8729393226699412
もみ合い -0.15475023299510088
リバウンド 0.8729393226699412
急落 -0.5654458252623342
下落 -0.1780611055460032
下げ止まり 0.21571529539920017
  • 「1802 大林組」は、「上昇」
  • 「1939 四電工」は、「急上昇」
  • 「4062 イビデン」は、「リバウンド」

という判定結果になりました。

corr_coeff_3.png

そこそこいいかんじ。

全銘柄での抽出例

2021/3/26の全銘柄で、相関係数≧0.85 で形状抽出した結果は、以下の通りでした。

  • 上昇ストップ 57件
  • 上昇 345件
  • 急上昇 604件
  • 調整 0件
  • もみ合い 4件
  • リバウンド 94件
  • 急落 10件
  • 下落 34件
  • 下げ止まり 196件

よくよく考えてみると、「もみ合い」を相関係数で判断するのはふさわしくない(そもそも全データが同じ価だと相関係数は算出不能)ので、「もみ合い」は、期間中の株価の標準偏差が十分小さいかで判定するように変えました。すなわち、

期間中の株価の標準偏差/期間中の株価の平均値<0.01 ならば「もみ合い」として抽出する。

また、上昇や下落でも期間の始めと終わりで10%以上価格差がある場合のみ抽出対象としました。

「調整」が0件というのは精査が必要。

これらの変更を加えた、2021/3/26の全銘柄での結果は、以下の通りでした。

  • 上昇ストップ 41件
  • 上昇 234件
  • 急上昇 322件
  • 調整 0件
  • もみ合い 106件
  • リバウンド 69件
  • 急落 6件
  • 下落 21件
  • 下げ止まり 111件

以下は抽出結果の一部です。

上昇ストップ

result_0.png

上昇

result_1.png

急上昇

result_2.png

もみ合い

result_4.png

リバウンド

result_5.png

急落

result_6.png

下落

result_7.png

下げ止まり

result_8.png

チャート形状認識の実装例

以下のcsvファイルを使って、相関係数算出のテストコードを実装してみます。

1802.csv
2021/02/10,906,915,905,914,2677000000
2021/02/12,910,914,902,910,3371500000
2021/02/15,926,944,924,943,3315900000
2021/02/16,955,959,942,947,2673800000
2021/02/17,946,948,935,936,2365200000
2021/02/18,939,948,920,922,2997000000
2021/02/19,915,919,909,911,2833900000
2021/02/22,932,943,931,936,3182100000
2021/02/24,929,937,921,921,3191300000
2021/02/25,950,963,943,957,3063000000
2021/02/26,950,952,909,913,5239900000
2021/03/01,924,942,924,937,1922500000
2021/03/02,941,947,929,938,2153100000
2021/03/03,936,957,936,957,2786100000
2021/03/04,957,964,942,953,2482400000
2021/03/05,965,967,946,967,2417800000
2021/03/08,984,1003,975,982,3167600000
2021/03/09,997,999,979,995,2824800000
2021/03/10,968,995,968,990,3594900000
2021/03/11,999,1014,994,1000,2865800000
2021/03/12,1007,1008,996,1002,3294000000
2021/03/15,1005,1016,1004,1014,2113000000
2021/03/16,1012,1035,1008,1027,2581500000
2021/03/17,1010,1012,997,1007,3222500000
2021/03/18,1016,1037,1014,1028,3015600000
2021/03/22,1044,1062,1039,1057,2573700000
2021/03/23,1053,1068,1049,1056,2476700000
2021/03/24,1045,1046,1011,1013,3031600000
2021/03/25,1031,1052,1028,1040,2509100000
2021/03/26,1059,1066,1050,1056,2274600000

サンプルなので、形状テンプレートは、「上昇」と「急上昇」の2つしか用意していません。normalize_close_value()でpandasで読み込んだ株価データを0~1で正規化します。これをcalc_corr_coeff()により、形状テンプレートとの相関係数を算出しています。

corr_test.py
# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd

def full_up_shape(term):
    # 上昇
    up = range(0, term)
    normalized = list(map(lambda v: v/float(term), up))
    return normalized

def flat_up_shape(term):
    # 急上昇
    half = int(term/2)
    flat = [0]*half
    up = range(1, half+1)
    flat[len(flat):len(flat)] = up
    return list(map(lambda v: v/float(half), flat))

def normalize_close_value(df):
    close_value = df['Close'].to_list()
    min_value = min(close_value)
    max_value = max(close_value)
    return list(map(lambda v: (v-min_value)/(max_value-min_value), close_value))

def calc_corr_coeff(v1, v2):
    corr = np.corrcoef(v1, v2)
    return corr[1, 0]
    
def main():
    term = 30
    path = '1802.csv'
    df = pd.read_csv(path, header=None,
                     names=['Date','Open','High','Low','Close','Volume'], encoding='UTF-8')
    
    df['Date'] = pd.to_datetime(df['Date'])
    df = df.set_index("Date")
    df = df.tail(term)
    
    close_value = normalize_close_value(df)
    
    f_full_up = full_up_shape(term)
    corr1 = calc_corr_coeff(close_value, f_full_up)
    
    f_flat_up = flat_up_shape(term)
    corr2 = calc_corr_coeff(close_value, f_flat_up)
    
    print(corr1, corr2)

if __name__ == '__main__':
    main()

実行結果は、

0.9302408236379394 0.9169569543912299

となるはずです。

サムネイルチャート表示

本記事中では、複数のチャートをまとめてサムネイル表示させていますが、この方法を紹介しておきます。

株価データのcsvファイルが、以下のようなディレクトリに格納させているものとします。

data/   ※6
    1000/
        1001.csv
        ...
    2000/
        ...
    9000/
        9001.csv
        ...

以下のような銘柄リストをあらわすテキストファイルを読み込み、そのサムネイルを作成します。

list.txt
1383,ベルグアース
1413,桧家H
1419,タマホーム
1489,日経高配指数
1543,パラジウム信託
1651,ダイワ 高配40
1670,Maxsp三菱
1675,ETFSパラジ
1766,東建コーポ
1787,ナカボテック
1801,大成建
1808,長谷工
1811,銭高組
1812,鹿島
1814,大末建
1852,浅沼組
1860,戸田建
1887,日本国土開発
1904,大成温調
1928,積ハウス
...

matplotlibのsubplots()により、複数チャートを描画しています。 ただし、以下のコードでは、株価のcsvファイル中に、出来ずの場合の株価が0円となっている場合や、株式分割による不連続などは考慮されていません。

thumbnail_test.py
# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

DATA_PATH = "../data/"

def get_close_value(df):
    close_value = df['Close'].to_list()
    return close_value

def read_csv(code, term):
    code_dir = code[0]+"000"
    path = DATA_PATH + code_dir + "/" + code + ".csv"
    df = pd.read_csv(path, header=None,
                     names=['Date','Open','High','Low','Close','Volume'], encoding='UTF-8')
    df['Date'] = pd.to_datetime(df['Date'])
    df = df.set_index("Date")
    df = df.tail(term)
    return df
        
def visualize(term, codes, names):
    x = np.array(list(range(0, term)))
    pages = int(len(codes)/20)
    if len(codes)%20 != 0: pages = pages + 1
    ncols = 5
    nrows = 4
    i = 0
    for page in range(0, pages):
        fig, axes = plt.subplots(
            nrows=nrows, ncols=ncols, sharex=False)
        for j in range(0, ncols):
            for k in range(0, nrows):
                axes[k,j].tick_params(labelbottom=False,
                                      labelleft=False,
                                      labelright=False,
                                      labeltop=False)
        for j in range(0, nrows*ncols):
            if i>=len(codes): break
            df = read_csv(str(codes[i]), term)
            close_value = get_close_value(df)
            row = j // ncols
            col = j % ncols
            title =str(codes[i])+' '+names[i][:4]
            axes[row,col].plot(x, np.array(close_value))
            axes[row,col].set_title(title)
            i = i + 1
        fig.tight_layout()
        filename = 'chart_thumbs'+str(page)+'.png'
        plt.savefig(filename) 
    
def main():
    term = 30
    filename = 'list.txt'
    df = pd.read_csv(filename, header=None,
                     names=['code','name'], encoding='UTF-8')
    codes = df['code'].to_list()
    names = df['name'].to_list()
    if len(codes)>0:
        visualize(term, codes, names)

if __name__ == '__main__':
    main()

実行結果

chart_thumbs0.png

まとめ

無一物中Project第9回では、チャートの形状を認識させる方法を紹介しました。形状テンプレートとの正規化相関で判定するだけで、それなりに使える結果が得られました。


Written by questions6768 who lives in Uji, Kyoto.