見出し画像

BTCFX SFDをグラフで可視化する方法 Python Matplotlib

ビットコインFX(BTCFX)の邪魔者SFDの変化を、PythonのMatplotlibを使ってグラフ化してみました。全コードのコピペだけが必要な場合は、中段の(☆☆)マークまですっ飛ばしてください。

SFDとは、bitflyerのFX_BTC_JPYとBTC_JPYの価格乖離率のことで、5%を超えると発生します。5%を超える方向にエントリー(もしくはイグジット)すると2.5%の手数料が取られ、反対だと2.5%の報奨金がもらえます。結果として、このSFDの報奨金を狙ったbotが大量発生し、相場がSFD=5% を挟んだ状態で異常な動きをし始めます。

このSFD手数料は、勝ちトレードから利益を奪うだけでなく、損失を生み出すこともあるので注意が必要です。

そこで、この邪魔者でしかないSFDに無駄な手数料を取られるような下手なところで売買しないように、SFDの動きをグラフ化してみました。


まず、SFDの計算式ですが、最終約定価格である”ltp”を指標に計算されているそうです。

最終約定価格とは、歩値の一番最後(直近)に約定した価格のことです。

APIを使う場合、以下のurlを叩けば知ることができます。

# BTC_JPYの場合
https://api.bitflyer.jp/v1/getticker?product_code=BTC_JPY

# FX_BTC_JPYの場合
https://api.bitflyer.jp/v1/getticker?product_code=FX_BTC_JPY

結果は、下記のような辞書型で返されます。

{
    "product_code":"FX_BTC_JPY",
    "timestamp":"2018-07-22T02:07:06.427",
    "tick_id":64402199,
    "best_bid":872132.0,
    "best_ask":872133.0,
    "best_bid_size":0.99,
    "best_ask_size":3.99,
    "total_bid_depth":8439.37885844,
    "total_ask_depth":8900.68923088,
    "ltp":872133.0,
    "volume":226708.17562323,
    "volume_by_product":223050.87738331
}

SFDの計算に使用されているのは、下から3行目の'ltp'だそうです。


まずは、Pythonのrequestsを使って'ltp'を抜き出します。

# requestsモジュールを入れます
import requests

# 最終価格を取得する関数作成
def get_LastPrice():
    url = "https://api.bitflyer.jp/v1/getticker"
    param = {
        'product_code' : "FX_BTC_JPY"
        }
    res = requests.get(url,param).json()
    price = res["ltp"]
    
    return price

# 関数を呼び出して、変数priceに代入
price = get_LastPrice()
print(price)

これを叩くと、pythonコンソールに最終価格が表示されます。

872500.0


次に、BTC_JPYでも同様の処理を行い、それぞれの'ltp'からSFDを計算します。

# requestsモジュールを入れます
import requests

# SFDを取得する関数作成
def get_SFD():

    # FX_BTC_JPYの最終価格取得
    url = "https://api.bitflyer.jp/v1/getticker"
    param = {
        'product_code' : "FX_BTC_JPY"
        }
    res = requests.get(url,param).json()
    price = res["ltp"]

    # BTC_JPYの最終価格取得
    param2 = {
        'product_code' : "BTC_JPY"
        }
    res2 = requests.get(url,param2).json()
    price2 = res2["ltp"]

    # SFDの乖離率を計算
    if price>price2:
        SFD = 100*(price-price2)/price2
    elif price<price2:
        SFD = 100*(price2-price)/price2

    return price,price2,SFD

# 関数を呼び出して、変数に代入
price,price2,SFD = get_SFD()
print("FXBTC : " + str(price) + " BTC : " + str(price2) + 
" SFD : " + str(SFD) + "%")

SFDは上下それぞれに乖離する可能性があるので、ifで条件分岐しています。また、SFDを%で表示するために100倍しています。

FXBTC : 869034.0 BTC : 828838.0 SFD : 4.849681119832826%

このままだとSFDの小数点以下がうるさいので、roundを使います。

    # SFDの乖離率を計算
    if price>price2:
        SFD = round(100*(price-price2)/price2,3)
    elif price<price2:
        SFD = round(100*(price2-price)/price2,3)

結果です。

FXBTC : 868827.0 BTC : 828723.0 SFD : 4.839%

だいぶスッキリしました。


しかし、これでは単発のSFDしか得られないので、リアルタイムに継続して取得するようにwhileを使用します。

while文は、time.sleep()と合わせて書くことで、同じ処理を繰り返し行ってくれます。

# requestsモジュールを入れます
# 繰り返し処理のため、timeモジュールも入れます
import requests, time

