[BTC][Bot] [Python] 第三回 取引所の取引データを保存する

はじめに

こんにちは _santa365です。
第一回 はじめのBot環境構築 ではベースとなるプロジェクトの作成、第二回 取引所の取引データを取得する では実際の取引所と通信をしてデータの取得を行いました。
今回は第二回の内容を少し発展し、取引所の実際の取引データを取得し、保存していこうと思います。取引データを保存することでバックテスト(過去のデータを使ってBotの有効性をテストする)をすることができるようになります。

取引所の取引データを保存する

前回の最終的なコードは以下のようになってました

import asyncio
import pybotters

async def ftx():
  async with pybotters.Client() as client:
      wstask = await client.ws_connect(
          'wss://ftx.com/ws/',
          send_json=[
              {'op': 'subscribe', 'channel': 'trades', 'market': 'BTC-PERP'},
          ],
          hdlr_json=pybotters.print_handler,
      )
      await wstask

def main():
  try:
      asyncio.run(ftx())
  except KeyboardInterrupt:
      pass

今回は、これを改良して手元にデータを保存するようにします。

方針
方針としては簡単です。データ保存用のHandlerを作成し、それを`pybotters.print_handler` の代わりに使います。
まずpybottersのprint_handlerの中身を見てみると下記のような形になっています(Code)。

def print_handler(msg: Any, ws: aiohttp.ClientWebSocketResponse):
   print(msg)

これと同様の引数の形式をもつ関数`save_to_csv_handler`を定義します。今回はws引数は使用しないため引数名を_にしています。

from typing import Any
import aiohttp

def save_to_csv_handler(msg: Any, _: aiohttp.ClientWebSocketResponse):
    print(msg)

pybotters.print_handlerの部分をsave_to_csv_handlerに書き換えます。ここまでのコードをまとめると下記のようになります。
書き終えたコードを実行してみてください。挙動は `pybotters.print_handler` のときと同じですが、中身は定義した関数を使うようになっています。

import asyncio
import pybotters
import aiohttp
from typing import Any

async def ftx():
   async with pybotters.Client() as client:
       wstask = await client.ws_connect(
           'wss://ftx.com/ws/',
           send_json=[
               {'op': 'subscribe', 'channel': 'trades', 'market': 'BTC-PERP'},
           ],
           hdlr_json=save_to_csv_handler,
       )
       await wstask

       pybotters.FTXDataStore()

def save_to_csv_handler(msg: Any, _: aiohttp.ClientWebSocketResponse):
   print(msg)

def main():
   try:
       asyncio.run(ftx())
   except KeyboardInterrupt:
       pass

次に、`save_to_csv_handler` の中身を変更して受信したデータをファイルに保存する処理にします。
Websocketによって受信するデータは主に以下のようになっています。

{'type': 'pong'}
{'type': 'subscribed', 'channel': 'trades', 'market': 'BTC-PERP'}
{'channel': 'trades', 'market': 'BTC-PERP', 'type': 'update', 'data': [{'id': 1514850859, 'price': 39022.0, 'size': 0.0128, 'side': 'buy', 'liquidation': False, 'time': '2021-07-30T15:37:06.154989+00:00'}]}

上記の情報では `pong` や `subscribed` といった不必要なデータが含まれています。ファイルを保存する前にデータの不必要な部分を削除するため関数の中身を少し書き換えます。

def save_to_csv_handler(msg: Any, _: aiohttp.ClientWebSocketResponse):
   # msgに`data`がなければ、Trade情報ではない
   if 'data' in msg:
       channel: str = msg['channel']
       market: str = msg['market'] if 'market' in msg else ''
       datas: Any = msg['data']
       # Trade情報の場合、msgの`channel`は`trades`になる
       if msg['channel'] == 'trades':
           for data in datas:
               print(data)

やってることはシンプルです。トレード情報のデータには`data`キーが絶対含まれているので、まず受信したデータの中に`data`キーがあるかチェック。その後、受信したデータの中からさらに必要なデータのを書き抜きます。
これを再度実行すると表示は以下のようになります

$ poetry run pybot
{'id': 1514858964, 'price': 39066.0, 'size': 0.0828, 'side': 'sell', 'liquidation': False, 'time': '2021-07-30T15:40:07.506434+00:00'}
{'id': 1514858965, 'price': 39066.0, 'size': 0.0172, 'side': 'sell', 'liquidation': False, 'time': '2021-07-30T15:40:07.506434+00:00'}
{'id': 1514858972, 'price': 39066.0, 'size': 0.0019, 'side': 'sell', 'liquidation': False, 'time': '2021-07-30T15:40:07.586899+00:00'}

必要な情報のみを取得することができましたね。上記のデータをファイルに保存していきます。今回はCSVファイルに保存します。また一つのファイルがすごく長くなるのを避けるため、日付毎にファイルを分けます

import os
import csv
from datetime import datetime as dt

def save_to_csv_handler(msg: Any, _: aiohttp.ClientWebSocketResponse):
   if 'data' in msg:
       channel: str = msg['channel']
       market: str = msg['market'] if 'market' in msg else ''
       datas: Any = msg['data']
       if msg['channel'] == 'trades':
           for data in datas:
               dir_path = 'data/ftx/raw'
               file_name = data['time'][0:10];
               file_path = f'{dir_path}/{file_name}.csv';

               if not os.path.isdir(dir_path):
                   os.makedirs(dir_path, exist_ok=True)

               if not os.path.isfile(file_path):
                   with open(file_path, 'w') as f:
                       writer = csv.writer(f)
                       writer.writerow(['time', 'side', 'price', 'size'])

               with open(file_path, 'a') as f:
                   writer = csv.writer(f)
                   writer.writerow([data['time'], data['side'], data['price'], data['size']])

処理の内容は上から順番にせ大きく3つに別れています。

1. 保存する先のディレクトリがなければ作成する。今回は、`data/ftx/raw`に保存する。
2. 保存ファイルが無ければ、作成する。CSVなのでファイル作成時はHead情報をはじめに書き加えています。
3. 実際のトレードデータを保存する

今回保存したデータは生データ(raw)になるのでここからローソク足の作成やバックテストに必要なデータを作成をすることができます。

以上で取引データの保存は完了です。おつかれさまでした!
次は保存した生データを使ってローソク足の作成にトライしていきます!

Twitterで最新記事情報等流しています!お見逃しがないようフォローまたはリストに追加していただけると幸いです!

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