【BitFlyer】特定日時のトレードID(exec_id)を自動で取得する

【BitFlyer】特定日時のトレードID(exec_id)を自動で取得する


# python # 仮想通貨

環境

Python: 3.6.3 ccxt: 1.18.466

やりたかったこと

BitFlyerのAPIでは、日時を指定して約定履歴を取得することができない仕様になっています。 なんでやねんと言いたくなる気持ちは抑えつつ、手動で地道に探すのは大変すぎるので2分探索で指定日時のIDを取得するスクリプトを簡易的ですが書きました。 これで約定履歴を地道に管理せずとも、ある程度簡単にIDは取得できるはず。 (rate limit 部分を書いては見たものの、実際に引っかかってはいないです。他も軽く動かした程度なのでバグがあればコメント等頂けると助かります。)

コード

# get target date's id
import re
import ccxt
from datetime import datetime
from time import sleep

TRADE_PAIR = 'FX_BTC_JPY'
BIT_FLYER = ccxt.bitflyer()
INVALID_ID_MESSAGE = r'.*Execution history is limited to the most recent 31 days..*'


def check_finish(target_date: datetime, trade_date: datetime):
    if trade_date is None:
        return False

    if trade_date > target_date:
        return False

    delta = trade_date - target_date
    if delta.seconds < 60 * 5:
        return True
    else:
        return False


def get_datetime(trade_info) -> datetime:
    return datetime.fromtimestamp(int(str(trade_info['timestamp'])[0:10]))


def main():
    exist_rate_limit = 480
    rate_limit_time = None

    target_date = datetime(2019, 5, 28, 0, 0, 0)

    trade_info = None
    trade_date = None

    # bitflyerは1ヶ月前までの履歴しか取得できないので、idがわかれば入れる。
    # わからなければなしでok
    min_id = None
    max_id = None

    # ----
    if min_id is None:
        min_id = 0
    while not check_finish(target_date, trade_date):
        if exist_rate_limit < 0:
            delta = datetime.now() - rate_limit_time
            if delta.seconds > 60 * 5:
                rate_limit_time = datetime.now()
            else:
                print(f'wait for rate limits, {delta.seconds} seconds is passed.')
                sleep(60 * 5 - delta.seconds)
                continue

        if max_id is None:
            trade_info = BIT_FLYER.fetch_trades(TRADE_PAIR, limit=1)[0]
            exist_rate_limit -= 1

            trade_date = get_datetime(trade_info)
            max_id = int(trade_info['id'])
        else:
            check_id = int((max_id + min_id) / 2) if trade_info else check_id
            try:
                trade_info = BIT_FLYER.fetch_trades(TRADE_PAIR, limit=1, params={'before': check_id})[0]
                exist_rate_limit -= 1

                trade_date = get_datetime(trade_info)

                if trade_date > target_date:
                    max_id = check_id
                else:
                    min_id = check_id

                print('-------')
                print(f'max_id: {max_id}')
                print(f'min_id: {min_id}')
                print(f'check_id: {check_id}')
                print(f'check_date: {trade_date}')

            except ccxt.ExchangeNotAvailable as e:
                if re.match(INVALID_ID_MESSAGE, e.args[0]):
                    # trade履歴が古すぎて参照できない時用の処理
                    print('-------')
                    print('order id is too old.')
                    print(f'check_id: {check_id}')
                    min_id = check_id
                else:
                    raise ccxt.ExchangeNotAvailable(e)

    print(f'------')
    print(f'finish binary search. final id is {trade_info["id"]}')


if __name__ == '__main__':
    main()