次のような銀行があると、考えてみましょう。
その銀行は、毎朝あなたの口座へ
86,400ドル
振り込んでくれます。
同時に、その口座の残高は毎日
ゼロ
になります。
あなただったらどうしますか?
毎日86,400ドルを全額を引き出す
ことでしょう。
私たちは、一人一人が同じような銀行を持っています。
時間です。
毎朝、あなたに
86,400秒
が与えられています。
毎晩、あなたがうまく使いきらなかった時間は消されてしまいます。
それは、翌日に繰り越されません。
だから、与えられた時間に最大限の投資をしましょう。
時計の針は、走り続けています。
1年の価値を理解するには、浪人した学生に聞いてみるといいでしょう。
1ヶ月の価値を理解するには、未熟児を産んだ母親に聞いてみるといいでしょう。
1週間の価値を理解するには、週刊誌の編集者に聞いてみるといいでしょう。
1時間の価値を理解するには、待ち合わせをしている恋人たちに聞いてみるといいでしょう。
1分の価値を理解するには、電車をちょうど乗り過ごした人に聞いてみるといいでしょう。
1秒の価値を理解するには、たった今、事故を避けることができた人に聞いてみるといいでしょう。
10分の1秒の価値を理解するためには、オリンピックで銀メダルに終わってしまったランナーに聞いてみるといいでしょう。
一瞬一瞬を大切にしましょう。
時は誰も待ってくれません。
昨日は、もう過ぎ去ってしまいました。
明日は、まだわからないのです。
今日は、今あなたに与えられれるものです。
だから、英語では
「今」をプレゼント(= present)
といいます。
[元ネタ一部改変]エレノア・ルーズベルト(アメリカ第32代大統領の妻)
現在、Protraの株価データをCSVに変換してPythonで読みこんで使っている。
このため自動売買を完成するには、この処理の自動化が必要だ。
バイナリファイルをそのまま読めば良いんじゃない?
車輪の再発明を行わないためにProtraを使っているのに、いつまでも車輪を作っている感がある……。
毎日86,400秒の無駄使いの40年……。
バイナリファイルをPythonで読む
「Protra.Lib/Data/PriceData.cs」を読めば書いてある。
いや……そうだけどさ。
C#なんて扱ったことないし読むの大変なのよ……。
該当部分を確認すると「ReadInt」などを使って読み込んでいた。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/// /// 価格データを読み込む。 /// /// BinaryStream public void Read(BinaryReader b) { Date = new DateTime((long)b.ReadInt32() * 86400 * 10000000); Open = b.ReadInt32(); High = b.ReadInt32(); Low = b.ReadInt32(); Close = b.ReadInt32(); Volume = b.ReadDouble(); } |
うーん、色々とググってバイナリを読み出してみたけど、日付部分が正しく取り出せん。
だったさC#のBinaryStreamをそのままPythonで実装すれば良いじゃん。
と思ってググると、ドンビシャなstackoverflowを発見!
ここに書いてあるコードを使うと確かに取り出せた。
でも、やっぱり日付がオカシイ。
1 2 |
Date = new DateTime((long)b.ReadInt32() * 86400 * 10000000); |
この「86,400」の数字は何なのさ!
これは冒頭の説明通り1日を意味する。
DateTimeの最小値である0001年1月1日 0時0分0秒からの経過時間を100ナノ秒単位で表していると思ってる。
しかし、それを踏まえてもPythonの計算結果が全然違う。
1 2 3 4 5 6 |
出力結果 期待値 729393 1998/01/05 729394 1998/01/06 729395 1998/01/07 729396 1998/01/08 729397 1998/01/09 |
日付データの最古が
1998年01月05日(UNIX時間=883926000)
なので、その数値が「ゼロ」になるように計算してみると正しい日付が取得できた。
1 2 |
# UNIX時間から、C#のDateTimeの開始を初期値に変換 _time = 883926000 + (_time - 729393) * 86400 |
これは「-62135629200=883926000 – 729393 * 86400」(0001年01月03日 0時0分0秒)を表している。、
1 2 |
# 1970年01月01日 を 01年01月03日 に初期化 _time = _time * 86400 - 62135629200 |
つまり、UNIX時間とC#のDateTimeの時間軸、UNIX時間(1970年01月01日~)と時間の開始時点をあわせる必要があったようだ。
株分割に対応する
Protraのバイナリデータは、株式分割・併合が考慮されていない。
株式分割は「index.txt」内にあるデータを用いている。
1 |
7957,フジコピアン,T2,100,S:19881224:1.15,S:19901225:1.1,S:20170628:0.1 |
「Protra.Lib/Data/BrandData.cs」を読んでみた。
1 2 3 4 5 6 7 8 9 10 11 |
if (!entries[i].StartsWith("S:")) throw new ApplicationException("index.txtが不正です。:\n" + line); // 分割比率を処理。 var split = new Split(); var y = int.Parse(entries[i].Substring(2, 4)); var m = int.Parse(entries[i].Substring(6, 2)); var d = int.Parse(entries[i].Substring(8, 2)); split.Date = new DateTime(y, m, d); split.Ratio = double.Parse(entries[i].Substring(11)); brand.Split.Add(split); break; |
「index.txt」をCSV形式で読み込んで、必要な情報を抽出すれば良さそうだ。
これでデータは完全に一致した。
まとめ
日付計算の解釈が正しいのかは自信がない。
でも、とりあえず合ってそうだけどな……。
間違っていたら誰か教えて下さい。
ソースコード
Protraで事前に株価のダウンロードが必要。
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 |
import datetime from struct import * import csv # 変更箇所 ################# g_Path = "C:\\Users\\hoge\\Desktop\\Protra\\data\\price" stock_names = [ "1925", # 大和ハウス工業 "2914", # JT "3407", # 旭化成 "4502", # 武田薬品工業 "5108", # ブリヂストン "6758", # ソニー "7203", # トヨタ "8306", # 三菱UFJフィナンシャル・グループ "9432", # 日本電信電話 ] ############################ class BinaryStream: def __init__(self, base_stream): self.base_stream = base_stream def readBytes(self, length): return self.base_stream.read(length) def readInt32(self): return self.unpack('i', 4) def readDouble(self): return self.unpack('d', 8) def unpack(self, fmt, length = 1): b = self.readBytes(length) if not b: return False return unpack(fmt, b)[0] class PriceList: def __init__(self, file): self.index_file = file self.path = g_Path # index.txtの読み込み def readInex(self, id): f = open(self.index_file, 'r') reader = csv.reader(f) # Skip header header = next(reader) ratio_list = [] for row in reader: for i in range(4, len(row)): if not (row[i] in ["OBS", "N225", "A500"]): if (id == row[0]): date = (row[i].split(':')[1][0:4] + "/" + row[i].split(':')[1][4:6] + "/" + row[i].split(':')[1][6:8]) ratio = row[i].split(':')[2] ratio_list.append([date, ratio]) f.close() return ratio_list def readPriceList(self, id): ratio_list = self.readInex(id) path = self.path + "\\" + id[0] + "\\" + id with open(path, "rb") as f: b = BinaryStream(f) while True: _time = b.readInt32() if not _time: break # UNIX時間から、C#のDateTimeの開始を初期値に変換 _time = 883926000 + (_time - 729393) * 86400 _date = datetime.datetime.fromtimestamp(_time).strftime('%Y/%m/%d') # Compre datatime ratio = (float)(1) for i in ratio_list: first_date = datetime.datetime.strptime(_date, '%Y/%m/%d') second_date = datetime.datetime.strptime(i[0], '%Y/%m/%d') if (first_date < second_date): ratio = i[1] # ratio break _open = (int)(b.readInt32() / (float)(ratio)) _high = (int)(b.readInt32() / (float)(ratio)) _low = (int)(b.readInt32() / (float)(ratio)) _close = (int)(b.readInt32() / (float)(ratio)) _volume = format(b.readDouble() * (float)(ratio), '.1f') _volume = _volume.replace('.0', '') print("%s,%s,%s,%s,%s,%s" % (_date,_open,_high,_low,_close,_volume)) if __name__ == '__main__': p = PriceList("index.txt") for id in stock_names: p.readPriceList(id) |