In [1]:
import os
import numpy as np
import pandas as pd
In [2]:
workdir = 'R:/WORK/'

DataFrameをMT4のhst形式にして書き出す関数

In [3]:
def df_to_hst(outfilepath, df, symbol=None, period=None, digits=None, timesign=0, last_sync=0):
    """df to mt4 .hst"""
    # df は datetimeindexで、Open High Low Close Volume というカラム名で値を持っているものを想定
    # outfilepath はファイル名まで含めたパスを指定する
    # symbol, period の引数が省略されている場合、USDJPY60.hst のようなファイル名なら
    # USDJPY, 60 をパースしてそれをヘッダ情報として使う
    # 引数で指定があればそっちをヘッダ情報として使う
    # 特殊なシンボル名や数値を含むシンボル名の場合はそれぞれ引数を指定したほうが確実
    # digits は指定が無い場合、シンボル名にJPYが含まれていればdigitsは 3、そうでないなら5にする
    # これも引数で指定があればその値をヘッダ情報に使う
    import re
    if symbol is None:
        symbol = re.search('[A-Z]+', os.path.split(outfilepath)[-1]).group()
    if period is None:
        period = re.search('[0-9]+', os.path.split(outfilepath)[-1]).group()
    if digits is None:
        digits = 3 if 'JPY' in symbol else 5
    if symbol is None or period is None or digits is None:
        print('ヘッダに書き出す情報が足りないので中止!')
        return
    
    dtype = [('version','i4'), ('copyright','S64'), ('symbol','S12'), ('period','i4'),
             ('digits','i4'), ('timesign','i4'), ('last_sync','i4'), ('unused','S52')]
    header = np.empty(1, dtype=dtype)
    header['version'] = 401
    header['copyright'] = '(C)opyright 2003, MetaQuotes Software Corp.'
    header['symbol'] = symbol
    header['period'] = period
    header['digits'] = digits
    header['timesign'] = timesign
    header['last_sync'] = last_sync
    header['unused'] = ''
    
    dtype = [('Time','u8'), ('Open','f8'), ('High','f8'), ('Low','f8'),
             ('Close','f8'), ('Volume','i8'), ('s','i4'), ('r','i8')]
    data = np.zeros(len(df), dtype=dtype)
    data['Time'] = df.index.astype(np.int64)//10**9
    for c in ['Open', 'High', 'Low', 'Close', 'Volume']:
        data[c] = df[c]
    with open(outfilepath, 'wb') as f:
        f.write(header.tobytes()+data.tobytes())

こっちは読み込みテスト用

'testdata.hst'の中身はMT4からコピーしてきたUSDJPY60.hst
USDJPY60.hstは出力するファイル名で使いたいので読み込む前に名前を変えた

In [4]:
def read_hst(path):
    with open(path, 'rb') as f:
        dtype = [('version','i4'), ('copyright','S64'), ('symbol','S12'), ('period','i4'),
                 ('digits','i4'), ('timesign','i4'), ('last_sync','i4'), ('unused','S52')]
        header = np.frombuffer(f.read(148), dtype=dtype)
        print(header[0])
        dtype = [('Time','u8'), ('Open','f8'), ('High','f8'), ('Low','f8'),
                 ('Close','f8'), ('Volume','i8'), ('s','i4'), ('r','i8')]
        df = pd.DataFrame(np.frombuffer(f.read(), dtype=dtype).astype(dtype[:-2]))
        dt = df['Time']
        df = df.set_index(pd.to_datetime(df['Time'], unit='s')).drop('Time', axis=1)
        return df


src_df = read_hst(os.path.join(workdir, 'testdata.hst'))
print('shape', src_df.shape)
(401, b'(C)opyright 2003, MetaQuotes Software Corp.', b'USDJPY', 60, 3, 1326176242, 0, b'')
shape (107240, 5)

↑で読み込んだDataFrameをhst形式でUSDJPY60.hstという名前で書き出す

In [5]:
df_to_hst(os.path.join(workdir, 'USDJPY60.hst'), src_df)

書き出したものを読み込みテスト用の関数で読み込んでみる

In [6]:
df = read_hst(os.path.join(workdir, 'USDJPY60.hst'))
(401, b'(C)opyright 2003, MetaQuotes Software Corp.', b'USDJPY', 60, 3, 0, 0, b'')
In [7]:
# 一応値が違うところが無いか確認
(src_df != df).sum()
Out[7]:
Open      0
High      0
Low       0
Close     0
Volume    0
dtype: int64

大丈夫ぽいですな(´・ω・`)

In [ ]:
 

せっかくなのでなにかhstを作ってみます

http://api.bitcoincharts.com/v1/csv/ の coincheckJPY.csv.gz

を使ってhstを作ってみる(´・ω・`)

In [8]:
btc = pd.read_csv(os.path.join(workdir, '.coincheckJPY.csv'), names=['Time', 'Price', 'Volume'])
btc['Time'] = pd.to_datetime(btc['Time'], unit='s')
btc.set_index('Time', inplace=True)
print('data len', len(btc))
btc.tail()
data len 4389809
Out[8]:
Price Volume
Time
2017-04-19 01:14:48 132772.0 0.04572
2017-04-19 01:14:59 132671.0 0.10000
2017-04-19 01:15:05 132765.0 0.38140
2017-04-19 01:15:05 132766.0 0.40000
2017-04-19 01:15:05 132767.0 0.19284

↑を1, 5, 15, 30, 60, 240, 1440 分でサンプリングしてhstで書き出してみる

In [9]:
for tf in [1, 5, 15, 30, 60, 240, 1440]:
    btc_ohlcv = btc['Price'].resample('{}T'.format(tf)).ohlc().dropna()
    btc_ohlcv.columns = ['Open', 'High', 'Low', 'Close']
    btc_ohlcv['Volume'] = btc['Volume'].resample('1T').sum().dropna()
    btc_ohlcv['Volume'] /= 0.00000001 # .hstのVolumeはlong型なのでSatoshiにする
    hstpath = os.path.join(workdir, 'BTCJPY{}.hst'.format(tf))
    df_to_hst(hstpath, btc_ohlcv, symbol='BTCJPY', period=tf, digits=0)
memo

日足などを作るとき任意の時間を区切りにしたいときは
resample('24H', base=6) のようにすればOK(´・ω・`)

In [10]:
for tf in [1, 5, 15, 30, 60, 240, 1440]:
    print('shape', read_hst(os.path.join(workdir, 'BTCJPY{}.hst'.format(tf))).shape)
(401, b'(C)opyright 2003, MetaQuotes Software Corp.', b'BTCJPY', 1, 0, 0, 0, b'')
shape (474365, 5)
(401, b'(C)opyright 2003, MetaQuotes Software Corp.', b'BTCJPY', 5, 0, 0, 0, b'')
shape (168926, 5)
(401, b'(C)opyright 2003, MetaQuotes Software Corp.', b'BTCJPY', 15, 0, 0, 0, b'')
shape (71268, 5)
(401, b'(C)opyright 2003, MetaQuotes Software Corp.', b'BTCJPY', 30, 0, 0, 0, b'')
shape (38577, 5)
(401, b'(C)opyright 2003, MetaQuotes Software Corp.', b'BTCJPY', 60, 0, 0, 0, b'')
shape (20129, 5)
(401, b'(C)opyright 2003, MetaQuotes Software Corp.', b'BTCJPY', 240, 0, 0, 0, b'')
shape (5298, 5)
(401, b'(C)opyright 2003, MetaQuotes Software Corp.', b'BTCJPY', 1440, 0, 0, 0, b'')
shape (902, 5)