From 51520cfca9ffc774211ab175423e68d1374f5584 Mon Sep 17 00:00:00 2001 From: CHIKAMATSU Naohiro Date: Sun, 4 Aug 2024 00:13:54 +0900 Subject: [PATCH] Add GetExchangeStatus() --- README.md | 1 + exchange.go | 84 +++++++++++++++++++++++ exchange_test.go | 175 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 exchange.go create mode 100644 exchange_test.go diff --git a/README.md b/README.md index c56ebde..c5348bf 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ If you want to execute the Private API, you need to create a client with the API | GET /api/order_books | [GetOrderBooks()](https://pkg.go.dev/github.com/nao1215/coincheck#Client.GetOrderBooks) | Fetch order book information. | | GET /api/exchange/orders/rate | [GetExchangeOrdersRate()](https://pkg.go.dev/github.com/nao1215/coincheck#Client.GetExchangeOrdersRate) | To calculate the rate from the order of the exchange. | | GET /api/rate/[pair] | [GetRate()](https://pkg.go.dev/github.com/nao1215/coincheck#Client.GetRate) | Get the Standard Rate of Coin. | +| GET /api/exchange_status | [GetExchangeStatus()](https://pkg.go.dev/github.com/nao1215/coincheck#Client.GetExchangeStatus) | Retrieving the status of the exchange. | ### Private API diff --git a/exchange.go b/exchange.go new file mode 100644 index 0000000..deec689 --- /dev/null +++ b/exchange.go @@ -0,0 +1,84 @@ +package coincheck + +import ( + "context" + "net/http" +) + +// GetExchangeStatusInput represents the input parameter for the GetExchangeStatus method. +type GetExchangeStatusInput struct { + // Pair represents the pair of the currency. + // If pair is not specified, information on all tradable pairs is returned. + // If pair is specified, information is returned only for that particular currency. + Pair *Pair +} + +// GetExchangeStatusResponse represents the output from the GetExchangeStatus method. +type GetExchangeStatusResponse struct { + ExchangeStatus []ExchangeStatus `json:"exchange_status"` +} + +// ExchangeStatus represents the exchange status. +type ExchangeStatus struct { + // Pair is the pair of the currency. + Pair Pair `json:"pair"` + // Status is the exchange status (available, itayose, stop). + Status ExchangeStatusAvailability `json:"status"` + // Timestamp is the time of the status. + Timestamp float64 `json:"timestamp"` + // Availability response whether limit orders ( order ), market orders ( market_order ), or cancel orders ( cancel ) can be placed. + Availability Availability `json:"availability"` +} + +// ExchangeStatusAvailability represents the availability of the exchange. +type ExchangeStatusAvailability string + +const ( + // ExchangeStatusAvailabilityAvailable means the availability of available. + ExchangeStatusAvailabilityAvailable ExchangeStatusAvailability = "available" + // ExchangeStatusAvailabilityItayose is the availability of itayose. + // Itayose (Itayose method) is a method of determining the transaction price + // before orders (bids) are executed by recording them on an order book called "ita." + // Following the price priority principle, market orders are given priority first. + // Then, orders are matched starting from the lowest sell orders and highest buy orders. + // When the quantities of buy and sell orders match, a trade is executed at that price. + ExchangeStatusAvailabilityItayose ExchangeStatusAvailability = "itayose" + // ExchangeStatusAvailabilityStop is the availability of stop. + ExchangeStatusAvailabilityStop ExchangeStatusAvailability = "stop" +) + +// Availability represents the availability of the exchange. +type Availability struct { + // Order is the availability of limit orders. + Order bool `json:"order"` + // MarketOrder is the availability of market orders. + MarketOrder bool `json:"market_order"` + // Cancel is the availability of cancel orders. + Cancel bool `json:"cancel"` +} + +// GetExchangeStatus get retrieving the status of the exchange. +// API: GET /api/exchange_status +// Visibility: Public +// If GetExchangeStatusInput.Pair is not specified, information on all tradable pairs is returned. +func (c *Client) GetExchangeStatus(ctx context.Context, input GetExchangeStatusInput) (*GetExchangeStatusResponse, error) { + queryParam := map[string]string{} + if input.Pair != nil { + queryParam["pair"] = input.Pair.String() + } + + req, err := c.createRequest(ctx, createRequestInput{ + method: http.MethodGet, + path: "/api/exchange_status", + queryParam: queryParam, + }) + if err != nil { + return nil, err + } + + var output GetExchangeStatusResponse + if err := c.do(req, &output); err != nil { + return nil, err + } + return &output, nil +} diff --git a/exchange_test.go b/exchange_test.go new file mode 100644 index 0000000..f6140d4 --- /dev/null +++ b/exchange_test.go @@ -0,0 +1,175 @@ +package coincheck + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/shogo82148/pointer" +) + +func TestClient_GetExchangeStatus(t *testing.T) { + t.Run("GetExchangeStatus returns the all exchange status", func(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + wantMethod := http.MethodGet + if got := r.Method; got != wantMethod { + t.Errorf("Method: got %v, want %v", got, wantMethod) + } + + wantEndpoint := "/api/exchange_status" + if got := r.URL.Path; got != wantEndpoint { + t.Errorf("Endpoint: got %v, want %v", got, wantEndpoint) + } + + result := GetExchangeStatusResponse{ + ExchangeStatus: []ExchangeStatus{ + { + Pair: PairBTCJPY, + Status: ExchangeStatusAvailabilityAvailable, + Timestamp: 1609459200, + Availability: Availability{ + Order: true, + MarketOrder: true, + Cancel: true, + }, + }, + { + Pair: PairBrilJPY, + Status: ExchangeStatusAvailabilityItayose, + Timestamp: 1609459200, + Availability: Availability{ + Order: false, + MarketOrder: false, + Cancel: false, + }, + }, + }, + } + if err := json.NewEncoder(w).Encode(result); err != nil { + t.Fatal(err) + } + })) + + client, err := NewClient(WithBaseURL(testServer.URL)) + if err != nil { + t.Fatal(err) + } + + got, err := client.GetExchangeStatus(context.Background(), GetExchangeStatusInput{}) + if err != nil { + t.Fatal(err) + } + want := &GetExchangeStatusResponse{ + ExchangeStatus: []ExchangeStatus{ + { + Pair: PairBTCJPY, + Status: ExchangeStatusAvailabilityAvailable, + Timestamp: 1609459200, + Availability: Availability{ + Order: true, + MarketOrder: true, + Cancel: true, + }, + }, + { + Pair: PairBrilJPY, + Status: ExchangeStatusAvailabilityItayose, + Timestamp: 1609459200, + Availability: Availability{ + Order: false, + MarketOrder: false, + Cancel: false, + }, + }, + }, + } + if diff := cmp.Diff(want, got); diff != "" { + printDiff(t, diff) + } + }) + + t.Run("If pair is PairETCJPY, GetExchangeStatus returns the exchange status of PairETCJPY", func(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + wantMethod := http.MethodGet + if got := r.Method; got != wantMethod { + t.Errorf("Method: got %v, want %v", got, wantMethod) + } + + wantEndpoint := "/api/exchange_status" + if got := r.URL.Path; got != wantEndpoint { + t.Errorf("Endpoint: got %v, want %v", got, wantEndpoint) + } + + wantPair := PairETCJPY + if got := r.URL.Query().Get("pair"); got != wantPair.String() { + t.Errorf("Pair: got %v, want %v", got, wantPair) + } + + result := GetExchangeStatusResponse{ + ExchangeStatus: []ExchangeStatus{ + { + Pair: PairETCJPY, + Status: ExchangeStatusAvailabilityAvailable, + Timestamp: 1609459200, + Availability: Availability{ + Order: true, + MarketOrder: true, + Cancel: true, + }, + }, + }, + } + if err := json.NewEncoder(w).Encode(result); err != nil { + t.Fatal(err) + } + })) + + client, err := NewClient(WithBaseURL(testServer.URL)) + if err != nil { + t.Fatal(err) + } + + got, err := client.GetExchangeStatus(context.Background(), GetExchangeStatusInput{ + Pair: pointer.Ptr(PairETCJPY), + }) + if err != nil { + t.Fatal(err) + } + want := &GetExchangeStatusResponse{ + ExchangeStatus: []ExchangeStatus{ + { + Pair: PairETCJPY, + Status: ExchangeStatusAvailabilityAvailable, + Timestamp: 1609459200, + Availability: Availability{ + Order: true, + MarketOrder: true, + Cancel: true, + }, + }, + }, + } + if diff := cmp.Diff(want, got); diff != "" { + printDiff(t, diff) + } + }) + + t.Run("GetExchangeStatus returns an error", func(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer testServer.Close() + + client, err := NewClient(WithBaseURL(testServer.URL)) + if err != nil { + t.Fatal(err) + } + + if _, err = client.GetExchangeStatus(context.Background(), GetExchangeStatusInput{}); err == nil { + t.Error("want error, but got nil") + } + }) +}