mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-30 23:16:52 +00:00
* Added new base logger * updated example and test configs * updated exchange helpers restful router & server * logPath is now passed to the logger to remove dependency on common package * updated everything besides exchanges to use new logger * alphapoint to bitmex done * updated bitmex bitstamp bittrex btcc and also performance changes to logger * btcmarkets coinbase coinut exmo gateio wrappers updated * gateio and gemini logger updated * hitbtc huobi itbit & kraken updated * All exchanges updatd * return correct error for disabled websocket * don't disconnect client on invalid json * updated router internal logging * log.Fatal to t.Error for tests * Changed from fatal to error failure to set maxprocs * output ANSI codes for everything but windows for now due to lack of windows support * added error handling to logger and unit tests * clear wording on print -> log.print * added benchmark test * cleaned up import sections * Updated logger based on PR requests (added default config options on failure/setting errors) * ah this should fix travici enc config issue * Load entire config and clear out logging to hopefully fix travisci issue * wording & test error handling * fixed formatting issues based on feedback * fixed formatting issues based on feedback * changed CheckDir to use mkdirall instead of mkdir and other changes based on feedback
414 lines
12 KiB
Go
414 lines
12 KiB
Go
package lakebtc
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/thrasher-/gocryptotrader/common"
|
|
"github.com/thrasher-/gocryptotrader/config"
|
|
"github.com/thrasher-/gocryptotrader/currency/symbol"
|
|
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
|
"github.com/thrasher-/gocryptotrader/exchanges/request"
|
|
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
|
log "github.com/thrasher-/gocryptotrader/logger"
|
|
)
|
|
|
|
const (
|
|
lakeBTCAPIURL = "https://api.lakebtc.com/api_v2"
|
|
lakeBTCAPIVersion = "2"
|
|
lakeBTCTicker = "ticker"
|
|
lakeBTCOrderbook = "bcorderbook"
|
|
lakeBTCTrades = "bctrades"
|
|
lakeBTCGetAccountInfo = "getAccountInfo"
|
|
lakeBTCBuyOrder = "buyOrder"
|
|
lakeBTCSellOrder = "sellOrder"
|
|
lakeBTCOpenOrders = "openOrders"
|
|
lakeBTCGetOrders = "getOrders"
|
|
lakeBTCCancelOrder = "cancelOrders"
|
|
lakeBTCGetTrades = "getTrades"
|
|
lakeBTCGetExternalAccounts = "getExternalAccounts"
|
|
lakeBTCCreateWithdraw = "createWithdraw"
|
|
|
|
lakeBTCAuthRate = 0
|
|
lakeBTCUnauth = 0
|
|
)
|
|
|
|
// LakeBTC is the overarching type across the LakeBTC package
|
|
type LakeBTC struct {
|
|
exchange.Base
|
|
}
|
|
|
|
// SetDefaults sets LakeBTC defaults
|
|
func (l *LakeBTC) SetDefaults() {
|
|
l.Name = "LakeBTC"
|
|
l.Enabled = false
|
|
l.TakerFee = 0.2
|
|
l.MakerFee = 0.15
|
|
l.Verbose = false
|
|
l.RESTPollingDelay = 10
|
|
l.APIWithdrawPermissions = exchange.AutoWithdrawCrypto | exchange.WithdrawFiatViaWebsiteOnly
|
|
l.RequestCurrencyPairFormat.Delimiter = ""
|
|
l.RequestCurrencyPairFormat.Uppercase = true
|
|
l.ConfigCurrencyPairFormat.Delimiter = ""
|
|
l.ConfigCurrencyPairFormat.Uppercase = true
|
|
l.AssetTypes = []string{ticker.Spot}
|
|
l.SupportsAutoPairUpdating = true
|
|
l.SupportsRESTTickerBatching = true
|
|
l.Requester = request.New(l.Name,
|
|
request.NewRateLimit(time.Second, lakeBTCAuthRate),
|
|
request.NewRateLimit(time.Second, lakeBTCUnauth),
|
|
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
|
|
l.APIUrlDefault = lakeBTCAPIURL
|
|
l.APIUrl = l.APIUrlDefault
|
|
l.WebsocketInit()
|
|
}
|
|
|
|
// Setup sets exchange configuration profile
|
|
func (l *LakeBTC) Setup(exch config.ExchangeConfig) {
|
|
if !exch.Enabled {
|
|
l.SetEnabled(false)
|
|
} else {
|
|
l.Enabled = true
|
|
l.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
|
l.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
|
l.SetHTTPClientTimeout(exch.HTTPTimeout)
|
|
l.SetHTTPClientUserAgent(exch.HTTPUserAgent)
|
|
l.RESTPollingDelay = exch.RESTPollingDelay
|
|
l.Verbose = exch.Verbose
|
|
l.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
|
l.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
|
l.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
|
err := l.SetCurrencyPairFormat()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = l.SetAssetTypes()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = l.SetAutoPairDefaults()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = l.SetAPIURL(exch)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = l.SetClientProxyAddress(exch.ProxyAddress)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetTradablePairs returns a list of available pairs from the exchange
|
|
func (l *LakeBTC) GetTradablePairs() ([]string, error) {
|
|
result, err := l.GetTicker()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var currencies []string
|
|
for x := range result {
|
|
currencies = append(currencies, common.StringToUpper(x))
|
|
}
|
|
|
|
return currencies, nil
|
|
}
|
|
|
|
// GetTicker returns the current ticker from lakeBTC
|
|
func (l *LakeBTC) GetTicker() (map[string]Ticker, error) {
|
|
response := make(map[string]TickerResponse)
|
|
path := fmt.Sprintf("%s/%s", l.APIUrl, lakeBTCTicker)
|
|
|
|
if err := l.SendHTTPRequest(path, &response); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make(map[string]Ticker)
|
|
|
|
var addresses []string
|
|
for k, v := range response {
|
|
var ticker Ticker
|
|
key := common.StringToUpper(k)
|
|
if v.Ask != nil {
|
|
ticker.Ask, _ = strconv.ParseFloat(v.Ask.(string), 64)
|
|
}
|
|
if v.Bid != nil {
|
|
ticker.Bid, _ = strconv.ParseFloat(v.Bid.(string), 64)
|
|
}
|
|
if v.High != nil {
|
|
ticker.High, _ = strconv.ParseFloat(v.High.(string), 64)
|
|
}
|
|
if v.Last != nil {
|
|
ticker.Last, _ = strconv.ParseFloat(v.Last.(string), 64)
|
|
}
|
|
if v.Low != nil {
|
|
ticker.Low, _ = strconv.ParseFloat(v.Low.(string), 64)
|
|
}
|
|
if v.Volume != nil {
|
|
ticker.Volume, _ = strconv.ParseFloat(v.Volume.(string), 64)
|
|
}
|
|
result[key] = ticker
|
|
addresses = append(addresses, key)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetOrderBook returns the order book from LakeBTC
|
|
func (l *LakeBTC) GetOrderBook(currency string) (Orderbook, error) {
|
|
type Response struct {
|
|
Bids [][]string `json:"bids"`
|
|
Asks [][]string `json:"asks"`
|
|
}
|
|
path := fmt.Sprintf("%s/%s?symbol=%s", l.APIUrl, lakeBTCOrderbook, common.StringToLower(currency))
|
|
resp := Response{}
|
|
err := l.SendHTTPRequest(path, &resp)
|
|
if err != nil {
|
|
return Orderbook{}, err
|
|
}
|
|
orderbook := Orderbook{}
|
|
|
|
for _, x := range resp.Bids {
|
|
price, err := strconv.ParseFloat(x[0], 64)
|
|
if err != nil {
|
|
log.Error(err)
|
|
continue
|
|
}
|
|
amount, err := strconv.ParseFloat(x[1], 64)
|
|
if err != nil {
|
|
log.Error(err)
|
|
continue
|
|
}
|
|
orderbook.Bids = append(orderbook.Bids, OrderbookStructure{price, amount})
|
|
}
|
|
|
|
for _, x := range resp.Asks {
|
|
price, err := strconv.ParseFloat(x[0], 64)
|
|
if err != nil {
|
|
log.Error(err)
|
|
continue
|
|
}
|
|
amount, err := strconv.ParseFloat(x[1], 64)
|
|
if err != nil {
|
|
log.Error(err)
|
|
continue
|
|
}
|
|
orderbook.Asks = append(orderbook.Asks, OrderbookStructure{price, amount})
|
|
}
|
|
return orderbook, nil
|
|
}
|
|
|
|
// GetTradeHistory returns the trade history for a given currency pair
|
|
func (l *LakeBTC) GetTradeHistory(currency string) ([]TradeHistory, error) {
|
|
path := fmt.Sprintf("%s/%s?symbol=%s", l.APIUrl, lakeBTCTrades, common.StringToLower(currency))
|
|
resp := []TradeHistory{}
|
|
|
|
return resp, l.SendHTTPRequest(path, &resp)
|
|
}
|
|
|
|
// GetAccountInformation returns your current account information
|
|
func (l *LakeBTC) GetAccountInformation() (AccountInfo, error) {
|
|
resp := AccountInfo{}
|
|
|
|
return resp, l.SendAuthenticatedHTTPRequest(lakeBTCGetAccountInfo, "", &resp)
|
|
}
|
|
|
|
// Trade executes an order on the exchange and returns trade inforamtion or an
|
|
// error
|
|
func (l *LakeBTC) Trade(isBuyOrder bool, amount, price float64, currency string) (Trade, error) {
|
|
resp := Trade{}
|
|
params := strconv.FormatFloat(price, 'f', -1, 64) + "," + strconv.FormatFloat(amount, 'f', -1, 64) + "," + currency
|
|
|
|
if isBuyOrder {
|
|
if err := l.SendAuthenticatedHTTPRequest(lakeBTCBuyOrder, params, &resp); err != nil {
|
|
return resp, err
|
|
}
|
|
} else {
|
|
if err := l.SendAuthenticatedHTTPRequest(lakeBTCSellOrder, params, &resp); err != nil {
|
|
return resp, err
|
|
}
|
|
}
|
|
|
|
if resp.Result != "order received" {
|
|
return resp, fmt.Errorf("Unexpected result: %s", resp.Result)
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// GetOpenOrders returns all open orders associated with your account
|
|
func (l *LakeBTC) GetOpenOrders() ([]OpenOrders, error) {
|
|
orders := []OpenOrders{}
|
|
|
|
return orders, l.SendAuthenticatedHTTPRequest(lakeBTCOpenOrders, "", &orders)
|
|
}
|
|
|
|
// GetOrders returns your orders
|
|
func (l *LakeBTC) GetOrders(orders []int64) ([]Orders, error) {
|
|
var ordersStr []string
|
|
for _, x := range orders {
|
|
ordersStr = append(ordersStr, strconv.FormatInt(x, 10))
|
|
}
|
|
|
|
resp := []Orders{}
|
|
return resp,
|
|
l.SendAuthenticatedHTTPRequest(lakeBTCGetOrders, common.JoinStrings(ordersStr, ","), &resp)
|
|
}
|
|
|
|
// CancelExistingOrder cancels an order by ID number and returns an error
|
|
func (l *LakeBTC) CancelExistingOrder(orderID int64) error {
|
|
type Response struct {
|
|
Result bool `json:"Result"`
|
|
}
|
|
|
|
resp := Response{}
|
|
params := strconv.FormatInt(orderID, 10)
|
|
err := l.SendAuthenticatedHTTPRequest(lakeBTCCancelOrder, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.Result != true {
|
|
return errors.New("unable to cancel order")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CancelExistingOrders cancels an order by ID number and returns an error
|
|
func (l *LakeBTC) CancelExistingOrders(orderIDs []string) error {
|
|
type Response struct {
|
|
Result bool `json:"Result"`
|
|
}
|
|
|
|
resp := Response{}
|
|
params := common.JoinStrings(orderIDs, ",")
|
|
err := l.SendAuthenticatedHTTPRequest(lakeBTCCancelOrder, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.Result != true {
|
|
return fmt.Errorf("unable to cancel order(s): %v", orderIDs)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetTrades returns trades associated with your account by timestamp
|
|
func (l *LakeBTC) GetTrades(timestamp int64) ([]AuthenticatedTradeHistory, error) {
|
|
params := ""
|
|
if timestamp != 0 {
|
|
params = strconv.FormatInt(timestamp, 10)
|
|
}
|
|
|
|
trades := []AuthenticatedTradeHistory{}
|
|
return trades, l.SendAuthenticatedHTTPRequest(lakeBTCGetTrades, params, &trades)
|
|
}
|
|
|
|
// GetExternalAccounts returns your external accounts WARNING: Only for BTC!
|
|
func (l *LakeBTC) GetExternalAccounts() ([]ExternalAccounts, error) {
|
|
resp := []ExternalAccounts{}
|
|
|
|
return resp, l.SendAuthenticatedHTTPRequest(lakeBTCGetExternalAccounts, "", &resp)
|
|
}
|
|
|
|
// CreateWithdraw allows your to withdraw to external account WARNING: Only for
|
|
// BTC!
|
|
func (l *LakeBTC) CreateWithdraw(amount float64, accountID string) (Withdraw, error) {
|
|
resp := Withdraw{}
|
|
params := strconv.FormatFloat(amount, 'f', -1, 64) + ",btc," + accountID
|
|
|
|
err := l.SendAuthenticatedHTTPRequest(lakeBTCCreateWithdraw, params, &resp)
|
|
if err != nil {
|
|
return Withdraw{}, err
|
|
}
|
|
if len(resp.Error) > 0 {
|
|
return resp, errors.New(resp.Error)
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// SendHTTPRequest sends an unauthenticated http request
|
|
func (l *LakeBTC) SendHTTPRequest(path string, result interface{}) error {
|
|
return l.SendPayload("GET", path, nil, nil, result, false, l.Verbose)
|
|
}
|
|
|
|
// SendAuthenticatedHTTPRequest sends an autheticated HTTP request to a LakeBTC
|
|
func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result interface{}) (err error) {
|
|
if !l.AuthenticatedAPISupport {
|
|
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name)
|
|
}
|
|
|
|
if l.Nonce.Get() == 0 {
|
|
l.Nonce.Set(time.Now().UnixNano())
|
|
} else {
|
|
l.Nonce.Inc()
|
|
}
|
|
|
|
req := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=1&method=%s¶ms=%s", l.Nonce.String(), l.APIKey, method, params)
|
|
hmac := common.GetHMAC(common.HashSHA1, []byte(req), []byte(l.APISecret))
|
|
|
|
if l.Verbose {
|
|
log.Debugf("Sending POST request to %s calling method %s with params %s\n", l.APIUrl, method, req)
|
|
}
|
|
|
|
postData := make(map[string]interface{})
|
|
postData["method"] = method
|
|
postData["id"] = 1
|
|
postData["params"] = common.SplitStrings(params, ",")
|
|
|
|
data, err := common.JSONEncode(postData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
headers := make(map[string]string)
|
|
headers["Json-Rpc-Tonce"] = l.Nonce.String()
|
|
headers["Authorization"] = "Basic " + common.Base64Encode([]byte(l.APIKey+":"+common.HexEncodeToString(hmac)))
|
|
headers["Content-Type"] = "application/json-rpc"
|
|
|
|
return l.SendPayload("POST", l.APIUrl, headers, strings.NewReader(string(data)), result, true, l.Verbose)
|
|
}
|
|
|
|
// GetFee returns an estimate of fee based on type of transaction
|
|
func (l *LakeBTC) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) {
|
|
var fee float64
|
|
switch feeBuilder.FeeType {
|
|
case exchange.CryptocurrencyTradeFee:
|
|
fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker)
|
|
case exchange.CyptocurrencyDepositFee:
|
|
fee = getCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency)
|
|
case exchange.InternationalBankWithdrawalFee:
|
|
// fees for withdrawals are dynamic. They cannot be calculated in advance
|
|
// As they are manually performed via the website, it can only be determined when submitting the request
|
|
}
|
|
|
|
if fee < 0 {
|
|
fee = 0
|
|
}
|
|
|
|
return fee, nil
|
|
}
|
|
|
|
func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) {
|
|
if isMaker {
|
|
// TODO: Volume based fee calculation
|
|
fee = 0.0015
|
|
} else {
|
|
fee = 0.002
|
|
}
|
|
|
|
return fee * amount * purchasePrice
|
|
}
|
|
|
|
func getCryptocurrencyWithdrawalFee(currency string) (fee float64) {
|
|
if currency == symbol.BTC {
|
|
fee = 0.001
|
|
}
|
|
return fee
|
|
}
|