From 542828e9570434a985ee20a9f89570bbe7e41747 Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Sat, 16 Sep 2017 13:03:00 +1000 Subject: [PATCH] Rename BTC-e to WEX and reinstate exchange --- README.md | 2 +- config_example.dat | 44 +- exchanges/btce/btce.go | 363 ---------------- exchanges/btce/btce_wrapper.go | 135 ------ exchanges/wex/wex.go | 393 ++++++++++++++++++ .../{btce/btce_types.go => wex/wex_types.go} | 42 +- exchanges/wex/wex_wrapper.go | 114 +++++ main.go | 6 +- 8 files changed, 556 insertions(+), 543 deletions(-) delete mode 100644 exchanges/btce/btce.go delete mode 100644 exchanges/btce/btce_wrapper.go create mode 100644 exchanges/wex/wex.go rename exchanges/{btce/btce_types.go => wex/wex_types.go} (83%) create mode 100644 exchanges/wex/wex_wrapper.go diff --git a/README.md b/README.md index 9841947f..4c48fe1f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader | Bitstamp | Yes | Yes | NA | | Bittrex | Yes | No | NA | | BTCC | Yes | Yes | No | -| BTCE | Yes | NA | NA | | BTCMarkets | Yes | NA | NA | | COINUT | Yes | No | NA | | GDAX(Coinbase) | Yes | Yes | No| @@ -37,6 +36,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader | LocalBitcoins | Yes | NA | NA | | OKCoin (both) | Yes | Yes | No | | Poloniex | Yes | Yes | NA | +| WEX | Yes | NA | NA | We are aiming to support the top 20 highest volume exchanges based off the [CoinMarketCap exchange data](https://coinmarketcap.com/exchanges/volume/24-hour/). diff --git a/config_example.dat b/config_example.dat index ff0a64e2..baad385a 100644 --- a/config_example.dat +++ b/config_example.dat @@ -158,28 +158,6 @@ "Uppercase": false } }, - { - "Name": "BTCE", - "Enabled": false, - "Verbose": false, - "Websocket": false, - "RESTPollingDelay": 10, - "AuthenticatedAPISupport": false, - "APIKey": "Key", - "APISecret": "Secret", - "AvailablePairs": "BTCUSD,BTCRUR,BTCEUR,LTCBTC,LTCUSD,LTCRUR,LTCEUR,NMCBTC,NMCUSD,NVCBTC,NVCUSD,USDRUR,EURUSD,EURRUR,PPCBTC,PPCUSD", - "EnabledPairs": "BTCUSD,BTCRUR,BTCEUR,LTCBTC,LTCUSD,LTCRUR,LTCEUR,NMCBTC,NMCUSD,NVCBTC,NVCUSD,USDRUR,EURUSD,EURRUR,PPCBTC,PPCUSD", - "BaseCurrencies": "USD,RUR,EUR", - "AssetTypes": "SPOT", - "ConfigCurrencyPairFormat": { - "Uppercase": true - }, - "RequestCurrencyPairFormat": { - "Uppercase": false, - "Delimiter": "_", - "Separator": "-" - } - }, { "Name": "BTC Markets", "Enabled": true, @@ -451,6 +429,28 @@ "Uppercase": true, "Delimiter": "_" } + }, + { + "Name": "WEX", + "Enabled": true, + "Verbose": false, + "Websocket": false, + "RESTPollingDelay": 10, + "AuthenticatedAPISupport": false, + "APIKey": "Key", + "APISecret": "Secret", + "AvailablePairs": "BTCUSD,BTCRUR,BTCEUR,LTCBTC,LTCUSD,LTCRUR,LTCEUR,NMCBTC,NMCUSD,NVCBTC,NVCUSD,USDRUR,EURUSD,EURRUR,PPCBTC,PPCUSD", + "EnabledPairs": "BTCUSD,BTCRUR,BTCEUR,LTCBTC,LTCUSD,LTCRUR,LTCEUR,NMCBTC,NMCUSD,NVCBTC,NVCUSD,USDRUR,EURUSD,EURRUR,PPCBTC,PPCUSD", + "BaseCurrencies": "USD,RUR,EUR", + "AssetTypes": "SPOT", + "ConfigCurrencyPairFormat": { + "Uppercase": true + }, + "RequestCurrencyPairFormat": { + "Uppercase": false, + "Delimiter": "_", + "Separator": "-" + } } ] } \ No newline at end of file diff --git a/exchanges/btce/btce.go b/exchanges/btce/btce.go deleted file mode 100644 index b251a4b5..00000000 --- a/exchanges/btce/btce.go +++ /dev/null @@ -1,363 +0,0 @@ -package btce - -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 ( - BTCE_API_PUBLIC_URL = "https://btc-e.com/api" - BTCE_API_PRIVATE_URL = "https://btc-e.com/tapi" - BTCE_API_PUBLIC_VERSION = "3" - BTCE_API_PRIVATE_VERSION = "1" - BTCE_INFO = "info" - BTCE_TICKER = "ticker" - BTCE_DEPTH = "depth" - BTCE_TRADES = "trades" - BTCE_ACCOUNT_INFO = "getInfo" - BTCE_TRADE = "Trade" - BTCE_ACTIVE_ORDERS = "ActiveOrders" - BTCE_ORDER_INFO = "OrderInfo" - BTCE_CANCEL_ORDER = "CancelOrder" - BTCE_TRADE_HISTORY = "TradeHistory" - BTCE_TRANSACTION_HISTORY = "TransHistory" - BTCE_WITHDRAW_COIN = "WithdrawCoin" - BTCE_CREATE_COUPON = "CreateCoupon" - BTCE_REDEEM_COUPON = "RedeemCoupon" -) - -type BTCE struct { - exchange.Base - Ticker map[string]BTCeTicker -} - -func (b *BTCE) SetDefaults() { - b.Name = "BTCE" - b.Enabled = false - b.Fee = 0.2 - b.Verbose = false - b.Websocket = false - b.RESTPollingDelay = 10 - b.Ticker = make(map[string]BTCeTicker) - b.RequestCurrencyPairFormat.Delimiter = "_" - b.RequestCurrencyPairFormat.Uppercase = false - b.RequestCurrencyPairFormat.Separator = "-" - b.ConfigCurrencyPairFormat.Delimiter = "" - b.ConfigCurrencyPairFormat.Uppercase = true - b.AssetTypes = []string{ticker.Spot} -} - -func (b *BTCE) Setup(exch config.ExchangeConfig) { - if !exch.Enabled { - b.SetEnabled(false) - } else { - b.Enabled = true - b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport - b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) - b.RESTPollingDelay = exch.RESTPollingDelay - b.Verbose = exch.Verbose - b.Websocket = exch.Websocket - b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",") - b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",") - b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",") - err := b.SetCurrencyPairFormat() - if err != nil { - log.Fatal(err) - } - err = b.SetAssetTypes() - if err != nil { - log.Fatal(err) - } - } -} - -func (b *BTCE) GetFee() float64 { - return b.Fee -} - -func (b *BTCE) GetInfo() (BTCEInfo, error) { - req := fmt.Sprintf("%s/%s/%s/", BTCE_API_PUBLIC_URL, BTCE_API_PUBLIC_VERSION, BTCE_INFO) - resp := BTCEInfo{} - err := common.SendHTTPGetRequest(req, true, &resp) - - if err != nil { - return resp, err - } - - return resp, nil -} - -func (b *BTCE) GetTicker(symbol string) (map[string]BTCeTicker, error) { - type Response struct { - Data map[string]BTCeTicker - } - - response := Response{} - req := fmt.Sprintf("%s/%s/%s/%s", BTCE_API_PUBLIC_URL, BTCE_API_PUBLIC_VERSION, BTCE_TICKER, symbol) - err := common.SendHTTPGetRequest(req, true, &response.Data) - - if err != nil { - return nil, err - } - return response.Data, nil -} - -func (b *BTCE) GetDepth(symbol string) (BTCEOrderbook, error) { - type Response struct { - Data map[string]BTCEOrderbook - } - - response := Response{} - req := fmt.Sprintf("%s/%s/%s/%s", BTCE_API_PUBLIC_URL, BTCE_API_PUBLIC_VERSION, BTCE_DEPTH, symbol) - - err := common.SendHTTPGetRequest(req, true, &response.Data) - if err != nil { - return BTCEOrderbook{}, err - } - - depth := response.Data[symbol] - return depth, nil -} - -func (b *BTCE) GetTrades(symbol string) ([]BTCETrades, error) { - type Response struct { - Data map[string][]BTCETrades - } - - response := Response{} - req := fmt.Sprintf("%s/%s/%s/%s", BTCE_API_PUBLIC_URL, BTCE_API_PUBLIC_VERSION, BTCE_TRADES, symbol) - - err := common.SendHTTPGetRequest(req, true, &response.Data) - if err != nil { - return []BTCETrades{}, err - } - - trades := response.Data[symbol] - return trades, nil -} - -func (b *BTCE) GetAccountInfo() (BTCEAccountInfo, error) { - var result BTCEAccountInfo - err := b.SendAuthenticatedHTTPRequest(BTCE_ACCOUNT_INFO, url.Values{}, &result) - - if err != nil { - return result, err - } - - return result, nil -} - -func (b *BTCE) GetActiveOrders(pair string) (map[string]BTCEActiveOrders, error) { - req := url.Values{} - req.Add("pair", pair) - - var result map[string]BTCEActiveOrders - err := b.SendAuthenticatedHTTPRequest(BTCE_ACTIVE_ORDERS, req, &result) - - if err != nil { - return result, err - } - - return result, nil -} - -func (b *BTCE) GetOrderInfo(OrderID int64) (map[string]BTCEOrderInfo, error) { - req := url.Values{} - req.Add("order_id", strconv.FormatInt(OrderID, 10)) - - var result map[string]BTCEOrderInfo - err := b.SendAuthenticatedHTTPRequest(BTCE_ORDER_INFO, req, &result) - - if err != nil { - return result, err - } - - return result, nil -} - -func (b *BTCE) CancelOrder(OrderID int64) (bool, error) { - req := url.Values{} - req.Add("order_id", strconv.FormatInt(OrderID, 10)) - - var result BTCECancelOrder - err := b.SendAuthenticatedHTTPRequest(BTCE_CANCEL_ORDER, req, &result) - - if err != nil { - return false, err - } - - return true, nil -} - -//to-do: convert orderid to int64 -func (b *BTCE) Trade(pair, orderType string, amount, price float64) (float64, 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)) - - var result BTCETrade - err := b.SendAuthenticatedHTTPRequest(BTCE_TRADE, req, &result) - - if err != nil { - return 0, err - } - - return result.OrderID, nil -} - -func (b *BTCE) GetTransactionHistory(TIDFrom, Count, TIDEnd int64, order, since, end string) (map[string]BTCETransHistory, 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) - - var result map[string]BTCETransHistory - err := b.SendAuthenticatedHTTPRequest(BTCE_TRANSACTION_HISTORY, req, &result) - - if err != nil { - return result, err - } - - return result, nil -} - -func (b *BTCE) GetTradeHistory(TIDFrom, Count, TIDEnd int64, order, since, end, pair string) (map[string]BTCETradeHistory, 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) - - var result map[string]BTCETradeHistory - err := b.SendAuthenticatedHTTPRequest(BTCE_TRADE_HISTORY, req, &result) - - if err != nil { - return result, err - } - - return result, nil -} - -func (b *BTCE) WithdrawCoins(coin string, amount float64, address string) (BTCEWithdrawCoins, error) { - req := url.Values{} - - req.Add("coinName", coin) - req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64)) - req.Add("address", address) - - var result BTCEWithdrawCoins - err := b.SendAuthenticatedHTTPRequest(BTCE_WITHDRAW_COIN, req, &result) - - if err != nil { - return result, err - } - return result, nil -} - -func (b *BTCE) CreateCoupon(currency string, amount float64) (BTCECreateCoupon, error) { - req := url.Values{} - - req.Add("currency", currency) - req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64)) - - var result BTCECreateCoupon - err := b.SendAuthenticatedHTTPRequest(BTCE_CREATE_COUPON, req, &result) - - if err != nil { - return result, err - } - - return result, nil -} - -func (b *BTCE) RedeemCoupon(coupon string) (BTCERedeemCoupon, error) { - req := url.Values{} - - req.Add("coupon", coupon) - - var result BTCERedeemCoupon - err := b.SendAuthenticatedHTTPRequest(BTCE_REDEEM_COUPON, req, &result) - - if err != nil { - return result, err - } - - return result, nil -} - -func (b *BTCE) SendAuthenticatedHTTPRequest(method string, values url.Values, result interface{}) (err error) { - if !b.AuthenticatedAPISupport { - return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name) - } - - if b.Nonce.Get() == 0 { - b.Nonce.Set(time.Now().Unix()) - } else { - b.Nonce.Inc() - } - values.Set("nonce", b.Nonce.String()) - values.Set("method", method) - - encoded := values.Encode() - hmac := common.GetHMAC(common.HashSHA512, []byte(encoded), []byte(b.APISecret)) - - if b.Verbose { - log.Printf("Sending POST request to %s calling method %s with params %s\n", BTCE_API_PRIVATE_URL, method, encoded) - } - - headers := make(map[string]string) - headers["Key"] = b.APIKey - headers["Sign"] = common.HexEncodeToString(hmac) - headers["Content-Type"] = "application/x-www-form-urlencoded" - - resp, err := common.SendHTTPRequest("POST", BTCE_API_PRIVATE_URL, headers, strings.NewReader(encoded)) - - if err != nil { - return err - } - - response := BTCEResponse{} - err = common.JSONDecode([]byte(resp), &response) - - if err != nil { - return err - } - - if response.Success != 1 { - return errors.New(response.Error) - } - - JSONEncoded, err := common.JSONEncode(response.Return) - - if err != nil { - return err - } - - err = common.JSONDecode(JSONEncoded, &result) - - if err != nil { - return err - } - return nil -} diff --git a/exchanges/btce/btce_wrapper.go b/exchanges/btce/btce_wrapper.go deleted file mode 100644 index 282f9a38..00000000 --- a/exchanges/btce/btce_wrapper.go +++ /dev/null @@ -1,135 +0,0 @@ -package btce - -import ( - "errors" - "log" - "time" - - "github.com/thrasher-/gocryptotrader/common" - "github.com/thrasher-/gocryptotrader/currency/pair" - "github.com/thrasher-/gocryptotrader/exchanges" - "github.com/thrasher-/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-/gocryptotrader/exchanges/ticker" -) - -// Start starts the BTCE go routine -func (b *BTCE) Start() { - go b.Run() -} - -// Run implements the BTCE wrapper -func (b *BTCE) Run() { - if b.Verbose { - log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket)) - log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay) - log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs) - } - - pairs := b.GetEnabledCurrencies() - pairsCollated, err := exchange.GetAndFormatExchangeCurrencies(b.Name, pairs) - if err != nil { - log.Println(err) - b.Enabled = false - return - } - - for b.Enabled { - go func() { - ticker, err := b.GetTicker(pairsCollated.String()) - if err != nil { - log.Println(err) - return - } - for x, y := range ticker { - x = common.StringToUpper(x[0:3] + x[4:]) - log.Printf("BTC-e %s: Last %f High %f Low %f Volume %f\n", x, y.Last, y.High, y.Low, y.Vol_cur) - b.Ticker[x] = y - } - }() - time.Sleep(time.Second * b.RESTPollingDelay) - } -} - -// UpdateTicker updates and returns the ticker for a currency pair -func (b *BTCE) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) { - var tickerPrice ticker.Price - result, err := b.GetTicker(p.Pair().String()) - if err != nil { - return tickerPrice, err - } - - tick, ok := result[p.Pair().Lower().String()] - if !ok { - return tickerPrice, errors.New("unable to get currency") - } - tickerPrice.Pair = p - tickerPrice.Ask = tick.Buy - tickerPrice.Bid = tick.Sell - tickerPrice.Low = tick.Low - tickerPrice.Last = tick.Last - tickerPrice.Volume = tick.Vol_cur - tickerPrice.High = tick.High - ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType) - return ticker.GetTicker(b.Name, p, assetType) -} - -// GetTickerPrice returns the ticker for a currency pair -func (b *BTCE) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) { - tick, err := ticker.GetTicker(b.GetName(), p, assetType) - if err != nil { - return b.UpdateTicker(p, assetType) - } - return tick, nil -} - -// GetOrderbookEx returns the orderbook for a currency pair -func (b *BTCE) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { - ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType) - if err == nil { - return b.UpdateOrderbook(p, assetType) - } - return ob, nil -} - -// UpdateOrderbook updates and returns the orderbook for a currency pair -func (b *BTCE) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { - var orderBook orderbook.Base - orderbookNew, err := b.GetDepth(exchange.FormatExchangeCurrency(b.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(b.GetName(), p, orderBook, assetType) - return orderbook.GetOrderbook(b.Name, p, assetType) -} - -// GetExchangeAccountInfo retrieves balances for all enabled currencies for the -// BTCE exchange -func (b *BTCE) GetExchangeAccountInfo() (exchange.AccountInfo, error) { - var response exchange.AccountInfo - response.ExchangeName = b.GetName() - accountBalance, err := b.GetAccountInfo() - if err != nil { - return response, err - } - - for x, y := range accountBalance.Funds { - var exchangeCurrency exchange.AccountCurrencyInfo - exchangeCurrency.CurrencyName = common.StringToUpper(x) - exchangeCurrency.TotalValue = y - exchangeCurrency.Hold = 0 - response.Currencies = append(response.Currencies, exchangeCurrency) - } - - return response, nil -} diff --git a/exchanges/wex/wex.go b/exchanges/wex/wex.go new file mode 100644 index 00000000..3ac246c1 --- /dev/null +++ b/exchanges/wex/wex.go @@ -0,0 +1,393 @@ +package wex + +import ( + "errors" + "fmt" + "log" + "net/url" + "strconv" + "strings" + "time" + + "github.com/thrasher-/gocryptotrader/common" + "github.com/thrasher-/gocryptotrader/config" + exchange "github.com/thrasher-/gocryptotrader/exchanges" + "github.com/thrasher-/gocryptotrader/exchanges/ticker" +) + +const ( + wexAPIPublicURL = "https://wex.nz/api" + wexAPIPrivateURL = "https://wex.nz/tapi" + wexAPIPublicVersion = "3" + wexAPIPrivateVersion = "1" + wexInfo = "info" + wexTicker = "ticker" + wexDepth = "depth" + wexTrades = "trades" + wexAccountInfo = "getInfo" + wexTrade = "Trade" + wexActiveOrders = "ActiveOrders" + wexOrderInfo = "OrderInfo" + wexCancelOrder = "CancelOrder" + wexTradeHistory = "TradeHistory" + wexTransactionHistory = "TransHistory" + wexWithdrawCoin = "WithdrawCoin" + wexCoinDepositAddress = "CoinDepositAddress" + wexCreateCoupon = "CreateCoupon" + wexRedeemCoupon = "RedeemCoupon" +) + +// WEX is the overarching type across the wex package +type WEX struct { + exchange.Base + Ticker map[string]Ticker +} + +// SetDefaults sets current default value for WEX +func (w *WEX) SetDefaults() { + w.Name = "WEX" + w.Enabled = false + w.Fee = 0.2 + w.Verbose = false + w.Websocket = false + w.RESTPollingDelay = 10 + w.Ticker = make(map[string]Ticker) + w.RequestCurrencyPairFormat.Delimiter = "_" + w.RequestCurrencyPairFormat.Uppercase = false + w.RequestCurrencyPairFormat.Separator = "-" + w.ConfigCurrencyPairFormat.Delimiter = "" + w.ConfigCurrencyPairFormat.Uppercase = true + w.AssetTypes = []string{ticker.Spot} +} + +// Setup sets exchange configuration parameters for WEX +func (w *WEX) Setup(exch config.ExchangeConfig) { + if !exch.Enabled { + w.SetEnabled(false) + } else { + w.Enabled = true + w.AuthenticatedAPISupport = exch.AuthenticatedAPISupport + w.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + w.RESTPollingDelay = exch.RESTPollingDelay + w.Verbose = exch.Verbose + w.Websocket = exch.Websocket + w.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",") + w.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",") + w.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",") + err := w.SetCurrencyPairFormat() + if err != nil { + log.Fatal(err) + } + err = w.SetAssetTypes() + if err != nil { + log.Fatal(err) + } + } +} + +// GetFee returns the exchange fee +func (w *WEX) GetFee() float64 { + return w.Fee +} + +// GetInfo returns the WEX info +func (w *WEX) GetInfo() (Info, error) { + req := fmt.Sprintf("%s/%s/%s/", wexAPIPublicURL, wexAPIPublicVersion, wexInfo) + resp := Info{} + err := common.SendHTTPGetRequest(req, true, &resp) + + if err != nil { + return resp, err + } + + return resp, nil +} + +// GetTicker returns a ticker for a specific currency +func (w *WEX) GetTicker(symbol string) (map[string]Ticker, error) { + type Response struct { + Data map[string]Ticker + } + + response := Response{} + req := fmt.Sprintf("%s/%s/%s/%s", wexAPIPublicURL, wexAPIPublicVersion, wexTicker, symbol) + err := common.SendHTTPGetRequest(req, true, &response.Data) + + if err != nil { + return nil, err + } + return response.Data, nil +} + +// GetDepth returns the depth for a specific currency +func (w *WEX) GetDepth(symbol string) (Orderbook, error) { + type Response struct { + Data map[string]Orderbook + } + + response := Response{} + req := fmt.Sprintf("%s/%s/%s/%s", wexAPIPublicURL, wexAPIPublicVersion, wexDepth, symbol) + + err := common.SendHTTPGetRequest(req, true, &response.Data) + if err != nil { + return Orderbook{}, err + } + + depth := response.Data[symbol] + return depth, nil +} + +// GetTrades returns the trades for a specific currency +func (w *WEX) GetTrades(symbol string) ([]Trades, error) { + type Response struct { + Data map[string][]Trades + } + + response := Response{} + req := fmt.Sprintf("%s/%s/%s/%s", wexAPIPublicURL, wexAPIPublicVersion, wexTrades, symbol) + + err := common.SendHTTPGetRequest(req, true, &response.Data) + if err != nil { + return nil, err + } + + trades := response.Data[symbol] + return trades, nil +} + +// GetAccountInfo returns a users account info +func (w *WEX) GetAccountInfo() (AccountInfo, error) { + var result AccountInfo + err := w.SendAuthenticatedHTTPRequest(wexAccountInfo, url.Values{}, &result) + + if err != nil { + return result, err + } + + return result, nil +} + +// GetActiveOrders returns the active orders for a specific currency +func (w *WEX) GetActiveOrders(pair string) (map[string]ActiveOrders, error) { + req := url.Values{} + req.Add("pair", pair) + + var result map[string]ActiveOrders + err := w.SendAuthenticatedHTTPRequest(wexActiveOrders, req, &result) + + if err != nil { + return result, err + } + + return result, nil +} + +// GetOrderInfo returns the order info for a specific order ID +func (w *WEX) GetOrderInfo(OrderID int64) (map[string]OrderInfo, error) { + req := url.Values{} + req.Add("order_id", strconv.FormatInt(OrderID, 10)) + + var result map[string]OrderInfo + err := w.SendAuthenticatedHTTPRequest(wexOrderInfo, req, &result) + + if err != nil { + return result, err + } + + return result, nil +} + +// CancelOrder cancels an order for a specific order ID +func (w *WEX) CancelOrder(OrderID int64) (bool, error) { + req := url.Values{} + req.Add("order_id", strconv.FormatInt(OrderID, 10)) + + var result CancelOrder + err := w.SendAuthenticatedHTTPRequest(wexCancelOrder, req, &result) + + if err != nil { + return false, err + } + + return true, nil +} + +// Trade places an order and returns the order ID if successful or an error +func (w *WEX) 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)) + + var result Trade + err := w.SendAuthenticatedHTTPRequest(wexTrade, req, &result) + + if err != nil { + return 0, err + } + + return int64(result.OrderID), nil +} + +// GetTransactionHistory returns the transaction history +func (w *WEX) GetTransactionHistory(TIDFrom, Count, TIDEnd int64, order, since, end string) (map[string]TransHistory, 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) + + var result map[string]TransHistory + err := w.SendAuthenticatedHTTPRequest(wexTransactionHistory, req, &result) + + if err != nil { + return result, err + } + + return result, nil +} + +// GetTradeHistory returns the trade history +func (w *WEX) 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) + + var result map[string]TradeHistory + err := w.SendAuthenticatedHTTPRequest(wexTradeHistory, req, &result) + + if err != nil { + return result, err + } + + return result, nil +} + +// WithdrawCoins withdraws coins for a specific coin +func (w *WEX) WithdrawCoins(coin string, amount float64, address string) (WithdrawCoins, error) { + req := url.Values{} + req.Add("coinName", coin) + req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64)) + req.Add("address", address) + + var result WithdrawCoins + err := w.SendAuthenticatedHTTPRequest(wexWithdrawCoin, req, &result) + + if err != nil { + return result, err + } + return result, nil +} + +// CoinDepositAddress returns the deposit address for a specific currency +func (w *WEX) CoinDepositAddress(coin string) (string, error) { + req := url.Values{} + req.Add("coinName", coin) + + var result CoinDepositAddress + err := w.SendAuthenticatedHTTPRequest(wexCoinDepositAddress, req, &result) + + if err != nil { + return "", nil + } + + return result.Address, nil +} + +// CreateCoupon creates an exchange coupon for a sepcific currency +func (w *WEX) 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 + err := w.SendAuthenticatedHTTPRequest(wexCreateCoupon, req, &result) + + if err != nil { + return result, err + } + + return result, nil +} + +// RedeemCoupon redeems an exchange coupon +func (w *WEX) RedeemCoupon(coupon string) (RedeemCoupon, error) { + req := url.Values{} + req.Add("coupon", coupon) + + var result RedeemCoupon + err := w.SendAuthenticatedHTTPRequest(wexRedeemCoupon, req, &result) + + if err != nil { + return result, err + } + + return result, nil +} + +// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to WEX +func (w *WEX) SendAuthenticatedHTTPRequest(method string, values url.Values, result interface{}) (err error) { + if !w.AuthenticatedAPISupport { + return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, w.Name) + } + + if w.Nonce.Get() == 0 { + w.Nonce.Set(time.Now().Unix()) + } else { + w.Nonce.Inc() + } + values.Set("nonce", w.Nonce.String()) + values.Set("method", method) + + encoded := values.Encode() + hmac := common.GetHMAC(common.HashSHA512, []byte(encoded), []byte(w.APISecret)) + + if w.Verbose { + log.Printf("Sending POST request to %s calling method %s with params %s\n", wexAPIPrivateURL, method, encoded) + } + + headers := make(map[string]string) + headers["Key"] = w.APIKey + headers["Sign"] = common.HexEncodeToString(hmac) + headers["Content-Type"] = "application/x-www-form-urlencoded" + + resp, err := common.SendHTTPRequest("POST", wexAPIPrivateURL, headers, strings.NewReader(encoded)) + + if err != nil { + return err + } + + response := Response{} + err = common.JSONDecode([]byte(resp), &response) + + if err != nil { + return err + } + + if response.Success != 1 { + return errors.New(response.Error) + } + + JSONEncoded, err := common.JSONEncode(response.Return) + + if err != nil { + return err + } + + err = common.JSONDecode(JSONEncoded, &result) + + if err != nil { + return err + } + return nil +} diff --git a/exchanges/btce/btce_types.go b/exchanges/wex/wex_types.go similarity index 83% rename from exchanges/btce/btce_types.go rename to exchanges/wex/wex_types.go index a1f8b8e5..5f365166 100644 --- a/exchanges/btce/btce_types.go +++ b/exchanges/wex/wex_types.go @@ -1,6 +1,6 @@ -package btce +package wex -type BTCeTicker struct { +type Ticker struct { High float64 Low float64 Avg float64 @@ -12,12 +12,12 @@ type BTCeTicker struct { Updated int64 } -type BTCEOrderbook struct { +type Orderbook struct { Asks [][]float64 `json:"asks"` Bids [][]float64 `json:"bids"` } -type BTCETrades struct { +type Trades struct { Type string `json:"type"` Price float64 `json:"bid"` Amount float64 `json:"amount"` @@ -25,13 +25,13 @@ type BTCETrades struct { Timestamp int64 `json:"timestamp"` } -type BTCEResponse struct { +type Response struct { Return interface{} `json:"return"` Success int `json:"success"` Error string `json:"error"` } -type BTCEPair struct { +type Pair struct { DecimalPlaces int `json:"decimal_places"` MinPrice float64 `json:"min_price"` MaxPrice float64 `json:"max_price"` @@ -40,12 +40,12 @@ type BTCEPair struct { Fee float64 `json:"fee"` } -type BTCEInfo struct { - ServerTime int64 `json:"server_time"` - Pairs map[string]BTCEPair `json:"pairs"` +type Info struct { + ServerTime int64 `json:"server_time"` + Pairs map[string]Pair `json:"pairs"` } -type BTCEAccountInfo struct { +type AccountInfo struct { Funds map[string]float64 `json:"funds"` OpenOrders int `json:"open_orders"` Rights struct { @@ -57,7 +57,7 @@ type BTCEAccountInfo struct { TransactionCount int `json:"transaction_count"` } -type BTCEActiveOrders struct { +type ActiveOrders struct { Pair string `json:"pair"` Type string `json:"sell"` Amount float64 `json:"amount"` @@ -66,7 +66,7 @@ type BTCEActiveOrders struct { Status int `json:"status"` } -type BTCEOrderInfo struct { +type OrderInfo struct { Pair string `json:"pair"` Type string `json:"sell"` StartAmount float64 `json:"start_amount"` @@ -76,19 +76,19 @@ type BTCEOrderInfo struct { Status int `json:"status"` } -type BTCECancelOrder struct { +type CancelOrder struct { OrderID float64 `json:"order_id"` Funds map[string]float64 `json:"funds"` } -type BTCETrade struct { +type Trade struct { Received float64 `json:"received"` Remains float64 `json:"remains"` OrderID float64 `json:"order_id"` Funds map[string]float64 `json:"funds"` } -type BTCETransHistory struct { +type TransHistory struct { Type int `json:"type"` Amount float64 `json:"amount"` Currency string `json:"currency"` @@ -97,7 +97,7 @@ type BTCETransHistory struct { Timestamp float64 `json:"timestamp"` } -type BTCETradeHistory struct { +type TradeHistory struct { Pair string `json:"pair"` Type string `json:"type"` Amount float64 `json:"amount"` @@ -107,19 +107,23 @@ type BTCETradeHistory struct { Timestamp float64 `json:"timestamp"` } -type BTCEWithdrawCoins struct { +type CoinDepositAddress struct { + Address string `json:"address"` +} + +type WithdrawCoins struct { TID int64 `json:"tId"` AmountSent float64 `json:"amountSent"` Funds map[string]float64 `json:"funds"` } -type BTCECreateCoupon struct { +type CreateCoupon struct { Coupon string `json:"coupon"` TransID int64 `json:"transID"` Funds map[string]float64 `json:"funds"` } -type BTCERedeemCoupon struct { +type RedeemCoupon struct { CouponAmount float64 `json:"couponAmount,string"` CouponCurrency string `json:"couponCurrency"` TransID int64 `json:"transID"` diff --git a/exchanges/wex/wex_wrapper.go b/exchanges/wex/wex_wrapper.go new file mode 100644 index 00000000..d2d3a884 --- /dev/null +++ b/exchanges/wex/wex_wrapper.go @@ -0,0 +1,114 @@ +package wex + +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 BTCE go routine +func (w *WEX) Start() { + go w.Run() +} + +// Run implements the BTCE wrapper +func (w *WEX) Run() { + if w.Verbose { + log.Printf("%s Websocket: %s.", w.GetName(), common.IsEnabled(w.Websocket)) + log.Printf("%s polling delay: %ds.\n", w.GetName(), w.RESTPollingDelay) + log.Printf("%s %d currencies enabled: %s.\n", w.GetName(), len(w.EnabledPairs), w.EnabledPairs) + } +} + +// UpdateTicker updates and returns the ticker for a currency pair +func (w *WEX) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) { + var tickerPrice ticker.Price + pairsCollated, err := exchange.GetAndFormatExchangeCurrencies(w.Name, w.GetEnabledCurrencies()) + if err != nil { + return tickerPrice, err + } + + result, err := w.GetTicker(pairsCollated.String()) + if err != nil { + return tickerPrice, err + } + + for _, x := range w.GetEnabledCurrencies() { + currency := exchange.FormatExchangeCurrency(w.Name, x).Lower().String() + var tp ticker.Price + tp.Pair = x + tp.Last = result[currency].Last + tp.Ask = result[currency].Sell + tp.Bid = result[currency].Buy + tp.Last = result[currency].Last + tp.Low = result[currency].Low + tp.Volume = result[currency].Vol_cur + ticker.ProcessTicker(w.Name, x, tp, assetType) + } + return ticker.GetTicker(w.Name, p, assetType) +} + +// GetTickerPrice returns the ticker for a currency pair +func (w *WEX) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) { + tick, err := ticker.GetTicker(w.GetName(), p, assetType) + if err != nil { + return w.UpdateTicker(p, assetType) + } + return tick, nil +} + +// GetOrderbookEx returns the orderbook for a currency pair +func (w *WEX) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(w.GetName(), p, assetType) + if err == nil { + return w.UpdateOrderbook(p, assetType) + } + return ob, nil +} + +// UpdateOrderbook updates and returns the orderbook for a currency pair +func (w *WEX) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base + orderbookNew, err := w.GetDepth(exchange.FormatExchangeCurrency(w.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(w.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(w.Name, p, assetType) +} + +// GetExchangeAccountInfo retrieves balances for all enabled currencies for the +// BTCE exchange +func (w *WEX) GetExchangeAccountInfo() (exchange.AccountInfo, error) { + var response exchange.AccountInfo + response.ExchangeName = w.GetName() + accountBalance, err := w.GetAccountInfo() + if err != nil { + return response, err + } + + for x, y := range accountBalance.Funds { + var exchangeCurrency exchange.AccountCurrencyInfo + exchangeCurrency.CurrencyName = common.StringToUpper(x) + exchangeCurrency.TotalValue = y + exchangeCurrency.Hold = 0 + response.Currencies = append(response.Currencies, exchangeCurrency) + } + + return response, nil +} diff --git a/main.go b/main.go index 967b20af..7d6dc1a5 100644 --- a/main.go +++ b/main.go @@ -19,7 +19,6 @@ import ( "github.com/thrasher-/gocryptotrader/exchanges/bitstamp" "github.com/thrasher-/gocryptotrader/exchanges/bittrex" "github.com/thrasher-/gocryptotrader/exchanges/btcc" - "github.com/thrasher-/gocryptotrader/exchanges/btce" "github.com/thrasher-/gocryptotrader/exchanges/btcmarkets" "github.com/thrasher-/gocryptotrader/exchanges/coinut" "github.com/thrasher-/gocryptotrader/exchanges/gdax" @@ -33,6 +32,7 @@ import ( "github.com/thrasher-/gocryptotrader/exchanges/okcoin" "github.com/thrasher-/gocryptotrader/exchanges/poloniex" "github.com/thrasher-/gocryptotrader/exchanges/ticker" + "github.com/thrasher-/gocryptotrader/exchanges/wex" "github.com/thrasher-/gocryptotrader/portfolio" "github.com/thrasher-/gocryptotrader/smsglobal" ) @@ -44,7 +44,7 @@ type ExchangeMain struct { bitstamp bitstamp.Bitstamp bitfinex bitfinex.Bitfinex bittrex bittrex.Bittrex - btce btce.BTCE + wex wex.WEX btcmarkets btcmarkets.BTCMarkets coinut coinut.COINUT gdax gdax.GDAX @@ -144,7 +144,7 @@ func main() { new(bitstamp.Bitstamp), new(bitfinex.Bitfinex), new(bittrex.Bittrex), - new(btce.BTCE), + new(wex.WEX), new(btcmarkets.BTCMarkets), new(coinut.COINUT), new(gdax.GDAX),