FTXの取引履歴を一括で取得するプログラム


Yuckeyといいます!
bot作ってトレードしてます。よろしくです!

タイトルの通り、FTXの取引履歴を指定期間分一括で取得するプログラムを作りました。

既に某所で無料公開してるので、noteも無料公開です。
有料パートは投げ銭用にプログラムコードのzipファイルだけ置いてあります。

作った経緯は…

あ、ここ長いので興味ない人はプログラムコード出てくるとこまで飛ばしてください!

今年の確定申告の時に取引履歴をFTXの画面からCSVダウンロードを使って取得してたんですが、結果的に80万件ぐらいあって正直「何回ダウンロードするねん!」って思いながらダウンロードしてました。

しかもダウンロードしたCSVの履歴を合算すると損益合わないんですよね…
一回でダウンロードできるのが5000件までなので、途切れたところの日時設定してダウンロード…を100回以上もやってればミスするし、ちょうど5000件のところで運悪く同一時刻の取引明細が分割されてると取りこぼす上に、どこを取りこぼしたか全然わからんのです。

そんな状況になると

「あ、これプログラム書いて取得した方がもしかして早いんじゃない?」

とか思ったりもするんだけど、既に1週間近く手動で取得した大量の(不整合な)CSVファイル達を見ると

「も、もうちょっと頑張れば綺麗になるはずだからプログラム作るより手動でやった方が早いはず!」

とかちょっと頭の悪い思考になってくるんですよね。

結果、ちょっと整理してはbotメンテするみたいなgdgdな感じで1ヶ月ぐらい明細の整理してました…

そんな経緯から次の確定申告までには必ず取引履歴取得するプログラムを書こう!と決意し、苦節5ヶ月ようやくできました!

…と言っても実際コード書くのに使ったのは1日か2日ですけど
しかもひよっこさんにつつかれてw

そんな感じで自分用に作成する予定だったのですが、トレードの情報交換をよくしてるひよっこさんも似たような状況で困っていたので、プログラムはそんなに詳しくなくてもググってPythonコードを動かせるレベルの人が簡単に動かせる内容で作成しました。

ここまでは何も有益な情報はないです!
読んでくれた人いるなら…何かありがとうございます!

プログラムコード

そのままコピペしてファイルに保存してAPIキーなど設定すれば動きます。

import datetime
import time
import urllib.parse
from typing import Optional, Dict, Any, List

from requests import Request, Session, Response
from collections import deque
import hmac

"""
【要設定】
 _api_key:取得したいアカウントのAPI Key
 _api_secret:API Keyに対応したAPI Secret
 _subaccount_name:サブアカウントの名称(空文字はメイン)
"""
_api_key = ''
_api_secret = ''
_subaccount_name = ''

"""
【要設定】
 _start:()内にカンマ区切りで年,月,日,時,分,秒
 _end: 上と同じ
 _start <= 取得対象 <= _end で取得する
"""
_start = datetime.datetime(2021, 1, 1).timestamp()
_end = datetime.datetime(2022, 1, 1).timestamp()

"""
【要設定】
 出力するファイル名を設定
 同名のファイルが存在する場合は上書き
 相対パス可。このまま実行するとこのプログラムと同じ場所に出力する。
"""
_csv_file = 'ftx_fills_data_2021.csv'


_session = Session()
_ENDPOINT = 'https://ftx.com/api/'
def _get(path, params = None):
   return _request('GET', path, params=params)


def _request(method, path, **kwargs):
   request = Request(method, _ENDPOINT + path, **kwargs)
   _sign_request(request)
   response = _session.send(request.prepare())
   return _process_response(response)


def _sign_request(request):
   ts = int(time.time() * 1000)
   prepared = request.prepare()
   signature_payload = f'{ts}{prepared.method}{prepared.path_url}'.encode()
   if prepared.body:
       signature_payload += prepared.body
   signature = hmac.new(_api_secret.encode(), signature_payload, 'sha256').hexdigest()
   request.headers['FTX-KEY'] = _api_key
   request.headers['FTX-SIGN'] = signature
   request.headers['FTX-TS'] = str(ts)
   if _subaccount_name:
       request.headers['FTX-SUBACCOUNT'] = urllib.parse.quote(_subaccount_name)


def _process_response(response):
   try:
       data = response.json()
   except ValueError:
       response.raise_for_status()
       raise
   else:
       if not data['success']:
           raise Exception(data['error'])
       return data['result']


def get_fills(market = None, limit = None, start_time = None, end_time = None, order = None, orderId = None):
   return _get(f'fills', {'market': market, 'limit': limit, 'start_time': start_time, 'end_time': end_time, 'order': order, 'orderId': orderId})


