Exchanges: Remove ANX from codebase (#408)

* Remove ANX from codebase

* Doc changes
This commit is contained in:
Adrian Gallagher
2020-01-03 14:12:52 +11:00
committed by GitHub
parent f8ef6da93d
commit 4e05ad41e3
31 changed files with 42 additions and 4585 deletions

View File

@@ -1,143 +0,0 @@
# GoCryptoTrader package Anx
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
[![Build Status](https://travis-ci.org/thrasher-corp/gocryptotrader.svg?branch=master)](https://travis-ci.org/thrasher-corp/gocryptotrader)
[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/anx)
[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
This anx package is part of the GoCryptoTrader codebase.
## This is still in active development
You can track ideas, planned features and what's in progresss 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://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
## ANX Exchange
### Current Features
+ REST functions
### Features not yet included
+ Long polling streaming
### 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 a exchange.IBotExchange
for i := range bot.Exchanges {
if bot.Exchanges[i].GetName() == "ANX" {
a = bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := a.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := a.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 := a.GetAccountInfo()
if err != nil {
// Handle error
}
```
+ If enabled via individually importing package, rudimentary example below:
```go
// Public calls
// Fetches current ticker information
ticker, err := a.GetTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := a.GetOrderBook()
if err != nil {
// Handle error
}
// Private calls - make sure your APIKEY and APISECRET are set and
// AuthenticatedAPISupport is set to true
// GetUserInfo returns account info
accountInfo, err := a.GetUserInfo(...)
if err != nil {
// Handle error
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := a.Trade(...)
if err != nil {
// Handle error
}
```
### How to do LongPolling public/private calls
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### 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
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***

View File

@@ -1,474 +0,0 @@
package anx
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
const (
anxAPIURL = "https://anxpro.com/"
anxAPIVersion = "3"
anxAPIKey = "apiKey"
anxCurrencies = "currencyStatic"
anxDataToken = "dataToken"
anxOrderNew = "order/new"
anxOrderCancel = "order/cancel"
anxOrderList = "order/list"
anxOrderInfo = "order/info"
anxSend = "send"
anxSubaccountNew = "subaccount/new"
anxReceieveAddress = "receive"
anxCreateAddress = "receive/create"
anxTicker = "money/ticker"
anxDepth = "money/depth/full"
anxAccount = "account"
// ANX rate limites for authenticated and unauthenticated requests
anxAuthRate = 0
anxUnauthRate = 0
)
// ANX is the overarching type across the alphapoint package
type ANX struct {
exchange.Base
}
// GetCurrencies returns a list of supported currencies (both fiat
// and cryptocurrencies)
func (a *ANX) GetCurrencies() (CurrenciesStore, error) {
var result CurrenciesStaticResponse
path := fmt.Sprintf("%sapi/3/%s", a.API.Endpoints.URL, anxCurrencies)
err := a.SendHTTPRequest(path, &result)
if err != nil {
return CurrenciesStore{}, err
}
return result.CurrenciesResponse, nil
}
// GetTicker returns the current ticker
func (a *ANX) GetTicker(currency string) (Ticker, error) {
var t Ticker
path := fmt.Sprintf("%sapi/2/%s/%s", a.API.Endpoints.URL, currency, anxTicker)
return t, a.SendHTTPRequest(path, &t)
}
// GetDepth returns current orderbook depth.
func (a *ANX) GetDepth(currency string) (Depth, error) {
var depth Depth
path := fmt.Sprintf("%sapi/2/%s/%s", a.API.Endpoints.URL, currency, anxDepth)
return depth, a.SendHTTPRequest(path, &depth)
}
// GetAPIKey returns a new generated API key set.
func (a *ANX) GetAPIKey(username, password, otp, deviceID string) (apiKey, apiSecret string, err error) {
req := make(map[string]interface{})
req["nonce"] = strconv.FormatInt(time.Now().UnixNano(), 10)[0:13]
req["username"] = username
req["password"] = password
if otp != "" {
req["otp"] = otp
}
req["deviceId"] = deviceID
type APIKeyResponse struct {
APIKey string `json:"apiKey"`
APISecret string `json:"apiSecret"`
ResultCode string `json:"resultCode"`
Timestamp int64 `json:"timestamp"`
}
var response APIKeyResponse
err = a.SendAuthenticatedHTTPRequest(anxAPIKey, req, &response)
if err != nil {
return apiKey, apiSecret, err
}
if response.ResultCode != "OK" {
return apiKey, apiSecret, errors.New("Response code is not OK: " + response.ResultCode)
}
apiKey = response.APIKey
apiSecret = response.APISecret
return apiKey, apiSecret, err
}
// GetDataToken returns token data
func (a *ANX) GetDataToken() (string, error) {
req := make(map[string]interface{})
type DataTokenResponse struct {
ResultCode string `json:"resultCode"`
Timestamp int64 `json:"timestamp"`
Token string `json:"token"`
UUID string `json:"uuid"`
}
var response DataTokenResponse
err := a.SendAuthenticatedHTTPRequest(anxDataToken, req, &response)
if err != nil {
return "", err
}
if response.ResultCode != "OK" {
return "", errors.New("Response code is not OK: %s" + response.ResultCode)
}
return response.Token, nil
}
// NewOrder sends a new order request to the exchange.
func (a *ANX) NewOrder(orderType string, buy bool, tradedCurrency string, tradedCurrencyAmount float64, settlementCurrency string, settlementCurrencyAmount, limitPriceSettlement float64,
replace bool, replaceUUID string, replaceIfActive bool) (string, error) {
req := make(map[string]interface{})
var order Order
order.OrderType = orderType
order.BuyTradedCurrency = buy
if buy {
order.TradedCurrencyAmount = tradedCurrencyAmount
} else {
order.SettlementCurrencyAmount = settlementCurrencyAmount
}
order.TradedCurrency = tradedCurrency
order.SettlementCurrency = settlementCurrency
order.LimitPriceInSettlementCurrency = limitPriceSettlement
if replace {
order.ReplaceExistingOrderUUID = replaceUUID
order.ReplaceOnlyIfActive = replaceIfActive
}
req["order"] = order
type OrderResponse struct {
OrderID string `json:"orderId"`
Timestamp int64 `json:"timestamp,string"`
ResultCode string `json:"resultCode"`
}
var response OrderResponse
err := a.SendAuthenticatedHTTPRequest(anxOrderNew, req, &response)
if err != nil {
return "", err
}
if response.ResultCode != "OK" {
return "", errors.New("Response code is not OK: " + response.ResultCode)
}
return response.OrderID, nil
}
// CancelOrderByIDs cancels orders, requires already knowing order IDs
// There is no existing API call to retrieve orderIds
func (a *ANX) CancelOrderByIDs(orderIds []string) (OrderCancelResponse, error) {
var response OrderCancelResponse
if len(orderIds) == 0 {
return response, errors.New("no order ids provided")
}
req := make(map[string]interface{})
req["orderIds"] = orderIds
err := a.SendAuthenticatedHTTPRequest(anxOrderCancel, req, &response)
if response.ResultCode != "OK" {
return response, errors.New(response.ResultCode)
}
return response, err
}
// GetOrderList retrieves orders from the exchange
func (a *ANX) GetOrderList(isActiveOrdersOnly bool) ([]OrderResponse, error) {
req := make(map[string]interface{})
req["activeOnly"] = isActiveOrdersOnly
type OrderListResponse struct {
Timestamp int64 `json:"timestamp,string"`
ResultCode string `json:"resultCode"`
Count int64 `json:"count"`
OrderResponses []OrderResponse `json:"orders"`
}
var response OrderListResponse
err := a.SendAuthenticatedHTTPRequest(anxOrderList, req, &response)
if err != nil {
return nil, err
}
if response.ResultCode != "OK" {
return nil, errors.New(response.ResultCode)
}
return response.OrderResponses, err
}
// OrderInfo returns information about a specific order
func (a *ANX) OrderInfo(orderID string) (OrderResponse, error) {
req := make(map[string]interface{})
req["orderId"] = orderID
type OrderInfoResponse struct {
Order OrderResponse `json:"order"`
ResultCode string `json:"resultCode"`
Timestamp int64 `json:"timestamp"`
}
var response OrderInfoResponse
err := a.SendAuthenticatedHTTPRequest(anxOrderInfo, req, &response)
if err != nil {
return OrderResponse{}, err
}
if response.ResultCode != "OK" {
return OrderResponse{}, errors.New(response.ResultCode)
}
return response.Order, nil
}
// Send withdraws a currency to an address
func (a *ANX) Send(currency, address, otp, amount string) (string, error) {
req := make(map[string]interface{})
req["ccy"] = currency
req["amount"] = amount
req["address"] = address
if otp != "" {
req["otp"] = otp
}
type SendResponse struct {
TransactionID string `json:"transactionId"`
ResultCode string `json:"resultCode"`
Timestamp int64 `json:"timestamp,string"`
}
var response SendResponse
err := a.SendAuthenticatedHTTPRequest(anxSend, req, &response)
if err != nil {
return "", err
}
if response.ResultCode != "OK" {
return "", errors.New(response.ResultCode)
}
return response.TransactionID, nil
}
// CreateNewSubAccount generates a new sub account
func (a *ANX) CreateNewSubAccount(currency, name string) (string, error) {
req := make(map[string]interface{})
req["ccy"] = currency
req["customRef"] = name
type SubaccountResponse struct {
SubAccount string `json:"subAccount"`
ResultCode string `json:"resultCode"`
Timestamp int64 `json:"timestamp"`
}
var response SubaccountResponse
err := a.SendAuthenticatedHTTPRequest(anxSubaccountNew, req, &response)
if err != nil {
return "", err
}
if response.ResultCode != "OK" {
return "", errors.New(response.ResultCode)
}
return response.SubAccount, nil
}
// GetDepositAddressByCurrency returns a deposit address for a specific currency
func (a *ANX) GetDepositAddressByCurrency(currency, name string, newAddr bool) (string, error) {
req := make(map[string]interface{})
req["ccy"] = currency
if name != "" {
req["subAccount"] = name
}
type AddressResponse struct {
Address string `json:"address"`
SubAccount string `json:"subAccount"`
ResultCode string `json:"resultCode"`
Timestamp int64 `json:"timestamp,string"`
}
var response AddressResponse
path := anxReceieveAddress
if newAddr {
path = anxCreateAddress
}
err := a.SendAuthenticatedHTTPRequest(path, req, &response)
if err != nil {
return "", err
}
if response.ResultCode != "OK" {
return "", errors.New(response.ResultCode)
}
return response.Address, nil
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (a *ANX) SendHTTPRequest(path string, result interface{}) error {
return a.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
a.Verbose,
a.HTTPDebugging,
a.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends a authenticated HTTP request
func (a *ANX) SendAuthenticatedHTTPRequest(path string, params map[string]interface{}, result interface{}) error {
if !a.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, a.Name)
}
n := a.Requester.GetNonce(true)
req := make(map[string]interface{})
req["nonce"] = n.String()[0:13]
path = fmt.Sprintf("api/%s/%s", anxAPIVersion, path)
for key, value := range params {
req[key] = value
}
PayloadJSON, err := json.Marshal(req)
if err != nil {
return errors.New("unable to JSON request")
}
if a.Verbose {
log.Debugf(log.ExchangeSys, "Request JSON: %s\n", PayloadJSON)
}
hmac := crypto.GetHMAC(crypto.HashSHA512, []byte(path+string("\x00")+string(PayloadJSON)), []byte(a.API.Credentials.Secret))
headers := make(map[string]string)
headers["Rest-Key"] = a.API.Credentials.Key
headers["Rest-Sign"] = crypto.Base64Encode(hmac)
headers["Content-Type"] = "application/json"
return a.SendPayload(http.MethodPost,
a.API.Endpoints.URL+path,
headers,
bytes.NewBuffer(PayloadJSON),
result,
true,
true,
a.Verbose,
a.HTTPDebugging,
a.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction
func (a *ANX) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
var fee float64
switch feeBuilder.FeeType {
case exchange.CryptocurrencyTradeFee:
fee = a.calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker)
case exchange.CryptocurrencyWithdrawalFee:
fee = getCryptocurrencyWithdrawalFee(feeBuilder.Pair.Base)
case exchange.InternationalBankWithdrawalFee:
fee = getInternationalBankWithdrawalFee(feeBuilder.FiatCurrency, feeBuilder.Amount)
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.002 * price * amount
}
func (a *ANX) calculateTradingFee(purchasePrice, amount float64, isMaker bool) float64 {
var fee float64
if isMaker {
fee = 0.01 * amount * purchasePrice
} else {
fee = 0.02 * amount * purchasePrice
}
return fee
}
func getCryptocurrencyWithdrawalFee(c currency.Code) float64 {
return WithdrawalFees[c]
}
func getInternationalBankWithdrawalFee(c currency.Code, amount float64) float64 {
var fee float64
if c == currency.HKD {
fee = 250 + (WithdrawalFees[c] * amount)
}
// TODO, other fiat currencies require consultation with ANXPRO
return fee
}
// GetAccountInformation retrieves details including API permissions
func (a *ANX) GetAccountInformation() (AccountInformation, error) {
var response AccountInformation
err := a.SendAuthenticatedHTTPRequest(anxAccount, nil, &response)
if err != nil {
return response, err
}
if response.ResultCode != "OK" {
log.Errorf(log.ExchangeSys, "Response code is not OK: %s\n", response.ResultCode)
return response, errors.New(response.ResultCode)
}
return response, nil
}
// CheckAPIWithdrawPermission checks if the API key is allowed to withdraw
func (a *ANX) CheckAPIWithdrawPermission() (bool, error) {
accountInfo, err := a.GetAccountInformation()
if err != nil {
return false, err
}
var apiAllowsWithdraw bool
for _, a := range accountInfo.Rights {
if a == "withdraw" {
apiAllowsWithdraw = true
}
}
if !apiAllowsWithdraw {
log.Warn(log.ExchangeSys, "API key is missing withdrawal permissions")
}
return apiAllowsWithdraw, nil
}

View File

@@ -1,38 +0,0 @@
//+build mock_test_off
// This will build if build tag mock_test_off is parsed and will do live testing
// using all tests in (exchange)_test.go
package anx
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
var mockTests = false
func TestMain(m *testing.M) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatalf("ANX Setup() load config error: %s", err)
}
anxConfig, err := cfg.GetExchangeConfig("ANX")
if err != nil {
log.Fatalf("ANX Setup() init error: %s", err)
}
anxConfig.API.AuthenticatedSupport = true
anxConfig.API.Credentials.Key = apiKey
anxConfig.API.Credentials.Secret = apiSecret
a.SetDefaults()
err = a.Setup(anxConfig)
if err != nil {
log.Fatal("ANX setup error", err)
}
log.Printf(sharedtestvalues.LiveTesting, a.Name, a.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -1,51 +0,0 @@
//+build !mock_test_off
// This will build if build tag mock_test_off is not parsed and will try to mock
// all tests in _test.go
package anx
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
const mockFile = "../../testdata/http_mock/anx/anx.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatal("ANX load config error", err)
}
anxConfig, err := cfg.GetExchangeConfig("ANX")
if err != nil {
log.Fatal("Mock server error", err)
}
a.SkipAuthCheck = true
anxConfig.API.AuthenticatedSupport = true
anxConfig.API.Credentials.Key = apiKey
anxConfig.API.Credentials.Secret = apiSecret
a.SetDefaults()
err = a.Setup(anxConfig)
if err != nil {
log.Fatal("ANX setup error", err)
}
serverDetails, newClient, err := mock.NewVCRServer(mockFile)
if err != nil {
log.Fatalf("Mock server error %s", err)
}
a.HTTPClient = newClient
a.API.Endpoints.URL = serverDetails + "/"
log.Printf(sharedtestvalues.MockTesting, a.Name, a.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -1,391 +0,0 @@
package anx
import (
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/withdraw"
)
// Please supply your own keys here for due diligence testing
const (
apiKey = ""
apiSecret = ""
canManipulateRealOrders = false
)
var a ANX
func TestGetCurrencies(t *testing.T) {
t.Parallel()
_, err := a.GetCurrencies()
if err != nil {
t.Fatalf("TestGetCurrencies failed. Err: %s", err)
}
}
func TestGetTradablePairs(t *testing.T) {
t.Parallel()
_, err := a.FetchTradablePairs(asset.Spot)
if err != nil {
t.Fatalf("TestGetTradablePairs failed. Err: %s", err)
}
}
func TestGetTicker(t *testing.T) {
t.Parallel()
ticker, err := a.GetTicker("BTCUSD")
if err != nil {
t.Errorf("ANX GetTicker() error: %s", err)
}
if ticker.Result != "success" {
t.Error("ANX GetTicker() unsuccessful")
}
}
func TestGetDepth(t *testing.T) {
t.Parallel()
depth, err := a.GetDepth("BTCUSD")
if err != nil {
t.Errorf("ANX GetDepth() error: %s", err)
}
if depth.Result != "success" {
t.Error("ANX GetDepth() unsuccessful")
}
}
func TestGetAPIKey(t *testing.T) {
t.Parallel()
apiKey, apiSecret, err := a.GetAPIKey("userName", "passWord", "", "1337")
if err == nil {
t.Error("ANX GetAPIKey() Expected error")
}
if apiKey != "" {
t.Error("ANX GetAPIKey() Expected error")
}
if apiSecret != "" {
t.Error("ANX GetAPIKey() Expected error")
}
}
func setFeeBuilder() *exchange.FeeBuilder {
return &exchange.FeeBuilder{
Amount: 1,
FeeType: exchange.CryptocurrencyTradeFee,
Pair: currency.NewPair(currency.BTC, currency.LTC),
IsMaker: false,
PurchasePrice: 1,
}
}
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
a.GetFeeByType(feeBuilder)
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
} else {
if feeBuilder.FeeType != exchange.CryptocurrencyTradeFee {
t.Errorf("Expected %v, received %v", exchange.CryptocurrencyTradeFee, feeBuilder.FeeType)
}
}
}
func TestGetFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := a.GetFee(feeBuilder); resp != float64(0.02) || err != nil {
t.Error(err)
t.Errorf("GetFee() error. Expected: %f, Received: %f", float64(0), resp)
}
// CryptocurrencyTradeFee High quantity
feeBuilder = setFeeBuilder()
feeBuilder.Amount = 1000
feeBuilder.PurchasePrice = 1000
if resp, err := a.GetFee(feeBuilder); resp != float64(20000) || err != nil {
t.Errorf("GetFee() error. Expected: %f, Received: %f", float64(20000), resp)
t.Error(err)
}
// CryptocurrencyTradeFee IsMaker
feeBuilder = setFeeBuilder()
feeBuilder.IsMaker = true
if resp, err := a.GetFee(feeBuilder); resp != float64(0.01) || err != nil {
t.Errorf("GetFee() error. Expected: %f, Received: %f", float64(0.01), resp)
t.Error(err)
}
// CryptocurrencyTradeFee Negative purchase price
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
if resp, err := a.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
// CryptocurrencyWithdrawalFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := a.GetFee(feeBuilder); resp != float64(0.002) || err != nil {
t.Errorf("GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
// CyptocurrencyDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CyptocurrencyDepositFee
if resp, err := a.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
// InternationalBankDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankDepositFee
feeBuilder.FiatCurrency = currency.HKD
if resp, err := a.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
// InternationalBankWithdrawalFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
feeBuilder.FiatCurrency = currency.HKD
if resp, err := a.GetFee(feeBuilder); resp != float64(250.01) || err != nil {
t.Errorf("GetFee() error. Expected: %f, Received: %f", float64(250.01), resp)
t.Error(err)
}
}
func TestFormatWithdrawPermissions(t *testing.T) {
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawCryptoWith2FAText + " & " +
exchange.WithdrawCryptoWithEmailText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := a.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
_, err := a.GetActiveOrders(&getOrdersRequest)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get open orders: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get open orders: %s", err)
}
}
func TestGetOrderHistory(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
_, err := a.GetOrderHistory(&getOrdersRequest)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get order history: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("GetBalance() error", err)
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
return a.ValidateAPICredentials()
}
func TestSubmitOrder(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderSubmission = &order.Submit{
Pair: currency.Pair{
Delimiter: "_",
Base: currency.BTC,
Quote: currency.USD,
},
OrderSide: order.Buy,
OrderType: order.Limit,
Price: 1,
Amount: 1,
ClientID: "meowOrder",
}
response, err := a.SubmitOrder(orderSubmission)
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) && !mockTests {
// TODO: QA Pass to submit order
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
}
func TestCancelExchangeOrder(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.BTC, currency.LTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
}
err := a.CancelOrder(orderCancellation)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel order: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not cancel order: %s", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.BTC, currency.LTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
}
resp, err := a.CancelAllOrders(orderCancellation)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel order: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Errorf("QA pass needs to be completed and mock needs to be updated error cannot be nil")
}
if len(resp.Status) > 0 {
t.Errorf("%v orders failed to cancel", len(resp.Status))
}
}
func TestGetAccountInfo(t *testing.T) {
t.Parallel()
_, err := a.GetAccountInfo()
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("GetAccountInfo() error:", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("GetAccountInfo() error")
case mockTests && err != nil:
t.Error("GetAccountInfo() error:", err)
}
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
_, err := a.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
}
}
func TestWithdraw(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
withdrawCryptoRequest := withdraw.CryptoRequest{
GenericInfo: withdraw.GenericInfo{
Amount: -1,
Currency: currency.BTC,
Description: "WITHDRAW IT ALL",
},
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AddressTag: "0123456789",
}
_, err := a.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if areTestAPIKeysSet() && err != nil && !mockTests {
t.Errorf("Withdraw failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil && mockTests {
t.Error("Expecting an error when no keys are set")
}
}
func TestWithdrawFiat(t *testing.T) {
t.Parallel()
var withdrawFiatRequest = withdraw.FiatRequest{}
_, err := a.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestWithdrawInternationalBank(t *testing.T) {
t.Parallel()
var withdrawFiatRequest = withdraw.FiatRequest{}
_, err := a.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestGetDepositAddress(t *testing.T) {
t.Parallel()
_, err := a.GetDepositAddress(currency.BTC, "")
if areTestAPIKeysSet() && err != nil && !mockTests {
t.Error("GetDepositAddress() error", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("GetDepositAddress() error cannot be nil")
}
}
func TestUpdateOrderbook(t *testing.T) {
t.Parallel()
q := currency.Pair{
Delimiter: "_",
Base: currency.BTC,
Quote: currency.USD}
_, err := a.UpdateOrderbook(q, "spot")
if err == nil {
t.Fatalf("error cannot be nil as the endpoint returns no orderbook information")
}
}

View File

@@ -1,207 +0,0 @@
package anx
import "github.com/thrasher-corp/gocryptotrader/currency"
// List of strings
const (
CancelOrderNotFound string = "ORDER_NOT_FOUND"
CancelRequestSubmitted string = "CANCEL_REQUEST_SUBMITTED"
CancelOrderWrongState string = "ORDER_CANCEL_WRONG_STATE"
)
// CurrencyData holds the currency information
type CurrencyData struct {
Decimals int `json:"decimals"`
MinOrderSize float64 `json:"minOrderSize"`
MaxOrderSize float64 `json:"maxOrderSize"`
DisplayDenominator float64 `json:"displayDenominator"`
SummaryDecimals int `json:"summaryDecimals"`
DisplayUnit string `json:"displayUnit"`
Symbol string `json:"symbol"`
Type string `json:"type"`
ConfirmationThresholds []struct {
ConfosRequired int `json:"confosRequired"`
} `json:"confirmationThresholds"`
NetworkFee float64 `json:"networkFee"`
EngineSettings struct {
DepositsEnabled bool `json:"depositsEnabled"`
WithdrawalsEnabled bool `json:"withdrawalsEnabled"`
DisplayEnabled bool `json:"displayEnabled"`
MobileAccessEnabled bool `json:"mobileAccessEnabled"`
} `json:"engineSettings"`
MinOrderValue float64 `json:"minOrderValue"`
MaxOrderValue float64 `json:"maxOrderValue"`
MaxMarketOrderValue float64 `json:"maxMarketOrderValue"`
MaxMarketOrderSize float64 `json:"maxMarketOrderSize"`
DigitalCurrencyType string `json:"digitalCurrencyType"`
AssetName string `json:"assetName"`
AssetDivisibility int `json:"assetDivisibility"`
AssetIcon string `json:"assetIcon"`
AssetIssueQuantity string `json:"assetIssueQuantity"`
}
// Currencies stores a list of currencies
type Currencies map[string]CurrencyData
// CurrencyPairData holds the currency information
type CurrencyPairData struct {
PriceDecimals int `json:"priceDecimals"`
EngineSettings struct {
TradingEnabled bool `json:"tradingEnabled"`
DisplayEnabled bool `json:"displayEnabled"`
CancelOnly bool `json:"cancelOnly"`
VerifyRequired bool `json:"verifyRequired"`
RestrictedBuy bool `json:"restrictedBuy"`
RestrictedSell bool `json:"restrictedSell"`
} `json:"engineSettings"`
MinOrderRate float64 `json:"minOrderRate"`
MaxOrderRate float64 `json:"maxOrderRate"`
DisplayPriceDecimals int `json:"displayPriceDecimals"`
TradedCcy string `json:"tradedCcy"`
SettlementCcy string `json:"settlementCcy"`
PreferredMarket string `json:"preferredMarket"`
ChartEnabled bool `json:"chartEnabled"`
SimpleTradeEnabled bool `json:"simpleTradeEnabled"`
}
// AccountInformation Used by Get account information
// Retrieves details of the account and api's
type AccountInformation struct {
UserUUID string `json:"userUuid"`
Rights []string `json:"Rights"`
ResultCode string `json:"resultCode"`
Wallets map[string]struct {
Balance Amount `json:"Balance"`
AvailableBalance Amount `json:"Available_Balance"`
DailyWithdrawalLimit Amount `json:"Daily_Withdrawal_Limit"`
MaxWithdraw Amount `json:"Max_Withdraw"`
} `json:"Wallets"`
}
// Amount basic storage of wallet details
type Amount struct {
DisplayShort string `json:"displayShort"`
ValueInt int64 `json:"valueInt"`
Currency string `json:"currency"`
Display string `json:"display"`
Value float64 `json:"value"`
}
// CurrencyPairs stores currency pair info
type CurrencyPairs map[string]CurrencyPairData
// CurrenciesStore stores the available cryptocurrencies
// and fiat currencies
type CurrenciesStore struct {
Currencies Currencies `json:"currencies"`
CurrencyPairs CurrencyPairs `json:"currencyPairs"`
Timestamp string `json:"timestamp"`
ResultCode string `json:"resultCode"`
}
// CurrenciesStaticResponse stores the currencyStatic response
type CurrenciesStaticResponse struct {
CurrenciesResponse CurrenciesStore `json:"CurrencyStatic"`
}
// Order holds order information
type Order struct {
OrderType string `json:"orderType"`
BuyTradedCurrency bool `json:"buyTradedCurrency"`
TradedCurrency string `json:"tradedCurrency"`
SettlementCurrency string `json:"settlementCurrency"`
TradedCurrencyAmount float64 `json:"tradedCurrencyAmount,string"`
SettlementCurrencyAmount float64 `json:"settlementCurrencyAmount,string"`
LimitPriceInSettlementCurrency float64 `json:"limitPriceInSettlementCurrency,string"`
ReplaceExistingOrderUUID string `json:"replaceExistingOrderUuid"`
ReplaceOnlyIfActive bool `json:"replaceOnlyIfActive"`
}
// OrderResponse holds order response data
type OrderResponse struct {
BuyTradedCurrency bool `json:"buyTradedCurrency"`
ExecutedAverageRate string `json:"executedAverageRate"`
LimitPriceInSettlementCurrency string `json:"limitPriceInSettlementCurrency"`
OrderID string `json:"orderId"`
OrderStatus string `json:"orderStatus"`
OrderType string `json:"orderType"`
ReplaceExistingOrderUUID string `json:"replaceExistingOrderId"`
SettlementCurrency string `json:"settlementCurrency"`
SettlementCurrencyAmount float64 `json:"settlementCurrencyAmount,string"`
SettlementCurrencyOutstanding string `json:"settlementCurrencyOutstanding"`
Timestamp int64 `json:"timestamp"`
TradedCurrency string `json:"tradedCurrency"`
TradedCurrencyAmount float64 `json:"tradedCurrencyAmount,string"`
TradedCurrencyOutstanding string `json:"tradedCurrencyOutstanding"`
}
// OrderCancelResponse returned when cancelling multiple orders
type OrderCancelResponse struct {
OrderCancellationResponses []OrderCancellationResponse `json:"orderIds"`
ResultCode string `json:"resultCode"`
UUID int64 `json:"uuid"`
ErrorCode int64 `json:"errorCode"`
}
// OrderCancellationResponse contains the orderID and error when cancelling multiple orders
type OrderCancellationResponse struct {
UUID string `json:"uuid"`
Error string `json:"errorCode"`
}
// TickerComponent is a sub-type for ticker
type TickerComponent struct {
Currency string `json:"currency"`
Display string `json:"display"`
DisplayShort string `json:"display_short"`
Value string `json:"value"`
}
// Ticker contains ticker data
type Ticker struct {
Result string `json:"result"`
Data struct {
High TickerComponent `json:"high"`
Low TickerComponent `json:"low"`
Average TickerComponent `json:"avg"`
VolumeWeightedAveragePrice TickerComponent `json:"vwap"`
Volume TickerComponent `json:"vol"`
Last TickerComponent `json:"last"`
Buy TickerComponent `json:"buy"`
Sell TickerComponent `json:"sell"`
Now int64 `json:"now,string"`
UpdateTime int64 `json:"dataUpdateTime,string"`
} `json:"data"`
}
// DepthItem contains depth information
type DepthItem struct {
Price float64 `json:"price,string"`
PriceInt float64 `json:"price_int,string"`
Amount float64 `json:"amount,string"`
AmountInt int64 `json:"amount_int,string"`
}
// Depth contains full depth information
type Depth struct {
Result string `json:"result"`
Data struct {
Now string `json:"now"`
DataUpdateTime string `json:"dataUpdateTime"`
Asks []DepthItem `json:"asks"`
Bids []DepthItem `json:"bids"`
} `json:"data"`
}
// WithdrawalFees the large list of predefined withdrawal fees
// Prone to change
var WithdrawalFees = map[currency.Code]float64{
currency.BTC: 0.002,
currency.DOGE: 0.1,
currency.ETH: 0.005,
currency.GNT: 0.001,
currency.LTC: 0.02,
currency.OAX: 0.001,
currency.XRP: 1,
currency.HKD: 0.01,
}

View File

@@ -1,545 +0,0 @@
package anx
import (
"strconv"
"strings"
"sync"
"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/asset"
"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/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
"github.com/thrasher-corp/gocryptotrader/exchanges/withdraw"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
// GetDefaultConfig returns a default exchange config for Alphapoint
func (a *ANX) GetDefaultConfig() (*config.ExchangeConfig, error) {
a.SetDefaults()
exchCfg := new(config.ExchangeConfig)
exchCfg.Name = a.Name
exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
exchCfg.BaseCurrencies = a.BaseCurrencies
err := a.SetupDefaults(exchCfg)
if err != nil {
return nil, err
}
if a.Features.Supports.RESTCapabilities.AutoPairUpdates {
err = a.UpdateTradablePairs(true)
if err != nil {
return nil, err
}
}
return exchCfg, nil
}
// SetDefaults sets current default settings
func (a *ANX) SetDefaults() {
a.Name = "ANX"
a.Enabled = true
a.Verbose = true
a.BaseCurrencies = currency.Currencies{
currency.USD,
currency.HKD,
currency.EUR,
currency.CAD,
currency.AUD,
currency.SGD,
currency.JPY,
currency.GBP,
currency.NZD,
}
a.API.CredentialsValidator.RequiresKey = true
a.API.CredentialsValidator.RequiresSecret = true
a.API.CredentialsValidator.RequiresBase64DecodeSecret = true
a.CurrencyPairs = currency.PairsManager{
AssetTypes: asset.Items{
asset.Spot,
},
UseGlobalFormat: true,
RequestFormat: &currency.PairFormat{
Uppercase: true,
},
ConfigFormat: &currency.PairFormat{
Delimiter: "_",
Uppercase: true,
},
}
a.Features = exchange.Features{
Supports: exchange.FeaturesSupported{
REST: true,
Websocket: false,
RESTCapabilities: protocol.Features{
TickerFetching: true,
OrderbookFetching: true,
AutoPairUpdates: true,
AccountInfo: true,
CryptoDeposit: true,
CryptoWithdrawal: true,
GetOrder: true,
GetOrders: true,
CancelOrders: true,
CancelOrder: true,
SubmitOrder: true,
DepositHistory: true,
WithdrawalHistory: true,
UserTradeHistory: true,
TradeFee: true,
FiatWithdrawalFee: true,
CryptoWithdrawalFee: true,
},
WithdrawPermissions: exchange.WithdrawCryptoWithEmail |
exchange.AutoWithdrawCryptoWithSetup |
exchange.WithdrawCryptoWith2FA |
exchange.WithdrawFiatViaWebsiteOnly,
},
Enabled: exchange.FeaturesEnabled{
AutoPairUpdates: false,
},
}
a.Requester = request.New(a.Name,
request.NewRateLimit(time.Second, anxAuthRate),
request.NewRateLimit(time.Second, anxUnauthRate),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
a.API.Endpoints.URLDefault = anxAPIURL
a.API.Endpoints.URL = a.API.Endpoints.URLDefault
}
// Setup is run on startup to setup exchange with config values
func (a *ANX) Setup(exch *config.ExchangeConfig) error {
if !exch.Enabled {
a.SetEnabled(false)
return nil
}
return a.SetupDefaults(exch)
}
// Start starts the ANX go routine
func (a *ANX) Start(wg *sync.WaitGroup) {
wg.Add(1)
go func() {
a.Run()
wg.Done()
}()
}
// Run implements the ANX wrapper
func (a *ANX) Run() {
if a.Verbose {
a.PrintEnabledPairs()
}
forceUpdate := false
delim := a.GetPairFormat(asset.Spot, false).Delimiter
if !common.StringDataContains(a.GetEnabledPairs(asset.Spot).Strings(), delim) ||
!common.StringDataContains(a.GetAvailablePairs(asset.Spot).Strings(), delim) {
enabledPairs := currency.NewPairsFromStrings(
[]string{currency.BTC.String() + delim + currency.USD.String()},
)
log.Warn(log.ExchangeSys,
"Enabled pairs for ANX reset due to config upgrade, please enable the ones you would like again.")
forceUpdate = true
err := a.UpdatePairs(enabledPairs, asset.Spot, true, true)
if err != nil {
log.Errorf(log.ExchangeSys, "%s failed to update currencies.\n", a.Name)
return
}
}
if !a.GetEnabledFeatures().AutoPairUpdates && !forceUpdate {
return
}
err := a.UpdateTradablePairs(forceUpdate)
if err != nil {
log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", a.Name, err)
}
}
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (a *ANX) UpdateTradablePairs(forceUpdate bool) error {
pairs, err := a.FetchTradablePairs(asset.Spot)
if err != nil {
return err
}
return a.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate)
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func (a *ANX) FetchTradablePairs(asset asset.Item) ([]string, error) {
result, err := a.GetCurrencies()
if err != nil {
return nil, err
}
var currencies []string
for x := range result.CurrencyPairs {
currencies = append(currencies, result.CurrencyPairs[x].TradedCcy+
a.GetPairFormat(asset, false).Delimiter+
result.CurrencyPairs[x].SettlementCcy)
}
return currencies, nil
}
// UpdateTicker updates and returns the ticker for a currency pair
func (a *ANX) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
tickerPrice := new(ticker.Price)
tick, err := a.GetTicker(a.FormatExchangeCurrency(p, assetType).String())
if err != nil {
return tickerPrice, err
}
last, _ := convert.FloatFromString(tick.Data.Last.Value)
high, _ := convert.FloatFromString(tick.Data.High.Value)
low, _ := convert.FloatFromString(tick.Data.Low.Value)
bid, _ := convert.FloatFromString(tick.Data.Buy.Value)
ask, _ := convert.FloatFromString(tick.Data.Sell.Value)
volume, _ := convert.FloatFromString(tick.Data.Volume.Value)
tickerPrice = &ticker.Price{
Last: last,
High: high,
Low: low,
Bid: bid,
Ask: ask,
Volume: volume,
Pair: p,
LastUpdated: time.Unix(0, tick.Data.UpdateTime),
}
err = ticker.ProcessTicker(a.Name, tickerPrice, assetType)
if err != nil {
return tickerPrice, err
}
return ticker.GetTicker(a.Name, p, assetType)
}
// FetchTicker returns the ticker for a currency pair
func (a *ANX) FetchTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
tickerNew, err := ticker.GetTicker(a.Name, p, assetType)
if err != nil {
return a.UpdateTicker(p, assetType)
}
return tickerNew, nil
}
// FetchOrderbook returns the orderbook for a currency pair
func (a *ANX) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
ob, err := orderbook.Get(a.Name, p, assetType)
if err != nil {
return a.UpdateOrderbook(p, assetType)
}
return ob, nil
}
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (a *ANX) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
orderBook := new(orderbook.Base)
orderbookNew, err := a.GetDepth(a.FormatExchangeCurrency(p, assetType).String())
if err != nil {
return orderBook, err
}
for x := range orderbookNew.Data.Asks {
orderBook.Asks = append(orderBook.Asks,
orderbook.Item{
Price: orderbookNew.Data.Asks[x].Price,
Amount: orderbookNew.Data.Asks[x].Amount})
}
for x := range orderbookNew.Data.Bids {
orderBook.Bids = append(orderBook.Bids,
orderbook.Item{
Price: orderbookNew.Data.Bids[x].Price,
Amount: orderbookNew.Data.Bids[x].Amount})
}
orderBook.Pair = p
orderBook.ExchangeName = a.Name
orderBook.AssetType = assetType
err = orderBook.Process()
if err != nil {
return orderBook, err
}
return orderbook.Get(a.Name, p, assetType)
}
// GetAccountInfo retrieves balances for all enabled currencies on the
// exchange
func (a *ANX) GetAccountInfo() (exchange.AccountInfo, error) {
var info exchange.AccountInfo
raw, err := a.GetAccountInformation()
if err != nil {
return info, err
}
var balance []exchange.AccountCurrencyInfo
for c := range raw.Wallets {
balance = append(balance, exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(c),
TotalValue: raw.Wallets[c].AvailableBalance.Value,
Hold: raw.Wallets[c].Balance.Value,
})
}
info.Exchange = a.Name
info.Accounts = append(info.Accounts, exchange.Account{
Currencies: balance,
})
return info, nil
}
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (a *ANX) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.
func (a *ANX) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
// SubmitOrder submits a new order
func (a *ANX) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
var submitOrderResponse order.SubmitResponse
if err := s.Validate(); err != nil {
return submitOrderResponse, err
}
var isBuying bool
var limitPriceInSettlementCurrency float64
if s.OrderSide == order.Buy {
isBuying = true
}
if s.OrderType == order.Limit {
limitPriceInSettlementCurrency = s.Price
}
response, err := a.NewOrder(s.OrderType.String(),
isBuying,
s.Pair.Base.String(),
s.Amount,
s.Pair.Quote.String(),
s.Amount,
limitPriceInSettlementCurrency,
false,
"",
false)
if err != nil {
return submitOrderResponse, err
}
if response != "" {
submitOrderResponse.OrderID = response
}
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (a *ANX) ModifyOrder(action *order.Modify) (string, error) {
return "", common.ErrFunctionNotSupported
}
// CancelOrder cancels an order by its corresponding ID number
func (a *ANX) CancelOrder(order *order.Cancel) error {
orderIDs := []string{order.OrderID}
_, err := a.CancelOrderByIDs(orderIDs)
return err
}
// CancelAllOrders cancels all orders associated with a currency pair
func (a *ANX) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{
Status: make(map[string]string),
}
placedOrders, err := a.GetOrderList(true)
if err != nil {
return cancelAllOrdersResponse, err
}
var orderIDs []string
for i := range placedOrders {
orderIDs = append(orderIDs, placedOrders[i].OrderID)
}
resp, err := a.CancelOrderByIDs(orderIDs)
if err != nil {
return cancelAllOrdersResponse, err
}
for i := range resp.OrderCancellationResponses {
if resp.OrderCancellationResponses[i].Error != CancelRequestSubmitted {
cancelAllOrdersResponse.Status[resp.OrderCancellationResponses[i].UUID] = resp.OrderCancellationResponses[i].Error
}
}
return cancelAllOrdersResponse, err
}
// GetOrderInfo returns information on a current open order
func (a *ANX) GetOrderInfo(orderID string) (order.Detail, error) {
var orderDetail order.Detail
return orderDetail, common.ErrNotYetImplemented
}
// GetDepositAddress returns a deposit address for a specified currency
func (a *ANX) GetDepositAddress(cryptocurrency currency.Code, _ string) (string, error) {
return a.GetDepositAddressByCurrency(cryptocurrency.String(), "", false)
}
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
// submitted
func (a *ANX) WithdrawCryptocurrencyFunds(withdrawRequest *withdraw.CryptoRequest) (string, error) {
return a.Send(withdrawRequest.Currency.String(), withdrawRequest.Address, "", strconv.FormatFloat(withdrawRequest.Amount, 'f', -1, 64))
}
// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is
// submitted
func (a *ANX) WithdrawFiatFunds(withdrawRequest *withdraw.FiatRequest) (string, error) {
// Fiat withdrawals available via website
return "", common.ErrFunctionNotSupported
}
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a withdrawal is
// submitted
func (a *ANX) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw.FiatRequest) (string, error) {
// Fiat withdrawals available via website
return "", common.ErrFunctionNotSupported
}
// GetWebsocket returns a pointer to the exchange websocket
func (a *ANX) GetWebsocket() (*wshandler.Websocket, error) {
return nil, common.ErrFunctionNotSupported
}
// GetFeeByType returns an estimate of fee based on type of transaction
func (a *ANX) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
if (!a.AllowAuthenticatedRequest() || a.SkipAuthCheck) && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}
return a.GetFee(feeBuilder)
}
// GetActiveOrders retrieves any orders that are active/open
func (a *ANX) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
resp, err := a.GetOrderList(true)
if err != nil {
return nil, err
}
var orders []order.Detail
for i := range resp {
orderDate := time.Unix(resp[i].Timestamp, 0)
orderType := order.Type(strings.ToUpper(resp[i].OrderType))
orderDetail := order.Detail{
Amount: resp[i].TradedCurrencyAmount,
CurrencyPair: currency.NewPairWithDelimiter(resp[i].TradedCurrency,
resp[i].SettlementCurrency,
a.GetPairFormat(asset.Spot, false).Delimiter),
OrderDate: orderDate,
Exchange: a.Name,
ID: resp[i].OrderID,
OrderType: orderType,
Price: resp[i].SettlementCurrencyAmount,
Status: order.Status(resp[i].OrderStatus),
}
orders = append(orders, orderDetail)
}
order.FilterOrdersByType(&orders, getOrdersRequest.OrderType)
order.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks,
getOrdersRequest.EndTicks)
order.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies)
return orders, nil
}
// GetOrderHistory retrieves account order information
// Can Limit response to specific order status
func (a *ANX) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error) {
resp, err := a.GetOrderList(false)
if err != nil {
return nil, err
}
var orders []order.Detail
for i := range resp {
orderDate := time.Unix(resp[i].Timestamp, 0)
orderType := order.Type(strings.ToUpper(resp[i].OrderType))
orderDetail := order.Detail{
Amount: resp[i].TradedCurrencyAmount,
OrderDate: orderDate,
Exchange: a.Name,
ID: resp[i].OrderID,
OrderType: orderType,
Price: resp[i].SettlementCurrencyAmount,
Status: order.Status(resp[i].OrderStatus),
CurrencyPair: currency.NewPairWithDelimiter(resp[i].TradedCurrency,
resp[i].SettlementCurrency,
a.GetPairFormat(asset.Spot, false).Delimiter),
}
orders = append(orders, orderDetail)
}
order.FilterOrdersByType(&orders, req.OrderType)
order.FilterOrdersByTickRange(&orders, req.StartTicks, req.EndTicks)
order.FilterOrdersByCurrencies(&orders, req.Currencies)
return orders, nil
}
// SubscribeToWebsocketChannels appends to ChannelsToSubscribe
// which lets websocket.manageSubscriptions handle subscribing
func (a *ANX) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
return common.ErrFunctionNotSupported
}
// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe
// which lets websocket.manageSubscriptions handle unsubscribing
func (a *ANX) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
return common.ErrFunctionNotSupported
}
// GetSubscriptions returns a copied list of subscriptions
func (a *ANX) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) {
return nil, common.ErrFunctionNotSupported
}
// AuthenticateWebsocket sends an authentication message to the websocket
func (a *ANX) AuthenticateWebsocket() error {
return common.ErrFunctionNotSupported
}

View File

@@ -16,7 +16,7 @@ import (
)
const (
defaultTestExchange = "ANX"
defaultTestExchange = "Bitfinex"
defaultTestCurrencyPair = "BTC-USD"
)
@@ -1175,13 +1175,13 @@ func TestUpdatePairs(t *testing.T) {
t.Fatal("TestUpdatePairs failed to load config")
}
anxCfg, err := cfg.GetExchangeConfig(defaultTestExchange)
exchCfg, err := cfg.GetExchangeConfig(defaultTestExchange)
if err != nil {
t.Fatal("TestUpdatePairs failed to load config")
}
UAC := Base{Name: defaultTestExchange}
UAC.Config = anxCfg
UAC.Config = exchCfg
exchangeProducts := currency.NewPairsFromStrings([]string{"ltc", "btc", "usd", "aud", ""})
err = UAC.UpdatePairs(exchangeProducts, asset.Spot, true, false)
if err != nil {

View File

@@ -5,7 +5,7 @@ import (
)
func TestNewOrder(t *testing.T) {
ID := NewOrder("ANX", 2000, 20.00)
ID := NewOrder("OKEX", 2000, 20.00)
if ID != 0 {
t.Error("Orders_test.go NewOrder() - Error")
}
@@ -25,7 +25,7 @@ func TestDeleteOrder(t *testing.T) {
}
func TestGetOrdersByExchange(t *testing.T) {
if value := GetOrdersByExchange("ANX"); len(value) != 0 {
if value := GetOrdersByExchange("OKEX"); len(value) != 0 {
t.Error("Orders_test.go GetOrdersByExchange() - Error")
}
}

View File

@@ -7,11 +7,15 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
)
const (
testExchange = "OKEX"
)
func TestLenByPrice(t *testing.T) {
p := currency.NewPairFromStrings("BTC", "USD")
Items = []Item{
{
Exchange: "ANX",
Exchange: testExchange,
Pair: p,
AssetType: asset.Spot,
Price: 1200,
@@ -104,7 +108,7 @@ func TestSwapByVolume(t *testing.T) {
func TestAdd(t *testing.T) {
Items = Items[:0]
p := currency.NewPairFromStrings("BTC", "USD")
Add("ANX", p, asset.Spot, 1200, 42)
Add(testExchange, p, asset.Spot, 1200, 42)
if len(Items) < 1 {
t.Error("stats Add did not add exchange info.")
@@ -117,14 +121,14 @@ func TestAdd(t *testing.T) {
}
p.Base = currency.XBT
Add("ANX", p, asset.Spot, 1201, 43)
Add(testExchange, p, asset.Spot, 1201, 43)
if Items[1].Pair.String() != "XBTUSD" {
t.Fatal("stats Add did not add exchange info.")
}
p = currency.NewPairFromStrings("ETH", "USDT")
Add("ANX", p, asset.Spot, 300, 1000)
Add(testExchange, p, asset.Spot, 300, 1000)
if Items[2].Pair.String() != "ETHUSD" {
t.Fatal("stats Add did not add exchange info.")
@@ -146,7 +150,7 @@ func TestAppend(t *testing.T) {
func TestAlreadyExists(t *testing.T) {
p := currency.NewPairFromStrings("BTC", "USD")
if !AlreadyExists("ANX", p, asset.Spot, 1200, 42) {
if !AlreadyExists(testExchange, p, asset.Spot, 1200, 42) {
t.Error("stats AlreadyExists exchange does not exist.")
}
p.Base = currency.NewCode("dii")
@@ -163,7 +167,7 @@ func TestSortExchangesByVolume(t *testing.T) {
}
topVolume = SortExchangesByVolume(p, asset.Spot, false)
if topVolume[0].Exchange != "ANX" {
if topVolume[0].Exchange != testExchange {
t.Error("stats SortExchangesByVolume incorrectly sorted values.")
}
}
@@ -176,7 +180,7 @@ func TestSortExchangesByPrice(t *testing.T) {
}
topPrice = SortExchangesByPrice(p, asset.Spot, false)
if topPrice[0].Exchange != "ANX" {
if topPrice[0].Exchange != testExchange {
t.Error("stats SortExchangesByPrice incorrectly sorted values.")
}
}

View File

@@ -14,7 +14,6 @@ func IsSupported(exchangeName string) bool {
// Exchanges stores a list of supported exchanges
var Exchanges = []string{
"anx",
"binance",
"bitfinex",
"bitflyer",