Skip to content

Commit

Permalink
Coinbase wrapper & codebase cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
cranktakular committed Dec 11, 2023
1 parent a8cae40 commit 0564e45
Show file tree
Hide file tree
Showing 21 changed files with 581 additions and 146 deletions.
5 changes: 4 additions & 1 deletion common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ var (
ErrNotYetImplemented = errors.New("not yet implemented")
// ErrFunctionNotSupported defines a standardised error for an unsupported
// wrapper function by an API
ErrFunctionNotSupported = errors.New("unsupported wrapper function")
ErrFunctionNotSupported = errors.New("unsupported wrapper function")
// ErrExchangeNameUnset defines a common error across the code base for
// when the exchange name is unset
ErrExchangeNameUnset = errors.New("exchange name unset")
errInvalidCryptoCurrency = errors.New("invalid crypto currency")
// ErrDateUnset is an error for start end check calculations
ErrDateUnset = errors.New("date unset")
Expand Down
4 changes: 2 additions & 2 deletions engine/datahistory_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -1195,11 +1195,11 @@ func (m *DataHistoryManager) validateJob(job *DataHistoryJob) error {
exchangeName := job.Exchange
if job.DataType == dataHistoryCandleValidationSecondarySourceType {
if job.SecondaryExchangeSource == "" {
return fmt.Errorf("job %s %w, secondary exchange name required to lookup existing results", job.Nickname, errExchangeNameUnset)
return fmt.Errorf("job %s %w, secondary exchange name required to lookup existing results", job.Nickname, common.ErrExchangeNameUnset)
}
exchangeName = job.SecondaryExchangeSource
if job.Exchange == "" {
return fmt.Errorf("job %s %w, exchange name required to lookup existing results", job.Nickname, errExchangeNameUnset)
return fmt.Errorf("job %s %w, exchange name required to lookup existing results", job.Nickname, common.ErrExchangeNameUnset)
}
}
exch, err := m.exchangeManager.GetExchangeByName(exchangeName)
Expand Down
8 changes: 4 additions & 4 deletions engine/datahistory_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,14 +529,14 @@ func TestValidateJob(t *testing.T) {

dhj.DataType = dataHistoryCandleValidationSecondarySourceType
err = m.validateJob(dhj)
if !errors.Is(err, errExchangeNameUnset) {
t.Errorf("error '%v', expected '%v'", err, errExchangeNameUnset)
if !errors.Is(err, common.ErrExchangeNameUnset) {
t.Errorf("error '%v', expected '%v'", err, common.ErrExchangeNameUnset)
}
dhj.SecondaryExchangeSource = "lol"
dhj.Exchange = ""
err = m.validateJob(dhj)
if !errors.Is(err, errExchangeNameUnset) {
t.Errorf("error '%v', expected '%v'", err, errExchangeNameUnset)
if !errors.Is(err, common.ErrExchangeNameUnset) {
t.Errorf("error '%v', expected '%v'", err, common.ErrExchangeNameUnset)
}
}

Expand Down
7 changes: 3 additions & 4 deletions engine/rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ var (
errExchangeNotEnabled = errors.New("exchange is not enabled")
errExchangeBaseNotFound = errors.New("cannot get exchange base")
errInvalidArguments = errors.New("invalid arguments received")
errExchangeNameUnset = errors.New("exchange name unset")
errCurrencyPairUnset = errors.New("currency pair unset")
errInvalidTimes = errors.New("invalid start and end times")
errAssetTypeUnset = errors.New("asset type unset")
Expand Down Expand Up @@ -2181,7 +2180,7 @@ func (s *RPCServer) GetOrderbookStream(r *gctrpc.GetOrderbookStreamRequest, stre
// GetExchangeOrderbookStream streams all orderbooks associated with an exchange
func (s *RPCServer) GetExchangeOrderbookStream(r *gctrpc.GetExchangeOrderbookStreamRequest, stream gctrpc.GoCryptoTraderService_GetExchangeOrderbookStreamServer) error {
if r.Exchange == "" {
return errExchangeNameUnset
return common.ErrExchangeNameUnset
}

if _, err := s.GetExchangeByName(r.Exchange); err != nil {
Expand Down Expand Up @@ -2249,7 +2248,7 @@ func (s *RPCServer) GetExchangeOrderbookStream(r *gctrpc.GetExchangeOrderbookStr
// GetTickerStream streams the requested updated ticker
func (s *RPCServer) GetTickerStream(r *gctrpc.GetTickerStreamRequest, stream gctrpc.GoCryptoTraderService_GetTickerStreamServer) error {
if r.Exchange == "" {
return errExchangeNameUnset
return common.ErrExchangeNameUnset
}

if _, err := s.GetExchangeByName(r.Exchange); err != nil {
Expand Down Expand Up @@ -2320,7 +2319,7 @@ func (s *RPCServer) GetTickerStream(r *gctrpc.GetTickerStreamRequest, stream gct
// GetExchangeTickerStream streams all tickers associated with an exchange
func (s *RPCServer) GetExchangeTickerStream(r *gctrpc.GetExchangeTickerStreamRequest, stream gctrpc.GoCryptoTraderService_GetExchangeTickerStreamServer) error {
if r.Exchange == "" {
return errExchangeNameUnset
return common.ErrExchangeNameUnset
}

if _, err := s.GetExchangeByName(r.Exchange); err != nil {
Expand Down
7 changes: 3 additions & 4 deletions exchanges/account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ func init() {

var (
errHoldingsIsNil = errors.New("holdings cannot be nil")
errExchangeNameUnset = errors.New("exchange name unset")
errExchangeHoldingsNotFound = errors.New("exchange holdings not found")
errAssetHoldingsNotFound = errors.New("asset holdings not found")
errExchangeAccountsNotFound = errors.New("exchange accounts not found")
Expand Down Expand Up @@ -76,7 +75,7 @@ func Process(h *Holdings, c *Credentials) error {
// TODO: Add jurisdiction and differentiation between APIKEY holdings.
func GetHoldings(exch string, creds *Credentials, assetType asset.Item) (Holdings, error) {
if exch == "" {
return Holdings{}, errExchangeNameUnset
return Holdings{}, common.ErrExchangeNameUnset
}

if creds.IsEmpty() {
Expand Down Expand Up @@ -152,7 +151,7 @@ func GetHoldings(exch string, creds *Credentials, assetType asset.Item) (Holding
// GetBalance returns the internal balance for that asset item.
func GetBalance(exch, subAccount string, creds *Credentials, ai asset.Item, c currency.Code) (*ProtectedBalance, error) {
if exch == "" {
return nil, fmt.Errorf("cannot get balance: %w", errExchangeNameUnset)
return nil, fmt.Errorf("cannot get balance: %w", common.ErrExchangeNameUnset)
}

if !ai.IsValid() {
Expand Down Expand Up @@ -201,7 +200,7 @@ func (s *Service) Update(incoming *Holdings, creds *Credentials) error {
}

if incoming.Exchange == "" {
return fmt.Errorf("cannot update holdings: %w", errExchangeNameUnset)
return fmt.Errorf("cannot update holdings: %w", common.ErrExchangeNameUnset)
}

if creds.IsEmpty() {
Expand Down
17 changes: 9 additions & 8 deletions exchanges/account/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"
"time"

"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/key"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/dispatch"
Expand Down Expand Up @@ -72,8 +73,8 @@ func TestGetHoldings(t *testing.T) {
}

err = Process(&Holdings{}, nil)
if !errors.Is(err, errExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchangeNameUnset)
if !errors.Is(err, common.ErrExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrExchangeNameUnset)
}

holdings := Holdings{
Expand Down Expand Up @@ -145,8 +146,8 @@ func TestGetHoldings(t *testing.T) {
}

_, err = GetHoldings("", nil, asset.Spot)
if !errors.Is(err, errExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchangeNameUnset)
if !errors.Is(err, common.ErrExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrExchangeNameUnset)
}

_, err = GetHoldings("bla", nil, asset.Spot)
Expand Down Expand Up @@ -245,8 +246,8 @@ func TestGetHoldings(t *testing.T) {

func TestGetBalance(t *testing.T) {
_, err := GetBalance("", "", nil, asset.Empty, currency.Code{})
if !errors.Is(err, errExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchangeNameUnset)
if !errors.Is(err, common.ErrExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrExchangeNameUnset)
}

_, err = GetBalance("bruh", "", nil, asset.Empty, currency.Code{})
Expand Down Expand Up @@ -408,8 +409,8 @@ func TestUpdate(t *testing.T) {
}

err = s.Update(&Holdings{}, nil)
if !errors.Is(err, errExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchangeNameUnset)
if !errors.Is(err, common.ErrExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrExchangeNameUnset)
}

err = s.Update(&Holdings{
Expand Down
55 changes: 31 additions & 24 deletions exchanges/coinbasepro/coinbasepro.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@ var (
errAccountIDEmpty = errors.New("account id cannot be empty")
errClientOrderIDEmpty = errors.New("client order id cannot be empty")
errProductIDEmpty = errors.New("product id cannot be empty")
errAmountZeroNonMarketBuy = errors.New("base amount cannot be 0 unless a market buy order")
errAmountZeroMarketBuy = errors.New("quote amount cannot be 0 for market buy orders")
errAmountZero = errors.New("amount cannot be 0")
errOrderIDEmpty = errors.New("order ids cannot be empty")
errOpenPairWithOtherTypes = errors.New("cannot pair open orders with other order types")
errUserIDEmpty = errors.New("user id cannot be empty")
Expand All @@ -109,6 +108,8 @@ var (
errInvalidPriceType = errors.New("price type must be spot, buy, or sell")
errInvalidOrderType = errors.New("order type must be market, limit, or stop")
errNoMatchingWallets = errors.New("no matching wallets returned")
errOrderModFailNoErr = errors.New("order modification failed but no error returned")
errNoMatchingOrders = errors.New("no matching orders returned")
)

// GetAllAccounts returns information on all trading accounts associated with the API key
Expand Down Expand Up @@ -275,57 +276,54 @@ func (c *CoinbasePro) GetTicker(ctx context.Context, productID string, limit uin
}

// PlaceOrder places either a limit, market, or stop order
func (c *CoinbasePro) PlaceOrder(ctx context.Context, clientOID, productID, side, stopDirection, orderType string, baseAmount, quoteAmount, limitPrice, stopPrice float64, postOnly bool, endTime time.Time) (*PlaceOrderResp, error) {
func (c *CoinbasePro) PlaceOrder(ctx context.Context, clientOID, productID, side, stopDirection, orderType string, amount, limitPrice, stopPrice float64, postOnly bool, endTime time.Time) (*PlaceOrderResp, error) {
if clientOID == "" {
return nil, errClientOrderIDEmpty
}
if productID == "" {
return nil, errProductIDEmpty
}
if baseAmount == 0 && orderType != order.Market.Lower() && side != order.Buy.String() {
return nil, errAmountZeroNonMarketBuy
}
if quoteAmount == 0 && orderType == order.Market.Lower() && side == order.Buy.String() {
return nil, errAmountZeroMarketBuy
if amount == 0 {
return nil, errAmountZero
}

var resp PlaceOrderResp

var orderConfig OrderConfiguration

switch orderType {
case order.Market.Lower():
case order.Market.String(), order.ImmediateOrCancel.String():
orderConfig.MarketMarketIOC = &MarketMarketIOC{}
if side == order.Buy.Lower() {
orderConfig.MarketMarketIOC.QuoteSize = strconv.FormatFloat(quoteAmount, 'f', -1, 64)
if side == order.Buy.String() {
orderConfig.MarketMarketIOC.QuoteSize = strconv.FormatFloat(amount, 'f', -1, 64)
}
if side == order.Sell.Lower() {
orderConfig.MarketMarketIOC.BaseSize = strconv.FormatFloat(baseAmount, 'f', -1, 64)
if side == order.Sell.String() {
orderConfig.MarketMarketIOC.BaseSize = strconv.FormatFloat(amount, 'f', -1, 64)
}
case order.Limit.Lower():
case order.Limit.String():
if endTime == (time.Time{}) {
orderConfig.LimitLimitGTC = &LimitLimitGTC{}
orderConfig.LimitLimitGTC.BaseSize = strconv.FormatFloat(baseAmount, 'f', -1, 64)
orderConfig.LimitLimitGTC.BaseSize = strconv.FormatFloat(amount, 'f', -1, 64)
orderConfig.LimitLimitGTC.LimitPrice = strconv.FormatFloat(limitPrice, 'f', -1, 64)
orderConfig.LimitLimitGTC.PostOnly = postOnly
} else {
orderConfig.LimitLimitGTD = &LimitLimitGTD{}
orderConfig.LimitLimitGTD.BaseSize = strconv.FormatFloat(baseAmount, 'f', -1, 64)
orderConfig.LimitLimitGTD.BaseSize = strconv.FormatFloat(amount, 'f', -1, 64)
orderConfig.LimitLimitGTD.LimitPrice = strconv.FormatFloat(limitPrice, 'f', -1, 64)
orderConfig.LimitLimitGTD.PostOnly = postOnly
orderConfig.LimitLimitGTD.EndTime = endTime
}
case order.Stop.Lower():
case order.StopLimit.String():
if endTime == (time.Time{}) {
orderConfig.StopLimitStopLimitGTC = &StopLimitStopLimitGTC{}
orderConfig.StopLimitStopLimitGTC.BaseSize = strconv.FormatFloat(baseAmount, 'f', -1, 64)
orderConfig.StopLimitStopLimitGTC.BaseSize = strconv.FormatFloat(amount, 'f', -1, 64)
orderConfig.StopLimitStopLimitGTC.LimitPrice = strconv.FormatFloat(limitPrice, 'f', -1,
64)
orderConfig.StopLimitStopLimitGTC.StopPrice = strconv.FormatFloat(stopPrice, 'f', -1, 64)
orderConfig.StopLimitStopLimitGTC.StopDirection = stopDirection
} else {
orderConfig.StopLimitStopLimitGTD = &StopLimitStopLimitGTD{}
orderConfig.StopLimitStopLimitGTD.BaseSize = strconv.FormatFloat(baseAmount, 'f', -1, 64)
orderConfig.StopLimitStopLimitGTD.BaseSize = strconv.FormatFloat(amount, 'f', -1, 64)
orderConfig.StopLimitStopLimitGTD.LimitPrice = strconv.FormatFloat(limitPrice, 'f', -1,
64)
orderConfig.StopLimitStopLimitGTD.StopPrice = strconv.FormatFloat(stopPrice, 'f', -1, 64)
Expand Down Expand Up @@ -356,7 +354,7 @@ func (c *CoinbasePro) CancelOrders(ctx context.Context, orderIDs []string) (Canc
req := map[string]interface{}{"order_ids": orderIDs}

return resp, c.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, path, "",
nil, Version3, req, nil)
req, Version3, &resp, nil)
}

// EditOrder edits an order to a new size or price. Only limit orders with a good-till-cancelled time
Expand Down Expand Up @@ -582,6 +580,14 @@ func (c *CoinbasePro) GetConvertTradeByID(ctx context.Context, tradeID, from, to
"", req, Version3, &resp, nil)
}

// GetV3Time returns the current server time, calling V3 of the API
func (c *CoinbasePro) GetV3Time(ctx context.Context) (ServerTimeV3, error) {
var resp ServerTimeV3

return resp, c.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodGet,
coinbaseV3+coinbaseTime, "", nil, Version3, &resp, nil)
}

// ListNotifications lists the notifications the user is subscribed to
func (c *CoinbasePro) ListNotifications(ctx context.Context, pag PaginationInp) (ListNotificationsResponse, error) {
var resp ListNotificationsResponse
Expand Down Expand Up @@ -1076,9 +1082,9 @@ func (c *CoinbasePro) GetPrice(ctx context.Context, currencyPair, priceType stri
return resp, c.SendHTTPRequest(ctx, exchange.RestSpot, path, &resp)
}

// GetCurrentServerTime returns the API server time
func (c *CoinbasePro) GetCurrentServerTime(ctx context.Context) (ServerTime, error) {
resp := ServerTime{}
// GetV2Time returns the current server time, calling V2 of the API
func (c *CoinbasePro) GetV2Time(ctx context.Context) (ServerTimeV2, error) {
resp := ServerTimeV2{}
return resp, c.SendHTTPRequest(ctx, exchange.RestSpot, coinbaseV2+coinbaseTime, &resp)
}

Expand Down Expand Up @@ -1944,7 +1950,8 @@ func (c *CoinbasePro) SendAuthenticatedHTTPRequest(ctx context.Context, ep excha
PreviewFailureReason string `json:"preview_failure_reason"`
}
}{}
if err := json.Unmarshal(interim, &manyErrCap); err == nil {
err = json.Unmarshal(interim, &manyErrCap)
if err == nil {
if len(manyErrCap.Errors) > 0 {
errMessage := ""
for i := range manyErrCap.Errors {
Expand Down
Loading

0 comments on commit 0564e45

Please sign in to comment.