bitFlyerで取引をしていても、目を離さなければいけないことがあったりするので、約定をSlackに通知してくれるアプリケーションをGolangで作ってみました。

環境

Go v1.9以上がインストールさていることとします。Go言語の日本語ページはこちら。
今回使用するgoのパッケージはslackの通知用のAPIbitflyerの約定を取得するためのAPIです。go getして、とってきましょう。

shell> go get github.com/nlopes/slack
shell> go get github.com/yorumiru/bitflyerapi

ファイル構成

ファイル構成は、以下の写真のようになります。
ディレクトリ構成

$GOPATHの下にgo getで取って来たパッケージのディレクトリとAPIを使用するために必要なTokenを保存するTokenディレクトリを作成します。Tokensディレクトリには、slackのAPIを使用するためのtokenが書かれたslack_token.txt、メッセージを送るチャンネルのIDの書かれた書かれたslack_token.txt、そして、bitflyerから発行されるTokenとシークレットTokenが書かれたbitflyer_token.txtの3つのファイルを作成します。

それぞれのファイルには、それぞれTokenをそのまま書きます。ダブルクォーテーションなどは必要ありません。ちなみに、全くエラー処理を書いていないので、デバック等をしたいときは、各自で追加してください。

slack_token.txt

YOUR_SLACK_TOKEN

slack_channel.txt

YOUR_SLACK_CHANNEL_ID

bitflyer_token.txt

YOUR_BITFLYER_TOKEN_ID
YOUR_BITFLYER_SECRET_TOKEN_ID

SlackのChannelIDはこちらにアクセスして取得します。
https://api.slack.com/methods/channels.list/test
argument token のところでSlackのTOKENが発行されていなければGenerate tokens to test with here.をクリックして生成します。

取得したいSlackのワークスペースのTokenをセットして緑色の Test method というボタンをクリックすると、
それぞれのidとnameなどの値が取得されます。その中から使用したチャンネルに対応するidを取得します。

Slack APIの設定

bitFlyerのAPI用のTokenはLightningの左側のバーガーメニューをクリックして出て来るAPIの項目から生成します。
注意事項をしっかりと読み、生成します。
今回、使用したのは約定一覧の取得だけなので、そこにだけチェックを入れました。

Bitflyer API設定

メインロジック

ファイルの準備ができたら、mainパッケージを書いていきます。”/Users/eng-labs/go”を$GOPATHとして設定していると想定して書いていますので、適宜書き換えてください。基本はコピペで大丈夫だと思います。今回は”FX_BTC_JPY”を指定しているのでBTCのFXに関する約定を取得しています。

package main

import (
    "encoding/json"
    "bufio"
    "fmt"
    "os"
    "time"
    "strconv"

    "github.com/nlopes/slack"
    "github.com/yorumiru/bitflyerapi"
)

const(
    SLACK_USERNAME = "BITFLYER BOT"  //slackに通知するときのBOTの名前
)

type Tokens struct{
    Slack_token string
    Slack_channel_id string
    Bitflyer_token string
    Bitflyer_secret_token string
}

type Execution struct{
    Id int `json:"id"`
    Side string `json:"side"` 
    Size float32 `json:"size"` 
    Price float32 `json:"price"` 
}

func getLatestExecution(bitflyerapi *bitflyerapi.BitflyerAPI) int {
    var exe []Execution
    var queries = make(map[string]string)
    queries["product_code"]     = "FX_BTC_JPY"  //"BTC_JPY"で現物
    queries["count"] = "1"
    
    var s string = bitflyerapi.GetExecutions(queries)
    json.Unmarshal([]byte(s), &exe);
    return exe[0].Id
}

func getExecution(id int, bitflyerapi *bitflyerapi.BitflyerAPI) (int, string){
    var exe []Execution
    var message string
    var queries = make(map[string]string)
    queries["product_code"]     = "FX_BTC_JPY"  //"BTC_JPY"で現物
    queries["after"] = strconv.Itoa(id)
    var s string = bitflyerapi.GetExecutions(queries)
    json.Unmarshal([]byte(s), &exe);
    if len(exe) == 0 {
        return id, ""
    }else{
        for _, e := range exe{
            message += fmt.Sprintf("%s %.3f[BTC] Price: %.2f[円]\n", e.Side, e.Size, e.Price)
            id = e.Id
        }
        return id, message 
    }
}

func runTrade(channel string, slack_api *slack.Client, bitflyer_api *bitflyerapi.BitflyerAPI) int{
    var id int
    var str string
    id = getLatestExecution(bitflyer_api)

    params_first := slack.PostMessageParameters{Username: SLACK_USERNAME}
    _,_,err := slack_api.PostMessage(channel, "start bot", params_first)   //動作開始時に通知
    if err != nil{
        fmt.Println(err)
        return 0
    }
    for{
        id, str = getExecution(id,bitflyer_api)
        if str != "" {
            params := slack.PostMessageParameters{Username: SLACK_USERNAME}
            _,_,err := slack_api.PostMessage(channel, str, params) 
            if err != nil{
                fmt.Println(err)
                return 0
            }
            fmt.Print(str)
        }
        time.Sleep(3 * time.Second)
    }
    return 0
}

func main() {
    var tokens Tokens

    scanTokens(&tokens)
    
    slack_api := slack.New(tokens.Slack_token)
    bitflyer_api := bitflyerapi.New( tokens.Bitflyer_token,tokens.Bitflyer_secret_token)

    os.Exit(runTrade(tokens.Slack_channel_id, slack_api, bitflyer_api))
}

func scanTokens(tokens *Tokens) {
    //"/Users/eng-labs/go"を$GOPATHとして設定していると想定して書いていますので、適宜書き換えてください。

    fp_slack, _ := os.Open("/Users/eng-labs/go/src/tokens/slack_token.txt") //書き換えが必要
    scanner_slack := bufio.NewScanner(fp_slack)
    scanner_slack.Scan()
    tokens.Slack_token = scanner_slack.Text()
    fp_slack.Close()

    fp_slack_channel, _ := os.Open("/Users/eng-labs/go/src/tokens/slack_channel.txt")  //書き換えが必要
    scanner_slack_channel := bufio.NewScanner(fp_slack_channel)
    scanner_slack_channel.Scan()
    tokens.Slack_channel_id = scanner_slack_channel.Text()
    fp_slack_channel.Close()

    fp_bitflyer, _ := os.Open("/Users/eng-labs/go/src/tokens/bitflyer_token.txt")  //書き換えが必要
    scanner_bitflyer := bufio.NewScanner(fp_bitflyer)
    scanner_bitflyer.Scan()
    tokens.Bitflyer_token = scanner_bitflyer.Text()
    scanner_bitflyer.Scan()
    tokens.Bitflyer_secret_token = scanner_bitflyer.Text()
    fp_bitflyer.Close()
}

保存をしたら、実行をします。

shell> go run main.go

実行をして、対応するslackのチャンネルに投稿ができていれば成功です。あとは、適当に約定して見ると、下のキャプチャのように通知されます。
Slack通知画面
常に動かしたい方はサーバーなどにアップロードして設定してみてください。

解説

getLatestExecution()で最後に約定した情報(ID)を取得します。
getExecution()で取得したIDより新しいものがあれば取得し、Slackに表示します。
詳しくはbitFlyerのAPIのドキュメントを参照してください。