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

@@ -60,18 +60,18 @@ const (
bitstampRequestRate = 8000
)
// Bitstamp is the overarching type across the bitstamp package
type Bitstamp struct {
// Exchange implements exchange.IBotExchange and contains additional specific api methods for interacting with Bitstamp
type Exchange struct {
exchange.Base
}
// GetFee returns an estimate of fee based on type of transaction
func (b *Bitstamp) 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:
tradingFee, err := b.getTradingFee(ctx, feeBuilder)
tradingFee, err := e.getTradingFee(ctx, feeBuilder)
if err != nil {
return 0, fmt.Errorf("error getting trading fee: %w", err)
}
@@ -92,8 +92,8 @@ func (b *Bitstamp) GetFee(ctx context.Context, feeBuilder *exchange.FeeBuilder)
}
// GetTradingFee returns a trading fee based on a currency
func (b *Bitstamp) getTradingFee(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) {
tradingFees, err := b.GetAccountTradingFee(ctx, feeBuilder.Pair)
func (e *Exchange) getTradingFee(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) {
tradingFees, err := e.GetAccountTradingFee(ctx, feeBuilder.Pair)
if err != nil {
return 0, err
}
@@ -106,23 +106,23 @@ func (b *Bitstamp) getTradingFee(ctx context.Context, feeBuilder *exchange.FeeBu
}
// GetAccountTradingFee returns a TradingFee for a pair
func (b *Bitstamp) GetAccountTradingFee(ctx context.Context, pair currency.Pair) (TradingFees, error) {
func (e *Exchange) GetAccountTradingFee(ctx context.Context, pair currency.Pair) (TradingFees, error) {
path := bitstampAPITradingFees + "/" + strings.ToLower(pair.String())
var resp TradingFees
if pair.IsEmpty() {
return resp, currency.ErrCurrencyPairEmpty
}
err := b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, nil, &resp)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, nil, &resp)
return resp, err
}
// GetAccountTradingFees returns a slice of TradingFee
func (b *Bitstamp) GetAccountTradingFees(ctx context.Context) ([]TradingFees, error) {
func (e *Exchange) GetAccountTradingFees(ctx context.Context) ([]TradingFees, error) {
path := bitstampAPITradingFees
var resp []TradingFees
err := b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, nil, &resp)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, nil, &resp)
return resp, err
}
@@ -155,7 +155,7 @@ func getInternationalBankDepositFee(amount float64) float64 {
}
// GetTicker returns ticker information
func (b *Bitstamp) GetTicker(ctx context.Context, currency string, hourly bool) (*Ticker, error) {
func (e *Exchange) GetTicker(ctx context.Context, currency string, hourly bool) (*Ticker, error) {
response := Ticker{}
tickerEndpoint := bitstampAPITicker
@@ -163,13 +163,13 @@ func (b *Bitstamp) GetTicker(ctx context.Context, currency string, hourly bool)
tickerEndpoint = bitstampAPITickerHourly
}
path := "/v" + bitstampAPIVersion + "/" + tickerEndpoint + "/" + strings.ToLower(currency) + "/"
return &response, b.SendHTTPRequest(ctx, exchange.RestSpot, path, &response)
return &response, e.SendHTTPRequest(ctx, exchange.RestSpot, path, &response)
}
// GetOrderbook Returns a JSON dictionary with "bids" and "asks". Each is a list
// of open orders and each order is represented as a list holding the price and
// the amount.
func (b *Bitstamp) GetOrderbook(ctx context.Context, currency string) (*Orderbook, error) {
func (e *Exchange) GetOrderbook(ctx context.Context, currency string) (*Orderbook, error) {
type response struct {
Timestamp types.Time `json:"timestamp"`
Bids [][2]types.Number `json:"bids"`
@@ -178,7 +178,7 @@ func (b *Bitstamp) GetOrderbook(ctx context.Context, currency string) (*Orderboo
path := "/v" + bitstampAPIVersion + "/" + bitstampAPIOrderbook + "/" + strings.ToLower(currency) + "/"
var resp response
err := b.SendHTTPRequest(ctx, exchange.RestSpot, path, &resp)
err := e.SendHTTPRequest(ctx, exchange.RestSpot, path, &resp)
if err != nil {
return nil, err
}
@@ -204,35 +204,35 @@ func (b *Bitstamp) GetOrderbook(ctx context.Context, currency string) (*Orderboo
// GetTradingPairs returns a list of trading pairs which Bitstamp
// currently supports
func (b *Bitstamp) GetTradingPairs(ctx context.Context) ([]TradingPair, error) {
func (e *Exchange) GetTradingPairs(ctx context.Context) ([]TradingPair, error) {
var result []TradingPair
path := "/v" + bitstampAPIVersion + "/" + bitstampAPITradingPairsInfo
return result, b.SendHTTPRequest(ctx, exchange.RestSpot, path, &result)
return result, e.SendHTTPRequest(ctx, exchange.RestSpot, path, &result)
}
// GetTransactions returns transaction information
// value parameter ["time"] = "minute", "hour", "day" will collate your
// response into time intervals.
func (b *Bitstamp) GetTransactions(ctx context.Context, currencyPair, timePeriod string) ([]Transactions, error) {
func (e *Exchange) GetTransactions(ctx context.Context, currencyPair, timePeriod string) ([]Transactions, error) {
var transactions []Transactions
requestURL := "/v" + bitstampAPIVersion + "/" + bitstampAPITransactions + "/" + strings.ToLower(currencyPair) + "/"
if timePeriod != "" {
requestURL += "?time=" + url.QueryEscape(timePeriod)
}
return transactions, b.SendHTTPRequest(ctx, exchange.RestSpot, requestURL, &transactions)
return transactions, e.SendHTTPRequest(ctx, exchange.RestSpot, requestURL, &transactions)
}
// GetEURUSDConversionRate returns the conversion rate between Euro and USD
func (b *Bitstamp) GetEURUSDConversionRate(ctx context.Context) (EURUSDConversionRate, error) {
func (e *Exchange) GetEURUSDConversionRate(ctx context.Context) (EURUSDConversionRate, error) {
rate := EURUSDConversionRate{}
path := "/" + bitstampAPIEURUSD
return rate, b.SendHTTPRequest(ctx, exchange.RestSpot, path, &rate)
return rate, e.SendHTTPRequest(ctx, exchange.RestSpot, path, &rate)
}
// GetBalance returns full balance of currency held on the exchange
func (b *Bitstamp) GetBalance(ctx context.Context) (Balances, error) {
func (e *Exchange) GetBalance(ctx context.Context) (Balances, error) {
var balance map[string]types.Number
err := b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIBalance, true, nil, &balance)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIBalance, true, nil, &balance)
if err != nil {
return nil, err
}
@@ -258,41 +258,41 @@ func (b *Bitstamp) GetBalance(ctx context.Context) (Balances, error) {
}
// GetUserTransactions returns an array of transactions
func (b *Bitstamp) GetUserTransactions(ctx context.Context, currencyPair string) ([]UserTransactions, error) {
func (e *Exchange) GetUserTransactions(ctx context.Context, currencyPair string) ([]UserTransactions, error) {
var resp []UserTransactions
var err error
if currencyPair == "" {
err = b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIUserTransactions, true, url.Values{}, &resp)
err = e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIUserTransactions, true, url.Values{}, &resp)
} else {
err = b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIUserTransactions+"/"+currencyPair, true, url.Values{}, &resp)
err = e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIUserTransactions+"/"+currencyPair, true, url.Values{}, &resp)
}
return resp, err
}
// GetOpenOrders returns all open orders on the exchange
func (b *Bitstamp) GetOpenOrders(ctx context.Context, currencyPair string) ([]Order, error) {
func (e *Exchange) GetOpenOrders(ctx context.Context, currencyPair string) ([]Order, error) {
var resp []Order
path := bitstampAPIOpenOrders + "/" + strings.ToLower(currencyPair)
return resp, b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, nil, &resp)
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, nil, &resp)
}
// GetOrderStatus returns an the status of an order by its ID
func (b *Bitstamp) GetOrderStatus(ctx context.Context, orderID int64) (OrderStatus, error) {
func (e *Exchange) GetOrderStatus(ctx context.Context, orderID int64) (OrderStatus, error) {
resp := OrderStatus{}
req := url.Values{}
req.Add("id", strconv.FormatInt(orderID, 10))
return resp,
b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIOrderStatus, false, req, &resp)
e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIOrderStatus, false, req, &resp)
}
// CancelExistingOrder cancels order by ID
func (b *Bitstamp) CancelExistingOrder(ctx context.Context, orderID int64) (CancelOrder, error) {
func (e *Exchange) CancelExistingOrder(ctx context.Context, orderID int64) (CancelOrder, error) {
req := url.Values{}
req.Add("id", strconv.FormatInt(orderID, 10))
var result CancelOrder
err := b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPICancelOrder, true, req, &result)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPICancelOrder, true, req, &result)
if err != nil {
return result, err
}
@@ -301,15 +301,15 @@ func (b *Bitstamp) CancelExistingOrder(ctx context.Context, orderID int64) (Canc
}
// CancelAllExistingOrders cancels all open orders on the exchange
func (b *Bitstamp) CancelAllExistingOrders(ctx context.Context) (bool, error) {
func (e *Exchange) CancelAllExistingOrders(ctx context.Context) (bool, error) {
result := false
return result,
b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPICancelAllOrders, false, nil, &result)
e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPICancelAllOrders, false, nil, &result)
}
// PlaceOrder places an order on the exchange.
func (b *Bitstamp) PlaceOrder(ctx context.Context, currencyPair string, price, amount float64, buy, market bool) (Order, error) {
func (e *Exchange) PlaceOrder(ctx context.Context, currencyPair string, price, amount float64, buy, market bool) (Order, error) {
req := url.Values{}
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
req.Add("price", strconv.FormatFloat(price, 'f', -1, 64))
@@ -328,13 +328,13 @@ func (b *Bitstamp) PlaceOrder(ctx context.Context, currencyPair string, price, a
}
return response,
b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, req, &response)
e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, req, &response)
}
// GetWithdrawalRequests returns withdrawal requests for the account
// timedelta - positive integer with max value 50000000 which returns requests
// from number of seconds ago to now.
func (b *Bitstamp) GetWithdrawalRequests(ctx context.Context, timedelta int64) ([]WithdrawalRequests, error) {
func (e *Exchange) GetWithdrawalRequests(ctx context.Context, timedelta int64) ([]WithdrawalRequests, error) {
var resp []WithdrawalRequests
if timedelta > 50000000 || timedelta < 0 {
return resp, errors.New("time delta exceeded, max: 50000000 min: 0")
@@ -348,7 +348,7 @@ func (b *Bitstamp) GetWithdrawalRequests(ctx context.Context, timedelta int64) (
}
return resp,
b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIWithdrawalRequests, false, value, &resp)
e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIWithdrawalRequests, false, value, &resp)
}
// CryptoWithdrawal withdraws a cryptocurrency into a supplied wallet, returns ID
@@ -356,7 +356,7 @@ func (b *Bitstamp) GetWithdrawalRequests(ctx context.Context, timedelta int64) (
// address - The wallet address of the cryptocurrency
// symbol - the type of crypto ie "ltc", "btc", "eth"
// destTag - only for XRP default to ""
func (b *Bitstamp) CryptoWithdrawal(ctx context.Context, amount float64, address, symbol, destTag string) (*CryptoWithdrawalResponse, error) {
func (e *Exchange) CryptoWithdrawal(ctx context.Context, amount float64, address, symbol, destTag string) (*CryptoWithdrawalResponse, error) {
req := url.Values{}
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
req.Add("address", address)
@@ -375,11 +375,11 @@ func (b *Bitstamp) CryptoWithdrawal(ctx context.Context, amount float64, address
var resp CryptoWithdrawalResponse
endpoint = strings.ToLower(symbol) + "_withdrawal"
return &resp, b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, endpoint, true, req, &resp)
return &resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, endpoint, true, req, &resp)
}
// OpenBankWithdrawal Opens a bank withdrawal request (SEPA or international)
func (b *Bitstamp) OpenBankWithdrawal(ctx context.Context, amount float64, currency,
func (e *Exchange) OpenBankWithdrawal(ctx context.Context, amount float64, currency,
name, iban, bic, address, postalCode, city, country,
comment, withdrawalType string,
) (FIATWithdrawalResponse, error) {
@@ -397,11 +397,11 @@ func (b *Bitstamp) OpenBankWithdrawal(ctx context.Context, amount float64, curre
req.Add("comment", comment)
resp := FIATWithdrawalResponse{}
return resp, b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIOpenWithdrawal, true, req, &resp)
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIOpenWithdrawal, true, req, &resp)
}
// OpenInternationalBankWithdrawal Opens a bank withdrawal request (international)
func (b *Bitstamp) OpenInternationalBankWithdrawal(ctx context.Context, amount float64, currency,
func (e *Exchange) OpenInternationalBankWithdrawal(ctx context.Context, amount float64, currency,
name, iban, bic, address, postalCode, city, country,
bankName, bankAddress, bankPostCode, bankCity, bankCountry, internationalCurrency,
comment, withdrawalType string,
@@ -426,27 +426,27 @@ func (b *Bitstamp) OpenInternationalBankWithdrawal(ctx context.Context, amount f
req.Add("bank_country", bankCountry)
resp := FIATWithdrawalResponse{}
return resp, b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIOpenWithdrawal, true, req, &resp)
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIOpenWithdrawal, true, req, &resp)
}
// GetCryptoDepositAddress returns a depositing address by crypto.
// crypto - example "btc", "ltc", "eth", "xrp" or "bch"
func (b *Bitstamp) GetCryptoDepositAddress(ctx context.Context, crypto currency.Code) (*DepositAddress, error) {
func (e *Exchange) GetCryptoDepositAddress(ctx context.Context, crypto currency.Code) (*DepositAddress, error) {
path := crypto.Lower().String() + "_address"
var resp DepositAddress
return &resp, b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, nil, &resp)
return &resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, nil, &resp)
}
// GetUnconfirmedBitcoinDeposits returns unconfirmed transactions
func (b *Bitstamp) GetUnconfirmedBitcoinDeposits(ctx context.Context) ([]UnconfirmedBTCTransactions, error) {
func (e *Exchange) GetUnconfirmedBitcoinDeposits(ctx context.Context) ([]UnconfirmedBTCTransactions, error) {
var response []UnconfirmedBTCTransactions
return response,
b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIUnconfirmedBitcoin, false, nil, &response)
e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIUnconfirmedBitcoin, false, nil, &response)
}
// OHLC returns OHLCV data for step (interval)
func (b *Bitstamp) OHLC(ctx context.Context, currency string, start, end time.Time, step, limit string) (resp OHLCResponse, err error) {
func (e *Exchange) OHLC(ctx context.Context, currency string, start, end time.Time, step, limit string) (resp OHLCResponse, err error) {
v := url.Values{}
v.Add("limit", limit)
v.Add("step", step)
@@ -460,7 +460,7 @@ func (b *Bitstamp) OHLC(ctx context.Context, currency string, start, end time.Ti
if !end.IsZero() {
v.Add("end", strconv.FormatInt(end.Unix(), 10))
}
return resp, b.SendHTTPRequest(ctx, exchange.RestSpot, common.EncodeURLValues("/v"+bitstampAPIVersion+"/"+bitstampAPIOHLC+"/"+currency, v), &resp)
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, common.EncodeURLValues("/v"+bitstampAPIVersion+"/"+bitstampAPIOHLC+"/"+currency, v), &resp)
}
// TransferAccountBalance transfers funds from either a main or sub account
@@ -468,7 +468,7 @@ func (b *Bitstamp) OHLC(ctx context.Context, currency string, start, end time.Ti
// currency - which currency to transfer
// subaccount - name of account
// toMain - bool either to or from account
func (b *Bitstamp) TransferAccountBalance(ctx context.Context, amount float64, currency, subAccount string, toMain bool) error {
func (e *Exchange) TransferAccountBalance(ctx context.Context, amount float64, currency, subAccount string, toMain bool) error {
req := url.Values{}
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
req.Add("currency", currency)
@@ -488,12 +488,12 @@ func (b *Bitstamp) TransferAccountBalance(ctx context.Context, amount float64, c
var resp any
return b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, req, &resp)
return e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, path, true, req, &resp)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bitstamp) SendHTTPRequest(ctx context.Context, ep exchange.URL, path string, result any) error {
endpoint, err := b.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
}
@@ -501,22 +501,22 @@ func (b *Bitstamp) SendHTTPRequest(ctx context.Context, ep exchange.URL, path st
Method: http.MethodGet,
Path: endpoint + path,
Result: result,
Verbose: b.Verbose,
HTTPDebugging: b.HTTPDebugging,
HTTPRecording: b.HTTPRecording,
Verbose: e.Verbose,
HTTPDebugging: e.HTTPDebugging,
HTTPRecording: e.HTTPRecording,
}
return b.SendPayload(ctx, request.Unset, func() (*request.Item, error) {
return e.SendPayload(ctx, request.Unset, func() (*request.Item, error) {
return item, nil
}, request.UnauthenticatedRequest)
}
// SendAuthenticatedHTTPRequest sends an authenticated request
func (b *Bitstamp) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange.URL, path string, v2 bool, values url.Values, result any) error {
creds, err := b.GetCredentials(ctx)
func (e *Exchange) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange.URL, path string, v2 bool, values url.Values, result any) error {
creds, err := e.GetCredentials(ctx)
if err != nil {
return err
}
endpoint, err := b.API.Endpoints.GetURL(ep)
endpoint, err := e.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
@@ -526,8 +526,8 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange
}
interim := json.RawMessage{}
err = b.SendPayload(ctx, request.Unset, func() (*request.Item, error) {
n := b.Requester.GetNonce(nonce.UnixNano).String()
err = e.SendPayload(ctx, request.Unset, func() (*request.Item, error) {
n := e.Requester.GetNonce(nonce.UnixNano).String()
values.Set("key", creds.Key)
values.Set("nonce", n)
@@ -559,9 +559,9 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange
Body: readerValues,
Result: &interim,
NonceEnabled: true,
Verbose: b.Verbose,
HTTPDebugging: b.HTTPDebugging,
HTTPRecording: b.HTTPRecording,
Verbose: e.Verbose,
HTTPDebugging: e.HTTPDebugging,
HTTPRecording: e.HTTPRecording,
}, nil
}, request.AuthenticatedRequest)
if err != nil {

View File

@@ -16,13 +16,13 @@ import (
var mockTests = false
func TestMain(m *testing.M) {
b = new(Bitstamp)
if err := testexch.Setup(b); err != nil {
e = new(Exchange)
if err := testexch.Setup(e); err != nil {
log.Fatalf("Bitstamp Setup error: %s", err)
}
if apiKey != "" && apiSecret != "" {
b.API.AuthenticatedSupport = true
b.SetCredentials(apiKey, apiSecret, customerID, "", "", "")
e.API.AuthenticatedSupport = true
e.SetCredentials(apiKey, apiSecret, customerID, "", "", "")
}
log.Printf(sharedtestvalues.LiveTesting, b.Name)
os.Exit(m.Run())

View File

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

File diff suppressed because one or more lines are too long

View File

@@ -55,51 +55,51 @@ var subscriptionNames = map[string]string{
}
// WsConnect connects to a websocket feed
func (b *Bitstamp) WsConnect() error {
if !b.Websocket.IsEnabled() || !b.IsEnabled() {
func (e *Exchange) WsConnect() error {
if !e.Websocket.IsEnabled() || !e.IsEnabled() {
return websocket.ErrWebsocketNotEnabled
}
ctx := context.TODO()
var dialer gws.Dialer
err := b.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
err := e.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
if err != nil {
return err
}
if b.Verbose {
log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", b.Name)
if e.Verbose {
log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", e.Name)
}
b.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
e.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
MessageType: gws.TextMessage,
Message: hbMsg,
Delay: hbInterval,
})
err = b.seedOrderBook(ctx)
err = e.seedOrderBook(ctx)
if err != nil {
b.Websocket.DataHandler <- err
e.Websocket.DataHandler <- err
}
b.Websocket.Wg.Add(1)
go b.wsReadData(ctx)
e.Websocket.Wg.Add(1)
go e.wsReadData(ctx)
return nil
}
// wsReadData receives and passes on websocket messages for processing
func (b *Bitstamp) wsReadData(ctx context.Context) {
defer b.Websocket.Wg.Done()
func (e *Exchange) wsReadData(ctx context.Context) {
defer e.Websocket.Wg.Done()
for {
resp := b.Websocket.Conn.ReadMessage()
resp := e.Websocket.Conn.ReadMessage()
if resp.Raw == nil {
return
}
if err := b.wsHandleData(ctx, resp.Raw); err != nil {
b.Websocket.DataHandler <- err
if err := e.wsHandleData(ctx, resp.Raw); err != nil {
e.Websocket.DataHandler <- err
}
}
}
func (b *Bitstamp) wsHandleData(_ context.Context, respRaw []byte) error {
func (e *Exchange) wsHandleData(_ context.Context, respRaw []byte) error {
event, err := jsonparser.GetUnsafeString(respRaw, "event")
if err != nil {
return fmt.Errorf("%w `event`: %w", common.ErrParsingWSField, err)
@@ -110,40 +110,40 @@ func (b *Bitstamp) wsHandleData(_ context.Context, respRaw []byte) error {
case "heartbeat":
return nil
case "subscription_succeeded", "unsubscription_succeeded":
return b.handleWSSubscription(event, respRaw)
return e.handleWSSubscription(event, respRaw)
case "data":
return b.handleWSOrderbook(respRaw)
return e.handleWSOrderbook(respRaw)
case "trade":
return b.handleWSTrade(respRaw)
return e.handleWSTrade(respRaw)
case "order_created", "order_deleted", "order_changed":
return b.handleWSOrder(event, respRaw)
return e.handleWSOrder(event, respRaw)
case "request_reconnect":
go func() {
if err := b.Websocket.Shutdown(); err != nil { // Connection monitor will reconnect
log.Errorf(log.WebsocketMgr, "%s failed to shutdown websocket: %v", b.Name, err)
if err := e.Websocket.Shutdown(); err != nil { // Connection monitor will reconnect
log.Errorf(log.WebsocketMgr, "%s failed to shutdown websocket: %v", e.Name, err)
}
}()
default:
b.Websocket.DataHandler <- websocket.UnhandledMessageWarning{Message: b.Name + websocket.UnhandledMessage + string(respRaw)}
e.Websocket.DataHandler <- websocket.UnhandledMessageWarning{Message: e.Name + websocket.UnhandledMessage + string(respRaw)}
}
return nil
}
func (b *Bitstamp) handleWSSubscription(event string, respRaw []byte) error {
func (e *Exchange) handleWSSubscription(event string, respRaw []byte) error {
channel, err := jsonparser.GetUnsafeString(respRaw, "channel")
if err != nil {
return fmt.Errorf("%w `channel`: %w", common.ErrParsingWSField, err)
}
event = strings.TrimSuffix(event, "scription_succeeded")
return b.Websocket.Match.RequireMatchWithData(event+":"+channel, respRaw)
return e.Websocket.Match.RequireMatchWithData(event+":"+channel, respRaw)
}
func (b *Bitstamp) handleWSTrade(msg []byte) error {
if !b.IsSaveTradeDataEnabled() {
func (e *Exchange) handleWSTrade(msg []byte) error {
if !e.IsSaveTradeDataEnabled() {
return nil
}
_, p, err := b.parseChannelName(msg)
_, p, err := e.parseChannelName(msg)
if err != nil {
return err
}
@@ -161,7 +161,7 @@ func (b *Bitstamp) handleWSTrade(msg []byte) error {
Timestamp: wsTradeTemp.Data.Timestamp.Time(),
CurrencyPair: p,
AssetType: asset.Spot,
Exchange: b.Name,
Exchange: e.Name,
Price: wsTradeTemp.Data.Price,
Amount: wsTradeTemp.Data.Amount,
Side: side,
@@ -169,8 +169,8 @@ func (b *Bitstamp) handleWSTrade(msg []byte) error {
})
}
func (b *Bitstamp) handleWSOrder(event string, msg []byte) error {
channel, p, err := b.parseChannelName(msg)
func (e *Exchange) handleWSOrder(event string, msg []byte) error {
channel, p, err := e.parseChannelName(msg)
if err != nil {
return err
}
@@ -211,7 +211,7 @@ func (b *Bitstamp) handleWSOrder(event string, msg []byte) error {
Amount: r.Order.Amount,
RemainingAmount: r.Order.RemainingAmount,
ExecutedAmount: executedAmount,
Exchange: b.Name,
Exchange: e.Name,
OrderID: r.Order.IDStr,
ClientOrderID: r.Order.ClientOrderID,
Side: r.Order.Side.Side(),
@@ -221,43 +221,43 @@ func (b *Bitstamp) handleWSOrder(event string, msg []byte) error {
Pair: p,
}
b.Websocket.DataHandler <- d
e.Websocket.DataHandler <- d
return nil
}
func (b *Bitstamp) generateSubscriptions() (subscription.List, error) {
return b.Features.Subscriptions.ExpandTemplates(b)
func (e *Exchange) generateSubscriptions() (subscription.List, error) {
return e.Features.Subscriptions.ExpandTemplates(e)
}
// GetSubscriptionTemplate returns a subscription channel template
func (b *Bitstamp) 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}).Parse(subTplText)
}
// Subscribe sends a websocket message to receive data from a list of channels
func (b *Bitstamp) Subscribe(subs subscription.List) error {
func (e *Exchange) Subscribe(subs subscription.List) error {
ctx := context.TODO()
return b.manageSubsWithCreds(ctx, subs, "sub")
return e.manageSubsWithCreds(ctx, subs, "sub")
}
// Unsubscribe sends a websocket message to stop receiving data from a list of channels
func (b *Bitstamp) Unsubscribe(subs subscription.List) error {
func (e *Exchange) Unsubscribe(subs subscription.List) error {
ctx := context.TODO()
return b.manageSubsWithCreds(ctx, subs, "unsub")
return e.manageSubsWithCreds(ctx, subs, "unsub")
}
func (b *Bitstamp) manageSubsWithCreds(ctx context.Context, subs subscription.List, op string) error {
func (e *Exchange) manageSubsWithCreds(ctx context.Context, subs subscription.List, op string) error {
var errs error
var creds *WebsocketAuthResponse
if authed := subs.Private(); len(authed) > 0 {
creds, errs = b.FetchWSAuth(ctx)
creds, errs = e.FetchWSAuth(ctx)
}
return common.AppendError(errs, b.ParallelChanOp(ctx, subs, func(ctx context.Context, s subscription.List) error { return b.manageSubs(ctx, s, op, creds) }, 1))
return common.AppendError(errs, e.ParallelChanOp(ctx, subs, func(ctx context.Context, s subscription.List) error { return e.manageSubs(ctx, s, op, creds) }, 1))
}
func (b *Bitstamp) manageSubs(ctx context.Context, subs subscription.List, op string, creds *WebsocketAuthResponse) error {
subs, errs := subs.ExpandTemplates(b)
func (e *Exchange) manageSubs(ctx context.Context, subs subscription.List, op string, creds *WebsocketAuthResponse) error {
subs, errs := subs.ExpandTemplates(e)
for _, s := range subs {
req := websocketEventRequest{
Event: "bts:" + op + "scribe",
@@ -272,12 +272,12 @@ func (b *Bitstamp) manageSubs(ctx context.Context, subs subscription.List, op st
req.Data.Channel = "private-" + req.Data.Channel + "-" + strconv.Itoa(int(creds.UserID))
req.Data.Auth = creds.Token
}
_, err := b.Websocket.Conn.SendMessageReturnResponse(ctx, request.Unset, op+":"+req.Data.Channel, req)
_, err := e.Websocket.Conn.SendMessageReturnResponse(ctx, request.Unset, op+":"+req.Data.Channel, req)
if err == nil {
if op == "sub" {
err = b.Websocket.AddSuccessfulSubscriptions(b.Websocket.Conn, s)
err = e.Websocket.AddSuccessfulSubscriptions(e.Websocket.Conn, s)
} else {
err = b.Websocket.RemoveSubscriptions(b.Websocket.Conn, s)
err = e.Websocket.RemoveSubscriptions(e.Websocket.Conn, s)
}
}
if err != nil {
@@ -288,8 +288,8 @@ func (b *Bitstamp) manageSubs(ctx context.Context, subs subscription.List, op st
return errs
}
func (b *Bitstamp) handleWSOrderbook(msg []byte) error {
_, p, err := b.parseChannelName(msg)
func (e *Exchange) handleWSOrderbook(msg []byte) error {
_, p, err := e.parseChannelName(msg)
if err != nil {
return err
}
@@ -305,8 +305,8 @@ func (b *Bitstamp) handleWSOrderbook(msg []byte) error {
Pair: p,
LastUpdated: wsOrderBookResp.Data.Microtimestamp.Time(),
Asset: asset.Spot,
Exchange: b.Name,
ValidateOrderbook: b.ValidateOrderbook,
Exchange: e.Name,
ValidateOrderbook: e.ValidateOrderbook,
}
for i := range wsOrderBookResp.Data.Asks {
@@ -318,21 +318,21 @@ func (b *Bitstamp) handleWSOrderbook(msg []byte) error {
obUpdate.Bids[i].Amount = wsOrderBookResp.Data.Bids[i][1].Float64()
}
filterOrderbookZeroBidPrice(obUpdate)
return b.Websocket.Orderbook.LoadSnapshot(obUpdate)
return e.Websocket.Orderbook.LoadSnapshot(obUpdate)
}
func (b *Bitstamp) seedOrderBook(ctx context.Context) error {
p, err := b.GetEnabledPairs(asset.Spot)
func (e *Exchange) seedOrderBook(ctx context.Context) error {
p, err := e.GetEnabledPairs(asset.Spot)
if err != nil {
return err
}
for x := range p {
pairFmt, err := b.FormatExchangeCurrency(p[x], asset.Spot)
pairFmt, err := e.FormatExchangeCurrency(p[x], asset.Spot)
if err != nil {
return err
}
orderbookSeed, err := b.GetOrderbook(ctx, pairFmt.String())
orderbookSeed, err := e.GetOrderbook(ctx, pairFmt.String())
if err != nil {
return err
}
@@ -340,8 +340,8 @@ func (b *Bitstamp) seedOrderBook(ctx context.Context) error {
newOrderBook := &orderbook.Book{
Pair: p[x],
Asset: asset.Spot,
Exchange: b.Name,
ValidateOrderbook: b.ValidateOrderbook,
Exchange: e.Name,
ValidateOrderbook: e.ValidateOrderbook,
Bids: make(orderbook.Levels, len(orderbookSeed.Bids)),
Asks: make(orderbook.Levels, len(orderbookSeed.Asks)),
LastUpdated: orderbookSeed.Timestamp,
@@ -362,7 +362,7 @@ func (b *Bitstamp) seedOrderBook(ctx context.Context) error {
filterOrderbookZeroBidPrice(newOrderBook)
err = b.Websocket.Orderbook.LoadSnapshot(newOrderBook)
err = e.Websocket.Orderbook.LoadSnapshot(newOrderBook)
if err != nil {
return err
}
@@ -372,9 +372,9 @@ func (b *Bitstamp) seedOrderBook(ctx context.Context) error {
// FetchWSAuth Retrieves a userID and auth-token from REST for subscribing to a websocket channel
// The token life-expectancy is only about 60s; use it immediately and do not store it
func (b *Bitstamp) FetchWSAuth(ctx context.Context) (*WebsocketAuthResponse, error) {
func (e *Exchange) FetchWSAuth(ctx context.Context) (*WebsocketAuthResponse, error) {
resp := &WebsocketAuthResponse{}
err := b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIWSAuthToken, true, nil, resp)
err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIWSAuthToken, true, nil, resp)
if err != nil {
return nil, fmt.Errorf("error fetching auth token: %w", err)
}
@@ -382,7 +382,7 @@ func (b *Bitstamp) FetchWSAuth(ctx context.Context) (*WebsocketAuthResponse, err
}
// parseChannelName splits the ws message channel and returns the channel name and pair
func (b *Bitstamp) parseChannelName(respRaw []byte) (string, currency.Pair, error) {
func (e *Exchange) parseChannelName(respRaw []byte) (string, currency.Pair, error) {
channel, err := jsonparser.GetUnsafeString(respRaw, "channel")
if err != nil {
return "", currency.EMPTYPAIR, fmt.Errorf("%w `channel`: %w", common.ErrParsingWSField, err)
@@ -403,7 +403,7 @@ func (b *Bitstamp) parseChannelName(respRaw []byte) (string, currency.Pair, erro
return "", currency.EMPTYPAIR, fmt.Errorf("%w: %s", errChannelUnderscores, channel)
}
enabledPairs, err := b.GetEnabledPairs(asset.Spot)
enabledPairs, err := e.GetEnabledPairs(asset.Spot)
if err != nil {
return "", currency.EMPTYPAIR, err
}

View File

@@ -31,24 +31,24 @@ import (
)
// SetDefaults sets default for Bitstamp
func (b *Bitstamp) SetDefaults() {
b.Name = "Bitstamp"
b.Enabled = true
b.Verbose = true
b.API.CredentialsValidator.RequiresKey = true
b.API.CredentialsValidator.RequiresSecret = true
b.API.CredentialsValidator.RequiresClientID = true
func (e *Exchange) SetDefaults() {
e.Name = "Bitstamp"
e.Enabled = true
e.Verbose = true
e.API.CredentialsValidator.RequiresKey = true
e.API.CredentialsValidator.RequiresSecret = true
e.API.CredentialsValidator.RequiresClientID = true
requestFmt := &currency.EMPTYFORMAT
configFmt := &currency.PairFormat{
Uppercase: true,
Delimiter: currency.ForwardSlashDelimiter,
}
err := b.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
err := e.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
b.Features = exchange.Features{
e.Features = exchange.Features{
Supports: exchange.FeaturesSupported{
REST: true,
Websocket: true,
@@ -110,70 +110,70 @@ func (b *Bitstamp) SetDefaults() {
Subscriptions: defaultSubscriptions.Clone(),
}
b.Requester, err = request.New(b.Name,
e.Requester, err = request.New(e.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(request.NewBasicRateLimit(bitstampRateInterval, bitstampRequestRate, 1)))
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
b.API.Endpoints = b.NewEndpoints()
err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
e.API.Endpoints = e.NewEndpoints()
err = e.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: bitstampAPIURL,
exchange.WebsocketSpot: bitstampWSURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
b.Websocket = websocket.NewManager()
b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
b.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
e.Websocket = websocket.NewManager()
e.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
e.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
e.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
}
// Setup sets configuration values to bitstamp
func (b *Bitstamp) Setup(exch *config.Exchange) error {
func (e *Exchange) Setup(exch *config.Exchange) error {
err := exch.Validate()
if err != nil {
return err
}
if !exch.Enabled {
b.SetEnabled(false)
e.SetEnabled(false)
return nil
}
err = b.SetupDefaults(exch)
err = e.SetupDefaults(exch)
if err != nil {
return err
}
wsURL, err := b.API.Endpoints.GetURL(exchange.WebsocketSpot)
wsURL, err := e.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = b.Websocket.Setup(&websocket.ManagerSetup{
err = e.Websocket.Setup(&websocket.ManagerSetup{
ExchangeConfig: exch,
DefaultURL: bitstampWSURL,
RunningURL: wsURL,
Connector: b.WsConnect,
Subscriber: b.Subscribe,
Unsubscriber: b.Unsubscribe,
GenerateSubscriptions: b.generateSubscriptions,
Features: &b.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
}
return b.Websocket.SetupNewConnection(&websocket.ConnectionSetup{
URL: b.Websocket.GetWebsocketURL(),
return e.Websocket.SetupNewConnection(&websocket.ConnectionSetup{
URL: e.Websocket.GetWebsocketURL(),
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
})
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func (b *Bitstamp) FetchTradablePairs(ctx context.Context, _ asset.Item) (currency.Pairs, error) {
symbols, err := b.GetTradingPairs(ctx)
func (e *Exchange) FetchTradablePairs(ctx context.Context, _ asset.Item) (currency.Pairs, error) {
symbols, err := e.GetTradingPairs(ctx)
if err != nil {
return nil, err
}
@@ -194,24 +194,24 @@ func (b *Bitstamp) FetchTradablePairs(ctx context.Context, _ asset.Item) (curren
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (b *Bitstamp) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error {
pairs, err := b.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 = b.UpdatePairs(pairs, asset.Spot, false, forceUpdate)
err = e.UpdatePairs(pairs, asset.Spot, false, forceUpdate)
if err != nil {
return err
}
return b.EnsureOnePairEnabled()
return e.EnsureOnePairEnabled()
}
// UpdateOrderExecutionLimits sets exchange execution order limits for an asset type
func (b *Bitstamp) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error {
func (e *Exchange) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error {
if a != asset.Spot {
return common.ErrNotYetImplemented
}
symbols, err := b.GetTradingPairs(ctx)
symbols, err := e.GetTradingPairs(ctx)
if err != nil {
return err
}
@@ -232,25 +232,25 @@ func (b *Bitstamp) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item)
MinimumQuoteAmount: info.MinimumOrder,
})
}
if err := b.LoadLimits(limits); err != nil {
return fmt.Errorf("%s Error loading exchange limits: %v", b.Name, err)
if err := e.LoadLimits(limits); err != nil {
return fmt.Errorf("%s Error loading exchange limits: %v", e.Name, err)
}
return nil
}
// UpdateTickers updates the ticker for all currency pairs of a given asset type
func (b *Bitstamp) 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 (b *Bitstamp) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) {
fPair, err := b.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 := b.GetTicker(ctx, fPair.String(), false)
tick, err := e.GetTicker(ctx, fPair.String(), false)
if err != nil {
return nil, err
}
@@ -265,48 +265,48 @@ func (b *Bitstamp) UpdateTicker(ctx context.Context, p currency.Pair, a asset.It
Open: tick.Open,
Pair: fPair,
LastUpdated: tick.Timestamp.Time(),
ExchangeName: b.Name,
ExchangeName: e.Name,
AssetType: a,
})
if err != nil {
return nil, err
}
return ticker.GetTicker(b.Name, fPair, a)
return ticker.GetTicker(e.Name, fPair, a)
}
// GetFeeByType returns an estimate of fee based on type of transaction
func (b *Bitstamp) 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 (!b.AreCredentialsValid(ctx) || b.SkipAuthCheck) && // Todo check connection status
if (!e.AreCredentialsValid(ctx) || e.SkipAuthCheck) && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}
return b.GetFee(ctx, feeBuilder)
return e.GetFee(ctx, feeBuilder)
}
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (b *Bitstamp) 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 := b.CurrencyPairs.IsAssetEnabled(assetType); err != nil {
if err := e.CurrencyPairs.IsAssetEnabled(assetType); err != nil {
return nil, err
}
book := &orderbook.Book{
Exchange: b.Name,
Exchange: e.Name,
Pair: p,
Asset: assetType,
ValidateOrderbook: b.ValidateOrderbook,
ValidateOrderbook: e.ValidateOrderbook,
}
fPair, err := b.FormatExchangeCurrency(p, assetType)
fPair, err := e.FormatExchangeCurrency(p, assetType)
if err != nil {
return book, err
}
orderbookNew, err := b.GetOrderbook(ctx, fPair.String())
orderbookNew, err := e.GetOrderbook(ctx, fPair.String())
if err != nil {
return book, err
}
@@ -333,15 +333,15 @@ func (b *Bitstamp) UpdateOrderbook(ctx context.Context, p currency.Pair, assetTy
if err != nil {
return book, err
}
return orderbook.Get(b.Name, fPair, assetType)
return orderbook.Get(e.Name, fPair, assetType)
}
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// Bitstamp exchange
func (b *Bitstamp) 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 = b.Name
accountBalance, err := b.GetBalance(ctx)
response.Exchange = e.Name
accountBalance, err := e.GetBalance(ctx)
if err != nil {
return response, err
}
@@ -360,7 +360,7 @@ func (b *Bitstamp) UpdateAccountInfo(ctx context.Context, assetType asset.Item)
Currencies: currencies,
})
creds, err := b.GetCredentials(ctx)
creds, err := e.GetCredentials(ctx)
if err != nil {
return account.Holdings{}, err
}
@@ -374,13 +374,13 @@ func (b *Bitstamp) UpdateAccountInfo(ctx context.Context, assetType asset.Item)
// GetAccountFundingHistory returns funding history, deposits and
// withdrawals
func (b *Bitstamp) GetAccountFundingHistory(_ context.Context) ([]exchange.FundingHistory, error) {
func (e *Exchange) GetAccountFundingHistory(_ context.Context) ([]exchange.FundingHistory, error) {
return nil, common.ErrFunctionNotSupported
}
// GetWithdrawalsHistory returns previous withdrawals data
func (b *Bitstamp) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) ([]exchange.WithdrawalHistory, error) {
withdrawals, err := b.GetWithdrawalRequests(ctx, 0)
func (e *Exchange) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) ([]exchange.WithdrawalHistory, error) {
withdrawals, err := e.GetWithdrawalRequests(ctx, 0)
if err != nil {
return nil, err
}
@@ -402,13 +402,13 @@ func (b *Bitstamp) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _
}
// GetRecentTrades returns the most recent trades for a currency and asset
func (b *Bitstamp) GetRecentTrades(ctx context.Context, p currency.Pair, assetType asset.Item) ([]trade.Data, error) {
p, err := b.FormatExchangeCurrency(p, assetType)
func (e *Exchange) GetRecentTrades(ctx context.Context, p currency.Pair, assetType asset.Item) ([]trade.Data, error) {
p, err := e.FormatExchangeCurrency(p, assetType)
if err != nil {
return nil, err
}
tradeData, err := b.GetTransactions(ctx, p.String(), "")
tradeData, err := e.GetTransactions(ctx, p.String(), "")
if err != nil {
return nil, err
}
@@ -420,7 +420,7 @@ func (b *Bitstamp) GetRecentTrades(ctx context.Context, p currency.Pair, assetTy
s = order.Sell
}
resp[i] = trade.Data{
Exchange: b.Name,
Exchange: e.Name,
TID: strconv.FormatInt(tradeData[i].TradeID, 10),
CurrencyPair: p,
AssetType: assetType,
@@ -431,7 +431,7 @@ func (b *Bitstamp) GetRecentTrades(ctx context.Context, p currency.Pair, assetTy
}
}
err = b.AddTradesToBuffer(resp...)
err = e.AddTradesToBuffer(resp...)
if err != nil {
return nil, err
}
@@ -441,22 +441,22 @@ func (b *Bitstamp) GetRecentTrades(ctx context.Context, p currency.Pair, assetTy
}
// GetHistoricTrades returns historic trade data within the timeframe provided
func (b *Bitstamp) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.Item, _, _ time.Time) ([]trade.Data, error) {
func (e *Exchange) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.Item, _, _ time.Time) ([]trade.Data, error) {
return nil, common.ErrFunctionNotSupported
}
// SubmitOrder submits a new order
func (b *Bitstamp) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) {
if err := s.Validate(b.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
}
fPair, err := b.FormatExchangeCurrency(s.Pair, s.AssetType)
fPair, err := e.FormatExchangeCurrency(s.Pair, s.AssetType)
if err != nil {
return nil, err
}
response, err := b.PlaceOrder(ctx,
response, err := e.PlaceOrder(ctx,
fPair.String(),
s.Price,
s.Amount,
@@ -470,12 +470,12 @@ func (b *Bitstamp) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Sub
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *Bitstamp) 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 (b *Bitstamp) 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
}
@@ -484,18 +484,18 @@ func (b *Bitstamp) CancelOrder(ctx context.Context, o *order.Cancel) error {
if err != nil {
return err
}
_, err = b.CancelExistingOrder(ctx, orderIDInt)
_, err = e.CancelExistingOrder(ctx, orderIDInt)
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (b *Bitstamp) CancelBatchOrders(_ context.Context, _ []order.Cancel) (*order.CancelBatchResponse, error) {
func (e *Exchange) CancelBatchOrders(_ context.Context, _ []order.Cancel) (*order.CancelBatchResponse, error) {
return nil, common.ErrFunctionNotSupported
}
// CancelAllOrders cancels all orders associated with a currency pair
func (b *Bitstamp) CancelAllOrders(ctx context.Context, _ *order.Cancel) (order.CancelAllResponse, error) {
success, err := b.CancelAllExistingOrders(ctx)
func (e *Exchange) CancelAllOrders(ctx context.Context, _ *order.Cancel) (order.CancelAllResponse, error) {
success, err := e.CancelAllExistingOrders(ctx)
if err != nil {
return order.CancelAllResponse{}, err
}
@@ -507,12 +507,12 @@ func (b *Bitstamp) CancelAllOrders(ctx context.Context, _ *order.Cancel) (order.
}
// GetOrderInfo returns order information based on order ID
func (b *Bitstamp) 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
}
o, err := b.GetOrderStatus(ctx, iOID)
o, err := e.GetOrderStatus(ctx, iOID)
if err != nil {
return nil, err
}
@@ -540,8 +540,8 @@ func (b *Bitstamp) GetOrderInfo(ctx context.Context, orderID string, _ currency.
}
// GetDepositAddress returns a deposit address for a specified currency
func (b *Bitstamp) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _, _ string) (*deposit.Address, error) {
addr, err := b.GetCryptoDepositAddress(ctx, cryptocurrency)
func (e *Exchange) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _, _ string) (*deposit.Address, error) {
addr, err := e.GetCryptoDepositAddress(ctx, cryptocurrency)
if err != nil {
return nil, err
}
@@ -559,11 +559,11 @@ func (b *Bitstamp) GetDepositAddress(ctx context.Context, cryptocurrency currenc
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
// submitted
func (b *Bitstamp) 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 := b.CryptoWithdrawal(ctx,
resp, err := e.CryptoWithdrawal(ctx,
withdrawRequest.Amount,
withdrawRequest.Crypto.Address,
withdrawRequest.Currency.String(),
@@ -579,11 +579,11 @@ func (b *Bitstamp) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequ
// WithdrawFiatFunds returns a withdrawal ID when a
// withdrawal is submitted
func (b *Bitstamp) WithdrawFiatFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
func (e *Exchange) WithdrawFiatFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
if err := withdrawRequest.Validate(); err != nil {
return nil, err
}
resp, err := b.OpenBankWithdrawal(ctx,
resp, err := e.OpenBankWithdrawal(ctx,
withdrawRequest.Amount,
withdrawRequest.Currency.String(),
withdrawRequest.Fiat.Bank.AccountName,
@@ -606,11 +606,11 @@ func (b *Bitstamp) WithdrawFiatFunds(ctx context.Context, withdrawRequest *withd
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
// withdrawal is submitted
func (b *Bitstamp) WithdrawFiatFundsToInternationalBank(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
func (e *Exchange) WithdrawFiatFundsToInternationalBank(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
if err := withdrawRequest.Validate(); err != nil {
return nil, err
}
resp, err := b.OpenInternationalBankWithdrawal(ctx,
resp, err := e.OpenInternationalBankWithdrawal(ctx,
withdrawRequest.Amount,
withdrawRequest.Currency.String(),
withdrawRequest.Fiat.Bank.AccountName,
@@ -638,7 +638,7 @@ func (b *Bitstamp) WithdrawFiatFundsToInternationalBank(ctx context.Context, wit
}
// GetActiveOrders retrieves any orders that are active/open
func (b *Bitstamp) 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
@@ -649,14 +649,14 @@ func (b *Bitstamp) GetActiveOrders(ctx context.Context, req *order.MultiOrderReq
currPair = "all"
} else {
var fPair currency.Pair
fPair, err = b.FormatExchangeCurrency(req.Pairs[0], asset.Spot)
fPair, err = e.FormatExchangeCurrency(req.Pairs[0], asset.Spot)
if err != nil {
return nil, err
}
currPair = fPair.String()
}
resp, err := b.GetOpenOrders(ctx, currPair)
resp, err := e.GetOpenOrders(ctx, currPair)
if err != nil {
return nil, err
}
@@ -688,15 +688,15 @@ func (b *Bitstamp) GetActiveOrders(ctx context.Context, req *order.MultiOrderReq
Side: orderSide,
Date: resp[i].DateTime.Time(),
Pair: p,
Exchange: b.Name,
Exchange: e.Name,
}
}
return req.Filter(b.Name, orders), nil
return req.Filter(e.Name, orders), nil
}
// GetOrderHistory retrieves account order information
// Can Limit response to specific order status
func (b *Bitstamp) 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
@@ -705,19 +705,19 @@ func (b *Bitstamp) GetOrderHistory(ctx context.Context, req *order.MultiOrderReq
var currPair string
if len(req.Pairs) == 1 {
var fPair currency.Pair
fPair, err = b.FormatExchangeCurrency(req.Pairs[0], asset.Spot)
fPair, err = e.FormatExchangeCurrency(req.Pairs[0], asset.Spot)
if err != nil {
return nil, err
}
currPair = fPair.String()
}
format, err := b.GetPairFormat(asset.Spot, false)
format, err := e.GetPairFormat(asset.Spot, false)
if err != nil {
return nil, err
}
resp, err := b.GetUserTransactions(ctx, currPair)
resp, err := e.GetUserTransactions(ctx, currPair)
if err != nil {
return nil, err
}
@@ -737,7 +737,7 @@ func (b *Bitstamp) GetOrderHistory(ctx context.Context, req *order.MultiOrderReq
default:
log.Warnf(log.ExchangeSys,
"%s No base currency found for ID '%d'\n",
b.Name,
e.Name,
resp[i].OrderID)
}
@@ -749,7 +749,7 @@ func (b *Bitstamp) GetOrderHistory(ctx context.Context, req *order.MultiOrderReq
default:
log.Warnf(log.ExchangeSys,
"%s No quote currency found for orderID '%d'\n",
b.Name,
e.Name,
resp[i].OrderID)
}
@@ -763,32 +763,32 @@ func (b *Bitstamp) GetOrderHistory(ctx context.Context, req *order.MultiOrderReq
orders = append(orders, order.Detail{
OrderID: strconv.FormatInt(resp[i].OrderID, 10),
Date: resp[i].Date.Time(),
Exchange: b.Name,
Exchange: e.Name,
Pair: currPair,
})
}
return req.Filter(b.Name, orders), nil
return req.Filter(e.Name, orders), nil
}
// ValidateAPICredentials validates current credentials used for wrapper
// functionality
func (b *Bitstamp) ValidateAPICredentials(ctx context.Context, assetType asset.Item) error {
_, err := b.UpdateAccountInfo(ctx, assetType)
return b.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 (b *Bitstamp) GetHistoricCandles(ctx context.Context, pair currency.Pair, a asset.Item, interval kline.Interval, start, end time.Time) (*kline.Item, error) {
req, err := b.GetKlineRequest(pair, a, interval, start, end, false)
func (e *Exchange) GetHistoricCandles(ctx context.Context, pair currency.Pair, a asset.Item, interval kline.Interval, start, end time.Time) (*kline.Item, error) {
req, err := e.GetKlineRequest(pair, a, interval, start, end, false)
if err != nil {
return nil, err
}
candles, err := b.OHLC(ctx,
candles, err := e.OHLC(ctx,
req.RequestFormatted.String(),
req.Start,
req.End,
b.FormatExchangeKlineInterval(req.ExchangeInterval),
e.FormatExchangeKlineInterval(req.ExchangeInterval),
strconv.FormatUint(req.RequestLimit, 10))
if err != nil {
return nil, err
@@ -813,8 +813,8 @@ func (b *Bitstamp) GetHistoricCandles(ctx context.Context, pair currency.Pair, a
}
// GetHistoricCandlesExtended returns candles between a time period for a set time interval
func (b *Bitstamp) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pair, a asset.Item, interval kline.Interval, start, end time.Time) (*kline.Item, error) {
req, err := b.GetKlineExtendedRequest(pair, a, interval, start, end)
func (e *Exchange) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pair, a asset.Item, interval kline.Interval, start, end time.Time) (*kline.Item, error) {
req, err := e.GetKlineExtendedRequest(pair, a, interval, start, end)
if err != nil {
return nil, err
}
@@ -822,11 +822,11 @@ func (b *Bitstamp) GetHistoricCandlesExtended(ctx context.Context, pair currency
timeSeries := make([]kline.Candle, 0, req.Size())
for x := range req.RangeHolder.Ranges {
var candles OHLCResponse
candles, err = b.OHLC(ctx,
candles, err = e.OHLC(ctx,
req.RequestFormatted.String(),
req.RangeHolder.Ranges[x].Start.Time,
req.RangeHolder.Ranges[x].End.Time,
b.FormatExchangeKlineInterval(req.ExchangeInterval),
e.FormatExchangeKlineInterval(req.ExchangeInterval),
strconv.FormatUint(req.RequestLimit, 10),
)
if err != nil {
@@ -853,23 +853,23 @@ func (b *Bitstamp) GetHistoricCandlesExtended(ctx context.Context, pair currency
}
// GetServerTime returns the current exchange server time.
func (b *Bitstamp) GetServerTime(_ context.Context, _ asset.Item) (time.Time, error) {
func (e *Exchange) GetServerTime(_ context.Context, _ asset.Item) (time.Time, error) {
return time.Time{}, common.ErrFunctionNotSupported
}
// GetFuturesContractDetails returns all contracts from the exchange by asset type
func (b *Bitstamp) GetFuturesContractDetails(context.Context, asset.Item) ([]futures.Contract, error) {
func (e *Exchange) GetFuturesContractDetails(context.Context, asset.Item) ([]futures.Contract, error) {
return nil, common.ErrFunctionNotSupported
}
// GetLatestFundingRates returns the latest funding rates data
func (b *Bitstamp) 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 (b *Bitstamp) GetCurrencyTradeURL(_ context.Context, a asset.Item, cp currency.Pair) (string, error) {
_, err := b.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
}