diff --git a/exchanges/okcoin/okcoin.go b/exchanges/okcoin/okcoin.go
index f42d6924..36cf0307 100644
--- a/exchanges/okcoin/okcoin.go
+++ b/exchanges/okcoin/okcoin.go
@@ -1,22 +1,783 @@
package okcoin
import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
"time"
- "github.com/thrasher-corp/gocryptotrader/exchanges/okgroup"
+ "github.com/google/go-querystring/query"
+ "github.com/thrasher-corp/gocryptotrader/common"
+ "github.com/thrasher-corp/gocryptotrader/common/convert"
+ "github.com/thrasher-corp/gocryptotrader/common/crypto"
+ exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/account"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/asset"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/kline"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/request"
)
const (
okCoinRateInterval = time.Second
okCoinStandardRequestRate = 6
- okCoinAPIPath = "api/"
- okCoinAPIURL = "https://www.okcoin.com/" + okCoinAPIPath
+ apiPath = "api/"
+ okCoinAPIURL = "https://www.okcoin.com/" + apiPath
okCoinAPIVersion = "/v3/"
okCoinExchangeName = "OKCOIN International"
okCoinWebsocketURL = "wss://real.okcoin.com:8443/ws/v3"
)
-// OKCoin bases all methods off okgroup implementation
+// OKCoin is the overarching type used for OKCoin's exchange API implementation
type OKCoin struct {
- okgroup.OKGroup
+ exchange.Base
+ // Spot and contract market error codes
+ ErrorCodes map[string]error
+}
+
+const (
+ accountSubsection = "account"
+ tokenSubsection = "spot"
+ marginTradingSubsection = "margin"
+ accounts = "accounts"
+ ledger = "ledger"
+ orders = "orders"
+ batchOrders = "batch_orders"
+ cancelOrders = "cancel_orders"
+ cancelBatchOrders = "cancel_batch_orders"
+ pendingOrders = "orders_pending"
+ trades = "trades"
+ tickerData = "ticker"
+ instruments = "instruments"
+ getAccountDepositHistory = "deposit/history"
+ getSpotTransactionDetails = "fills"
+ getSpotOrderBook = "book"
+ getSpotMarketData = "candles"
+ // Account based endpoints
+ getAccountCurrencies = "currencies"
+ getAccountWalletInformation = "wallet"
+ fundsTransfer = "transfer"
+ withdrawRequest = "withdrawal"
+ getWithdrawalFees = "withdrawal/fee"
+ getWithdrawalHistory = "withdrawal/history"
+ getDepositAddress = "deposit/address"
+ // Margin based endpoints
+ getMarketAvailability = "availability"
+ getLoan = "borrow"
+ getRepayment = "repayment"
+)
+
+// GetAccountCurrencies returns a list of tradable spot instruments and their properties
+func (o *OKCoin) GetAccountCurrencies(ctx context.Context) ([]GetAccountCurrenciesResponse, error) {
+ var respData []struct {
+ Name string `json:"name"`
+ Currency string `json:"currency"`
+ Chain string `json:"chain"`
+ CanInternal int64 `json:"can_internal,string"`
+ CanWithdraw int64 `json:"can_withdraw,string"`
+ CanDeposit int64 `json:"can_deposit,string"`
+ MinWithdrawal string `json:"min_withdrawal"`
+ }
+ err := o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, accountSubsection, getAccountCurrencies, nil, &respData, true)
+ if err != nil {
+ return nil, err
+ }
+ resp := make([]GetAccountCurrenciesResponse, len(respData))
+ for i := range respData {
+ var mw float64
+ if respData[i].MinWithdrawal != "" {
+ mw, err = strconv.ParseFloat(respData[i].MinWithdrawal, 64)
+ if err != nil {
+ return nil, err
+ }
+ }
+ resp[i] = GetAccountCurrenciesResponse{
+ Name: respData[i].Name,
+ Currency: respData[i].Currency,
+ Chain: respData[i].Chain,
+ CanInternal: respData[i].CanInternal == 1,
+ CanWithdraw: respData[i].CanWithdraw == 1,
+ CanDeposit: respData[i].CanDeposit == 1,
+ MinWithdrawal: mw,
+ }
+ }
+ return resp, nil
+}
+
+// GetAccountWalletInformation returns a list of wallets and their properties
+func (o *OKCoin) GetAccountWalletInformation(ctx context.Context, currency string) ([]WalletInformationResponse, error) {
+ requestURL := getAccountWalletInformation
+ if currency != "" {
+ requestURL += "/" + currency
+ }
+ var resp []WalletInformationResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, accountSubsection, requestURL, nil, &resp, true)
+}
+
+// TransferAccountFunds the transfer of funds between wallet, trading accounts, main account and subaccounts
+func (o *OKCoin) TransferAccountFunds(ctx context.Context, request *TransferAccountFundsRequest) (*TransferAccountFundsResponse, error) {
+ var resp *TransferAccountFundsResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, accountSubsection, fundsTransfer, request, &resp, true)
+}
+
+// AccountWithdraw withdrawal of tokens to OKCoin International or other addresses.
+func (o *OKCoin) AccountWithdraw(ctx context.Context, request *AccountWithdrawRequest) (*AccountWithdrawResponse, error) {
+ var resp *AccountWithdrawResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, accountSubsection, withdrawRequest, request, &resp, true)
+}
+
+// GetAccountWithdrawalFee retrieves the information about the recommended network transaction fee for withdrawals to digital asset addresses. The higher the fees are, the sooner the confirmations you will get.
+func (o *OKCoin) GetAccountWithdrawalFee(ctx context.Context, currency string) ([]GetAccountWithdrawalFeeResponse, error) {
+ requestURL := getWithdrawalFees
+ if currency != "" {
+ requestURL += "?currency=" + currency
+ }
+
+ var resp []GetAccountWithdrawalFeeResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, accountSubsection, requestURL, nil, &resp, true)
+}
+
+// GetAccountWithdrawalHistory retrieves all recent withdrawal records.
+func (o *OKCoin) GetAccountWithdrawalHistory(ctx context.Context, currency string) ([]WithdrawalHistoryResponse, error) {
+ requestURL := getWithdrawalHistory
+ if currency != "" {
+ requestURL += "/" + currency
+ }
+ var resp []WithdrawalHistoryResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, accountSubsection, requestURL, nil, &resp, true)
+}
+
+// GetAccountBillDetails retrieves the bill details of the wallet. All the information will be paged and sorted in reverse chronological order,
+// which means the latest will be at the top. Please refer to the pagination section for additional records after the first page.
+// 3 months recent records will be returned at maximum
+func (o *OKCoin) GetAccountBillDetails(ctx context.Context, request *GetAccountBillDetailsRequest) ([]GetAccountBillDetailsResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := ledger + encodedRequest
+ var resp []GetAccountBillDetailsResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, accountSubsection, requestURL, nil, &resp, true)
+}
+
+// GetAccountDepositAddressForCurrency retrieves the deposit addresses of different tokens, including previously used addresses.
+func (o *OKCoin) GetAccountDepositAddressForCurrency(ctx context.Context, currency string) ([]GetDepositAddressResponse, error) {
+ urlValues := url.Values{}
+ urlValues.Set("currency", currency)
+ requestURL := getDepositAddress + "?" + urlValues.Encode()
+ var resp []GetDepositAddressResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, accountSubsection, requestURL, nil, &resp, true)
+}
+
+// GetAccountDepositHistory retrieves the deposit history of all tokens.100 recent records will be returned at maximum
+func (o *OKCoin) GetAccountDepositHistory(ctx context.Context, currency string) ([]GetAccountDepositHistoryResponse, error) {
+ requestURL := getAccountDepositHistory
+ if currency != "" {
+ requestURL += "/" + currency
+ }
+ var resp []GetAccountDepositHistoryResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, accountSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotTradingAccounts retrieves the list of assets(only show pairs with balance larger than 0), the balances, amount available/on hold in spot accounts.
+func (o *OKCoin) GetSpotTradingAccounts(ctx context.Context) ([]GetSpotTradingAccountResponse, error) {
+ var resp []GetSpotTradingAccountResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, accounts, nil, &resp, true)
+}
+
+// GetSpotTradingAccountForCurrency This endpoint supports getting the balance, amount available/on hold of a token in spot account.
+func (o *OKCoin) GetSpotTradingAccountForCurrency(ctx context.Context, currency string) (*GetSpotTradingAccountResponse, error) {
+ requestURL := accounts + "/" + currency
+ var resp *GetSpotTradingAccountResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotBillDetailsForCurrency This endpoint supports getting the balance, amount available/on hold of a token in spot account.
+func (o *OKCoin) GetSpotBillDetailsForCurrency(ctx context.Context, request *GetSpotBillDetailsForCurrencyRequest) ([]GetSpotBillDetailsForCurrencyResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := accounts + "/" + request.Currency + "/" + ledger + encodedRequest
+ var resp []GetSpotBillDetailsForCurrencyResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, nil, &resp, true)
+}
+
+// PlaceSpotOrder token trading only supports limit and market orders (more order types will become available in the future).
+// You can place an order only if you have enough funds.
+// Once your order is placed, the amount will be put on hold.
+func (o *OKCoin) PlaceSpotOrder(ctx context.Context, request *PlaceOrderRequest) (*PlaceOrderResponse, error) {
+ if request.OrderType == "" {
+ request.OrderType = "0"
+ }
+ var resp *PlaceOrderResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, tokenSubsection, orders, request, &resp, true)
+}
+
+// PlaceMultipleSpotOrders supports placing multiple orders for specific trading pairs
+// up to 4 trading pairs, maximum 4 orders for each pair
+func (o *OKCoin) PlaceMultipleSpotOrders(ctx context.Context, request []PlaceOrderRequest) (map[string][]PlaceOrderResponse, []error) {
+ currencyPairOrders := make(map[string]int)
+ resp := make(map[string][]PlaceOrderResponse)
+
+ for i := range request {
+ if request[i].OrderType == "" {
+ request[i].OrderType = "0"
+ }
+ currencyPairOrders[request[i].InstrumentID]++
+ }
+
+ if len(currencyPairOrders) > 4 {
+ return resp, []error{errors.New("up to 4 trading pairs")}
+ }
+ for _, orderCount := range currencyPairOrders {
+ if orderCount > 4 {
+ return resp, []error{errors.New("maximum 4 orders for each pair")}
+ }
+ }
+
+ err := o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, tokenSubsection, batchOrders, request, &resp, true)
+ if err != nil {
+ return resp, []error{err}
+ }
+
+ var orderErrors []error
+ for currency, orderResponse := range resp {
+ for i := range orderResponse {
+ if !orderResponse[i].Result {
+ orderErrors = append(orderErrors, fmt.Errorf("order for currency %v failed to be placed", currency))
+ }
+ }
+ }
+
+ return resp, orderErrors
+}
+
+// CancelSpotOrder Cancelling an unfilled order.
+func (o *OKCoin) CancelSpotOrder(ctx context.Context, request *CancelSpotOrderRequest) (*CancelSpotOrderResponse, error) {
+ requestURL := cancelOrders + "/" + strconv.FormatInt(request.OrderID, 10)
+ var resp *CancelSpotOrderResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, tokenSubsection, requestURL, request, &resp, true)
+}
+
+// CancelMultipleSpotOrders Cancelling multiple unfilled orders.
+func (o *OKCoin) CancelMultipleSpotOrders(ctx context.Context, request *CancelMultipleSpotOrdersRequest) (map[string][]CancelMultipleSpotOrdersResponse, error) {
+ if len(request.OrderIDs) > 4 {
+ return nil, errors.New("maximum 4 order cancellations for each pair")
+ }
+
+ resp := make(map[string][]CancelMultipleSpotOrdersResponse)
+ err := o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, tokenSubsection, cancelBatchOrders, []CancelMultipleSpotOrdersRequest{*request}, &resp, true)
+ if err != nil {
+ return nil, err
+ }
+ for currency, orderResponse := range resp {
+ for i := range orderResponse {
+ cancellationResponse := CancelMultipleSpotOrdersResponse{
+ OrderID: orderResponse[i].OrderID,
+ Result: orderResponse[i].Result,
+ ClientOID: orderResponse[i].ClientOID,
+ }
+
+ if !orderResponse[i].Result {
+ cancellationResponse.Error = fmt.Errorf("order %v for currency %v failed to be cancelled", orderResponse[i].OrderID, currency)
+ }
+
+ resp[currency] = append(resp[currency], cancellationResponse)
+ }
+ }
+
+ return resp, nil
+}
+
+// GetSpotOrders List your orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKCoin) GetSpotOrders(ctx context.Context, request *GetSpotOrdersRequest) ([]GetSpotOrderResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := orders + encodedRequest
+ var resp []GetSpotOrderResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotOpenOrders List all your current open orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKCoin) GetSpotOpenOrders(ctx context.Context, request *GetSpotOpenOrdersRequest) ([]GetSpotOrderResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := pendingOrders + encodedRequest
+ var resp []GetSpotOrderResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotOrder Get order details by order ID.
+func (o *OKCoin) GetSpotOrder(ctx context.Context, request *GetSpotOrderRequest) (*GetSpotOrderResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := orders + "/" + request.OrderID + encodedRequest
+ var resp *GetSpotOrderResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, request, &resp, true)
+}
+
+// GetSpotTransactionDetails Get details of the recent filled orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKCoin) GetSpotTransactionDetails(ctx context.Context, request *GetSpotTransactionDetailsRequest) ([]GetSpotTransactionDetailsResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := getSpotTransactionDetails + encodedRequest
+ var resp []GetSpotTransactionDetailsResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotTokenPairDetails Get market data. This endpoint provides the snapshots of market data and can be used without verifications.
+// List trading pairs and get the trading limit, price, and more information of different trading pairs.
+func (o *OKCoin) GetSpotTokenPairDetails(ctx context.Context) ([]GetSpotTokenPairDetailsResponse, error) {
+ var resp []GetSpotTokenPairDetailsResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, instruments, nil, &resp, false)
+}
+
+// GetOrderBook Getting the order book of a trading pair. Pagination is not
+// supported here. The whole book will be returned for one request. Websocket is
+// recommended here.
+func (o *OKCoin) GetOrderBook(ctx context.Context, request *GetOrderBookRequest, a asset.Item) (*GetOrderBookResponse, error) {
+ var resp *GetOrderBookResponse
+ if a != asset.Spot {
+ return nil, fmt.Errorf("%w %v", asset.ErrNotSupported, a)
+ }
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := instruments + "/" + request.InstrumentID + "/" + getSpotOrderBook + encodedRequest
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, nil, &resp, false)
+}
+
+// GetSpotAllTokenPairsInformation Get the last traded price, best bid/ask price, 24 hour trading volume and more info of all trading pairs.
+func (o *OKCoin) GetSpotAllTokenPairsInformation(ctx context.Context) ([]GetSpotTokenPairsInformationResponse, error) {
+ requestURL := instruments + "/" + tickerData
+ var resp []GetSpotTokenPairsInformationResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, nil, &resp, false)
+}
+
+// GetSpotAllTokenPairsInformationForCurrency Get the last traded price, best bid/ask price, 24 hour trading volume and more info of a currency
+func (o *OKCoin) GetSpotAllTokenPairsInformationForCurrency(ctx context.Context, currency string) (*GetSpotTokenPairsInformationResponse, error) {
+ requestURL := instruments + "/" + currency + "/" + tickerData
+ var resp *GetSpotTokenPairsInformationResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, nil, &resp, false)
+}
+
+// GetSpotFilledOrdersInformation Get the recent 60 transactions of all trading pairs.
+// Cursor pagination is used. All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKCoin) GetSpotFilledOrdersInformation(ctx context.Context, request *GetSpotFilledOrdersInformationRequest) ([]GetSpotFilledOrdersInformationResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := instruments + "/" + request.InstrumentID + "/" + trades + encodedRequest
+ var resp []GetSpotFilledOrdersInformationResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, nil, &resp, false)
+}
+
+// GetMarketData Get the charts of the trading pairs. Charts are returned in grouped buckets based on requested granularity.
+func (o *OKCoin) GetMarketData(ctx context.Context, request *GetMarketDataRequest) ([]kline.Candle, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := instruments + "/" + request.InstrumentID + "/" + getSpotMarketData + encodedRequest
+ if request.Asset != asset.Spot && request.Asset != asset.Margin {
+ return nil, asset.ErrNotSupported
+ }
+ var resp []interface{}
+ err = o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, tokenSubsection, requestURL, nil, &resp, false)
+ if err != nil {
+ return nil, err
+ }
+ candles := make([]kline.Candle, len(resp))
+ for x := range resp {
+ t, ok := resp[x].([]interface{})
+ if !ok {
+ return nil, common.GetAssertError("[]interface{}", resp[x])
+ }
+ if len(t) < 6 {
+ return nil, fmt.Errorf("%w expteced %v received %v", errIncorrectCandleDataLength, 6, len(t))
+ }
+ v, ok := t[0].(string)
+ if !ok {
+ return nil, common.GetAssertError("string", t[0])
+ }
+ var tempCandle kline.Candle
+ if tempCandle.Time, err = time.Parse(time.RFC3339, v); err != nil {
+ return nil, err
+ }
+ if tempCandle.Open, err = convert.FloatFromString(t[1]); err != nil {
+ return nil, err
+ }
+ if tempCandle.High, err = convert.FloatFromString(t[2]); err != nil {
+ return nil, err
+ }
+ if tempCandle.Low, err = convert.FloatFromString(t[3]); err != nil {
+ return nil, err
+ }
+ if tempCandle.Close, err = convert.FloatFromString(t[4]); err != nil {
+ return nil, err
+ }
+ if tempCandle.Volume, err = convert.FloatFromString(t[5]); err != nil {
+ return nil, err
+ }
+ candles[x] = tempCandle
+ }
+ return candles, nil
+}
+
+// GetMarginTradingAccounts List all assets under token margin trading account, including information such as balance, amount on hold and more.
+func (o *OKCoin) GetMarginTradingAccounts(ctx context.Context) ([]GetMarginAccountsResponse, error) {
+ var resp []GetMarginAccountsResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginTradingSubsection, accounts, nil, &resp, true)
+}
+
+// GetMarginTradingAccountsForCurrency Get the balance, amount on hold and more useful information.
+func (o *OKCoin) GetMarginTradingAccountsForCurrency(ctx context.Context, currency string) (*GetMarginAccountsResponse, error) {
+ requestURL := accounts + "/" + currency
+ var resp *GetMarginAccountsResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginBillDetails List all bill details. Pagination is used here.
+// before and after cursor arguments should not be confused with before and after in chronological time.
+// Most paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKCoin) GetMarginBillDetails(ctx context.Context, request *GetMarginBillDetailsRequest) ([]GetSpotBillDetailsForCurrencyResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := accounts + "/" + request.InstrumentID + "/" + ledger + encodedRequest
+ var resp []GetSpotBillDetailsForCurrencyResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginAccountSettings Get all information of the margin trading account,
+// including the maximum loan amount, interest rate, and maximum leverage.
+func (o *OKCoin) GetMarginAccountSettings(ctx context.Context, currency string) ([]GetMarginAccountSettingsResponse, error) {
+ requestURL := accounts + "/" + getMarketAvailability
+ if currency != "" {
+ requestURL = accounts + "/" + currency + "/" + getMarketAvailability
+ }
+ var resp []GetMarginAccountSettingsResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginLoanHistory Get loan history of the margin trading account.
+// Pagination is used here. before and after cursor arguments should not be confused with before and after in chronological time.
+// Most paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKCoin) GetMarginLoanHistory(ctx context.Context, request *GetMarginLoanHistoryRequest) ([]GetMarginLoanHistoryResponse, error) {
+ requestURL := accounts + "/" + getLoan
+ if len(request.InstrumentID) > 0 {
+ requestURL = accounts + "/" + request.InstrumentID + "/" + getLoan
+ }
+ var resp []GetMarginLoanHistoryResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// OpenMarginLoan Borrowing tokens in a margin trading account.
+func (o *OKCoin) OpenMarginLoan(ctx context.Context, request *OpenMarginLoanRequest) (*OpenMarginLoanResponse, error) {
+ requestURL := accounts + "/" + getLoan
+ var resp *OpenMarginLoanResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, marginTradingSubsection, requestURL, request, &resp, true)
+}
+
+// RepayMarginLoan Repaying tokens in a margin trading account.
+func (o *OKCoin) RepayMarginLoan(ctx context.Context, request *RepayMarginLoanRequest) (*RepayMarginLoanResponse, error) {
+ requestURL := accounts + "/" + getRepayment
+ var resp *RepayMarginLoanResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, marginTradingSubsection, requestURL, request, &resp, true)
+}
+
+// PlaceMarginOrder You can place an order only if you have enough funds. Once your order is placed, the amount will be put on hold.
+func (o *OKCoin) PlaceMarginOrder(ctx context.Context, request *PlaceOrderRequest) (*PlaceOrderResponse, error) {
+ var resp *PlaceOrderResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, marginTradingSubsection, orders, request, &resp, true)
+}
+
+// PlaceMultipleMarginOrders Place multiple orders for specific trading pairs (up to 4 trading pairs, maximum 4 orders each)
+func (o *OKCoin) PlaceMultipleMarginOrders(ctx context.Context, request []PlaceOrderRequest) (map[string][]PlaceOrderResponse, []error) {
+ currencyPairOrders := make(map[string]int)
+ resp := make(map[string][]PlaceOrderResponse)
+ for i := range request {
+ currencyPairOrders[request[i].InstrumentID]++
+ }
+ if len(currencyPairOrders) > 4 {
+ return resp, []error{errors.New("up to 4 trading pairs")}
+ }
+ for _, orderCount := range currencyPairOrders {
+ if orderCount > 4 {
+ return resp, []error{errors.New("maximum 4 orders for each pair")}
+ }
+ }
+
+ err := o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, marginTradingSubsection, batchOrders, request, &resp, true)
+ if err != nil {
+ return resp, []error{err}
+ }
+
+ var orderErrors []error
+ for currency, orderResponse := range resp {
+ for i := range orderResponse {
+ if !orderResponse[i].Result {
+ orderErrors = append(orderErrors, fmt.Errorf("order for currency %v failed to be placed", currency))
+ }
+ }
+ }
+
+ return resp, orderErrors
+}
+
+// CancelMarginOrder Cancelling an unfilled order.
+func (o *OKCoin) CancelMarginOrder(ctx context.Context, request *CancelSpotOrderRequest) (*CancelSpotOrderResponse, error) {
+ requestURL := cancelOrders + "/" + strconv.FormatInt(request.OrderID, 10)
+ var resp *CancelSpotOrderResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, marginTradingSubsection, requestURL, request, &resp, true)
+}
+
+// CancelMultipleMarginOrders Cancelling multiple unfilled orders.
+func (o *OKCoin) CancelMultipleMarginOrders(ctx context.Context, request *CancelMultipleSpotOrdersRequest) (map[string][]CancelMultipleSpotOrdersResponse, []error) {
+ resp := make(map[string][]CancelMultipleSpotOrdersResponse)
+ if len(request.OrderIDs) > 4 {
+ return resp, []error{errors.New("maximum 4 order cancellations for each pair")}
+ }
+
+ err := o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, marginTradingSubsection, cancelBatchOrders, []CancelMultipleSpotOrdersRequest{*request}, &resp, true)
+ if err != nil {
+ return resp, []error{err}
+ }
+
+ var orderErrors []error
+ for currency, orderResponse := range resp {
+ for i := range orderResponse {
+ if !orderResponse[i].Result {
+ orderErrors = append(orderErrors, fmt.Errorf("order %v for currency %v failed to be cancelled", orderResponse[i].OrderID, currency))
+ }
+ }
+ }
+
+ return resp, orderErrors
+}
+
+// GetMarginOrders List your orders. Cursor pagination is used. All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKCoin) GetMarginOrders(ctx context.Context, request *GetSpotOrdersRequest) ([]GetSpotOrderResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := orders + encodedRequest
+ var resp []GetSpotOrderResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginOpenOrders List all your current open orders. Cursor pagination is used. All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKCoin) GetMarginOpenOrders(ctx context.Context, request *GetSpotOpenOrdersRequest) ([]GetSpotOrderResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := pendingOrders + encodedRequest
+ var resp []GetSpotOrderResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginOrder Get order details by order ID.
+func (o *OKCoin) GetMarginOrder(ctx context.Context, request *GetSpotOrderRequest) (*GetSpotOrderResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := orders + "/" + request.OrderID + encodedRequest
+ var resp *GetSpotOrderResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginTradingSubsection, requestURL, request, &resp, true)
+}
+
+// GetMarginTransactionDetails Get details of the recent filled orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKCoin) GetMarginTransactionDetails(ctx context.Context, request *GetSpotTransactionDetailsRequest) ([]GetSpotTransactionDetailsResponse, error) {
+ encodedRequest, err := encodeRequest(request)
+ if err != nil {
+ return nil, err
+ }
+ requestURL := getSpotTransactionDetails + encodedRequest
+ var resp []GetSpotTransactionDetailsResponse
+ return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// encodeRequest Formats URL parameters, useful for optional parameters due to OKCoin signature check
+func encodeRequest(request interface{}) (string, error) {
+ v, err := query.Values(request)
+ if err != nil {
+ return "", err
+ }
+ resp := v.Encode()
+ if resp == "" {
+ return resp, nil
+ }
+ return "?" + resp, nil
+}
+
+// GetErrorCode returns an error code
+func (o *OKCoin) GetErrorCode(code interface{}) error {
+ var assertedCode string
+
+ switch d := code.(type) {
+ case float64:
+ assertedCode = strconv.FormatFloat(d, 'f', -1, 64)
+ case string:
+ assertedCode = d
+ default:
+ return errors.New("unusual type returned")
+ }
+
+ if i, ok := o.ErrorCodes[assertedCode]; ok {
+ return i
+ }
+ return errors.New("unable to find SPOT error code")
+}
+
+// SendHTTPRequest sends an authenticated http request to a desired
+// path with a JSON payload (of present)
+// URL arguments must be in the request path and not as url.URL values
+func (o *OKCoin) SendHTTPRequest(ctx context.Context, ep exchange.URL, httpMethod, requestType, requestPath string, data, result interface{}, authenticated bool) error {
+ endpoint, err := o.API.Endpoints.GetURL(ep)
+ if err != nil {
+ return err
+ }
+
+ var intermediary json.RawMessage
+ newRequest := func() (*request.Item, error) {
+ utcTime := time.Now().UTC().Format(time.RFC3339)
+ payload := []byte("")
+
+ if data != nil {
+ payload, err = json.Marshal(data)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ path := endpoint + requestType + okCoinAPIVersion + requestPath
+ headers := make(map[string]string)
+ headers["Content-Type"] = "application/json"
+ if authenticated {
+ var creds *account.Credentials
+ creds, err = o.GetCredentials(ctx)
+ if err != nil {
+ return nil, err
+ }
+ signPath := "/" + apiPath + requestType + okCoinAPIVersion + requestPath
+
+ var hmac []byte
+ hmac, err = crypto.GetHMAC(crypto.HashSHA256,
+ []byte(utcTime+httpMethod+signPath+string(payload)),
+ []byte(creds.Secret))
+ if err != nil {
+ return nil, err
+ }
+ headers["OK-ACCESS-KEY"] = creds.Key
+ headers["OK-ACCESS-SIGN"] = crypto.Base64Encode(hmac)
+ headers["OK-ACCESS-TIMESTAMP"] = utcTime
+ headers["OK-ACCESS-PASSPHRASE"] = creds.ClientID
+ }
+
+ return &request.Item{
+ Method: strings.ToUpper(httpMethod),
+ Path: path,
+ Headers: headers,
+ Body: bytes.NewBuffer(payload),
+ Result: &intermediary,
+ AuthRequest: authenticated,
+ Verbose: o.Verbose,
+ HTTPDebugging: o.HTTPDebugging,
+ HTTPRecording: o.HTTPRecording,
+ }, nil
+ }
+
+ err = o.SendPayload(ctx, request.Unset, newRequest)
+ if err != nil {
+ return err
+ }
+
+ type errCapFormat struct {
+ Error int64 `json:"error_code"`
+ ErrorMessage string `json:"error_message"`
+ Result bool `json:"result,string"`
+ }
+ errCap := errCapFormat{Result: true}
+
+ err = json.Unmarshal(intermediary, &errCap)
+ if err == nil {
+ if errCap.ErrorMessage != "" {
+ return fmt.Errorf("error: %v", errCap.ErrorMessage)
+ }
+ if errCap.Error > 0 {
+ return fmt.Errorf("sendHTTPRequest error - %s",
+ o.ErrorCodes[strconv.FormatInt(errCap.Error, 10)])
+ }
+ if !errCap.Result {
+ return errors.New("unspecified error occurred")
+ }
+ }
+
+ return json.Unmarshal(intermediary, result)
+}
+
+// GetFee returns an estimate of fee based on type of transaction
+func (o *OKCoin) GetFee(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) {
+ var fee float64
+ switch feeBuilder.FeeType {
+ case exchange.CryptocurrencyTradeFee:
+ fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker)
+ case exchange.CryptocurrencyWithdrawalFee:
+ withdrawFees, err := o.GetAccountWithdrawalFee(ctx, feeBuilder.FiatCurrency.String())
+ if err != nil {
+ return -1, err
+ }
+ for _, withdrawFee := range withdrawFees {
+ if withdrawFee.Currency == feeBuilder.FiatCurrency.String() {
+ fee = withdrawFee.MinFee
+ break
+ }
+ }
+ case exchange.OfflineTradeFee:
+ fee = getOfflineTradeFee(feeBuilder.PurchasePrice, feeBuilder.Amount)
+ }
+ if fee < 0 {
+ fee = 0
+ }
+
+ return fee, nil
+}
+
+// getOfflineTradeFee calculates the worst case-scenario trading fee
+func getOfflineTradeFee(price, amount float64) float64 {
+ return 0.0015 * price * amount
+}
+
+func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) {
+ // TODO volume based fees
+ if isMaker {
+ fee = 0.0005
+ } else {
+ fee = 0.0015
+ }
+ return fee * amount * purchasePrice
}
diff --git a/exchanges/okcoin/okcoin_test.go b/exchanges/okcoin/okcoin_test.go
index ca4ba3df..c107e043 100644
--- a/exchanges/okcoin/okcoin_test.go
+++ b/exchanges/okcoin/okcoin_test.go
@@ -5,14 +5,12 @@ import (
"encoding/json"
"errors"
"log"
- "net/http"
"os"
"strings"
"sync"
"testing"
"time"
- "github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/core"
@@ -20,10 +18,8 @@ import (
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
- "github.com/thrasher-corp/gocryptotrader/exchanges/okgroup"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
- "github.com/thrasher-corp/gocryptotrader/exchanges/stream"
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
)
@@ -32,37 +28,26 @@ const (
apiKey = ""
apiSecret = ""
passphrase = ""
- OKGroupExchange = "OKCOIN International"
canManipulateRealOrders = false
)
-var o OKCoin
-var testSetupRan bool
-var spotCurrency = currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-").Lower().String()
-var websocketEnabled bool
+var (
+ o OKCoin
+ spotCurrency = currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-")
+ spotCurrencyLowerStr = spotCurrency.Lower().String()
+ spotCurrencyUpperStr = spotCurrency.Upper().String()
+)
-// TestSetRealOrderDefaults Sets test defaults when test can impact real money/orders
-func TestSetRealOrderDefaults(t *testing.T) {
- if !areTestAPIKeysSet() || !canManipulateRealOrders {
- t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
- }
-}
-
-// TestSetup Sets defaults for test environment
func TestMain(m *testing.M) {
o.SetDefaults()
- o.ExchangeName = OKGroupExchange
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatal("Okcoin load config error", err)
}
- okcoinConfig, err := cfg.GetExchangeConfig(OKGroupExchange)
+ okcoinConfig, err := cfg.GetExchangeConfig(o.Name)
if err != nil {
- log.Fatalf("%v Setup() init error", OKGroupExchange)
- }
- if okcoinConfig.Features.Enabled.Websocket {
- websocketEnabled = true
+ log.Fatalf("%v Setup() init error", o.Name)
}
okcoinConfig.API.AuthenticatedSupport = true
@@ -75,7 +60,6 @@ func TestMain(m *testing.M) {
if err != nil {
log.Fatal("OKCoin setup error", err)
}
- testSetupRan = true
os.Exit(m.Run())
}
@@ -97,24 +81,19 @@ func TestStart(t *testing.T) {
testWg.Wait()
}
-func testStandardErrorHandling(t *testing.T, err error) {
- t.Helper()
+func TestGetAccountCurrencies(t *testing.T) {
+ t.Parallel()
+ _, err := o.GetAccountCurrencies(context.Background())
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
- t.Errorf("Encountered error: %v", err)
+ t.Error(err)
}
}
-// TestGetAccountCurrencies API endpoint test
-func TestGetAccountCurrencies(t *testing.T) {
- _, err := o.GetAccountCurrencies(context.Background())
- testStandardErrorHandling(t, err)
-}
-
-// TestGetAccountWalletInformation API endpoint test
func TestGetAccountWalletInformation(t *testing.T) {
+ t.Parallel()
resp, err := o.GetAccountWalletInformation(context.Background(), "")
if areTestAPIKeysSet() {
if err != nil {
@@ -128,8 +107,8 @@ func TestGetAccountWalletInformation(t *testing.T) {
}
}
-// TestGetAccountWalletInformationForCurrency API endpoint test
func TestGetAccountWalletInformationForCurrency(t *testing.T) {
+ t.Parallel()
resp, err := o.GetAccountWalletInformation(context.Background(),
currency.BTC.String())
if areTestAPIKeysSet() {
@@ -144,23 +123,32 @@ func TestGetAccountWalletInformationForCurrency(t *testing.T) {
}
}
-// TestTransferAccountFunds API endpoint test
func TestTransferAccountFunds(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.TransferAccountFundsRequest{
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &TransferAccountFundsRequest{
Amount: -10,
Currency: currency.BTC.String(),
From: 6,
To: 1,
}
_, err := o.TransferAccountFunds(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestBaseWithdraw API endpoint test
func TestAccountWithdrawRequest(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.AccountWithdrawRequest{
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &AccountWithdrawRequest{
Amount: -10,
Currency: currency.BTC.String(),
TradePwd: "1234",
@@ -169,172 +157,232 @@ func TestAccountWithdrawRequest(t *testing.T) {
Fee: 1,
}
_, err := o.AccountWithdraw(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetAccountWithdrawalFee API endpoint test
func TestGetAccountWithdrawalFee(t *testing.T) {
- resp, err := o.GetAccountWithdrawalFee(context.Background(), "")
+ t.Parallel()
+ _, err := o.GetAccountWithdrawalFee(context.Background(), "")
if areTestAPIKeysSet() {
if err != nil {
t.Error(err)
}
- if len(resp) == 0 {
- t.Error("Expected fees")
- }
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
}
-// TestGetWithdrawalFeeForCurrency API endpoint test
func TestGetAccountWithdrawalFeeForCurrency(t *testing.T) {
- resp, err := o.GetAccountWithdrawalFee(context.Background(), currency.BTC.String())
+ t.Parallel()
+ _, err := o.GetAccountWithdrawalFee(context.Background(), currency.BTC.String())
if areTestAPIKeysSet() {
if err != nil {
t.Error(err)
}
- if len(resp) != 1 {
- t.Error("Expected fee for one currency")
- }
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
}
-// TestGetAccountWithdrawalHistory API endpoint test
func TestGetAccountWithdrawalHistory(t *testing.T) {
+ t.Parallel()
_, err := o.GetAccountWithdrawalHistory(context.Background(), "")
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetAccountWithdrawalHistoryForCurrency API endpoint test
func TestGetAccountWithdrawalHistoryForCurrency(t *testing.T) {
+ t.Parallel()
_, err := o.GetAccountWithdrawalHistory(context.Background(), currency.BTC.String())
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetAccountBillDetails API endpoint test
func TestGetAccountBillDetails(t *testing.T) {
+ t.Parallel()
_, err := o.GetAccountBillDetails(context.Background(),
- okgroup.GetAccountBillDetailsRequest{})
- testStandardErrorHandling(t, err)
+ &GetAccountBillDetailsRequest{})
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetAccountDepositAddressForCurrency API endpoint test
func TestGetAccountDepositAddressForCurrency(t *testing.T) {
+ t.Parallel()
_, err := o.GetAccountDepositAddressForCurrency(context.Background(), currency.BTC.String())
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetAccountDepositHistory API endpoint test
func TestGetAccountDepositHistory(t *testing.T) {
+ t.Parallel()
_, err := o.GetAccountDepositHistory(context.Background(), "")
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetAccountDepositHistoryForCurrency API endpoint test
func TestGetAccountDepositHistoryForCurrency(t *testing.T) {
+ t.Parallel()
_, err := o.GetAccountDepositHistory(context.Background(), currency.BTC.String())
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetSpotTradingAccounts API endpoint test
func TestGetSpotTradingAccounts(t *testing.T) {
+ t.Parallel()
_, err := o.GetSpotTradingAccounts(context.Background())
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetSpotTradingAccountsForCurrency API endpoint test
func TestGetSpotTradingAccountsForCurrency(t *testing.T) {
+ t.Parallel()
_, err := o.GetSpotTradingAccountForCurrency(context.Background(), currency.BTC.String())
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetSpotBillDetailsForCurrency API endpoint test
func TestGetSpotBillDetailsForCurrency(t *testing.T) {
- request := okgroup.GetSpotBillDetailsForCurrencyRequest{
+ t.Parallel()
+ request := &GetSpotBillDetailsForCurrencyRequest{
Currency: currency.BTC.String(),
Limit: 100,
}
_, err := o.GetSpotBillDetailsForCurrency(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetSpotBillDetailsForCurrencyBadLimit API logic test
func TestGetSpotBillDetailsForCurrencyBadLimit(t *testing.T) {
- request := okgroup.GetSpotBillDetailsForCurrencyRequest{
+ t.Parallel()
+ request := &GetSpotBillDetailsForCurrencyRequest{
Currency: currency.BTC.String(),
Limit: -1,
}
_, err := o.GetSpotBillDetailsForCurrency(context.Background(), request)
- if areTestAPIKeysSet() && err == nil {
- t.Errorf("Expecting an error when invalid request sent")
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
}
}
-// TestPlaceSpotOrderLimit API endpoint test
func TestPlaceSpotOrderLimit(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.PlaceOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &PlaceOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Price: "-100",
Size: "100",
}
- _, err := o.PlaceSpotOrder(context.Background(), &request)
- testStandardErrorHandling(t, err)
+ _, err := o.PlaceSpotOrder(context.Background(), request)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestPlaceSpotOrderMarket API endpoint test
func TestPlaceSpotOrderMarket(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.PlaceOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &PlaceOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
Type: order.Market.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Notional: "100",
}
- _, err := o.PlaceSpotOrder(context.Background(), &request)
- testStandardErrorHandling(t, err)
+ _, err := o.PlaceSpotOrder(context.Background(), request)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestPlaceMultipleSpotOrders API endpoint test
func TestPlaceMultipleSpotOrders(t *testing.T) {
- TestSetRealOrderDefaults(t)
- ord := okgroup.PlaceOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ ord := PlaceOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Price: "1",
}
- request := []okgroup.PlaceOrderRequest{
+ request := []PlaceOrderRequest{
ord,
}
_, errs := o.PlaceMultipleSpotOrders(context.Background(), request)
if len(errs) > 0 {
- testStandardErrorHandling(t, errs[0])
+ t.Error(errs)
}
}
-// TestPlaceMultipleSpotOrdersOverCurrencyLimits API logic test
func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
- ord := okgroup.PlaceOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ ord := PlaceOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Price: "1",
}
- request := []okgroup.PlaceOrderRequest{
+ request := []PlaceOrderRequest{
ord,
ord,
ord,
@@ -348,25 +396,25 @@ func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
}
}
-// TestPlaceMultipleSpotOrdersOverPairLimits API logic test
func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
- ord := okgroup.PlaceOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ ord := PlaceOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
Size: "-100",
Price: "1",
}
- request := []okgroup.PlaceOrderRequest{
+ request := []PlaceOrderRequest{
ord,
}
pairs := currency.Pairs{
- currency.NewPair(currency.LTC, currency.USDT),
- currency.NewPair(currency.ETH, currency.USDT),
- currency.NewPair(currency.BCH, currency.USDT),
- currency.NewPair(currency.XMR, currency.USDT),
+ currency.NewPair(currency.LTC, currency.USD),
+ currency.NewPair(currency.ETH, currency.USD),
+ currency.NewPair(currency.BCH, currency.USD),
+ currency.NewPair(currency.XMR, currency.USD),
}
for x := range pairs {
@@ -380,42 +428,58 @@ func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
}
}
-// TestCancelSpotOrder API endpoint test
func TestCancelSpotOrder(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.CancelSpotOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &CancelSpotOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
OrderID: 1234,
}
_, err := o.CancelSpotOrder(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestCancelMultipleSpotOrders API endpoint test
func TestCancelMultipleSpotOrders(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.CancelMultipleSpotOrdersRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrencyLowerStr,
OrderIDs: []int64{1, 2, 3, 4},
}
cancellations, err := o.CancelMultipleSpotOrders(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
for _, cancellationsPerCurrency := range cancellations {
for _, cancellation := range cancellationsPerCurrency {
- if !cancellation.Result {
+ if cancellation.Error != nil {
t.Error(cancellation.Error)
}
}
}
}
-// TestCancelMultipleSpotOrdersOverCurrencyLimits API logic test
func TestCancelMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.CancelMultipleSpotOrdersRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrencyLowerStr,
OrderIDs: []int64{1, 2, 3, 4, 5},
}
@@ -425,72 +489,92 @@ func TestCancelMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
}
}
-// TestGetSpotOrders API endpoint test
func TestGetSpotOrders(t *testing.T) {
- request := okgroup.GetSpotOrdersRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ request := &GetSpotOrdersRequest{
+ InstrumentID: spotCurrencyLowerStr,
Status: "all",
}
_, err := o.GetSpotOrders(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetSpotOpenOrders API endpoint test
func TestGetSpotOpenOrders(t *testing.T) {
- request := okgroup.GetSpotOpenOrdersRequest{}
+ t.Parallel()
+ request := &GetSpotOpenOrdersRequest{}
_, err := o.GetSpotOpenOrders(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetSpotOrder API endpoint test
func TestGetSpotOrder(t *testing.T) {
- request := okgroup.GetSpotOrderRequest{
- OrderID: "-1234",
+ t.Parallel()
+ request := &GetSpotOrderRequest{
+ OrderID: "1234",
InstrumentID: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-").Upper().String(),
}
_, err := o.GetSpotOrder(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetSpotTransactionDetails API endpoint test
func TestGetSpotTransactionDetails(t *testing.T) {
- request := okgroup.GetSpotTransactionDetailsRequest{
+ t.Parallel()
+ request := &GetSpotTransactionDetailsRequest{
OrderID: 1234,
- InstrumentID: spotCurrency,
+ InstrumentID: spotCurrencyLowerStr,
}
_, err := o.GetSpotTransactionDetails(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetSpotTokenPairDetails API endpoint test
func TestGetSpotTokenPairDetails(t *testing.T) {
+ t.Parallel()
_, err := o.GetSpotTokenPairDetails(context.Background())
if err != nil {
t.Error(err)
}
}
-// TestGetSpotAllTokenPairsInformation API endpoint test
func TestGetSpotAllTokenPairsInformation(t *testing.T) {
+ t.Parallel()
_, err := o.GetSpotAllTokenPairsInformation(context.Background())
if err != nil {
t.Error(err)
}
}
-// TestGetSpotAllTokenPairsInformationForCurrency API endpoint test
func TestGetSpotAllTokenPairsInformationForCurrency(t *testing.T) {
+ t.Parallel()
_, err := o.GetSpotAllTokenPairsInformationForCurrency(context.Background(),
- spotCurrency)
+ spotCurrencyLowerStr)
if err != nil {
t.Error(err)
}
}
-// TestGetSpotFilledOrdersInformation API endpoint test
func TestGetSpotFilledOrdersInformation(t *testing.T) {
- request := okgroup.GetSpotFilledOrdersInformationRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ request := &GetSpotFilledOrdersInformationRequest{
+ InstrumentID: spotCurrencyLowerStr,
}
_, err := o.GetSpotFilledOrdersInformation(context.Background(), request)
if err != nil {
@@ -498,85 +582,133 @@ func TestGetSpotFilledOrdersInformation(t *testing.T) {
}
}
-// TestGetSpotMarketData API endpoint test
func TestGetSpotMarketData(t *testing.T) {
- request := &okgroup.GetMarketDataRequest{
+ t.Parallel()
+ _, err := o.GetMarketData(context.Background(), &GetMarketDataRequest{
Asset: asset.Spot,
- InstrumentID: spotCurrency,
+ InstrumentID: spotCurrencyLowerStr,
Granularity: "604800",
- }
- _, err := o.GetMarketData(context.Background(), request)
+ })
if err != nil {
t.Error(err)
}
+ _, err = o.GetMarketData(context.Background(), &GetMarketDataRequest{
+ Asset: asset.Binary,
+ InstrumentID: spotCurrencyLowerStr,
+ Granularity: "604800",
+ })
+ if !errors.Is(err, asset.ErrNotSupported) {
+ t.Error(err)
+ }
}
-// TestGetMarginTradingAccounts API endpoint test
func TestGetMarginTradingAccounts(t *testing.T) {
+ t.Parallel()
_, err := o.GetMarginTradingAccounts(context.Background())
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetMarginTradingAccountsForCurrency API endpoint test
func TestGetMarginTradingAccountsForCurrency(t *testing.T) {
- _, err := o.GetMarginTradingAccountsForCurrency(context.Background(), spotCurrency)
- testStandardErrorHandling(t, err)
+ t.Parallel()
+ _, err := o.GetMarginTradingAccountsForCurrency(context.Background(), spotCurrencyLowerStr)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetMarginBillDetails API endpoint test
func TestGetMarginBillDetails(t *testing.T) {
- request := okgroup.GetMarginBillDetailsRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ request := &GetMarginBillDetailsRequest{
+ InstrumentID: spotCurrencyLowerStr,
Limit: 100,
}
_, err := o.GetMarginBillDetails(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetMarginAccountSettings API endpoint test
func TestGetMarginAccountSettings(t *testing.T) {
+ t.Parallel()
_, err := o.GetMarginAccountSettings(context.Background(), "")
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetMarginAccountSettingsForCurrency API endpoint test
func TestGetMarginAccountSettingsForCurrency(t *testing.T) {
- _, err := o.GetMarginAccountSettings(context.Background(), spotCurrency)
- testStandardErrorHandling(t, err)
+ t.Parallel()
+ _, err := o.GetMarginAccountSettings(context.Background(), "")
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestOpenMarginLoan API endpoint test
func TestOpenMarginLoan(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.OpenMarginLoanRequest{
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &OpenMarginLoanRequest{
Amount: -100,
- InstrumentID: spotCurrency,
+ InstrumentID: spotCurrencyLowerStr,
QuoteCurrency: currency.USD.String(),
}
_, err := o.OpenMarginLoan(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestRepayMarginLoan API endpoint test
func TestRepayMarginLoan(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.RepayMarginLoanRequest{
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &RepayMarginLoanRequest{
Amount: -100,
- InstrumentID: spotCurrency,
+ InstrumentID: spotCurrencyLowerStr,
QuoteCurrency: currency.USD.String(),
BorrowID: 1,
}
_, err := o.RepayMarginLoan(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestPlaceMarginOrderLimit API endpoint test
func TestPlaceMarginOrderLimit(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.PlaceOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &PlaceOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "2",
@@ -584,15 +716,22 @@ func TestPlaceMarginOrderLimit(t *testing.T) {
Size: "100",
}
- _, err := o.PlaceMarginOrder(context.Background(), &request)
- testStandardErrorHandling(t, err)
+ _, err := o.PlaceMarginOrder(context.Background(), request)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestPlaceMarginOrderMarket API endpoint test
func TestPlaceMarginOrderMarket(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.PlaceOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &PlaceOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
Type: order.Market.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "2",
@@ -600,36 +739,43 @@ func TestPlaceMarginOrderMarket(t *testing.T) {
Notional: "100",
}
- _, err := o.PlaceMarginOrder(context.Background(), &request)
- testStandardErrorHandling(t, err)
+ _, err := o.PlaceMarginOrder(context.Background(), request)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestPlaceMultipleMarginOrders API endpoint test
func TestPlaceMultipleMarginOrders(t *testing.T) {
- TestSetRealOrderDefaults(t)
- ord := okgroup.PlaceOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ ord := PlaceOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
- MarginTrading: "1",
+ MarginTrading: "2",
Size: "-100",
Notional: "100",
}
- request := []okgroup.PlaceOrderRequest{
+ request := []PlaceOrderRequest{
ord,
}
_, errs := o.PlaceMultipleMarginOrders(context.Background(), request)
if len(errs) > 0 {
- testStandardErrorHandling(t, errs[0])
+ t.Error(errs)
}
}
-// TestPlaceMultipleMarginOrdersOverCurrencyLimits API logic test
func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
- ord := okgroup.PlaceOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ ord := PlaceOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
MarginTrading: "1",
@@ -637,7 +783,7 @@ func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
Notional: "100",
}
- request := []okgroup.PlaceOrderRequest{
+ request := []PlaceOrderRequest{
ord,
ord,
ord,
@@ -651,26 +797,26 @@ func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
}
}
-// TestPlaceMultipleMarginOrdersOverPairLimits API logic test
func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
- ord := okgroup.PlaceOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ ord := PlaceOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
- MarginTrading: "1",
+ MarginTrading: "2",
Size: "-100",
Notional: "100",
}
- request := []okgroup.PlaceOrderRequest{
+ request := []PlaceOrderRequest{
ord,
}
pairs := currency.Pairs{
- currency.NewPair(currency.LTC, currency.USDT),
- currency.NewPair(currency.ETH, currency.USDT),
- currency.NewPair(currency.BCH, currency.USDT),
- currency.NewPair(currency.XMR, currency.USDT),
+ currency.NewPair(currency.LTC, currency.USD),
+ currency.NewPair(currency.ETH, currency.USD),
+ currency.NewPair(currency.BCH, currency.USD),
+ currency.NewPair(currency.XMR, currency.USD),
}
for x := range pairs {
@@ -684,37 +830,48 @@ func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
}
}
-// TestCancelMarginOrder API endpoint test
func TestCancelMarginOrder(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.CancelSpotOrderRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &CancelSpotOrderRequest{
+ InstrumentID: spotCurrencyLowerStr,
OrderID: 1234,
}
_, err := o.CancelMarginOrder(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestCancelMultipleMarginOrders API endpoint test
func TestCancelMultipleMarginOrders(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.CancelMultipleSpotOrdersRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrencyLowerStr,
OrderIDs: []int64{1, 2, 3, 4},
}
_, errs := o.CancelMultipleMarginOrders(context.Background(), request)
if len(errs) > 0 {
- testStandardErrorHandling(t, errs[0])
+ t.Error(errs)
}
}
-// TestCancelMultipleMarginOrdersOverCurrencyLimits API logic test
func TestCancelMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
- TestSetRealOrderDefaults(t)
- request := okgroup.CancelMultipleSpotOrdersRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+ request := &CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrencyLowerStr,
OrderIDs: []int64{1, 2, 3, 4, 5},
}
@@ -724,96 +881,77 @@ func TestCancelMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
}
}
-// TestGetMarginOrders API endpoint test
func TestGetMarginOrders(t *testing.T) {
- request := okgroup.GetSpotOrdersRequest{
- InstrumentID: spotCurrency,
+ t.Parallel()
+ request := &GetSpotOrdersRequest{
+ InstrumentID: spotCurrencyLowerStr,
Status: "all",
}
_, err := o.GetMarginOrders(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetMarginOpenOrders API endpoint test
func TestGetMarginOpenOrders(t *testing.T) {
- request := okgroup.GetSpotOpenOrdersRequest{}
+ t.Parallel()
+ request := &GetSpotOpenOrdersRequest{}
_, err := o.GetMarginOpenOrders(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetMarginOrder API endpoint test
func TestGetMarginOrder(t *testing.T) {
- request := okgroup.GetSpotOrderRequest{
+ t.Parallel()
+ request := &GetSpotOrderRequest{
OrderID: "1234",
InstrumentID: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-").Upper().String(),
}
_, err := o.GetMarginOrder(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestGetMarginTransactionDetails API endpoint test
func TestGetMarginTransactionDetails(t *testing.T) {
- request := okgroup.GetSpotTransactionDetailsRequest{
+ t.Parallel()
+ request := &GetSpotTransactionDetailsRequest{
OrderID: 1234,
- InstrumentID: spotCurrency,
+ InstrumentID: spotCurrencyLowerStr,
}
_, err := o.GetMarginTransactionDetails(context.Background(), request)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
// Websocket tests ----------------------------------------------------------------------------------------------
-// TestSendWsMessages Logic test
-// Attempts to subscribe to a channel that doesn't exist
-// Will log in if credentials are present
-func TestSendWsMessages(t *testing.T) {
- if !o.Websocket.IsEnabled() && !o.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
- t.Skip(stream.WebsocketNotEnabled)
- }
- var ok bool
- var dialer websocket.Dialer
- err := o.Websocket.Conn.Dial(&dialer, http.Header{})
- if err != nil {
- t.Fatal(err)
- }
- go o.WsReadData()
- subscriptions := []stream.ChannelSubscription{
- {
- Channel: "badChannel",
- },
- }
- err = o.Subscribe(subscriptions)
- if err != nil {
- t.Fatal(err)
- }
- response := <-o.Websocket.DataHandler
- if err, ok = response.(error); ok && err != nil {
- if !strings.Contains(err.Error(), subscriptions[0].Channel) {
- t.Error("Expecting OKCoin error - 30040 message: Channel badChannel doesn't exist")
- }
- }
- err = o.WsLogin(context.Background())
- if err != nil {
- t.Error(err)
- }
- responseTwo := <-o.Websocket.DataHandler
- if err, ok := responseTwo.(error); ok && err != nil {
- t.Error(err)
- }
-}
-
-// TestGetAssetTypeFromTableName logic test
func TestGetAssetTypeFromTableName(t *testing.T) {
- str := "spot/candle300s:BTC-USDT"
+ t.Parallel()
+ str := "spot/candle300s:BTC-USD"
spot := o.GetAssetTypeFromTableName(str)
if !strings.EqualFold(spot.String(), asset.Spot.String()) {
t.Errorf("Error, expected 'SPOT', received: '%v'", spot)
}
}
-// TestGetWsChannelWithoutOrderType logic test
func TestGetWsChannelWithoutOrderType(t *testing.T) {
- str := "spot/depth5:BTC-USDT"
+ t.Parallel()
+ str := "spot/depth5:BTC-USD"
expected := "depth5"
resp := o.GetWsChannelWithoutOrderType(str)
if resp != expected {
@@ -832,13 +970,10 @@ func TestGetWsChannelWithoutOrderType(t *testing.T) {
}
}
-// TestOrderBookUpdateChecksumCalculator logic test
func TestOrderBookUpdateChecksumCalculator(t *testing.T) {
- if !websocketEnabled {
- t.Skip("Websocket not enabled, skipping")
- }
- original := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"BTC-USDT","asks":[["3864.6786","0.145",1],["3864.7682","0.005",1],["3864.9851","0.57",1],["3864.9852","0.30137754",1],["3864.9986","2.81818419",1],["3864.9995","0.002",1],["3865","0.0597",1],["3865.0309","0.4",1],["3865.1995","0.004",1],["3865.3995","0.004",1],["3865.5995","0.004",1],["3865.7995","0.004",1],["3865.9995","0.004",1],["3866.0961","0.25865886",1],["3866.1995","0.004",1],["3866.3995","0.004",1],["3866.4004","0.3243",2],["3866.5995","0.004",1],["3866.7633","0.44247086",1],["3866.7995","0.004",1],["3866.9197","0.511",1],["3867.256","0.51716256",1],["3867.3951","0.02588112",1],["3867.4014","0.025",1],["3867.4566","0.02499999",1],["3867.4675","4.01155057",5],["3867.5515","1.1",1],["3867.6113","0.009",1],["3867.7349","0.026",1],["3867.7781","0.03738652",1],["3867.9163","0.0521",1],["3868.0381","0.34354941",1],["3868.0436","0.051",1],["3868.0657","0.90552172",3],["3868.1819","0.03863346",1],["3868.2013","0.194",1],["3868.346","0.051",1],["3868.3863","0.01155",1],["3868.7716","0.009",1],["3868.947","0.025",1],["3868.98","0.001",1],["3869.0764","1.03487931",1],["3869.2773","0.07724578",1],["3869.4039","0.025",1],["3869.4068","1.03",1],["3869.7068","2.06976398",1],["3870","0.5",1],["3870.0465","0.01",1],["3870.7042","0.02099651",1],["3870.9451","2.07047375",1],["3871.5254","1.2",1],["3871.5596","0.001",1],["3871.6605","0.01035032",1],["3871.7179","2.07047375",1],["3871.8816","0.51751625",1],["3872.1","0.75",1],["3872.2464","0.0646",1],["3872.3747","0.283",1],["3872.4039","0.2",1],["3872.7655","0.23179307",1],["3872.8005","2.06976398",1],["3873.1509","2",1],["3873.3215","0.26",1],["3874.1392","0.001",1],["3874.1487","3.88224364",4],["3874.1685","1.8",1],["3874.5571","0.08974762",1],["3874.734","2.06976398",1],["3874.99","0.3",1],["3875","1.001",2],["3875.0041","1.03505051",1],["3875.45","0.3",1],["3875.4766","0.15",1],["3875.7057","0.51751625",1],["3876","0.001",1],["3876.68","0.3",1],["3876.7188","0.001",1],["3877","0.75",1],["3877.31","0.035",1],["3877.38","0.3",1],["3877.7","0.3",1],["3877.88","0.3",1],["3878.0364","0.34770122",1],["3878.4525","0.48579748",1],["3878.4955","0.02812511",1],["3878.8855","0.00258579",1],["3878.9605","0.895",1],["3879","0.001",1],["3879.2984","0.002",2],["3879.432","0.001",1],["3879.6313","6",1],["3879.9999","0.002",2],["3880","1.25132834",5],["3880.2526","0.04075162",1],["3880.7145","0.0647",1],["3881.2469","1.883",1],["3881.878","0.002",2],["3884.4576","0.002",2],["3885","0.002",2],["3885.2233","0.28304103",1],["3885.7416","18",1],["3886","0.001",1],["3886.1554","5.4",1],["3887","0.001",1],["3887.0372","0.002",2],["3887.2559","0.05214011",1],["3887.9238","0.0019",1],["3888","0.15810538",4],["3889","0.001",1],["3889.5175","0.50510653",1],["3889.6168","0.002",2],["3889.9999","0.001",1],["3890","2.34968109",4],["3890.5222","0.00257806",1],["3891.2659","5",1],["3891.9999","0.00893897",1],["3892.1964","0.002",2],["3892.4358","0.0176",1],["3893.1388","1.4279",1],["3894","0.0026321",1],["3894.776","0.001",1],["3895","1.501",2],["3895.379","0.25881288",1],["3897","0.05",1],["3897.3556","0.001",1],["3897.8432","0.73708079",1],["3898","3.31353018",7],["3898.4462","4.757",1],["3898.6","0.47159638",1],["3898.8769","0.0129",1],["3899","6",2],["3899.6516","0.025",1],["3899.9352","0.001",1],["3899.9999","0.013",2],["3900","22.37447743",24],["3900.9999","0.07763916",1],["3901","0.10192487",1],["3902.1937","0.00257034",1],["3902.3991","1.5532141",1],["3902.5148","0.001",1],["3904","1.49331984",1],["3904.9999","0.95905447",1],["3905","0.501",2],["3905.0944","0.001",1],["3905.61","0.099",1],["3905.6801","0.54343686",1],["3906.2901","0.0258",1],["3907.674","0.001",1],["3907.85","1.35778084",1],["3908","0.03846153",1],["3908.23","1.95189531",1],["3908.906","0.03148978",1],["3909","0.001",1],["3909.9999","0.01398721",2],["3910","0.016",2],["3910.2536","0.001",1],["3912.5406","0.88270517",1],["3912.8332","0.001",1],["3913","1.2640608",1],["3913.87","1.69114184",1],["3913.9003","0.00256266",1],["3914","1.21766411",1],["3915","0.001",1],["3915.4128","0.001",1],["3915.7425","6.848",1],["3916","0.0050949",1],["3917.36","1.28658296",1],["3917.9924","0.001",1],["3919","0.001",1],["3919.9999","0.001",1],["3920","1.21171832",3],["3920.0002","0.20217038",1],["3920.572","0.001",1],["3921","0.128",1],["3923.0756","0.00148064",1],["3923.1516","0.001",1],["3923.86","1.38831714",1],["3925","0.01867801",2],["3925.642","0.00255499",1],["3925.7312","0.001",1],["3926","0.04290757",1],["3927","0.023",1],["3927.3175","0.01212865",1],["3927.65","1.51375612",1],["3928","0.5",1],["3928.3108","0.001",1],["3929","0.001",1],["3929.9999","0.01519338",2],["3930","0.0174985",3],["3930.21","1.49335799",1],["3930.8904","0.001",1],["3932.2999","0.01953",1],["3932.8962","7.96",1],["3933.0387","11.808",1],["3933.47","0.001",1],["3934","1.40839932",1],["3935","0.001",1],["3936.8","0.62879518",1],["3937.23","1.56977841",1],["3937.4189","0.00254735",1]],"bids":[["3864.5217","0.00540709",1],["3864.5216","0.14068758",2],["3864.2275","0.01033576",1],["3864.0989","0.00825047",1],["3864.0273","0.38",1],["3864.0272","0.4",1],["3863.9957","0.01083539",1],["3863.9184","0.01653723",1],["3863.8282","0.25588165",1],["3863.8153","0.154",1],["3863.7791","1.14122492",1],["3863.6866","0.01733662",1],["3863.6093","0.02645958",1],["3863.3775","0.02773862",1],["3863.0297","0.513",1],["3863.0286","1.1028564",2],["3862.8489","0.01",1],["3862.5972","0.01890179",1],["3862.3431","0.01152944",1],["3862.313","0.009",1],["3862.2445","0.90551002",3],["3862.0734","0.014",1],["3862.0539","0.64976067",1],["3861.8586","0.025",1],["3861.7888","0.025",1],["3861.7673","0.008",1],["3861.5785","0.01",1],["3861.3895","0.005",1],["3861.3338","0.25875855",1],["3861.161","0.01",1],["3861.1111","0.03863352",1],["3861.0732","0.51703882",1],["3860.9116","0.17754895",1],["3860.75","0.19",1],["3860.6554","0.015",1],["3860.6172","0.005",1],["3860.6088","0.008",1],["3860.4724","0.12940042",1],["3860.4424","0.25880084",1],["3860.42","0.01",1],["3860.3725","0.51760102",1],["3859.8449","0.005",1],["3859.8285","0.03738652",1],["3859.7638","0.07726703",1],["3859.4502","0.008",1],["3859.3772","0.05173471",1],["3859.3409","0.194",1],["3859","5",1],["3858.827","0.0521",1],["3858.8208","0.001",1],["3858.679","0.26",1],["3858.4814","0.07477305",1],["3858.1669","1.03503422",1],["3857.6005","0.006",1],["3857.4005","0.004",1],["3857.2005","0.004",1],["3857.1871","1.218",1],["3857.0005","0.004",1],["3856.8135","0.0646",1],["3856.8005","0.004",1],["3856.2412","0.001",1],["3856.2349","1.03503422",1],["3856.0197","0.01037339",1],["3855.8781","0.23178117",1],["3855.8005","0.004",1],["3855.7165","0.00259355",1],["3855.4858","0.25875855",1],["3854.4584","0.01",1],["3853.6616","0.001",1],["3853.1373","0.92",1],["3852.5072","0.48599702",1],["3851.3926","0.13008333",1],["3851.082","0.001",1],["3850.9317","2",1],["3850.6359","0.34770165",1],["3850.2058","0.51751624",1],["3850.0823","0.15",1],["3850.0042","0.5175171",1],["3850","0.001",1],["3849.6325","1.8",1],["3849.41","0.3",1],["3848.9686","1.85",1],["3848.7426","0.18511466",1],["3848.52","0.3",1],["3848.5024","0.001",1],["3848.42","0.3",1],["3848.1618","2.204",1],["3847.77","0.3",1],["3847.48","0.3",1],["3847.3581","2.05",1],["3846.8259","0.0646",1],["3846.59","0.3",1],["3846.49","0.3",1],["3845.9228","0.001",1],["3844.184","0.00260133",1],["3844.0092","6.3",1],["3843.3432","0.001",1],["3841","0.06300963",1],["3840.7636","0.001",1],["3840","0.201",3],["3839.7681","18",1],["3839.5328","0.05214011",1],["3838.184","0.001",1],["3837.2344","0.27589557",1],["3836.6479","5.2",1],["3836","2.37196773",3],["3835.6044","0.001",1],["3833.6053","0.25873556",1],["3833.0248","0.001",1],["3833","0.8726502",1],["3832.6859","0.00260913",1],["3832","0.007",1],["3831.637","6",1],["3831.0602","0.001",1],["3830.4452","0.001",1],["3830","0.20375718",4],["3829.7125","0.07833486",1],["3829.6283","0.3519681",1],["3829","0.0039261",1],["3827.8656","0.001",1],["3826.0001","0.53251232",1],["3826","0.0509",1],["3825.7834","0.00698562",1],["3825.286","0.001",1],["3823.0001","0.03010127",1],["3822.8014","0.00261588",1],["3822.7064","0.001",1],["3822.2","1",1],["3822.1121","0.35994101",1],["3821.2222","0.00261696",1],["3821","0.001",1],["3820.1268","0.001",1],["3820","1.12992803",4],["3819","0.01331195",2],["3817.5472","0.001",1],["3816","1.13807184",2],["3815.8343","0.32463428",1],["3815.7834","0.00525295",1],["3815","28.99386799",4],["3814.9676","0.001",1],["3813","0.91303023",4],["3812.388","0.002",2],["3811.2257","0.07",1],["3810","0.32573997",2],["3809.8084","0.001",1],["3809.7928","0.00262481",1],["3807.2288","0.001",1],["3806.8421","0.07003461",1],["3806","0.19",1],["3805.8041","0.05678805",1],["3805","1.01",2],["3804.6492","0.001",1],["3804.3551","0.1",1],["3803","0.005",1],["3802.22","2.05042631",1],["3802.0696","0.001",1],["3802","1.63290092",1],["3801.2257","0.07",1],["3801","57.4",3],["3800.9853","0.02492278",1],["3800.8421","0.06503533",1],["3800.7844","0.02812628",1],["3800.0001","0.00409473",1],["3800","17.91401074",15],["3799.49","0.001",1],["3799","0.1",1],["3796.9104","0.001",1],["3796","9.00128053",2],["3795.5441","0.0028",1],["3794.3308","0.001",1],["3791","55",1],["3790.7777","0.07",1],["3790","12.03238184",7],["3789","1",1],["3788","0.21110454",2],["3787.2959","9",1],["3786.592","0.001",1],["3786","9.01916822",2],["3785","12.87914268",5],["3784.0124","0.001",1],["3781.4328","0.002",2],["3781","56.3",2],["3780.7777","0.07",1],["3780","23.41537654",10],["3778.8532","0.002",2],["3776","9",1],["3774","0.003",1],["3772.2481","0.06901672",1],["3771","55.1",2],["3770.7777","0.07",1],["3770","7.30268416",5],["3769","0.25",1],["3768","1.3725",3],["3766.66","0.02",1],["3766","7.64837924",2],["3765.58","1.22775492",1],["3762.58","1.22873383",1],["3761","51.68262164",1],["3760.8031","0.0399",1],["3760.7777","0.07",1]],"timestamp":"2019-03-06T23:19:17.705Z","checksum":-1785549915}]}`
- update := `{"table":"spot/depth","action":"update","data":[{"instrument_id":"BTC-USDT","asks":[["3864.6786","0",0],["3864.9852","0",0],["3865.9994","0.48402971",1],["3866.4004","0.001",1],["3866.7995","0.3273",2],["3867.4566","0",0],["3867.7031","0.025",1],["3868.0436","0",0],["3868.346","0",0],["3868.3695","0.051",1],["3870.9243","0.642",1],["3874.9942","0.51751796",1],["3875.7057","0",0],["3939","0.001",1]],"bids":[["3864.55","0.0565449",1],["3863.8282","0",0],["3863.8153","0",0],["3863.7898","0.01320077",1],["3863.4807","0.02112123",1],["3863.3002","0.04233533",1],["3863.1717","0.03379397",1],["3863.0685","0.04438179",1],["3863.0286","0.7362564",1],["3862.9912","0.06773651",1],["3862.8626","0.05407035",1],["3862.7595","0.07101087",1],["3862.313","0.3756",2],["3862.1848","0.012",1],["3862.0734","0",0],["3861.8391","0.025",1],["3861.7888","0",0],["3856.6716","0.38893641",1],["3768","0",0],["3766.66","0",0],["3766","0",0],["3765.58","0",0],["3762.58","0",0],["3761","0",0],["3760.8031","0",0],["3760.7777","0",0]],"timestamp":"2019-03-06T23:19:18.239Z","checksum":-1587788848}]}`
+ t.Parallel()
+ original := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"BTC-USD","asks":[["3864.6786","0.145",1],["3864.7682","0.005",1],["3864.9851","0.57",1],["3864.9852","0.30137754",1],["3864.9986","2.81818419",1],["3864.9995","0.002",1],["3865","0.0597",1],["3865.0309","0.4",1],["3865.1995","0.004",1],["3865.3995","0.004",1],["3865.5995","0.004",1],["3865.7995","0.004",1],["3865.9995","0.004",1],["3866.0961","0.25865886",1],["3866.1995","0.004",1],["3866.3995","0.004",1],["3866.4004","0.3243",2],["3866.5995","0.004",1],["3866.7633","0.44247086",1],["3866.7995","0.004",1],["3866.9197","0.511",1],["3867.256","0.51716256",1],["3867.3951","0.02588112",1],["3867.4014","0.025",1],["3867.4566","0.02499999",1],["3867.4675","4.01155057",5],["3867.5515","1.1",1],["3867.6113","0.009",1],["3867.7349","0.026",1],["3867.7781","0.03738652",1],["3867.9163","0.0521",1],["3868.0381","0.34354941",1],["3868.0436","0.051",1],["3868.0657","0.90552172",3],["3868.1819","0.03863346",1],["3868.2013","0.194",1],["3868.346","0.051",1],["3868.3863","0.01155",1],["3868.7716","0.009",1],["3868.947","0.025",1],["3868.98","0.001",1],["3869.0764","1.03487931",1],["3869.2773","0.07724578",1],["3869.4039","0.025",1],["3869.4068","1.03",1],["3869.7068","2.06976398",1],["3870","0.5",1],["3870.0465","0.01",1],["3870.7042","0.02099651",1],["3870.9451","2.07047375",1],["3871.5254","1.2",1],["3871.5596","0.001",1],["3871.6605","0.01035032",1],["3871.7179","2.07047375",1],["3871.8816","0.51751625",1],["3872.1","0.75",1],["3872.2464","0.0646",1],["3872.3747","0.283",1],["3872.4039","0.2",1],["3872.7655","0.23179307",1],["3872.8005","2.06976398",1],["3873.1509","2",1],["3873.3215","0.26",1],["3874.1392","0.001",1],["3874.1487","3.88224364",4],["3874.1685","1.8",1],["3874.5571","0.08974762",1],["3874.734","2.06976398",1],["3874.99","0.3",1],["3875","1.001",2],["3875.0041","1.03505051",1],["3875.45","0.3",1],["3875.4766","0.15",1],["3875.7057","0.51751625",1],["3876","0.001",1],["3876.68","0.3",1],["3876.7188","0.001",1],["3877","0.75",1],["3877.31","0.035",1],["3877.38","0.3",1],["3877.7","0.3",1],["3877.88","0.3",1],["3878.0364","0.34770122",1],["3878.4525","0.48579748",1],["3878.4955","0.02812511",1],["3878.8855","0.00258579",1],["3878.9605","0.895",1],["3879","0.001",1],["3879.2984","0.002",2],["3879.432","0.001",1],["3879.6313","6",1],["3879.9999","0.002",2],["3880","1.25132834",5],["3880.2526","0.04075162",1],["3880.7145","0.0647",1],["3881.2469","1.883",1],["3881.878","0.002",2],["3884.4576","0.002",2],["3885","0.002",2],["3885.2233","0.28304103",1],["3885.7416","18",1],["3886","0.001",1],["3886.1554","5.4",1],["3887","0.001",1],["3887.0372","0.002",2],["3887.2559","0.05214011",1],["3887.9238","0.0019",1],["3888","0.15810538",4],["3889","0.001",1],["3889.5175","0.50510653",1],["3889.6168","0.002",2],["3889.9999","0.001",1],["3890","2.34968109",4],["3890.5222","0.00257806",1],["3891.2659","5",1],["3891.9999","0.00893897",1],["3892.1964","0.002",2],["3892.4358","0.0176",1],["3893.1388","1.4279",1],["3894","0.0026321",1],["3894.776","0.001",1],["3895","1.501",2],["3895.379","0.25881288",1],["3897","0.05",1],["3897.3556","0.001",1],["3897.8432","0.73708079",1],["3898","3.31353018",7],["3898.4462","4.757",1],["3898.6","0.47159638",1],["3898.8769","0.0129",1],["3899","6",2],["3899.6516","0.025",1],["3899.9352","0.001",1],["3899.9999","0.013",2],["3900","22.37447743",24],["3900.9999","0.07763916",1],["3901","0.10192487",1],["3902.1937","0.00257034",1],["3902.3991","1.5532141",1],["3902.5148","0.001",1],["3904","1.49331984",1],["3904.9999","0.95905447",1],["3905","0.501",2],["3905.0944","0.001",1],["3905.61","0.099",1],["3905.6801","0.54343686",1],["3906.2901","0.0258",1],["3907.674","0.001",1],["3907.85","1.35778084",1],["3908","0.03846153",1],["3908.23","1.95189531",1],["3908.906","0.03148978",1],["3909","0.001",1],["3909.9999","0.01398721",2],["3910","0.016",2],["3910.2536","0.001",1],["3912.5406","0.88270517",1],["3912.8332","0.001",1],["3913","1.2640608",1],["3913.87","1.69114184",1],["3913.9003","0.00256266",1],["3914","1.21766411",1],["3915","0.001",1],["3915.4128","0.001",1],["3915.7425","6.848",1],["3916","0.0050949",1],["3917.36","1.28658296",1],["3917.9924","0.001",1],["3919","0.001",1],["3919.9999","0.001",1],["3920","1.21171832",3],["3920.0002","0.20217038",1],["3920.572","0.001",1],["3921","0.128",1],["3923.0756","0.00148064",1],["3923.1516","0.001",1],["3923.86","1.38831714",1],["3925","0.01867801",2],["3925.642","0.00255499",1],["3925.7312","0.001",1],["3926","0.04290757",1],["3927","0.023",1],["3927.3175","0.01212865",1],["3927.65","1.51375612",1],["3928","0.5",1],["3928.3108","0.001",1],["3929","0.001",1],["3929.9999","0.01519338",2],["3930","0.0174985",3],["3930.21","1.49335799",1],["3930.8904","0.001",1],["3932.2999","0.01953",1],["3932.8962","7.96",1],["3933.0387","11.808",1],["3933.47","0.001",1],["3934","1.40839932",1],["3935","0.001",1],["3936.8","0.62879518",1],["3937.23","1.56977841",1],["3937.4189","0.00254735",1]],"bids":[["3864.5217","0.00540709",1],["3864.5216","0.14068758",2],["3864.2275","0.01033576",1],["3864.0989","0.00825047",1],["3864.0273","0.38",1],["3864.0272","0.4",1],["3863.9957","0.01083539",1],["3863.9184","0.01653723",1],["3863.8282","0.25588165",1],["3863.8153","0.154",1],["3863.7791","1.14122492",1],["3863.6866","0.01733662",1],["3863.6093","0.02645958",1],["3863.3775","0.02773862",1],["3863.0297","0.513",1],["3863.0286","1.1028564",2],["3862.8489","0.01",1],["3862.5972","0.01890179",1],["3862.3431","0.01152944",1],["3862.313","0.009",1],["3862.2445","0.90551002",3],["3862.0734","0.014",1],["3862.0539","0.64976067",1],["3861.8586","0.025",1],["3861.7888","0.025",1],["3861.7673","0.008",1],["3861.5785","0.01",1],["3861.3895","0.005",1],["3861.3338","0.25875855",1],["3861.161","0.01",1],["3861.1111","0.03863352",1],["3861.0732","0.51703882",1],["3860.9116","0.17754895",1],["3860.75","0.19",1],["3860.6554","0.015",1],["3860.6172","0.005",1],["3860.6088","0.008",1],["3860.4724","0.12940042",1],["3860.4424","0.25880084",1],["3860.42","0.01",1],["3860.3725","0.51760102",1],["3859.8449","0.005",1],["3859.8285","0.03738652",1],["3859.7638","0.07726703",1],["3859.4502","0.008",1],["3859.3772","0.05173471",1],["3859.3409","0.194",1],["3859","5",1],["3858.827","0.0521",1],["3858.8208","0.001",1],["3858.679","0.26",1],["3858.4814","0.07477305",1],["3858.1669","1.03503422",1],["3857.6005","0.006",1],["3857.4005","0.004",1],["3857.2005","0.004",1],["3857.1871","1.218",1],["3857.0005","0.004",1],["3856.8135","0.0646",1],["3856.8005","0.004",1],["3856.2412","0.001",1],["3856.2349","1.03503422",1],["3856.0197","0.01037339",1],["3855.8781","0.23178117",1],["3855.8005","0.004",1],["3855.7165","0.00259355",1],["3855.4858","0.25875855",1],["3854.4584","0.01",1],["3853.6616","0.001",1],["3853.1373","0.92",1],["3852.5072","0.48599702",1],["3851.3926","0.13008333",1],["3851.082","0.001",1],["3850.9317","2",1],["3850.6359","0.34770165",1],["3850.2058","0.51751624",1],["3850.0823","0.15",1],["3850.0042","0.5175171",1],["3850","0.001",1],["3849.6325","1.8",1],["3849.41","0.3",1],["3848.9686","1.85",1],["3848.7426","0.18511466",1],["3848.52","0.3",1],["3848.5024","0.001",1],["3848.42","0.3",1],["3848.1618","2.204",1],["3847.77","0.3",1],["3847.48","0.3",1],["3847.3581","2.05",1],["3846.8259","0.0646",1],["3846.59","0.3",1],["3846.49","0.3",1],["3845.9228","0.001",1],["3844.184","0.00260133",1],["3844.0092","6.3",1],["3843.3432","0.001",1],["3841","0.06300963",1],["3840.7636","0.001",1],["3840","0.201",3],["3839.7681","18",1],["3839.5328","0.05214011",1],["3838.184","0.001",1],["3837.2344","0.27589557",1],["3836.6479","5.2",1],["3836","2.37196773",3],["3835.6044","0.001",1],["3833.6053","0.25873556",1],["3833.0248","0.001",1],["3833","0.8726502",1],["3832.6859","0.00260913",1],["3832","0.007",1],["3831.637","6",1],["3831.0602","0.001",1],["3830.4452","0.001",1],["3830","0.20375718",4],["3829.7125","0.07833486",1],["3829.6283","0.3519681",1],["3829","0.0039261",1],["3827.8656","0.001",1],["3826.0001","0.53251232",1],["3826","0.0509",1],["3825.7834","0.00698562",1],["3825.286","0.001",1],["3823.0001","0.03010127",1],["3822.8014","0.00261588",1],["3822.7064","0.001",1],["3822.2","1",1],["3822.1121","0.35994101",1],["3821.2222","0.00261696",1],["3821","0.001",1],["3820.1268","0.001",1],["3820","1.12992803",4],["3819","0.01331195",2],["3817.5472","0.001",1],["3816","1.13807184",2],["3815.8343","0.32463428",1],["3815.7834","0.00525295",1],["3815","28.99386799",4],["3814.9676","0.001",1],["3813","0.91303023",4],["3812.388","0.002",2],["3811.2257","0.07",1],["3810","0.32573997",2],["3809.8084","0.001",1],["3809.7928","0.00262481",1],["3807.2288","0.001",1],["3806.8421","0.07003461",1],["3806","0.19",1],["3805.8041","0.05678805",1],["3805","1.01",2],["3804.6492","0.001",1],["3804.3551","0.1",1],["3803","0.005",1],["3802.22","2.05042631",1],["3802.0696","0.001",1],["3802","1.63290092",1],["3801.2257","0.07",1],["3801","57.4",3],["3800.9853","0.02492278",1],["3800.8421","0.06503533",1],["3800.7844","0.02812628",1],["3800.0001","0.00409473",1],["3800","17.91401074",15],["3799.49","0.001",1],["3799","0.1",1],["3796.9104","0.001",1],["3796","9.00128053",2],["3795.5441","0.0028",1],["3794.3308","0.001",1],["3791","55",1],["3790.7777","0.07",1],["3790","12.03238184",7],["3789","1",1],["3788","0.21110454",2],["3787.2959","9",1],["3786.592","0.001",1],["3786","9.01916822",2],["3785","12.87914268",5],["3784.0124","0.001",1],["3781.4328","0.002",2],["3781","56.3",2],["3780.7777","0.07",1],["3780","23.41537654",10],["3778.8532","0.002",2],["3776","9",1],["3774","0.003",1],["3772.2481","0.06901672",1],["3771","55.1",2],["3770.7777","0.07",1],["3770","7.30268416",5],["3769","0.25",1],["3768","1.3725",3],["3766.66","0.02",1],["3766","7.64837924",2],["3765.58","1.22775492",1],["3762.58","1.22873383",1],["3761","51.68262164",1],["3760.8031","0.0399",1],["3760.7777","0.07",1]],"timestamp":"2019-03-06T23:19:17.705Z","checksum":-1785549915}]}`
+ update := `{"table":"spot/depth","action":"update","data":[{"instrument_id":"BTC-USD","asks":[["3864.6786","0",0],["3864.9852","0",0],["3865.9994","0.48402971",1],["3866.4004","0.001",1],["3866.7995","0.3273",2],["3867.4566","0",0],["3867.7031","0.025",1],["3868.0436","0",0],["3868.346","0",0],["3868.3695","0.051",1],["3870.9243","0.642",1],["3874.9942","0.51751796",1],["3875.7057","0",0],["3939","0.001",1]],"bids":[["3864.55","0.0565449",1],["3863.8282","0",0],["3863.8153","0",0],["3863.7898","0.01320077",1],["3863.4807","0.02112123",1],["3863.3002","0.04233533",1],["3863.1717","0.03379397",1],["3863.0685","0.04438179",1],["3863.0286","0.7362564",1],["3862.9912","0.06773651",1],["3862.8626","0.05407035",1],["3862.7595","0.07101087",1],["3862.313","0.3756",2],["3862.1848","0.012",1],["3862.0734","0",0],["3861.8391","0.025",1],["3861.7888","0",0],["3856.6716","0.38893641",1],["3768","0",0],["3766.66","0",0],["3766","0",0],["3765.58","0",0],["3762.58","0",0],["3761","0",0],["3760.8031","0",0],["3760.7777","0",0]],"timestamp":"2019-03-06T23:19:18.239Z","checksum":-1587788848}]}`
err := o.WsProcessOrderBook([]byte(original))
if err != nil {
t.Fatal(err)
@@ -849,11 +984,8 @@ func TestOrderBookUpdateChecksumCalculator(t *testing.T) {
}
}
-// TestOrderBookUpdateChecksumCalculatorWithDash logic test
func TestOrderBookUpdateChecksumCalculatorWith8DecimalPlaces(t *testing.T) {
- if !websocketEnabled {
- t.Skip("Websocket not enabled, skipping")
- }
+ t.Parallel()
original := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"WAVES-BTC","asks":[["0.000714","1.15414979",1],["0.000715","3.3",2],["0.000717","426.71348",2],["0.000719","140.84507042",1],["0.00072","590.77",1],["0.000721","991.77",1],["0.000724","0.3532032",1],["0.000725","58.82698567",1],["0.000726","1033.15469748",2],["0.000729","0.35320321",1],["0.00073","352.77",1],["0.000735","0.38469748",1],["0.000736","625.77",1],["0.00075191","152.44796961",1],["0.00075192","114.3359772",1],["0.00075193","85.7519829",1],["0.00075194","64.31398718",1],["0.00075195","48.23549038",1],["0.00075196","36.17661779",1],["0.00075199","61.04804253",1],["0.0007591","70.71318474",1],["0.0007621","53.03488855",1],["0.00076211","39.77616642",1],["0.00076212","29.83212481",1],["0.0007635","22.37409361",1],["0.00076351","29.36599786",2],["0.00076352","9.43907074",1],["0.00076353","7.07930306",1],["0.00076354","14.15860612",1],["0.00076355","3.53965153",1],["0.00076369","3.53965153",1],["0.0008","34.36841101",1],["0.00082858","1.69936503",1],["0.00083232","2.8",1],["0.00084","15.69220129",1],["0.00085","4.42785042",1],["0.00088","0.1",1],["0.000891","0.1",1],["0.0009","12.41486491",2],["0.00093","5",1],["0.0012","12.31486492",1],["0.00531314","6.91803114",1],["0.00799999","0.02",1],["0.0084","0.05989",1],["0.00931314","5.18852336",1],["0.0799999","0.02",1],["0.499","6.00423396",1],["0.5","0.4995",1],["0.799999","0.02",1],["4.99","2",1],["5","3.98583144",1],["7.99999999","0.02",1],["79.99999999","0.02",1],["799.99999999","0.02986704",1]],"bids":[["0.000709","222.91679881",3],["0.000703","0.47161952",1],["0.000701","140.73015789",2],["0.0007","0.3",1],["0.000699","401",1],["0.000698","232.61801667",2],["0.000689","0.71396896",1],["0.000688","0.69910125",1],["0.000613","227.54771052",1],["0.0005","0.01",1],["0.00026789","3.69905341",1],["0.000238","2.4",1],["0.00022","0.53",1],["0.0000055","374.09871696",1],["0.00000056","222",1],["0.00000055","736.84761363",1],["0.0000002","999",1],["0.00000009","1222.22222417",1],["0.00000008","20868.64520447",1],["0.00000002","110000",1],["0.00000001","10000",1]],"timestamp":"2019-03-12T22:22:42.274Z","checksum":1319037905}]}`
update := `{"table":"spot/depth","action":"update","data":[{"instrument_id":"WAVES-BTC","asks":[["0.000715","100.48199596",3],["0.000716","62.21679881",1]],"bids":[["0.000713","38.95772168",1]],"timestamp":"2019-03-12T22:22:42.938Z","checksum":-131160897}]}`
err := o.WsProcessOrderBook([]byte(original))
@@ -866,10 +998,10 @@ func TestOrderBookUpdateChecksumCalculatorWith8DecimalPlaces(t *testing.T) {
}
}
-// TestOrderBookPartialChecksumCalculator logic test
func TestOrderBookPartialChecksumCalculator(t *testing.T) {
- orderbookPartialJSON := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"EOS-USDT","asks":[["3.5196","0.1077",1],["3.5198","21.71",1],["3.5199","51.1805",1],["3.5208","75.09",1],["3.521","196.3333",1],["3.5213","0.1",1],["3.5218","39.276",2],["3.5219","395.6334",1],["3.522","27.956",1],["3.5222","404.9595",1],["3.5225","300",1],["3.5227","143.5442",2],["3.523","42.4746",1],["3.5231","852.64",2],["3.5235","34.9602",1],["3.5237","442.0918",2],["3.5238","352.8404",2],["3.5239","341.6759",2],["3.524","84.9493",1],["3.5241","148.4882",1],["3.5242","261.64",1],["3.5243","142.045",1],["3.5246","10",1],["3.5247","284.0788",1],["3.5248","720",1],["3.5249","89.2518",2],["3.5251","1201.8965",2],["3.5254","426.2938",1],["3.5255","213.0863",1],["3.5257","568.1576",1],["3.5258","0.3",1],["3.5259","34.4602",1],["3.526","0.1",1],["3.5263","850.771",1],["3.5265","5.9",1],["3.5268","10.5064",2],["3.5272","1136.8965",1],["3.5274","255.1481",1],["3.5276","29.5374",1],["3.5278","50",1],["3.5282","284.1797",1],["3.5283","1136.8965",1],["3.5284","0.4275",1],["3.5285","100",1],["3.5292","90.9",1],["3.5298","0.2",1],["3.5303","568.1576",1],["3.5305","279.9999",1],["3.532","0.409",1],["3.5321","568.1576",1],["3.5326","6016.8756",1],["3.5328","4.9849",1],["3.533","92.88",2],["3.5343","1200.2383",2],["3.5344","100",1],["3.535","359.7047",1],["3.5354","100",1],["3.5355","100",1],["3.5356","10",1],["3.5358","200",2],["3.5362","435.139",1],["3.5365","2152",1],["3.5366","284.1756",1],["3.5367","568.4644",1],["3.5369","33.9878",1],["3.537","337.1191",2],["3.5373","0.4045",1],["3.5383","1136.7188",1],["3.5386","12.1614",1],["3.5387","90.89",1],["3.54","4.54",1],["3.5423","90.8",1],["3.5436","0.1",1],["3.5454","853.4156",1],["3.5468","142.0656",1],["3.5491","0.0008",1],["3.55","14478.8206",6],["3.5537","21521",1],["3.5555","11.53",1],["3.5573","50.6001",1],["3.5599","4591.4221",1],["3.56","1227.0002",4],["3.5603","2670",1],["3.5608","58.6638",1],["3.5613","0.1",1],["3.5621","45.9473",1],["3.57","2141.7274",3],["3.5712","2956.9816",1],["3.5717","27.9978",1],["3.5718","0.9285",1],["3.5739","299.73",1],["3.5761","864",1],["3.579","22.5225",1],["3.5791","38.26",2],["3.58","7618.4634",5],["3.5801","457.2184",1],["3.582","24.5",1],["3.5822","1572.6425",1],["3.5845","14.1438",1],["3.585","527.169",1],["3.5865","20",1],["3.5867","4490",1],["3.5876","39.0493",1],["3.5879","392.9083",1],["3.5888","436.42",2],["3.5896","50",1],["3.59","2608.9128",8],["3.5913","19.5246",1],["3.5938","7082",1],["3.597","0.1",1],["3.5979","399",1],["3.5995","315.1509",1],["3.5999","2566.2648",1],["3.6","18511.2292",35],["3.603","22.3379",2],["3.605","499.5",1],["3.6055","100",1],["3.6058","499.5",1],["3.608","1021.1485",1],["3.61","11755.4596",13],["3.611","42.8571",1],["3.6131","6690",1],["3.6157","19.5247",1],["3.618","2500",1],["3.6197","525.7146",1],["3.6198","0.4455",1],["3.62","6440.6295",8],["3.6219","0.4175",1],["3.6237","168",1],["3.6265","0.1001",1],["3.628","64.9345",1],["3.63","4435.4985",6],["3.6308","1.7815",1],["3.6331","0.1",1],["3.6338","355.527",2],["3.6358","50",1],["3.6363","2074.7096",1],["3.6376","4000",1],["3.6396","11090",1],["3.6399","0.4055",1],["3.64","4161.9805",4],["3.6437","117.6524",1],["3.648","190",1],["3.6488","200",1],["3.65","11740.5045",25],["3.6512","0.1",1],["3.6521","728",1],["3.6555","100",1],["3.6598","36.6914",1],["3.66","4331.2148",6],["3.6638","200",1],["3.6673","100",1],["3.6679","38",1],["3.6688","2",1],["3.6695","0.1",1],["3.67","7984.698",6],["3.672","300",1],["3.6777","257.8247",1],["3.6789","393.4217",2],["3.68","9202.3222",11],["3.6818","500",1],["3.6823","299.7",1],["3.6839","422.3748",1],["3.685","100",1],["3.6878","0.1",1],["3.6888","72.0958",2],["3.6889","2876",1],["3.689","28",1],["3.6891","28",1],["3.6892","28",1],["3.6895","28",1],["3.6898","28",1],["3.69","643.96",7],["3.6908","118",2],["3.691","28",1],["3.6916","28",1],["3.6918","28",1],["3.6926","28",1],["3.6928","28",1],["3.6932","28",1],["3.6933","200",1],["3.6935","28",1],["3.6936","28",1],["3.6938","28",1],["3.694","28",1],["3.698","1498.5",1],["3.6988","2014.2004",2],["3.7","21904.2689",22],["3.7029","71.95",1],["3.704","3690.1362",1],["3.7055","100",1],["3.7063","0.1",1],["3.71","4421.3468",4],["3.719","17.3491",1],["3.72","1304.5995",3],["3.7211","10",1],["3.7248","0.1",1],["3.725","1900",1],["3.73","31.1785",2],["3.7375","38",1]],"bids":[["3.5182","151.5343",6],["3.5181","0.3691",1],["3.518","271.3967",2],["3.5179","257.8352",1],["3.5178","12.3811",1],["3.5173","34.1921",2],["3.5171","1013.8256",2],["3.517","272.1119",2],["3.5168","395.3376",1],["3.5166","317.1756",2],["3.5165","348.302",3],["3.5164","142.0414",1],["3.5163","96.8933",2],["3.516","600.1034",3],["3.5159","27.481",1],["3.5158","27.33",1],["3.5157","583.1898",2],["3.5156","24.6819",2],["3.5154","25",1],["3.5153","0.429",1],["3.5152","453.9204",3],["3.5151","2131.592",4],["3.515","335",3],["3.5149","37.1586",1],["3.5147","41.6759",1],["3.5146","54.569",1],["3.5145","70.3515",1],["3.5143","68.206",3],["3.5142","359.4538",2],["3.5139","45.4123",2],["3.5137","71.673",2],["3.5136","25",1],["3.5135","300",1],["3.5134","442.57",2],["3.5132","83.3518",1],["3.513","1245.2529",3],["3.5127","20",1],["3.512","284.1353",1],["3.5119","1136.8319",1],["3.5113","56.9351",1],["3.5111","588.1898",2],["3.5109","255.0946",1],["3.5105","48.65",1],["3.5103","50.2",1],["3.5098","720",1],["3.5096","148.95",1],["3.5094","570.5758",2],["3.509","2.386",1],["3.5089","0.4065",1],["3.5087","282.3859",2],["3.5086","145.036",2],["3.5084","2.386",1],["3.5082","90.98",1],["3.5081","2.386",1],["3.5079","2.386",1],["3.5078","857.6229",2],["3.5075","2.386",1],["3.5074","284.1877",1],["3.5073","100",1],["3.5071","100",1],["3.507","768.4159",3],["3.5069","313.0863",2],["3.5068","426.2938",1],["3.5066","568.3594",1],["3.5063","1136.6865",1],["3.5059","0.3",1],["3.5054","9.9999",1],["3.5053","0.2",1],["3.5051","392.428",1],["3.505","13.79",1],["3.5048","99.5497",2],["3.5047","78.5331",2],["3.5046","2153",1],["3.5041","5983.999",1],["3.5037","668.5682",1],["3.5036","160.5948",1],["3.5024","534.8075",1],["3.5014","28.5604",1],["3.5011","91",1],["3.5","1058.8771",2],["3.4997","50.2",1],["3.4985","3430.0414",1],["3.4949","232.0591",1],["3.4942","21521",1],["3.493","2",1],["3.4928","2",1],["3.4925","0.44",1],["3.4917","142.0656",1],["3.49","2051.8826",4],["3.488","280.7459",1],["3.4852","643.4038",1],["3.4851","86.0807",1],["3.485","213.2436",1],["3.484","0.1",1],["3.4811","144.3399",1],["3.4808","89",1],["3.4803","12.1999",1],["3.4801","2390",1],["3.48","930.8453",9],["3.4791","310",1],["3.4768","206",1],["3.4767","0.9415",1],["3.4754","1.4387",1],["3.4728","20",1],["3.4701","1219.2873",1],["3.47","1904.3139",7],["3.468","0.4035",1],["3.4667","0.1",1],["3.4666","3020.0101",1],["3.465","10",1],["3.464","0.4485",1],["3.462","2119.6556",1],["3.46","1305.6113",8],["3.4589","8.0228",1],["3.457","100",1],["3.456","70.3859",2],["3.4538","20",1],["3.4536","4323.9486",2],["3.4531","827.0427",1],["3.4528","0.439",1],["3.4522","8.0381",1],["3.4513","441.1873",1],["3.4512","50.707",1],["3.451","87.0902",1],["3.4509","200",1],["3.4506","100",1],["3.4505","86.4045",2],["3.45","12409.4595",28],["3.4494","0.5365",2],["3.449","10761",1],["3.4482","8.0476",1],["3.4469","0.449",1],["3.445","2000",1],["3.4427","14",1],["3.4421","100",1],["3.4416","8.0631",1],["3.4404","1",1],["3.44","4580.733",11],["3.4388","1868.2085",1],["3.438","937.7246",2],["3.4367","1500",1],["3.4366","62",1],["3.436","29.8743",1],["3.4356","25.4801",1],["3.4349","4.3086",1],["3.4343","43.2402",1],["3.433","2.0688",1],["3.4322","2.7335",2],["3.432","93.3233",1],["3.4302","328.8301",2],["3.43","4440.8158",11],["3.4288","754.574",2],["3.4283","125.7043",2],["3.428","744.3154",2],["3.4273","5460",1],["3.4258","50",1],["3.4255","109.005",1],["3.4248","100",1],["3.4241","129.2048",2],["3.4233","5.3598",1],["3.4228","4498.866",1],["3.4222","3.5435",1],["3.4217","404.3252",2],["3.4211","1000",1],["3.4208","31",1],["3.42","1834.024",9],["3.4175","300",1],["3.4162","400",1],["3.4152","0.1",1],["3.4151","4.3336",1],["3.415","1.5974",1],["3.414","1146",1],["3.4134","306.4246",1],["3.4129","7.5556",1],["3.4111","198.5188",1],["3.4109","500",1],["3.4106","4305",1],["3.41","2150.7635",13],["3.4085","4.342",1],["3.4054","5.6985",1],["3.4019","5.438",1],["3.4015","1010.846",1],["3.4009","8610",1],["3.4005","1.9122",1],["3.4004","1",1],["3.4","27081.1806",67],["3.3955","3.2682",1],["3.3953","5.4486",1],["3.3937","1591.3805",1],["3.39","3221.4155",8],["3.3899","3.2736",1],["3.3888","1500",2],["3.3887","5.4592",1],["3.385","117.0969",2],["3.3821","5.4699",1],["3.382","100.0529",1],["3.3818","172.0164",1],["3.3815","165.6288",1],["3.381","887.3115",1],["3.3808","100",1]],"timestamp":"2019-03-04T00:15:04.155Z","checksum":-2036653089}]}`
- var dataResponse okgroup.WebsocketOrderBook
+ t.Parallel()
+ orderbookPartialJSON := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"EOS-USD","asks":[["3.5196","0.1077",1],["3.5198","21.71",1],["3.5199","51.1805",1],["3.5208","75.09",1],["3.521","196.3333",1],["3.5213","0.1",1],["3.5218","39.276",2],["3.5219","395.6334",1],["3.522","27.956",1],["3.5222","404.9595",1],["3.5225","300",1],["3.5227","143.5442",2],["3.523","42.4746",1],["3.5231","852.64",2],["3.5235","34.9602",1],["3.5237","442.0918",2],["3.5238","352.8404",2],["3.5239","341.6759",2],["3.524","84.9493",1],["3.5241","148.4882",1],["3.5242","261.64",1],["3.5243","142.045",1],["3.5246","10",1],["3.5247","284.0788",1],["3.5248","720",1],["3.5249","89.2518",2],["3.5251","1201.8965",2],["3.5254","426.2938",1],["3.5255","213.0863",1],["3.5257","568.1576",1],["3.5258","0.3",1],["3.5259","34.4602",1],["3.526","0.1",1],["3.5263","850.771",1],["3.5265","5.9",1],["3.5268","10.5064",2],["3.5272","1136.8965",1],["3.5274","255.1481",1],["3.5276","29.5374",1],["3.5278","50",1],["3.5282","284.1797",1],["3.5283","1136.8965",1],["3.5284","0.4275",1],["3.5285","100",1],["3.5292","90.9",1],["3.5298","0.2",1],["3.5303","568.1576",1],["3.5305","279.9999",1],["3.532","0.409",1],["3.5321","568.1576",1],["3.5326","6016.8756",1],["3.5328","4.9849",1],["3.533","92.88",2],["3.5343","1200.2383",2],["3.5344","100",1],["3.535","359.7047",1],["3.5354","100",1],["3.5355","100",1],["3.5356","10",1],["3.5358","200",2],["3.5362","435.139",1],["3.5365","2152",1],["3.5366","284.1756",1],["3.5367","568.4644",1],["3.5369","33.9878",1],["3.537","337.1191",2],["3.5373","0.4045",1],["3.5383","1136.7188",1],["3.5386","12.1614",1],["3.5387","90.89",1],["3.54","4.54",1],["3.5423","90.8",1],["3.5436","0.1",1],["3.5454","853.4156",1],["3.5468","142.0656",1],["3.5491","0.0008",1],["3.55","14478.8206",6],["3.5537","21521",1],["3.5555","11.53",1],["3.5573","50.6001",1],["3.5599","4591.4221",1],["3.56","1227.0002",4],["3.5603","2670",1],["3.5608","58.6638",1],["3.5613","0.1",1],["3.5621","45.9473",1],["3.57","2141.7274",3],["3.5712","2956.9816",1],["3.5717","27.9978",1],["3.5718","0.9285",1],["3.5739","299.73",1],["3.5761","864",1],["3.579","22.5225",1],["3.5791","38.26",2],["3.58","7618.4634",5],["3.5801","457.2184",1],["3.582","24.5",1],["3.5822","1572.6425",1],["3.5845","14.1438",1],["3.585","527.169",1],["3.5865","20",1],["3.5867","4490",1],["3.5876","39.0493",1],["3.5879","392.9083",1],["3.5888","436.42",2],["3.5896","50",1],["3.59","2608.9128",8],["3.5913","19.5246",1],["3.5938","7082",1],["3.597","0.1",1],["3.5979","399",1],["3.5995","315.1509",1],["3.5999","2566.2648",1],["3.6","18511.2292",35],["3.603","22.3379",2],["3.605","499.5",1],["3.6055","100",1],["3.6058","499.5",1],["3.608","1021.1485",1],["3.61","11755.4596",13],["3.611","42.8571",1],["3.6131","6690",1],["3.6157","19.5247",1],["3.618","2500",1],["3.6197","525.7146",1],["3.6198","0.4455",1],["3.62","6440.6295",8],["3.6219","0.4175",1],["3.6237","168",1],["3.6265","0.1001",1],["3.628","64.9345",1],["3.63","4435.4985",6],["3.6308","1.7815",1],["3.6331","0.1",1],["3.6338","355.527",2],["3.6358","50",1],["3.6363","2074.7096",1],["3.6376","4000",1],["3.6396","11090",1],["3.6399","0.4055",1],["3.64","4161.9805",4],["3.6437","117.6524",1],["3.648","190",1],["3.6488","200",1],["3.65","11740.5045",25],["3.6512","0.1",1],["3.6521","728",1],["3.6555","100",1],["3.6598","36.6914",1],["3.66","4331.2148",6],["3.6638","200",1],["3.6673","100",1],["3.6679","38",1],["3.6688","2",1],["3.6695","0.1",1],["3.67","7984.698",6],["3.672","300",1],["3.6777","257.8247",1],["3.6789","393.4217",2],["3.68","9202.3222",11],["3.6818","500",1],["3.6823","299.7",1],["3.6839","422.3748",1],["3.685","100",1],["3.6878","0.1",1],["3.6888","72.0958",2],["3.6889","2876",1],["3.689","28",1],["3.6891","28",1],["3.6892","28",1],["3.6895","28",1],["3.6898","28",1],["3.69","643.96",7],["3.6908","118",2],["3.691","28",1],["3.6916","28",1],["3.6918","28",1],["3.6926","28",1],["3.6928","28",1],["3.6932","28",1],["3.6933","200",1],["3.6935","28",1],["3.6936","28",1],["3.6938","28",1],["3.694","28",1],["3.698","1498.5",1],["3.6988","2014.2004",2],["3.7","21904.2689",22],["3.7029","71.95",1],["3.704","3690.1362",1],["3.7055","100",1],["3.7063","0.1",1],["3.71","4421.3468",4],["3.719","17.3491",1],["3.72","1304.5995",3],["3.7211","10",1],["3.7248","0.1",1],["3.725","1900",1],["3.73","31.1785",2],["3.7375","38",1]],"bids":[["3.5182","151.5343",6],["3.5181","0.3691",1],["3.518","271.3967",2],["3.5179","257.8352",1],["3.5178","12.3811",1],["3.5173","34.1921",2],["3.5171","1013.8256",2],["3.517","272.1119",2],["3.5168","395.3376",1],["3.5166","317.1756",2],["3.5165","348.302",3],["3.5164","142.0414",1],["3.5163","96.8933",2],["3.516","600.1034",3],["3.5159","27.481",1],["3.5158","27.33",1],["3.5157","583.1898",2],["3.5156","24.6819",2],["3.5154","25",1],["3.5153","0.429",1],["3.5152","453.9204",3],["3.5151","2131.592",4],["3.515","335",3],["3.5149","37.1586",1],["3.5147","41.6759",1],["3.5146","54.569",1],["3.5145","70.3515",1],["3.5143","68.206",3],["3.5142","359.4538",2],["3.5139","45.4123",2],["3.5137","71.673",2],["3.5136","25",1],["3.5135","300",1],["3.5134","442.57",2],["3.5132","83.3518",1],["3.513","1245.2529",3],["3.5127","20",1],["3.512","284.1353",1],["3.5119","1136.8319",1],["3.5113","56.9351",1],["3.5111","588.1898",2],["3.5109","255.0946",1],["3.5105","48.65",1],["3.5103","50.2",1],["3.5098","720",1],["3.5096","148.95",1],["3.5094","570.5758",2],["3.509","2.386",1],["3.5089","0.4065",1],["3.5087","282.3859",2],["3.5086","145.036",2],["3.5084","2.386",1],["3.5082","90.98",1],["3.5081","2.386",1],["3.5079","2.386",1],["3.5078","857.6229",2],["3.5075","2.386",1],["3.5074","284.1877",1],["3.5073","100",1],["3.5071","100",1],["3.507","768.4159",3],["3.5069","313.0863",2],["3.5068","426.2938",1],["3.5066","568.3594",1],["3.5063","1136.6865",1],["3.5059","0.3",1],["3.5054","9.9999",1],["3.5053","0.2",1],["3.5051","392.428",1],["3.505","13.79",1],["3.5048","99.5497",2],["3.5047","78.5331",2],["3.5046","2153",1],["3.5041","5983.999",1],["3.5037","668.5682",1],["3.5036","160.5948",1],["3.5024","534.8075",1],["3.5014","28.5604",1],["3.5011","91",1],["3.5","1058.8771",2],["3.4997","50.2",1],["3.4985","3430.0414",1],["3.4949","232.0591",1],["3.4942","21521",1],["3.493","2",1],["3.4928","2",1],["3.4925","0.44",1],["3.4917","142.0656",1],["3.49","2051.8826",4],["3.488","280.7459",1],["3.4852","643.4038",1],["3.4851","86.0807",1],["3.485","213.2436",1],["3.484","0.1",1],["3.4811","144.3399",1],["3.4808","89",1],["3.4803","12.1999",1],["3.4801","2390",1],["3.48","930.8453",9],["3.4791","310",1],["3.4768","206",1],["3.4767","0.9415",1],["3.4754","1.4387",1],["3.4728","20",1],["3.4701","1219.2873",1],["3.47","1904.3139",7],["3.468","0.4035",1],["3.4667","0.1",1],["3.4666","3020.0101",1],["3.465","10",1],["3.464","0.4485",1],["3.462","2119.6556",1],["3.46","1305.6113",8],["3.4589","8.0228",1],["3.457","100",1],["3.456","70.3859",2],["3.4538","20",1],["3.4536","4323.9486",2],["3.4531","827.0427",1],["3.4528","0.439",1],["3.4522","8.0381",1],["3.4513","441.1873",1],["3.4512","50.707",1],["3.451","87.0902",1],["3.4509","200",1],["3.4506","100",1],["3.4505","86.4045",2],["3.45","12409.4595",28],["3.4494","0.5365",2],["3.449","10761",1],["3.4482","8.0476",1],["3.4469","0.449",1],["3.445","2000",1],["3.4427","14",1],["3.4421","100",1],["3.4416","8.0631",1],["3.4404","1",1],["3.44","4580.733",11],["3.4388","1868.2085",1],["3.438","937.7246",2],["3.4367","1500",1],["3.4366","62",1],["3.436","29.8743",1],["3.4356","25.4801",1],["3.4349","4.3086",1],["3.4343","43.2402",1],["3.433","2.0688",1],["3.4322","2.7335",2],["3.432","93.3233",1],["3.4302","328.8301",2],["3.43","4440.8158",11],["3.4288","754.574",2],["3.4283","125.7043",2],["3.428","744.3154",2],["3.4273","5460",1],["3.4258","50",1],["3.4255","109.005",1],["3.4248","100",1],["3.4241","129.2048",2],["3.4233","5.3598",1],["3.4228","4498.866",1],["3.4222","3.5435",1],["3.4217","404.3252",2],["3.4211","1000",1],["3.4208","31",1],["3.42","1834.024",9],["3.4175","300",1],["3.4162","400",1],["3.4152","0.1",1],["3.4151","4.3336",1],["3.415","1.5974",1],["3.414","1146",1],["3.4134","306.4246",1],["3.4129","7.5556",1],["3.4111","198.5188",1],["3.4109","500",1],["3.4106","4305",1],["3.41","2150.7635",13],["3.4085","4.342",1],["3.4054","5.6985",1],["3.4019","5.438",1],["3.4015","1010.846",1],["3.4009","8610",1],["3.4005","1.9122",1],["3.4004","1",1],["3.4","27081.1806",67],["3.3955","3.2682",1],["3.3953","5.4486",1],["3.3937","1591.3805",1],["3.39","3221.4155",8],["3.3899","3.2736",1],["3.3888","1500",2],["3.3887","5.4592",1],["3.385","117.0969",2],["3.3821","5.4699",1],["3.382","100.0529",1],["3.3818","172.0164",1],["3.3815","165.6288",1],["3.381","887.3115",1],["3.3808","100",1]],"timestamp":"2019-03-04T00:15:04.155Z","checksum":-2036653089}]}`
+ var dataResponse WebsocketOrderBook
err := json.Unmarshal([]byte(orderbookPartialJSON), &dataResponse)
if err != nil {
t.Error(err)
@@ -884,7 +1016,6 @@ func TestOrderBookPartialChecksumCalculator(t *testing.T) {
}
}
-// Function tests ----------------------------------------------------------------------------------------------
func setFeeBuilder() *exchange.FeeBuilder {
return &exchange.FeeBuilder{
Amount: 1,
@@ -899,8 +1030,8 @@ func setFeeBuilder() *exchange.FeeBuilder {
}
}
-// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
+ t.Parallel()
var feeBuilder = setFeeBuilder()
_, err := o.GetFeeByType(context.Background(), feeBuilder)
if err != nil {
@@ -918,6 +1049,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
+ t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if _, err := o.GetFee(context.Background(), feeBuilder); err != nil {
@@ -963,8 +1095,8 @@ func TestGetFee(t *testing.T) {
}
}
-// TestFormatWithdrawPermissions helper test
func TestFormatWithdrawPermissions(t *testing.T) {
+ t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := o.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
@@ -974,9 +1106,11 @@ func TestFormatWithdrawPermissions(t *testing.T) {
// Wrapper tests --------------------------------------------------------------------------------------------------
-// TestSubmitOrder Wrapper test
func TestSubmitOrder(t *testing.T) {
- TestSetRealOrderDefaults(t)
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
var orderSubmission = &order.Submit{
Exchange: o.Name,
Pair: currency.Pair{
@@ -998,48 +1132,67 @@ func TestSubmitOrder(t *testing.T) {
}
}
-// TestCancelExchangeOrder Wrapper test
func TestCancelExchangeOrder(t *testing.T) {
- TestSetRealOrderDefaults(t)
- currencyPair := currency.NewPair(currency.LTC, currency.BTC)
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
var orderCancellation = order.Cancel{
OrderID: "1",
WalletAddress: core.BitcoinDonationAddress,
AccountID: "1",
- Pair: currencyPair,
+ Pair: spotCurrency,
}
err := o.CancelOrder(context.Background(), &orderCancellation)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestCancelAllExchangeOrders Wrapper test
func TestCancelAllExchangeOrders(t *testing.T) {
- TestSetRealOrderDefaults(t)
- currencyPair := currency.NewPair(currency.LTC, currency.BTC)
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
var orderCancellation = order.Cancel{
OrderID: "1",
WalletAddress: core.BitcoinDonationAddress,
AccountID: "1",
- Pair: currencyPair,
+ Pair: spotCurrency,
}
resp, err := o.CancelAllOrders(context.Background(), &orderCancellation)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
if len(resp.Status) > 0 {
t.Errorf("%v orders failed to cancel", len(resp.Status))
}
}
-// TestGetAccountInfo Wrapper test
func TestGetAccountInfo(t *testing.T) {
+ t.Parallel()
_, err := o.UpdateAccountInfo(context.Background(), asset.Spot)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestModifyOrder Wrapper test
func TestModifyOrder(t *testing.T) {
- TestSetRealOrderDefaults(t)
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
_, err := o.ModifyOrder(context.Background(),
&order.Modify{AssetType: asset.Spot})
if err != common.ErrFunctionNotSupported {
@@ -1047,9 +1200,11 @@ func TestModifyOrder(t *testing.T) {
}
}
-// TestWithdraw Wrapper test
func TestWithdraw(t *testing.T) {
- TestSetRealOrderDefaults(t)
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
withdrawCryptoRequest := withdraw.Request{
Exchange: o.Name,
@@ -1065,12 +1220,19 @@ func TestWithdraw(t *testing.T) {
_, err := o.WithdrawCryptocurrencyFunds(context.Background(),
&withdrawCryptoRequest)
- testStandardErrorHandling(t, err)
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
}
-// TestWithdrawFiat Wrapper test
func TestWithdrawFiat(t *testing.T) {
- TestSetRealOrderDefaults(t)
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
var withdrawFiatRequest = withdraw.Request{}
_, err := o.WithdrawFiatFunds(context.Background(), &withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
@@ -1078,9 +1240,11 @@ func TestWithdrawFiat(t *testing.T) {
}
}
-// TestSubmitOrder Wrapper test
func TestWithdrawInternationalBank(t *testing.T) {
- TestSetRealOrderDefaults(t)
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
var withdrawFiatRequest = withdraw.Request{}
_, err := o.WithdrawFiatFundsToInternationalBank(context.Background(),
&withdrawFiatRequest)
@@ -1089,70 +1253,52 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
}
-// TestGetOrderbook logic test
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := o.GetOrderBook(context.Background(),
- &okgroup.GetOrderBookRequest{InstrumentID: "BTC-USDT"},
+ &GetOrderBookRequest{InstrumentID: spotCurrencyUpperStr},
asset.Spot)
if err != nil {
t.Error(err)
}
_, err = o.GetOrderBook(context.Background(),
- &okgroup.GetOrderBookRequest{InstrumentID: "Payload"},
+ &GetOrderBookRequest{InstrumentID: "Payload"},
asset.Futures)
if err == nil {
t.Error("error cannot be nil")
}
-
- _, err = o.GetOrderBook(context.Background(),
- &okgroup.GetOrderBookRequest{InstrumentID: "BTC-USD-SWAP"},
- asset.PerpetualSwap)
- if err == nil {
- t.Error("error cannot be nil")
- }
}
func TestGetHistoricCandles(t *testing.T) {
- currencyPair, err := currency.NewPairFromString("BTC-USD")
- if err != nil {
- t.Fatal(err)
- }
+ t.Parallel()
startTime := time.Unix(1588636800, 0)
- _, err = o.GetHistoricCandles(context.Background(),
- currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin)
+ _, err := o.GetHistoricCandles(context.Background(),
+ spotCurrency, asset.Spot, startTime, time.Now(), kline.OneMin)
if err != nil {
t.Fatal(err)
}
}
func TestGetHistoricCandlesExtended(t *testing.T) {
- currencyPair, err := currency.NewPairFromString("BTC-USD")
- if err != nil {
- t.Fatal(err)
- }
+ t.Parallel()
startTime := time.Unix(1588636800, 0)
- _, err = o.GetHistoricCandlesExtended(context.Background(),
- currencyPair, asset.Spot, startTime, time.Now(), kline.OneWeek)
+ _, err := o.GetHistoricCandlesExtended(context.Background(),
+ spotCurrency, asset.Spot, startTime, time.Now(), kline.OneWeek)
if err != nil {
t.Fatal(err)
}
_, err = o.GetHistoricCandles(context.Background(),
- currencyPair, asset.Spot, startTime, time.Now(), kline.Interval(time.Hour*7))
+ spotCurrency, asset.Spot, startTime, time.Now(), kline.Interval(time.Hour*7))
if err == nil {
- t.Fatal("unexpected result")
+ t.Error("error cannot be nil")
}
}
func TestGetRecentTrades(t *testing.T) {
t.Parallel()
- currencyPair, err := currency.NewPairFromString("BTC-USDT")
- if err != nil {
- t.Fatal(err)
- }
- _, err = o.GetRecentTrades(context.Background(), currencyPair, asset.Spot)
+ _, err := o.GetRecentTrades(context.Background(), spotCurrency, asset.Spot)
if err != nil {
t.Error(err)
}
@@ -1160,12 +1306,8 @@ func TestGetRecentTrades(t *testing.T) {
func TestGetHistoricTrades(t *testing.T) {
t.Parallel()
- currencyPair, err := currency.NewPairFromString("BTC-USDT")
- if err != nil {
- t.Fatal(err)
- }
- _, err = o.GetHistoricTrades(context.Background(),
- currencyPair, asset.Spot, time.Now().Add(-time.Minute*15), time.Now())
+ _, err := o.GetHistoricTrades(context.Background(),
+ spotCurrency, asset.Spot, time.Now().Add(-time.Minute*15), time.Now())
if err != nil && err != common.ErrFunctionNotSupported {
t.Error(err)
}
@@ -1173,11 +1315,7 @@ func TestGetHistoricTrades(t *testing.T) {
func TestUpdateTicker(t *testing.T) {
t.Parallel()
- cp, err := currency.NewPairFromString("BTC-USD")
- if err != nil {
- t.Fatal(err)
- }
- _, err = o.UpdateTicker(context.Background(), cp, asset.Spot)
+ _, err := o.UpdateTicker(context.Background(), spotCurrency, asset.Spot)
if err != nil {
t.Error(err)
}
@@ -1190,3 +1328,20 @@ func TestUpdateTickers(t *testing.T) {
t.Error(err)
}
}
+
+func TestGetMarginLoanHistory(t *testing.T) {
+ t.Parallel()
+ _, err := o.GetMarginLoanHistory(context.Background(), &GetMarginLoanHistoryRequest{
+ InstrumentID: spotCurrencyUpperStr,
+ Status: 1,
+ From: 1,
+ To: 1,
+ Limit: 1,
+ })
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Error(err)
+ }
+}
diff --git a/exchanges/okgroup/okgroup_types.go b/exchanges/okcoin/okcoin_types.go
similarity index 81%
rename from exchanges/okgroup/okgroup_types.go
rename to exchanges/okcoin/okcoin_types.go
index 5e1b102a..6a82bc0c 100644
--- a/exchanges/okgroup/okgroup_types.go
+++ b/exchanges/okcoin/okcoin_types.go
@@ -1,18 +1,17 @@
-package okgroup
+package okcoin
import (
+ "errors"
+ "sync"
"time"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
)
-// Order types
-const (
- NormalOrder = iota
- PostOnlyOrder
- FillOrKillOrder
- ImmediateOrCancelOrder
+var (
+ errNoAccountDepositAddress = errors.New("no account deposit address")
+ errIncorrectCandleDataLength = errors.New("incorrect candles data length")
)
// PerpSwapInstrumentData stores instrument data for perpetual swap contracts
@@ -106,9 +105,11 @@ type PerpSwapFundingRates struct {
type GetAccountCurrenciesResponse struct {
Name string `json:"name"`
Currency string `json:"currency"`
- CanDeposit int `json:"can_deposit,string"`
- CanWithdraw int `json:"can_withdraw,string"`
- MinWithdrawal float64 `json:"min_withdrawal,string"`
+ Chain string `json:"chain"`
+ CanInternal bool `json:"can_internal,string"`
+ CanWithdraw bool `json:"can_withdraw,string"`
+ CanDeposit bool `json:"can_deposit,string"`
+ MinWithdrawal float64 `json:"min_withdrawal"`
}
// WalletInformationResponse response data for WalletInformation
@@ -435,7 +436,8 @@ type GetMarketDataRequest struct {
// low string Lowest price
// close string Close price
// volume string Trading volume
-type GetMarketDataResponse []interface{}
+type GetMarketDataResponse struct {
+}
// GetMarginAccountsResponse response data for GetMarginAccounts
type GetMarginAccountsResponse struct {
@@ -1354,7 +1356,7 @@ type GetETTSettlementPriceHistoryResponse struct {
Price float64 `json:"price"`
}
-// OrderStatus Holds OKGroup order status values
+// OrderStatus Holds Okcoin order status values
var OrderStatus = map[int64]string{
-3: "pending cancel",
-2: "cancelled",
@@ -1517,3 +1519,294 @@ type WebsocketErrorResponse struct {
Message string `json:"message"`
ErrorCode int64 `json:"errorCode"`
}
+
+// List of all websocket channels to subscribe to
+const (
+ // Orderbook events
+ okcoinWsOrderbookUpdate = "update"
+ okcoinWsOrderbookPartial = "partial"
+ // API subsections
+ okcoinWsSwapSubsection = "swap/"
+ okcoinWsIndexSubsection = "index/"
+ okcoinWsFuturesSubsection = "futures/"
+ okcoinWsSpotSubsection = "spot/"
+ // Shared API endpoints
+ okcoinWsCandle = "candle"
+ okcoinWsCandle60s = okcoinWsCandle + "60s"
+ okcoinWsCandle180s = okcoinWsCandle + "180s"
+ okcoinWsCandle300s = okcoinWsCandle + "300s"
+ okcoinWsCandle900s = okcoinWsCandle + "900s"
+ okcoinWsCandle1800s = okcoinWsCandle + "1800s"
+ okcoinWsCandle3600s = okcoinWsCandle + "3600s"
+ okcoinWsCandle7200s = okcoinWsCandle + "7200s"
+ okcoinWsCandle14400s = okcoinWsCandle + "14400s"
+ okcoinWsCandle21600s = okcoinWsCandle + "21600"
+ okcoinWsCandle43200s = okcoinWsCandle + "43200s"
+ okcoinWsCandle86400s = okcoinWsCandle + "86400s"
+ okcoinWsCandle604900s = okcoinWsCandle + "604800s"
+ okcoinWsTicker = "ticker"
+ okcoinWsTrade = "trade"
+ okcoinWsDepth = "depth"
+ okcoinWsDepth5 = "depth5"
+ okcoinWsAccount = "account"
+ okcoinWsMarginAccount = "margin_account"
+ okcoinWsOrder = "order"
+ okcoinWsFundingRate = "funding_rate"
+ okcoinWsPriceRange = "price_range"
+ okcoinWsMarkPrice = "mark_price"
+ okcoinWsPosition = "position"
+ okcoinWsEstimatedPrice = "estimated_price"
+ // Spot endpoints
+ okcoinWsSpotTicker = okcoinWsSpotSubsection + okcoinWsTicker
+ okcoinWsSpotCandle60s = okcoinWsSpotSubsection + okcoinWsCandle60s
+ okcoinWsSpotCandle180s = okcoinWsSpotSubsection + okcoinWsCandle180s
+ okcoinWsSpotCandle300s = okcoinWsSpotSubsection + okcoinWsCandle300s
+ okcoinWsSpotCandle900s = okcoinWsSpotSubsection + okcoinWsCandle900s
+ okcoinWsSpotCandle1800s = okcoinWsSpotSubsection + okcoinWsCandle1800s
+ okcoinWsSpotCandle3600s = okcoinWsSpotSubsection + okcoinWsCandle3600s
+ okcoinWsSpotCandle7200s = okcoinWsSpotSubsection + okcoinWsCandle7200s
+ okcoinWsSpotCandle14400s = okcoinWsSpotSubsection + okcoinWsCandle14400s
+ okcoinWsSpotCandle21600s = okcoinWsSpotSubsection + okcoinWsCandle21600s
+ okcoinWsSpotCandle43200s = okcoinWsSpotSubsection + okcoinWsCandle43200s
+ okcoinWsSpotCandle86400s = okcoinWsSpotSubsection + okcoinWsCandle86400s
+ okcoinWsSpotCandle604900s = okcoinWsSpotSubsection + okcoinWsCandle604900s
+ okcoinWsSpotTrade = okcoinWsSpotSubsection + okcoinWsTrade
+ okcoinWsSpotDepth = okcoinWsSpotSubsection + okcoinWsDepth
+ okcoinWsSpotDepth5 = okcoinWsSpotSubsection + okcoinWsDepth5
+ okcoinWsSpotAccount = okcoinWsSpotSubsection + okcoinWsAccount
+ okcoinWsSpotMarginAccount = okcoinWsSpotSubsection + okcoinWsMarginAccount
+ okcoinWsSpotOrder = okcoinWsSpotSubsection + okcoinWsOrder
+ // Swap endpoints
+ okcoinWsSwapTicker = okcoinWsSwapSubsection + okcoinWsTicker
+ okcoinWsSwapCandle60s = okcoinWsSwapSubsection + okcoinWsCandle60s
+ okcoinWsSwapCandle180s = okcoinWsSwapSubsection + okcoinWsCandle180s
+ okcoinWsSwapCandle300s = okcoinWsSwapSubsection + okcoinWsCandle300s
+ okcoinWsSwapCandle900s = okcoinWsSwapSubsection + okcoinWsCandle900s
+ okcoinWsSwapCandle1800s = okcoinWsSwapSubsection + okcoinWsCandle1800s
+ okcoinWsSwapCandle3600s = okcoinWsSwapSubsection + okcoinWsCandle3600s
+ okcoinWsSwapCandle7200s = okcoinWsSwapSubsection + okcoinWsCandle7200s
+ okcoinWsSwapCandle14400s = okcoinWsSwapSubsection + okcoinWsCandle14400s
+ okcoinWsSwapCandle21600s = okcoinWsSwapSubsection + okcoinWsCandle21600s
+ okcoinWsSwapCandle43200s = okcoinWsSwapSubsection + okcoinWsCandle43200s
+ okcoinWsSwapCandle86400s = okcoinWsSwapSubsection + okcoinWsCandle86400s
+ okcoinWsSwapCandle604900s = okcoinWsSwapSubsection + okcoinWsCandle604900s
+ okcoinWsSwapTrade = okcoinWsSwapSubsection + okcoinWsTrade
+ okcoinWsSwapDepth = okcoinWsSwapSubsection + okcoinWsDepth
+ okcoinWsSwapDepth5 = okcoinWsSwapSubsection + okcoinWsDepth5
+ okcoinWsSwapFundingRate = okcoinWsSwapSubsection + okcoinWsFundingRate
+ okcoinWsSwapPriceRange = okcoinWsSwapSubsection + okcoinWsPriceRange
+ okcoinWsSwapMarkPrice = okcoinWsSwapSubsection + okcoinWsMarkPrice
+ okcoinWsSwapPosition = okcoinWsSwapSubsection + okcoinWsPosition
+ okcoinWsSwapAccount = okcoinWsSwapSubsection + okcoinWsAccount
+ okcoinWsSwapOrder = okcoinWsSwapSubsection + okcoinWsOrder
+ // Index endpoints
+ okcoinWsIndexTicker = okcoinWsIndexSubsection + okcoinWsTicker
+ okcoinWsIndexCandle60s = okcoinWsIndexSubsection + okcoinWsCandle60s
+ okcoinWsIndexCandle180s = okcoinWsIndexSubsection + okcoinWsCandle180s
+ okcoinWsIndexCandle300s = okcoinWsIndexSubsection + okcoinWsCandle300s
+ okcoinWsIndexCandle900s = okcoinWsIndexSubsection + okcoinWsCandle900s
+ okcoinWsIndexCandle1800s = okcoinWsIndexSubsection + okcoinWsCandle1800s
+ okcoinWsIndexCandle3600s = okcoinWsIndexSubsection + okcoinWsCandle3600s
+ okcoinWsIndexCandle7200s = okcoinWsIndexSubsection + okcoinWsCandle7200s
+ okcoinWsIndexCandle14400s = okcoinWsIndexSubsection + okcoinWsCandle14400s
+ okcoinWsIndexCandle21600s = okcoinWsIndexSubsection + okcoinWsCandle21600s
+ okcoinWsIndexCandle43200s = okcoinWsIndexSubsection + okcoinWsCandle43200s
+ okcoinWsIndexCandle86400s = okcoinWsIndexSubsection + okcoinWsCandle86400s
+ okcoinWsIndexCandle604900s = okcoinWsIndexSubsection + okcoinWsCandle604900s
+ // Futures endpoints
+ okcoinWsFuturesTicker = okcoinWsFuturesSubsection + okcoinWsTicker
+ okcoinWsFuturesCandle60s = okcoinWsFuturesSubsection + okcoinWsCandle60s
+ okcoinWsFuturesCandle180s = okcoinWsFuturesSubsection + okcoinWsCandle180s
+ okcoinWsFuturesCandle300s = okcoinWsFuturesSubsection + okcoinWsCandle300s
+ okcoinWsFuturesCandle900s = okcoinWsFuturesSubsection + okcoinWsCandle900s
+ okcoinWsFuturesCandle1800s = okcoinWsFuturesSubsection + okcoinWsCandle1800s
+ okcoinWsFuturesCandle3600s = okcoinWsFuturesSubsection + okcoinWsCandle3600s
+ okcoinWsFuturesCandle7200s = okcoinWsFuturesSubsection + okcoinWsCandle7200s
+ okcoinWsFuturesCandle14400s = okcoinWsFuturesSubsection + okcoinWsCandle14400s
+ okcoinWsFuturesCandle21600s = okcoinWsFuturesSubsection + okcoinWsCandle21600s
+ okcoinWsFuturesCandle43200s = okcoinWsFuturesSubsection + okcoinWsCandle43200s
+ okcoinWsFuturesCandle86400s = okcoinWsFuturesSubsection + okcoinWsCandle86400s
+ okcoinWsFuturesCandle604900s = okcoinWsFuturesSubsection + okcoinWsCandle604900s
+ okcoinWsFuturesTrade = okcoinWsFuturesSubsection + okcoinWsTrade
+ okcoinWsFuturesEstimatedPrice = okcoinWsFuturesSubsection + okcoinWsTrade
+ okcoinWsFuturesPriceRange = okcoinWsFuturesSubsection + okcoinWsPriceRange
+ okcoinWsFuturesDepth = okcoinWsFuturesSubsection + okcoinWsDepth
+ okcoinWsFuturesDepth5 = okcoinWsFuturesSubsection + okcoinWsDepth5
+ okcoinWsFuturesMarkPrice = okcoinWsFuturesSubsection + okcoinWsMarkPrice
+ okcoinWsFuturesAccount = okcoinWsFuturesSubsection + okcoinWsAccount
+ okcoinWsFuturesPosition = okcoinWsFuturesSubsection + okcoinWsPosition
+ okcoinWsFuturesOrder = okcoinWsFuturesSubsection + okcoinWsOrder
+
+ okcoinWsRateLimit = 30
+
+ allowableIterations = 25
+ delimiterColon = ":"
+ delimiterDash = "-"
+
+ maxConnByteLen = 4096
+)
+
+// orderbookMutex Ensures if two entries arrive at once, only one can be
+// processed at a time
+var orderbookMutex sync.Mutex
+
+var defaultSpotSubscribedChannels = []string{okcoinWsSpotDepth,
+ okcoinWsSpotCandle300s,
+ okcoinWsSpotTicker,
+ okcoinWsSpotTrade}
+
+var defaultFuturesSubscribedChannels = []string{okcoinWsFuturesDepth,
+ okcoinWsFuturesCandle300s,
+ okcoinWsFuturesTicker,
+ okcoinWsFuturesTrade}
+
+var defaultIndexSubscribedChannels = []string{okcoinWsIndexCandle300s,
+ okcoinWsIndexTicker}
+
+var defaultSwapSubscribedChannels = []string{okcoinWsSwapDepth,
+ okcoinWsSwapCandle300s,
+ okcoinWsSwapTicker,
+ okcoinWsSwapTrade,
+ okcoinWsSwapFundingRate,
+ okcoinWsSwapMarkPrice}
+
+// SetErrorDefaults sets the full error default list
+func (o *OKCoin) SetErrorDefaults() {
+ o.ErrorCodes = map[string]error{
+ "0": errors.New("successful"),
+ "1": errors.New("invalid parameter in url normally"),
+ "30001": errors.New("request header \"OK_ACCESS_KEY\" cannot be blank"),
+ "30002": errors.New("request header \"OK_ACCESS_SIGN\" cannot be blank"),
+ "30003": errors.New("request header \"OK_ACCESS_TIMESTAMP\" cannot be blank"),
+ "30004": errors.New("request header \"OK_ACCESS_PASSPHRASE\" cannot be blank"),
+ "30005": errors.New("invalid OK_ACCESS_TIMESTAMP"),
+ "30006": errors.New("invalid OK_ACCESS_KEY"),
+ "30007": errors.New("invalid Content_Type, please use \"application/json\" format"),
+ "30008": errors.New("timestamp request expired"),
+ "30009": errors.New("system error"),
+ "30010": errors.New("api validation failed"),
+ "30011": errors.New("invalid IP"),
+ "30012": errors.New("invalid authorization"),
+ "30013": errors.New("invalid sign"),
+ "30014": errors.New("request too frequent"),
+ "30015": errors.New("request header \"OK_ACCESS_PASSPHRASE\" incorrect"),
+ "30016": errors.New("you are using v1 apiKey, please use v1 endpoint. If you would like to use v3 endpoint, please subscribe to v3 apiKey"),
+ "30017": errors.New("apikey's broker id does not match"),
+ "30018": errors.New("apikey's domain does not match"),
+ "30020": errors.New("body cannot be blank"),
+ "30021": errors.New("json data format error"),
+ "30023": errors.New("required parameter cannot be blank"),
+ "30024": errors.New("parameter value error"),
+ "30025": errors.New("parameter category error"),
+ "30026": errors.New("requested too frequent; endpoint limit exceeded"),
+ "30027": errors.New("login failure"),
+ "30028": errors.New("unauthorized execution"),
+ "30029": errors.New("account suspended"),
+ "30030": errors.New("endpoint request failed. Please try again"),
+ "30031": errors.New("token does not exist"),
+ "30032": errors.New("pair does not exist"),
+ "30033": errors.New("exchange domain does not exist"),
+ "30034": errors.New("exchange ID does not exist"),
+ "30035": errors.New("trading is not supported in this website"),
+ "30036": errors.New("no relevant data"),
+ "30037": errors.New("endpoint is offline or unavailable"),
+ "30038": errors.New("user does not exist"),
+ "32001": errors.New("futures account suspended"),
+ "32002": errors.New("futures account does not exist"),
+ "32003": errors.New("canceling, please wait"),
+ "32004": errors.New("you have no unfilled orders"),
+ "32005": errors.New("max order quantity"),
+ "32006": errors.New("the order price or trigger price exceeds USD 1 million"),
+ "32007": errors.New("leverage level must be the same for orders on the same side of the contract"),
+ "32008": errors.New("max. positions to open (cross margin)"),
+ "32009": errors.New("max. positions to open (fixed margin)"),
+ "32010": errors.New("leverage cannot be changed with open positions"),
+ "32011": errors.New("futures status error"),
+ "32012": errors.New("futures order update error"),
+ "32013": errors.New("token type is blank"),
+ "32014": errors.New("your number of contracts closing is larger than the number of contracts available"),
+ "32015": errors.New("margin ratio is lower than 100% before opening positions"),
+ "32016": errors.New("margin ratio is lower than 100% after opening position"),
+ "32017": errors.New("no BBO"),
+ "32018": errors.New("the order quantity is less than 1, please try again"),
+ "32019": errors.New("the order price deviates from the price of the previous minute by more than 3%"),
+ "32020": errors.New("the price is not in the range of the price limit"),
+ "32021": errors.New("leverage error"),
+ "32022": errors.New("this function is not supported in your country or region according to the regulations"),
+ "32023": errors.New("this account has outstanding loan"),
+ "32024": errors.New("order cannot be placed during delivery"),
+ "32025": errors.New("order cannot be placed during settlement"),
+ "32026": errors.New("your account is restricted from opening positions"),
+ "32027": errors.New("cancelled over 20 orders"),
+ "32028": errors.New("account is suspended and liquidated"),
+ "32029": errors.New("order info does not exist"),
+ "33001": errors.New("margin account for this pair is not enabled yet"),
+ "33002": errors.New("margin account for this pair is suspended"),
+ "33003": errors.New("no loan balance"),
+ "33004": errors.New("loan amount cannot be smaller than the minimum limit"),
+ "33005": errors.New("repayment amount must exceed 0"),
+ "33006": errors.New("loan order not found"),
+ "33007": errors.New("status not found"),
+ "33008": errors.New("loan amount cannot exceed the maximum limit"),
+ "33009": errors.New("user ID is blank"),
+ "33010": errors.New("you cannot cancel an order during session 2 of call auction"),
+ "33011": errors.New("no new market data"),
+ "33012": errors.New("order cancellation failed"),
+ "33013": errors.New("order placement failed"),
+ "33014": errors.New("order does not exist"),
+ "33015": errors.New("exceeded maximum limit"),
+ "33016": errors.New("margin trading is not open for this token"),
+ "33017": errors.New("insufficient balance"),
+ "33018": errors.New("this parameter must be smaller than 1"),
+ "33020": errors.New("request not supported"),
+ "33021": errors.New("token and the pair do not match"),
+ "33022": errors.New("pair and the order do not match"),
+ "33023": errors.New("you can only place market orders during call auction"),
+ "33024": errors.New("trading amount too small"),
+ "33025": errors.New("base token amount is blank"),
+ "33026": errors.New("transaction completed"),
+ "33027": errors.New("cancelled order or order cancelling"),
+ "33028": errors.New("the decimal places of the trading price exceeded the limit"),
+ "33029": errors.New("the decimal places of the trading size exceeded the limit"),
+ "34001": errors.New("withdrawal suspended"),
+ "34002": errors.New("please add a withdrawal address"),
+ "34003": errors.New("sorry, this token cannot be withdrawn to xx at the moment"),
+ "34004": errors.New("withdrawal fee is smaller than minimum limit"),
+ "34005": errors.New("withdrawal fee exceeds the maximum limit"),
+ "34006": errors.New("withdrawal amount is lower than the minimum limit"),
+ "34007": errors.New("withdrawal amount exceeds the maximum limit"),
+ "34008": errors.New("insufficient balance"),
+ "34009": errors.New("your withdrawal amount exceeds the daily limit"),
+ "34010": errors.New("transfer amount must be larger than 0"),
+ "34011": errors.New("conditions not met"),
+ "34012": errors.New("the minimum withdrawal amount for NEO is 1, and the amount must be an integer"),
+ "34013": errors.New("please transfer"),
+ "34014": errors.New("transfer limited"),
+ "34015": errors.New("subaccount does not exist"),
+ "34016": errors.New("transfer suspended"),
+ "34017": errors.New("account suspended"),
+ "34018": errors.New("incorrect trades password"),
+ "34019": errors.New("please bind your email before withdrawal"),
+ "34020": errors.New("please bind your funds password before withdrawal"),
+ "34021": errors.New("not verified address"),
+ "34022": errors.New("withdrawals are not available for sub accounts"),
+ "35001": errors.New("contract subscribing does not exist"),
+ "35002": errors.New("contract is being settled"),
+ "35003": errors.New("contract is being paused"),
+ "35004": errors.New("pending contract settlement"),
+ "35005": errors.New("perpetual swap trading is not enabled"),
+ "35008": errors.New("margin ratio too low when placing order"),
+ "35010": errors.New("closing position size larger than available size"),
+ "35012": errors.New("placing an order with less than 1 contract"),
+ "35014": errors.New("order size is not in acceptable range"),
+ "35015": errors.New("leverage level unavailable"),
+ "35017": errors.New("changing leverage level"),
+ "35019": errors.New("order size exceeds limit"),
+ "35020": errors.New("order price exceeds limit"),
+ "35021": errors.New("order size exceeds limit of the current tier"),
+ "35022": errors.New("contract is paused or closed"),
+ "35030": errors.New("place multiple orders"),
+ "35031": errors.New("cancel multiple orders"),
+ "35061": errors.New("invalid instrument_id"),
+ }
+}
diff --git a/exchanges/okgroup/okgroup_websocket.go b/exchanges/okcoin/okcoin_websocket.go
similarity index 54%
rename from exchanges/okgroup/okgroup_websocket.go
rename to exchanges/okcoin/okcoin_websocket.go
index e5b34eec..5ae3ac7f 100644
--- a/exchanges/okgroup/okgroup_websocket.go
+++ b/exchanges/okcoin/okcoin_websocket.go
@@ -1,4 +1,4 @@
-package okgroup
+package okcoin
import (
"context"
@@ -9,7 +9,6 @@ import (
"net/http"
"strconv"
"strings"
- "sync"
"time"
"github.com/gorilla/websocket"
@@ -24,158 +23,8 @@ import (
"github.com/thrasher-corp/gocryptotrader/log"
)
-// List of all websocket channels to subscribe to
-const (
- // Orderbook events
- okGroupWsOrderbookUpdate = "update"
- okGroupWsOrderbookPartial = "partial"
- // API subsections
- okGroupWsSwapSubsection = "swap/"
- okGroupWsIndexSubsection = "index/"
- okGroupWsFuturesSubsection = "futures/"
- okGroupWsSpotSubsection = "spot/"
- // Shared API endpoints
- okGroupWsCandle = "candle"
- okGroupWsCandle60s = okGroupWsCandle + "60s"
- okGroupWsCandle180s = okGroupWsCandle + "180s"
- okGroupWsCandle300s = okGroupWsCandle + "300s"
- okGroupWsCandle900s = okGroupWsCandle + "900s"
- okGroupWsCandle1800s = okGroupWsCandle + "1800s"
- okGroupWsCandle3600s = okGroupWsCandle + "3600s"
- okGroupWsCandle7200s = okGroupWsCandle + "7200s"
- okGroupWsCandle14400s = okGroupWsCandle + "14400s"
- okGroupWsCandle21600s = okGroupWsCandle + "21600"
- okGroupWsCandle43200s = okGroupWsCandle + "43200s"
- okGroupWsCandle86400s = okGroupWsCandle + "86400s"
- okGroupWsCandle604900s = okGroupWsCandle + "604800s"
- okGroupWsTicker = "ticker"
- okGroupWsTrade = "trade"
- okGroupWsDepth = "depth"
- okGroupWsDepth5 = "depth5"
- okGroupWsAccount = "account"
- okGroupWsMarginAccount = "margin_account"
- okGroupWsOrder = "order"
- okGroupWsFundingRate = "funding_rate"
- okGroupWsPriceRange = "price_range"
- okGroupWsMarkPrice = "mark_price"
- okGroupWsPosition = "position"
- okGroupWsEstimatedPrice = "estimated_price"
- // Spot endpoints
- okGroupWsSpotTicker = okGroupWsSpotSubsection + okGroupWsTicker
- okGroupWsSpotCandle60s = okGroupWsSpotSubsection + okGroupWsCandle60s
- okGroupWsSpotCandle180s = okGroupWsSpotSubsection + okGroupWsCandle180s
- okGroupWsSpotCandle300s = okGroupWsSpotSubsection + okGroupWsCandle300s
- okGroupWsSpotCandle900s = okGroupWsSpotSubsection + okGroupWsCandle900s
- okGroupWsSpotCandle1800s = okGroupWsSpotSubsection + okGroupWsCandle1800s
- okGroupWsSpotCandle3600s = okGroupWsSpotSubsection + okGroupWsCandle3600s
- okGroupWsSpotCandle7200s = okGroupWsSpotSubsection + okGroupWsCandle7200s
- okGroupWsSpotCandle14400s = okGroupWsSpotSubsection + okGroupWsCandle14400s
- okGroupWsSpotCandle21600s = okGroupWsSpotSubsection + okGroupWsCandle21600s
- okGroupWsSpotCandle43200s = okGroupWsSpotSubsection + okGroupWsCandle43200s
- okGroupWsSpotCandle86400s = okGroupWsSpotSubsection + okGroupWsCandle86400s
- okGroupWsSpotCandle604900s = okGroupWsSpotSubsection + okGroupWsCandle604900s
- okGroupWsSpotTrade = okGroupWsSpotSubsection + okGroupWsTrade
- okGroupWsSpotDepth = okGroupWsSpotSubsection + okGroupWsDepth
- okGroupWsSpotDepth5 = okGroupWsSpotSubsection + okGroupWsDepth5
- okGroupWsSpotAccount = okGroupWsSpotSubsection + okGroupWsAccount
- okGroupWsSpotMarginAccount = okGroupWsSpotSubsection + okGroupWsMarginAccount
- okGroupWsSpotOrder = okGroupWsSpotSubsection + okGroupWsOrder
- // Swap endpoints
- okGroupWsSwapTicker = okGroupWsSwapSubsection + okGroupWsTicker
- okGroupWsSwapCandle60s = okGroupWsSwapSubsection + okGroupWsCandle60s
- okGroupWsSwapCandle180s = okGroupWsSwapSubsection + okGroupWsCandle180s
- okGroupWsSwapCandle300s = okGroupWsSwapSubsection + okGroupWsCandle300s
- okGroupWsSwapCandle900s = okGroupWsSwapSubsection + okGroupWsCandle900s
- okGroupWsSwapCandle1800s = okGroupWsSwapSubsection + okGroupWsCandle1800s
- okGroupWsSwapCandle3600s = okGroupWsSwapSubsection + okGroupWsCandle3600s
- okGroupWsSwapCandle7200s = okGroupWsSwapSubsection + okGroupWsCandle7200s
- okGroupWsSwapCandle14400s = okGroupWsSwapSubsection + okGroupWsCandle14400s
- okGroupWsSwapCandle21600s = okGroupWsSwapSubsection + okGroupWsCandle21600s
- okGroupWsSwapCandle43200s = okGroupWsSwapSubsection + okGroupWsCandle43200s
- okGroupWsSwapCandle86400s = okGroupWsSwapSubsection + okGroupWsCandle86400s
- okGroupWsSwapCandle604900s = okGroupWsSwapSubsection + okGroupWsCandle604900s
- okGroupWsSwapTrade = okGroupWsSwapSubsection + okGroupWsTrade
- okGroupWsSwapDepth = okGroupWsSwapSubsection + okGroupWsDepth
- okGroupWsSwapDepth5 = okGroupWsSwapSubsection + okGroupWsDepth5
- okGroupWsSwapFundingRate = okGroupWsSwapSubsection + okGroupWsFundingRate
- okGroupWsSwapPriceRange = okGroupWsSwapSubsection + okGroupWsPriceRange
- okGroupWsSwapMarkPrice = okGroupWsSwapSubsection + okGroupWsMarkPrice
- okGroupWsSwapPosition = okGroupWsSwapSubsection + okGroupWsPosition
- okGroupWsSwapAccount = okGroupWsSwapSubsection + okGroupWsAccount
- okGroupWsSwapOrder = okGroupWsSwapSubsection + okGroupWsOrder
- // Index endpoints
- okGroupWsIndexTicker = okGroupWsIndexSubsection + okGroupWsTicker
- okGroupWsIndexCandle60s = okGroupWsIndexSubsection + okGroupWsCandle60s
- okGroupWsIndexCandle180s = okGroupWsIndexSubsection + okGroupWsCandle180s
- okGroupWsIndexCandle300s = okGroupWsIndexSubsection + okGroupWsCandle300s
- okGroupWsIndexCandle900s = okGroupWsIndexSubsection + okGroupWsCandle900s
- okGroupWsIndexCandle1800s = okGroupWsIndexSubsection + okGroupWsCandle1800s
- okGroupWsIndexCandle3600s = okGroupWsIndexSubsection + okGroupWsCandle3600s
- okGroupWsIndexCandle7200s = okGroupWsIndexSubsection + okGroupWsCandle7200s
- okGroupWsIndexCandle14400s = okGroupWsIndexSubsection + okGroupWsCandle14400s
- okGroupWsIndexCandle21600s = okGroupWsIndexSubsection + okGroupWsCandle21600s
- okGroupWsIndexCandle43200s = okGroupWsIndexSubsection + okGroupWsCandle43200s
- okGroupWsIndexCandle86400s = okGroupWsIndexSubsection + okGroupWsCandle86400s
- okGroupWsIndexCandle604900s = okGroupWsIndexSubsection + okGroupWsCandle604900s
- // Futures endpoints
- okGroupWsFuturesTicker = okGroupWsFuturesSubsection + okGroupWsTicker
- okGroupWsFuturesCandle60s = okGroupWsFuturesSubsection + okGroupWsCandle60s
- okGroupWsFuturesCandle180s = okGroupWsFuturesSubsection + okGroupWsCandle180s
- okGroupWsFuturesCandle300s = okGroupWsFuturesSubsection + okGroupWsCandle300s
- okGroupWsFuturesCandle900s = okGroupWsFuturesSubsection + okGroupWsCandle900s
- okGroupWsFuturesCandle1800s = okGroupWsFuturesSubsection + okGroupWsCandle1800s
- okGroupWsFuturesCandle3600s = okGroupWsFuturesSubsection + okGroupWsCandle3600s
- okGroupWsFuturesCandle7200s = okGroupWsFuturesSubsection + okGroupWsCandle7200s
- okGroupWsFuturesCandle14400s = okGroupWsFuturesSubsection + okGroupWsCandle14400s
- okGroupWsFuturesCandle21600s = okGroupWsFuturesSubsection + okGroupWsCandle21600s
- okGroupWsFuturesCandle43200s = okGroupWsFuturesSubsection + okGroupWsCandle43200s
- okGroupWsFuturesCandle86400s = okGroupWsFuturesSubsection + okGroupWsCandle86400s
- okGroupWsFuturesCandle604900s = okGroupWsFuturesSubsection + okGroupWsCandle604900s
- okGroupWsFuturesTrade = okGroupWsFuturesSubsection + okGroupWsTrade
- okGroupWsFuturesEstimatedPrice = okGroupWsFuturesSubsection + okGroupWsTrade
- okGroupWsFuturesPriceRange = okGroupWsFuturesSubsection + okGroupWsPriceRange
- okGroupWsFuturesDepth = okGroupWsFuturesSubsection + okGroupWsDepth
- okGroupWsFuturesDepth5 = okGroupWsFuturesSubsection + okGroupWsDepth5
- okGroupWsFuturesMarkPrice = okGroupWsFuturesSubsection + okGroupWsMarkPrice
- okGroupWsFuturesAccount = okGroupWsFuturesSubsection + okGroupWsAccount
- okGroupWsFuturesPosition = okGroupWsFuturesSubsection + okGroupWsPosition
- okGroupWsFuturesOrder = okGroupWsFuturesSubsection + okGroupWsOrder
-
- okGroupWsRateLimit = 30
-
- allowableIterations = 25
- delimiterColon = ":"
- delimiterDash = "-"
-
- maxConnByteLen = 4096
-)
-
-// orderbookMutex Ensures if two entries arrive at once, only one can be
-// processed at a time
-var orderbookMutex sync.Mutex
-
-var defaultSpotSubscribedChannels = []string{okGroupWsSpotDepth,
- okGroupWsSpotCandle300s,
- okGroupWsSpotTicker,
- okGroupWsSpotTrade}
-
-var defaultFuturesSubscribedChannels = []string{okGroupWsFuturesDepth,
- okGroupWsFuturesCandle300s,
- okGroupWsFuturesTicker,
- okGroupWsFuturesTrade}
-
-var defaultIndexSubscribedChannels = []string{okGroupWsIndexCandle300s,
- okGroupWsIndexTicker}
-
-var defaultSwapSubscribedChannels = []string{okGroupWsSwapDepth,
- okGroupWsSwapCandle300s,
- okGroupWsSwapTicker,
- okGroupWsSwapTrade,
- okGroupWsSwapFundingRate,
- okGroupWsSwapMarkPrice}
-
// WsConnect initiates a websocket connection
-func (o *OKGroup) WsConnect() error {
+func (o *OKCoin) WsConnect() error {
if !o.Websocket.IsEnabled() || !o.IsEnabled() {
return errors.New(stream.WebsocketNotEnabled)
}
@@ -208,7 +57,7 @@ func (o *OKGroup) WsConnect() error {
}
// WsLogin sends a login request to websocket to enable access to authenticated endpoints
-func (o *OKGroup) WsLogin(ctx context.Context) error {
+func (o *OKCoin) WsLogin(ctx context.Context) error {
creds, err := o.GetCredentials(ctx)
if err != nil {
return err
@@ -242,7 +91,7 @@ func (o *OKGroup) WsLogin(ctx context.Context) error {
}
// WsReadData receives and passes on websocket messages for processing
-func (o *OKGroup) WsReadData() {
+func (o *OKCoin) WsReadData() {
defer o.Websocket.Wg.Done()
for {
@@ -258,7 +107,7 @@ func (o *OKGroup) WsReadData() {
}
// WsHandleData will read websocket raw data and pass to appropriate handler
-func (o *OKGroup) WsHandleData(respRaw []byte) error {
+func (o *OKCoin) WsHandleData(respRaw []byte) error {
var dataResponse WebsocketDataResponse
err := json.Unmarshal(respRaw, &dataResponse)
if err != nil {
@@ -266,18 +115,18 @@ func (o *OKGroup) WsHandleData(respRaw []byte) error {
}
if len(dataResponse.Data) > 0 {
switch o.GetWsChannelWithoutOrderType(dataResponse.Table) {
- case okGroupWsCandle60s, okGroupWsCandle180s, okGroupWsCandle300s,
- okGroupWsCandle900s, okGroupWsCandle1800s, okGroupWsCandle3600s,
- okGroupWsCandle7200s, okGroupWsCandle14400s, okGroupWsCandle21600s,
- okGroupWsCandle43200s, okGroupWsCandle86400s, okGroupWsCandle604900s:
+ case okcoinWsCandle60s, okcoinWsCandle180s, okcoinWsCandle300s,
+ okcoinWsCandle900s, okcoinWsCandle1800s, okcoinWsCandle3600s,
+ okcoinWsCandle7200s, okcoinWsCandle14400s, okcoinWsCandle21600s,
+ okcoinWsCandle43200s, okcoinWsCandle86400s, okcoinWsCandle604900s:
return o.wsProcessCandles(respRaw)
- case okGroupWsDepth, okGroupWsDepth5:
+ case okcoinWsDepth, okcoinWsDepth5:
return o.WsProcessOrderBook(respRaw)
- case okGroupWsTicker:
+ case okcoinWsTicker:
return o.wsProcessTickers(respRaw)
- case okGroupWsTrade:
+ case okcoinWsTrade:
return o.wsProcessTrades(respRaw)
- case okGroupWsOrder:
+ case okcoinWsOrder:
return o.wsProcessOrder(respRaw)
}
o.Websocket.DataHandler <- stream.UnhandledMessageWarning{
@@ -332,7 +181,7 @@ func StringToOrderStatus(num int64) (order.Status, error) {
}
}
-func (o *OKGroup) wsProcessOrder(respRaw []byte) error {
+func (o *OKCoin) wsProcessOrder(respRaw []byte) error {
var resp WebsocketSpotOrderResponse
err := json.Unmarshal(respRaw, &resp)
if err != nil {
@@ -398,7 +247,7 @@ func (o *OKGroup) wsProcessOrder(respRaw []byte) error {
}
// wsProcessTickers converts ticker data and sends it to the datahandler
-func (o *OKGroup) wsProcessTickers(respRaw []byte) error {
+func (o *OKCoin) wsProcessTickers(respRaw []byte) error {
var response WebsocketTickerData
err := json.Unmarshal(respRaw, &response)
if err != nil {
@@ -408,15 +257,7 @@ func (o *OKGroup) wsProcessTickers(respRaw []byte) error {
for i := range response.Data {
f := strings.Split(response.Data[i].InstrumentID, delimiterDash)
- var c currency.Pair
- switch a {
- case asset.Futures, asset.PerpetualSwap:
- c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1],
- f[2],
- currency.UnderscoreDelimiter)
- default:
- c = currency.NewPairWithDelimiter(f[0], f[1], delimiterDash)
- }
+ c := currency.NewPairWithDelimiter(f[0], f[1], delimiterDash)
baseVolume := response.Data[i].BaseVolume24h
if response.Data[i].ContractVolume24h != 0 {
@@ -439,7 +280,7 @@ func (o *OKGroup) wsProcessTickers(respRaw []byte) error {
Bid: response.Data[i].BestBid,
Ask: response.Data[i].BestAsk,
Last: response.Data[i].Last,
- AssetType: o.GetAssetTypeFromTableName(response.Table),
+ AssetType: a,
Pair: c,
LastUpdated: response.Data[i].Timestamp,
}
@@ -448,7 +289,7 @@ func (o *OKGroup) wsProcessTickers(respRaw []byte) error {
}
// wsProcessTrades converts trade data and sends it to the datahandler
-func (o *OKGroup) wsProcessTrades(respRaw []byte) error {
+func (o *OKCoin) wsProcessTrades(respRaw []byte) error {
if !o.IsSaveTradeDataEnabled() {
return nil
}
@@ -462,16 +303,7 @@ func (o *OKGroup) wsProcessTrades(respRaw []byte) error {
trades := make([]trade.Data, len(response.Data))
for i := range response.Data {
f := strings.Split(response.Data[i].InstrumentID, delimiterDash)
-
- var c currency.Pair
- switch a {
- case asset.Futures, asset.PerpetualSwap:
- c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1],
- f[2],
- currency.UnderscoreDelimiter)
- default:
- c = currency.NewPairWithDelimiter(f[0], f[1], delimiterDash)
- }
+ c := currency.NewPairWithDelimiter(f[0], f[1], delimiterDash)
tSide, err := order.StringToOrderSide(response.Data[i].Side)
if err != nil {
@@ -487,7 +319,7 @@ func (o *OKGroup) wsProcessTrades(respRaw []byte) error {
}
trades[i] = trade.Data{
Amount: amount,
- AssetType: o.GetAssetTypeFromTableName(response.Table),
+ AssetType: a,
CurrencyPair: c,
Exchange: o.Name,
Price: response.Data[i].Price,
@@ -500,7 +332,7 @@ func (o *OKGroup) wsProcessTrades(respRaw []byte) error {
}
// wsProcessCandles converts candle data and sends it to the data handler
-func (o *OKGroup) wsProcessCandles(respRaw []byte) error {
+func (o *OKCoin) wsProcessCandles(respRaw []byte) error {
var response WebsocketCandleResponse
err := json.Unmarshal(respRaw, &response)
if err != nil {
@@ -510,16 +342,7 @@ func (o *OKGroup) wsProcessCandles(respRaw []byte) error {
a := o.GetAssetTypeFromTableName(response.Table)
for i := range response.Data {
f := strings.Split(response.Data[i].InstrumentID, delimiterDash)
-
- var c currency.Pair
- switch a {
- case asset.Futures, asset.PerpetualSwap:
- c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1],
- f[2],
- currency.UnderscoreDelimiter)
- default:
- c = currency.NewPairWithDelimiter(f[0], f[1], delimiterDash)
- }
+ c := currency.NewPairWithDelimiter(f[0], f[1], delimiterDash)
timeData, err := time.Parse(time.RFC3339Nano,
response.Data[i].Candle[0])
@@ -529,11 +352,11 @@ func (o *OKGroup) wsProcessCandles(respRaw []byte) error {
response.Data[i].Candle[0])
}
- candleIndex := strings.LastIndex(response.Table, okGroupWsCandle)
- candleInterval := response.Table[candleIndex+len(okGroupWsCandle):]
+ candleIndex := strings.LastIndex(response.Table, okcoinWsCandle)
+ candleInterval := response.Table[candleIndex+len(okcoinWsCandle):]
klineData := stream.KlineData{
- AssetType: o.GetAssetTypeFromTableName(response.Table),
+ AssetType: a,
Pair: c,
Exchange: o.Name,
Timestamp: timeData,
@@ -565,7 +388,7 @@ func (o *OKGroup) wsProcessCandles(respRaw []byte) error {
}
// WsProcessOrderBook Validates the checksum and updates internal orderbook values
-func (o *OKGroup) WsProcessOrderBook(respRaw []byte) error {
+func (o *OKCoin) WsProcessOrderBook(respRaw []byte) error {
var response WebsocketOrderBooksData
err := json.Unmarshal(respRaw, &response)
if err != nil {
@@ -576,18 +399,9 @@ func (o *OKGroup) WsProcessOrderBook(respRaw []byte) error {
a := o.GetAssetTypeFromTableName(response.Table)
for i := range response.Data {
f := strings.Split(response.Data[i].InstrumentID, delimiterDash)
+ c := currency.NewPairWithDelimiter(f[0], f[1], delimiterDash)
- var c currency.Pair
- switch a {
- case asset.Futures, asset.PerpetualSwap:
- c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1],
- f[2],
- currency.UnderscoreDelimiter)
- default:
- c = currency.NewPairWithDelimiter(f[0], f[1], delimiterDash)
- }
-
- if response.Action == okGroupWsOrderbookPartial {
+ if response.Action == okcoinWsOrderbookPartial {
err := o.WsProcessPartialOrderBook(&response.Data[i], c, a)
if err != nil {
err2 := o.wsResubscribeToOrderbook(&response)
@@ -596,7 +410,7 @@ func (o *OKGroup) WsProcessOrderBook(respRaw []byte) error {
}
return err
}
- } else if response.Action == okGroupWsOrderbookUpdate {
+ } else if response.Action == okcoinWsOrderbookUpdate {
if len(response.Data[i].Asks) == 0 && len(response.Data[i].Bids) == 0 {
return nil
}
@@ -613,18 +427,12 @@ func (o *OKGroup) WsProcessOrderBook(respRaw []byte) error {
return nil
}
-func (o *OKGroup) wsResubscribeToOrderbook(response *WebsocketOrderBooksData) error {
+func (o *OKCoin) wsResubscribeToOrderbook(response *WebsocketOrderBooksData) error {
a := o.GetAssetTypeFromTableName(response.Table)
for i := range response.Data {
f := strings.Split(response.Data[i].InstrumentID, delimiterDash)
- var c currency.Pair
- switch a {
- case asset.Futures, asset.PerpetualSwap:
- c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1], f[2], delimiterDash)
- default:
- c = currency.NewPairWithDelimiter(f[0], f[1], delimiterDash)
- }
+ c := currency.NewPairWithDelimiter(f[0], f[1], delimiterDash)
channelToResubscribe := &stream.ChannelSubscription{
Channel: response.Table,
@@ -641,7 +449,7 @@ func (o *OKGroup) wsResubscribeToOrderbook(response *WebsocketOrderBooksData) er
// AppendWsOrderbookItems adds websocket orderbook data bid/asks into an
// orderbook item array
-func (o *OKGroup) AppendWsOrderbookItems(entries [][]interface{}) ([]orderbook.Item, error) {
+func (o *OKCoin) AppendWsOrderbookItems(entries [][]interface{}) ([]orderbook.Item, error) {
items := make([]orderbook.Item, len(entries))
for j := range entries {
amount, err := strconv.ParseFloat(entries[j][1].(string), 64)
@@ -659,7 +467,7 @@ func (o *OKGroup) AppendWsOrderbookItems(entries [][]interface{}) ([]orderbook.I
// WsProcessPartialOrderBook takes websocket orderbook data and creates an
// orderbook Calculates checksum to ensure it is valid
-func (o *OKGroup) WsProcessPartialOrderBook(wsEventData *WebsocketOrderBook, instrument currency.Pair, a asset.Item) error {
+func (o *OKCoin) WsProcessPartialOrderBook(wsEventData *WebsocketOrderBook, instrument currency.Pair, a asset.Item) error {
signedChecksum, err := o.CalculatePartialOrderbookChecksum(wsEventData)
if err != nil {
return fmt.Errorf("%s channel: %s. Orderbook unable to calculate partial orderbook checksum: %s",
@@ -705,7 +513,7 @@ func (o *OKGroup) WsProcessPartialOrderBook(wsEventData *WebsocketOrderBook, ins
// WsProcessUpdateOrderbook updates an existing orderbook using websocket data
// After merging WS data, it will sort, validate and finally update the existing
// orderbook
-func (o *OKGroup) WsProcessUpdateOrderbook(wsEventData *WebsocketOrderBook, instrument currency.Pair, a asset.Item) error {
+func (o *OKCoin) WsProcessUpdateOrderbook(wsEventData *WebsocketOrderBook, instrument currency.Pair, a asset.Item) error {
update := orderbook.Update{
Asset: a,
Pair: instrument,
@@ -748,7 +556,7 @@ func (o *OKGroup) WsProcessUpdateOrderbook(wsEventData *WebsocketOrderBook, inst
// quantity with a semicolon (:) deliminating them. This will also work when
// there are less than 25 entries (for whatever reason)
// eg Bid:Ask:Bid:Ask:Ask:Ask
-func (o *OKGroup) CalculatePartialOrderbookChecksum(orderbookData *WebsocketOrderBook) (int32, error) {
+func (o *OKCoin) CalculatePartialOrderbookChecksum(orderbookData *WebsocketOrderBook) (int32, error) {
var checksum strings.Builder
for i := 0; i < allowableIterations; i++ {
if len(orderbookData.Bids)-1 >= i {
@@ -789,7 +597,7 @@ func (o *OKGroup) CalculatePartialOrderbookChecksum(orderbookData *WebsocketOrde
// quantity with a semicolon (:) deliminating them. This will also work when
// there are less than 25 entries (for whatever reason)
// eg Bid:Ask:Bid:Ask:Ask:Ask
-func (o *OKGroup) CalculateUpdateOrderbookChecksum(orderbookData *orderbook.Base) int32 {
+func (o *OKCoin) CalculateUpdateOrderbookChecksum(orderbookData *orderbook.Base) int32 {
var checksum strings.Builder
for i := 0; i < allowableIterations; i++ {
if len(orderbookData.Bids)-1 >= i {
@@ -809,7 +617,7 @@ func (o *OKGroup) CalculateUpdateOrderbookChecksum(orderbookData *orderbook.Base
// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be
// handled by ManageSubscriptions()
-func (o *OKGroup) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) {
+func (o *OKCoin) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) {
var subscriptions []stream.ChannelSubscription
assets := o.GetAssetTypes(true)
for x := range assets {
@@ -818,146 +626,46 @@ func (o *OKGroup) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription,
return nil, err
}
- switch assets[x] {
- case asset.Spot:
- channels := defaultSpotSubscribedChannels
- if o.IsWebsocketAuthenticationSupported() {
- channels = append(channels,
- okGroupWsSpotMarginAccount,
- okGroupWsSpotAccount,
- okGroupWsSpotOrder)
+ if assets[x] != asset.Spot {
+ o.Websocket.DataHandler <- fmt.Errorf("%w %v", asset.ErrNotSupported, assets[x])
+ return nil, fmt.Errorf("%w %v", asset.ErrNotSupported, assets[x])
+ }
+ channels := defaultSpotSubscribedChannels
+ if o.IsWebsocketAuthenticationSupported() {
+ channels = append(channels,
+ okcoinWsSpotMarginAccount,
+ okcoinWsSpotAccount,
+ okcoinWsSpotOrder)
+ }
+ for i := range pairs {
+ p, err := o.FormatExchangeCurrency(pairs[i], asset.Spot)
+ if err != nil {
+ return nil, err
}
-
- for i := range pairs {
- p, err := o.FormatExchangeCurrency(pairs[i], asset.Spot)
- if err != nil {
- return nil, err
- }
- for y := range channels {
- subscriptions = append(subscriptions,
- stream.ChannelSubscription{
- Channel: channels[y],
- Currency: p,
- Asset: asset.Spot,
- })
- }
+ for y := range channels {
+ subscriptions = append(subscriptions,
+ stream.ChannelSubscription{
+ Channel: channels[y],
+ Currency: p,
+ Asset: asset.Spot,
+ })
}
- case asset.Futures:
- channels := defaultFuturesSubscribedChannels
- if o.IsWebsocketAuthenticationSupported() {
- channels = append(channels,
- okGroupWsFuturesAccount,
- okGroupWsFuturesPosition,
- okGroupWsFuturesOrder)
- }
- var futuresAccountPairs currency.Pairs
- var futuresAccountCodes currency.Currencies
-
- for i := range pairs {
- p, err := o.FormatExchangeCurrency(pairs[i], asset.Futures)
- if err != nil {
- return nil, err
- }
- for y := range channels {
- if channels[y] == okGroupWsFuturesAccount {
- currencyString := strings.Split(pairs[i].String(),
- currency.UnderscoreDelimiter)[0]
- newP, err := currency.NewPairFromString(currencyString)
- if err != nil {
- return nil, err
- }
-
- if !futuresAccountCodes.Contains(newP.Base) {
- // subscribe to coin-margin futures trading mode
- subscriptions = append(subscriptions,
- stream.ChannelSubscription{
- Channel: channels[y],
- Currency: currency.NewPair(newP.Base, currency.EMPTYCODE),
- Asset: asset.Futures,
- })
- futuresAccountCodes = append(futuresAccountCodes, newP.Base)
- }
-
- if newP.Quote != currency.USDT {
- // Only allows subscription to USDT margined pair
- continue
- }
-
- if !futuresAccountPairs.Contains(newP, true) {
- subscriptions = append(subscriptions,
- stream.ChannelSubscription{
- Channel: channels[y],
- Currency: newP,
- Asset: asset.Futures,
- })
- futuresAccountPairs = futuresAccountPairs.Add(newP)
- }
-
- continue
- }
- subscriptions = append(subscriptions,
- stream.ChannelSubscription{
- Channel: channels[y],
- Currency: p,
- Asset: asset.Futures,
- })
- }
- }
- case asset.PerpetualSwap:
- channels := defaultSwapSubscribedChannels
- if o.IsWebsocketAuthenticationSupported() {
- channels = append(channels,
- okGroupWsSwapAccount,
- okGroupWsSwapPosition,
- okGroupWsSwapOrder)
- }
- for i := range pairs {
- p, err := o.FormatExchangeCurrency(pairs[i], asset.PerpetualSwap)
- if err != nil {
- return nil, err
- }
- for y := range channels {
- subscriptions = append(subscriptions,
- stream.ChannelSubscription{
- Channel: channels[y],
- Currency: p,
- Asset: asset.PerpetualSwap,
- })
- }
- }
- case asset.Index:
- for i := range pairs {
- p, err := o.FormatExchangeCurrency(pairs[i], asset.Index)
- if err != nil {
- return nil, err
- }
- for y := range defaultIndexSubscribedChannels {
- subscriptions = append(subscriptions,
- stream.ChannelSubscription{
- Channel: defaultIndexSubscribedChannels[y],
- Currency: p,
- Asset: asset.Index,
- })
- }
- }
- default:
- o.Websocket.DataHandler <- errors.New("unhandled asset type")
}
}
return subscriptions, nil
}
// Subscribe sends a websocket message to receive data from the channel
-func (o *OKGroup) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error {
+func (o *OKCoin) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error {
return o.handleSubscriptions("subscribe", channelsToSubscribe)
}
// Unsubscribe sends a websocket message to stop receiving data from the channel
-func (o *OKGroup) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error {
+func (o *OKCoin) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error {
return o.handleSubscriptions("unsubscribe", channelsToUnsubscribe)
}
-func (o *OKGroup) handleSubscriptions(operation string, subs []stream.ChannelSubscription) error {
+func (o *OKCoin) handleSubscriptions(operation string, subs []stream.ChannelSubscription) error {
request := WebsocketEventRequest{
Operation: operation,
}
@@ -972,7 +680,7 @@ func (o *OKGroup) handleSubscriptions(operation string, subs []stream.ChannelSub
copy(temp.Arguments, request.Arguments)
arg := subs[i].Channel + delimiterColon
- if strings.EqualFold(subs[i].Channel, okGroupWsSpotAccount) {
+ if strings.EqualFold(subs[i].Channel, okcoinWsSpotAccount) {
arg += subs[i].Currency.Base.String()
} else {
arg += subs[i].Currency.String()
@@ -1026,7 +734,7 @@ func (o *OKGroup) handleSubscriptions(operation string, subs []stream.ChannelSub
// GetWsChannelWithoutOrderType takes WebsocketDataResponse.Table and returns
// The base channel name eg receive "spot/depth5:BTC-USDT" return "depth5"
-func (o *OKGroup) GetWsChannelWithoutOrderType(table string) string {
+func (o *OKCoin) GetWsChannelWithoutOrderType(table string) string {
index := strings.Index(table, "/")
if index == -1 {
return table
@@ -1043,17 +751,11 @@ func (o *OKGroup) GetWsChannelWithoutOrderType(table string) string {
// GetAssetTypeFromTableName gets the asset type from the table name
// eg "spot/ticker:BTCUSD" results in "SPOT"
-func (o *OKGroup) GetAssetTypeFromTableName(table string) asset.Item {
+func (o *OKCoin) GetAssetTypeFromTableName(table string) asset.Item {
assetIndex := strings.Index(table, "/")
switch table[:assetIndex] {
- case asset.Futures.String():
- return asset.Futures
case asset.Spot.String():
return asset.Spot
- case "swap":
- return asset.PerpetualSwap
- case asset.Index.String():
- return asset.Index
default:
log.Warnf(log.ExchangeSys, "%s unhandled asset type %s",
o.Name,
diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go
index 4ee78457..553b3cf1 100644
--- a/exchanges/okcoin/okcoin_wrapper.go
+++ b/exchanges/okcoin/okcoin_wrapper.go
@@ -4,22 +4,28 @@ import (
"context"
"fmt"
"sort"
+ "strconv"
+ "strings"
"sync"
+ "time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/account"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
- "github.com/thrasher-corp/gocryptotrader/exchanges/okgroup"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/protocol"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
"github.com/thrasher-corp/gocryptotrader/log"
+ "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
)
// GetDefaultConfig returns a default exchange config
@@ -48,7 +54,6 @@ func (o *OKCoin) GetDefaultConfig() (*config.Exchange, error) {
// SetDefaults method assignes the default values for OKCoin
func (o *OKCoin) SetDefaults() {
o.SetErrorDefaults()
- o.SetCheckVarDefaults()
o.Name = okCoinExchangeName
o.Enabled = true
o.Verbose = true
@@ -149,14 +154,53 @@ func (o *OKCoin) SetDefaults() {
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
- o.APIVersion = okCoinAPIVersion
o.Websocket = stream.New()
o.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
o.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
o.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
}
-// Start starts the OKGroup go routine
+// Setup sets user exchange configuration settings
+func (o *OKCoin) Setup(exch *config.Exchange) error {
+ err := exch.Validate()
+ if err != nil {
+ return err
+ }
+ if !exch.Enabled {
+ o.SetEnabled(false)
+ return nil
+ }
+ err = o.SetupDefaults(exch)
+ if err != nil {
+ return err
+ }
+
+ wsEndpoint, err := o.API.Endpoints.GetURL(exchange.WebsocketSpot)
+ if err != nil {
+ return err
+ }
+ err = o.Websocket.Setup(&stream.WebsocketSetup{
+ ExchangeConfig: exch,
+ DefaultURL: wsEndpoint,
+ RunningURL: wsEndpoint,
+ Connector: o.WsConnect,
+ Subscriber: o.Subscribe,
+ Unsubscriber: o.Unsubscribe,
+ GenerateSubscriptions: o.GenerateDefaultSubscriptions,
+ Features: &o.Features.Supports.WebsocketCapabilities,
+ })
+ if err != nil {
+ return err
+ }
+
+ return o.Websocket.SetupNewConnection(stream.ConnectionSetup{
+ RateLimit: okcoinWsRateLimit,
+ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
+ ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
+ })
+}
+
+// Start starts the OKCoin go routine
func (o *OKCoin) Start(wg *sync.WaitGroup) error {
if wg == nil {
return fmt.Errorf("%T %w", wg, common.ErrNilPointer)
@@ -173,15 +217,17 @@ func (o *OKCoin) Start(wg *sync.WaitGroup) error {
func (o *OKCoin) Run() {
if o.Verbose {
log.Debugf(log.ExchangeSys,
- "%s Websocket: %s. (url: %s).\n",
+ "%s Websocket: %s.",
o.Name,
- common.IsEnabled(o.Websocket.IsEnabled()),
- o.WebsocketURL)
+ common.IsEnabled(o.Websocket.IsEnabled()))
+ o.PrintEnabledPairs()
}
forceUpdate := false
+ var err error
if !o.BypassConfigFormatUpgrades {
- format, err := o.GetPairFormat(asset.Spot, false)
+ var format currency.PairFormat
+ format, err = o.GetPairFormat(asset.Spot, false)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update currencies. Err: %s\n",
@@ -189,7 +235,8 @@ func (o *OKCoin) Run() {
err)
return
}
- enabled, err := o.CurrencyPairs.GetPairs(asset.Spot, true)
+ var enabled, avail currency.Pairs
+ enabled, err = o.CurrencyPairs.GetPairs(asset.Spot, true)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update currencies. Err: %s\n",
@@ -198,7 +245,7 @@ func (o *OKCoin) Run() {
return
}
- avail, err := o.CurrencyPairs.GetPairs(asset.Spot, false)
+ avail, err = o.CurrencyPairs.GetPairs(asset.Spot, false)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update currencies. Err: %s\n",
@@ -237,7 +284,7 @@ func (o *OKCoin) Run() {
return
}
- err := o.UpdateTradablePairs(context.TODO(), forceUpdate)
+ err = o.UpdateTradablePairs(context.TODO(), forceUpdate)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
@@ -247,7 +294,7 @@ func (o *OKCoin) Run() {
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
-func (o *OKCoin) FetchTradablePairs(ctx context.Context, a asset.Item) (currency.Pairs, error) {
+func (o *OKCoin) FetchTradablePairs(ctx context.Context, _ asset.Item) (currency.Pairs, error) {
prods, err := o.GetSpotTokenPairDetails(ctx)
if err != nil {
return nil, err
@@ -323,12 +370,12 @@ func (o *OKCoin) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item
}
// FetchTicker returns the ticker for a currency pair
-func (o *OKCoin) FetchTicker(ctx context.Context, p currency.Pair, assetType asset.Item) (tickerData *ticker.Price, err error) {
- tickerData, err = ticker.GetTicker(o.Name, p, assetType)
+func (o *OKCoin) FetchTicker(ctx context.Context, p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
+ tickerData, err := ticker.GetTicker(o.Name, p, assetType)
if err != nil {
return o.UpdateTicker(ctx, p, assetType)
}
- return
+ return tickerData, nil
}
// GetRecentTrades returns the most recent trades for a currency and asset
@@ -341,9 +388,9 @@ func (o *OKCoin) GetRecentTrades(ctx context.Context, p currency.Pair, assetType
var resp []trade.Data
switch assetType {
case asset.Spot:
- var tradeData []okgroup.GetSpotFilledOrdersInformationResponse
+ var tradeData []GetSpotFilledOrdersInformationResponse
tradeData, err = o.GetSpotFilledOrdersInformation(ctx,
- okgroup.GetSpotFilledOrdersInformationRequest{
+ &GetSpotFilledOrdersInformationRequest{
InstrumentID: p.String(),
})
if err != nil {
@@ -379,6 +426,680 @@ func (o *OKCoin) GetRecentTrades(ctx context.Context, p currency.Pair, assetType
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
-func (o *OKCoin) CancelBatchOrders(ctx context.Context, orders []order.Cancel) (order.CancelBatchResponse, error) {
+func (o *OKCoin) CancelBatchOrders(_ context.Context, _ []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
+
+// FetchOrderbook returns orderbook base on the currency pair
+func (o *OKCoin) FetchOrderbook(ctx context.Context, p currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
+ fPair, err := o.FormatExchangeCurrency(p, assetType)
+ if err != nil {
+ return nil, err
+ }
+ ob, err := orderbook.Get(o.Name, fPair, assetType)
+ if err != nil {
+ return o.UpdateOrderbook(ctx, fPair, assetType)
+ }
+ return ob, nil
+}
+
+// UpdateOrderbook updates and returns the orderbook for a currency pair
+func (o *OKCoin) UpdateOrderbook(ctx context.Context, p currency.Pair, a asset.Item) (*orderbook.Base, error) {
+ book := &orderbook.Base{
+ Exchange: o.Name,
+ Pair: p,
+ Asset: a,
+ VerifyOrderbook: o.CanVerifyOrderbook,
+ }
+
+ fPair, err := o.FormatExchangeCurrency(p, a)
+ if err != nil {
+ return book, err
+ }
+
+ orderbookNew, err := o.GetOrderBook(ctx,
+ &GetOrderBookRequest{
+ InstrumentID: fPair.String(),
+ Size: 200,
+ }, a)
+ if err != nil {
+ return book, err
+ }
+
+ book.Bids = make(orderbook.Items, len(orderbookNew.Bids))
+ for x := range orderbookNew.Bids {
+ amount, convErr := strconv.ParseFloat(orderbookNew.Bids[x][1], 64)
+ if convErr != nil {
+ return book, err
+ }
+ price, convErr := strconv.ParseFloat(orderbookNew.Bids[x][0], 64)
+ if convErr != nil {
+ return book, err
+ }
+
+ var liquidationOrders, orderCount int64
+ // Contract specific variables
+ if len(orderbookNew.Bids[x]) == 4 {
+ liquidationOrders, convErr = strconv.ParseInt(orderbookNew.Bids[x][2], 10, 64)
+ if convErr != nil {
+ return book, err
+ }
+
+ orderCount, convErr = strconv.ParseInt(orderbookNew.Bids[x][3], 10, 64)
+ if convErr != nil {
+ return book, err
+ }
+ }
+
+ book.Bids[x] = orderbook.Item{
+ Amount: amount,
+ Price: price,
+ LiquidationOrders: liquidationOrders,
+ OrderCount: orderCount,
+ }
+ }
+
+ book.Asks = make(orderbook.Items, len(orderbookNew.Asks))
+ for x := range orderbookNew.Asks {
+ amount, convErr := strconv.ParseFloat(orderbookNew.Asks[x][1], 64)
+ if convErr != nil {
+ return book, err
+ }
+ price, convErr := strconv.ParseFloat(orderbookNew.Asks[x][0], 64)
+ if convErr != nil {
+ return book, err
+ }
+
+ var liquidationOrders, orderCount int64
+ // Contract specific variables
+ if len(orderbookNew.Asks[x]) == 4 {
+ liquidationOrders, convErr = strconv.ParseInt(orderbookNew.Asks[x][2], 10, 64)
+ if convErr != nil {
+ return book, err
+ }
+
+ orderCount, convErr = strconv.ParseInt(orderbookNew.Asks[x][3], 10, 64)
+ if convErr != nil {
+ return book, err
+ }
+ }
+
+ book.Asks[x] = orderbook.Item{
+ Amount: amount,
+ Price: price,
+ LiquidationOrders: liquidationOrders,
+ OrderCount: orderCount,
+ }
+ }
+
+ err = book.Process()
+ if err != nil {
+ return book, err
+ }
+
+ return orderbook.Get(o.Name, fPair, a)
+}
+
+// UpdateAccountInfo retrieves balances for all enabled currencies
+func (o *OKCoin) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) {
+ currencies, err := o.GetSpotTradingAccounts(ctx)
+ if err != nil {
+ return account.Holdings{}, err
+ }
+
+ var resp account.Holdings
+ resp.Exchange = o.Name
+ currencyAccount := account.SubAccount{AssetType: assetType}
+
+ for i := range currencies {
+ hold, parseErr := strconv.ParseFloat(currencies[i].Hold, 64)
+ if parseErr != nil {
+ return resp, parseErr
+ }
+ totalValue, parseErr := strconv.ParseFloat(currencies[i].Balance, 64)
+ if parseErr != nil {
+ return resp, parseErr
+ }
+ currencyAccount.Currencies = append(currencyAccount.Currencies,
+ account.Balance{
+ CurrencyName: currency.NewCode(currencies[i].Currency),
+ Total: totalValue,
+ Hold: hold,
+ Free: totalValue - hold,
+ })
+ }
+
+ resp.Accounts = append(resp.Accounts, currencyAccount)
+
+ creds, err := o.GetCredentials(ctx)
+ if err != nil {
+ return account.Holdings{}, err
+ }
+ err = account.Process(&resp, creds)
+ if err != nil {
+ return resp, err
+ }
+
+ return resp, nil
+}
+
+// FetchAccountInfo retrieves balances for all enabled currencies
+func (o *OKCoin) FetchAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) {
+ creds, err := o.GetCredentials(ctx)
+ if err != nil {
+ return account.Holdings{}, err
+ }
+ acc, err := account.GetHoldings(o.Name, creds, assetType)
+ if err != nil {
+ return o.UpdateAccountInfo(ctx, assetType)
+ }
+ return acc, nil
+}
+
+// GetFundingHistory returns funding history, deposits and
+// withdrawals
+func (o *OKCoin) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, error) {
+ accountDepositHistory, err := o.GetAccountDepositHistory(ctx, "")
+ if err != nil {
+ return nil, err
+ }
+ accountWithdrawlHistory, err := o.GetAccountWithdrawalHistory(ctx, "")
+ if err != nil {
+ return nil, err
+ }
+ resp := make([]exchange.FundHistory, len(accountDepositHistory)+len(accountWithdrawlHistory))
+ for x := range accountDepositHistory {
+ orderStatus := ""
+ switch accountDepositHistory[x].Status {
+ case 0:
+ orderStatus = "waiting"
+ case 1:
+ orderStatus = "confirmation account"
+ case 2:
+ orderStatus = "recharge success"
+ }
+
+ resp[x] = exchange.FundHistory{
+ Amount: accountDepositHistory[x].Amount,
+ Currency: accountDepositHistory[x].Currency,
+ ExchangeName: o.Name,
+ Status: orderStatus,
+ Timestamp: accountDepositHistory[x].Timestamp,
+ TransferID: accountDepositHistory[x].TransactionID,
+ TransferType: "deposit",
+ }
+ }
+
+ for i := range accountWithdrawlHistory {
+ resp[len(accountDepositHistory)+i] = exchange.FundHistory{
+ Amount: accountWithdrawlHistory[i].Amount,
+ Currency: accountWithdrawlHistory[i].Currency,
+ ExchangeName: o.Name,
+ Status: OrderStatus[accountWithdrawlHistory[i].Status],
+ Timestamp: accountWithdrawlHistory[i].Timestamp,
+ TransferID: accountWithdrawlHistory[i].TransactionID,
+ TransferType: "withdrawal",
+ }
+ }
+ return resp, nil
+}
+
+// SubmitOrder submits a new order
+func (o *OKCoin) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) {
+ err := s.Validate()
+ if err != nil {
+ return nil, err
+ }
+
+ fPair, err := o.FormatExchangeCurrency(s.Pair, s.AssetType)
+ if err != nil {
+ return nil, err
+ }
+
+ req := PlaceOrderRequest{
+ ClientOID: s.ClientID,
+ InstrumentID: fPair.String(),
+ Side: s.Side.Lower(),
+ Type: s.Type.Lower(),
+ Size: strconv.FormatFloat(s.Amount, 'f', -1, 64),
+ }
+ if s.Type == order.Limit {
+ req.Price = strconv.FormatFloat(s.Price, 'f', -1, 64)
+ }
+
+ orderResponse, err := o.PlaceSpotOrder(ctx, &req)
+ if err != nil {
+ return nil, err
+ }
+
+ if !orderResponse.Result {
+ return nil, order.ErrUnableToPlaceOrder
+ }
+ return s.DeriveSubmitResponse(orderResponse.OrderID)
+}
+
+// ModifyOrder will allow of changing orderbook placement and limit to
+// market conversion
+func (o *OKCoin) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
+ return nil, common.ErrFunctionNotSupported
+}
+
+// CancelOrder cancels an order by its corresponding ID number
+func (o *OKCoin) CancelOrder(ctx context.Context, cancel *order.Cancel) error {
+ err := cancel.Validate(cancel.StandardCancel())
+ if err != nil {
+ return err
+ }
+
+ orderID, err := strconv.ParseInt(cancel.OrderID, 10, 64)
+ if err != nil {
+ return err
+ }
+
+ fpair, err := o.FormatExchangeCurrency(cancel.Pair,
+ cancel.AssetType)
+ if err != nil {
+ return err
+ }
+
+ orderCancellationResponse, err := o.CancelSpotOrder(ctx,
+ &CancelSpotOrderRequest{
+ InstrumentID: fpair.String(),
+ OrderID: orderID,
+ })
+ if err != nil {
+ return err
+ }
+ if !orderCancellationResponse.Result {
+ return fmt.Errorf("order %d failed to be cancelled",
+ orderCancellationResponse.OrderID)
+ }
+
+ return nil
+}
+
+// CancelAllOrders cancels all orders associated with a currency pair
+func (o *OKCoin) CancelAllOrders(ctx context.Context, orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
+ if err := orderCancellation.Validate(); err != nil {
+ return order.CancelAllResponse{}, err
+ }
+
+ orderIDs := strings.Split(orderCancellation.OrderID, ",")
+ resp := order.CancelAllResponse{}
+ resp.Status = make(map[string]string)
+ orderIDNumbers := make([]int64, 0, len(orderIDs))
+ for i := range orderIDs {
+ orderIDNumber, err := strconv.ParseInt(orderIDs[i], 10, 64)
+ if err != nil {
+ resp.Status[orderIDs[i]] = err.Error()
+ continue
+ }
+ orderIDNumbers = append(orderIDNumbers, orderIDNumber)
+ }
+
+ fpair, err := o.FormatExchangeCurrency(orderCancellation.Pair,
+ orderCancellation.AssetType)
+ if err != nil {
+ return resp, err
+ }
+
+ cancelOrdersResponse, err := o.CancelMultipleSpotOrders(ctx,
+ &CancelMultipleSpotOrdersRequest{
+ InstrumentID: fpair.String(),
+ OrderIDs: orderIDNumbers,
+ })
+ if err != nil {
+ return resp, err
+ }
+
+ for x := range cancelOrdersResponse {
+ for y := range cancelOrdersResponse[x] {
+ resp.Status[strconv.FormatInt(cancelOrdersResponse[x][y].OrderID, 10)] = strconv.FormatBool(cancelOrdersResponse[x][y].Result)
+ }
+ }
+
+ return resp, err
+}
+
+// GetOrderInfo returns order information based on order ID
+func (o *OKCoin) GetOrderInfo(ctx context.Context, orderID string, pair currency.Pair, assetType asset.Item) (order.Detail, error) {
+ var resp order.Detail
+ if assetType != asset.Spot {
+ return resp, fmt.Errorf("%s %w", assetType, asset.ErrNotSupported)
+ }
+
+ mOrder, err := o.GetSpotOrder(ctx, &GetSpotOrderRequest{OrderID: orderID})
+ if err != nil {
+ return resp, err
+ }
+
+ format, err := o.GetPairFormat(assetType, false)
+ if err != nil {
+ return resp, err
+ }
+
+ p, err := currency.NewPairDelimiter(mOrder.InstrumentID, format.Delimiter)
+ if err != nil {
+ return resp, err
+ }
+
+ status, err := order.StringToOrderStatus(mOrder.Status)
+ if err != nil {
+ log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
+ }
+
+ side, err := order.StringToOrderSide(mOrder.Side)
+ if err != nil {
+ log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
+ }
+ resp = order.Detail{
+ Amount: mOrder.Size,
+ Pair: p,
+ Exchange: o.Name,
+ Date: mOrder.Timestamp,
+ ExecutedAmount: mOrder.FilledSize,
+ Status: status,
+ Side: side,
+ }
+ return resp, nil
+}
+
+// GetDepositAddress returns a deposit address for a specified currency
+func (o *OKCoin) GetDepositAddress(ctx context.Context, c currency.Code, _, _ string) (*deposit.Address, error) {
+ wallet, err := o.GetAccountDepositAddressForCurrency(ctx, c.Lower().String())
+ if err != nil {
+ return nil, err
+ }
+ if len(wallet) == 0 {
+ return nil, fmt.Errorf("%w for currency %s",
+ errNoAccountDepositAddress,
+ c)
+ }
+ return &deposit.Address{
+ Address: wallet[0].Address,
+ Tag: wallet[0].Tag,
+ }, nil
+}
+
+// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
+// submitted
+func (o *OKCoin) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
+ if err := withdrawRequest.Validate(); err != nil {
+ return nil, err
+ }
+ withdrawal, err := o.AccountWithdraw(ctx,
+ &AccountWithdrawRequest{
+ Amount: withdrawRequest.Amount,
+ Currency: withdrawRequest.Currency.Lower().String(),
+ Destination: 4, // 1, 2, 3 are all internal
+ Fee: withdrawRequest.Crypto.FeeAmount,
+ ToAddress: withdrawRequest.Crypto.Address,
+ TradePwd: withdrawRequest.TradePassword,
+ })
+ if err != nil {
+ return nil, err
+ }
+ if !withdrawal.Result {
+ return nil,
+ fmt.Errorf("could not withdraw currency %s to %s, no error specified",
+ withdrawRequest.Currency,
+ withdrawRequest.Crypto.Address)
+ }
+
+ return &withdraw.ExchangeResponse{
+ ID: strconv.FormatInt(withdrawal.WithdrawalID, 10),
+ }, nil
+}
+
+// WithdrawFiatFunds returns a withdrawal ID when a
+// withdrawal is submitted
+func (o *OKCoin) WithdrawFiatFunds(_ context.Context, _ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
+ return nil, common.ErrFunctionNotSupported
+}
+
+// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
+// withdrawal is submitted
+func (o *OKCoin) WithdrawFiatFundsToInternationalBank(_ context.Context, _ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
+ return nil, common.ErrFunctionNotSupported
+}
+
+// GetWithdrawalsHistory returns previous withdrawals data
+func (o *OKCoin) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) ([]exchange.WithdrawalHistory, error) {
+ return nil, common.ErrNotYetImplemented
+}
+
+// GetActiveOrders retrieves any orders that are active/open
+func (o *OKCoin) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest) (order.FilteredOrders, error) {
+ err := req.Validate()
+ if err != nil {
+ return nil, err
+ }
+
+ var resp []order.Detail
+ for x := range req.Pairs {
+ var fPair currency.Pair
+ fPair, err = o.FormatExchangeCurrency(req.Pairs[x], asset.Spot)
+ if err != nil {
+ return nil, err
+ }
+ var spotOpenOrders []GetSpotOrderResponse
+ spotOpenOrders, err = o.GetSpotOpenOrders(ctx,
+ &GetSpotOpenOrdersRequest{
+ InstrumentID: fPair.String(),
+ })
+ if err != nil {
+ return nil, err
+ }
+ for i := range spotOpenOrders {
+ var status order.Status
+ status, err = order.StringToOrderStatus(spotOpenOrders[i].Status)
+ if err != nil {
+ log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
+ }
+ var side order.Side
+ side, err = order.StringToOrderSide(spotOpenOrders[i].Side)
+ if err != nil {
+ log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
+ }
+ var orderType order.Type
+ orderType, err = order.StringToOrderType(spotOpenOrders[i].Type)
+ if err != nil {
+ log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
+ }
+ resp = append(resp, order.Detail{
+ OrderID: spotOpenOrders[i].OrderID,
+ Price: spotOpenOrders[i].Price,
+ Amount: spotOpenOrders[i].Size,
+ Pair: req.Pairs[x],
+ Exchange: o.Name,
+ Side: side,
+ Type: orderType,
+ ExecutedAmount: spotOpenOrders[i].FilledSize,
+ Date: spotOpenOrders[i].Timestamp,
+ Status: status,
+ })
+ }
+ }
+ return req.Filter(o.Name, resp), nil
+}
+
+// GetOrderHistory retrieves account order information
+// Can Limit response to specific order status
+func (o *OKCoin) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest) (order.FilteredOrders, error) {
+ err := req.Validate()
+ if err != nil {
+ return nil, err
+ }
+
+ var resp []order.Detail
+ for x := range req.Pairs {
+ var fPair currency.Pair
+ fPair, err = o.FormatExchangeCurrency(req.Pairs[x], asset.Spot)
+ if err != nil {
+ return nil, err
+ }
+ var spotOrders []GetSpotOrderResponse
+ spotOrders, err = o.GetSpotOrders(ctx,
+ &GetSpotOrdersRequest{
+ Status: strings.Join([]string{"filled", "cancelled", "failure"}, "|"),
+ InstrumentID: fPair.String(),
+ })
+ if err != nil {
+ return nil, err
+ }
+ for i := range spotOrders {
+ var status order.Status
+ status, err = order.StringToOrderStatus(spotOrders[i].Status)
+ if err != nil {
+ log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
+ }
+ var side order.Side
+ side, err = order.StringToOrderSide(spotOrders[i].Side)
+ if err != nil {
+ log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
+ }
+ var orderType order.Type
+ orderType, err = order.StringToOrderType(spotOrders[i].Type)
+ if err != nil {
+ log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
+ }
+ detail := order.Detail{
+ OrderID: spotOrders[i].OrderID,
+ Price: spotOrders[i].Price,
+ AverageExecutedPrice: spotOrders[i].PriceAvg,
+ Amount: spotOrders[i].Size,
+ ExecutedAmount: spotOrders[i].FilledSize,
+ RemainingAmount: spotOrders[i].Size - spotOrders[i].FilledSize,
+ Pair: req.Pairs[x],
+ Exchange: o.Name,
+ Side: side,
+ Type: orderType,
+ Date: spotOrders[i].Timestamp,
+ Status: status,
+ }
+ detail.InferCostsAndTimes()
+ resp = append(resp, detail)
+ }
+ }
+ return req.Filter(o.Name, resp), nil
+}
+
+// GetFeeByType returns an estimate of fee based on type of transaction
+func (o *OKCoin) GetFeeByType(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) {
+ if feeBuilder == nil {
+ return 0, fmt.Errorf("%T %w", feeBuilder, common.ErrNilPointer)
+ }
+ if !o.AreCredentialsValid(ctx) && // Todo check connection status
+ feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
+ feeBuilder.FeeType = exchange.OfflineTradeFee
+ }
+ return o.GetFee(ctx, feeBuilder)
+}
+
+// GetWithdrawCapabilities returns the types of withdrawal methods permitted by the exchange
+func (o *OKCoin) GetWithdrawCapabilities() uint32 {
+ return o.GetWithdrawPermissions()
+}
+
+// AuthenticateWebsocket sends an authentication message to the websocket
+func (o *OKCoin) AuthenticateWebsocket(ctx context.Context) error {
+ return o.WsLogin(ctx)
+}
+
+// ValidateCredentials validates current credentials used for wrapper
+// functionality
+func (o *OKCoin) ValidateCredentials(ctx context.Context, assetType asset.Item) error {
+ _, err := o.UpdateAccountInfo(ctx, assetType)
+ return o.CheckTransientError(err)
+}
+
+// GetHistoricTrades returns historic trade data within the timeframe provided
+func (o *OKCoin) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.Item, _, _ time.Time) ([]trade.Data, error) {
+ return nil, common.ErrFunctionNotSupported
+}
+
+// GetHistoricCandles returns candles between a time period for a set time interval
+func (o *OKCoin) GetHistoricCandles(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
+ if err := o.ValidateKline(pair, a, interval); err != nil {
+ return kline.Item{}, err
+ }
+
+ formattedPair, err := o.FormatExchangeCurrency(pair, a)
+ if err != nil {
+ return kline.Item{}, err
+ }
+
+ req := &GetMarketDataRequest{
+ Asset: a,
+ Start: start.UTC().Format(time.RFC3339),
+ End: end.UTC().Format(time.RFC3339),
+ Granularity: o.FormatExchangeKlineInterval(interval),
+ InstrumentID: formattedPair.String(),
+ }
+
+ ret := kline.Item{
+ Exchange: o.Name,
+ Pair: pair,
+ Asset: a,
+ Interval: interval,
+ }
+ ret.Candles, err = o.GetMarketData(ctx, req)
+ if err != nil {
+ return kline.Item{}, err
+ }
+
+ ret.RemoveDuplicates()
+ ret.RemoveOutsideRange(start, end)
+ ret.SortCandlesByTimestamp(false)
+ return ret, nil
+}
+
+// GetHistoricCandlesExtended returns candles between a time period for a set time interval
+func (o *OKCoin) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
+ if err := o.ValidateKline(pair, a, interval); err != nil {
+ return kline.Item{}, err
+ }
+
+ ret := kline.Item{
+ Exchange: o.Name,
+ Pair: pair,
+ Asset: a,
+ Interval: interval,
+ }
+
+ dates, err := kline.CalculateCandleDateRanges(start, end, interval, o.Features.Enabled.Kline.ResultLimit)
+ if err != nil {
+ return kline.Item{}, err
+ }
+ formattedPair, err := o.FormatExchangeCurrency(pair, a)
+ if err != nil {
+ return kline.Item{}, err
+ }
+
+ for x := range dates.Ranges {
+ req := &GetMarketDataRequest{
+ Asset: a,
+ Start: dates.Ranges[x].Start.Time.UTC().Format(time.RFC3339),
+ End: dates.Ranges[x].End.Time.UTC().Format(time.RFC3339),
+ Granularity: o.FormatExchangeKlineInterval(interval),
+ InstrumentID: formattedPair.String(),
+ }
+
+ var candles []kline.Candle
+ candles, err = o.GetMarketData(ctx, req)
+ if err != nil {
+ return kline.Item{}, err
+ }
+ ret.Candles = append(ret.Candles, candles...)
+ }
+
+ dates.SetHasDataFromCandles(ret.Candles)
+ summary := dates.DataSummary(false)
+ if len(summary) > 0 {
+ log.Warnf(log.ExchangeSys, "%v - %v", o.Base.Name, summary)
+ }
+ ret.RemoveDuplicates()
+ ret.RemoveOutsideRange(start, end)
+ ret.SortCandlesByTimestamp(false)
+ return ret, nil
+}
diff --git a/exchanges/okgroup/README.md b/exchanges/okgroup/README.md
deleted file mode 100644
index 44e5e797..00000000
--- a/exchanges/okgroup/README.md
+++ /dev/null
@@ -1,133 +0,0 @@
-# GoCryptoTrader package Okgroup
-
-
-
-
-[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
-[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
-[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/okgroup)
-[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
-[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
-
-
-This OKCoin package is part of the GoCryptoTrader codebase.
-
-## This is still in active development
-
-You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
-
-Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://gocryptotrader.herokuapp.com/)
-
-## OKCoin Exchange
-
-### Current Features
-
-+ REST Support
-
-### How to enable
-
-+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
-
-+ Individual package example below:
-
-```go
- // Exchanges will be abstracted out in further updates and examples will be
- // supplied then
-```
-
-### How to do REST public/private calls
-
-+ If enabled via "configuration".json file the exchange will be added to the
-IBotExchange array in the ```go var bot Bot``` and you will only be able to use
-the wrapper interface functions for accessing exchange data. View routines.go
-for an example of integration usage with GoCryptoTrader. Rudimentary example
-below:
-
-main.go
-```go
-var o exchange.IBotExchange
-
-for i := range Bot.Exchanges {
- if Bot.Exchanges[i].GetName() == "OKCoin" {
- o = Bot.Exchanges[i]
- }
-}
-
-// Public calls - wrapper functions
-
-// Fetches current ticker information
-tick, err := o.FetchTicker()
-if err != nil {
- // Handle error
-}
-
-// Fetches current orderbook information
-ob, err := o.FetchOrderbook()
-if err != nil {
- // Handle error
-}
-
-// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
-// set and AuthenticatedAPISupport is set to true
-
-// Fetches current account information
-accountInfo, err := o.GetAccountInfo()
-if err != nil {
- // Handle error
-}
-```
-
-+ If enabled via individually importing package, rudimentary example below:
-
-```go
-// Public calls
-
-// Fetches current ticker information
-ticker, err := o.GetSpotTicker()
-if err != nil {
- // Handle error
-}
-
-// Fetches current orderbook information
-ob, err := o.GetSpotMarketDepth()
-if err != nil {
- // Handle error
-}
-
-// Private calls - make sure your APIKEY and APISECRET are set and
-// AuthenticatedAPISupport is set to true
-
-// GetContractPosition returns contract positioning
-accountInfo, err := o.GetContractPosition(...)
-if err != nil {
- // Handle error
-}
-
-// Submits an order and the exchange and returns its tradeID
-tradeID, err := o.PlaceContractOrders(...)
-if err != nil {
- // Handle error
-}
-```
-
-### Please click GoDocs chevron above to view current GoDoc information for this package
-
-## Contribution
-
-Please feel free to submit any pull requests or suggest any desired features to be added.
-
-When submitting a PR, please abide by our coding guidelines:
-
-+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
-+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
-+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
-+ Pull requests need to be based on and opened against the `master` branch.
-
-## Donations
-
-
-
-If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
-
-***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc***
-
diff --git a/exchanges/okgroup/okgroup.go b/exchanges/okgroup/okgroup.go
deleted file mode 100644
index 817b5f42..00000000
--- a/exchanges/okgroup/okgroup.go
+++ /dev/null
@@ -1,842 +0,0 @@
-package okgroup
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "net/http"
- "net/url"
- "reflect"
- "strconv"
- "strings"
- "time"
-
- "github.com/google/go-querystring/query"
- "github.com/thrasher-corp/gocryptotrader/common/crypto"
- exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
- "github.com/thrasher-corp/gocryptotrader/exchanges/account"
- "github.com/thrasher-corp/gocryptotrader/exchanges/asset"
- "github.com/thrasher-corp/gocryptotrader/exchanges/request"
- "github.com/thrasher-corp/gocryptotrader/log"
-)
-
-const (
- // OKGroupAPIPath const to help with api url formatting
- OKGroupAPIPath = "api/"
- // API subsections
- okGroupAccountSubsection = "account"
- okGroupTokenSubsection = "spot"
- okGroupMarginTradingSubsection = "margin"
- okGroupFuturesTradingSubSection = "futures"
- oKGroupSwapTradingSubSection = "swap"
- // OKGroupAccounts common api endpoint
- OKGroupAccounts = "accounts"
- // OKGroupLedger common api endpoint
- OKGroupLedger = "ledger"
- // OKGroupOrders common api endpoint
- OKGroupOrders = "orders"
- // OKGroupBatchOrders common api endpoint
- OKGroupBatchOrders = "batch_orders"
- // OKGroupCancelOrders common api endpoint
- OKGroupCancelOrders = "cancel_orders"
- // OKGroupCancelOrder common api endpoint
- OKGroupCancelOrder = "cancel_order"
- // OKGroupCancelBatchOrders common api endpoint
- OKGroupCancelBatchOrders = "cancel_batch_orders"
- // OKGroupPendingOrders common api endpoint
- OKGroupPendingOrders = "orders_pending"
- // OKGroupTrades common api endpoint
- OKGroupTrades = "trades"
- // OKGroupTicker common api endpoint
- OKGroupTicker = "ticker"
- // OKGroupInstruments common api endpoint
- OKGroupInstruments = "instruments"
- // OKGroupLiquidation common api endpoint
- OKGroupLiquidation = "liquidation"
- // OKGroupMarkPrice common api endpoint
- OKGroupMarkPrice = "mark_price"
- // OKGroupGetAccountDepositHistory common api endpoint
- OKGroupGetAccountDepositHistory = "deposit/history"
- // OKGroupGetSpotTransactionDetails common api endpoint
- OKGroupGetSpotTransactionDetails = "fills"
- // OKGroupGetSpotOrderBook common api endpoint
- OKGroupGetSpotOrderBook = "book"
- // OKGroupGetSpotMarketData common api endpoint
- OKGroupGetSpotMarketData = "candles"
- // OKGroupPriceLimit common api endpoint
- OKGroupPriceLimit = "price_limit"
- // Account based endpoints
- okGroupGetAccountCurrencies = "currencies"
- okGroupGetAccountWalletInformation = "wallet"
- okGroupFundsTransfer = "transfer"
- okGroupWithdraw = "withdrawal"
- okGroupGetWithdrawalFees = "withdrawal/fee"
- okGroupGetWithdrawalHistory = "withdrawal/history"
- okGroupGetDepositAddress = "deposit/address"
- // Margin based endpoints
- okGroupGetMarketAvailability = "availability"
- okGroupGetLoanHistory = "borrowed"
- okGroupGetLoan = "borrow"
- okGroupGetRepayment = "repayment"
-)
-
-// OKGroup is the overaching type across the all of OKCoin's exchange methods
-type OKGroup struct {
- exchange.Base
- ExchangeName string
- // Spot and contract market error codes
- ErrorCodes map[string]error
- // Stores for corresponding variable checks
- ContractTypes []string
- CurrencyPairsDefaults []string
- ContractPosition []string
- Types []string
- // URLs to be overridden by implementations of OKGroup
- APIURL string
- APIVersion string
- WebsocketURL string
-}
-
-// GetAccountCurrencies returns a list of tradable spot instruments and their properties
-func (o *OKGroup) GetAccountCurrencies(ctx context.Context) (resp []GetAccountCurrenciesResponse, _ error) {
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupAccountSubsection, okGroupGetAccountCurrencies, nil, &resp, true)
-}
-
-// GetAccountWalletInformation returns a list of wallets and their properties
-func (o *OKGroup) GetAccountWalletInformation(ctx context.Context, currency string) (resp []WalletInformationResponse, _ error) {
- var requestURL string
- if currency != "" {
- requestURL = fmt.Sprintf("%v/%v", okGroupGetAccountWalletInformation, currency)
- } else {
- requestURL = okGroupGetAccountWalletInformation
- }
-
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
-}
-
-// TransferAccountFunds the transfer of funds between wallet, trading accounts, main account and sub accounts.
-func (o *OKGroup) TransferAccountFunds(ctx context.Context, request TransferAccountFundsRequest) (resp TransferAccountFundsResponse, _ error) {
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupAccountSubsection, okGroupFundsTransfer, request, &resp, true)
-}
-
-// AccountWithdraw withdrawal of tokens to OKCoin International or other addresses.
-func (o *OKGroup) AccountWithdraw(ctx context.Context, request AccountWithdrawRequest) (resp AccountWithdrawResponse, _ error) {
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupAccountSubsection, okGroupWithdraw, request, &resp, true)
-}
-
-// GetAccountWithdrawalFee retrieves the information about the recommended network transaction fee for withdrawals to digital asset addresses. The higher the fees are, the sooner the confirmations you will get.
-func (o *OKGroup) GetAccountWithdrawalFee(ctx context.Context, currency string) (resp []GetAccountWithdrawalFeeResponse, _ error) {
- var requestURL string
- if currency != "" {
- requestURL = fmt.Sprintf("%v?currency=%v", okGroupGetWithdrawalFees, currency)
- } else {
- requestURL = okGroupGetAccountWalletInformation
- }
-
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
-}
-
-// GetAccountWithdrawalHistory retrieves all recent withdrawal records.
-func (o *OKGroup) GetAccountWithdrawalHistory(ctx context.Context, currency string) (resp []WithdrawalHistoryResponse, _ error) {
- var requestURL string
- if currency != "" {
- requestURL = fmt.Sprintf("%v/%v", okGroupGetWithdrawalHistory, currency)
- } else {
- requestURL = okGroupGetWithdrawalHistory
- }
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
-}
-
-// GetAccountBillDetails retrieves the bill details of the wallet. All the information will be paged and sorted in reverse chronological order,
-// which means the latest will be at the top. Please refer to the pagination section for additional records after the first page.
-// 3 months recent records will be returned at maximum
-func (o *OKGroup) GetAccountBillDetails(ctx context.Context, request GetAccountBillDetailsRequest) (resp []GetAccountBillDetailsResponse, _ error) {
- requestURL := fmt.Sprintf("%v%v", OKGroupLedger, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
-}
-
-// GetAccountDepositAddressForCurrency retrieves the deposit addresses of different tokens, including previously used addresses.
-func (o *OKGroup) GetAccountDepositAddressForCurrency(ctx context.Context, currency string) (resp []GetDepositAddressResponse, _ error) {
- urlValues := url.Values{}
- urlValues.Set("currency", currency)
- requestURL := fmt.Sprintf("%v?%v", okGroupGetDepositAddress, urlValues.Encode())
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
-}
-
-// GetAccountDepositHistory retrieves the deposit history of all tokens.100 recent records will be returned at maximum
-func (o *OKGroup) GetAccountDepositHistory(ctx context.Context, currency string) (resp []GetAccountDepositHistoryResponse, _ error) {
- var requestURL string
- if currency != "" {
- requestURL = fmt.Sprintf("%v/%v", OKGroupGetAccountDepositHistory, currency)
- } else {
- requestURL = OKGroupGetAccountDepositHistory
- }
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
-}
-
-// GetSpotTradingAccounts retrieves the list of assets(only show pairs with balance larger than 0), the balances, amount available/on hold in spot accounts.
-func (o *OKGroup) GetSpotTradingAccounts(ctx context.Context) (resp []GetSpotTradingAccountResponse, _ error) {
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, OKGroupAccounts, nil, &resp, true)
-}
-
-// GetSpotTradingAccountForCurrency This endpoint supports getting the balance, amount available/on hold of a token in spot account.
-func (o *OKGroup) GetSpotTradingAccountForCurrency(ctx context.Context, currency string) (resp GetSpotTradingAccountResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v", OKGroupAccounts, currency)
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
-}
-
-// GetSpotBillDetailsForCurrency This endpoint supports getting the balance, amount available/on hold of a token in spot account.
-func (o *OKGroup) GetSpotBillDetailsForCurrency(ctx context.Context, request GetSpotBillDetailsForCurrencyRequest) (resp []GetSpotBillDetailsForCurrencyResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v/%v%v", OKGroupAccounts, request.Currency, OKGroupLedger, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
-}
-
-// PlaceSpotOrder token trading only supports limit and market orders (more order types will become available in the future).
-// You can place an order only if you have enough funds.
-// Once your order is placed, the amount will be put on hold.
-func (o *OKGroup) PlaceSpotOrder(ctx context.Context, request *PlaceOrderRequest) (resp PlaceOrderResponse, _ error) {
- if request.OrderType == "" {
- request.OrderType = strconv.Itoa(NormalOrder)
- }
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupTokenSubsection, OKGroupOrders, request, &resp, true)
-}
-
-// PlaceMultipleSpotOrders supports placing multiple orders for specific trading pairs
-// up to 4 trading pairs, maximum 4 orders for each pair
-func (o *OKGroup) PlaceMultipleSpotOrders(ctx context.Context, request []PlaceOrderRequest) (map[string][]PlaceOrderResponse, []error) {
- currencyPairOrders := make(map[string]int)
- resp := make(map[string][]PlaceOrderResponse)
-
- for i := range request {
- if request[i].OrderType == "" {
- request[i].OrderType = strconv.Itoa(NormalOrder)
- }
- currencyPairOrders[request[i].InstrumentID]++
- }
-
- if len(currencyPairOrders) > 4 {
- return resp, []error{errors.New("up to 4 trading pairs")}
- }
- for _, orderCount := range currencyPairOrders {
- if orderCount > 4 {
- return resp, []error{errors.New("maximum 4 orders for each pair")}
- }
- }
-
- err := o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupTokenSubsection, OKGroupBatchOrders, request, &resp, true)
- if err != nil {
- return resp, []error{err}
- }
-
- var orderErrors []error
- for currency, orderResponse := range resp {
- for i := range orderResponse {
- if !orderResponse[i].Result {
- orderErrors = append(orderErrors, fmt.Errorf("order for currency %v failed to be placed", currency))
- }
- }
- }
-
- return resp, orderErrors
-}
-
-// CancelSpotOrder Cancelling an unfilled order.
-func (o *OKGroup) CancelSpotOrder(ctx context.Context, request CancelSpotOrderRequest) (resp CancelSpotOrderResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v", OKGroupCancelOrders, request.OrderID)
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupTokenSubsection, requestURL, request, &resp, true)
-}
-
-// CancelMultipleSpotOrders Cancelling multiple unfilled orders.
-func (o *OKGroup) CancelMultipleSpotOrders(ctx context.Context, request CancelMultipleSpotOrdersRequest) (resp map[string][]CancelMultipleSpotOrdersResponse, err error) {
- resp = make(map[string][]CancelMultipleSpotOrdersResponse)
- if len(request.OrderIDs) > 4 {
- return resp, errors.New("maximum 4 order cancellations for each pair")
- }
-
- err = o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupTokenSubsection, OKGroupCancelBatchOrders, []CancelMultipleSpotOrdersRequest{request}, &resp, true)
- if err != nil {
- return
- }
-
- for currency, orderResponse := range resp {
- for i := range orderResponse {
- cancellationResponse := CancelMultipleSpotOrdersResponse{
- OrderID: orderResponse[i].OrderID,
- Result: orderResponse[i].Result,
- ClientOID: orderResponse[i].ClientOID,
- }
-
- if !orderResponse[i].Result {
- cancellationResponse.Error = fmt.Errorf("order %v for currency %v failed to be cancelled", orderResponse[i].OrderID, currency)
- }
-
- resp[currency] = append(resp[currency], cancellationResponse)
- }
- }
-
- return
-}
-
-// GetSpotOrders List your orders. Cursor pagination is used.
-// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
-func (o *OKGroup) GetSpotOrders(ctx context.Context, request GetSpotOrdersRequest) (resp []GetSpotOrderResponse, _ error) {
- requestURL := fmt.Sprintf("%v%v", OKGroupOrders, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
-}
-
-// GetSpotOpenOrders List all your current open orders. Cursor pagination is used.
-// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
-func (o *OKGroup) GetSpotOpenOrders(ctx context.Context, request GetSpotOpenOrdersRequest) (resp []GetSpotOrderResponse, _ error) {
- requestURL := fmt.Sprintf("%v%v", OKGroupPendingOrders, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
-}
-
-// GetSpotOrder Get order details by order ID.
-func (o *OKGroup) GetSpotOrder(ctx context.Context, request GetSpotOrderRequest) (resp GetSpotOrderResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v%v", OKGroupOrders, request.OrderID, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, requestURL, request, &resp, true)
-}
-
-// GetSpotTransactionDetails Get details of the recent filled orders. Cursor pagination is used.
-// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
-func (o *OKGroup) GetSpotTransactionDetails(ctx context.Context, request GetSpotTransactionDetailsRequest) (resp []GetSpotTransactionDetailsResponse, _ error) {
- requestURL := fmt.Sprintf("%v%v", OKGroupGetSpotTransactionDetails, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, false)
-}
-
-// GetSpotTokenPairDetails Get market data. This endpoint provides the snapshots of market data and can be used without verifications.
-// List trading pairs and get the trading limit, price, and more information of different trading pairs.
-func (o *OKGroup) GetSpotTokenPairDetails(ctx context.Context) (resp []GetSpotTokenPairDetailsResponse, _ error) {
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, OKGroupInstruments, nil, &resp, false)
-}
-
-// GetOrderBook Getting the order book of a trading pair. Pagination is not
-// supported here. The whole book will be returned for one request. Websocket is
-// recommended here.
-func (o *OKGroup) GetOrderBook(ctx context.Context, request *GetOrderBookRequest, a asset.Item) (resp *GetOrderBookResponse, _ error) {
- var requestType, endpoint string
- switch a {
- case asset.Spot:
- endpoint = OKGroupGetSpotOrderBook
- requestType = okGroupTokenSubsection
- case asset.Futures:
- endpoint = OKGroupGetSpotOrderBook
- requestType = "futures"
- case asset.PerpetualSwap:
- endpoint = "depth"
- requestType = "swap"
- default:
- return resp, errors.New("unhandled asset type")
- }
- requestURL := fmt.Sprintf("%v/%v/%v%v",
- OKGroupInstruments,
- request.InstrumentID,
- endpoint,
- FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet,
- requestType,
- requestURL,
- nil,
- &resp,
- false)
-}
-
-// GetSpotAllTokenPairsInformation Get the last traded price, best bid/ask price, 24 hour trading volume and more info of all trading pairs.
-func (o *OKGroup) GetSpotAllTokenPairsInformation(ctx context.Context) (resp []GetSpotTokenPairsInformationResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v", OKGroupInstruments, OKGroupTicker)
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, false)
-}
-
-// GetSpotAllTokenPairsInformationForCurrency Get the last traded price, best bid/ask price, 24 hour trading volume and more info of a currency
-func (o *OKGroup) GetSpotAllTokenPairsInformationForCurrency(ctx context.Context, currency string) (resp GetSpotTokenPairsInformationResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v/%v", OKGroupInstruments, currency, OKGroupTicker)
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, false)
-}
-
-// GetSpotFilledOrdersInformation Get the recent 60 transactions of all trading pairs.
-// Cursor pagination is used. All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
-func (o *OKGroup) GetSpotFilledOrdersInformation(ctx context.Context, request GetSpotFilledOrdersInformationRequest) (resp []GetSpotFilledOrdersInformationResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v/%v%v", OKGroupInstruments, request.InstrumentID, OKGroupTrades, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, false)
-}
-
-// GetMarketData Get the charts of the trading pairs. Charts are returned in grouped buckets based on requested granularity.
-func (o *OKGroup) GetMarketData(ctx context.Context, request *GetMarketDataRequest) (resp GetMarketDataResponse, err error) {
- requestURL := fmt.Sprintf("%v/%v/%v%v", OKGroupInstruments, request.InstrumentID, OKGroupGetSpotMarketData, FormatParameters(request))
- var requestType string
- switch request.Asset {
- case asset.Spot, asset.Margin:
- requestType = okGroupTokenSubsection
- case asset.Futures:
- requestType = okGroupFuturesTradingSubSection
- case asset.PerpetualSwap:
- requestType = oKGroupSwapTradingSubSection
- default:
- return nil, errors.New("asset not supported")
- }
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, requestType, requestURL, nil, &resp, false)
-}
-
-// GetMarginTradingAccounts List all assets under token margin trading account, including information such as balance, amount on hold and more.
-func (o *OKGroup) GetMarginTradingAccounts(ctx context.Context) (resp []GetMarginAccountsResponse, _ error) {
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupMarginTradingSubsection, OKGroupAccounts, nil, &resp, true)
-}
-
-// GetMarginTradingAccountsForCurrency Get the balance, amount on hold and more useful information.
-func (o *OKGroup) GetMarginTradingAccountsForCurrency(ctx context.Context, currency string) (resp GetMarginAccountsResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v", OKGroupAccounts, currency)
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
-}
-
-// GetMarginBillDetails List all bill details. Pagination is used here.
-// before and after cursor arguments should not be confused with before and after in chronological time.
-// Most paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
-func (o *OKGroup) GetMarginBillDetails(ctx context.Context, request GetMarginBillDetailsRequest) (resp []GetSpotBillDetailsForCurrencyResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v/%v%v", OKGroupAccounts, request.InstrumentID, OKGroupLedger, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
-}
-
-// GetMarginAccountSettings Get all information of the margin trading account,
-// including the maximum loan amount, interest rate, and maximum leverage.
-func (o *OKGroup) GetMarginAccountSettings(ctx context.Context, currency string) (resp []GetMarginAccountSettingsResponse, _ error) {
- var requestURL string
- if currency != "" {
- requestURL = fmt.Sprintf("%v/%v/%v", OKGroupAccounts, currency, okGroupGetMarketAvailability)
- } else {
- requestURL = fmt.Sprintf("%v/%v", OKGroupAccounts, okGroupGetMarketAvailability)
- }
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
-}
-
-// GetMarginLoanHistory Get loan history of the margin trading account.
-// Pagination is used here. before and after cursor arguments should not be confused with before and after in chronological time.
-// Most paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
-func (o *OKGroup) GetMarginLoanHistory(ctx context.Context, request GetMarginLoanHistoryRequest) (resp []GetMarginLoanHistoryResponse, _ error) {
- var requestURL string
- if len(request.InstrumentID) > 0 {
- requestURL = fmt.Sprintf("%v/%v/%v", OKGroupAccounts, request.InstrumentID, okGroupGetLoan)
- } else {
- requestURL = fmt.Sprintf("%v/%v", OKGroupAccounts, okGroupGetLoan)
- }
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
-}
-
-// OpenMarginLoan Borrowing tokens in a margin trading account.
-func (o *OKGroup) OpenMarginLoan(ctx context.Context, request OpenMarginLoanRequest) (resp OpenMarginLoanResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v", OKGroupAccounts, okGroupGetLoan)
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupMarginTradingSubsection, requestURL, request, &resp, true)
-}
-
-// RepayMarginLoan Repaying tokens in a margin trading account.
-func (o *OKGroup) RepayMarginLoan(ctx context.Context, request RepayMarginLoanRequest) (resp RepayMarginLoanResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v", OKGroupAccounts, okGroupGetRepayment)
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupMarginTradingSubsection, requestURL, request, &resp, true)
-}
-
-// PlaceMarginOrder You can place an order only if you have enough funds. Once your order is placed, the amount will be put on hold.
-func (o *OKGroup) PlaceMarginOrder(ctx context.Context, request *PlaceOrderRequest) (resp PlaceOrderResponse, _ error) {
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupMarginTradingSubsection, OKGroupOrders, request, &resp, true)
-}
-
-// PlaceMultipleMarginOrders Place multiple orders for specific trading pairs (up to 4 trading pairs, maximum 4 orders each)
-func (o *OKGroup) PlaceMultipleMarginOrders(ctx context.Context, request []PlaceOrderRequest) (map[string][]PlaceOrderResponse, []error) {
- currencyPairOrders := make(map[string]int)
- resp := make(map[string][]PlaceOrderResponse)
- for i := range request {
- currencyPairOrders[request[i].InstrumentID]++
- }
- if len(currencyPairOrders) > 4 {
- return resp, []error{errors.New("up to 4 trading pairs")}
- }
- for _, orderCount := range currencyPairOrders {
- if orderCount > 4 {
- return resp, []error{errors.New("maximum 4 orders for each pair")}
- }
- }
-
- err := o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupMarginTradingSubsection, OKGroupBatchOrders, request, &resp, true)
- if err != nil {
- return resp, []error{err}
- }
-
- var orderErrors []error
- for currency, orderResponse := range resp {
- for i := range orderResponse {
- if !orderResponse[i].Result {
- orderErrors = append(orderErrors, fmt.Errorf("order for currency %v failed to be placed", currency))
- }
- }
- }
-
- return resp, orderErrors
-}
-
-// CancelMarginOrder Cancelling an unfilled order.
-func (o *OKGroup) CancelMarginOrder(ctx context.Context, request CancelSpotOrderRequest) (resp CancelSpotOrderResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v", OKGroupCancelOrders, request.OrderID)
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupMarginTradingSubsection, requestURL, request, &resp, true)
-}
-
-// CancelMultipleMarginOrders Cancelling multiple unfilled orders.
-func (o *OKGroup) CancelMultipleMarginOrders(ctx context.Context, request CancelMultipleSpotOrdersRequest) (map[string][]CancelMultipleSpotOrdersResponse, []error) {
- resp := make(map[string][]CancelMultipleSpotOrdersResponse)
- if len(request.OrderIDs) > 4 {
- return resp, []error{errors.New("maximum 4 order cancellations for each pair")}
- }
-
- err := o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupMarginTradingSubsection, OKGroupCancelBatchOrders, []CancelMultipleSpotOrdersRequest{request}, &resp, true)
- if err != nil {
- return resp, []error{err}
- }
-
- var orderErrors []error
- for currency, orderResponse := range resp {
- for i := range orderResponse {
- if !orderResponse[i].Result {
- orderErrors = append(orderErrors, fmt.Errorf("order %v for currency %v failed to be cancelled", orderResponse[i].OrderID, currency))
- }
- }
- }
-
- return resp, orderErrors
-}
-
-// GetMarginOrders List your orders. Cursor pagination is used. All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
-func (o *OKGroup) GetMarginOrders(ctx context.Context, request GetSpotOrdersRequest) (resp []GetSpotOrderResponse, _ error) {
- requestURL := fmt.Sprintf("%v%v", OKGroupOrders, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
-}
-
-// GetMarginOpenOrders List all your current open orders. Cursor pagination is used. All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
-func (o *OKGroup) GetMarginOpenOrders(ctx context.Context, request GetSpotOpenOrdersRequest) (resp []GetSpotOrderResponse, _ error) {
- requestURL := fmt.Sprintf("%v%v", OKGroupPendingOrders, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
-}
-
-// GetMarginOrder Get order details by order ID.
-func (o *OKGroup) GetMarginOrder(ctx context.Context, request GetSpotOrderRequest) (resp GetSpotOrderResponse, _ error) {
- requestURL := fmt.Sprintf("%v/%v%v", OKGroupOrders, request.OrderID, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupMarginTradingSubsection, requestURL, request, &resp, true)
-}
-
-// GetMarginTransactionDetails Get details of the recent filled orders. Cursor pagination is used.
-// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
-func (o *OKGroup) GetMarginTransactionDetails(ctx context.Context, request GetSpotTransactionDetailsRequest) (resp []GetSpotTransactionDetailsResponse, _ error) {
- requestURL := fmt.Sprintf("%v%v", OKGroupGetSpotTransactionDetails, FormatParameters(request))
- return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
-}
-
-// FormatParameters Formats URL parameters, useful for optional parameters due to OKCoin signature check
-func FormatParameters(request interface{}) (parameters string) {
- v, err := query.Values(request)
- if err != nil {
- log.Errorf(log.ExchangeSys, "Could not parse %v to URL values. Check that the type has url fields", reflect.TypeOf(request).Name())
- return
- }
- if urlEncodedValues := v.Encode(); len(urlEncodedValues) > 0 {
- parameters = fmt.Sprintf("?%v", urlEncodedValues)
- }
- return
-}
-
-// GetErrorCode returns an error code
-func (o *OKGroup) GetErrorCode(code interface{}) error {
- var assertedCode string
-
- switch d := code.(type) {
- case float64:
- assertedCode = strconv.FormatFloat(d, 'f', -1, 64)
- case string:
- assertedCode = d
- default:
- return errors.New("unusual type returned")
- }
-
- if i, ok := o.ErrorCodes[assertedCode]; ok {
- return i
- }
- return errors.New("unable to find SPOT error code")
-}
-
-// SendHTTPRequest sends an authenticated http request to a desired
-// path with a JSON payload (of present)
-// URL arguments must be in the request path and not as url.URL values
-func (o *OKGroup) SendHTTPRequest(ctx context.Context, ep exchange.URL, httpMethod, requestType, requestPath string, data, result interface{}, authenticated bool) (err error) {
- endpoint, err := o.API.Endpoints.GetURL(ep)
- if err != nil {
- return err
- }
-
- var intermediary json.RawMessage
- newRequest := func() (*request.Item, error) {
- utcTime := time.Now().UTC().Format(time.RFC3339)
- payload := []byte("")
-
- if data != nil {
- payload, err = json.Marshal(data)
- if err != nil {
- return nil, err
- }
- }
-
- path := endpoint + requestType + o.APIVersion + requestPath
- headers := make(map[string]string)
- headers["Content-Type"] = "application/json"
- if authenticated {
- var creds *account.Credentials
- creds, err = o.GetCredentials(ctx)
- if err != nil {
- return nil, err
- }
- signPath := fmt.Sprintf("/%v%v%v%v", OKGroupAPIPath,
- requestType, o.APIVersion, requestPath)
-
- var hmac []byte
- hmac, err = crypto.GetHMAC(crypto.HashSHA256,
- []byte(utcTime+httpMethod+signPath+string(payload)),
- []byte(creds.Secret))
- if err != nil {
- return nil, err
- }
- headers["OK-ACCESS-KEY"] = creds.Key
- headers["OK-ACCESS-SIGN"] = crypto.Base64Encode(hmac)
- headers["OK-ACCESS-TIMESTAMP"] = utcTime
- headers["OK-ACCESS-PASSPHRASE"] = creds.ClientID
- }
-
- return &request.Item{
- Method: strings.ToUpper(httpMethod),
- Path: path,
- Headers: headers,
- Body: bytes.NewBuffer(payload),
- Result: &intermediary,
- AuthRequest: authenticated,
- Verbose: o.Verbose,
- HTTPDebugging: o.HTTPDebugging,
- HTTPRecording: o.HTTPRecording,
- }, nil
- }
-
- err = o.SendPayload(ctx, request.Unset, newRequest)
- if err != nil {
- return err
- }
-
- type errCapFormat struct {
- Error int64 `json:"error_code,omitempty"`
- ErrorMessage string `json:"error_message,omitempty"`
- Result bool `json:"result,string,omitempty"`
- }
- errCap := errCapFormat{Result: true}
-
- err = json.Unmarshal(intermediary, &errCap)
- if err == nil {
- if errCap.ErrorMessage != "" {
- return fmt.Errorf("error: %v", errCap.ErrorMessage)
- }
- if errCap.Error > 0 {
- return fmt.Errorf("sendHTTPRequest error - %s",
- o.ErrorCodes[strconv.FormatInt(errCap.Error, 10)])
- }
- if !errCap.Result {
- return errors.New("unspecified error occurred")
- }
- }
-
- return json.Unmarshal(intermediary, result)
-}
-
-// SetCheckVarDefaults sets main variables that will be used in requests because
-// api does not return an error if there are misspellings in strings. So better
-// to check on this, this end.
-func (o *OKGroup) SetCheckVarDefaults() {
- o.ContractTypes = []string{"this_week", "next_week", "quarter"}
- o.CurrencyPairsDefaults = []string{"btc_usd", "ltc_usd", "eth_usd", "etc_usd", "bch_usd"}
- o.Types = []string{"1min", "3min", "5min", "15min", "30min", "1day", "3day",
- "1week", "1hour", "2hour", "4hour", "6hour", "12hour"}
- o.ContractPosition = []string{"1", "2", "3", "4"}
-}
-
-// GetFee returns an estimate of fee based on type of transaction
-func (o *OKGroup) GetFee(ctx context.Context, feeBuilder *exchange.FeeBuilder) (fee float64, _ error) {
- switch feeBuilder.FeeType {
- case exchange.CryptocurrencyTradeFee:
- fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker)
- case exchange.CryptocurrencyWithdrawalFee:
- withdrawFees, err := o.GetAccountWithdrawalFee(ctx, feeBuilder.FiatCurrency.String())
- if err != nil {
- return -1, err
- }
- for _, withdrawFee := range withdrawFees {
- if withdrawFee.Currency == feeBuilder.FiatCurrency.String() {
- fee = withdrawFee.MinFee
- break
- }
- }
- case exchange.OfflineTradeFee:
- fee = getOfflineTradeFee(feeBuilder.PurchasePrice, feeBuilder.Amount)
- }
- if fee < 0 {
- fee = 0
- }
-
- return fee, nil
-}
-
-// getOfflineTradeFee calculates the worst case-scenario trading fee
-func getOfflineTradeFee(price, amount float64) float64 {
- return 0.0015 * price * amount
-}
-
-func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) {
- // TODO volume based fees
- if isMaker {
- fee = 0.0005
- } else {
- fee = 0.0015
- }
- return fee * amount * purchasePrice
-}
-
-// SetErrorDefaults sets the full error default list
-func (o *OKGroup) SetErrorDefaults() {
- o.ErrorCodes = map[string]error{
- "0": errors.New("successful"),
- "1": errors.New("invalid parameter in url normally"),
- "30001": errors.New("request header \"OK_ACCESS_KEY\" cannot be blank"),
- "30002": errors.New("request header \"OK_ACCESS_SIGN\" cannot be blank"),
- "30003": errors.New("request header \"OK_ACCESS_TIMESTAMP\" cannot be blank"),
- "30004": errors.New("request header \"OK_ACCESS_PASSPHRASE\" cannot be blank"),
- "30005": errors.New("invalid OK_ACCESS_TIMESTAMP"),
- "30006": errors.New("invalid OK_ACCESS_KEY"),
- "30007": errors.New("invalid Content_Type, please use \"application/json\" format"),
- "30008": errors.New("timestamp request expired"),
- "30009": errors.New("system error"),
- "30010": errors.New("api validation failed"),
- "30011": errors.New("invalid IP"),
- "30012": errors.New("invalid authorization"),
- "30013": errors.New("invalid sign"),
- "30014": errors.New("request too frequent"),
- "30015": errors.New("request header \"OK_ACCESS_PASSPHRASE\" incorrect"),
- "30016": errors.New("you are using v1 apiKey, please use v1 endpoint. If you would like to use v3 endpoint, please subscribe to v3 apiKey"),
- "30017": errors.New("apikey's broker id does not match"),
- "30018": errors.New("apikey's domain does not match"),
- "30020": errors.New("body cannot be blank"),
- "30021": errors.New("json data format error"),
- "30023": errors.New("required parameter cannot be blank"),
- "30024": errors.New("parameter value error"),
- "30025": errors.New("parameter category error"),
- "30026": errors.New("requested too frequent; endpoint limit exceeded"),
- "30027": errors.New("login failure"),
- "30028": errors.New("unauthorized execution"),
- "30029": errors.New("account suspended"),
- "30030": errors.New("endpoint request failed. Please try again"),
- "30031": errors.New("token does not exist"),
- "30032": errors.New("pair does not exist"),
- "30033": errors.New("exchange domain does not exist"),
- "30034": errors.New("exchange ID does not exist"),
- "30035": errors.New("trading is not supported in this website"),
- "30036": errors.New("no relevant data"),
- "30037": errors.New("endpoint is offline or unavailable"),
- "30038": errors.New("user does not exist"),
- "32001": errors.New("futures account suspended"),
- "32002": errors.New("futures account does not exist"),
- "32003": errors.New("canceling, please wait"),
- "32004": errors.New("you have no unfilled orders"),
- "32005": errors.New("max order quantity"),
- "32006": errors.New("the order price or trigger price exceeds USD 1 million"),
- "32007": errors.New("leverage level must be the same for orders on the same side of the contract"),
- "32008": errors.New("max. positions to open (cross margin)"),
- "32009": errors.New("max. positions to open (fixed margin)"),
- "32010": errors.New("leverage cannot be changed with open positions"),
- "32011": errors.New("futures status error"),
- "32012": errors.New("futures order update error"),
- "32013": errors.New("token type is blank"),
- "32014": errors.New("your number of contracts closing is larger than the number of contracts available"),
- "32015": errors.New("margin ratio is lower than 100% before opening positions"),
- "32016": errors.New("margin ratio is lower than 100% after opening position"),
- "32017": errors.New("no BBO"),
- "32018": errors.New("the order quantity is less than 1, please try again"),
- "32019": errors.New("the order price deviates from the price of the previous minute by more than 3%"),
- "32020": errors.New("the price is not in the range of the price limit"),
- "32021": errors.New("leverage error"),
- "32022": errors.New("this function is not supported in your country or region according to the regulations"),
- "32023": errors.New("this account has outstanding loan"),
- "32024": errors.New("order cannot be placed during delivery"),
- "32025": errors.New("order cannot be placed during settlement"),
- "32026": errors.New("your account is restricted from opening positions"),
- "32027": errors.New("cancelled over 20 orders"),
- "32028": errors.New("account is suspended and liquidated"),
- "32029": errors.New("order info does not exist"),
- "33001": errors.New("margin account for this pair is not enabled yet"),
- "33002": errors.New("margin account for this pair is suspended"),
- "33003": errors.New("no loan balance"),
- "33004": errors.New("loan amount cannot be smaller than the minimum limit"),
- "33005": errors.New("repayment amount must exceed 0"),
- "33006": errors.New("loan order not found"),
- "33007": errors.New("status not found"),
- "33008": errors.New("loan amount cannot exceed the maximum limit"),
- "33009": errors.New("user ID is blank"),
- "33010": errors.New("you cannot cancel an order during session 2 of call auction"),
- "33011": errors.New("no new market data"),
- "33012": errors.New("order cancellation failed"),
- "33013": errors.New("order placement failed"),
- "33014": errors.New("order does not exist"),
- "33015": errors.New("exceeded maximum limit"),
- "33016": errors.New("margin trading is not open for this token"),
- "33017": errors.New("insufficient balance"),
- "33018": errors.New("this parameter must be smaller than 1"),
- "33020": errors.New("request not supported"),
- "33021": errors.New("token and the pair do not match"),
- "33022": errors.New("pair and the order do not match"),
- "33023": errors.New("you can only place market orders during call auction"),
- "33024": errors.New("trading amount too small"),
- "33025": errors.New("base token amount is blank"),
- "33026": errors.New("transaction completed"),
- "33027": errors.New("cancelled order or order cancelling"),
- "33028": errors.New("the decimal places of the trading price exceeded the limit"),
- "33029": errors.New("the decimal places of the trading size exceeded the limit"),
- "34001": errors.New("withdrawal suspended"),
- "34002": errors.New("please add a withdrawal address"),
- "34003": errors.New("sorry, this token cannot be withdrawn to xx at the moment"),
- "34004": errors.New("withdrawal fee is smaller than minimum limit"),
- "34005": errors.New("withdrawal fee exceeds the maximum limit"),
- "34006": errors.New("withdrawal amount is lower than the minimum limit"),
- "34007": errors.New("withdrawal amount exceeds the maximum limit"),
- "34008": errors.New("insufficient balance"),
- "34009": errors.New("your withdrawal amount exceeds the daily limit"),
- "34010": errors.New("transfer amount must be larger than 0"),
- "34011": errors.New("conditions not met"),
- "34012": errors.New("the minimum withdrawal amount for NEO is 1, and the amount must be an integer"),
- "34013": errors.New("please transfer"),
- "34014": errors.New("transfer limited"),
- "34015": errors.New("subaccount does not exist"),
- "34016": errors.New("transfer suspended"),
- "34017": errors.New("account suspended"),
- "34018": errors.New("incorrect trades password"),
- "34019": errors.New("please bind your email before withdrawal"),
- "34020": errors.New("please bind your funds password before withdrawal"),
- "34021": errors.New("not verified address"),
- "34022": errors.New("withdrawals are not available for sub accounts"),
- "35001": errors.New("contract subscribing does not exist"),
- "35002": errors.New("contract is being settled"),
- "35003": errors.New("contract is being paused"),
- "35004": errors.New("pending contract settlement"),
- "35005": errors.New("perpetual swap trading is not enabled"),
- "35008": errors.New("margin ratio too low when placing order"),
- "35010": errors.New("closing position size larger than available size"),
- "35012": errors.New("placing an order with less than 1 contract"),
- "35014": errors.New("order size is not in acceptable range"),
- "35015": errors.New("leverage level unavailable"),
- "35017": errors.New("changing leverage level"),
- "35019": errors.New("order size exceeds limit"),
- "35020": errors.New("order price exceeds limit"),
- "35021": errors.New("order size exceeds limit of the current tier"),
- "35022": errors.New("contract is paused or closed"),
- "35030": errors.New("place multiple orders"),
- "35031": errors.New("cancel multiple orders"),
- "35061": errors.New("invalid instrument_id"),
- }
-}
diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go
deleted file mode 100644
index dc81d1c0..00000000
--- a/exchanges/okgroup/okgroup_wrapper.go
+++ /dev/null
@@ -1,810 +0,0 @@
-package okgroup
-
-import (
- "context"
- "errors"
- "fmt"
- "strconv"
- "strings"
- "time"
-
- "github.com/thrasher-corp/gocryptotrader/common"
- "github.com/thrasher-corp/gocryptotrader/common/convert"
- "github.com/thrasher-corp/gocryptotrader/config"
- "github.com/thrasher-corp/gocryptotrader/currency"
- exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
- "github.com/thrasher-corp/gocryptotrader/exchanges/account"
- "github.com/thrasher-corp/gocryptotrader/exchanges/asset"
- "github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
- "github.com/thrasher-corp/gocryptotrader/exchanges/kline"
- "github.com/thrasher-corp/gocryptotrader/exchanges/order"
- "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
- "github.com/thrasher-corp/gocryptotrader/exchanges/stream"
- "github.com/thrasher-corp/gocryptotrader/exchanges/trade"
- "github.com/thrasher-corp/gocryptotrader/log"
- "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
-)
-
-// Note: GoCryptoTrader wrapper funcs currently only support SPOT trades.
-// Therefore this OKGroup_Wrapper can be shared between OKCoin and OKCoin.
-// When circumstances change, wrapper funcs can be split appropriately
-
-var errNoAccountDepositAddress = errors.New("no account deposit address")
-
-// Setup sets user exchange configuration settings
-func (o *OKGroup) Setup(exch *config.Exchange) error {
- err := exch.Validate()
- if err != nil {
- return err
- }
- if !exch.Enabled {
- o.SetEnabled(false)
- return nil
- }
- err = o.SetupDefaults(exch)
- if err != nil {
- return err
- }
-
- wsEndpoint, err := o.API.Endpoints.GetURL(exchange.WebsocketSpot)
- if err != nil {
- return err
- }
- err = o.Websocket.Setup(&stream.WebsocketSetup{
- ExchangeConfig: exch,
- DefaultURL: wsEndpoint,
- RunningURL: wsEndpoint,
- Connector: o.WsConnect,
- Subscriber: o.Subscribe,
- Unsubscriber: o.Unsubscribe,
- GenerateSubscriptions: o.GenerateDefaultSubscriptions,
- Features: &o.Features.Supports.WebsocketCapabilities,
- })
- if err != nil {
- return err
- }
-
- return o.Websocket.SetupNewConnection(stream.ConnectionSetup{
- RateLimit: okGroupWsRateLimit,
- ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
- ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
- })
-}
-
-// FetchOrderbook returns orderbook base on the currency pair
-func (o *OKGroup) FetchOrderbook(ctx context.Context, p currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
- fPair, err := o.FormatExchangeCurrency(p, assetType)
- if err != nil {
- return nil, err
- }
- ob, err := orderbook.Get(o.Name, fPair, assetType)
- if err != nil {
- return o.UpdateOrderbook(ctx, fPair, assetType)
- }
- return ob, nil
-}
-
-// UpdateOrderbook updates and returns the orderbook for a currency pair
-func (o *OKGroup) UpdateOrderbook(ctx context.Context, p currency.Pair, a asset.Item) (*orderbook.Base, error) {
- book := &orderbook.Base{
- Exchange: o.Name,
- Pair: p,
- Asset: a,
- VerifyOrderbook: o.CanVerifyOrderbook,
- }
-
- if a == asset.Index {
- return book, errors.New("no orderbooks for index")
- }
-
- fPair, err := o.FormatExchangeCurrency(p, a)
- if err != nil {
- return book, err
- }
-
- orderbookNew, err := o.GetOrderBook(ctx,
- &GetOrderBookRequest{
- InstrumentID: fPair.String(),
- Size: 200,
- }, a)
- if err != nil {
- return book, err
- }
-
- book.Bids = make(orderbook.Items, len(orderbookNew.Bids))
- for x := range orderbookNew.Bids {
- amount, convErr := strconv.ParseFloat(orderbookNew.Bids[x][1], 64)
- if convErr != nil {
- return book, err
- }
- price, convErr := strconv.ParseFloat(orderbookNew.Bids[x][0], 64)
- if convErr != nil {
- return book, err
- }
-
- var liquidationOrders, orderCount int64
- // Contract specific variables
- if len(orderbookNew.Bids[x]) == 4 {
- liquidationOrders, convErr = strconv.ParseInt(orderbookNew.Bids[x][2], 10, 64)
- if convErr != nil {
- return book, err
- }
-
- orderCount, convErr = strconv.ParseInt(orderbookNew.Bids[x][3], 10, 64)
- if convErr != nil {
- return book, err
- }
- }
-
- book.Bids[x] = orderbook.Item{
- Amount: amount,
- Price: price,
- LiquidationOrders: liquidationOrders,
- OrderCount: orderCount,
- }
- }
-
- book.Asks = make(orderbook.Items, len(orderbookNew.Asks))
- for x := range orderbookNew.Asks {
- amount, convErr := strconv.ParseFloat(orderbookNew.Asks[x][1], 64)
- if convErr != nil {
- return book, err
- }
- price, convErr := strconv.ParseFloat(orderbookNew.Asks[x][0], 64)
- if convErr != nil {
- return book, err
- }
-
- var liquidationOrders, orderCount int64
- // Contract specific variables
- if len(orderbookNew.Asks[x]) == 4 {
- liquidationOrders, convErr = strconv.ParseInt(orderbookNew.Asks[x][2], 10, 64)
- if convErr != nil {
- return book, err
- }
-
- orderCount, convErr = strconv.ParseInt(orderbookNew.Asks[x][3], 10, 64)
- if convErr != nil {
- return book, err
- }
- }
-
- book.Asks[x] = orderbook.Item{
- Amount: amount,
- Price: price,
- LiquidationOrders: liquidationOrders,
- OrderCount: orderCount,
- }
- }
-
- err = book.Process()
- if err != nil {
- return book, err
- }
-
- return orderbook.Get(o.Name, fPair, a)
-}
-
-// UpdateAccountInfo retrieves balances for all enabled currencies
-func (o *OKGroup) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) {
- currencies, err := o.GetSpotTradingAccounts(ctx)
- if err != nil {
- return account.Holdings{}, err
- }
-
- var resp account.Holdings
- resp.Exchange = o.Name
- currencyAccount := account.SubAccount{AssetType: assetType}
-
- for i := range currencies {
- hold, parseErr := strconv.ParseFloat(currencies[i].Hold, 64)
- if parseErr != nil {
- return resp, parseErr
- }
- totalValue, parseErr := strconv.ParseFloat(currencies[i].Balance, 64)
- if parseErr != nil {
- return resp, parseErr
- }
- currencyAccount.Currencies = append(currencyAccount.Currencies,
- account.Balance{
- CurrencyName: currency.NewCode(currencies[i].Currency),
- Total: totalValue,
- Hold: hold,
- Free: totalValue - hold,
- })
- }
-
- resp.Accounts = append(resp.Accounts, currencyAccount)
-
- creds, err := o.GetCredentials(ctx)
- if err != nil {
- return account.Holdings{}, err
- }
- err = account.Process(&resp, creds)
- if err != nil {
- return resp, err
- }
-
- return resp, nil
-}
-
-// FetchAccountInfo retrieves balances for all enabled currencies
-func (o *OKGroup) FetchAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) {
- creds, err := o.GetCredentials(ctx)
- if err != nil {
- return account.Holdings{}, err
- }
- acc, err := account.GetHoldings(o.Name, creds, assetType)
- if err != nil {
- return o.UpdateAccountInfo(ctx, assetType)
- }
- return acc, nil
-}
-
-// GetFundingHistory returns funding history, deposits and
-// withdrawals
-func (o *OKGroup) GetFundingHistory(ctx context.Context) (resp []exchange.FundHistory, err error) {
- accountDepositHistory, err := o.GetAccountDepositHistory(ctx, "")
- if err != nil {
- return
- }
- for x := range accountDepositHistory {
- orderStatus := ""
- switch accountDepositHistory[x].Status {
- case 0:
- orderStatus = "waiting"
- case 1:
- orderStatus = "confirmation account"
- case 2:
- orderStatus = "recharge success"
- }
-
- resp = append(resp, exchange.FundHistory{
- Amount: accountDepositHistory[x].Amount,
- Currency: accountDepositHistory[x].Currency,
- ExchangeName: o.Name,
- Status: orderStatus,
- Timestamp: accountDepositHistory[x].Timestamp,
- TransferID: accountDepositHistory[x].TransactionID,
- TransferType: "deposit",
- })
- }
- accountWithdrawlHistory, err := o.GetAccountWithdrawalHistory(ctx, "")
- for i := range accountWithdrawlHistory {
- resp = append(resp, exchange.FundHistory{
- Amount: accountWithdrawlHistory[i].Amount,
- Currency: accountWithdrawlHistory[i].Currency,
- ExchangeName: o.Name,
- Status: OrderStatus[accountWithdrawlHistory[i].Status],
- Timestamp: accountWithdrawlHistory[i].Timestamp,
- TransferID: accountWithdrawlHistory[i].TransactionID,
- TransferType: "withdrawal",
- })
- }
- return resp, err
-}
-
-// SubmitOrder submits a new order
-func (o *OKGroup) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) {
- if err := s.Validate(); err != nil {
- return nil, err
- }
-
- fpair, err := o.FormatExchangeCurrency(s.Pair, s.AssetType)
- if err != nil {
- return nil, err
- }
-
- request := PlaceOrderRequest{
- ClientOID: s.ClientID,
- InstrumentID: fpair.String(),
- Side: s.Side.Lower(),
- Type: s.Type.Lower(),
- Size: strconv.FormatFloat(s.Amount, 'f', -1, 64),
- }
- if s.Type == order.Limit {
- request.Price = strconv.FormatFloat(s.Price, 'f', -1, 64)
- }
-
- orderResponse, err := o.PlaceSpotOrder(ctx, &request)
- if err != nil {
- return nil, err
- }
-
- if !orderResponse.Result {
- return nil, order.ErrUnableToPlaceOrder
- }
- return s.DeriveSubmitResponse(orderResponse.OrderID)
-}
-
-// ModifyOrder will allow of changing orderbook placement and limit to
-// market conversion
-func (o *OKGroup) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
- return nil, common.ErrFunctionNotSupported
-}
-
-// CancelOrder cancels an order by its corresponding ID number
-func (o *OKGroup) CancelOrder(ctx context.Context, cancel *order.Cancel) (err error) {
- err = cancel.Validate(cancel.StandardCancel())
- if err != nil {
- return
- }
-
- orderID, err := strconv.ParseInt(cancel.OrderID, 10, 64)
- if err != nil {
- return
- }
-
- fpair, err := o.FormatExchangeCurrency(cancel.Pair,
- cancel.AssetType)
- if err != nil {
- return
- }
-
- orderCancellationResponse, err := o.CancelSpotOrder(ctx,
- CancelSpotOrderRequest{
- InstrumentID: fpair.String(),
- OrderID: orderID,
- })
-
- if !orderCancellationResponse.Result {
- err = fmt.Errorf("order %d failed to be cancelled",
- orderCancellationResponse.OrderID)
- }
-
- return
-}
-
-// CancelAllOrders cancels all orders associated with a currency pair
-func (o *OKGroup) CancelAllOrders(ctx context.Context, orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
- if err := orderCancellation.Validate(); err != nil {
- return order.CancelAllResponse{}, err
- }
-
- orderIDs := strings.Split(orderCancellation.OrderID, ",")
- resp := order.CancelAllResponse{}
- resp.Status = make(map[string]string)
- orderIDNumbers := make([]int64, 0, len(orderIDs))
- for i := range orderIDs {
- orderIDNumber, err := strconv.ParseInt(orderIDs[i], 10, 64)
- if err != nil {
- resp.Status[orderIDs[i]] = err.Error()
- continue
- }
- orderIDNumbers = append(orderIDNumbers, orderIDNumber)
- }
-
- fpair, err := o.FormatExchangeCurrency(orderCancellation.Pair,
- orderCancellation.AssetType)
- if err != nil {
- return resp, err
- }
-
- cancelOrdersResponse, err := o.CancelMultipleSpotOrders(ctx,
- CancelMultipleSpotOrdersRequest{
- InstrumentID: fpair.String(),
- OrderIDs: orderIDNumbers,
- })
- if err != nil {
- return resp, err
- }
-
- for x := range cancelOrdersResponse {
- for y := range cancelOrdersResponse[x] {
- resp.Status[strconv.FormatInt(cancelOrdersResponse[x][y].OrderID, 10)] = strconv.FormatBool(cancelOrdersResponse[x][y].Result)
- }
- }
-
- return resp, err
-}
-
-// GetOrderInfo returns order information based on order ID
-func (o *OKGroup) GetOrderInfo(ctx context.Context, orderID string, pair currency.Pair, assetType asset.Item) (resp order.Detail, err error) {
- if assetType != asset.Spot {
- return resp, fmt.Errorf("%s %w", assetType, asset.ErrNotSupported)
- }
-
- mOrder, err := o.GetSpotOrder(ctx, GetSpotOrderRequest{OrderID: orderID})
- if err != nil {
- return
- }
-
- format, err := o.GetPairFormat(assetType, false)
- if err != nil {
- return resp, err
- }
-
- p, err := currency.NewPairDelimiter(mOrder.InstrumentID, format.Delimiter)
- if err != nil {
- return resp, err
- }
-
- status, err := order.StringToOrderStatus(mOrder.Status)
- if err != nil {
- log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
- }
-
- side, err := order.StringToOrderSide(mOrder.Side)
- if err != nil {
- log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
- }
- resp = order.Detail{
- Amount: mOrder.Size,
- Pair: p,
- Exchange: o.Name,
- Date: mOrder.Timestamp,
- ExecutedAmount: mOrder.FilledSize,
- Status: status,
- Side: side,
- }
- return resp, nil
-}
-
-// GetDepositAddress returns a deposit address for a specified currency
-func (o *OKGroup) GetDepositAddress(ctx context.Context, c currency.Code, _, _ string) (*deposit.Address, error) {
- wallet, err := o.GetAccountDepositAddressForCurrency(ctx, c.Lower().String())
- if err != nil {
- return nil, err
- }
- if len(wallet) == 0 {
- return nil, fmt.Errorf("%w for currency %s",
- errNoAccountDepositAddress,
- c)
- }
- return &deposit.Address{
- Address: wallet[0].Address,
- Tag: wallet[0].Tag,
- }, nil
-}
-
-// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
-// submitted
-func (o *OKGroup) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
- if err := withdrawRequest.Validate(); err != nil {
- return nil, err
- }
- withdrawal, err := o.AccountWithdraw(ctx,
- AccountWithdrawRequest{
- Amount: withdrawRequest.Amount,
- Currency: withdrawRequest.Currency.Lower().String(),
- Destination: 4, // 1, 2, 3 are all internal
- Fee: withdrawRequest.Crypto.FeeAmount,
- ToAddress: withdrawRequest.Crypto.Address,
- TradePwd: withdrawRequest.TradePassword,
- })
- if err != nil {
- return nil, err
- }
- if !withdrawal.Result {
- return nil,
- fmt.Errorf("could not withdraw currency %s to %s, no error specified",
- withdrawRequest.Currency,
- withdrawRequest.Crypto.Address)
- }
-
- return &withdraw.ExchangeResponse{
- ID: strconv.FormatInt(withdrawal.WithdrawalID, 10),
- }, nil
-}
-
-// WithdrawFiatFunds returns a withdrawal ID when a
-// withdrawal is submitted
-func (o *OKGroup) WithdrawFiatFunds(_ context.Context, _ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
- return nil, common.ErrFunctionNotSupported
-}
-
-// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
-// withdrawal is submitted
-func (o *OKGroup) WithdrawFiatFundsToInternationalBank(_ context.Context, _ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
- return nil, common.ErrFunctionNotSupported
-}
-
-// GetWithdrawalsHistory returns previous withdrawals data
-func (o *OKGroup) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
- return nil, common.ErrNotYetImplemented
-}
-
-// GetActiveOrders retrieves any orders that are active/open
-func (o *OKGroup) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest) (order.FilteredOrders, error) {
- err := req.Validate()
- if err != nil {
- return nil, err
- }
-
- var resp []order.Detail
- for x := range req.Pairs {
- var fPair currency.Pair
- fPair, err = o.FormatExchangeCurrency(req.Pairs[x], asset.Spot)
- if err != nil {
- return nil, err
- }
- var spotOpenOrders []GetSpotOrderResponse
- spotOpenOrders, err = o.GetSpotOpenOrders(ctx,
- GetSpotOpenOrdersRequest{
- InstrumentID: fPair.String(),
- })
- if err != nil {
- return nil, err
- }
- for i := range spotOpenOrders {
- var status order.Status
- status, err = order.StringToOrderStatus(spotOpenOrders[i].Status)
- if err != nil {
- log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
- }
- var side order.Side
- side, err = order.StringToOrderSide(spotOpenOrders[i].Side)
- if err != nil {
- log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
- }
- var orderType order.Type
- orderType, err = order.StringToOrderType(spotOpenOrders[i].Type)
- if err != nil {
- log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
- }
- resp = append(resp, order.Detail{
- OrderID: spotOpenOrders[i].OrderID,
- Price: spotOpenOrders[i].Price,
- Amount: spotOpenOrders[i].Size,
- Pair: req.Pairs[x],
- Exchange: o.Name,
- Side: side,
- Type: orderType,
- ExecutedAmount: spotOpenOrders[i].FilledSize,
- Date: spotOpenOrders[i].Timestamp,
- Status: status,
- })
- }
- }
- return req.Filter(o.Name, resp), nil
-}
-
-// GetOrderHistory retrieves account order information
-// Can Limit response to specific order status
-func (o *OKGroup) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest) (order.FilteredOrders, error) {
- err := req.Validate()
- if err != nil {
- return nil, err
- }
-
- var resp []order.Detail
- for x := range req.Pairs {
- var fPair currency.Pair
- fPair, err = o.FormatExchangeCurrency(req.Pairs[x], asset.Spot)
- if err != nil {
- return nil, err
- }
- var spotOrders []GetSpotOrderResponse
- spotOrders, err = o.GetSpotOrders(ctx,
- GetSpotOrdersRequest{
- Status: strings.Join([]string{"filled", "cancelled", "failure"}, "|"),
- InstrumentID: fPair.String(),
- })
- if err != nil {
- return nil, err
- }
- for i := range spotOrders {
- var status order.Status
- status, err = order.StringToOrderStatus(spotOrders[i].Status)
- if err != nil {
- log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
- }
- var side order.Side
- side, err = order.StringToOrderSide(spotOrders[i].Side)
- if err != nil {
- log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
- }
- var orderType order.Type
- orderType, err = order.StringToOrderType(spotOrders[i].Type)
- if err != nil {
- log.Errorf(log.ExchangeSys, "%s %v", o.Name, err)
- }
- detail := order.Detail{
- OrderID: spotOrders[i].OrderID,
- Price: spotOrders[i].Price,
- AverageExecutedPrice: spotOrders[i].PriceAvg,
- Amount: spotOrders[i].Size,
- ExecutedAmount: spotOrders[i].FilledSize,
- RemainingAmount: spotOrders[i].Size - spotOrders[i].FilledSize,
- Pair: req.Pairs[x],
- Exchange: o.Name,
- Side: side,
- Type: orderType,
- Date: spotOrders[i].Timestamp,
- Status: status,
- }
- detail.InferCostsAndTimes()
- resp = append(resp, detail)
- }
- }
- return req.Filter(o.Name, resp), nil
-}
-
-// GetFeeByType returns an estimate of fee based on type of transaction
-func (o *OKGroup) GetFeeByType(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) {
- if feeBuilder == nil {
- return 0, fmt.Errorf("%T %w", feeBuilder, common.ErrNilPointer)
- }
- if !o.AreCredentialsValid(ctx) && // Todo check connection status
- feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
- feeBuilder.FeeType = exchange.OfflineTradeFee
- }
- return o.GetFee(ctx, feeBuilder)
-}
-
-// GetWithdrawCapabilities returns the types of withdrawal methods permitted by the exchange
-func (o *OKGroup) GetWithdrawCapabilities() uint32 {
- return o.GetWithdrawPermissions()
-}
-
-// AuthenticateWebsocket sends an authentication message to the websocket
-func (o *OKGroup) AuthenticateWebsocket(ctx context.Context) error {
- return o.WsLogin(ctx)
-}
-
-// ValidateCredentials validates current credentials used for wrapper
-// functionality
-func (o *OKGroup) ValidateCredentials(ctx context.Context, assetType asset.Item) error {
- _, err := o.UpdateAccountInfo(ctx, assetType)
- return o.CheckTransientError(err)
-}
-
-// GetHistoricTrades returns historic trade data within the timeframe provided
-func (o *OKGroup) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.Item, _, _ time.Time) ([]trade.Data, error) {
- return nil, common.ErrFunctionNotSupported
-}
-
-// GetHistoricCandles returns candles between a time period for a set time interval
-func (o *OKGroup) GetHistoricCandles(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
- if err := o.ValidateKline(pair, a, interval); err != nil {
- return kline.Item{}, err
- }
-
- formattedPair, err := o.FormatExchangeCurrency(pair, a)
- if err != nil {
- return kline.Item{}, err
- }
-
- req := &GetMarketDataRequest{
- Asset: a,
- Start: start.UTC().Format(time.RFC3339),
- End: end.UTC().Format(time.RFC3339),
- Granularity: o.FormatExchangeKlineInterval(interval),
- InstrumentID: formattedPair.String(),
- }
-
- candles, err := o.GetMarketData(ctx, req)
- if err != nil {
- return kline.Item{}, err
- }
-
- ret := kline.Item{
- Exchange: o.Name,
- Pair: pair,
- Asset: a,
- Interval: interval,
- }
-
- for x := range candles {
- t, ok := candles[x].([]interface{})
- if !ok {
- return kline.Item{}, errors.New("unable to type asset candle data")
- }
- if len(t) < 6 {
- return kline.Item{}, errors.New("incorrect candles data length")
- }
- v, ok := t[0].(string)
- if !ok {
- return kline.Item{}, errors.New("unable to type asset time data")
- }
- var tempCandle kline.Candle
- if tempCandle.Time, err = time.Parse(time.RFC3339, v); err != nil {
- return kline.Item{}, err
- }
- if tempCandle.Open, err = convert.FloatFromString(t[1]); err != nil {
- return kline.Item{}, err
- }
- if tempCandle.High, err = convert.FloatFromString(t[2]); err != nil {
- return kline.Item{}, err
- }
- if tempCandle.Low, err = convert.FloatFromString(t[3]); err != nil {
- return kline.Item{}, err
- }
- if tempCandle.Close, err = convert.FloatFromString(t[4]); err != nil {
- return kline.Item{}, err
- }
- if tempCandle.Volume, err = convert.FloatFromString(t[5]); err != nil {
- return kline.Item{}, err
- }
- ret.Candles = append(ret.Candles, tempCandle)
- }
-
- ret.SortCandlesByTimestamp(false)
- return ret, nil
-}
-
-// GetHistoricCandlesExtended returns candles between a time period for a set time interval
-func (o *OKGroup) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
- if err := o.ValidateKline(pair, a, interval); err != nil {
- return kline.Item{}, err
- }
-
- ret := kline.Item{
- Exchange: o.Name,
- Pair: pair,
- Asset: a,
- Interval: interval,
- }
-
- dates, err := kline.CalculateCandleDateRanges(start, end, interval, o.Features.Enabled.Kline.ResultLimit)
- if err != nil {
- return kline.Item{}, err
- }
- formattedPair, err := o.FormatExchangeCurrency(pair, a)
- if err != nil {
- return kline.Item{}, err
- }
-
- for x := range dates.Ranges {
- req := &GetMarketDataRequest{
- Asset: a,
- Start: dates.Ranges[x].Start.Time.UTC().Format(time.RFC3339),
- End: dates.Ranges[x].End.Time.UTC().Format(time.RFC3339),
- Granularity: o.FormatExchangeKlineInterval(interval),
- InstrumentID: formattedPair.String(),
- }
-
- var candles GetMarketDataResponse
- candles, err = o.GetMarketData(ctx, req)
- if err != nil {
- return kline.Item{}, err
- }
-
- for i := range candles {
- t, ok := candles[i].([]interface{})
- if !ok {
- return kline.Item{}, errors.New("unable to type assert candles data")
- }
- if len(t) < 6 {
- return kline.Item{}, errors.New("candle data length invalid")
- }
- v, ok := t[0].(string)
- if !ok {
- return kline.Item{}, errors.New("unable to type assert time value")
- }
- var tempCandle kline.Candle
- if tempCandle.Time, err = time.Parse(time.RFC3339, v); err != nil {
- return kline.Item{}, err
- }
- if tempCandle.Open, err = convert.FloatFromString(t[1]); err != nil {
- return kline.Item{}, err
- }
- if tempCandle.High, err = convert.FloatFromString(t[2]); err != nil {
- return kline.Item{}, err
- }
-
- if tempCandle.Low, err = convert.FloatFromString(t[3]); err != nil {
- return kline.Item{}, err
- }
-
- if tempCandle.Close, err = convert.FloatFromString(t[4]); err != nil {
- return kline.Item{}, err
- }
-
- if tempCandle.Volume, err = convert.FloatFromString(t[5]); err != nil {
- return kline.Item{}, err
- }
- ret.Candles = append(ret.Candles, tempCandle)
- }
- }
-
- dates.SetHasDataFromCandles(ret.Candles)
- summary := dates.DataSummary(false)
- if len(summary) > 0 {
- log.Warnf(log.ExchangeSys, "%v - %v", o.Base.Name, summary)
- }
- ret.RemoveDuplicates()
- ret.RemoveOutsideRange(start, end)
- ret.SortCandlesByTimestamp(false)
- return ret, nil
-}