はじめに
今回は自分のAPIサーバで仮想通貨の現在価格情報を返す機能が欲しかったので取引所のAPIを使って実装しました。
データの取得先
価格を取得するために、GMOコインとCoincheck を利用しました。
APIについて
■ レスポンス結果(例)
- GMOコイン
{ "data": [ { "symbol": "BTC_JPY", "last": "4554436", "timestamp": "2022-03-11T07:22:19.507Z" } ] }
{ "symbol": "btc_jpy", "last": 4518231, "time": "2022/02/21-21:25:42" }
ソースコード
├── README.md ├── api │ ├── base.go │ ├── client.go │ ├── crypto_coincheck.go │ ├── crypto_coincheck_test.go │ ├── crypto_gmo.go │ ├── crypto_gmo_test.go │ ├── healthcheck.go │ ├── investment_trust.go │ ├── metal.go │ └── staticcheck.conf ├── app.yaml ├── authentication │ └── basic_auth.go ├── go.mod ├── go.sum ├── main.go └── secret.yaml
■ main.go
package main import ( "net/http" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "google.golang.org/appengine" "github.com/yhidetoshi/apiEchoGAE-private/api" "github.com/yhidetoshi/apiEchoGAE-private/authentication" ) var e = createMux() func createMux() *echo.Echo { e := echo.New() return e } func main() { // Echoインスタンス作成 // http.Handle("/", e) e.Use(middleware.Logger()) e.Use(middleware.Recover()) e.Use(middleware.Gzip()) e.Use(authentication.BasicAuth()) // Basic Auth cGmo := api.ClientCryptoGMO{} cGmo.NewClientCryptoGMO() cCc := api.ClientCryptoCoincheck{} cCc.NewClientCryptoCoincheck() // ルーティング // // ヘルスチェック e.GET("/api/healthcheck", api.Healthcheck) // 貴金属の価格を取得 e.GET("/api/metal", api.FetchMetal) // ISINコードを引数に基準価格を取得 // curl -sS localhost:1323/investment-trust/JP90C000GKC6 e.GET("/api/investment-trust/:isin", api.FetchInvestTrust) // 仮想通貨の価格を取得 e.GET("/api/crypto/gmo", cGmo.FetchCryptoGMO) e.GET("/api/crypto/coincheck", cCc.FetchCryptoCoincheck) // LocalとGAEの切り替え appengine.Main() // GAEの場合に有効化 //e.Start(":1323") // Localの場合に有効化 }
■ client.go
package api var ( defaultURLCryptoGMO = "https://api.coin.z.com/public/v1/ticker?symbol=" defaultURLCryptoCoincheck = "https://coincheck.com/api/ticker?symbol=" ) type ClientCryptoCoincheck struct { URL *string } type ClientCryptoGMO struct { URL *string } func (c *ClientCryptoGMO) NewClientCryptoGMO() { c.URL = &defaultURLCryptoGMO } func (c *ClientCryptoCoincheck) NewClientCryptoCoincheck() { c.URL = &defaultURLCryptoCoincheck }
■ crypto_gmo.go
package api import ( "encoding/json" "fmt" "log" "net/http" "github.com/labstack/echo/v4" ) type CryptoInfoGMO struct { DataGMO []DataGMO `json:"data"` } type DataGMO struct { Symbol string `json:"symbol"` Last string `json:"last"` // 最後の取引価格 Timestamp string `json:"timestamp"` } type ResponseCryptoGMO struct { ResponseDataGMO []ResponseDataGMO `json:"data"` } type ResponseDataGMO struct { Symbol string `json:"symbol"` Last string `json:"last"` Timestamp string `json:"timestamp"` } func (cl *ClientCryptoGMO) FetchCryptoGMO(c echo.Context) error { var url string cig := &CryptoInfoGMO{} symbol := c.QueryParam("symbol") // BTC, ETH.. // URLを場合別け(テストと外部API) if *cl.URL == defaultURLCryptoGMO { url = *cl.URL + symbol } else { url = *cl.URL } req, err := http.NewRequest("GET", url, nil) if err != nil { log.Println(err) } client := new(http.Client) resp, err := client.Do(req) if err != nil { log.Println(err) } defer resp.Body.Close() err = json.NewDecoder(resp.Body).Decode(cig) if err != nil { log.Println(err) } responseJSON := []ResponseCryptoGMO{ { ResponseDataGMO: []ResponseDataGMO{ { Symbol: cig.DataGMO[0].Symbol, Last: cig.DataGMO[0].Last, Timestamp: cig.DataGMO[0].Timestamp, }, }, }, } response, err := json.Marshal(responseJSON[0]) fmt.Printf("response=%s\n", string(response)) if err != nil { log.Println(err) } return c.String(http.StatusOK, string(response)) }
■ crypto_coincheck.go
package api import ( "encoding/json" "fmt" "log" "net/http" "github.com/labstack/echo/v4" ) type CryptoInfoCoincheck struct { Last interface{} `json:"last"` Timestamp int64 `json:"timestamp"` } type ResponseCryptoCoincheck struct { Symbol string `json:"symbol"` Last string `json:"last"` Timestamp int64 `json:"timestamp"` } func (cl *ClientCryptoCoincheck) FetchCryptoCoincheck(c echo.Context) error { var url, last string coincheck := &CryptoInfoCoincheck{} symbol := c.QueryParam("symbol") // BTC, ETH.. // URLを場合別け(テストと外部API) if *cl.URL == defaultURLCryptoCoincheck { url = *cl.URL + symbol } else { url = *cl.URL + "/" + symbol } fmt.Printf("url=%s\n", url) req, err := http.NewRequest("GET", url, nil) if err != nil { log.Println(err) } client := new(http.Client) resp, err := client.Do(req) if err != nil { log.Println(err) } defer resp.Body.Close() err = json.NewDecoder(resp.Body).Decode(coincheck) if err != nil { log.Println(err) } // check type for interface{} value of last switch coincheck.Last.(type) { case float64: last = fmt.Sprintf("%.10g", coincheck.Last) // no use 'exponential(e+)' case string: last = coincheck.Last.(string) } responseJSON := ResponseCryptoCoincheck{ Symbol: symbol, Last: last, Timestamp: coincheck.Timestamp, } response, err := json.Marshal(responseJSON) //fmt.Println(string(response)) if err != nil { log.Println(err) } return c.String(http.StatusOK, string(response)) }
■ 確認
- GMOコインの場合 curl -sSu ${ID}:${PW} ${ENDPOINT}/api/crypto/gmo\?symbol=BTC | jq { "data": [ { "symbol": "BTC_JPY", "last": "4592081", "timestamp": "2022-03-13T05:56:32.907Z" } ] } - Coincheckの場合 curl -sSu ${ID}:${PW} ${ENDPOINT}/api/crypto/coincheck\?symbol=btc_jpy | jq { "symbol": "btc_jpy", "last": "4592591", "timestamp": 1647151043 }
デプロイについて
今回のGoアプリはGoogle App Engineにデプロイしているので、Github Actionsで自動デプロイするようにしています。 そちらについては以前に記事でまとめています。