Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GetExchangeOrdersRate() #5

Merged
merged 3 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ If you want to execute the Private API, you need to create a client with the API
| GET /api/ticker | [GetTicker()](https://pkg.go.dev/github.com/nao1215/coincheck#Client.GetTicker) | Check latest ticker information. |
| GET /api/trades | [GetTrades()](https://pkg.go.dev/github.com/nao1215/coincheck#Client.GetTrades) | You can get current order transactions. |
| 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. |

### Private API
Expand All @@ -104,6 +105,9 @@ If you want to execute the Private API, you need to create a client with the API
## Contribution
First off, thanks for taking the time to contribute! See [CONTRIBUTING.md](./CONTRIBUTING.md) for more information. Contributions are not only related to development. For example, GitHub Star motivates me to develop! Please feel free to contribute to this project.

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=nao1215/coincheck&type=Date)](https://star-history.com/#nao1215/coincheck&Date)

## Contributors ✨

Expand Down Expand Up @@ -135,3 +139,13 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

## Reasons for Creating the coincheck package

I wanted to create a Bot that is reason why. Another reason is that I started creating it without much thought. Foolish. Now, I would rather have a bitFlyer Bot.

I received a bonus and bought cryptocurrency out of curiosity (I bought it secretly from my spouse and have already lost 10,000 yen). I became interested in the mechanism of cryptocurrency itself, as well as in how to trade cryptocurrency automatically using a Bot.

The cryptocurrency exchanges I use are Coincheck and bitFlyer, with the former being the one where I hold the most coins. Given this situation, it was natural for me to consider creating a Bot for Coincheck.

However, the official Coincheck API client (Golang) was quite simplistic. I had no choice but to create my own. At this point, I realized my mistake. There was a lot of information that could not be gleaned from the official documentation.
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module github.com/nao1215/coincheck

go 1.20

require github.com/google/go-cmp v0.6.0
require (
github.com/google/go-cmp v0.6.0
github.com/shogo82148/pointer v1.3.0
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/shogo82148/pointer v1.3.0 h1:LW5V2jUAjFNjS8e7k/PgFoh3EavOSB/vvN85aGue5+I=
github.com/shogo82148/pointer v1.3.0/go.mod h1:agZ5JFpavFPXznbWonIvbG78NDfvDTFppe+7o53up5w=
5 changes: 5 additions & 0 deletions order.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import (
// OrderType represents the order type.
type OrderType string

// String returns the string representation of the OrderType.
func (o OrderType) String() string {
return string(o)
}

const (
// OrderTypeBuy is the order type of buy.
OrderTypeBuy OrderType = "buy"
Expand Down
62 changes: 62 additions & 0 deletions rate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package coincheck

import (
"context"
"errors"
"fmt"
"net/http"
)

Expand Down Expand Up @@ -41,3 +43,63 @@ func (c *Client) GetRate(ctx context.Context, input GetRateInput) (*GetRateRespo
}
return &output, nil
}

// GetExchangeOrdersRateInput represents the input for the GetExchangeOrdersRate function.
// Either price or amount must be specified as a parameter.
// When both Price and Amount were specified, a status code of 400 was returned in the response.
type GetExchangeOrdersRateInput struct {
// OrderType is the order type. Order type("sell" or "buy")
OrderType OrderType
// Pair is the pair of the currency. e.g. btc_jpy.
// Specify a currency pair to trade. btc_jpy, etc_jpy, lsk_jpy, mona_jpy, plt_jpy, fnct_jpy, dai_jpy, wbtc_jpy, bril_jpy are now available.
Pair Pair
// Price is the price of the order. e.g. 30000.
Price *float64
// Amount is the amount of the order. e.g. 0.1.
Amount *float64
}

// GetExchangeOrdersRateResponse represents the output from GetExchangeOrdersRate.
type GetExchangeOrdersRateResponse struct {
// Success is a boolean value that indicates the success of the API call.
Success bool `json:"success"`
// Rate is the rate of the order.
Rate string `json:"rate"`
// Price is the price of the order.
Price string `json:"price"`
// Amount is the amount of the order.
Amount string `json:"amount"`
}

// GetExchangeOrdersRate calculate the rate from the order of the exchange.
// API: GET /api/exchange/orders/rate
// Visibility: Public
func (c *Client) GetExchangeOrdersRate(ctx context.Context, input GetExchangeOrdersRateInput) (*GetExchangeOrdersRateResponse, error) {
queryParam := map[string]string{
"order_type": input.OrderType.String(),
"pair": input.Pair.String(),
}
if input.Price == nil && input.Amount == nil {
return nil, withPrefixError(errors.New("either price or amount must be specified as a parameter"))
}
if input.Price != nil {
queryParam["price"] = fmt.Sprintf("%f", *input.Price)
} else {
queryParam["amount"] = fmt.Sprintf("%f", *input.Amount)
}

req, err := c.createRequest(ctx, createRequestInput{
method: http.MethodGet,
path: "/api/exchange/orders/rate",
queryParam: queryParam,
})
if err != nil {
return nil, err
}

var output GetExchangeOrdersRateResponse
if err := c.do(req, &output); err != nil {
return nil, err
}
return &output, nil
}
159 changes: 159 additions & 0 deletions rate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/shogo82148/pointer"
)

func TestClient_GetStandardRate(t *testing.T) {
Expand Down Expand Up @@ -100,3 +101,161 @@ func TestClient_GetStandardRate(t *testing.T) {
}
})
}

