# 僕の個人的なクセと基本的なapiの処理 ## 一番初めの状態 たとえばbinanceでUSDT先物のkline/candlestickデータ(open,high,low,close,volumeとかのohlcデータ)を取る関数だったらbinanceのapiドキュメント https://binance-docs.github.io/apidocs/futures/en/#kline-candlestick-data を参照して ```python= import requests import pandas as pd class binance_api: def ohlc_data(self): symbol = "BTCUSDT" #今回はビットコインのデータをとる(例えばイーサリアムがほしければETHUSDTとか) interval = "4h" #4時間足のデータをとる(enum形式の記述についてはbinanceのapiドキュメント参照) response = requests.get(f"https://fapi.binance.com/fapi/v1/klines?symbol={symbol}&interval={interval}") #BTCUSDT、4時間足データ response = response.json() #responseの二次元配列から必要なデータのみをpandasで扱いやすい形に直す関数 def two_dim_array_to_dic(res): dic = { "symbol" : symbol, "date" : res[0], "open" : res[1], "high" : res[2], "low" : res[3], "close" : res[4], "volume" : res[5], } return dic df = map(two_dim_array_to_dic, response) df = pd.DataFrame(df) df["date"] = df["date"].apply(lambda x: pd.to_datetime(x,unit="ms")) #timestampだと分かりにくいから日付型に直す、binanceのtimestampは12ケタのmsオーダー df[["open","high","low","close"]] = df[["open","high","low","close"]].applymap(float) #レスポンスは全部strで返されてだるいから全部floatに直しとく print(df) return if __name__ == "__main__": ba = binance_api() ba.ohlc_data() ``` ``` symbol date open high low close volume 0 BTCUSDT 2022-04-20 12:00:00 42070.70 42160.00 41037.30 41247.10 84586.898 1 BTCUSDT 2022-04-20 16:00:00 41247.00 41444.00 40800.00 41177.60 51498.554 2 BTCUSDT 2022-04-20 20:00:00 41177.50 41660.00 41118.20 41327.70 26596.361 3 BTCUSDT 2022-04-21 00:00:00 41326.00 41768.00 41292.00 41673.90 22093.892 4 BTCUSDT 2022-04-21 04:00:00 41673.90 41940.00 41398.00 41833.70 26747.921 .. ... ... ... ... ... ... ... 495 BTCUSDT 2022-07-12 00:00:00 19954.30 19994.80 19780.00 19934.30 75346.940 496 BTCUSDT 2022-07-12 04:00:00 19934.20 20048.90 19618.20 19722.90 105992.070 497 BTCUSDT 2022-07-12 08:00:00 19722.90 19843.00 19552.40 19765.10 106267.951 498 BTCUSDT 2022-07-12 12:00:00 19765.00 19999.50 19680.80 19876.90 132675.241 499 BTCUSDT 2022-07-12 16:00:00 19876.90 19993.00 19749.10 19756.20 49451.165 [500 rows x 7 columns] ``` みたいに僕は書く。 pandas大好き人間だから結構特殊(?)な書き方かも。 ## try-exceptでエラー処理 でも上のコードのままだと、例えば8行目で通信エラーが起きたときの処理を一切考慮していない。 だからとりあえずtry-exceptでエラー処理を実装したい。 当然本当は各行の処理でエラー内容に応じて細かく分岐させるのが良いに決まってるけど、横着して僕は関数全体の処理をtryでくくって、Exceptionに全部エラーぶん投げるとかいう力技くそコードで書いてる。 そのコードで例えばこんな風に存在しない銘柄のapiを呼ぼうとしたときに発生したエラーに対してはこんな風に分岐できる。 ```python= import requests import pandas as pd import traceback class binance_api: def ohlc_data(self): try: symbol = "BTUSDT" #今回はビットコインのデータをとる(例えばイーサリアムがほしければETHUSDTとか) interval = "4h" #4時間足のデータをとる(enum形式の記述についてはbinanceのapiドキュメント参照) response = requests.get(f"https://fapi.binance.com/fapi/v1/klines?symbol={symbol}&interval={interval}") #BTCUSDT、4時間足データ response = response.json() #responseの二次元配列から必要なデータのみをpandasで扱いやすい形に直す関数 def two_dim_array_to_dic(res): dic = { "symbol" : symbol, "date" : res[0], "open" : res[1], "high" : res[2], "low" : res[3], "close" : res[4], "volume" : res[5], } return dic df = map(two_dim_array_to_dic, response) df = pd.DataFrame(df) df["date"] = df["date"].apply(lambda x: pd.to_datetime(x,unit="ms")) #timestampだと分かりにくいから日付型に直す、binance df[["open","high","low","close"]] = df[["open","high","low","close"]].applymap(float) #レスポンスは全部strで返されてだるいから全部floatに直しとく print(df) return except Exception as e: print("\n___エラー発生___\n") traceback.print_exc() return if __name__ == "__main__": ba = binance_api() ba.ohlc_data() ``` ``` ___エラー発生___ Traceback (most recent call last): File "c:/Users/nagoy/OneDrive/デスクトップ/save/to_kishimoto/test.py", line 27, in ohlc_data df = pd.DataFrame(df) File "C:\Users\nagoy\anaconda3\lib\site-packages\pandas\core\frame.py", line 502, in __init__ data = list(data) File "c:/Users/nagoy/OneDrive/デスクトップ/save/to_kishimoto/test.py", line 21, in two_dim_array_to_dic "close" : res[4], IndexError: string index out of range ``` このエラー内容を読んでも原因が探りにくいし正直くそみたいな設計だとは思うけど、とりま最低限上手く行ってないことだけは分かるしまあこれでいったんヨシとする。 ## whileでエラー時リトライ、閾値超えたら強制終了 エラーの時は同じ処理を何回か繰り返してみて、ある一定回数以上試してもだめなら処理を強制終了とすれば実運用として最低限使えるものにはなりそう。 だからtryの上からさらにwhileでくくってみる。 ```python= import requests import pandas as pd import traceback import time import sys class binance_api: def ohlc_data(self): retry_time = 10 retry_th = 10240 while retry_time<=retry_th: try: symbol = "BTUSDT" #今回はビットコインのデータをとる(例えばイーサリアムがほしければETHUSDTとか) interval = "4h" #4時間足のデータをとる(enum形式の記述についてはbinanceのapiドキュメント参照) response = requests.get(f"https://fapi.binance.com/fapi/v1/klines?symbol={symbol}&interval={interval}") #BTCUSDT、4時間足データ response = response.json() #responseの二次元配列から必要なデータのみをpandasで扱いやすい形に直す関数 def two_dim_array_to_dic(res): dic = { "symbol" : symbol, "date" : res[0], "open" : res[1], "high" : res[2], "low" : res[3], "close" : res[4], "volume" : res[5], } return dic df = map(two_dim_array_to_dic, response) df = pd.DataFrame(df) df["date"] = df["date"].apply(lambda x: pd.to_datetime(x,unit="ms")) #timestampだと分かりにくいから日付型に直す、binance df[["open","high","low","close"]] = df[["open","high","low","close"]].applymap(float) #レスポンスは全部strで返されてだるいから全部floatに直しとく print(df) return except Exception as e: print("\n___エラー発生___\n") traceback.print_exc() print(f"{retry_time} 秒後にリトライします") time.sleep(retry_time) retry_time *= 2 print("リトライ回数の閾値を超えたためbotを強制終了します") sys.exit() if __name__ == "__main__": ba = binance_api() ba.ohlc_data() ``` ``` ___エラー発生___ Traceback (most recent call last): File "c:/Users/nagoy/OneDrive/デスクトップ/save/to_kishimoto/test.py", line 32, in ohlc_data df = pd.DataFrame(df) File "C:\Users\nagoy\anaconda3\lib\site-packages\pandas\core\frame.py", line 502, in __init__ data = list(data) File "c:/Users/nagoy/OneDrive/デスクトップ/save/to_kishimoto/test.py", line 26, in two_dim_array_to_dic "close" : res[4], IndexError: string index out of range 10 秒後にリトライします ... リトライ回数の閾値を超えたためbotを強制終了します ``` こんな感じ。 ftxapimの全関数をこの考え方を基本にして書いてる。 でもさすがにこの書き方だと保守管理しにくすぎて発狂しそうだからいつか根本的に変えると思う。 今のところはこれが独学素人の限界。