2018年6月17日日曜日
MT4 のヒストリカルデータを Python で読み込んでみた
MetaTrader のフォーラムに `.hst` ファイルフォーマットについて書かれていたので、Python で読み込むクラスを作ってみました。 > 参考 > > [.hst file format . . . Old and New (Jan 2014) - Symbols - MQL4 and MetaTrader 4 - MQL4 programming forum](https://www.mql5.com/en/forum/149178) この手のバイト列を扱うクラスは [struct](https://docs.python.jp/3/library/struct.html) を使うと比較的簡単に実装できます。 ```python # -*- coding: utf-8 -*- import calendar import collections import mmap import struct import time class History: """ .hst ファイルを collection にマップするクラス 各レコードは以下の値を持つ ctm: レコードの時間(1970/1/1 からの秒) open: 始値 high: 高値 low: 安値 close: 終値 volume: 出来高 以下は version 401 spread: スプレッド real_volume: 実出来高? """ def __init__(self, file): self.mm = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) # Read Header self.version, self.copyright, self.symbol, self.period, self.digits, self.timesign, self.last_sync =\ struct.unpack("<i64s12s4i", self.mm[:96]) # Check version and detect record structure self.header_size = 148 if self.version == 400: self.record_size = 44 self.record_struct = struct.Struct("<i5d") self.candle = collections.namedtuple( 'Candle', ['ctm', 'open', 'low', 'high', 'close', 'volume']) elif self.version == 401: self.record_size = 60 self.record_struct = struct.Struct("<q4dqiq") self.candle = collections.namedtuple( 'Candle', ['ctm', 'open', 'high', 'low', 'close', 'volume', 'spread', 'real_volume']) else: raise NotImplementedError self.len = int((len(self.mm) - self.header_size) / self.record_size) def __len__(self): return self.len def __getitem__(self, i): if i < 0 or self.len <= i: raise IndexError pivot = self.header_size + self.record_size * i return self.candle(*self.record_struct.unpack(self.mm[pivot:pivot + self.record_size])) ``` 以下のように、普通の配列同等に扱えます。 ```python with open("USDJPY1.hst", "rb") as f: history = History(f) for record in history: print("{0} Open:{1} High:{2} Low:{3} Close:{4} Volume:{5} Spread:{6} RealVolume:{7}".format( time.strftime("%Y/%m/%d %H:%M", time.gmtime(record.ctm)), record.open, record.high, record.low, record.close, record.volume, record.spread, record.real_volume)) ``` 次は日付でアクセス出来るようにするかな。