# SFDを取得する関数作成
def get_SFD():

    # FX_BTC_JPYの最終価格取得
    url = "https://api.bitflyer.jp/v1/getticker"
    param = {
        'product_code' : "FX_BTC_JPY"
        }
    res = requests.get(url,param).json()
    price = res["ltp"]

    # BTC_JPYの最終価格取得
    param2 = {
        'product_code' : "BTC_JPY"
        }
    res2 = requests.get(url,param2).json()
    price2 = res2["ltp"]

    # SFDの乖離率を計算
    if price>price2:
        SFD = 100*(price-price2)/price2
    elif price<price2:
        SFD = 100*(price2-price)/price2

    return price,price2,SFD

# while文で囲み、time.sleep()で繰り返し時間を設定します
while True:
    price,price2,SFD = get_SFD()
    print("FXBTC : " + str(price) + " BTC : " + str(price2) + 
    " SFD : " + str(SFD) + "%")
    time.sleep(1)

while文は同じ処理を繰り返し行います。今回はget_SFDとprintを繰り返します。繰り返し処理はtime.sleep()で設定された時間毎に繰り返され、今回はtime.sleep(1)なので1秒おきです。

FXBTC : 869261.0 BTC : 828358.0 SFD : 4.938%
FXBTC : 869334.0 BTC : 828358.0 SFD : 4.947%
FXBTC : 869250.0 BTC : 828358.0 SFD : 4.937%

ちなみにbitflyerのAPIには呼び出し回数の制限があり、制限を超えると一定期間呼び出しができなくなります。

パブリックAPIなので1アドレスに付き500回/分のようです。1分間に500回ってことは、time.sleep(0.12)以下だとエラー制限にかかるようです。私は0.1にして制限にかかりました。


下記に、説明文の抜粋を引用しておきます。

==================================

HTTP API は、以下のとおり呼出回数を制限いたします。

Private API は 1 分間に約 200 回を上限とします。
IP アドレスごとに 1 分間に約 500 回を上限とします。

注文数量が 0.1 以下の注文を大量に発注するユーザーは、一時的に、発注できる注文数が 1 分間に約 10 回までに制限されることがあります。
システムに負荷をかける目的での発注を繰り返していると当社が判断した場合は、API の使用を制限することがあります。ご了承ください。

==================================


さらに、SFDの詳細な時間も取得しましょう。

bitflyerのAPIが返す時間は、日本時間より9時間遅いので、時間の補正を行います。また、日にちは不要なので、時間だけ表示するように変更しました。

# requestsモジュールを入れます
# 繰り返し処理のため、timeモジュールも入れます
# 時間処理のため、datetimeモジュールを入れます

import requests, time, datetime

# SFDを取得する関数作成
def get_SFD():

    # FX_BTC_JPYの最終価格取得
    url = "https://api.bitflyer.jp/v1/getticker"
    param = {
        'product_code' : "FX_BTC_JPY"
        }
    res = requests.get(url,param).json()
    price = res["ltp"]
    ts = res["timestamp"]

    # BTC_JPYの最終価格取得
    param2 = {
        'product_code' : "BTC_JPY"
        }
    res2 = requests.get(url,param2).json()
    price2 = res2["ltp"]

    # SFDの乖離率を計算
    if price>price2:
        SFD = 100*(price-price2)/price2
    elif price<price2:
        SFD = 100*(price2-price)/price2

    return ts,price,price2,SFD

# while文で囲み、time.sleep()で繰り返し時間を設定します
while True:
    ts,price,price2,SFD = get_SFD()

    ts = datetime.datetime.strptime(ts, '%Y-%m-%dT%H:%M:%S.%f')
    ts = str(ts.hour+9) +":"+ str(ts.minute) +":"+ str(ts.second) +"."+ str(ts.microsecond)

    print("時間 : " + str(ts) + " FXBTC : " + str(price) 
    + " BTC : " + str(price2) + " SFD : " + str(SFD) + "%")
    time.sleep(1)

結果はこんな感じです。

時間 : 12:4:47.557000 FXBTC : 864237.0 BTC : 825446.0 SFD : 4.699%
時間 : 12:4:48.197000 FXBTC : 864049.0 BTC : 825446.0 SFD : 4.677%
時間 : 12:4:49.993000 FXBTC : 863158.0 BTC : 825446.0 SFD : 4.569%

この記事を書いている間に、SFDが落ちてきましたね。。。このプログラムに意味がなくなるかもと不安を感じながらも続けていきます。


最後は、matplotlibに変数を投げてグラフを作成する作業です。

まず、matplotlibは配列が必要ですので、whileで回しながら配列に保存していきます。


