bitFlyerのREALTIME APIを動かしてBitcoinの現在価格を取得する

2021-03-31

BitflyerのREALTIME APIを動かしてみよう

bitFlyerLightning:Realtime API JSON-RPC 2.0 over WebSockett 本日は、このサイトで記載されている機能を実装していきます。

モチベーション

不労所得というのは誰しも一度は夢見るものだと理解しております。 仮想通貨の自動売買プログラムを作ることを目標進めていきましょう。 今回はまず、常に最新の価格を取得するところから始めます。

免責事項

本サイトの掲載情報が元で発生した不利益に対する責任は一切負わず、いかなる保証も行いません。 仮想通貨取引にはリスクがあることを十分に承知頂き、実施する場合は全て自己責任でお願い致します。

事前準備

過去記事をベースに構築しております。 iniファイルを読み込む用のテンプレート logファイルを作成する方法 Lightning APIを読み出す方法

ファイル構成

ファイル構成はこのようになっています。

E:.
│  Config.ini
│  go.mod
│  go.sum
│  Trade.go
│─bitflyer
|   bitflyer.go
│─utils
|   logging.go
└─Config
    Config.go

bitflyer.go

Lightning APIを読み出す方法で作成したbitflyer.goに機能を追加していきます。

JsonRPC2構造体を作成する

JSON-RPC 2.0 Specification

  1. Response object 参考にJSON-RPC2.0フォーマットのデータを格納する構造体を記載する。

Response Messageはこのような形式で応答されることを想定している。

{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}

golangでこの応答を格納する構造体はこのように作成できます。

type JsonRPC2 struct {
    Version string      `json:"jsonrpc"`
    Method  string      `json:"method"`
    Params  interface{} `json:"params"`
    Result  interface{} `json:"result,omitempty"`
    Id      *int        `json:"id,omitempty"`
}

SubscribeParamsを用意する

Realtime APIのサーバーメソッドで定義されている。 この構造体をgolangで実装します。

params: { channel: "(Channel Name)" }

subscribe : チャンネルの購読を開始します。 購読が開始されるとtrueが返ります。

構造体はこのように定義しました。

type SubscribeParams struct {
    Channel string `json:"channel"`
}

GetRealTimeTickerで仮想通貨価格を実装します

関数の全体構成を示します

//https://bf-lightning-api.readme.io/docs/endpoint-json-rpc
func (api *APIClient) GetRealTimeTicker(product_code string, ch chan<- Ticker) {
    u := url.URL{Scheme: "wss", Host: "ws.lightstream.bitflyer.com", Path: "/json-rpc"}
    log.Printf("connecting to %s", u.String())

    c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
    if err != nil {
        log.Fatal("dial:", err)
    }
    defer c.Close()

    //https://bf-lightning-api.readme.io/docs/realtime-ticker
    channel := fmt.Sprintf("lightning_ticker_%s", product_code)
    if err := c.WriteJSON(&JsonRPC2{Version: "2.0", Method: "subscribe", Params: &SubscribeParams{channel}}); err != nil {
        log.Fatal("subscribe:", err)
        return
    }

    for {
        message := new(JsonRPC2)
        if err := c.ReadJSON(message); err != nil {
            log.Println("read:", err)
            return
        }

        if message.Method == "channelMessage" {
            switch v := message.Params.(type) {
            case map[string]interface{}:
                for key, binary := range v {
                    if key == "message" {
                        marshaTic, _ := json.Marshal(binary)
                        var ticker Ticker
                        json.Unmarshal(marshaTic, &ticker)
                        ch <- ticker
                    }
                }
            }
        }
    }

エンドポイントに接続

WebSocketに用意されているセキュア通信用のプロトコルwssを用いてAPIに接続します。

JSON-RPC 2.0 over WebSocketに記載されているアドレスに接続する。 手順としては、urlを生成しDial関数でセッションを張ります。

wss://ws.lightstream.bitflyer.com/json-rpc
    u := url.URL{Scheme: "wss", Host: "ws.lightstream.bitflyer.com", Path: "/json-rpc"}
    log.Printf("connecting to %s", u.String())

    c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
    if err != nil {
        log.Fatal("dial:", err)
    }
    defer c.Close()

realtime-Tickerチャンネル接続用のJSONを作成する

REALTIME API:Tickerで指定されているチャンネル情報をJSONRPC2に追記します。

チャンネル名はこのように定義されています。

lightning_ticker_{product_code}

product_codeはConfig.iniに追加します。

    channel := fmt.Sprintf("lightning_ticker_%s", product_code)
    if err := c.WriteJSON(&JsonRPC2{Version: "2.0", Method: "subscribe", Params: &SubscribeParams{channel}}); err != nil {
        log.Fatal("subscribe:", err)
        return
    }

websocketから値を受信し、チャンネルに書き込む処理

bitFlyer Lightning APIは、Tickerの内容に更新があったとき配信される作りとなっているため更新された値を、channelに書き込んでいる。

    for {
        message := new(JsonRPC2)
        if err := c.ReadJSON(message); err != nil {
            log.Println("read:", err)
            return
        }

        if message.Method == "channelMessage" {
            switch v := message.Params.(type) {
            case map[string]interface{}:
                for key, binary := range v {
                    if key == "message" {
                        marshaTic, _ := json.Marshal(binary)
                        var ticker Ticker
                        json.Unmarshal(marshaTic, &ticker)
                        ch <- ticker
                    }
                }
            }
        }
    }

main.go

先程作成した関数を呼び出します。 make(chan)でTickerチャンネルを作成し、実装したGetRealTimeTickerを呼び出します。

func main() {
    utils.LoggingSetting(config.Config.LogFile)

    apikey := config.Config.ApiKey
    secretKey := config.Config.ApiSecret
    productCode := config.Config.ProductCode

    apiClient := bitflyer.New(apikey, secretKey)

    tickerChannel := make(chan bitflyer.Ticker)

    go apiClient.GetRealTimeTicker(productCode, tickerChannel)
    for ticker := range tickerChannel {
        fmt.Println(ticker)
    }
}

これにて完成です。お疲れさまでした。

まとめ

bitFlyer APIを用いて価格を取得できました。 このAPIでで自動売買も行うことができるようです。 DBにデータを溜め込み、機械学習を使った価格予測というのが今後の遊び方としては考えられます。 実際、仮想通貨の価格だけで予測するのは限界があるような気もしますが…。