def date_to_jstdt(date):
   if len(date) > 25:
       utc_split = datetime.datetime(
           int(date[0:4]),int(date[5:7]),int(date[8:10]),
           int(date[11:13]),int(date[14:16]),int(date[17:19]),int(date[20:26])
       )
   else:
       utc_split = datetime.datetime(
           int(date[0:4]),int(date[5:7]),int(date[8:10]),
           int(date[11:13]),int(date[14:16]),int(date[17:19])
       )

   exec_date = utc_split + datetime.timedelta(hours=+9)
   return datetime.datetime.timestamp(exec_date)

### 被った取引履歴をチェックするための50件IDをスタックする
### 50件でも被る場合は増やせば解決するが処理速度は遅くなる
id_list = deque([], maxlen=50)

with open(_csv_file, 'w') as f:
   print(f'"id","time","market","side","type","size","price","fee","feeCurrency"', file=f)

   while True:

       res = get_fills(limit=5000, start_time=_start, end_time=_end)
       if not res:
           break

       print(f"{time.asctime()} 出力中 -> FROM:{res[-1]['time']} TO:{res[0]['time']}")
       for x in res:
           if x['id'] not in id_list: #被った取引履歴を弾くための考慮
               id_list.append(x['id'])
               print(f'"{x["id"]}","{x["time"]}","{x["market"]}","{x["side"]}","{"Limit" if x["liquidity"]=="maker" else "Market"}","{x["size"]}","{x["price"]}","{x["fee"]}","{x["feeCurrency"]}"', file=f)

       end_t = date_to_jstdt(x['time'])
       if _start >= end_t or _end == end_t:
           break
       _end = end_t #同一秒で履歴が途切れることがあるのであえて次回取得の終了日時は被らせる

動かすのに必要なこと

Pythonのインストールとプログラムの稼働が自力でできることを前提に

 ・Python3.x
 ・タイムゾーンは日本標準時(GMT+9)

で動かしてください。
(pip install は必要ないように考慮したつもりです)

また、ファイル内の以下の部分はご自身で設定をしてください

 ①APIキー設定(必須)
 ②取得日時(任意)
 ③出力ファイル名(任意)

出力されるCSVの内容についての注意点

FTXでダウンロードできるCSVファイルとフォーマットは合わせる努力はしましたが以下の齟齬があります。

 ・小数部の桁数が多いと指数表記になる
 ・type(Market、Limit)の判定が違う場合がある

上記2点は修正方法も分かってますが、そのままでも確定申告用に取引履歴を取得するという目的には問題ないという判断から放置です。(修正する労力が見合わないので)

また、不具合がないことは保証できないので、出力されたCSVファイルの検算は実施してくださいね。

以上です。

役に立ったよ!って思ったら投げ銭してくれると喜びます!

もし、プログラム全然わからないけど動かしたい。という人は投げ銭した上でTwitter(@yukio_sugiyama)にDMしてくれればサポートします!

2021/9/27追記

単元に届かない端数の現物を処理するのに「Convert」というのをやっていると、'type'が'otc'の取引として明細が連携されるみたいです。
この情報はmarketがnull(未設定)となっているため、出力データがおかしくなります。

下のような分岐で出力しないように制御するか、正しい形式で出力されるように考慮入れた方が良さそうです。

if x['type'] == 'otc':

まあ、そんなに「Convert」やってる人は多くないと思うのでそのまま使ってて大丈夫だと思います。

有料パートには明細として出力されるように整形したverのプログラムを置いておきます。

2021/12/28追記

履歴を整理しているうちに色々とカスタマイズをしたので購入者向けにカスタマイズしたものを2つ追加しておきます。

カスタマイズの内容は

  • 全サブアカウント分も明細一括取得

  • 明細を圧縮

です。
詳しくは有料パートに書いておきます!
多分もうこれ以上のカスタマイズはないかなと思います。

2022/2/27追記

取引履歴のカスタマイズではないんですが、色々やってる間に「サブ口座いっぱいあるから手作業で漏れが出るの嫌だな!」と思って、取引履歴と同じような感じで以下を取得するものも作りました。

・funding payments履歴取得
・borrows履歴取得
・lending履歴取得

note増やそうかなとも思たけど、どうせFTX使えなくなるしこのタイミングだと大半の人は計算終わってて必要ないだろうから、このnoteの有料パートに全部載せておきます!

いっぱい取引してる人なら来年もまた使えるかも(¯―¯٥)
これで流石にもうこのnoteを更新するのは最後だと思います(笑)

ここから先は

1,716字 / 7ファイル

¥ 1,000

この記事が気に入ったらサポートをしてみませんか?