import requests,time,datetime

def get_SFD():
    url = "https://api.bitflyer.jp/v1/getticker"
    param = {
        'product_code' : "FX_BTC_JPY"
        }
    res = requests.get(url,param).json()
    price = res["ltp"]
    ts = res["timestamp"]

    param2 = {
        'product_code' : "BTC_JPY"
        }
    res2 = requests.get(url,param2).json()
    price2 = res2["ltp"]

    if price>price2:
        SFD = round(100*(price-price2)/price2,3)
    elif price<price2:
        SFD = round(100*(price2-price)/price2,3)

    return ts,price,price2,SFD

# 空の配列をwhileの外に用意
ts_array,price_array,price2_array,SFD_array = [],[],[],[]

while True:
    ts,price,price2,SFD = get_SFD()

    # appendを使って配列に追加保存
    price_array.append(price)
    price2_array.append(price2)
    SFD_array.append(SFD)

    ts = datetime.datetime.strptime(ts, '%Y-%m-%dT%H:%M:%S.%f')
    ts_array.append(str(ts.hour+9) +":"+ str(ts.minute) +":"+ str(ts.second) +"."+ str(ts.microsecond))

    # 最終(直近)の値は配列の最後なので[-1]で呼び出す
    print("時間 : " + str(ts_array[-1]) + " FXBTC : " + str(price_array[-1]) 
    + " BTC : " + str(price2_array[-1]) + " SFD : " + str(SFD_array[-1]) + "%")
    time.sleep(1)

すると結果はこんな感じ。

時間 : 12:16:52.373000 FXBTC : 865923.0 BTC : 826713.0 SFD : 4.743%
時間 : 12:16:53.763000 FXBTC : 865923.0 BTC : 826713.0 SFD : 4.743%
時間 : 12:16:54.840000 FXBTC : 865923.0 BTC : 826713.0 SFD : 4.743%
時間 : 12:16:56.340000 FXBTC : 865923.0 BTC : 826713.0 SFD : 4.743%
時間 : 12:16:57.340000 FXBTC : 865901.0 BTC : 826713.0 SFD : 4.74%


ここからmatplotlibのモジュールを入れて、グラフに表示します。



(☆☆)


ここからのコードが全コードです。コピペするならここからのコードをご利用ください。

import requests,time,datetime

# グラフ作成用にmatplotlibのモジュールをimport
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

def get_SFD():
    url = "https://api.bitflyer.jp/v1/getticker"
    param = {
        'product_code' : "FX_BTC_JPY"
        }
    res = requests.get(url,param).json()
    price = res["ltp"]
    ts = res["timestamp"]

    param2 = {
        'product_code' : "BTC_JPY"
        }
    res2 = requests.get(url,param2).json()
    price2 = res2["ltp"]

    if price>price2:
        SFD = round(100*(price-price2)/price2,3)
    elif price<price2:
        SFD = round(100*(price2-price)/price2,3)

    return ts,price,price2,SFD

ts_array,price_array,price2_array,SFD_array = [],[],[],[]

while True:
    ts,price,price2,SFD = get_SFD()

    price_array.append(price)
    price2_array.append(price2)
    SFD_array.append(SFD)

    # APIの時間情報を(分:秒)表示に変換
    ts = ts.split(".")
    ts = datetime.datetime.strptime(ts[0], '%Y-%m-%dT%H:%M:%S')
    ts_array.append(ts.strftime("%M:%S"))

    # グラフ表示
    plt.clf() 
    plt.figure
    plt.style.use('ggplot')
    plt.subplots_adjust(left=0.15,right=0.85,bottom=0.1)
    plt.suptitle("SFD (FXBTCJPY-BTCJPY)/BTCJPY [%]")

    # サブプロットでBTCとSFDのグラフ作成
    ax1 = plt.subplot(2,1,1)
    ax2 = ax1.twinx()
    ax3 = plt.subplot(2,1,2)

    # 配列情報をグラフにプロット
    ax1.plot(ts_array,price_array,color="r",label="FX_BTC_JPY")
    ax2.plot(ts_array,price2_array,color="b",label="BTC_JPY")
    ax3.plot(ts_array,SFD_array,color="g",label="SFD")

    # 直近から10個分のデータだけ表示
    ax1.set_xlim([len(ts_array)-10,len(ts_array)])
    ax2.set_xlim([len(ts_array)-10,len(ts_array)])
    ax3.set_xlim([len(ts_array)-10,len(ts_array)])

    # グラフにラベルを表示
    ax1.set_ylabel("FX_BTC_JPY price")
    ax2.set_ylabel("BTC_JPY price")
    ax3.set_xlabel("[min:sec]")
    ax3.set_ylabel("SFD [%]")

    # グラフに凡例を表示
    ax1.legend(loc='upper left')
    ax2.legend(loc='lower left')
    ax3.legend(loc='upper left')

    # SFDグラフに5%のラインを表示する
    ax3.hlines(5,len(ts_array)-10,len(ts_array),"yellow", linestyles='dashed')

    # time.sleep()の代わりにplt.pause()を使用
    plt.pause(1)