func TestClient_GetExchangeOrdersRate(t *testing.T) {
t.Run("GetExchangeOrdersRate returns the exchange orders rate (if input valuer set amount)", 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/orders/rate"
if got := r.URL.Path; got != wantEndpoint {
t.Errorf("Endpoint: got %v, want %v", got, wantEndpoint)
}

wantPair := PairBTCJPY
if got := r.URL.Query().Get("pair"); got != wantPair.String() {
t.Errorf("pair: got %v, want %v", got, wantPair)
}
wantOrderType := OrderTypeBuy
if got := r.URL.Query().Get("order_type"); got != wantOrderType.String() {
t.Errorf("order_type: got %v, want %v", got, wantOrderType)
}
wantAmount := "1.200000"
if got := r.URL.Query().Get("amount"); got != wantAmount {
t.Errorf("amount: got %v, want %v", got, wantAmount)
}

result := GetExchangeOrdersRateResponse{
Success: true,
Rate: "9118315.44305",
Price: "10941978.53166054",
Amount: "1.2",
}
if err := json.NewEncoder(w).Encode(result); err != nil {
t.Fatal(err)
}
}))

// Create a new client
client, err := NewClient(WithBaseURL(testServer.URL))
if err != nil {
t.Fatal(err)
}

// Start testing
input := GetExchangeOrdersRateInput{
OrderType: OrderTypeBuy,
Pair: PairBTCJPY,
Amount: pointer.Float64(1.2),
}
got, err := client.GetExchangeOrdersRate(context.Background(), input)
if err != nil {
t.Fatal(err)
}
want := &GetExchangeOrdersRateResponse{
Success: true,
Rate: "9118315.44305",
Price: "10941978.53166054",
Amount: "1.2",
}
if diff := cmp.Diff(want, got); diff != "" {
printDiff(t, diff)
}
})

t.Run("GetExchangeOrdersRate returns the exchange orders rate (if input valuer set price)", 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/orders/rate"
if got := r.URL.Path; got != wantEndpoint {
t.Errorf("Endpoint: got %v, want %v", got, wantEndpoint)
}

wantPair := PairBTCJPY
if got := r.URL.Query().Get("pair"); got != wantPair.String() {
t.Errorf("pair: got %v, want %v", got, wantPair)
}
wantOrderType := OrderTypeBuy
if got := r.URL.Query().Get("order_type"); got != wantOrderType.String() {
t.Errorf("order_type: got %v, want %v", got, wantOrderType)
}
wantPrice := "1200000.000000"
if got := r.URL.Query().Get("price"); got != wantPrice {
t.Errorf("price: got %v, want %v", got, wantPrice)
}

result := GetExchangeOrdersRateResponse{
Success: true,
Rate: "9118315.44305",
Price: "1200000",
Amount: "1.1",
}
if err := json.NewEncoder(w).Encode(result); err != nil {
t.Fatal(err)
}
}))

// Create a new client
client, err := NewClient(WithBaseURL(testServer.URL))
if err != nil {
t.Fatal(err)
}

// Start testing
input := GetExchangeOrdersRateInput{
OrderType: OrderTypeBuy,
Pair: PairBTCJPY,
Price: pointer.Float64(1200000),
}
got, err := client.GetExchangeOrdersRate(context.Background(), input)
if err != nil {
t.Fatal(err)
}
want := &GetExchangeOrdersRateResponse{
Success: true,
Rate: "9118315.44305",
Price: "1200000",
Amount: "1.1",
}
if diff := cmp.Diff(want, got); diff != "" {
printDiff(t, diff)
}
})

t.Run("If the amount and pair are empty, GetExchangeOrdersRate returns an error", func(t *testing.T) {
client, err := NewClient()
if err != nil {
t.Fatal(err)
}

if _, err = client.GetExchangeOrdersRate(context.Background(), GetExchangeOrdersRateInput{}); err == nil {
t.Fatal("expected an error, but got nil")
}
})

t.Run("GetExchangeOrdersRate returns an error", func(t *testing.T) {
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}))

client, err := NewClient(WithBaseURL(testServer.URL))
if err != nil {
t.Fatal(err)
}

if _, err = client.GetExchangeOrdersRate(context.Background(), GetExchangeOrdersRateInput{
OrderType: OrderTypeBuy,
Pair: PairBTCJPY,
Amount: pointer.Float64(1.2),
}); err == nil {
t.Fatal("expected an error, but got nil")
}
})
}