From 65ded172063006c67cc2d3205c8768deb5be01a2 Mon Sep 17 00:00:00 2001 From: askew Date: Fri, 2 Feb 2018 22:35:44 +0200 Subject: [PATCH] Added support for Yobit.net --- config_example.json | 24 +++ exchange.go | 3 + exchanges/yobit/yobit.go | 309 +++++++++++++++++++++++++++++++ exchanges/yobit/yobit_test.go | 137 ++++++++++++++ exchanges/yobit/yobit_types.go | 139 ++++++++++++++ exchanges/yobit/yobit_wrapper.go | 120 ++++++++++++ testdata/configtest.json | 25 +++ 7 files changed, 757 insertions(+) create mode 100644 exchanges/yobit/yobit.go create mode 100644 exchanges/yobit/yobit_test.go create mode 100644 exchanges/yobit/yobit_types.go create mode 100644 exchanges/yobit/yobit_wrapper.go diff --git a/config_example.json b/config_example.json index b8ba0313..1ab2703f 100644 --- a/config_example.json +++ b/config_example.json @@ -542,6 +542,30 @@ "Delimiter": "_", "Separator": "-" } + }, + { + "Name": "Yobit", + "Enabled": true, + "Verbose": false, + "Websocket": false, + "UseSandbox": false, + "RESTPollingDelay": 10, + "AuthenticatedAPISupport": false, + "APIKey": "Key", + "APISecret": "Secret", + "AvailablePairs": "DASH_BTC,WAVES_BTC,LSK_BTC,LIZA_BTC,BCC_BTC,ETH_BTC,LTC_BTC,TRX_BTC,DOGE_BTC,VNTX_BTC,SW_BTC,ZEC_BTC,DASH_ETH,WAVES_ETH,LSK_ETH,LIZA_ETH,BCC_ETH,,LTC_ETH,TRX_ETH,DOGE_ETH,VNTX_ETH,SW_ETH,ZEC_ETH,DASH_DOGE,WAVES_DOGE,LSK_DOGE,LIZA_DOGE,BCC_DOGE,LTC_DOGE,TRX_DOGE,VNTX_DOGE,SW_DOGE,ZEC_DOGE,DASH_USD,WAVES_USD,LSK_USD,LIZA_USD,BCC_USD,LTC_USD,TRX_USD,VNTX_USD,SW_USD,ZEC_USD,ETH_USD,BTC_USD,DASH_RUR,WAVES_BTC,WAVES_RUR,LSK_RUR,LIZA_RUR,BCC_RUR,LTC_RUR,TRX_RUR,VNTX_RUR,SW_RUR,ETH_RUR,ZEC_RUR", + "EnabledPairs": "LTC_BTC,ETH_BTC,BTC_USD,DASH_BTC", + "BaseCurrencies": "USD,RUR", + "AssetTypes": "SPOT", + "ConfigCurrencyPairFormat": { + "Uppercase": true, + "Delimiter": "_" + }, + "RequestCurrencyPairFormat": { + "Uppercase": false, + "Delimiter": "_", + "Separator": "-" + } } ] } diff --git a/exchange.go b/exchange.go index 78c678d5..0944133a 100644 --- a/exchange.go +++ b/exchange.go @@ -27,6 +27,7 @@ import ( "github.com/thrasher-/gocryptotrader/exchanges/okex" "github.com/thrasher-/gocryptotrader/exchanges/poloniex" "github.com/thrasher-/gocryptotrader/exchanges/wex" + "github.com/thrasher-/gocryptotrader/exchanges/yobit" ) // vars related to exchange functions @@ -171,6 +172,8 @@ func LoadExchange(name string) error { exch = new(poloniex.Poloniex) case "wex": exch = new(wex.WEX) + case "yobit": + exch = new(yobit.Yobit) default: return ErrExchangeNotFound } diff --git a/exchanges/yobit/yobit.go b/exchanges/yobit/yobit.go new file mode 100644 index 00000000..9eb5d559 --- /dev/null +++ b/exchanges/yobit/yobit.go @@ -0,0 +1,309 @@ +package yobit + +import ( + "errors" + "fmt" + "log" + "net/url" + "strconv" + "strings" + "time" + + "github.com/thrasher-/gocryptotrader/common" + "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/exchanges" + "github.com/thrasher-/gocryptotrader/exchanges/ticker" +) + +const ( + apiPublicURL = "https://yobit.net/api" + apiPrivateURL = "https://yobit.net/tapi" + apiPublicVersion = "3" + publicInfo = "info" + publicTicker = "ticker" + publicDepth = "depth" + publicTrades = "trades" + privateAccountInfo = "getInfo" + privateTrade = "Trade" + privateActiveOrders = "ActiveOrders" + privateOrderInfo = "OrderInfo" + privateCancelOrder = "CancelOrder" + privateTradeHistory = "TradeHistory" + privateGetDepositAddress = "GetDepositAddress" + privateWithdrawCoinsToAddress = "WithdrawCoinsToAddress" + privateCreateCoupon = "CreateYobicode" + privateRedeemCoupon = "RedeemYobicode" +) + +// Yobit is the overarching type across the Yobit package +type Yobit struct { + exchange.Base + Ticker map[string]Ticker +} + +// SetDefaults sets current default value for Yobit +func (y *Yobit) SetDefaults() { + y.Name = "Yobit" + y.Enabled = true + y.Fee = 0.2 + y.Verbose = false + y.Websocket = false + y.RESTPollingDelay = 10 + y.APIUrl = apiPublicURL + y.AuthenticatedAPISupport = true + y.Ticker = make(map[string]Ticker) + y.RequestCurrencyPairFormat.Delimiter = "_" + y.RequestCurrencyPairFormat.Uppercase = false + y.RequestCurrencyPairFormat.Separator = "-" + y.ConfigCurrencyPairFormat.Delimiter = "_" + y.ConfigCurrencyPairFormat.Uppercase = true + y.AssetTypes = []string{ticker.Spot} +} + +// Setup sets exchange configuration parameters for Yobit +func (y *Yobit) Setup(exch config.ExchangeConfig) { + if !exch.Enabled { + y.SetEnabled(false) + } else { + y.Enabled = true + y.AuthenticatedAPISupport = exch.AuthenticatedAPISupport + y.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + y.RESTPollingDelay = exch.RESTPollingDelay + y.Verbose = exch.Verbose + y.Websocket = exch.Websocket + y.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",") + y.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",") + y.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",") + err := y.SetCurrencyPairFormat() + if err != nil { + log.Fatal(err) + } + err = y.SetAssetTypes() + if err != nil { + log.Fatal(err) + } + } +} + +// GetFee returns the exchange fee +func (y *Yobit) GetFee() float64 { + return y.Fee +} + +// GetInfo returns the Yobit info +func (y *Yobit) GetInfo() (Info, error) { + resp := Info{} + path := fmt.Sprintf("%s/%s/%s/", apiPublicURL, apiPublicVersion, publicInfo) + + return resp, common.SendHTTPGetRequest(path, true, y.Verbose, &resp) +} + +// GetTicker returns a ticker for a specific currency +func (y *Yobit) GetTicker(symbol string) (map[string]Ticker, error) { + type Response struct { + Data map[string]Ticker + } + + response := Response{} + path := fmt.Sprintf("%s/%s/%s/%s", apiPublicURL, apiPublicVersion, publicTicker, symbol) + + return response.Data, common.SendHTTPGetRequest(path, true, y.Verbose, &response.Data) +} + +// GetDepth returns the depth for a specific currency +func (y *Yobit) GetDepth(symbol string) (Orderbook, error) { + type Response struct { + Data map[string]Orderbook + } + + response := Response{} + path := fmt.Sprintf("%s/%s/%s/%s", apiPublicURL, apiPublicVersion, publicDepth, symbol) + + return response.Data[symbol], + common.SendHTTPGetRequest(path, true, y.Verbose, &response.Data) +} + +// GetTrades returns the trades for a specific currency +func (y *Yobit) GetTrades(symbol string) ([]Trades, error) { + type Response struct { + Data map[string][]Trades + } + + response := Response{} + path := fmt.Sprintf("%s/%s/%s/%s", apiPublicURL, apiPublicVersion, publicTrades, symbol) + + return response.Data[symbol], common.SendHTTPGetRequest(path, true, y.Verbose, &response.Data) +} + +// GetAccountInfo returns a users account info +func (y *Yobit) GetAccountInfo() (AccountInfo, error) { + result := AccountInfo{} + + return result, y.SendAuthenticatedHTTPRequest(privateAccountInfo, url.Values{}, &result) +} + +// Trade places an order and returns the order ID if successful or an error +func (y *Yobit) Trade(pair, orderType string, amount, price float64) (int64, error) { + req := url.Values{} + req.Add("pair", pair) + req.Add("type", orderType) + req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64)) + req.Add("rate", strconv.FormatFloat(price, 'f', -1, 64)) + + result := Trade{} + + return int64(result.OrderID), y.SendAuthenticatedHTTPRequest(privateTrade, req, &result) +} + +// GetActiveOrders returns the active orders for a specific currency +func (y *Yobit) GetActiveOrders(pair string) (map[string]ActiveOrders, error) { + req := url.Values{} + req.Add("pair", pair) + + result := map[string]ActiveOrders{} + + return result, y.SendAuthenticatedHTTPRequest(privateActiveOrders, req, &result) +} + +// GetOrderInfo returns the order info for a specific order ID +func (y *Yobit) GetOrderInfo(OrderID int64) (map[string]OrderInfo, error) { + req := url.Values{} + req.Add("order_id", strconv.FormatInt(OrderID, 10)) + + result := map[string]OrderInfo{} + + return result, y.SendAuthenticatedHTTPRequest(privateOrderInfo, req, &result) +} + +// CancelOrder cancels an order for a specific order ID +func (y *Yobit) CancelOrder(OrderID int64) (bool, error) { + req := url.Values{} + req.Add("order_id", strconv.FormatInt(OrderID, 10)) + + result := CancelOrder{} + err := y.SendAuthenticatedHTTPRequest(privateCancelOrder, req, &result) + + if err != nil { + return false, err + } + + return true, nil +} + +// GetTradeHistory returns the trade history +func (y *Yobit) GetTradeHistory(TIDFrom, Count, TIDEnd int64, order, since, end, pair string) (map[string]TradeHistory, error) { + req := url.Values{} + req.Add("from", strconv.FormatInt(TIDFrom, 10)) + req.Add("count", strconv.FormatInt(Count, 10)) + req.Add("from_id", strconv.FormatInt(TIDFrom, 10)) + req.Add("end_id", strconv.FormatInt(TIDEnd, 10)) + req.Add("order", order) + req.Add("since", since) + req.Add("end", end) + req.Add("pair", pair) + + result := map[string]TradeHistory{} + + return result, y.SendAuthenticatedHTTPRequest(privateTradeHistory, req, &result) +} + +// CoinDepositAddress returns the deposit address for a specific currency +func (y *Yobit) GetDepositAddress(coin string) (DepositAddress, error) { + req := url.Values{} + req.Add("coinName", coin) + + result := DepositAddress{} + + return result, y.SendAuthenticatedHTTPRequest(privateGetDepositAddress, req, &result) +} + +// CoinDepositAddress returns the deposit address for a specific currency +func (y *Yobit) WithdrawCoinsToAddress(coin string, amount float64, address string) (WithdrawCoinsToAddress, error) { + req := url.Values{} + req.Add("coinName", coin) + req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64)) + req.Add("address", address) + + result := WithdrawCoinsToAddress{} + + return result, y.SendAuthenticatedHTTPRequest(privateWithdrawCoinsToAddress, req, &result) +} + +// CreateCoupon creates an exchange coupon for a sepcific currency +func (y *Yobit) CreateCoupon(currency string, amount float64) (CreateCoupon, error) { + req := url.Values{} + req.Add("currency", currency) + req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64)) + + var result CreateCoupon + + return result, y.SendAuthenticatedHTTPRequest(privateCreateCoupon, req, &result) +} + +// RedeemCoupon redeems an exchange coupon +func (y *Yobit) RedeemCoupon(coupon string) (RedeemCoupon, error) { + req := url.Values{} + req.Add("coupon", coupon) + + result := RedeemCoupon{} + + return result, y.SendAuthenticatedHTTPRequest(privateRedeemCoupon, req, &result) +} + +// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to Yobit +func (y *Yobit) SendAuthenticatedHTTPRequest(path string, params url.Values, result interface{}) (err error) { + if !y.AuthenticatedAPISupport { + return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, y.Name) + } + + if params == nil { + params = url.Values{} + } + + if y.Nonce.Get() == 0 { + y.Nonce.Set(time.Now().Unix()) + } else { + y.Nonce.Inc() + } + params.Set("nonce", y.Nonce.String()) + params.Set("method", path) + + encoded := params.Encode() + hmac := common.GetHMAC(common.HashSHA512, []byte(encoded), []byte(y.APISecret)) + + if y.Verbose { + log.Printf("Sending POST request to %s calling path %s with params %s\n", apiPrivateURL, path, encoded) + } + + headers := make(map[string]string) + headers["Key"] = y.APIKey + headers["Sign"] = common.HexEncodeToString(hmac) + headers["Content-Type"] = "application/x-www-form-urlencoded" + + resp, err := common.SendHTTPRequest( + "POST", apiPrivateURL, headers, strings.NewReader(encoded), + ) + if err != nil { + return err + } + + if y.Verbose { + log.Printf("Received raw: \n%s\n", resp) + } + + response := Response{} + if err = common.JSONDecode([]byte(resp), &response); err != nil { + return errors.New("sendAuthenticatedHTTPRequest: Unable to JSON Unmarshal response." + err.Error()) + } + + if response.Success != 1 { + return errors.New(response.Error) + } + + JSONEncoded, err := common.JSONEncode(response.Return) + if err != nil { + return err + } + + return common.JSONDecode(JSONEncoded, &result) +} diff --git a/exchanges/yobit/yobit_test.go b/exchanges/yobit/yobit_test.go new file mode 100644 index 00000000..fbb563c3 --- /dev/null +++ b/exchanges/yobit/yobit_test.go @@ -0,0 +1,137 @@ +package yobit + +import ( + "testing" + + "github.com/thrasher-/gocryptotrader/config" +) + +var y Yobit + +// Please supply your own keys for better unit testing +const ( + apiKey = "" + apiSecret = "" +) + +func TestSetDefaults(t *testing.T) { + y.SetDefaults() +} + +func TestSetup(t *testing.T) { + yobitConfig := config.GetConfig() + yobitConfig.LoadConfig("../../testdata/configtest.json") + conf, err := yobitConfig.GetExchangeConfig("Yobit") + if err != nil { + t.Error("Test Failed - Yobit init error") + } + conf.APIKey = apiKey + conf.APISecret = apiSecret + conf.AuthenticatedAPISupport = true + + y.Setup(conf) +} + +func TestGetFee(t *testing.T) { + if y.GetFee() != 0.2 { + t.Error("Test Failed - GetFee() error") + } +} + +func TestGetInfo(t *testing.T) { + _, err := y.GetInfo() + if err != nil { + t.Error("Test Failed - GetInfo() error") + } +} + +func TestGetTicker(t *testing.T) { + _, err := y.GetTicker("btc_usd") + if err != nil { + t.Error("Test Failed - GetTicker() error", err) + } +} + +func TestGetDepth(t *testing.T) { + _, err := y.GetDepth("btc_usd") + if err != nil { + t.Error("Test Failed - GetDepth() error", err) + } +} + +func TestGetTrades(t *testing.T) { + _, err := y.GetTrades("btc_usd") + if err != nil { + t.Error("Test Failed - GetTrades() error", err) + } +} + +func TestGetAccountInfo(t *testing.T) { + _, err := y.GetAccountInfo() + if err == nil { + t.Error("Test Failed - GetAccountInfo() error", err) + } +} + +func TestGetActiveOrders(t *testing.T) { + _, err := y.GetActiveOrders("") + if err == nil { + t.Error("Test Failed - GetActiveOrders() error", err) + } +} + +func TestGetOrderInfo(t *testing.T) { + _, err := y.GetOrderInfo(6196974) + if err == nil { + t.Error("Test Failed - GetOrderInfo() error", err) + } +} + +func TestCancelOrder(t *testing.T) { + _, err := y.CancelOrder(1337) + if err == nil { + t.Error("Test Failed - CancelOrder() error", err) + } +} + +func TestTrade(t *testing.T) { + _, err := y.Trade("", "buy", 0, 0) + if err == nil { + t.Error("Test Failed - Trade() error", err) + } +} + +func TestGetTradeHistory(t *testing.T) { + _, err := y.GetTradeHistory(0, 0, 0, "", "", "", "") + if err == nil { + t.Error("Test Failed - GetTradeHistory() error", err) + } +} + +func TestWithdrawCoinsToAddress(t *testing.T) { + _, err := y.WithdrawCoinsToAddress("", 0, "") + if err == nil { + t.Error("Test Failed - WithdrawCoinsToAddress() error", err) + } +} + +func TestGetDepositAddress(t *testing.T) { + _, err := y.GetDepositAddress("btc") + if err == nil { + t.Error("Test Failed - GetDepositAddress() error", err) + } +} + +func TestCreateYobicode(t *testing.T) { + _, err := y.CreateCoupon("bla", 0) + if err == nil { + t.Error("Test Failed - CreateYobicode() error", err) + } +} + +func TestRedeemYobicode(t *testing.T) { + _, err := y.RedeemCoupon("bla") + if err == nil { + t.Error("Test Failed - RedeemYobicode() error", err) + } +} diff --git a/exchanges/yobit/yobit_types.go b/exchanges/yobit/yobit_types.go new file mode 100644 index 00000000..7f8b3c20 --- /dev/null +++ b/exchanges/yobit/yobit_types.go @@ -0,0 +1,139 @@ +package yobit + +// Response is a generic struct used for exchange API request result +type Response struct { + Return interface{} `json:"return"` + Success int `json:"success"` + Error string `json:"error"` +} + +// Info holds server time and pair information +type Info struct { + ServerTime int64 `json:"server_time"` + Pairs map[string]Pair `json:"pairs"` +} + +// Ticker stores the ticker information +type Ticker struct { + High float64 // maximal price + Low float64 // minimal price + Avg float64 // average price + Vol float64 // traded volume + VolumeCurrent float64 `json:"vol_cur"` // traded volume in currency + Last float64 // last transaction price + Buy float64 // buying price + Sell float64 // selling price + Updated int64 // last cache upgrade +} + +// Orderbook stores the asks and bids orderbook information +type Orderbook struct { + Asks [][]float64 `json:"asks"` // selling orders + Bids [][]float64 `json:"bids"` // buying orders +} + +// Trades stores trade information +type Trades struct { + Type string `json:"type"` + Price float64 `json:"bid"` + Amount float64 `json:"amount"` + TID int64 `json:"tid"` + Timestamp int64 `json:"timestamp"` +} + +// ActiveOrders stores active order information +type ActiveOrders struct { + Pair string `json:"pair"` + Type string `json:"type"` + Amount float64 `json:"amount"` + Rate float64 `json:"rate"` + TimestampCreated float64 `json:"timestamp_created"` + Status int `json:"status"` +} + +// Pair holds pair information +type Pair struct { + DecimalPlaces int `json:"decimal_places"` // Quantity of permitted numbers after decimal point + MinPrice float64 `json:"min_price"` // Minimal permitted price + MaxPrice float64 `json:"max_price"` // Maximal permitted price + MinAmount float64 `json:"min_amount"` // Minimal permitted buy or sell amount + Hidden int `json:"hidden"` // Pair is hidden (0 or 1) + Fee float64 `json:"fee"` // Pair commission +} + +// AccountInfo stores the account information for a user +type AccountInfo struct { + Funds map[string]float64 `json:"funds"` + FundsInclOrders map[string]float64 `json:"funds_incl_orders"` + Rights struct { + Info int `json:"info"` + Trade int `json:"trade"` + Withdraw int `json:"withdraw"` + } `json:"rights"` + TransactionCount int `json:"transaction_count"` + OpenOrders int `json:"open_orders"` + ServerTime float64 `json:"server_time"` +} + +// OrderInfo stores order information +type OrderInfo struct { + Pair string `json:"pair"` + Type string `json:"type"` + StartAmount float64 `json:"start_amount"` + Amount float64 `json:"amount"` + Rate float64 `json:"rate"` + TimestampCreated float64 `json:"timestamp_created"` + Status int `json:"status"` +} + +// CancelOrder is used for the CancelOrder API request response +type CancelOrder struct { + OrderID float64 `json:"order_id"` + Funds map[string]float64 `json:"funds"` +} + +// Trade stores the trade information +type Trade struct { + Received float64 `json:"received"` + Remains float64 `json:"remains"` + OrderID float64 `json:"order_id"` + Funds map[string]float64 `json:"funds"` +} + +// TradeHistory stores trade history +type TradeHistory struct { + Pair string `json:"pair"` + Type string `json:"type"` + Amount float64 `json:"amount"` + Rate float64 `json:"rate"` + OrderID float64 `json:"order_id"` + MyOrder int `json:"is_your_order"` + Timestamp float64 `json:"timestamp"` +} + +// CoinDepositAddress stores a curency deposit address +type DepositAddress struct { + Address string `json:"address"` + ProcessedAmount float64 `json:"processed_amount"` + ServerTime int64 `json:"server_time"` +} + +// WithdrawCoins stores information for a withdrawcoins request +type WithdrawCoinsToAddress struct { + ServerTime int64 `json:"server_time"` +} + +// CreateCoupon stores information coupon information +type CreateCoupon struct { + Coupon string `json:"coupon"` + TransID int64 `json:"transID"` + Funds map[string]float64 `json:"funds"` +} + +// RedeemCoupon stores redeem coupon information +type RedeemCoupon struct { + CouponAmount float64 `json:"couponAmount,string"` + CouponCurrency string `json:"couponCurrency"` + TransID int64 `json:"transID"` + Funds map[string]float64 `json:"funds"` +} diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go new file mode 100644 index 00000000..3c2949c2 --- /dev/null +++ b/exchanges/yobit/yobit_wrapper.go @@ -0,0 +1,120 @@ +package yobit + +import ( + "log" + + "github.com/thrasher-/gocryptotrader/common" + "github.com/thrasher-/gocryptotrader/currency/pair" + exchange "github.com/thrasher-/gocryptotrader/exchanges" + "github.com/thrasher-/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-/gocryptotrader/exchanges/ticker" +) + +// Start starts the WEX go routine +func (y *Yobit) Start() { + go y.Run() +} + +// Run implements the Yobit wrapper +func (y *Yobit) Run() { + if y.Verbose { + log.Printf("%s Websocket: %s.", y.GetName(), common.IsEnabled(y.Websocket)) + log.Printf("%s polling delay: %ds.\n", y.GetName(), y.RESTPollingDelay) + log.Printf("%s %d currencies enabled: %s.\n", y.GetName(), len(y.EnabledPairs), y.EnabledPairs) + } +} + +// UpdateTicker updates and returns the ticker for a currency pair +func (y *Yobit) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) { + var tickerPrice ticker.Price + pairsCollated, err := exchange.GetAndFormatExchangeCurrencies(y.Name, y.GetEnabledCurrencies()) + if err != nil { + return tickerPrice, err + } + + result, err := y.GetTicker(pairsCollated.String()) + if err != nil { + return tickerPrice, err + } + + for _, x := range y.GetEnabledCurrencies() { + currency := exchange.FormatExchangeCurrency(y.Name, x).Lower().String() + var tickerPrice ticker.Price + tickerPrice.Pair = x + tickerPrice.Last = result[currency].Last + tickerPrice.Ask = result[currency].Sell + tickerPrice.Bid = result[currency].Buy + tickerPrice.Last = result[currency].Last + tickerPrice.Low = result[currency].Low + tickerPrice.Volume = result[currency].VolumeCurrent + ticker.ProcessTicker(y.Name, x, tickerPrice, assetType) + } + return ticker.GetTicker(y.Name, p, assetType) +} + +// GetTickerPrice returns the ticker for a currency pair +func (y *Yobit) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) { + tick, err := ticker.GetTicker(y.GetName(), p, assetType) + if err != nil { + return y.UpdateTicker(p, assetType) + } + return tick, nil +} + +// GetOrderbookEx returns the orderbook for a currency pair +func (y *Yobit) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(y.GetName(), p, assetType) + if err == nil { + return y.UpdateOrderbook(p, assetType) + } + return ob, nil +} + +// UpdateOrderbook updates and returns the orderbook for a currency pair +func (y *Yobit) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base + orderbookNew, err := y.GetDepth(exchange.FormatExchangeCurrency(y.Name, p).String()) + if err != nil { + return orderBook, err + } + + for x := range orderbookNew.Bids { + data := orderbookNew.Bids[x] + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Price: data[0], Amount: data[1]}) + } + + for x := range orderbookNew.Asks { + data := orderbookNew.Asks[x] + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Price: data[0], Amount: data[1]}) + } + + orderbook.ProcessOrderbook(y.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(y.Name, p, assetType) +} + +// GetExchangeAccountInfo retrieves balances for all enabled currencies for the +// Yobit exchange +func (y *Yobit) GetExchangeAccountInfo() (exchange.AccountInfo, error) { + var response exchange.AccountInfo + response.ExchangeName = y.GetName() + accountBalance, err := y.GetAccountInfo() + if err != nil { + return response, err + } + + for x, y := range accountBalance.FundsInclOrders { + var exchangeCurrency exchange.AccountCurrencyInfo + exchangeCurrency.CurrencyName = common.StringToUpper(x) + exchangeCurrency.TotalValue = y + exchangeCurrency.Hold = 0 + for z, w := range accountBalance.Funds { + if z == x { + exchangeCurrency.Hold = y - w + } + } + + response.Currencies = append(response.Currencies, exchangeCurrency) + } + + return response, nil +} diff --git a/testdata/configtest.json b/testdata/configtest.json index 781f5304..eb5761b3 100644 --- a/testdata/configtest.json +++ b/testdata/configtest.json @@ -542,6 +542,31 @@ "Uppercase": true, "Delimiter": "_" } + }, + { + "Name": "Yobit", + "Enabled": true, + "Verbose": false, + "Websocket": false, + "UseSandbox": false, + "RESTPollingDelay": 10, + "AuthenticatedAPISupport": false, + "APIKey": "Key", + "APISecret": "Secret", + "AvailablePairs": "DASH_BTC,WAVES_BTC,LSK_BTC,LIZA_BTC,BCC_BTC,ETH_BTC,LTC_BTC,TRX_BTC,DOGE_BTC,VNTX_BTC,SW_BTC,ZEC_BTC,DASH_ETH,WAVES_ETH,LSK_ETH,LIZA_ETH,BCC_ETH,,LTC_ETH,TRX_ETH,DOGE_ETH,VNTX_ETH,SW_ETH,ZEC_ETH,DASH_DOGE,WAVES_DOGE,LSK_DOGE,LIZA_DOGE,BCC_DOGE,LTC_DOGE,TRX_DOGE,VNTX_DOGE,SW_DOGE,ZEC_DOGE,DASH_USD,WAVES_USD,LSK_USD,LIZA_USD,BCC_USD,LTC_USD,TRX_USD,VNTX_USD,SW_USD,ZEC_USD,ETH_USD,BTC_USD,DASH_RUR,WAVES_BTC,WAVES_RUR,LSK_RUR,LIZA_RUR,BCC_RUR,LTC_RUR,TRX_RUR,VNTX_RUR,SW_RUR,ETH_RUR,ZEC_RUR", + "EnabledPairs": "LTC_BTC,ETH_BTC,BTC_USD,DASH_BTC", + "BaseCurrencies": "USD", + "AssetTypes": "SPOT", + "ConfigCurrencyPairFormat": { + "Uppercase": true, + "Delimiter": "_" + }, + "RequestCurrencyPairFormat": { + "Uppercase": false, + "Delimiter": "_", + "Separator": "-" + } } + ] } \ No newline at end of file