bitFlyer FXの約定履歴から損益グラフを描くpythonプログラム
今日はbitFlyer FXの損益グラフを描くプログラムを公開したいと思います。 bitFlyerにはデフォルトで損益グラフを表示する機能がありますが、現物もFXも全部込みの損益グラフが表示されるため、現物持った状態でFXでbotを動かしていると、純粋なbotの損益を知ることができません。 また、損益しか表示されないので、価格変化との関係性や、取引ごとのポジションの変化もグラフだけからは読み取れません。
今回ここで公開するプログラムは、APIから自分の約定履歴を取得し、価格、損益、ポジションをプロットするものです。 例えばこんなグラフを描くことができます。
グラフからは、ここで想定以上にポジション掴んでるなとか、ここの急変動で焼かれてるな、などということを読み取れるようになります。
なお、私はtwitterでもたまにこの損益グラフを晒していますが、たいしたbotでもないのでどうせ参考にならんでしょと思って晒しています。 botにおいて重要なのは見ている指標だと思いますが、正直自分でこのグラフだけ見ても自分のbotのロジックを推測できません… 強botterなら推測できるかもしれませんが、そんな人は私ごときのbotは参考にするまでもないはずなので…
というわけで、ロジックばれを恐れず損益を公開してくれる人が増えたらいいなぁということでプログラムを公開します。 どんなbotがいるんだろうという興味本位です(笑)
なお、私は普段pythonはほとんどさわらないので、初心者丸出しのコードになっています。 forとかappendバリバリ使ってますが、温かく見守ってもらえると助かります。
約定履歴取得&csv保存プログラム
指定した期間の自分の約定履歴を取得し、csv形式で保存します。
※Private APIをさわっています。悪意のあるコードは書いていませんが、コードをよく読んで、おかしな挙動をしていないか確認し、自己責任で実行してください。トラブルが起こっても責任は取れません。
import pandas as pd import pybitflyer from dateutil.parser import parse import datetime import time from pytz import timezone #約定履歴取得期間 t_start = parse("2019-03-20 00:00:00" + "+09:00") t_end = parse("2019-03-21 00:00:00" + "+09:00") #bitflyer APIキー api_key = "****" api_secret = "****" api = pybitflyer.API(api_key=api_key, api_secret=api_secret) product_code = "FX_BTC_JPY" list_executions = [] executions = api.getexecutions(product_code=product_code, count=500) count = 0 while len(executions) > 0: for execution in executions: dt = parse(execution["exec_date"] + "+00:00").astimezone(timezone("Asia/Tokyo")) if t_start <= dt < t_end: #取得期間内なら list_executions.append([dt, execution["side"], execution["price"], execution["size"]]) if dt < t_start: break else: last_id = executions[len(executions) - 1]["id"] print("取得中..." + dt.strftime("%Y-%m-%d %H:%M:%S")) time.sleep(2) executions = api.getexecutions(product_code=product_code, count=500, before=last_id) df = pd.DataFrame(list_executions[::-1], columns=["date", "side", "price", "size"]) df.to_csv("executions.csv")
上のプログラムは愚直に最新の約定履歴から順番に取得するので、昔の約定履歴を取得する場合は工夫が必要です。 Nagiさん(@Nagi7692)のnoteが参考になるかと思います。
損益プロットプログラム
上のプログラムで保存したcsvから、損益をプロットします。なお、bitFlyerの丸め処理やSFDは考慮していないので、厳密な精度はありません。 また、約定履歴保存期間の初期ポジションを手動で入力する必要があります(約定履歴から知る術を思いつきません…)。 ポジションが0に収束するタイプのbotであれば、ポジションの平均値が0になるようにすればある程度正しいと思います。
また、コード中にコメントで書いていますが、損益を正規化して絶対値を隠したり、ポジションのプロットの線を太くして細かい情報を隠したりできます。 差支えない範囲でいいので、損益を公開するbotterが増えたら嬉しいです。
import pandas as pd import statistics import matplotlib import matplotlib.pyplot as plt import pylab import matplotlib.dates as mdates init_position_BTC = 0 #初期ポジションを手動で指定 df = pd.read_csv("executions.csv") date = pd.to_datetime(df["date"]) side = df["side"] price = df["price"] size = df["size"] position_BTC = init_position_BTC position_JPY = -init_position_BTC * price[0] list_price = [] list_position = [] list_profit = [] list_date = [] #計算 for i in range(len(date)): list_date.append(date[i]) list_price.append(price[i]) if side[i] == "SELL": delta_BTC = -size[i] delta_JPY = size[i] * price[i] else: delta_BTC = size[i] delta_JPY = -size[i] * price[i] position_BTC += delta_BTC position_JPY += delta_JPY list_position.append(position_BTC) list_profit.append(position_BTC * price[i] + position_JPY) ave_position = statistics.mean(list_position) print("average position : {0}".format(ave_position)) print("profit : {0}".format(list_profit[len(list_profit) - 1])) #正規化したかったら #profit_max = max(list_profit) #list_profit = list(map(lambda x: x / profit_max, list_profit)) #グラフ matplotlib.rcParams["timezone"] = "Asia/Tokyo" fig = plt.figure(figsize=(10, 4)) fig.subplots_adjust(left=0.1, bottom=0.15, right=0.9, top=0.95) #price ax1 = fig.add_subplot(2, 1, 1) ax1.plot(list_date, list_price, "C0", label="Price") #profit #第2Y軸 ax2 = ax1.twinx() ax2.plot(list_date, list_profit, "C1", label="Profit") h1, l1 = ax1.get_legend_handles_labels() h2, l2 = ax2.get_legend_handles_labels() ax1.legend(h1+h2, l1+l2, loc='lower right') ax1.set_ylabel("Price [JPY/BTC]") ax1.grid(True) ax2.set_ylabel("Profit [JPY]") pylab.setp(ax1.get_xticklabels(), visible=False) #position ax3 = fig.add_subplot(2, 1, 2) ax3.plot(list_date, list_position) #線を太くしたかったらlinewidth=5とか ax3.set_ylabel("Position [BTC]") ax3.grid(True) ax3.set_xlabel("Date") ax3.xaxis.set_major_formatter(mdates.DateFormatter("%m/%d %H:%M")) plt.show()
バグなどありましたら、コメントかtwitterで教えていただけると助かります。