cmd/exchange_template, exchanges: Update templates and propogate to exchanges (#1777)

* Added TimeInForce type and updated related files

* Linter issue fix and minor coinbasepro type update

* Bitrex consts update

* added unit test and minor changes in bittrex

* Unit tests update

* Fix minor linter issues

* Update TestStringToTimeInForce unit test

* Exchange test template change

* A different approach

* fix conflict with gateio timeInForce

* minor exchange template update

* Minor fix to test_files template

* Update order tests

* Complete updating the order unit tests

* Updating exchange wrapper and test template files

* update kucoin and deribit wrapper to match the time in force change

* minor comment update

* fix time-in-force related test errors

* linter issue fix

* ADD_NEW_EXCHANGE documentation update

* time in force constants, functions and unit tests update

* shift tif policies to TimeInForce

* Update time-in-force, related functions, and unit tests

* fix linter issue and time-in-force processing

* added a good till crossing tif value

* order type fix and fix related tim-in-force entries

* update time-in-force unmarshaling and unit test

* consistency guideline added

* fix time-in-force error in gateio

* linter issue fix

* update based on review comments

* add unit test and fix missing issues

* minor fix and added benchmark unit test

* change GTT to GTC for limit

* fix linter issue

* added time-in-force value to place order param

* fix minor issues based on review comment and move tif code to separate files

* update on exchanges linked to time-in-force

* resolve missing review comments

* minor linter issues fix

* added time-in-force handler and update timeInForce parametered endpoint

* minor fixes based on review

* nits fix

* update based on review

* linter fix

* rm getTimeInForce func and minor change to time-in-force

* minor change

* update based on review comments

* wrappers and time-in-force calling approach

* minor change

* update gateio string to timeInForce conversion and unit test

* update exchange template

* update wrapper template file

* policy comments, and template files update

* rename all exchange types name to Exchange

* update on template files and template generation

* templates and generation code and other updates

* linter issue fix

* added subscriptions and websocket templates

* update ADD_NEW_EXCHANGE.md with recent binance functions and implementations

* rename template files and update unit tests

* minor template and unit test fix

* rename templates and fix on unit tests

* update on template files and documentation

* removed unnecessary tag fix and update templates

* fix Add_NEW_EXCHANGE.md doc file

* formatting, comments, and error checks update on template files

* rename exchange receivers to e and ex for consistency

* rename unit test exchange receiver and minor updates

* linter issues fix

* fix deribit issue and minor style update

* fix test issues caused by receiver change

* raname local variables exchange declaration variables

* update templates comments

* update templates and related comments

* renamed ex to e

* update template comments

* toggle WS to false to improve coverage

* template comments update

* added test coverage to Ws enabled and minor changes

---------

Co-authored-by: Samuel Reid <43227667+cranktakular@users.noreply.github.com>
This commit is contained in:
Samuael A.
2025-07-17 03:46:36 +03:00
committed by GitHub
parent 485397a0c7
commit 3f534a15f1
163 changed files with 20453 additions and 20313 deletions

View File

@@ -51,31 +51,31 @@ const (
geminiTransfers = "transfers"
)
// Gemini is the overarching type across the Gemini package, create multiple
// Exchange implements exchange.IBotExchange and contains additional specific api methods for interacting with Gemini, create multiple
// instances with differing APIkeys for segregation of roles for authenticated
// requests & sessions by appending new sessions to the Session map using
// AddSession. If sandbox test is needed, append a new session with the same
// API keys and change the IsSandbox variable to true.
type Gemini struct {
type Exchange struct {
exchange.Base
}
// GetSymbols returns all available symbols for trading
func (g *Gemini) GetSymbols(ctx context.Context) ([]string, error) {
func (e *Exchange) GetSymbols(ctx context.Context) ([]string, error) {
var symbols []string
path := fmt.Sprintf("/v%s/%s", geminiAPIVersion, geminiSymbols)
return symbols, g.SendHTTPRequest(ctx, exchange.RestSpot, path, &symbols)
return symbols, e.SendHTTPRequest(ctx, exchange.RestSpot, path, &symbols)
}
// GetSymbolDetails returns extra symbol details
// use symbol "all" to get everything
func (g *Gemini) GetSymbolDetails(ctx context.Context, symbol string) ([]SymbolDetails, error) {
func (e *Exchange) GetSymbolDetails(ctx context.Context, symbol string) ([]SymbolDetails, error) {
if symbol == "all" {
var details []SymbolDetails
return details, g.SendHTTPRequest(ctx, exchange.RestSpot, "/v"+geminiAPIVersion+"/"+geminiSymbolDetails+"/"+symbol, &details)
return details, e.SendHTTPRequest(ctx, exchange.RestSpot, "/v"+geminiAPIVersion+"/"+geminiSymbolDetails+"/"+symbol, &details)
}
var details SymbolDetails
err := g.SendHTTPRequest(ctx, exchange.RestSpot, "/v"+geminiAPIVersion+"/"+geminiSymbolDetails+"/"+symbol, &details)
err := e.SendHTTPRequest(ctx, exchange.RestSpot, "/v"+geminiAPIVersion+"/"+geminiSymbolDetails+"/"+symbol, &details)
if err != nil {
return nil, err
}
@@ -83,16 +83,16 @@ func (g *Gemini) GetSymbolDetails(ctx context.Context, symbol string) ([]SymbolD
}
// GetTicker returns information about recent trading activity for the symbol
func (g *Gemini) GetTicker(ctx context.Context, currencyPair string) (TickerV2, error) {
func (e *Exchange) GetTicker(ctx context.Context, currencyPair string) (TickerV2, error) {
ticker := TickerV2{}
path := "/v2/ticker/" + currencyPair
err := g.SendHTTPRequest(ctx, exchange.RestSpot, path, &ticker)
err := e.SendHTTPRequest(ctx, exchange.RestSpot, path, &ticker)
if err != nil {
return ticker, err
}
if ticker.Result == "error" {
return ticker, fmt.Errorf("%v %v %v",
g.Name,
e.Name,
ticker.Reason,
ticker.Message)
}
@@ -105,7 +105,7 @@ func (g *Gemini) GetTicker(ctx context.Context, currencyPair string) (TickerV2,
//
// params - limit_bids or limit_asks [OPTIONAL] default 50, 0 returns all Values
// Type is an integer ie "params.Set("limit_asks", 30)"
func (g *Gemini) GetOrderbook(ctx context.Context, currencyPair string, params url.Values) (*Orderbook, error) {
func (e *Exchange) GetOrderbook(ctx context.Context, currencyPair string, params url.Values) (*Orderbook, error) {
path := common.EncodeURLValues(
fmt.Sprintf("/v%s/%s/%s",
geminiAPIVersion,
@@ -114,7 +114,7 @@ func (g *Gemini) GetOrderbook(ctx context.Context, currencyPair string, params u
params)
var orderbook Orderbook
return &orderbook, g.SendHTTPRequest(ctx, exchange.RestSpot, path, &orderbook)
return &orderbook, e.SendHTTPRequest(ctx, exchange.RestSpot, path, &orderbook)
}
// GetTrades return the trades that have executed since the specified timestamp.
@@ -126,7 +126,7 @@ func (g *Gemini) GetOrderbook(ctx context.Context, currencyPair string, params u
// limit_trades integer Optional. The maximum number of trades to return.
// include_breaks boolean Optional. Whether to display broken trades. False by
// default. Can be '1' or 'true' to activate
func (g *Gemini) GetTrades(ctx context.Context, currencyPair string, since, limit int64, includeBreaks bool) ([]Trade, error) {
func (e *Exchange) GetTrades(ctx context.Context, currencyPair string, since, limit int64, includeBreaks bool) ([]Trade, error) {
params := url.Values{}
if since > 0 {
params.Add("since", strconv.FormatInt(since, 10))
@@ -140,15 +140,15 @@ func (g *Gemini) GetTrades(ctx context.Context, currencyPair string, since, limi
path := common.EncodeURLValues(fmt.Sprintf("/v%s/%s/%s", geminiAPIVersion, geminiTrades, currencyPair), params)
var trades []Trade
return trades, g.SendHTTPRequest(ctx, exchange.RestSpot, path, &trades)
return trades, e.SendHTTPRequest(ctx, exchange.RestSpot, path, &trades)
}
// GetAuction returns auction information
func (g *Gemini) GetAuction(ctx context.Context, currencyPair string) (Auction, error) {
func (e *Exchange) GetAuction(ctx context.Context, currencyPair string) (Auction, error) {
path := fmt.Sprintf("/v%s/%s/%s", geminiAPIVersion, geminiAuction, currencyPair)
auction := Auction{}
return auction, g.SendHTTPRequest(ctx, exchange.RestSpot, path, &auction)
return auction, e.SendHTTPRequest(ctx, exchange.RestSpot, path, &auction)
}
// GetAuctionHistory returns the auction events, optionally including
@@ -168,15 +168,15 @@ func (g *Gemini) GetAuction(ctx context.Context, currencyPair string) (Auction,
// include_indicative - [bool] Whether to include publication of
//
// indicative prices and quantities.
func (g *Gemini) GetAuctionHistory(ctx context.Context, currencyPair string, params url.Values) ([]AuctionHistory, error) {
func (e *Exchange) GetAuctionHistory(ctx context.Context, currencyPair string, params url.Values) ([]AuctionHistory, error) {
path := common.EncodeURLValues(fmt.Sprintf("/v%s/%s/%s/%s", geminiAPIVersion, geminiAuction, currencyPair, geminiAuctionHistory), params)
var auctionHist []AuctionHistory
return auctionHist, g.SendHTTPRequest(ctx, exchange.RestSpot, path, &auctionHist)
return auctionHist, e.SendHTTPRequest(ctx, exchange.RestSpot, path, &auctionHist)
}
// NewOrder Only limit orders are supported through the API at present.
// returns order ID if successful
func (g *Gemini) NewOrder(ctx context.Context, symbol string, amount, price float64, side, orderType string) (int64, error) {
func (e *Exchange) NewOrder(ctx context.Context, symbol string, amount, price float64, side, orderType string) (int64, error) {
req := make(map[string]any)
req["symbol"] = symbol
req["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
@@ -185,7 +185,7 @@ func (g *Gemini) NewOrder(ctx context.Context, symbol string, amount, price floa
req["type"] = orderType
response := Order{}
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiOrderNew, req, &response)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiOrderNew, req, &response)
if err != nil {
return 0, err
}
@@ -193,7 +193,7 @@ func (g *Gemini) NewOrder(ctx context.Context, symbol string, amount, price floa
}
// Transfers returns transfer history ie withdrawals and deposits
func (g *Gemini) Transfers(ctx context.Context, curr currency.Code, start time.Time, limit int64, account string, showCompletedDeposit bool) ([]TransferResponse, error) {
func (e *Exchange) Transfers(ctx context.Context, curr currency.Code, start time.Time, limit int64, account string, showCompletedDeposit bool) ([]TransferResponse, error) {
req := make(map[string]any)
if !curr.IsEmpty() {
req["symbol"] = curr.String()
@@ -212,17 +212,17 @@ func (g *Gemini) Transfers(ctx context.Context, curr currency.Code, start time.T
}
var response []TransferResponse
return response, g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiTransfers, req, &response)
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiTransfers, req, &response)
}
// CancelExistingOrder will cancel an order. If the order is already canceled, the
// message will succeed but have no effect.
func (g *Gemini) CancelExistingOrder(ctx context.Context, orderID int64) (Order, error) {
func (e *Exchange) CancelExistingOrder(ctx context.Context, orderID int64) (Order, error) {
req := make(map[string]any)
req["order_id"] = orderID
response := Order{}
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiOrderCancel, req, &response)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiOrderCancel, req, &response)
if err != nil {
return Order{}, err
}
@@ -237,14 +237,14 @@ func (g *Gemini) CancelExistingOrder(ctx context.Context, orderID int64) (Order,
// sessions owned by this account, including interactive orders placed through
// the UI. If sessions = true will only cancel the order that is called on this
// session associated with the APIKEY
func (g *Gemini) CancelExistingOrders(ctx context.Context, cancelBySession bool) (OrderResult, error) {
func (e *Exchange) CancelExistingOrders(ctx context.Context, cancelBySession bool) (OrderResult, error) {
path := geminiOrderCancelAll
if cancelBySession {
path = geminiOrderCancelSession
}
var response OrderResult
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, path, nil, &response)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, path, nil, &response)
if err != nil {
return response, err
}
@@ -255,13 +255,13 @@ func (g *Gemini) CancelExistingOrders(ctx context.Context, cancelBySession bool)
}
// GetOrderStatus returns the status for an order
func (g *Gemini) GetOrderStatus(ctx context.Context, orderID int64) (Order, error) {
func (e *Exchange) GetOrderStatus(ctx context.Context, orderID int64) (Order, error) {
req := make(map[string]any)
req["order_id"] = orderID
response := Order{}
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiOrderStatus, req, &response)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiOrderStatus, req, &response)
if err != nil {
return response, err
}
@@ -273,14 +273,14 @@ func (g *Gemini) GetOrderStatus(ctx context.Context, orderID int64) (Order, erro
}
// GetOrders returns active orders in the market
func (g *Gemini) GetOrders(ctx context.Context) ([]Order, error) {
func (e *Exchange) GetOrders(ctx context.Context) ([]Order, error) {
var response any
type orders struct {
orders []Order
}
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiOrders, nil, &response)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiOrders, nil, &response)
if err != nil {
return nil, err
}
@@ -297,7 +297,7 @@ func (g *Gemini) GetOrders(ctx context.Context) ([]Order, error) {
//
// currencyPair - example "btcusd"
// timestamp - [optional] Only return trades on or after this timestamp.
func (g *Gemini) GetTradeHistory(ctx context.Context, currencyPair string, timestamp int64) ([]TradeHistory, error) {
func (e *Exchange) GetTradeHistory(ctx context.Context, currencyPair string, timestamp int64) ([]TradeHistory, error) {
var response []TradeHistory
req := make(map[string]any)
req["symbol"] = currencyPair
@@ -307,35 +307,35 @@ func (g *Gemini) GetTradeHistory(ctx context.Context, currencyPair string, times
}
return response,
g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiMyTrades, req, &response)
e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiMyTrades, req, &response)
}
// GetNotionalVolume returns the volume in price currency that has been traded across all pairs over a period of 30 days
func (g *Gemini) GetNotionalVolume(ctx context.Context) (NotionalVolume, error) {
func (e *Exchange) GetNotionalVolume(ctx context.Context) (NotionalVolume, error) {
response := NotionalVolume{}
return response,
g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiVolume, nil, &response)
e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiVolume, nil, &response)
}
// GetTradeVolume returns a multi-arrayed volume response
func (g *Gemini) GetTradeVolume(ctx context.Context) ([][]TradeVolume, error) {
func (e *Exchange) GetTradeVolume(ctx context.Context) ([][]TradeVolume, error) {
var response [][]TradeVolume
return response,
g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiTradeVolume, nil, &response)
e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiTradeVolume, nil, &response)
}
// GetBalances returns available balances in the supported currencies
func (g *Gemini) GetBalances(ctx context.Context) ([]Balance, error) {
func (e *Exchange) GetBalances(ctx context.Context) ([]Balance, error) {
var response []Balance
return response,
g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiBalances, nil, &response)
e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiBalances, nil, &response)
}
// GetCryptoDepositAddress returns a deposit address
func (g *Gemini) GetCryptoDepositAddress(ctx context.Context, depositAddlabel, currency string) (DepositAddress, error) {
func (e *Exchange) GetCryptoDepositAddress(ctx context.Context, depositAddlabel, currency string) (DepositAddress, error) {
response := DepositAddress{}
req := make(map[string]any)
@@ -343,7 +343,7 @@ func (g *Gemini) GetCryptoDepositAddress(ctx context.Context, depositAddlabel, c
req["label"] = depositAddlabel
}
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiDeposit+"/"+currency+"/"+geminiNewAddress, req, &response)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiDeposit+"/"+currency+"/"+geminiNewAddress, req, &response)
if err != nil {
return response, err
}
@@ -354,13 +354,13 @@ func (g *Gemini) GetCryptoDepositAddress(ctx context.Context, depositAddlabel, c
}
// WithdrawCrypto withdraws crypto currency to a whitelisted address
func (g *Gemini) WithdrawCrypto(ctx context.Context, address, currency string, amount float64) (WithdrawalAddress, error) {
func (e *Exchange) WithdrawCrypto(ctx context.Context, address, currency string, amount float64) (WithdrawalAddress, error) {
response := WithdrawalAddress{}
req := make(map[string]any)
req["address"] = address
req["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiWithdraw+strings.ToLower(currency), req, &response)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiWithdraw+strings.ToLower(currency), req, &response)
if err != nil {
return response, err
}
@@ -372,14 +372,14 @@ func (g *Gemini) WithdrawCrypto(ctx context.Context, address, currency string, a
// PostHeartbeat sends a maintenance heartbeat to the exchange for all heartbeat
// maintained sessions
func (g *Gemini) PostHeartbeat(ctx context.Context) (string, error) {
func (e *Exchange) PostHeartbeat(ctx context.Context) (string, error) {
type Response struct {
Result string `json:"result"`
Message string `json:"message"`
}
response := Response{}
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiHeartbeat, nil, &response)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, geminiHeartbeat, nil, &response)
if err != nil {
return response.Result, err
}
@@ -390,8 +390,8 @@ func (g *Gemini) PostHeartbeat(ctx context.Context) (string, error) {
}
// SendHTTPRequest sends an unauthenticated request
func (g *Gemini) SendHTTPRequest(ctx context.Context, ep exchange.URL, path string, result any) error {
endpoint, err := g.API.Endpoints.GetURL(ep)
func (e *Exchange) SendHTTPRequest(ctx context.Context, ep exchange.URL, path string, result any) error {
endpoint, err := e.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
@@ -400,33 +400,33 @@ func (g *Gemini) SendHTTPRequest(ctx context.Context, ep exchange.URL, path stri
Method: http.MethodGet,
Path: endpoint + path,
Result: result,
Verbose: g.Verbose,
HTTPDebugging: g.HTTPDebugging,
HTTPRecording: g.HTTPRecording,
Verbose: e.Verbose,
HTTPDebugging: e.HTTPDebugging,
HTTPRecording: e.HTTPRecording,
}
return g.SendPayload(ctx, request.UnAuth, func() (*request.Item, error) {
return e.SendPayload(ctx, request.UnAuth, func() (*request.Item, error) {
return item, nil
}, request.UnauthenticatedRequest)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the
// exchange and returns an error
func (g *Gemini) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange.URL, method, path string, params map[string]any, result any) (err error) {
creds, err := g.GetCredentials(ctx)
func (e *Exchange) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange.URL, method, path string, params map[string]any, result any) (err error) {
creds, err := e.GetCredentials(ctx)
if err != nil {
return err
}
endpoint, err := g.API.Endpoints.GetURL(ep)
endpoint, err := e.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return g.SendPayload(ctx, request.Auth, func() (*request.Item, error) {
return e.SendPayload(ctx, request.Auth, func() (*request.Item, error) {
req := make(map[string]any)
req["request"] = fmt.Sprintf("/v%s/%s", geminiAPIVersion, path)
req["nonce"] = g.Requester.GetNonce(nonce.UnixNano).String()
req["nonce"] = e.Requester.GetNonce(nonce.UnixNano).String()
maps.Copy(req, params)
@@ -455,19 +455,19 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange.U
Headers: headers,
Result: result,
NonceEnabled: true,
Verbose: g.Verbose,
HTTPDebugging: g.HTTPDebugging,
HTTPRecording: g.HTTPRecording,
Verbose: e.Verbose,
HTTPDebugging: e.HTTPDebugging,
HTTPRecording: e.HTTPRecording,
}, nil
}, request.AuthenticatedRequest)
}
// GetFee returns an estimate of fee based on type of transaction
func (g *Gemini) GetFee(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) {
func (e *Exchange) GetFee(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) {
var fee float64
switch feeBuilder.FeeType {
case exchange.CryptocurrencyTradeFee:
notionVolume, err := g.GetNotionalVolume(ctx)
notionVolume, err := e.GetNotionalVolume(ctx)
if err != nil {
return 0, err
}

View File

@@ -17,17 +17,17 @@ import (
var mockTests = false
func TestMain(m *testing.M) {
g = new(Gemini)
if err := testexch.Setup(g); err != nil {
e = new(Exchange)
if err := testexch.Setup(e); err != nil {
log.Fatalf("Gemini Setup error: %s", err)
}
if apiKey != "" && apiSecret != "" {
g.API.AuthenticatedSupport = true
g.SetCredentials(apiKey, apiSecret, "", "", "", "")
e.API.AuthenticatedSupport = true
e.SetCredentials(apiKey, apiSecret, "", "", "", "")
}
if err := g.API.Endpoints.SetRunningURL(exchange.RestSpot.String(), geminiAPIURL); err != nil {
if err := e.API.Endpoints.SetRunningURL(exchange.RestSpot.String(), geminiAPIURL); err != nil {
log.Fatalf("Gemini SetRunningURL error: %s", err)
}
log.Printf(sharedtestvalues.LiveTesting, g.Name)
log.Printf(sharedtestvalues.LiveTesting, e.Name)
os.Exit(m.Run())
}

View File

@@ -15,12 +15,12 @@ import (
var mockTests = true
func TestMain(m *testing.M) {
g = new(Gemini)
if err := testexch.Setup(g); err != nil {
e = new(Exchange)
if err := testexch.Setup(e); err != nil {
log.Fatalf("Gemini Setup error: %s", err)
}
if err := testexch.MockHTTPInstance(g); err != nil {
if err := testexch.MockHTTPInstance(e); err != nil {
log.Fatalf("Gemini MockHTTPInstance error: %s", err)
}

View File

@@ -33,11 +33,11 @@ const (
const testCurrency = "btcusd"
var g = &Gemini{}
var e *Exchange
func TestGetSymbols(t *testing.T) {
t.Parallel()
_, err := g.GetSymbols(t.Context())
_, err := e.GetSymbols(t.Context())
if err != nil {
t.Error("GetSymbols() error", err)
}
@@ -45,7 +45,7 @@ func TestGetSymbols(t *testing.T) {
func TestFetchTradablePairs(t *testing.T) {
t.Parallel()
pairs, err := g.FetchTradablePairs(t.Context(), asset.Spot)
pairs, err := e.FetchTradablePairs(t.Context(), asset.Spot)
if err != nil {
t.Fatal(err)
}
@@ -62,11 +62,11 @@ func TestFetchTradablePairs(t *testing.T) {
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := g.GetTicker(t.Context(), "BTCUSD")
_, err := e.GetTicker(t.Context(), "BTCUSD")
if err != nil {
t.Error("GetTicker() error", err)
}
_, err = g.GetTicker(t.Context(), "bla")
_, err = e.GetTicker(t.Context(), "bla")
if err == nil {
t.Error("GetTicker() Expected error")
}
@@ -74,7 +74,7 @@ func TestGetTicker(t *testing.T) {
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := g.GetOrderbook(t.Context(), testCurrency, url.Values{})
_, err := e.GetOrderbook(t.Context(), testCurrency, url.Values{})
if err != nil {
t.Error("GetOrderbook() error", err)
}
@@ -82,7 +82,7 @@ func TestGetOrderbook(t *testing.T) {
func TestGetTrades(t *testing.T) {
t.Parallel()
_, err := g.GetTrades(t.Context(), testCurrency, 0, 0, false)
_, err := e.GetTrades(t.Context(), testCurrency, 0, 0, false)
if err != nil {
t.Error("GetTrades() error", err)
}
@@ -90,7 +90,7 @@ func TestGetTrades(t *testing.T) {
func TestGetNotionalVolume(t *testing.T) {
t.Parallel()
_, err := g.GetNotionalVolume(t.Context())
_, err := e.GetNotionalVolume(t.Context())
if err != nil && mockTests {
t.Error("GetNotionalVolume() error", err)
} else if err == nil && !mockTests {
@@ -100,7 +100,7 @@ func TestGetNotionalVolume(t *testing.T) {
func TestGetAuction(t *testing.T) {
t.Parallel()
_, err := g.GetAuction(t.Context(), testCurrency)
_, err := e.GetAuction(t.Context(), testCurrency)
if err != nil {
t.Error("GetAuction() error", err)
}
@@ -108,7 +108,7 @@ func TestGetAuction(t *testing.T) {
func TestGetAuctionHistory(t *testing.T) {
t.Parallel()
_, err := g.GetAuctionHistory(t.Context(), testCurrency, url.Values{})
_, err := e.GetAuctionHistory(t.Context(), testCurrency, url.Values{})
if err != nil {
t.Error("GetAuctionHistory() error", err)
}
@@ -116,7 +116,7 @@ func TestGetAuctionHistory(t *testing.T) {
func TestNewOrder(t *testing.T) {
t.Parallel()
_, err := g.NewOrder(t.Context(),
_, err := e.NewOrder(t.Context(),
testCurrency,
1,
9000000,
@@ -131,7 +131,7 @@ func TestNewOrder(t *testing.T) {
func TestCancelExistingOrder(t *testing.T) {
t.Parallel()
_, err := g.CancelExistingOrder(t.Context(), 265555413)
_, err := e.CancelExistingOrder(t.Context(), 265555413)
if err != nil && mockTests {
t.Error("CancelExistingOrder() error", err)
} else if err == nil && !mockTests {
@@ -141,7 +141,7 @@ func TestCancelExistingOrder(t *testing.T) {
func TestCancelExistingOrders(t *testing.T) {
t.Parallel()
_, err := g.CancelExistingOrders(t.Context(), false)
_, err := e.CancelExistingOrders(t.Context(), false)
if err != nil && mockTests {
t.Error("CancelExistingOrders() error", err)
} else if err == nil && !mockTests {
@@ -151,7 +151,7 @@ func TestCancelExistingOrders(t *testing.T) {
func TestGetOrderStatus(t *testing.T) {
t.Parallel()
_, err := g.GetOrderStatus(t.Context(), 265563260)
_, err := e.GetOrderStatus(t.Context(), 265563260)
if err != nil && mockTests {
t.Error("GetOrderStatus() error", err)
} else if err == nil && !mockTests {
@@ -161,7 +161,7 @@ func TestGetOrderStatus(t *testing.T) {
func TestGetOrders(t *testing.T) {
t.Parallel()
_, err := g.GetOrders(t.Context())
_, err := e.GetOrders(t.Context())
if err != nil && mockTests {
t.Error("GetOrders() error", err)
} else if err == nil && !mockTests {
@@ -171,7 +171,7 @@ func TestGetOrders(t *testing.T) {
func TestGetTradeHistory(t *testing.T) {
t.Parallel()
_, err := g.GetTradeHistory(t.Context(), testCurrency, 0)
_, err := e.GetTradeHistory(t.Context(), testCurrency, 0)
if err != nil && mockTests {
t.Error("GetTradeHistory() error", err)
} else if err == nil && !mockTests {
@@ -181,7 +181,7 @@ func TestGetTradeHistory(t *testing.T) {
func TestGetTradeVolume(t *testing.T) {
t.Parallel()
_, err := g.GetTradeVolume(t.Context())
_, err := e.GetTradeVolume(t.Context())
if err != nil && mockTests {
t.Error("GetTradeVolume() error", err)
} else if err == nil && !mockTests {
@@ -191,7 +191,7 @@ func TestGetTradeVolume(t *testing.T) {
func TestGetBalances(t *testing.T) {
t.Parallel()
_, err := g.GetBalances(t.Context())
_, err := e.GetBalances(t.Context())
if err != nil && mockTests {
t.Error("GetBalances() error", err)
} else if err == nil && !mockTests {
@@ -201,7 +201,7 @@ func TestGetBalances(t *testing.T) {
func TestGetCryptoDepositAddress(t *testing.T) {
t.Parallel()
_, err := g.GetCryptoDepositAddress(t.Context(), "LOL123", "btc")
_, err := e.GetCryptoDepositAddress(t.Context(), "LOL123", "btc")
if err == nil {
t.Error("GetCryptoDepositAddress() Expected error")
}
@@ -209,7 +209,7 @@ func TestGetCryptoDepositAddress(t *testing.T) {
func TestWithdrawCrypto(t *testing.T) {
t.Parallel()
_, err := g.WithdrawCrypto(t.Context(), "LOL123", "btc", 1)
_, err := e.WithdrawCrypto(t.Context(), "LOL123", "btc", 1)
if err == nil {
t.Error("WithdrawCrypto() Expected error")
}
@@ -217,7 +217,7 @@ func TestWithdrawCrypto(t *testing.T) {
func TestPostHeartbeat(t *testing.T) {
t.Parallel()
_, err := g.PostHeartbeat(t.Context())
_, err := e.PostHeartbeat(t.Context())
if err != nil && mockTests {
t.Error("PostHeartbeat() error", err)
} else if err == nil && !mockTests {
@@ -242,11 +242,11 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
feeBuilder := setFeeBuilder()
_, err := g.GetFeeByType(t.Context(), feeBuilder)
_, err := e.GetFeeByType(t.Context(), feeBuilder)
if err != nil {
t.Fatal(err)
}
if !sharedtestvalues.AreAPICredentialsSet(g) {
if !sharedtestvalues.AreAPICredentialsSet(e) {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v",
exchange.OfflineTradeFee,
@@ -264,9 +264,9 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
func TestGetFee(t *testing.T) {
t.Parallel()
feeBuilder := setFeeBuilder()
if sharedtestvalues.AreAPICredentialsSet(g) || mockTests {
if sharedtestvalues.AreAPICredentialsSet(e) || mockTests {
// CryptocurrencyTradeFee Basic
if _, err := g.GetFee(t.Context(), feeBuilder); err != nil {
if _, err := e.GetFee(t.Context(), feeBuilder); err != nil {
t.Error(err)
}
@@ -274,28 +274,28 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.Amount = 1000
feeBuilder.PurchasePrice = 1000
if _, err := g.GetFee(t.Context(), feeBuilder); err != nil {
if _, err := e.GetFee(t.Context(), feeBuilder); err != nil {
t.Error(err)
}
// CryptocurrencyTradeFee IsMaker
feeBuilder = setFeeBuilder()
feeBuilder.IsMaker = true
if _, err := g.GetFee(t.Context(), feeBuilder); err != nil {
if _, err := e.GetFee(t.Context(), feeBuilder); err != nil {
t.Error(err)
}
// CryptocurrencyTradeFee Negative purchase price
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
if _, err := g.GetFee(t.Context(), feeBuilder); err != nil {
if _, err := e.GetFee(t.Context(), feeBuilder); err != nil {
t.Error(err)
}
}
// CryptocurrencyWithdrawalFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if _, err := g.GetFee(t.Context(), feeBuilder); err != nil {
if _, err := e.GetFee(t.Context(), feeBuilder); err != nil {
t.Error(err)
}
@@ -303,21 +303,21 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.Pair.Base = currency.NewCode("hello")
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if _, err := g.GetFee(t.Context(), feeBuilder); err != nil {
if _, err := e.GetFee(t.Context(), feeBuilder); err != nil {
t.Error(err)
}
// CryptocurrencyDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyDepositFee
if _, err := g.GetFee(t.Context(), feeBuilder); err != nil {
if _, err := e.GetFee(t.Context(), feeBuilder); err != nil {
t.Error(err)
}
// InternationalBankDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankDepositFee
if _, err := g.GetFee(t.Context(), feeBuilder); err != nil {
if _, err := e.GetFee(t.Context(), feeBuilder); err != nil {
t.Error(err)
}
@@ -325,7 +325,7 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
feeBuilder.FiatCurrency = currency.USD
if _, err := g.GetFee(t.Context(), feeBuilder); err != nil {
if _, err := e.GetFee(t.Context(), feeBuilder); err != nil {
t.Error(err)
}
}
@@ -337,7 +337,7 @@ func TestFormatWithdrawPermissions(t *testing.T) {
exchange.AutoWithdrawCryptoWithSetupText +
" & " +
exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := g.FormatWithdrawPermissions()
withdrawPermissions := e.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s",
expectedResult,
@@ -356,11 +356,11 @@ func TestGetActiveOrders(t *testing.T) {
Side: order.AnySide,
}
_, err := g.GetActiveOrders(t.Context(), &getOrdersRequest)
_, err := e.GetActiveOrders(t.Context(), &getOrdersRequest)
switch {
case sharedtestvalues.AreAPICredentialsSet(g) && err != nil && !mockTests:
case sharedtestvalues.AreAPICredentialsSet(e) && err != nil && !mockTests:
t.Errorf("Could not get open orders: %s", err)
case !sharedtestvalues.AreAPICredentialsSet(g) && err == nil && !mockTests:
case !sharedtestvalues.AreAPICredentialsSet(e) && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get open orders: %s", err)
@@ -376,11 +376,11 @@ func TestGetOrderHistory(t *testing.T) {
Side: order.AnySide,
}
_, err := g.GetOrderHistory(t.Context(), &getOrdersRequest)
_, err := e.GetOrderHistory(t.Context(), &getOrdersRequest)
switch {
case sharedtestvalues.AreAPICredentialsSet(g) && err != nil:
case sharedtestvalues.AreAPICredentialsSet(e) && err != nil:
t.Errorf("Could not get order history: %s", err)
case !sharedtestvalues.AreAPICredentialsSet(g) && err == nil && !mockTests:
case !sharedtestvalues.AreAPICredentialsSet(e) && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case err != nil && mockTests:
t.Errorf("Could not get order history: %s", err)
@@ -392,11 +392,11 @@ func TestGetOrderHistory(t *testing.T) {
func TestSubmitOrder(t *testing.T) {
t.Parallel()
if !mockTests {
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, g, canManipulateRealOrders)
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, e, canManipulateRealOrders)
}
orderSubmission := &order.Submit{
Exchange: g.Name,
Exchange: e.Name,
Pair: currency.Pair{
Delimiter: "_",
Base: currency.LTC,
@@ -410,11 +410,11 @@ func TestSubmitOrder(t *testing.T) {
AssetType: asset.Spot,
}
response, err := g.SubmitOrder(t.Context(), orderSubmission)
response, err := e.SubmitOrder(t.Context(), orderSubmission)
switch {
case sharedtestvalues.AreAPICredentialsSet(g) && (err != nil || response.Status != order.New):
case sharedtestvalues.AreAPICredentialsSet(e) && (err != nil || response.Status != order.New):
t.Errorf("Order failed to be placed: %v", err)
case !sharedtestvalues.AreAPICredentialsSet(g) && err == nil && !mockTests:
case !sharedtestvalues.AreAPICredentialsSet(e) && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Order failed to be placed: %v", err)
@@ -424,7 +424,7 @@ func TestSubmitOrder(t *testing.T) {
func TestCancelExchangeOrder(t *testing.T) {
t.Parallel()
if !mockTests {
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, g, canManipulateRealOrders)
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, e, canManipulateRealOrders)
}
orderCancellation := &order.Cancel{
OrderID: "266029865",
@@ -432,11 +432,11 @@ func TestCancelExchangeOrder(t *testing.T) {
Pair: currency.NewBTCUSDT(),
}
err := g.CancelOrder(t.Context(), orderCancellation)
err := e.CancelOrder(t.Context(), orderCancellation)
switch {
case !sharedtestvalues.AreAPICredentialsSet(g) && err == nil && !mockTests:
case !sharedtestvalues.AreAPICredentialsSet(e) && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case sharedtestvalues.AreAPICredentialsSet(g) && err != nil:
case sharedtestvalues.AreAPICredentialsSet(e) && err != nil:
t.Errorf("Could not cancel orders: %v", err)
case err != nil && mockTests:
t.Errorf("Could not cancel orders: %v", err)
@@ -446,7 +446,7 @@ func TestCancelExchangeOrder(t *testing.T) {
func TestCancelAllExchangeOrders(t *testing.T) {
t.Parallel()
if !mockTests {
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, g, canManipulateRealOrders)
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, e, canManipulateRealOrders)
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
@@ -457,11 +457,11 @@ func TestCancelAllExchangeOrders(t *testing.T) {
AssetType: asset.Spot,
}
resp, err := g.CancelAllOrders(t.Context(), orderCancellation)
resp, err := e.CancelAllOrders(t.Context(), orderCancellation)
switch {
case !sharedtestvalues.AreAPICredentialsSet(g) && err == nil && !mockTests:
case !sharedtestvalues.AreAPICredentialsSet(e) && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case sharedtestvalues.AreAPICredentialsSet(g) && err != nil:
case sharedtestvalues.AreAPICredentialsSet(e) && err != nil:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Errorf("Could not cancel orders: %v", err)
@@ -474,9 +474,9 @@ func TestCancelAllExchangeOrders(t *testing.T) {
func TestModifyOrder(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, g, canManipulateRealOrders)
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, e, canManipulateRealOrders)
_, err := g.ModifyOrder(t.Context(), &order.Modify{AssetType: asset.Spot})
_, err := e.ModifyOrder(t.Context(), &order.Modify{AssetType: asset.Spot})
if err == nil {
t.Error("ModifyOrder() Expected error")
}
@@ -486,12 +486,12 @@ func TestWithdraw(t *testing.T) {
t.Parallel()
if !mockTests {
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, g, canManipulateRealOrders)
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, e, canManipulateRealOrders)
}
_, err := g.WithdrawCryptocurrencyFunds(t.Context(),
_, err := e.WithdrawCryptocurrencyFunds(t.Context(),
&withdraw.Request{
Exchange: g.Name,
Exchange: e.Name,
Amount: -1,
Currency: currency.BTC,
Description: "WITHDRAW IT ALL",
@@ -499,13 +499,13 @@ func TestWithdraw(t *testing.T) {
Address: core.BitcoinDonationAddress,
},
})
if !sharedtestvalues.AreAPICredentialsSet(g) && err == nil {
if !sharedtestvalues.AreAPICredentialsSet(e) && err == nil {
t.Error("Expecting an error when no keys are set")
}
if sharedtestvalues.AreAPICredentialsSet(g) && err != nil && !mockTests {
if sharedtestvalues.AreAPICredentialsSet(e) && err != nil && !mockTests {
t.Errorf("Withdraw failed to be placed: %v", err)
}
if sharedtestvalues.AreAPICredentialsSet(g) && err == nil && mockTests {
if sharedtestvalues.AreAPICredentialsSet(e) && err == nil && mockTests {
t.Errorf("Withdraw failed to be placed: %v", err)
}
}
@@ -513,11 +513,11 @@ func TestWithdraw(t *testing.T) {
func TestWithdrawFiat(t *testing.T) {
t.Parallel()
if !mockTests {
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, g, canManipulateRealOrders)
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, e, canManipulateRealOrders)
}
withdrawFiatRequest := withdraw.Request{}
_, err := g.WithdrawFiatFunds(t.Context(), &withdrawFiatRequest)
_, err := e.WithdrawFiatFunds(t.Context(), &withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
@@ -528,11 +528,11 @@ func TestWithdrawFiat(t *testing.T) {
func TestWithdrawInternationalBank(t *testing.T) {
t.Parallel()
if !mockTests {
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, g, canManipulateRealOrders)
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, e, canManipulateRealOrders)
}
withdrawFiatRequest := withdraw.Request{}
_, err := g.WithdrawFiatFundsToInternationalBank(t.Context(),
_, err := e.WithdrawFiatFundsToInternationalBank(t.Context(),
&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'",
@@ -543,7 +543,7 @@ func TestWithdrawInternationalBank(t *testing.T) {
func TestGetDepositAddress(t *testing.T) {
t.Parallel()
_, err := g.GetDepositAddress(t.Context(), currency.BTC, "", "")
_, err := e.GetDepositAddress(t.Context(), currency.BTC, "", "")
if err == nil {
t.Error("GetDepositAddress error cannot be nil")
}
@@ -552,24 +552,24 @@ func TestGetDepositAddress(t *testing.T) {
// TestWsAuth dials websocket, sends login request.
func TestWsAuth(t *testing.T) {
t.Parallel()
err := g.API.Endpoints.SetRunningURL(exchange.WebsocketSpot.String(), geminiWebsocketSandboxEndpoint)
err := e.API.Endpoints.SetRunningURL(exchange.WebsocketSpot.String(), geminiWebsocketSandboxEndpoint)
if err != nil {
t.Error(err)
}
if !g.Websocket.IsEnabled() &&
!g.API.AuthenticatedWebsocketSupport ||
!sharedtestvalues.AreAPICredentialsSet(g) {
if !e.Websocket.IsEnabled() &&
!e.API.AuthenticatedWebsocketSupport ||
!sharedtestvalues.AreAPICredentialsSet(e) {
t.Skip(websocket.ErrWebsocketNotEnabled.Error())
}
var dialer gws.Dialer
go g.wsReadData()
err = g.WsAuth(t.Context(), &dialer)
go e.wsReadData()
err = e.WsAuth(t.Context(), &dialer)
if err != nil {
t.Error(err)
}
timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout)
select {
case resp := <-g.Websocket.DataHandler:
case resp := <-e.Websocket.DataHandler:
subAck, ok := resp.(WsSubscriptionAcknowledgementResponse)
if !ok {
t.Error("unable to type assert WsSubscriptionAcknowledgementResponse")
@@ -589,7 +589,7 @@ func TestWsMissingRole(t *testing.T) {
"reason":"MissingRole",
"message":"To access this endpoint, you need to log in to the website and go to the settings page to assign one of these roles [FundManager] to API key wujB3szN54gtJ4QDhqRJ which currently has roles [Trader]"
}`)
if err := g.wsHandleData(pressXToJSON); err == nil {
if err := e.wsHandleData(pressXToJSON); err == nil {
t.Error("Expected error")
}
}
@@ -613,7 +613,7 @@ func TestWsOrderEventSubscriptionResponse(t *testing.T) {
"original_amount" : "14.0296",
"price" : "1059.54"
} ]`)
err := g.wsHandleData(pressXToJSON)
err := e.wsHandleData(pressXToJSON)
if err != nil {
t.Error(err)
}
@@ -635,7 +635,7 @@ func TestWsOrderEventSubscriptionResponse(t *testing.T) {
"price": "3592.00",
"socket_sequence": 13
}]`)
err = g.wsHandleData(pressXToJSON)
err = e.wsHandleData(pressXToJSON)
if err != nil {
t.Error(err)
}
@@ -656,7 +656,7 @@ func TestWsOrderEventSubscriptionResponse(t *testing.T) {
"total_spend": "200.00",
"socket_sequence": 29
}]`)
err = g.wsHandleData(pressXToJSON)
err = e.wsHandleData(pressXToJSON)
if err != nil {
t.Error(err)
}
@@ -677,7 +677,7 @@ func TestWsOrderEventSubscriptionResponse(t *testing.T) {
"original_amount": "25",
"socket_sequence": 26
}]`)
err = g.wsHandleData(pressXToJSON)
err = e.wsHandleData(pressXToJSON)
if err != nil {
t.Error(err)
}
@@ -699,7 +699,7 @@ func TestWsOrderEventSubscriptionResponse(t *testing.T) {
"original_amount" : "500",
"socket_sequence" : 32307
} ]`)
err = g.wsHandleData(pressXToJSON)
err = e.wsHandleData(pressXToJSON)
if err != nil {
t.Error(err)
}
@@ -721,7 +721,7 @@ func TestWsSubAck(t *testing.T) {
"closed"
]
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
}
@@ -734,7 +734,7 @@ func TestWsHeartbeat(t *testing.T) {
"trace_id": "b8biknoqppr32kc7gfgg",
"socket_sequence": 37
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
}
@@ -755,7 +755,7 @@ func TestWsUnsubscribe(t *testing.T) {
]}
]
}`)
err := g.wsHandleData(pressXToJSON)
err := e.wsHandleData(pressXToJSON)
if err != nil {
t.Error(err)
}
@@ -778,7 +778,7 @@ func TestWsTradeData(t *testing.T) {
}
]
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
}
@@ -810,7 +810,7 @@ func TestWsAuctionData(t *testing.T) {
],
"type": "update"
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
}
@@ -831,7 +831,7 @@ func TestWsBlockTrade(t *testing.T) {
}
]
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
}
@@ -846,7 +846,7 @@ func TestWSTrade(t *testing.T) {
"quantity": "0.09110000",
"side": "buy"
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
}
@@ -874,7 +874,7 @@ func TestWsCandles(t *testing.T) {
]
]
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
}
@@ -896,7 +896,7 @@ func TestWsAuctions(t *testing.T) {
],
"type": "update"
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
@@ -920,7 +920,7 @@ func TestWsAuctions(t *testing.T) {
}
]
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
@@ -951,7 +951,7 @@ func TestWsAuctions(t *testing.T) {
}
]
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
}
@@ -980,7 +980,7 @@ func TestWsMarketData(t *testing.T) {
}
]
} `)
err := g.wsHandleData(pressXToJSON)
err := e.wsHandleData(pressXToJSON)
if err != nil {
t.Error(err)
}
@@ -1008,7 +1008,7 @@ func TestWsMarketData(t *testing.T) {
}
]
} `)
err = g.wsHandleData(pressXToJSON)
err = e.wsHandleData(pressXToJSON)
if err != nil {
t.Error(err)
}
@@ -1030,7 +1030,7 @@ func TestWsMarketData(t *testing.T) {
}
]
} `)
err = g.wsHandleData(pressXToJSON)
err = e.wsHandleData(pressXToJSON)
if err != nil {
t.Error(err)
}
@@ -1068,7 +1068,7 @@ func TestWsError(t *testing.T) {
}
for x := range tt {
err := g.wsHandleData(tt[x].Data)
err := e.wsHandleData(tt[x].Data)
if tt[x].ErrorExpected && err != nil && !strings.Contains(err.Error(), tt[x].ErrorShouldContain) {
t.Errorf("expected error to contain: %s, got: %s",
tt[x].ErrorShouldContain, err.Error(),
@@ -1128,7 +1128,7 @@ func TestWsLevel2Update(t *testing.T) {
}
]
}`)
if err := g.wsHandleData(pressXToJSON); err != nil {
if err := e.wsHandleData(pressXToJSON); err != nil {
t.Error(err)
}
}
@@ -1183,7 +1183,7 @@ func TestGetRecentTrades(t *testing.T) {
if err != nil {
t.Fatal(err)
}
_, err = g.GetRecentTrades(t.Context(), currencyPair, asset.Spot)
_, err = e.GetRecentTrades(t.Context(), currencyPair, asset.Spot)
if err != nil {
t.Error(err)
}
@@ -1201,7 +1201,7 @@ func TestGetHistoricTrades(t *testing.T) {
tStart = time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.UTC)
tEnd = time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 15, 0, 0, time.UTC)
}
_, err = g.GetHistoricTrades(t.Context(),
_, err = e.GetHistoricTrades(t.Context(),
currencyPair, asset.Spot, tStart, tEnd)
if err != nil {
t.Error(err)
@@ -1210,9 +1210,9 @@ func TestGetHistoricTrades(t *testing.T) {
func TestTransfers(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, g)
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := g.Transfers(t.Context(), currency.BTC, time.Time{}, 100, "", true)
_, err := e.Transfers(t.Context(), currency.BTC, time.Time{}, 100, "", true)
if err != nil {
t.Error(err)
}
@@ -1220,9 +1220,9 @@ func TestTransfers(t *testing.T) {
func TestGetAccountFundingHistory(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, g)
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := g.GetAccountFundingHistory(t.Context())
_, err := e.GetAccountFundingHistory(t.Context())
if err != nil {
t.Error(err)
}
@@ -1230,9 +1230,9 @@ func TestGetAccountFundingHistory(t *testing.T) {
func TestGetWithdrawalsHistory(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, g)
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := g.GetWithdrawalsHistory(t.Context(), currency.BTC, asset.Spot)
_, err := e.GetWithdrawalsHistory(t.Context(), currency.BTC, asset.Spot)
if err != nil {
t.Error(err)
}
@@ -1240,9 +1240,9 @@ func TestGetWithdrawalsHistory(t *testing.T) {
func TestGetOrderInfo(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, g)
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := g.GetOrderInfo(t.Context(), "1234", currency.EMPTYPAIR, asset.Empty)
_, err := e.GetOrderInfo(t.Context(), "1234", currency.EMPTYPAIR, asset.Empty)
if err != nil {
t.Error(err)
}
@@ -1250,11 +1250,11 @@ func TestGetOrderInfo(t *testing.T) {
func TestGetSymbolDetails(t *testing.T) {
t.Parallel()
_, err := g.GetSymbolDetails(t.Context(), "all")
_, err := e.GetSymbolDetails(t.Context(), "all")
if err != nil {
t.Error(err)
}
_, err = g.GetSymbolDetails(t.Context(), "btcusd")
_, err = e.GetSymbolDetails(t.Context(), "btcusd")
if err != nil {
t.Error(err)
}
@@ -1262,18 +1262,18 @@ func TestGetSymbolDetails(t *testing.T) {
func TestSetExchangeOrderExecutionLimits(t *testing.T) {
t.Parallel()
err := g.UpdateOrderExecutionLimits(t.Context(), asset.Spot)
err := e.UpdateOrderExecutionLimits(t.Context(), asset.Spot)
if err != nil {
t.Fatal(err)
}
err = g.UpdateOrderExecutionLimits(t.Context(), asset.Futures)
err = e.UpdateOrderExecutionLimits(t.Context(), asset.Futures)
assert.ErrorIs(t, err, asset.ErrNotSupported)
availPairs, err := g.GetAvailablePairs(asset.Spot)
availPairs, err := e.GetAvailablePairs(asset.Spot)
require.NoError(t, err)
for x := range availPairs {
var limit order.MinMaxLevel
limit, err = g.GetOrderExecutionLimits(asset.Spot, availPairs[x])
limit, err = e.GetOrderExecutionLimits(asset.Spot, availPairs[x])
if err != nil {
t.Fatal(err, availPairs[x])
}
@@ -1285,12 +1285,12 @@ func TestSetExchangeOrderExecutionLimits(t *testing.T) {
func TestGetCurrencyTradeURL(t *testing.T) {
t.Parallel()
testexch.UpdatePairsOnce(t, g)
for _, a := range g.GetAssetTypes(false) {
pairs, err := g.CurrencyPairs.GetPairs(a, false)
testexch.UpdatePairsOnce(t, e)
for _, a := range e.GetAssetTypes(false) {
pairs, err := e.CurrencyPairs.GetPairs(a, false)
require.NoErrorf(t, err, "cannot get pairs for %s", a)
require.NotEmptyf(t, pairs, "no pairs for %s", a)
resp, err := g.GetCurrencyTradeURL(t.Context(), a, pairs[0])
resp, err := e.GetCurrencyTradeURL(t.Context(), a, pairs[0])
require.NoError(t, err)
assert.NotEmpty(t, resp)
}
@@ -1298,12 +1298,12 @@ func TestGetCurrencyTradeURL(t *testing.T) {
func TestGenerateSubscriptions(t *testing.T) {
t.Parallel()
g := new(Gemini)
require.NoError(t, testexch.Setup(g), "Test instance Setup must not error")
e := new(Exchange)
require.NoError(t, testexch.Setup(e), "Test instance Setup must not error")
p := currency.Pairs{currency.NewPairWithDelimiter("BTC", "USD", ""), currency.NewPairWithDelimiter("ETH", "BTC", "")}
require.NoError(t, g.CurrencyPairs.StorePairs(asset.Spot, p, false))
require.NoError(t, g.CurrencyPairs.StorePairs(asset.Spot, p, true))
subs, err := g.generateSubscriptions()
require.NoError(t, e.CurrencyPairs.StorePairs(asset.Spot, p, false))
require.NoError(t, e.CurrencyPairs.StorePairs(asset.Spot, p, true))
subs, err := e.generateSubscriptions()
require.NoError(t, err)
exp := subscription.List{
{Asset: asset.Spot, Channel: subscription.CandlesChannel, Pairs: p, QualifiedChannel: "candles_1d", Interval: kline.OneDay},
@@ -1312,12 +1312,12 @@ func TestGenerateSubscriptions(t *testing.T) {
testsubs.EqualLists(t, exp, subs)
for _, i := range []kline.Interval{kline.OneMin, kline.FiveMin, kline.FifteenMin, kline.ThirtyMin, kline.OneHour, kline.SixHour} {
subs, err = subscription.List{{Asset: asset.Spot, Channel: subscription.CandlesChannel, Pairs: p, Interval: i}}.ExpandTemplates(g)
subs, err = subscription.List{{Asset: asset.Spot, Channel: subscription.CandlesChannel, Pairs: p, Interval: i}}.ExpandTemplates(e)
assert.NoErrorf(t, err, "ExpandTemplates should not error on interval %s", i)
require.NotEmpty(t, subs)
assert.Equal(t, "candles_"+i.Short(), subs[0].QualifiedChannel)
}
_, err = subscription.List{{Asset: asset.Spot, Channel: subscription.CandlesChannel, Pairs: p, Interval: kline.FourHour}}.ExpandTemplates(g)
_, err = subscription.List{{Asset: asset.Spot, Channel: subscription.CandlesChannel, Pairs: p, Interval: kline.FourHour}}.ExpandTemplates(e)
assert.ErrorIs(t, err, kline.ErrUnsupportedInterval, "ExpandTemplates should error on invalid interval")
assert.PanicsWithError(t,

View File

@@ -58,39 +58,39 @@ var subscriptionNames = map[string]string{
var comms = make(chan websocket.Response)
// WsConnect initiates a websocket connection
func (g *Gemini) WsConnect() error {
func (e *Exchange) WsConnect() error {
ctx := context.TODO()
if !g.Websocket.IsEnabled() || !g.IsEnabled() {
if !e.Websocket.IsEnabled() || !e.IsEnabled() {
return websocket.ErrWebsocketNotEnabled
}
var dialer gws.Dialer
err := g.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
err := e.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
if err != nil {
return err
}
g.Websocket.Wg.Add(2)
go g.wsReadData()
go g.wsFunnelConnectionData(g.Websocket.Conn)
e.Websocket.Wg.Add(2)
go e.wsReadData()
go e.wsFunnelConnectionData(e.Websocket.Conn)
if g.Websocket.CanUseAuthenticatedEndpoints() {
err := g.WsAuth(ctx, &dialer)
if e.Websocket.CanUseAuthenticatedEndpoints() {
err := e.WsAuth(ctx, &dialer)
if err != nil {
log.Errorf(log.ExchangeSys, "%v - websocket authentication failed: %v\n", g.Name, err)
g.Websocket.SetCanUseAuthenticatedEndpoints(false)
log.Errorf(log.ExchangeSys, "%v - websocket authentication failed: %v\n", e.Name, err)
e.Websocket.SetCanUseAuthenticatedEndpoints(false)
}
}
return nil
}
// generateSubscriptions returns a list of subscriptions from the configured subscriptions feature
func (g *Gemini) generateSubscriptions() (subscription.List, error) {
return g.Features.Subscriptions.ExpandTemplates(g)
func (e *Exchange) generateSubscriptions() (subscription.List, error) {
return e.Features.Subscriptions.ExpandTemplates(e)
}
// GetSubscriptionTemplate returns a subscription channel template
func (g *Gemini) GetSubscriptionTemplate(_ *subscription.Subscription) (*template.Template, error) {
func (e *Exchange) GetSubscriptionTemplate(_ *subscription.Subscription) (*template.Template, error) {
return template.New("master.tmpl").Funcs(template.FuncMap{
"channelName": channelName,
"interval": channelInterval,
@@ -98,18 +98,18 @@ func (g *Gemini) GetSubscriptionTemplate(_ *subscription.Subscription) (*templat
}
// Subscribe sends a websocket message to receive data from the channel
func (g *Gemini) Subscribe(subs subscription.List) error {
func (e *Exchange) Subscribe(subs subscription.List) error {
ctx := context.TODO()
return g.manageSubs(ctx, subs, wsSubscribeOp)
return e.manageSubs(ctx, subs, wsSubscribeOp)
}
// Unsubscribe sends a websocket message to stop receiving data from the channel
func (g *Gemini) Unsubscribe(subs subscription.List) error {
func (e *Exchange) Unsubscribe(subs subscription.List) error {
ctx := context.TODO()
return g.manageSubs(ctx, subs, wsUnsubscribeOp)
return e.manageSubs(ctx, subs, wsUnsubscribeOp)
}
func (g *Gemini) manageSubs(ctx context.Context, subs subscription.List, op wsSubOp) error {
func (e *Exchange) manageSubs(ctx context.Context, subs subscription.List, op wsSubOp) error {
req := wsSubscribeRequest{
Type: op,
Subscriptions: make([]wsSubscriptions, 0, len(subs)),
@@ -121,23 +121,23 @@ func (g *Gemini) manageSubs(ctx context.Context, subs subscription.List, op wsSu
})
}
if err := g.Websocket.Conn.SendJSONMessage(ctx, request.Unset, req); err != nil {
if err := e.Websocket.Conn.SendJSONMessage(ctx, request.Unset, req); err != nil {
return err
}
if op == wsUnsubscribeOp {
return g.Websocket.RemoveSubscriptions(g.Websocket.Conn, subs...)
return e.Websocket.RemoveSubscriptions(e.Websocket.Conn, subs...)
}
return g.Websocket.AddSuccessfulSubscriptions(g.Websocket.Conn, subs...)
return e.Websocket.AddSuccessfulSubscriptions(e.Websocket.Conn, subs...)
}
// WsAuth will connect to Gemini's secure endpoint
func (g *Gemini) WsAuth(ctx context.Context, dialer *gws.Dialer) error {
if !g.IsWebsocketAuthenticationSupported() {
return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", g.Name)
func (e *Exchange) WsAuth(ctx context.Context, dialer *gws.Dialer) error {
if !e.IsWebsocketAuthenticationSupported() {
return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", e.Name)
}
creds, err := g.GetCredentials(ctx)
creds, err := e.GetCredentials(ctx)
if err != nil {
return err
}
@@ -147,9 +147,9 @@ func (g *Gemini) WsAuth(ctx context.Context, dialer *gws.Dialer) error {
}
payloadJSON, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("%v sendAuthenticatedHTTPRequest: Unable to JSON request", g.Name)
return fmt.Errorf("%v sendAuthenticatedHTTPRequest: Unable to JSON request", e.Name)
}
wsEndpoint, err := g.API.Endpoints.GetURL(exchange.WebsocketSpot)
wsEndpoint, err := e.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
@@ -168,18 +168,18 @@ func (g *Gemini) WsAuth(ctx context.Context, dialer *gws.Dialer) error {
headers.Add("X-GEMINI-SIGNATURE", hex.EncodeToString(hmac))
headers.Add("Cache-Control", "no-cache")
err = g.Websocket.AuthConn.Dial(ctx, dialer, headers)
err = e.Websocket.AuthConn.Dial(ctx, dialer, headers)
if err != nil {
return fmt.Errorf("%v Websocket connection %v error. Error %v", g.Name, endpoint, err)
return fmt.Errorf("%v Websocket connection %v error. Error %v", e.Name, endpoint, err)
}
g.Websocket.Wg.Add(1)
go g.wsFunnelConnectionData(g.Websocket.AuthConn)
e.Websocket.Wg.Add(1)
go e.wsFunnelConnectionData(e.Websocket.AuthConn)
return nil
}
// wsFunnelConnectionData receives data from multiple connections and passes it to wsReadData
func (g *Gemini) wsFunnelConnectionData(ws websocket.Connection) {
defer g.Websocket.Wg.Done()
func (e *Exchange) wsFunnelConnectionData(ws websocket.Connection) {
defer e.Websocket.Wg.Done()
for {
resp := ws.ReadMessage()
if resp.Raw == nil {
@@ -190,21 +190,21 @@ func (g *Gemini) wsFunnelConnectionData(ws websocket.Connection) {
}
// wsReadData receives and passes on websocket messages for processing
func (g *Gemini) wsReadData() {
defer g.Websocket.Wg.Done()
func (e *Exchange) wsReadData() {
defer e.Websocket.Wg.Done()
for {
select {
case <-g.Websocket.ShutdownC:
case <-e.Websocket.ShutdownC:
select {
case resp := <-comms:
err := g.wsHandleData(resp.Raw)
err := e.wsHandleData(resp.Raw)
if err != nil {
select {
case g.Websocket.DataHandler <- err:
case e.Websocket.DataHandler <- err:
default:
log.Errorf(log.WebsocketMgr,
"%s websocket handle data error: %v",
g.Name,
e.Name,
err)
}
}
@@ -212,15 +212,15 @@ func (g *Gemini) wsReadData() {
}
return
case resp := <-comms:
err := g.wsHandleData(resp.Raw)
err := e.wsHandleData(resp.Raw)
if err != nil {
g.Websocket.DataHandler <- err
e.Websocket.DataHandler <- err
}
}
}
}
func (g *Gemini) wsHandleData(respRaw []byte) error {
func (e *Exchange) wsHandleData(respRaw []byte) error {
// only order details are sent in arrays
if strings.HasPrefix(string(respRaw), "[") {
var result []WsOrderResponse
@@ -232,8 +232,8 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
for i := range result {
oSide, err := order.StringToOrderSide(result[i].Side)
if err != nil {
g.Websocket.DataHandler <- order.ClassificationError{
Exchange: g.Name,
e.Websocket.DataHandler <- order.ClassificationError{
Exchange: e.Name,
OrderID: result[i].OrderID,
Err: err,
}
@@ -241,8 +241,8 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
var oType order.Type
oType, err = stringToOrderType(result[i].OrderType)
if err != nil {
g.Websocket.DataHandler <- order.ClassificationError{
Exchange: g.Name,
e.Websocket.DataHandler <- order.ClassificationError{
Exchange: e.Name,
OrderID: result[i].OrderID,
Err: err,
}
@@ -250,19 +250,19 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
var oStatus order.Status
oStatus, err = stringToOrderStatus(result[i].Type)
if err != nil {
g.Websocket.DataHandler <- order.ClassificationError{
Exchange: g.Name,
e.Websocket.DataHandler <- order.ClassificationError{
Exchange: e.Name,
OrderID: result[i].OrderID,
Err: err,
}
}
enabledPairs, err := g.GetAvailablePairs(asset.Spot)
enabledPairs, err := e.GetAvailablePairs(asset.Spot)
if err != nil {
return err
}
format, err := g.GetPairFormat(asset.Spot, true)
format, err := e.GetPairFormat(asset.Spot, true)
if err != nil {
return err
}
@@ -272,13 +272,13 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
return err
}
g.Websocket.DataHandler <- &order.Detail{
e.Websocket.DataHandler <- &order.Detail{
HiddenOrder: result[i].IsHidden,
Price: result[i].Price,
Amount: result[i].OriginalAmount,
ExecutedAmount: result[i].ExecutedAmount,
RemainingAmount: result[i].RemainingAmount,
Exchange: g.Name,
Exchange: e.Name,
OrderID: result[i].OrderID,
Type: oType,
Side: oSide,
@@ -293,7 +293,7 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
var result map[string]any
err := json.Unmarshal(respRaw, &result)
if err != nil {
return fmt.Errorf("%v Error: %v, Raw: %v", g.Name, err, string(respRaw))
return fmt.Errorf("%v Error: %v, Raw: %v", e.Name, err, string(respRaw))
}
if _, ok := result["type"]; ok {
switch result["type"] {
@@ -303,9 +303,9 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
if err != nil {
return err
}
return g.wsProcessUpdate(l2MarketData)
return e.wsProcessUpdate(l2MarketData)
case "trade":
if !g.IsSaveTradeDataEnabled() {
if !e.IsSaveTradeDataEnabled() {
return nil
}
@@ -317,18 +317,18 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
tSide, err := order.StringToOrderSide(result.Side)
if err != nil {
g.Websocket.DataHandler <- order.ClassificationError{
Exchange: g.Name,
e.Websocket.DataHandler <- order.ClassificationError{
Exchange: e.Name,
Err: err,
}
}
enabledPairs, err := g.GetEnabledPairs(asset.Spot)
enabledPairs, err := e.GetEnabledPairs(asset.Spot)
if err != nil {
return err
}
format, err := g.GetPairFormat(asset.Spot, true)
format, err := e.GetPairFormat(asset.Spot, true)
if err != nil {
return err
}
@@ -342,7 +342,7 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
Timestamp: result.Timestamp.Time(),
CurrencyPair: pair,
AssetType: asset.Spot,
Exchange: g.Name,
Exchange: e.Name,
Price: result.Price,
Amount: result.Quantity,
Side: tSide,
@@ -356,14 +356,14 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
if err != nil {
return err
}
g.Websocket.DataHandler <- result
e.Websocket.DataHandler <- result
case "initial":
var result WsSubscriptionAcknowledgementResponse
err := json.Unmarshal(respRaw, &result)
if err != nil {
return err
}
g.Websocket.DataHandler <- result
e.Websocket.DataHandler <- result
case "heartbeat":
return nil
case "candles_1m_updates",
@@ -378,12 +378,12 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
if err != nil {
return err
}
enabledPairs, err := g.GetEnabledPairs(asset.Spot)
enabledPairs, err := e.GetEnabledPairs(asset.Spot)
if err != nil {
return err
}
format, err := g.GetPairFormat(asset.Spot, true)
format, err := e.GetPairFormat(asset.Spot, true)
if err != nil {
return err
}
@@ -401,11 +401,11 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
if !ok {
return errors.New("unable to type assert interval")
}
g.Websocket.DataHandler <- websocket.KlineData{
e.Websocket.DataHandler <- websocket.KlineData{
Timestamp: time.UnixMilli(int64(candle.Changes[i][0])),
Pair: pair,
AssetType: asset.Spot,
Exchange: g.Name,
Exchange: e.Name,
Interval: interval,
OpenPrice: candle.Changes[i][1],
HighPrice: candle.Changes[i][2],
@@ -415,7 +415,7 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
}
}
default:
g.Websocket.DataHandler <- websocket.UnhandledMessageWarning{Message: g.Name + websocket.UnhandledMessage + string(respRaw)}
e.Websocket.DataHandler <- websocket.UnhandledMessageWarning{Message: e.Name + websocket.UnhandledMessage + string(respRaw)}
return nil
}
} else if r, ok := result["result"].(string); ok {
@@ -427,9 +427,9 @@ func (g *Gemini) wsHandleData(respRaw []byte) error {
}
return errors.New(reason)
}
return fmt.Errorf("%v Unhandled websocket error %s", g.Name, respRaw)
return fmt.Errorf("%v Unhandled websocket error %s", e.Name, respRaw)
default:
g.Websocket.DataHandler <- websocket.UnhandledMessageWarning{Message: g.Name + websocket.UnhandledMessage + string(respRaw)}
e.Websocket.DataHandler <- websocket.UnhandledMessageWarning{Message: e.Name + websocket.UnhandledMessage + string(respRaw)}
return nil
}
}
@@ -468,14 +468,14 @@ func stringToOrderType(oType string) (order.Type, error) {
}
}
func (g *Gemini) wsProcessUpdate(result *wsL2MarketData) error {
func (e *Exchange) wsProcessUpdate(result *wsL2MarketData) error {
isInitial := len(result.Changes) > 0 && len(result.Trades) > 0
enabledPairs, err := g.GetEnabledPairs(asset.Spot)
enabledPairs, err := e.GetEnabledPairs(asset.Spot)
if err != nil {
return err
}
format, err := g.GetPairFormat(asset.Spot, true)
format, err := e.GetPairFormat(asset.Spot, true)
if err != nil {
return err
}
@@ -514,10 +514,10 @@ func (g *Gemini) wsProcessUpdate(result *wsL2MarketData) error {
newOrderBook.Bids = bids
newOrderBook.Asset = asset.Spot
newOrderBook.Pair = pair
newOrderBook.Exchange = g.Name
newOrderBook.ValidateOrderbook = g.ValidateOrderbook
newOrderBook.Exchange = e.Name
newOrderBook.ValidateOrderbook = e.ValidateOrderbook
newOrderBook.LastUpdated = time.Now() // No time is sent
err := g.Websocket.Orderbook.LoadSnapshot(&newOrderBook)
err := e.Websocket.Orderbook.LoadSnapshot(&newOrderBook)
if err != nil {
return err
}
@@ -525,7 +525,7 @@ func (g *Gemini) wsProcessUpdate(result *wsL2MarketData) error {
if len(asks) == 0 && len(bids) == 0 {
return nil
}
err := g.Websocket.Orderbook.Update(&orderbook.Update{
err := e.Websocket.Orderbook.Update(&orderbook.Update{
Asks: asks,
Bids: bids,
Pair: pair,
@@ -538,10 +538,10 @@ func (g *Gemini) wsProcessUpdate(result *wsL2MarketData) error {
}
if len(result.AuctionEvents) > 0 {
g.Websocket.DataHandler <- result.AuctionEvents
e.Websocket.DataHandler <- result.AuctionEvents
}
if !g.IsSaveTradeDataEnabled() {
if !e.IsSaveTradeDataEnabled() {
return nil
}
@@ -549,8 +549,8 @@ func (g *Gemini) wsProcessUpdate(result *wsL2MarketData) error {
for x := range result.Trades {
tSide, err := order.StringToOrderSide(result.Trades[x].Side)
if err != nil {
g.Websocket.DataHandler <- order.ClassificationError{
Exchange: g.Name,
e.Websocket.DataHandler <- order.ClassificationError{
Exchange: e.Name,
Err: err,
}
}
@@ -558,7 +558,7 @@ func (g *Gemini) wsProcessUpdate(result *wsL2MarketData) error {
Timestamp: result.Trades[x].Timestamp.Time(),
CurrencyPair: pair,
AssetType: asset.Spot,
Exchange: g.Name,
Exchange: e.Name,
Price: result.Trades[x].Price,
Amount: result.Trades[x].Quantity,
Side: tSide,

View File

@@ -32,12 +32,12 @@ import (
)
// SetDefaults sets package defaults for gemini exchange
func (g *Gemini) SetDefaults() {
g.Name = "Gemini"
g.Enabled = true
g.Verbose = true
g.API.CredentialsValidator.RequiresKey = true
g.API.CredentialsValidator.RequiresSecret = true
func (e *Exchange) SetDefaults() {
e.Name = "Gemini"
e.Enabled = true
e.Verbose = true
e.API.CredentialsValidator.RequiresKey = true
e.API.CredentialsValidator.RequiresSecret = true
requestFmt := &currency.PairFormat{
Uppercase: true,
@@ -47,12 +47,12 @@ func (g *Gemini) SetDefaults() {
Uppercase: true,
Delimiter: currency.DashDelimiter,
}
err := g.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
err := e.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
g.Features = exchange.Features{
e.Features = exchange.Features{
Supports: exchange.FeaturesSupported{
REST: true,
Websocket: true,
@@ -92,68 +92,68 @@ func (g *Gemini) SetDefaults() {
Subscriptions: defaultSubscriptions.Clone(),
}
g.Requester, err = request.New(g.Name,
e.Requester, err = request.New(e.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(GetRateLimit()))
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
g.API.Endpoints = g.NewEndpoints()
err = g.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
e.API.Endpoints = e.NewEndpoints()
err = e.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: geminiAPIURL,
exchange.WebsocketSpot: geminiWebsocketEndpoint,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
g.Websocket = websocket.NewManager()
g.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
g.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
g.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
e.Websocket = websocket.NewManager()
e.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
e.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
e.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
}
// Setup sets exchange configuration parameters
func (g *Gemini) Setup(exch *config.Exchange) error {
func (e *Exchange) Setup(exch *config.Exchange) error {
err := exch.Validate()
if err != nil {
return err
}
if !exch.Enabled {
g.SetEnabled(false)
e.SetEnabled(false)
return nil
}
err = g.SetupDefaults(exch)
err = e.SetupDefaults(exch)
if err != nil {
return err
}
if exch.UseSandbox {
err = g.API.Endpoints.SetRunningURL(exchange.RestSpot.String(), geminiSandboxAPIURL)
err = e.API.Endpoints.SetRunningURL(exchange.RestSpot.String(), geminiSandboxAPIURL)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
}
wsRunningURL, err := g.API.Endpoints.GetURL(exchange.WebsocketSpot)
wsRunningURL, err := e.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = g.Websocket.Setup(&websocket.ManagerSetup{
err = e.Websocket.Setup(&websocket.ManagerSetup{
ExchangeConfig: exch,
DefaultURL: geminiWebsocketEndpoint,
RunningURL: wsRunningURL,
Connector: g.WsConnect,
Subscriber: g.Subscribe,
Unsubscriber: g.Unsubscribe,
GenerateSubscriptions: g.generateSubscriptions,
Features: &g.Features.Supports.WebsocketCapabilities,
Connector: e.WsConnect,
Subscriber: e.Subscribe,
Unsubscriber: e.Unsubscribe,
GenerateSubscriptions: e.generateSubscriptions,
Features: &e.Features.Supports.WebsocketCapabilities,
})
if err != nil {
return err
}
err = g.Websocket.SetupNewConnection(&websocket.ConnectionSetup{
err = e.Websocket.SetupNewConnection(&websocket.ConnectionSetup{
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
URL: geminiWebsocketEndpoint + "/v2/" + geminiWsMarketData,
@@ -162,7 +162,7 @@ func (g *Gemini) Setup(exch *config.Exchange) error {
return err
}
return g.Websocket.SetupNewConnection(&websocket.ConnectionSetup{
return e.Websocket.SetupNewConnection(&websocket.ConnectionSetup{
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
URL: geminiWebsocketEndpoint + "/v1/" + geminiWsOrderEvents,
@@ -171,12 +171,12 @@ func (g *Gemini) Setup(exch *config.Exchange) error {
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func (g *Gemini) FetchTradablePairs(ctx context.Context, a asset.Item) (currency.Pairs, error) {
if !g.SupportsAsset(a) {
func (e *Exchange) FetchTradablePairs(ctx context.Context, a asset.Item) (currency.Pairs, error) {
if !e.SupportsAsset(a) {
return nil, asset.ErrNotSupported
}
details, err := g.GetSymbolDetails(ctx, "all")
details, err := e.GetSymbolDetails(ctx, "all")
if err != nil {
return nil, err
}
@@ -202,24 +202,24 @@ func (g *Gemini) FetchTradablePairs(ctx context.Context, a asset.Item) (currency
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (g *Gemini) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error {
pairs, err := g.FetchTradablePairs(ctx, asset.Spot)
func (e *Exchange) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error {
pairs, err := e.FetchTradablePairs(ctx, asset.Spot)
if err != nil {
return err
}
err = g.UpdatePairs(pairs, asset.Spot, false, forceUpdate)
err = e.UpdatePairs(pairs, asset.Spot, false, forceUpdate)
if err != nil {
return err
}
return g.EnsureOnePairEnabled()
return e.EnsureOnePairEnabled()
}
// UpdateAccountInfo Retrieves balances for all enabled currencies for the
// Gemini exchange
func (g *Gemini) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) {
func (e *Exchange) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) {
var response account.Holdings
response.Exchange = g.Name
accountBalance, err := g.GetBalances(ctx)
response.Exchange = e.Name
accountBalance, err := e.GetBalances(ctx)
if err != nil {
return response, err
}
@@ -239,7 +239,7 @@ func (g *Gemini) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a
Currencies: currencies,
})
creds, err := g.GetCredentials(ctx)
creds, err := e.GetCredentials(ctx)
if err != nil {
return account.Holdings{}, err
}
@@ -252,18 +252,18 @@ func (g *Gemini) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a
}
// UpdateTickers updates the ticker for all currency pairs of a given asset type
func (g *Gemini) UpdateTickers(_ context.Context, _ asset.Item) error {
func (e *Exchange) UpdateTickers(_ context.Context, _ asset.Item) error {
return common.ErrFunctionNotSupported
}
// UpdateTicker updates and returns the ticker for a currency pair
func (g *Gemini) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) {
fPair, err := g.FormatExchangeCurrency(p, a)
func (e *Exchange) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) {
fPair, err := e.FormatExchangeCurrency(p, a)
if err != nil {
return nil, err
}
tick, err := g.GetTicker(ctx, fPair.String())
tick, err := e.GetTicker(ctx, fPair.String())
if err != nil {
return nil, err
}
@@ -276,36 +276,36 @@ func (g *Gemini) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item
Open: tick.Open,
Close: tick.Close,
Pair: fPair,
ExchangeName: g.Name,
ExchangeName: e.Name,
AssetType: a,
})
if err != nil {
return nil, err
}
return ticker.GetTicker(g.Name, fPair, a)
return ticker.GetTicker(e.Name, fPair, a)
}
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (g *Gemini) UpdateOrderbook(ctx context.Context, p currency.Pair, assetType asset.Item) (*orderbook.Book, error) {
func (e *Exchange) UpdateOrderbook(ctx context.Context, p currency.Pair, assetType asset.Item) (*orderbook.Book, error) {
if p.IsEmpty() {
return nil, currency.ErrCurrencyPairEmpty
}
if err := g.CurrencyPairs.IsAssetEnabled(assetType); err != nil {
if err := e.CurrencyPairs.IsAssetEnabled(assetType); err != nil {
return nil, err
}
book := &orderbook.Book{
Exchange: g.Name,
Exchange: e.Name,
Pair: p,
Asset: assetType,
ValidateOrderbook: g.ValidateOrderbook,
ValidateOrderbook: e.ValidateOrderbook,
}
fPair, err := g.FormatExchangeCurrency(p, assetType)
fPair, err := e.FormatExchangeCurrency(p, assetType)
if err != nil {
return book, err
}
orderbookNew, err := g.GetOrderbook(ctx, fPair.String(), url.Values{})
orderbookNew, err := e.GetOrderbook(ctx, fPair.String(), url.Values{})
if err != nil {
return book, err
}
@@ -329,13 +329,13 @@ func (g *Gemini) UpdateOrderbook(ctx context.Context, p currency.Pair, assetType
if err != nil {
return book, err
}
return orderbook.Get(g.Name, fPair, assetType)
return orderbook.Get(e.Name, fPair, assetType)
}
// GetAccountFundingHistory returns funding history, deposits and
// withdrawals
func (g *Gemini) GetAccountFundingHistory(ctx context.Context) ([]exchange.FundingHistory, error) {
transfers, err := g.Transfers(ctx, currency.EMPTYCODE, time.Time{}, 50, "", false)
func (e *Exchange) GetAccountFundingHistory(ctx context.Context) ([]exchange.FundingHistory, error) {
transfers, err := e.Transfers(ctx, currency.EMPTYCODE, time.Time{}, 50, "", false)
if err != nil {
return nil, err
}
@@ -357,11 +357,11 @@ func (g *Gemini) GetAccountFundingHistory(ctx context.Context) ([]exchange.Fundi
}
// GetWithdrawalsHistory returns previous withdrawals data
func (g *Gemini) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) ([]exchange.WithdrawalHistory, error) {
if err := g.CurrencyPairs.IsAssetEnabled(a); err != nil {
func (e *Exchange) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) ([]exchange.WithdrawalHistory, error) {
if err := e.CurrencyPairs.IsAssetEnabled(a); err != nil {
return nil, err
}
transfers, err := g.Transfers(ctx, c, time.Time{}, 50, "", false)
transfers, err := e.Transfers(ctx, c, time.Time{}, 50, "", false)
if err != nil {
return nil, err
}
@@ -386,17 +386,17 @@ func (g *Gemini) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a a
}
// GetRecentTrades returns the most recent trades for a currency and asset
func (g *Gemini) GetRecentTrades(ctx context.Context, pair currency.Pair, assetType asset.Item) ([]trade.Data, error) {
return g.GetHistoricTrades(ctx, pair, assetType, time.Time{}, time.Time{})
func (e *Exchange) GetRecentTrades(ctx context.Context, pair currency.Pair, assetType asset.Item) ([]trade.Data, error) {
return e.GetHistoricTrades(ctx, pair, assetType, time.Time{}, time.Time{})
}
// GetHistoricTrades returns historic trade data within the timeframe provided
func (g *Gemini) GetHistoricTrades(ctx context.Context, p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]trade.Data, error) {
func (e *Exchange) GetHistoricTrades(ctx context.Context, p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]trade.Data, error) {
if err := common.StartEndTimeCheck(timestampStart, timestampEnd); err != nil && !errors.Is(err, common.ErrDateUnset) {
return nil, fmt.Errorf("invalid time range supplied. Start: %v End %v %w", timestampStart, timestampEnd, err)
}
var err error
p, err = g.FormatExchangeCurrency(p, assetType)
p, err = e.FormatExchangeCurrency(p, assetType)
if err != nil {
return nil, err
}
@@ -406,7 +406,7 @@ func (g *Gemini) GetHistoricTrades(ctx context.Context, p currency.Pair, assetTy
allTrades:
for {
var tradeData []Trade
tradeData, err = g.GetTrades(ctx, p.String(), ts.Unix(), int64(limit), false)
tradeData, err = e.GetTrades(ctx, p.String(), ts.Unix(), int64(limit), false)
if err != nil {
return nil, err
}
@@ -422,7 +422,7 @@ allTrades:
return nil, err
}
resp = append(resp, trade.Data{
Exchange: g.Name,
Exchange: e.Name,
TID: strconv.FormatInt(tradeData[i].TID, 10),
CurrencyPair: p,
AssetType: assetType,
@@ -443,7 +443,7 @@ allTrades:
}
}
err = g.AddTradesToBuffer(resp...)
err = e.AddTradesToBuffer(resp...)
if err != nil {
return nil, err
}
@@ -454,8 +454,8 @@ allTrades:
}
// SubmitOrder submits a new order
func (g *Gemini) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) {
if err := s.Validate(g.GetTradingRequirements()); err != nil {
func (e *Exchange) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) {
if err := s.Validate(e.GetTradingRequirements()); err != nil {
return nil, err
}
@@ -463,12 +463,12 @@ func (g *Gemini) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Submi
return nil, errors.New("only limit orders are enabled through this exchange")
}
fPair, err := g.FormatExchangeCurrency(s.Pair, asset.Spot)
fPair, err := e.FormatExchangeCurrency(s.Pair, asset.Spot)
if err != nil {
return nil, err
}
response, err := g.NewOrder(ctx,
response, err := e.NewOrder(ctx,
fPair.String(),
s.Amount,
s.Price,
@@ -483,12 +483,12 @@ func (g *Gemini) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Submi
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (g *Gemini) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
func (e *Exchange) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}
// CancelOrder cancels an order by its corresponding ID number
func (g *Gemini) CancelOrder(ctx context.Context, o *order.Cancel) error {
func (e *Exchange) CancelOrder(ctx context.Context, o *order.Cancel) error {
if err := o.Validate(o.StandardCancel()); err != nil {
return err
}
@@ -498,26 +498,26 @@ func (g *Gemini) CancelOrder(ctx context.Context, o *order.Cancel) error {
return err
}
_, err = g.CancelExistingOrder(ctx, orderIDInt)
_, err = e.CancelExistingOrder(ctx, orderIDInt)
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (g *Gemini) CancelBatchOrders(_ context.Context, _ []order.Cancel) (*order.CancelBatchResponse, error) {
func (e *Exchange) CancelBatchOrders(_ context.Context, _ []order.Cancel) (*order.CancelBatchResponse, error) {
return nil, common.ErrFunctionNotSupported
}
// GetServerTime returns the current exchange server time.
func (g *Gemini) GetServerTime(_ context.Context, _ asset.Item) (time.Time, error) {
func (e *Exchange) GetServerTime(_ context.Context, _ asset.Item) (time.Time, error) {
return time.Time{}, common.ErrFunctionNotSupported
}
// CancelAllOrders cancels all orders associated with a currency pair
func (g *Gemini) CancelAllOrders(ctx context.Context, _ *order.Cancel) (order.CancelAllResponse, error) {
func (e *Exchange) CancelAllOrders(ctx context.Context, _ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{
Status: make(map[string]string),
}
resp, err := g.CancelExistingOrders(ctx, false)
resp, err := e.CancelExistingOrders(ctx, false)
if err != nil {
return cancelAllOrdersResponse, err
}
@@ -530,12 +530,12 @@ func (g *Gemini) CancelAllOrders(ctx context.Context, _ *order.Cancel) (order.Ca
}
// GetOrderInfo returns order information based on order ID
func (g *Gemini) GetOrderInfo(ctx context.Context, orderID string, _ currency.Pair, _ asset.Item) (*order.Detail, error) {
func (e *Exchange) GetOrderInfo(ctx context.Context, orderID string, _ currency.Pair, _ asset.Item) (*order.Detail, error) {
iOID, err := strconv.ParseInt(orderID, 10, 64)
if err != nil {
return nil, err
}
resp, err := g.GetOrderStatus(ctx, iOID)
resp, err := e.GetOrderStatus(ctx, iOID)
if err != nil {
return nil, err
}
@@ -575,8 +575,8 @@ func (g *Gemini) GetOrderInfo(ctx context.Context, orderID string, _ currency.Pa
}
// GetDepositAddress returns a deposit address for a specified currency
func (g *Gemini) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _, _ string) (*deposit.Address, error) {
addr, err := g.GetCryptoDepositAddress(ctx, "", cryptocurrency.String())
func (e *Exchange) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _, _ string) (*deposit.Address, error) {
addr, err := e.GetCryptoDepositAddress(ctx, "", cryptocurrency.String())
if err != nil {
return nil, err
}
@@ -585,11 +585,11 @@ func (g *Gemini) GetDepositAddress(ctx context.Context, cryptocurrency currency.
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
// submitted
func (g *Gemini) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
func (e *Exchange) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
if err := withdrawRequest.Validate(); err != nil {
return nil, err
}
resp, err := g.WithdrawCrypto(ctx,
resp, err := e.WithdrawCrypto(ctx,
withdrawRequest.Crypto.Address,
withdrawRequest.Currency.String(),
withdrawRequest.Amount)
@@ -607,46 +607,46 @@ func (g *Gemini) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawReques
// WithdrawFiatFunds returns a withdrawal ID when a
// withdrawal is submitted
func (g *Gemini) WithdrawFiatFunds(_ context.Context, _ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
func (e *Exchange) WithdrawFiatFunds(_ context.Context, _ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
return nil, common.ErrFunctionNotSupported
}
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
// withdrawal is submitted
func (g *Gemini) WithdrawFiatFundsToInternationalBank(_ context.Context, _ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
func (e *Exchange) WithdrawFiatFundsToInternationalBank(_ context.Context, _ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
return nil, common.ErrFunctionNotSupported
}
// GetFeeByType returns an estimate of fee based on type of transaction
func (g *Gemini) GetFeeByType(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) {
func (e *Exchange) GetFeeByType(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) {
if feeBuilder == nil {
return 0, fmt.Errorf("%T %w", feeBuilder, common.ErrNilPointer)
}
if (!g.AreCredentialsValid(ctx) || g.SkipAuthCheck) && // Todo check connection status
if (!e.AreCredentialsValid(ctx) || e.SkipAuthCheck) && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}
return g.GetFee(ctx, feeBuilder)
return e.GetFee(ctx, feeBuilder)
}
// GetActiveOrders retrieves any orders that are active/open
func (g *Gemini) GetActiveOrders(ctx context.Context, req *order.MultiOrderRequest) (order.FilteredOrders, error) {
func (e *Exchange) GetActiveOrders(ctx context.Context, req *order.MultiOrderRequest) (order.FilteredOrders, error) {
err := req.Validate()
if err != nil {
return nil, err
}
resp, err := g.GetOrders(ctx)
resp, err := e.GetOrders(ctx)
if err != nil {
return nil, err
}
availPairs, err := g.GetAvailablePairs(asset.Spot)
availPairs, err := e.GetAvailablePairs(asset.Spot)
if err != nil {
return nil, err
}
format, err := g.GetPairFormat(asset.Spot, true)
format, err := e.GetPairFormat(asset.Spot, true)
if err != nil {
return nil, err
}
@@ -680,7 +680,7 @@ func (g *Gemini) GetActiveOrders(ctx context.Context, req *order.MultiOrderReque
RemainingAmount: resp[i].RemainingAmount,
OrderID: strconv.FormatInt(resp[i].OrderID, 10),
ExecutedAmount: resp[i].ExecutedAmount,
Exchange: g.Name,
Exchange: e.Name,
Type: orderType,
Side: side,
Price: resp[i].Price,
@@ -688,12 +688,12 @@ func (g *Gemini) GetActiveOrders(ctx context.Context, req *order.MultiOrderReque
Date: resp[i].Timestamp.Time(),
}
}
return req.Filter(g.Name, orders), nil
return req.Filter(e.Name, orders), nil
}
// GetOrderHistory retrieves account order information
// Can Limit response to specific order status
func (g *Gemini) GetOrderHistory(ctx context.Context, req *order.MultiOrderRequest) (order.FilteredOrders, error) {
func (e *Exchange) GetOrderHistory(ctx context.Context, req *order.MultiOrderRequest) (order.FilteredOrders, error) {
err := req.Validate()
if err != nil {
return nil, err
@@ -706,13 +706,13 @@ func (g *Gemini) GetOrderHistory(ctx context.Context, req *order.MultiOrderReque
var trades []TradeHistory
for j := range req.Pairs {
var fPair currency.Pair
fPair, err = g.FormatExchangeCurrency(req.Pairs[j], asset.Spot)
fPair, err = e.FormatExchangeCurrency(req.Pairs[j], asset.Spot)
if err != nil {
return nil, err
}
var resp []TradeHistory
resp, err = g.GetTradeHistory(ctx, fPair.String(), req.StartTime.Unix())
resp, err = e.GetTradeHistory(ctx, fPair.String(), req.StartTime.Unix())
if err != nil {
return nil, err
}
@@ -724,7 +724,7 @@ func (g *Gemini) GetOrderHistory(ctx context.Context, req *order.MultiOrderReque
}
}
format, err := g.GetPairFormat(asset.Spot, false)
format, err := e.GetPairFormat(asset.Spot, false)
if err != nil {
return nil, err
}
@@ -740,7 +740,7 @@ func (g *Gemini) GetOrderHistory(ctx context.Context, req *order.MultiOrderReque
OrderID: strconv.FormatInt(trades[i].OrderID, 10),
Amount: trades[i].Amount,
ExecutedAmount: trades[i].Amount,
Exchange: g.Name,
Exchange: e.Name,
Date: trades[i].Timestamp.Time(),
Side: side,
Fee: trades[i].FeeAmount,
@@ -755,37 +755,37 @@ func (g *Gemini) GetOrderHistory(ctx context.Context, req *order.MultiOrderReque
detail.InferCostsAndTimes()
orders[i] = detail
}
return req.Filter(g.Name, orders), nil
return req.Filter(e.Name, orders), nil
}
// ValidateAPICredentials validates current credentials used for wrapper
// functionality
func (g *Gemini) ValidateAPICredentials(ctx context.Context, assetType asset.Item) error {
_, err := g.UpdateAccountInfo(ctx, assetType)
return g.CheckTransientError(err)
func (e *Exchange) ValidateAPICredentials(ctx context.Context, assetType asset.Item) error {
_, err := e.UpdateAccountInfo(ctx, assetType)
return e.CheckTransientError(err)
}
// GetHistoricCandles returns candles between a time period for a set time interval
func (g *Gemini) GetHistoricCandles(_ context.Context, _ currency.Pair, _ asset.Item, _ kline.Interval, _, _ time.Time) (*kline.Item, error) {
func (e *Exchange) GetHistoricCandles(_ context.Context, _ currency.Pair, _ asset.Item, _ kline.Interval, _, _ time.Time) (*kline.Item, error) {
return nil, common.ErrFunctionNotSupported
}
// GetHistoricCandlesExtended returns candles between a time period for a set time interval
func (g *Gemini) GetHistoricCandlesExtended(_ context.Context, _ currency.Pair, _ asset.Item, _ kline.Interval, _, _ time.Time) (*kline.Item, error) {
func (e *Exchange) GetHistoricCandlesExtended(_ context.Context, _ currency.Pair, _ asset.Item, _ kline.Interval, _, _ time.Time) (*kline.Item, error) {
return nil, common.ErrFunctionNotSupported
}
// GetFuturesContractDetails returns all contracts from the exchange by asset type
func (g *Gemini) GetFuturesContractDetails(context.Context, asset.Item) ([]futures.Contract, error) {
func (e *Exchange) GetFuturesContractDetails(context.Context, asset.Item) ([]futures.Contract, error) {
return nil, common.ErrFunctionNotSupported
}
// UpdateOrderExecutionLimits sets exchange executions for a required asset type
func (g *Gemini) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error {
func (e *Exchange) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error {
if a != asset.Spot {
return fmt.Errorf("%w %v", asset.ErrNotSupported, a)
}
details, err := g.GetSymbolDetails(ctx, "all")
details, err := e.GetSymbolDetails(ctx, "all")
if err != nil {
return fmt.Errorf("cannot update exchange execution limits: %w", err)
}
@@ -807,17 +807,17 @@ func (g *Gemini) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) e
QuoteStepIncrementSize: details[i].QuoteIncrement,
})
}
return g.LoadLimits(resp)
return e.LoadLimits(resp)
}
// GetLatestFundingRates returns the latest funding rates data
func (g *Gemini) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
func (e *Exchange) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
return nil, common.ErrFunctionNotSupported
}
// GetCurrencyTradeURL returns the URL to the exchange's trade page for the given asset and currency pair
func (g *Gemini) GetCurrencyTradeURL(_ context.Context, a asset.Item, cp currency.Pair) (string, error) {
_, err := g.CurrencyPairs.IsPairEnabled(cp, a)
func (e *Exchange) GetCurrencyTradeURL(_ context.Context, a asset.Item, cp currency.Pair) (string, error) {
_, err := e.CurrencyPairs.IsPairEnabled(cp, a)
if err != nil {
return "", err
}