株価を直接動かすものは、売られる数と買われる数、つまり
需給(需要と供給)
買う人が多ければ株価は上がり、売る人が多ければ下がる。
こんな当たり前の事を、3年前に外資証券会社勤務で先輩であるプロのシステムトレーダーに言われるまで気づかなかった。
【関連ブログ】
その後、信用倍率をPythonを使った機械学習でデータを使った事はあったけど、その後は面倒になって放置していた。
OSSのProtraで使えないしな……面倒くさい。
そもそも論として本当に「信用倍率」って意味があるの?
先輩が勝手に言ってるだけじゃないの?
……それ自体も分からないので重い腰を上げてみた。
信用倍率を使った投資方法
そもそも先輩から投資方法を具体的に聞いたわけじゃない。
で、ネットでググると元証券マンでユーチューバーSho氏が、実際に利用していた手法を公開していた。
【手法】
- 1)信用倍率(1を切る/3以下など)
- 2)売上高営業利益率(前回より下落してない、高い)
- 3)経常利益進捗率(達成率が順調上昇 → 会社見通し正しい)
- 4)高値期日を過ぎてから買う
- 5)最後にテクニカル指標を使って銘柄を選択
色々とファンダメンタルズ分析指標も入っているのでバックテストは難しそうだ。
でも「信用倍率」だけは過去のデータもあるので効果があるのかは判断できそうだ。
YouTubeの中では理由を次のように語っている。
株式マーケットは機関投資家や海外投資家、一部の資金が潤沢にある大口投資家が動かしている。
彼らの思惑と反対にいくと、大抵やられてしまう。
彼らは需給が一方に偏っている時に稼ぎに来る。
だからターゲットにしているのは信用取引をしている人。
なるほど。
益々バックテストしてみたいよね。
OSSであるProtraでの実現方法
「信用倍率」は古いデータも下記から取得可能……だけどダウンロード禁止なサーバもあるのでソースコードは公開しない。
とりあえず、これをテクニカル分析指標として使うとストラテジー作成の幅が広がる気がする。
で、実際にProtraのバックテストで使うにはどうしたら良いのか色々と考えてみた。
【案1】データ読み込み部分を実装修正して実現
一番シンプルかつ正式な対応。
だけど、パッチの管理が大変だし、何よりC#知らないから何週間も必要な気がする。
別にProtraを皆に使ってもらう事が目的じゃないので、この案は却下。
誰かが作ってくれることを期待……したいけど斜陽なツールだし。
【案2】Protraの外部データの読み込みを使う
CSVファイルにしてProtraで読み込ませてしまおうというもの。
ただしProtraは「1~9999」番までしか読み込みできない。
「ダウ平均」とか数種類であれば登録可能だけど、そもそも銘柄は1300~9999番まで利用されているので、全借貸銘柄を登録するには数が足りない。
1300番台 | 水産・農林業 |
1500番台 | 鉱業 |
1600番台 | 鉱業(石油・ガス開発) |
1700~1900番台 | 建設業 |
2000番台 | 食料品 |
3000~3500番台 | 繊維製品 |
3700~3900番台 | パルプ・紙 |
4000番台 | 化学・医薬品 |
5000番台 | 石油・石炭製品 |
5100番台 | ゴム製品 |
5200~5300番台 | ガラス・土石製品 |
5400~5600番台 | 鉄鋼 |
5700~5800番台 | 非鉄金属 |
5900番台 | 金属製品 |
6000~6400番台 | 機械 |
6500~6900番台 | 電気機器 |
7000~7400番台 | 輸送用機器 |
7700番台 | 精密機器 |
7800~7900番台 | その他製品 |
8000~8200番台 | 卸売業 |
8300~8500番台 | 銀行・その他金融 |
8600番台 | 証券・先物取引業 |
8700番台 | 保険 |
8800番台 | 不動産 |
9000番台 | 陸運 |
9100番台 | 海運 |
9200番台 | 空運 |
9300番台 | 倉庫・運輸関連 |
9400番台 | 情報通信 |
9500番台 | 電気ガス |
9600~9900番台 | サービス業 |
これも駄目じゃんーー。
【案3】Protraからライブラリコードを読み込む
機械学習の結果をProtraで可視化するために利用している方法。
Protraで読み込み可能なデータファイルを作る
具体的には次のようなIf/Elseだらけのライブラリを作成し、Protraで読み込ませる……という超力技な方法が思いついた。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def IsBUYDATE if (2000 > (int)Code && (int)Code >= 1000) if ((int)Code == 1301) if (2000> (int)Year && (int)Year >= 1999) if (Year == 1999 && Month == 3 && Day == 5) return 7.95 end if (Year == 1999 && Month == 3 && Day == 8) return 7.95 end if (Year == 1999 && Month == 3 && Day == 9) return 7.95 end if (Year == 1999 && Month == 3 && Day == 10) return 7.95 end if (Year == 1999 && Month == 3 && Day == 11) return 7.95 end if (Year == 1999 && Month == 3 && Day == 12) |
ただしファイルは超ビックサイズになりそうだ。
案の定、
3476万行(2GB)のLIBファイル
でProtraのバックテストで読み込ませると、何時間経過してもうんともすんとも言わない。
インタプリタはメモリを多く消費する。
メモリが97%の状態が続いていたが全く返答がないわ……
【案3.5】利用する銘柄&信用倍率だけライブラリコードとして読み込む
暫定的に、現時点のプライムの貸借銘柄だけに絞り、かつ信用倍率が3.0以下だけ抽出することにした。
その結果
1283万行(400 MB)のLIBファイル
となり、このファイルサイズであればProtraで読み込ませはできそうだ。
おわりに
中途半端な結果になってしまった。
次回は、よりファイルサイズの小さくなる方法を模索してみつつバックテストもやってみる。
ソースコード
信用倍率のファイルは「out/*.csv」に銘柄名でダウンローしている前提で、Protra用のファイルを出力するソースコード。
加えて「stock_list.txt」というファイルに対象となる銘柄番号を保存して出力した。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# -*- coding: utf-8 -*- import os import re import csv import glob import datetime import numpy as np import pandas as pd from nehori import protra import functools print = functools.partial(print, flush=True) # しきい値 g_flag = 3.00 # Protraのパス g_path = 'C:\\Users\\Hoge\\Protra' def atoi(text): return int(text) if text.isdigit() else text def natural_keys(text): return [ atoi(c) for c in re.split(r'(\d+)', text) ] g_prefix = 0 # Protraファイルの作成 def start_protra_dataset(code): global g_prefix g_prefix = 0 s = '' s += ' if ((int)Code == ' + code + ')\n' return s def set_protra_date(date, value): global g_prefix (year, month, day) = date.split('/') s = '' if ((int)(g_prefix) != (int)(year)): if ((int)(g_prefix) != 0): s += ' end\n' s += ' if (' + str((int)(year) + 1) + ' > (int)Year && (int)Year >= ' + str((int)(year)) + ')\n' g_prefix = year s += ' if (Year == ' + str(int(year)) + ' && Month == ' + str(int(month)) + ' && Day == ' + str(int(day)) + ')\n' s += ' return ' + str(value) + '\n' s += ' end\n' return s def end_protra_dataset(): global g_prefix s = '' if ((int)(g_prefix) != 0): s += ' end\n' s += ' end\n' return s # 年月日変換+ゼロパディング def date_zeropadding(date): l = re.findall(r'\d+', date) return l[0].zfill(4) + '/' + l[1].zfill(2) + '/' + l[2].zfill(2) def shinyo_func(code, df_shinyou, df_date): global g_flag # 2) 含まれてない日付を前情報で補完する df_shinyou['日付'] = df_shinyou['日付'].apply(date_zeropadding) # 直前の値を使って埋めていく df_shinyou = pd.merge(df_date, df_shinyou, on='日付', how='left') df_shinyou = df_shinyou.fillna(method='ffill') # 3) 信用倍率が4.0以上を削除 # 0.0 は倍率が存在しない df_shinyou = df_shinyou.dropna(subset=['信用倍率']) df_shinyou['信用倍率'] = df_shinyou['信用倍率'].astype(float) df_shinyou = df_shinyou[df_shinyou['信用倍率'] != 0.0] df_shinyou = df_shinyou[df_shinyou['信用倍率'] < g_flag] # 4) 有効値としてProtraのライブラリに変換する s = "" s += start_protra_dataset(code) for index, data in df_shinyou.iterrows(): tmp = set_protra_date(data['日付'], data['信用倍率']) s += tmp s += end_protra_dataset() return s # Protraから読み込み def read_protra_stock_date(stock_id): global g_path p = protra.PriceList(g_path) l_2d = p.readPriceList(stock_id) df = pd.DataFrame(l_2d, columns=('日付', 'open', 'high', 'low', 'close', 'volume')) return df['日付'] def read_stock_list(file_path): with open(file_path) as f: lines = f.readlines() lines = [line.rstrip('\n') for line in lines] return lines def main(): # 1) 各銘柄のCSVを名前順で読み込む file_list = sorted(glob.glob('out/*.csv'), key=natural_keys) # トヨタ株で取引日を抽出 df_protra_date = read_protra_stock_date('7203') stock_lines = read_stock_list('stock_list.txt') s = '' s += 'def IsBUYDATE\n' pre_prefix = 0 for csv_name in file_list: df_shinyou = pd.read_csv(csv_name) df_shinyou = df_shinyou.replace('---', '0') code = os.path.splitext(os.path.basename(csv_name))[0] # 対象の借貸リストにある銘柄の場合 if (code in stock_lines): prefix = int(int(code) / 1000) if (pre_prefix != prefix): if (pre_prefix != 0): s += ' end\n' pre_prefix = prefix s += ' if (' + str(prefix + 1) + '000 > (int)Code && (int)Code >= ' + str(prefix) + '000)\n' s += shinyo_func(code, df_shinyou, df_protra_date) s += ' end\n' s += ' return 9999\n' s += 'end\n' print(s) # メインルーチン if __name__ == '__main__': main() |