これでコードは終了です。

このコードをコピペすれば、下記のようなグラフがリアルタイムで動きます。

上部のグラフはFXBTCとBTCの秒単位の価格を表示しており、下部のグラフがSFDを表示しています。SFDの手数料が発生する5%部分には、黄色の点線でラインを表示しました。

では、コードの中身です。

    # APIの時間情報を(分:秒)表示に変換
    ts = ts.split(".")
    ts = datetime.datetime.strptime(ts[0], '%Y-%m-%dT%H:%M:%S')
    ts_array.append(ts.strftime("%M:%S"))

bitflyerのAPIから排出される時間は以下の形をしています。

2018-07-22T15:08:28.03

小数点以下のマイクロ秒の値は、たまに0になることがあり、エラーが発生します。そこで、splitで小数点以下をカットしました。

カット後は配列になるので、ts[0]として取り出し使用。

datetime.datetime.strptimeで各数字に年月日などの意味合いを持たせます。さらに、strftimeで分と秒だけを抽出します。

    # グラフ表示
    plt.clf() 
    plt.figure
    plt.style.use('ggplot')
    plt.subplots_adjust(left=0.15,right=0.85,bottom=0.1)
    plt.suptitle("SFD (FXBTCJPY-BTCJPY)/BTCJPY [%]")

while文でそのまま回すと、グラフが重ね書きされるので、clf()でグラフをクリアします。plt.style.use('ggplot')は、おまじないのようなもので、グラフがスッキリします。

subplots_adjustでfig端からグラフまでの距離を変更できます。

    # サブプロットでBTCとSFDのグラフ作成
    ax1 = plt.subplot(2,1,1)
    ax2 = ax1.twinx()
    ax3 = plt.subplot(2,1,2)

    # 配列情報をグラフにプロット
    ax1.plot(ts_array,price_array,color="r",label="FX_BTC_JPY")
    ax2.plot(ts_array,price2_array,color="b",label="BTC_JPY")
    ax3.plot(ts_array,SFD_array,color="g",label="SFD")

subplot(2,1,1)は、2行1列のテーブルの1個目を表し、subplot(2,1,2)は、2行1列のテーブルの2個目を表しています。

BTCFXとBTCを同一グラフに2軸で表示するため、twinx()を使用しました。

    # 直近から10個分のデータだけ表示
    ax1.set_xlim([len(ts_array)-10,len(ts_array)])
    ax2.set_xlim([len(ts_array)-10,len(ts_array)])
    ax3.set_xlim([len(ts_array)-10,len(ts_array)])

    # グラフにラベルを表示
    ax1.set_ylabel("FX_BTC_JPY price")
    ax2.set_ylabel("BTC_JPY price")
    ax3.set_xlabel("[min:sec]")
    ax3.set_ylabel("SFD [%]")

    # グラフに凡例を表示
    ax1.legend(loc='upper left')
    ax2.legend(loc='lower left')
    ax3.legend(loc='upper left')

サブプロットはxlimではなくset_xlimです。直近の配列番号から、その10個前までの表示に設定しています。ラベルもylabelではなくset_ylabelです。

legendで凡例を設置するのですが、ax1とax2は同じ場所だと重なってしまうのでupperとlowerに分けました。

    # SFDグラフに5%のラインを表示する
    ax3.hlines(5,len(ts_array)-10,len(ts_array),"yellow", linestyles='dashed')

    # time.sleep()の代わりにplt.pause()を使用
    plt.pause(1)

hlinesでSFD=5%の部分に点線を表示しました。

最後にplt.pause(1)で1秒おきの繰り返し処理になります。


このグラフを表示しながらトレードを行っても、5%ギリギリの場所では手数料が発生したり、報奨金が発生しなかったりします。あくまで参考程度にしていただければと思います。

なお、月並みですが、このグラフを使用して損失を被ったとしても、一切の責任は負いませんので、ご理解ください。質問や提案、コードに対する指摘などは喜んで受け入れますので、コメントください。

今後もpythonを使っていろいろ作ったものを紹介していく予定です。

よろしくお願いします。

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