Initial overhaul of websocket connection and feeds (#189)

* Initial overhaul of websocket connection and feeds
* Added proxy support
* Piped to routines.go

* Added new websocket file in exchanges
Refactored orderbook handling into exchange_websocket.go
Added better error responses for binance_websocket.go
General clean for binance_websocket.go

* General fixes - bitfinex_websocket.go
Refactored orderbook cache code - bitfinex_websocket.go
Removed fatal error with unhandled type - routines.go

* Added general improvements to bitmex_websocket.go
Refactored orderbook handling to exchange_websocket.go
Added variable in Item struct in orderbook.go for looking up orders by ID

* Fix issue when routines are blocked due to Data Handler not started
Updated traffic handler
General fixes for bitstamp_websocket.go

* General fixes for coinbasepro_websocket.go

* General fixes for coinut_websocket.go
Fixed error return in exchange_websocket.go

* Removed comments in coinut_wrapper.go
Refactor orderbook logic from hitbtc_websocket.go to exchange_websocket.go

* General fixes

* Removed comments
General fixes

* Updated routines.go

* After rebase fix

* Fixed update config pairs in okcoin.go

* fixed config currency issue in okcoin.go for okcoin China

* exchange_websocket.go
*Removed unused const dec
*Removed state change routine
*Improved trafficMonitor routine
*Increased verbosity for error returns
*Removed uneeded mutex locks

exchange_websocket_test.go
*Added new tests for websocket and orderbook updating

routines.go
*Removed string cased

* Fixed race conditions on sync.waitgroup in exchanges_websocket.go

* Changes variable name in config.go

* Removes unnecessary comment

* Removes indefinite lock on error return

* Removes unnecessary comment

* Adds support for BTCC websocket
Drops support for BTCC REST

* Rewords comment in exchange_websocket.go
Moves types to poloniex_types.go

* Moves types to coinut_types.go

* Removes uneeded range for accessing array variables for coinbase_websocket.go
Removes comments in coinut_types.go

* Adds verbosity flag to GCT
Suppresses verbose output from routines.go

* Fixes setting proxy for REST and Websocket per exchange
Upgrades error handling
Drops unused *url.Url variable in exchange type

* Adds test for setting proxy

* Fixes bug that closes connection due to incorrect timeout time through a proxy connection

* Clarify verbose flag message
This commit is contained in:
Ryan O'Hara-Reid
2018-10-24 14:22:41 +11:00
committed by Adrian Gallagher
parent 7315e6604c
commit d3c2800fe0
99 changed files with 6515 additions and 3031 deletions

View File

@@ -1,14 +1,10 @@
package btcc
import (
"errors"
"fmt"
"log"
"net/url"
"strconv"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/config"
"github.com/thrasher-/gocryptotrader/exchanges"
@@ -17,39 +13,16 @@ import (
)
const (
btccAPIUrl = "https://spotusd-data.btcc.com"
btccAPIAuthenticatedMethod = "api_trade_v1.php"
btccAPIVersion = "2.0.1.3"
btccOrderBuy = "buyOrder2"
btccOrderSell = "sellOrder2"
btccOrderCancel = "cancelOrder"
btccIcebergBuy = "buyIcebergOrder"
btccIcebergSell = "sellIcebergOrder"
btccIcebergOrder = "getIcebergOrder"
btccIcebergOrders = "getIcebergOrders"
btccIcebergCancel = "cancelIcebergOrder"
btccAccountInfo = "getAccountInfo"
btccDeposits = "getDeposits"
btccMarketdepth = "getMarketDepth2"
btccOrder = "getOrder"
btccOrders = "getOrders"
btccTransactions = "getTransactions"
btccWithdrawal = "getWithdrawal"
btccWithdrawals = "getWithdrawals"
btccWithdrawalRequest = "requestWithdrawal"
btccStoporderBuy = "buyStopOrder"
btccStoporderSell = "sellStopOrder"
btccStoporderCancel = "cancelStopOrder"
btccStoporder = "getStopOrder"
btccStoporders = "getStopOrders"
btccAuthRate = 0
btccUnauthRate = 0
)
// BTCC is the main overaching type across the BTCC package
// NOTE this package is websocket connection dependant, the REST endpoints have
// been dropped
type BTCC struct {
exchange.Base
Conn *websocket.Conn
}
// SetDefaults sets default values for the exchange
@@ -58,10 +31,9 @@ func (b *BTCC) SetDefaults() {
b.Enabled = false
b.Fee = 0
b.Verbose = false
b.Websocket = false
b.RESTPollingDelay = 10
b.RequestCurrencyPairFormat.Delimiter = ""
b.RequestCurrencyPairFormat.Uppercase = false
b.RequestCurrencyPairFormat.Uppercase = true
b.ConfigCurrencyPairFormat.Delimiter = ""
b.ConfigCurrencyPairFormat.Uppercase = true
b.AssetTypes = []string{ticker.Spot}
@@ -71,8 +43,7 @@ func (b *BTCC) SetDefaults() {
request.NewRateLimit(time.Second, btccAuthRate),
request.NewRateLimit(time.Second, btccUnauthRate),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
b.APIUrlDefault = btccAPIUrl
b.APIUrl = b.APIUrlDefault
b.WebsocketInit()
}
// Setup is run on startup to setup exchange with config values
@@ -87,7 +58,7 @@ func (b *BTCC) Setup(exch config.ExchangeConfig) {
b.SetHTTPClientUserAgent(exch.HTTPUserAgent)
b.RESTPollingDelay = exch.RESTPollingDelay
b.Verbose = exch.Verbose
b.Websocket = exch.Websocket
b.Websocket.SetEnabled(exch.Websocket)
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
@@ -107,6 +78,18 @@ func (b *BTCC) Setup(exch config.ExchangeConfig) {
if err != nil {
log.Fatal(err)
}
err = b.SetClientProxyAddress(exch.ProxyAddress)
if err != nil {
log.Fatal(err)
}
err = b.WebsocketSetup(b.WsConnect,
exch.Name,
exch.Websocket,
btccSocketioAddress,
exch.WebsocketURL)
if err != nil {
log.Fatal(err)
}
}
}
@@ -114,528 +97,3 @@ func (b *BTCC) Setup(exch config.ExchangeConfig) {
func (b *BTCC) GetFee() float64 {
return b.Fee
}
// GetTicker returns ticker information
// currencyPair - Example "btccny", "ltccny" or "ltcbtc"
func (b *BTCC) GetTicker(currencyPair string) (Ticker, error) {
resp := Response{}
path := fmt.Sprintf("%s/data/pro/ticker?symbol=%s", b.APIUrl, currencyPair)
return resp.Ticker, b.SendHTTPRequest(path, &resp)
}
// GetTradeHistory returns trade history data
// currencyPair - Example "btccny", "ltccny" or "ltcbtc"
// limit - limits the returned trades example "10"
// sinceTid - returns trade records starting from id supplied example "5000"
// time - returns trade records starting from unix time 1406794449
func (b *BTCC) GetTradeHistory(currencyPair string, limit, sinceTid int64, time time.Time) ([]Trade, error) {
trades := []Trade{}
path := fmt.Sprintf("%s/data/pro/historydata?symbol=%s", b.APIUrl, currencyPair)
v := url.Values{}
if limit > 0 {
v.Set("limit", strconv.FormatInt(limit, 10))
}
if sinceTid > 0 {
v.Set("since", strconv.FormatInt(sinceTid, 10))
}
if !time.IsZero() {
v.Set("sincetype", strconv.FormatInt(time.Unix(), 10))
}
path = common.EncodeURLValues(path, v)
return trades, b.SendHTTPRequest(path, &trades)
}
// GetOrderBook returns current symbol order book
// currencyPair - Example "btccny", "ltccny" or "ltcbtc"
// limit - limits the returned trades example "10" if 0 will return full
// orderbook
func (b *BTCC) GetOrderBook(currencyPair string, limit int) (Orderbook, error) {
result := Orderbook{}
path := fmt.Sprintf("%s/data/pro/orderbook?symbol=%s&limit=%d", b.APIUrl, currencyPair, limit)
if limit == 0 {
path = fmt.Sprintf("%s/data/pro/orderbook?symbol=%s", b.APIUrl, currencyPair)
}
return result, b.SendHTTPRequest(path, &result)
}
// GetAccountInfo returns account information
func (b *BTCC) GetAccountInfo(infoType string) error {
params := make([]interface{}, 0)
if len(infoType) > 0 {
params = append(params, infoType)
}
return b.SendAuthenticatedHTTPRequest(btccAccountInfo, params)
}
// PlaceOrder places a new order
func (b *BTCC) PlaceOrder(buyOrder bool, price, amount float64, symbol string) {
params := make([]interface{}, 0)
params = append(params, strconv.FormatFloat(price, 'f', -1, 64))
params = append(params, strconv.FormatFloat(amount, 'f', -1, 64))
if len(symbol) > 0 {
params = append(params, symbol)
}
req := btccOrderBuy
if !buyOrder {
req = btccOrderSell
}
err := b.SendAuthenticatedHTTPRequest(req, params)
if err != nil {
log.Println(err)
}
}
// CancelOrder cancels an order
func (b *BTCC) CancelOrder(orderID int64, symbol string) {
params := make([]interface{}, 0)
params = append(params, orderID)
if len(symbol) > 0 {
params = append(params, symbol)
}
err := b.SendAuthenticatedHTTPRequest(btccOrderCancel, params)
if err != nil {
log.Println(err)
}
}
// GetDeposits returns deposit information
func (b *BTCC) GetDeposits(currency string, pending bool) {
params := make([]interface{}, 0)
params = append(params, currency)
if pending {
params = append(params, pending)
}
err := b.SendAuthenticatedHTTPRequest(btccDeposits, params)
if err != nil {
log.Println(err)
}
}
// GetMarketDepth returns market depth at limit
func (b *BTCC) GetMarketDepth(symbol string, limit int64) {
params := make([]interface{}, 0)
if limit > 0 {
params = append(params, limit)
}
if len(symbol) > 0 {
params = append(params, symbol)
}
err := b.SendAuthenticatedHTTPRequest(btccMarketdepth, params)
if err != nil {
log.Println(err)
}
}
// GetOrder returns information about a specific order
func (b *BTCC) GetOrder(orderID int64, symbol string, detailed bool) {
params := make([]interface{}, 0)
params = append(params, orderID)
if len(symbol) > 0 {
params = append(params, symbol)
}
if detailed {
params = append(params, detailed)
}
err := b.SendAuthenticatedHTTPRequest(btccOrder, params)
if err != nil {
log.Println(err)
}
}
// GetOrders returns information of a range of orders
func (b *BTCC) GetOrders(openonly bool, symbol string, limit, offset, since int64, detailed bool) {
params := make([]interface{}, 0)
if openonly {
params = append(params, openonly)
}
if len(symbol) > 0 {
params = append(params, symbol)
}
if limit > 0 {
params = append(params, limit)
}
if offset > 0 {
params = append(params, offset)
}
if since > 0 {
params = append(params, since)
}
if detailed {
params = append(params, detailed)
}
err := b.SendAuthenticatedHTTPRequest(btccOrders, params)
if err != nil {
log.Println(err)
}
}
// GetTransactions returns transaction lists
func (b *BTCC) GetTransactions(transType string, limit, offset, since int64, sinceType string) {
params := make([]interface{}, 0)
if len(transType) > 0 {
params = append(params, transType)
}
if limit > 0 {
params = append(params, limit)
}
if offset > 0 {
params = append(params, offset)
}
if since > 0 {
params = append(params, since)
}
if len(sinceType) > 0 {
params = append(params, sinceType)
}
err := b.SendAuthenticatedHTTPRequest(btccTransactions, params)
if err != nil {
log.Println(err)
}
}
// GetWithdrawal returns information about a withdrawal process
func (b *BTCC) GetWithdrawal(withdrawalID int64, currency string) {
params := make([]interface{}, 0)
params = append(params, withdrawalID)
if len(currency) > 0 {
params = append(params, currency)
}
err := b.SendAuthenticatedHTTPRequest(btccWithdrawal, params)
if err != nil {
log.Println(err)
}
}
// GetWithdrawals gets information about all withdrawals
func (b *BTCC) GetWithdrawals(currency string, pending bool) {
params := make([]interface{}, 0)
params = append(params, currency)
if pending {
params = append(params, pending)
}
err := b.SendAuthenticatedHTTPRequest(btccWithdrawals, params)
if err != nil {
log.Println(err)
}
}
// RequestWithdrawal requests a new withdrawal
func (b *BTCC) RequestWithdrawal(currency string, amount float64) {
params := make([]interface{}, 0)
params = append(params, currency)
params = append(params, amount)
err := b.SendAuthenticatedHTTPRequest(btccWithdrawalRequest, params)
if err != nil {
log.Println(err)
}
}
// IcebergOrder intiates a large order but at intervals to preserve orderbook
// integrity
func (b *BTCC) IcebergOrder(buyOrder bool, price, amount, discAmount, variance float64, symbol string) {
params := make([]interface{}, 0)
params = append(params, strconv.FormatFloat(price, 'f', -1, 64))
params = append(params, strconv.FormatFloat(amount, 'f', -1, 64))
params = append(params, strconv.FormatFloat(discAmount, 'f', -1, 64))
params = append(params, strconv.FormatFloat(variance, 'f', -1, 64))
if len(symbol) > 0 {
params = append(params, symbol)
}
req := btccIcebergBuy
if !buyOrder {
req = btccIcebergSell
}
err := b.SendAuthenticatedHTTPRequest(req, params)
if err != nil {
log.Println(err)
}
}
// GetIcebergOrder returns information on your iceberg order
func (b *BTCC) GetIcebergOrder(orderID int64, symbol string) {
params := make([]interface{}, 0)
params = append(params, orderID)
if len(symbol) > 0 {
params = append(params, symbol)
}
err := b.SendAuthenticatedHTTPRequest(btccIcebergOrder, params)
if err != nil {
log.Println(err)
}
}
// GetIcebergOrders returns information on all iceberg orders
func (b *BTCC) GetIcebergOrders(limit, offset int64, symbol string) {
params := make([]interface{}, 0)
if limit > 0 {
params = append(params, limit)
}
if offset > 0 {
params = append(params, offset)
}
if len(symbol) > 0 {
params = append(params, symbol)
}
err := b.SendAuthenticatedHTTPRequest(btccIcebergOrders, params)
if err != nil {
log.Println(err)
}
}
// CancelIcebergOrder cancels iceberg order
func (b *BTCC) CancelIcebergOrder(orderID int64, symbol string) {
params := make([]interface{}, 0)
params = append(params, orderID)
if len(symbol) > 0 {
params = append(params, symbol)
}
err := b.SendAuthenticatedHTTPRequest(btccIcebergCancel, params)
if err != nil {
log.Println(err)
}
}
// PlaceStopOrder inserts a stop loss order
func (b *BTCC) PlaceStopOrder(buyOder bool, stopPrice, price, amount, trailingAmt, trailingPct float64, symbol string) {
params := make([]interface{}, 0)
if stopPrice > 0 {
params = append(params, stopPrice)
}
params = append(params, strconv.FormatFloat(price, 'f', -1, 64))
params = append(params, strconv.FormatFloat(amount, 'f', -1, 64))
if trailingAmt > 0 {
params = append(params, strconv.FormatFloat(trailingAmt, 'f', -1, 64))
}
if trailingPct > 0 {
params = append(params, strconv.FormatFloat(trailingPct, 'f', -1, 64))
}
if len(symbol) > 0 {
params = append(params, symbol)
}
req := btccStoporderBuy
if !buyOder {
req = btccStoporderSell
}
err := b.SendAuthenticatedHTTPRequest(req, params)
if err != nil {
log.Println(err)
}
}
// GetStopOrder returns a stop order
func (b *BTCC) GetStopOrder(orderID int64, symbol string) {
params := make([]interface{}, 0)
params = append(params, orderID)
if len(symbol) > 0 {
params = append(params, symbol)
}
err := b.SendAuthenticatedHTTPRequest(btccStoporder, params)
if err != nil {
log.Println(err)
}
}
// GetStopOrders returns all stop orders
func (b *BTCC) GetStopOrders(status, orderType string, stopPrice float64, limit, offset int64, symbol string) {
params := make([]interface{}, 0)
if len(status) > 0 {
params = append(params, status)
}
if len(orderType) > 0 {
params = append(params, orderType)
}
if stopPrice > 0 {
params = append(params, stopPrice)
}
if limit > 0 {
params = append(params, limit)
}
if offset > 0 {
params = append(params, limit)
}
if len(symbol) > 0 {
params = append(params, symbol)
}
err := b.SendAuthenticatedHTTPRequest(btccStoporders, params)
if err != nil {
log.Println(err)
}
}
// CancelStopOrder cancels a stop order
func (b *BTCC) CancelStopOrder(orderID int64, symbol string) {
params := make([]interface{}, 0)
params = append(params, orderID)
if len(symbol) > 0 {
params = append(params, symbol)
}
err := b.SendAuthenticatedHTTPRequest(btccStoporderCancel, params)
if err != nil {
log.Println(err)
}
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *BTCC) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload("GET", path, nil, nil, result, false, b.Verbose)
}
// SendAuthenticatedHTTPRequest sends a valid authenticated HTTP request
func (b *BTCC) SendAuthenticatedHTTPRequest(method string, params []interface{}) (err error) {
if !b.AuthenticatedAPISupport {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
}
if b.Nonce.Get() == 0 {
b.Nonce.Set(time.Now().UnixNano())
} else {
b.Nonce.Inc()
}
encoded := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=%d&method=%s&params=", b.Nonce.String()[0:16], b.APIKey, 1, method)
if len(params) == 0 {
params = make([]interface{}, 0)
} else {
items := make([]string, 0)
for _, x := range params {
xType := fmt.Sprintf("%T", x)
switch xType {
case "int64", "int":
{
items = append(items, fmt.Sprintf("%d", x))
}
case "string":
{
items = append(items, fmt.Sprintf("%s", x))
}
case "float64":
{
items = append(items, fmt.Sprintf("%f", x))
}
case "bool":
{
if x == true {
items = append(items, "1")
} else {
items = append(items, "")
}
}
default:
{
items = append(items, fmt.Sprintf("%v", x))
}
}
}
encoded += common.JoinStrings(items, ",")
}
if b.Verbose {
log.Println(encoded)
}
hmac := common.GetHMAC(common.HashSHA1, []byte(encoded), []byte(b.APISecret))
postData := make(map[string]interface{})
postData["method"] = method
postData["params"] = params
postData["id"] = 1
apiURL := fmt.Sprintf("%s/%s", b.APIUrl, btccAPIAuthenticatedMethod)
data, err := common.JSONEncode(postData)
if err != nil {
return errors.New("Unable to JSON Marshal POST data")
}
if b.Verbose {
log.Printf("Sending POST request to %s calling method %s with params %s\n", apiURL, method, data)
}
headers := make(map[string]string)
headers["Content-type"] = "application/json-rpc"
headers["Authorization"] = "Basic " + common.Base64Encode([]byte(b.APIKey+":"+common.HexEncodeToString(hmac)))
headers["Json-Rpc-Tonce"] = b.Nonce.String()
return b.SendPayload("POST", apiURL, headers, strings.NewReader(string(data)), nil, true, b.Verbose)
}

View File

@@ -28,8 +28,9 @@ func TestSetup(t *testing.T) {
}
b.Setup(bConfig)
if !b.IsEnabled() || b.AuthenticatedAPISupport || b.RESTPollingDelay != time.Duration(10) ||
b.Verbose || b.Websocket || len(b.BaseCurrencies) < 1 ||
if !b.IsEnabled() || b.AuthenticatedAPISupport ||
b.RESTPollingDelay != time.Duration(10) || b.Verbose ||
b.Websocket.IsEnabled() || len(b.BaseCurrencies) < 1 ||
len(b.AvailablePairs) < 1 || len(b.EnabledPairs) < 1 {
t.Error("Test Failed - BTCC Setup values not set correctly")
}
@@ -41,38 +42,38 @@ func TestGetFee(t *testing.T) {
}
}
func TestGetTicker(t *testing.T) {
t.Skip()
_, err := b.GetTicker("BTCUSD")
if err != nil {
t.Error("Test failed - GetTicker() error", err)
}
}
// func TestGetTicker(t *testing.T) {
// t.Skip()
// _, err := b.GetTicker("BTCUSD")
// if err != nil {
// t.Error("Test failed - GetTicker() error", err)
// }
// }
func TestGetTradeHistory(t *testing.T) {
t.Skip()
_, err := b.GetTradeHistory("BTCUSD", 0, 0, time.Time{})
if err != nil {
t.Error("Test failed - GetTradeHistory() error", err)
}
}
// func TestGetTradeHistory(t *testing.T) {
// t.Skip()
// _, err := b.GetTradeHistory("BTCUSD", 0, 0, time.Time{})
// if err != nil {
// t.Error("Test failed - GetTradeHistory() error", err)
// }
// }
func TestGetOrderBook(t *testing.T) {
t.Skip()
_, err := b.GetOrderBook("BTCUSD", 100)
if err != nil {
t.Error("Test failed - GetOrderBook() error", err)
}
_, err = b.GetOrderBook("BTCUSD", 0)
if err != nil {
t.Error("Test failed - GetOrderBook() error", err)
}
}
// func TestGetOrderBook(t *testing.T) {
// t.Skip()
// _, err := b.GetOrderBook("BTCUSD", 100)
// if err != nil {
// t.Error("Test failed - GetOrderBook() error", err)
// }
// _, err = b.GetOrderBook("BTCUSD", 0)
// if err != nil {
// t.Error("Test failed - GetOrderBook() error", err)
// }
// }
func TestGetAccountInfo(t *testing.T) {
t.Skip()
err := b.GetAccountInfo("")
if err == nil {
t.Error("Test failed - GetAccountInfo() error", err)
}
}
// func TestGetAccountInfo(t *testing.T) {
// t.Skip()
// err := b.GetAccountInfo("")
// if err == nil {
// t.Error("Test failed - GetAccountInfo() error", err)
// }
// }

View File

@@ -1,12 +1,70 @@
package btcc
// Response is the generalized response type
type Response struct {
Ticker Ticker `json:"ticker"`
import "encoding/json"
// WsAllTickerData defines multiple ticker data
type WsAllTickerData []WsTicker
// WsOutgoing defines outgoing JSON
type WsOutgoing struct {
Action string `json:"action"`
Symbol string `json:"symbol,omitempty"`
Count int `json:"count,omitempty"`
Len int `json:"len,omitempty"`
}
// Ticker holds basic ticker information
type Ticker struct {
// WsResponseMain defines the main websocket response
type WsResponseMain struct {
MsgType string `json:"MsgType"`
CRID string `json:"CRID"`
RC interface{} `json:"RC"`
Reason string `json:"Reason"`
Data json.RawMessage `json:"data"`
}
// WsOrderbookSnapshot defines an orderbook from the websocket
type WsOrderbookSnapshot struct {
Timestamp int64 `json:"Timestamp"`
Symbol string `json:"Symbol"`
Version int64 `json:"Version"`
Type string `json:"Type"`
Content string `json:"Content"`
List []struct {
Side string `json:"Side"`
Size interface{} `json:"Size"`
Price float64 `json:"Price"`
} `json:"List"`
MsgType string `json:"MsgType"`
}
// WsOrderbookSnapshotOld defines an old orderbook from the websocket connection
type WsOrderbookSnapshotOld struct {
MsgType string `json:"MsgType"`
Symbol string `json:"Symbol"`
Data map[string][]interface{} `json:"Data"`
Timestamp int64 `json:"Timestamp"`
}
// WsTrades defines trading data from the websocket
type WsTrades struct {
Trades []struct {
TID int64 `json:"TID"`
Timestamp int64 `json:"Timestamp"`
Symbol string `json:"Symbol"`
Side string `json:"Side"`
Size float64 `json:"Size"`
Price float64 `json:"Price"`
MsgType string `json:"MsgType"`
} `json:"Trades"`
RC int64 `json:"RC"`
CRID string `json:"CRID"`
Reason string `json:"Reason"`
MsgType string `json:"MsgType"`
}
// WsTicker defines ticker data from the websocket
type WsTicker struct {
Symbol string `json:"Symbol"`
BidPrice float64 `json:"BidPrice"`
AskPrice float64 `json:"AskPrice"`
Open float64 `json:"Open"`
@@ -20,177 +78,5 @@ type Ticker struct {
Timestamp int64 `json:"Timestamp"`
ExecutionLimitDown float64 `json:"ExecutionLimitDown"`
ExecutionLimitUp float64 `json:"ExecutionLimitUp"`
}
// Trade holds executed trade data
type Trade struct {
ID int64 `json:"Id"`
Timestamp int64 `json:"Timestamp"`
Price float64 `json:"Price"`
Quantity float64 `json:"Quantity"`
Side string `json:"Side"`
}
// Orderbook holds orderbook data
type Orderbook struct {
Bids [][]float64 `json:"bids"`
Asks [][]float64 `json:"asks"`
Date int64 `json:"date"`
}
// Profile holds profile information
type Profile struct {
Username string
TradePasswordEnabled bool `json:"trade_password_enabled,bool"`
OTPEnabled bool `json:"otp_enabled,bool"`
TradeFee float64 `json:"trade_fee"`
TradeFeeCNYLTC float64 `json:"trade_fee_cnyltc"`
TradeFeeBTCLTC float64 `json:"trade_fee_btcltc"`
DailyBTCLimit float64 `json:"daily_btc_limit"`
DailyLTCLimit float64 `json:"daily_ltc_limit"`
BTCDespoitAddress string `json:"btc_despoit_address"`
BTCWithdrawalAddress string `json:"btc_withdrawal_address"`
LTCDepositAddress string `json:"ltc_deposit_address"`
LTCWithdrawalAddress string `json:"ltc_withdrawal_request"`
APIKeyPermission int64 `json:"api_key_permission"`
}
// CurrencyGeneric holds currency information
type CurrencyGeneric struct {
Currency string
Symbol string
Amount string
AmountInt int64 `json:"amount_integer"`
AmountDecimal float64 `json:"amount_decimal"`
}
// Order holds order information
type Order struct {
ID int64
Type string
Price float64
Currency string
Amount float64
AmountOrig float64 `json:"amount_original"`
Date int64
Status string
Detail OrderDetail
}
// OrderDetail holds order detail information
type OrderDetail struct {
Dateline int64
Price float64
Amount float64
}
// Withdrawal holds withdrawal transaction information
type Withdrawal struct {
ID int64
Address string
Currency string
Amount float64
Date int64
Transaction string
Status string
}
// Deposit holds deposit address information
type Deposit struct {
ID int64
Address string
Currency string
Amount float64
Date int64
Status string
}
// BidAsk holds bid and ask information
type BidAsk struct {
Price float64
Amount float64
}
// Depth holds order book depth
type Depth struct {
Bid []BidAsk
Ask []BidAsk
}
// Transaction holds transaction information
type Transaction struct {
ID int64
Type string
BTCAmount float64 `json:"btc_amount"`
LTCAmount float64 `json:"ltc_amount"`
CNYAmount float64 `json:"cny_amount"`
Date int64
}
// IcebergOrder holds iceberg lettuce
type IcebergOrder struct {
ID int64
Type string
Price float64
Market string
Amount float64
AmountOrig float64 `json:"amount_original"`
DisclosedAmount float64 `json:"disclosed_amount"`
Variance float64
Date int64
Status string
}
// StopOrder holds stop order information
type StopOrder struct {
ID int64
Type string
StopPrice float64 `json:"stop_price"`
TrailingAmt float64 `json:"trailing_amount"`
TrailingPct float64 `json:"trailing_percentage"`
Price float64
Market string
Amount float64
Date int64
Status string
OrderID int64 `json:"order_id"`
}
// WebsocketOrder holds websocket order information
type WebsocketOrder struct {
Price float64 `json:"price"`
TotalAmount float64 `json:"totalamount"`
Type string `json:"type"`
}
// WebsocketGroupOrder holds websocket group order book information
type WebsocketGroupOrder struct {
Asks []WebsocketOrder `json:"ask"`
Bids []WebsocketOrder `json:"bid"`
Market string `json:"market"`
}
// WebsocketTrade holds websocket trade information
type WebsocketTrade struct {
Amount float64 `json:"amount"`
Date float64 `json:"date"`
Market string `json:"market"`
Price float64 `json:"price"`
TradeID float64 `json:"trade_id"`
Type string `json:"type"`
}
// WebsocketTicker holds websocket ticker information
type WebsocketTicker struct {
Buy float64 `json:"buy"`
Date float64 `json:"date"`
High float64 `json:"high"`
Last float64 `json:"last"`
Low float64 `json:"low"`
Market string `json:"market"`
Open float64 `json:"open"`
PrevClose float64 `json:"prev_close"`
Sell float64 `json:"sell"`
Volume float64 `json:"vol"`
Vwap float64 `json:"vwap"`
MsgType string `json:"MsgType"`
}

View File

@@ -1,125 +1,569 @@
package btcc
import (
"errors"
"fmt"
"log"
"net/http"
"net/url"
"strconv"
"sync"
"time"
"github.com/gorilla/websocket"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/socketio"
"github.com/thrasher-/gocryptotrader/currency/pair"
exchange "github.com/thrasher-/gocryptotrader/exchanges"
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
)
const (
btccSocketioAddress = "https://websocket.btcc.com"
btccSocketioAddress = "wss://ws.btcc.com"
msgTypeHeartBeat = "Heartbeat"
msgTypeGetActiveContracts = "GetActiveContractsResponse"
msgTypeQuote = "QuoteResponse"
msgTypeLogin = "LoginResponse"
msgTypeAccountInfo = "AccountInfo"
msgTypeExecReport = "ExecReport"
msgTypePlaceOrder = "PlaceOrderResponse"
msgTypeCancelAllOrders = "CancelAllOrdersResponse"
msgTypeCancelOrder = "CancelOrderResponse"
msgTypeCancelReplaceOrder = "CancelReplaceOrderResponse"
msgTypeGetAccountInfo = "GetAccountInfoResponse"
msgTypeRetrieveOrder = "RetrieveOrderResponse"
msgTypeGetTrades = "GetTradesResponse"
msgTypeAllTickers = "AllTickersResponse"
)
// BTCCSocket is a pointer to a IO socket
var BTCCSocket *socketio.SocketIO
var (
mtx sync.Mutex
)
// OnConnect gets information from the server when its connected
func (b *BTCC) OnConnect(output chan socketio.Message) {
if b.Verbose {
log.Printf("%s Connected to Websocket.", b.GetName())
// WsConnect initiates a websocket client connection
func (b *BTCC) WsConnect() error {
if !b.Websocket.IsEnabled() || !b.IsEnabled() {
return errors.New(exchange.WebsocketNotEnabled)
}
currencies := []string{}
for _, x := range b.EnabledPairs {
currency := common.StringToLower(x[3:] + x[0:3])
currencies = append(currencies, currency)
}
endpoints := []string{"marketdata", "grouporder"}
var dialer websocket.Dialer
var err error
for _, x := range endpoints {
for _, y := range currencies {
channel := fmt.Sprintf(`"%s_%s"`, x, y)
if b.Verbose {
log.Printf("%s Websocket subscribing to channel: %s.", b.GetName(), channel)
if b.Websocket.GetProxyAddress() != "" {
prxy, err := url.Parse(b.Websocket.GetProxyAddress())
if err != nil {
return err
}
dialer.Proxy = http.ProxyURL(prxy)
}
b.Conn, _, err = dialer.Dial(b.Websocket.GetWebsocketURL(), http.Header{})
if err != nil {
return err
}
err = b.WsUpdateCurrencyPairs()
if err != nil {
return err
}
go b.WsReadData()
go b.WsHandleData()
err = b.WsSubscribeToOrderbook()
if err != nil {
return err
}
err = b.WsSubcribeToTicker()
if err != nil {
return err
}
return b.WsSubcribeToTrades()
}
// WsReadData reads data from the websocket connection
func (b *BTCC) WsReadData() {
b.Websocket.Wg.Add(1)
defer b.Websocket.Wg.Done()
for {
select {
case <-b.Websocket.ShutdownC:
return
default:
mtx.Lock()
_, resp, err := b.Conn.ReadMessage()
mtx.Unlock()
if err != nil {
b.Websocket.DataHandler <- err
}
b.Websocket.TrafficAlert <- struct{}{}
b.Websocket.Intercomm <- exchange.WebsocketResponse{
Raw: resp,
}
output <- socketio.CreateMessageEvent("subscribe", channel, b.OnMessage, BTCCSocket.Version)
}
}
}
// OnDisconnect alerts when disconnection occurs
func (b *BTCC) OnDisconnect(output chan socketio.Message) {
log.Printf("%s Disconnected from websocket server.. Reconnecting.\n", b.GetName())
b.WebsocketClient()
}
// WsHandleData handles read data
func (b *BTCC) WsHandleData() {
b.Websocket.Wg.Add(1)
defer b.Websocket.Wg.Done()
// OnError alerts when error occurs
func (b *BTCC) OnError() {
log.Printf("%s Error with Websocket connection.. Reconnecting.\n", b.GetName())
b.WebsocketClient()
}
for {
select {
case <-b.Websocket.ShutdownC:
return
// OnMessage if message received and verbose it is printed out
func (b *BTCC) OnMessage(message []byte, output chan socketio.Message) {
if b.Verbose {
log.Printf("%s Websocket message received which isn't handled by default.\n", b.GetName())
log.Println(string(message))
case resp := <-b.Websocket.Intercomm:
var Result WsResponseMain
err := common.JSONDecode(resp.Raw, &Result)
if err != nil {
log.Fatal(err)
}
switch Result.MsgType {
case msgTypeHeartBeat:
case msgTypeGetActiveContracts:
log.Println("Active Contracts")
log.Fatal(string(resp.Raw))
case msgTypeQuote:
log.Println("Quotes")
log.Fatal(string(resp.Raw))
case msgTypeLogin:
log.Println("Login")
log.Fatal(string(resp.Raw))
case msgTypeAccountInfo:
log.Println("Account info")
log.Fatal(string(resp.Raw))
case msgTypeExecReport:
log.Println("Exec Report")
log.Fatal(string(resp.Raw))
case msgTypePlaceOrder:
log.Println("Place order")
log.Fatal(string(resp.Raw))
case msgTypeCancelAllOrders:
log.Println("Cancel All orders")
log.Fatal(string(resp.Raw))
case msgTypeCancelOrder:
log.Println("Cancel order")
log.Fatal(string(resp.Raw))
case msgTypeCancelReplaceOrder:
log.Println("Replace order")
log.Fatal(string(resp.Raw))
case msgTypeGetAccountInfo:
log.Println("Account info")
log.Fatal(string(resp.Raw))
case msgTypeRetrieveOrder:
log.Println("Retrieve order")
log.Fatal(string(resp.Raw))
case msgTypeGetTrades:
var trades WsTrades
err := common.JSONDecode(resp.Raw, &trades)
if err != nil {
b.Websocket.DataHandler <- err
continue
}
case "OrderBook":
// NOTE: This seems to be a websocket update not reflected in
// current API docs, this comes in conjunction with the other
// orderbook feeds
var orderbook WsOrderbookSnapshot
err := common.JSONDecode(resp.Raw, &orderbook)
if err != nil {
b.Websocket.DataHandler <- err
continue
}
switch orderbook.Type {
case "F":
err = b.WsProcessOrderbookSnapshot(orderbook)
if err != nil {
b.Websocket.DataHandler <- err
}
case "I":
err = b.WsProcessOrderbookUpdate(orderbook)
if err != nil {
b.Websocket.DataHandler <- err
}
}
case "SubOrderBookResponse":
case "Ticker":
var ticker WsTicker
err = common.JSONDecode(resp.Raw, &ticker)
if err != nil {
b.Websocket.DataHandler <- err
continue
}
tick := exchange.TickerData{}
tick.AssetType = "SPOT"
tick.ClosePrice = ticker.PrevCls
tick.Exchange = b.GetName()
tick.HighPrice = ticker.High
tick.LowPrice = ticker.Low
tick.OpenPrice = ticker.Open
tick.Pair = pair.NewCurrencyPairFromString(ticker.Symbol)
tick.Quantity = ticker.Volume
timestamp := time.Unix(ticker.Timestamp, 0)
tick.Timestamp = timestamp
b.Websocket.DataHandler <- tick
default:
if common.StringContains(Result.MsgType, "OrderBook") {
var oldOrderbookType WsOrderbookSnapshotOld
err = common.JSONDecode(resp.Raw, &oldOrderbookType)
if err != nil {
b.Websocket.DataHandler <- err
continue
}
symbol := common.SplitStrings(Result.MsgType, ".")
err = b.WsProcessOldOrderbookSnapshot(oldOrderbookType, symbol[1])
if err != nil {
b.Websocket.DataHandler <- err
continue
}
continue
}
}
}
}
}
// OnTicker handles ticker information
func (b *BTCC) OnTicker(message []byte, output chan socketio.Message) {
type Response struct {
Ticker WebsocketTicker `json:"ticker"`
}
var resp Response
err := common.JSONDecode(message, &resp)
// WsSubscribeAllTickers subscribes to a ticker channel
func (b *BTCC) WsSubscribeAllTickers() error {
mtx.Lock()
defer mtx.Unlock()
return b.Conn.WriteJSON(WsOutgoing{
Action: "SubscribeAllTickers",
})
}
// WsUnSubscribeAllTickers unsubscribes from a ticker channel
func (b *BTCC) WsUnSubscribeAllTickers() error {
mtx.Lock()
defer mtx.Unlock()
return b.Conn.WriteJSON(WsOutgoing{
Action: "UnSubscribeAllTickers",
})
}
// WsUpdateCurrencyPairs updates currency pairs from the websocket connection
func (b *BTCC) WsUpdateCurrencyPairs() error {
err := b.WsSubscribeAllTickers()
if err != nil {
log.Println(err)
return
}
}
// OnGroupOrder handles group order information
func (b *BTCC) OnGroupOrder(message []byte, output chan socketio.Message) {
type Response struct {
GroupOrder WebsocketGroupOrder `json:"grouporder"`
}
var resp Response
err := common.JSONDecode(message, &resp)
if err != nil {
log.Println(err)
return
}
}
// OnTrade handles group trade information
func (b *BTCC) OnTrade(message []byte, output chan socketio.Message) {
trade := WebsocketTrade{}
err := common.JSONDecode(message, &trade)
if err != nil {
log.Println(err)
return
}
}
// WebsocketClient initiates a websocket client
func (b *BTCC) WebsocketClient() {
events := make(map[string]func(message []byte, output chan socketio.Message))
events["grouporder"] = b.OnGroupOrder
events["ticker"] = b.OnTicker
events["trade"] = b.OnTrade
BTCCSocket = &socketio.SocketIO{
Version: 1,
OnConnect: b.OnConnect,
OnEvent: events,
OnError: b.OnError,
OnMessage: b.OnMessage,
OnDisconnect: b.OnDisconnect,
return err
}
for b.Enabled && b.Websocket {
err := socketio.ConnectToSocket(btccSocketioAddress, BTCCSocket)
var currencyResponse WsResponseMain
for {
_, resp, err := b.Conn.ReadMessage()
if err != nil {
log.Printf("%s Unable to connect to Websocket. Err: %s\n", b.GetName(), err)
return err
}
b.Websocket.TrafficAlert <- struct{}{}
err = common.JSONDecode(resp, &currencyResponse)
if err != nil {
return err
}
switch currencyResponse.MsgType {
case msgTypeAllTickers:
var tickers WsAllTickerData
err := common.JSONDecode(currencyResponse.Data, &tickers)
if err != nil {
return err
}
var availableTickers []string
for _, tickerData := range tickers {
availableTickers = append(availableTickers, tickerData.Symbol)
}
err = b.UpdateCurrencies(availableTickers, false, true)
if err != nil {
return fmt.Errorf("%s failed to update available currencies. %s",
b.Name,
err)
}
return b.WsUnSubscribeAllTickers()
case "Heartbeat":
default:
return fmt.Errorf("btcc_websocket.go error - Updating currency pairs resp incorrect: %s",
string(resp))
}
}
}
// WsSubscribeToOrderbook subscribes to an orderbook channel
func (b *BTCC) WsSubscribeToOrderbook() error {
mtx.Lock()
defer mtx.Unlock()
for _, pair := range b.GetEnabledCurrencies() {
formattedPair := exchange.FormatExchangeCurrency(b.GetName(), pair)
err := b.Conn.WriteJSON(WsOutgoing{
Action: "SubOrderBook",
Symbol: formattedPair.String(),
Len: 100})
if err != nil {
return err
}
}
return nil
}
// WsSubcribeToTicker subscribes to a ticker channel
func (b *BTCC) WsSubcribeToTicker() error {
mtx.Lock()
defer mtx.Unlock()
for _, pair := range b.GetEnabledCurrencies() {
formattedPair := exchange.FormatExchangeCurrency(b.GetName(), pair)
err := b.Conn.WriteJSON(WsOutgoing{
Action: "Subscribe",
Symbol: formattedPair.String(),
})
if err != nil {
return err
}
}
return nil
}
// WsSubcribeToTrades subscribes to a trade channel
func (b *BTCC) WsSubcribeToTrades() error {
mtx.Lock()
defer mtx.Unlock()
for _, pair := range b.GetEnabledCurrencies() {
formattedPair := exchange.FormatExchangeCurrency(b.GetName(), pair)
err := b.Conn.WriteJSON(WsOutgoing{
Action: "GetTrades",
Symbol: formattedPair.String(),
Count: 100,
})
if err != nil {
return err
}
}
return nil
}
// WsProcessOrderbookSnapshot processes a new orderbook snapshot
func (b *BTCC) WsProcessOrderbookSnapshot(ob WsOrderbookSnapshot) error {
var asks, bids []orderbook.Item
for _, data := range ob.List {
var newSize float64
switch data.Size.(type) {
case float64:
newSize = data.Size.(float64)
case string:
var err error
newSize, err = strconv.ParseFloat(data.Size.(string), 64)
if err != nil {
return err
}
}
if data.Side == "1" {
asks = append(asks, orderbook.Item{Price: data.Price, Amount: newSize})
continue
}
log.Printf("%s Disconnected from Websocket.\n", b.GetName())
bids = append(bids, orderbook.Item{Price: data.Price, Amount: newSize})
}
var newOrderbook orderbook.Base
newOrderbook.Asks = asks
newOrderbook.AssetType = "SPOT"
newOrderbook.Bids = bids
newOrderbook.CurrencyPair = ob.Symbol
newOrderbook.LastUpdated = time.Now()
newOrderbook.Pair = pair.NewCurrencyPairFromString(ob.Symbol)
err := b.Websocket.Orderbook.LoadSnapshot(newOrderbook, b.GetName())
if err != nil {
return err
}
b.Websocket.DataHandler <- exchange.WebsocketOrderbookUpdate{
Exchange: b.GetName(),
Asset: "SPOT",
Pair: pair.NewCurrencyPairFromString(ob.Symbol),
}
return nil
}
// WsProcessOrderbookUpdate processes an orderbook update
func (b *BTCC) WsProcessOrderbookUpdate(ob WsOrderbookSnapshot) error {
var asks, bids []orderbook.Item
for _, data := range ob.List {
var newSize float64
switch data.Size.(type) {
case float64:
newSize = data.Size.(float64)
case string:
var err error
newSize, err = strconv.ParseFloat(data.Size.(string), 64)
if err != nil {
return err
}
}
if data.Side == "1" {
if newSize < 0 {
asks = append(asks, orderbook.Item{Price: data.Price, Amount: 0})
continue
}
asks = append(asks, orderbook.Item{Price: data.Price, Amount: newSize})
continue
}
if newSize < 0 {
bids = append(bids, orderbook.Item{Price: data.Price, Amount: 0})
continue
}
bids = append(bids, orderbook.Item{Price: data.Price, Amount: newSize})
}
p := pair.NewCurrencyPairFromString(ob.Symbol)
err := b.Websocket.Orderbook.Update(bids, asks, p, time.Now(), b.GetName(), "SPOT")
if err != nil {
return err
}
b.Websocket.DataHandler <- exchange.WebsocketOrderbookUpdate{
Exchange: b.GetName(),
Asset: "SPOT",
Pair: pair.NewCurrencyPairFromString(ob.Symbol),
}
return nil
}
// WsProcessOldOrderbookSnapshot processes an old orderbook snapshot
func (b *BTCC) WsProcessOldOrderbookSnapshot(ob WsOrderbookSnapshotOld, symbol string) error {
var asks, bids []orderbook.Item
askData, _ := ob.Data["Asks"]
bidData, _ := ob.Data["Bids"]
for _, ask := range askData {
data := ask.([]interface{})
var price, amount float64
switch data[0].(type) {
case string:
var err error
price, err = strconv.ParseFloat(data[0].(string), 64)
if err != nil {
return err
}
case float64:
price = data[0].(float64)
}
switch data[0].(type) {
case string:
var err error
amount, err = strconv.ParseFloat(data[0].(string), 64)
if err != nil {
return err
}
case float64:
amount = data[0].(float64)
}
asks = append(asks, orderbook.Item{
Price: price,
Amount: amount,
})
}
for _, bid := range bidData {
data := bid.([]interface{})
var price, amount float64
switch data[1].(type) {
case string:
var err error
price, err = strconv.ParseFloat(data[1].(string), 64)
if err != nil {
return err
}
case float64:
price = data[1].(float64)
}
switch data[1].(type) {
case string:
var err error
amount, err = strconv.ParseFloat(data[1].(string), 64)
if err != nil {
return err
}
case float64:
amount = data[1].(float64)
}
bids = append(bids, orderbook.Item{
Price: price,
Amount: amount,
})
}
p := pair.NewCurrencyPairFromString(symbol)
err := b.Websocket.Orderbook.Update(bids, asks, p, time.Now(), b.GetName(), "SPOT")
if err != nil {
return err
}
b.Websocket.DataHandler <- exchange.WebsocketOrderbookUpdate{
Exchange: b.GetName(),
Pair: p,
Asset: "SPOT",
}
return nil
}

View File

@@ -25,15 +25,11 @@ func (b *BTCC) Start(wg *sync.WaitGroup) {
// Run implements the BTCC wrapper
func (b *BTCC) Run() {
if b.Verbose {
log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket))
log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket.IsEnabled()))
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)
}
if b.Websocket {
go b.WebsocketClient()
}
if common.StringDataContains(b.EnabledPairs, "CNY") || common.StringDataContains(b.AvailablePairs, "CNY") || common.StringDataContains(b.BaseCurrencies, "CNY") {
log.Println("WARNING: BTCC only supports BTCUSD now, upgrading available, enabled and base currencies to BTCUSD/USD")
pairs := []string{"BTCUSD"}
@@ -69,82 +65,89 @@ func (b *BTCC) Run() {
// UpdateTicker updates and returns the ticker for a currency pair
func (b *BTCC) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
var tickerPrice ticker.Price
tick, err := b.GetTicker(exchange.FormatExchangeCurrency(b.GetName(), p).String())
if err != nil {
return tickerPrice, err
}
tickerPrice.Pair = p
tickerPrice.Ask = tick.AskPrice
tickerPrice.Bid = tick.BidPrice
tickerPrice.Low = tick.Low
tickerPrice.Last = tick.Last
tickerPrice.Volume = tick.Volume24H
tickerPrice.High = tick.High
ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
return ticker.GetTicker(b.Name, p, assetType)
// var tickerPrice ticker.Price
// tick, err := b.GetTicker(exchange.FormatExchangeCurrency(b.GetName(), p).String())
// if err != nil {
// return tickerPrice, err
// }
// tickerPrice.Pair = p
// tickerPrice.Ask = tick.AskPrice
// tickerPrice.Bid = tick.BidPrice
// tickerPrice.Low = tick.Low
// tickerPrice.Last = tick.Last
// tickerPrice.Volume = tick.Volume24H
// tickerPrice.High = tick.High
// ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
// return ticker.GetTicker(b.Name, p, assetType)
return ticker.Price{}, errors.New("REST NOT SUPPORTED")
}
// GetTickerPrice returns the ticker for a currency pair
func (b *BTCC) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
tickerNew, err := ticker.GetTicker(b.GetName(), p, assetType)
if err != nil {
return b.UpdateTicker(p, assetType)
}
return tickerNew, nil
// tickerNew, err := ticker.GetTicker(b.GetName(), p, assetType)
// if err != nil {
// return b.UpdateTicker(p, assetType)
// }
// return tickerNew, nil
return ticker.Price{}, errors.New("REST NOT SUPPORTED")
}
// GetOrderbookEx returns the orderbook for a currency pair
func (b *BTCC) 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
// ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType)
// if err != nil {
// return b.UpdateOrderbook(p, assetType)
// }
// return ob, nil
return orderbook.Base{}, errors.New("REST NOT SUPPORTED")
}
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (b *BTCC) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
var orderBook orderbook.Base
orderbookNew, err := b.GetOrderBook(exchange.FormatExchangeCurrency(b.GetName(), p).String(), 100)
if err != nil {
return orderBook, err
}
// var orderBook orderbook.Base
// orderbookNew, err := b.GetOrderBook(exchange.FormatExchangeCurrency(b.GetName(), p).String(), 100)
// 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.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]})
}
// 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)
// orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType)
// return orderbook.GetOrderbook(b.Name, p, assetType)
return orderbook.Base{}, errors.New("REST NOT SUPPORTED")
}
// GetExchangeAccountInfo : Retrieves balances for all enabled currencies for
// the Kraken exchange - TODO
func (b *BTCC) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
var response exchange.AccountInfo
response.ExchangeName = b.GetName()
return response, nil
// var response exchange.AccountInfo
// response.ExchangeName = b.GetName()
// return response, nil
return exchange.AccountInfo{}, errors.New("REST NOT SUPPORTED")
}
// GetExchangeFundTransferHistory returns funding history, deposits and
// withdrawals
func (b *BTCC) GetExchangeFundTransferHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, errors.New("not supported on exchange")
// var fundHistory []exchange.FundHistory
// return fundHistory, errors.New("not supported on exchange")
return nil, errors.New("REST NOT SUPPORTED")
}
// GetExchangeHistory returns historic trade data since exchange opening.
func (b *BTCC) GetExchangeHistory(p pair.CurrencyPair, assetType string) ([]exchange.TradeHistory, error) {
var resp []exchange.TradeHistory
// var resp []exchange.TradeHistory
return resp, errors.New("trade history not yet implemented")
// return resp, errors.New("trade history not yet implemented")
return nil, errors.New("REST NOT SUPPORTED")
}
// SubmitExchangeOrder submits a new order
@@ -196,3 +199,8 @@ func (b *BTCC) WithdrawFiatExchangeFunds(currency pair.CurrencyItem, amount floa
func (b *BTCC) WithdrawFiatExchangeFundsToInternationalBank(currency pair.CurrencyItem, amount float64) (string, error) {
return "", errors.New("not yet implemented")
}
// GetWebsocket returns a pointer to the exchange websocket
func (b *BTCC) GetWebsocket() (*exchange.Websocket, error) {
return b.Websocket, nil
}