mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-01 15:10:44 +00:00
* ALMOST THERE * more api wips * more api thingz * testing n more api wipz * more apiz * more wips * what is goin on * more wips * whip n testing * testing * testing no keys * remove log * kraken is broken ugh * still broken * fixing auth funcs + usdtm api docs * wip * api stuffs * whip * more wips * whip * more wip * api wip n testing * wip * wip * unsaved * wip n testing * wip * wip * wip * wip * wip * wip * wip * wip * wip * whip * wrapper authenticated functions * adding asset type and fixing dependencies * wip * binance auth wrapper start * wrapper functionality * wip * wip * wip * wrapper cancel functions * order submission for wrappers * wip * more error fixing and nits * websocket beginning n error fix * wip * WOW * glorious n shazzy nits * useless nits * wip * fixing things * merge stuffs * crapveyor * crapveyor rebuild * probably broke more things than he fixed * rm lns n other thangs * hope * please * stop it * done * ofcourse * rm vb * fix lbank * appveyor please * float lev * DONT ASK RYAN FOR HELP EVER * wip * wip * endpoint upgrades continued * path upgrade * NeeeNeeeNeeeNeeeNING * fix stuffs * fixing time issue * fixing broken funcs * glorious nits * shaz changes * fixing errors for fundmon * more error fixing for fundmon * test running past 30s * basic changes * THX AGAIN SHAZBERT * path system upgrade * config upgrade * unsaved stuffs * broken wip config upgrade * path system upgrade contd. * path system upgrade contd * path upgrade ready for review * testing verbose removed * linter stuffs * appveyor stuffs * appveyor stuff * fixed? * bugfix * wip * broken stuff * fix test * wierd hack fix * appveyor pls stop * error found * more useless nits * bitmex err * broken wip * broken wip path upgrade change to uint32 * changed url lookups to uint * WOW * ready4review * config fixed HOPEFULLY * config fix and glorious changes * efficient way of getting orders and open orders * binance wrapper logic fixing * testing, adding tests and fixing lot of errrrrs * merge master * appveyor stuffs * appveyor stuffs * fmt * test * octalLiteral issue fix? * octalLiteral fix? * rm vb * prnt ln to restart * adding testz * test fixzzz * READY FOR REVIEW * Actually ready now * FORMATTING * addressing shazzy n glorious nits * crapveyor * rm vb * small change * fixing err * shazbert nits * review changes * requested changes * more requested changes * noo * last nit fixes * restart appveyor * improving test cov * Update .golangci.yml * shazbert changes * moving pair formatting * format pair update wip * path upgrade complete * error fix * appveyor linters * more linters * remove testexch * more formatting changes * changes * shazbert changes * checking older requested changes to ensure completion * wip * fixing broken code * error fix * all fixed * additional changes * more changes * remove commented code * ftx margin api * appveyor fixes * more appveyor issues + test addition * more appveyor issues + test addition * remove unnecessary * testing * testing, fixing okex api, error fix * git merge fix * go sum * glorious changes and error fix * rm vb * more glorious changes and go mod tidy * fixed now * okex testing upgrade * old config migration and batch fetching fix * added test * glorious requested changes WIP * tested and fixed * go fmted * go fmt and test fix * additional funcs and tests for fundingRates * OKEX tested and fixed * appveyor fixes * ineff assign * 1 glorious change * error fix * typo * shazbert changes * glorious code changes and path fixing huobi WIP * adding assetType to accountinfo functions * fixing panic * panic fix and updating account info wrappers WIP * updateaccountinfo updated * testing WIP binance USDT n Coin Margined and Kraken Futures * auth functions tested and fixed * added test * config reverted * shazbert and glorious changes * shazbert and glorious changes * latest changes and portfolio update * go fmt change: * remove commented codes * improved error checking * index out of range fix * rm ln * critical nit * glorious latest changes * appveyor changes * shazbert change * easier readability * latest glorious changes * shadow dec * assetstore updated * last change * another last change * merge changes * go mod tidy * thrasher requested changes wip * improving struct layouts * appveyor go fmt * remove unnecessary code * shazbert changes * small change * oopsie * tidy * configtest reverted * error fix * oopsie * for what * test patch fix * insecurities * fixing tests * fix config
383 lines
11 KiB
Go
383 lines
11 KiB
Go
package lakebtc
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
|
"github.com/thrasher-corp/gocryptotrader/log"
|
|
)
|
|
|
|
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"
|
|
)
|
|
|
|
// LakeBTC is the overarching type across the LakeBTC package
|
|
type LakeBTC struct {
|
|
exchange.Base
|
|
WebsocketConn
|
|
}
|
|
|
|
// GetTicker returns the current ticker from lakeBTC
|
|
func (l *LakeBTC) GetTicker() (map[string]Ticker, error) {
|
|
response := make(map[string]TickerResponse)
|
|
path := fmt.Sprintf("/%s", lakeBTCTicker)
|
|
|
|
err := l.SendHTTPRequest(exchange.RestSpot, path, &response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make(map[string]Ticker)
|
|
|
|
for k, v := range response {
|
|
var tick Ticker
|
|
key := strings.ToUpper(k)
|
|
if v.Ask != nil {
|
|
tick.Ask, err = strconv.ParseFloat(v.Ask.(string), 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if v.Bid != nil {
|
|
tick.Bid, err = strconv.ParseFloat(v.Bid.(string), 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if v.High != nil {
|
|
tick.High, err = strconv.ParseFloat(v.High.(string), 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if v.Last != nil {
|
|
tick.Last, err = strconv.ParseFloat(v.Last.(string), 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if v.Low != nil {
|
|
tick.Low, err = strconv.ParseFloat(v.Low.(string), 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if v.Volume != nil {
|
|
tick.Volume, err = strconv.ParseFloat(v.Volume.(string), 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
result[key] = tick
|
|
}
|
|
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?symbol=%s", lakeBTCOrderbook, strings.ToLower(currency))
|
|
resp := Response{}
|
|
err := l.SendHTTPRequest(exchange.RestSpot, 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(log.ExchangeSys, err)
|
|
continue
|
|
}
|
|
amount, err := strconv.ParseFloat(x[1], 64)
|
|
if err != nil {
|
|
log.Error(log.ExchangeSys, 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(log.ExchangeSys, err)
|
|
continue
|
|
}
|
|
amount, err := strconv.ParseFloat(x[1], 64)
|
|
if err != nil {
|
|
log.Error(log.ExchangeSys, 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) {
|
|
v := url.Values{}
|
|
v.Set("symbol", strings.ToLower(currency))
|
|
path := fmt.Sprintf("/%s?%s", lakeBTCTrades, v.Encode())
|
|
var resp []TradeHistory
|
|
return resp, l.SendHTTPRequest(exchange.RestSpot, path, &resp)
|
|
}
|
|
|
|
// GetAccountInformation returns your current account information
|
|
func (l *LakeBTC) GetAccountInformation() (AccountInfo, error) {
|
|
resp := AccountInfo{}
|
|
return resp, l.SendAuthenticatedHTTPRequest(exchange.RestSpot, 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(exchange.RestSpot, lakeBTCBuyOrder, params, &resp); err != nil {
|
|
return resp, err
|
|
}
|
|
} else {
|
|
if err := l.SendAuthenticatedHTTPRequest(exchange.RestSpot, 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) {
|
|
var orders []OpenOrders
|
|
|
|
return orders, l.SendAuthenticatedHTTPRequest(exchange.RestSpot, 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))
|
|
}
|
|
|
|
var resp []Orders
|
|
return resp,
|
|
l.SendAuthenticatedHTTPRequest(exchange.RestSpot, lakeBTCGetOrders, strings.Join(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(exchange.RestSpot, lakeBTCCancelOrder, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Result {
|
|
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 := strings.Join(orderIDs, ",")
|
|
err := l.SendAuthenticatedHTTPRequest(exchange.RestSpot, lakeBTCCancelOrder, params, &resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Result {
|
|
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)
|
|
}
|
|
|
|
var trades []AuthenticatedTradeHistory
|
|
return trades, l.SendAuthenticatedHTTPRequest(exchange.RestSpot, lakeBTCGetTrades, params, &trades)
|
|
}
|
|
|
|
// GetExternalAccounts returns your external accounts WARNING: Only for BTC!
|
|
func (l *LakeBTC) GetExternalAccounts() ([]ExternalAccounts, error) {
|
|
var resp []ExternalAccounts
|
|
|
|
return resp, l.SendAuthenticatedHTTPRequest(exchange.RestSpot, 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(exchange.RestSpot, 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(endpoint exchange.URL, path string, result interface{}) error {
|
|
pathURL, err := l.API.Endpoints.GetURL(endpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return l.SendPayload(context.Background(), &request.Item{
|
|
Method: http.MethodGet,
|
|
Path: pathURL + path,
|
|
Result: result,
|
|
Verbose: l.Verbose,
|
|
HTTPDebugging: l.HTTPDebugging,
|
|
HTTPRecording: l.HTTPRecording,
|
|
})
|
|
}
|
|
|
|
// SendAuthenticatedHTTPRequest sends an autheticated HTTP request to a LakeBTC
|
|
func (l *LakeBTC) SendAuthenticatedHTTPRequest(ep exchange.URL, method, params string, result interface{}) (err error) {
|
|
if !l.AllowAuthenticatedRequest() {
|
|
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name)
|
|
}
|
|
endpoint, err := l.API.Endpoints.GetURL(ep)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n := l.Requester.GetNonce(true).String()
|
|
|
|
req := fmt.Sprintf("tonce=%s&accesskey=%s&requestmethod=post&id=1&method=%s¶ms=%s", n, l.API.Credentials.Key, method, params)
|
|
hmac := crypto.GetHMAC(crypto.HashSHA1, []byte(req), []byte(l.API.Credentials.Secret))
|
|
|
|
if l.Verbose {
|
|
log.Debugf(log.ExchangeSys, "Sending POST request to %s calling method %s with params %s\n", endpoint, method, req)
|
|
}
|
|
|
|
postData := make(map[string]interface{})
|
|
postData["method"] = method
|
|
postData["id"] = 1
|
|
postData["params"] = strings.Split(params, ",")
|
|
|
|
data, err := json.Marshal(postData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
headers := make(map[string]string)
|
|
headers["Json-Rpc-Tonce"] = l.Nonce.String()
|
|
headers["Authorization"] = "Basic " + crypto.Base64Encode([]byte(l.API.Credentials.Key+":"+crypto.HexEncodeToString(hmac)))
|
|
headers["Content-Type"] = "application/json-rpc"
|
|
|
|
return l.SendPayload(context.Background(), &request.Item{
|
|
Method: http.MethodPost,
|
|
Path: endpoint,
|
|
Headers: headers,
|
|
Body: strings.NewReader(string(data)),
|
|
Result: result,
|
|
AuthRequest: true,
|
|
NonceEnabled: true,
|
|
Verbose: l.Verbose,
|
|
HTTPDebugging: l.HTTPDebugging,
|
|
HTTPRecording: l.HTTPRecording,
|
|
})
|
|
}
|
|
|
|
// 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.Pair.Base)
|
|
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
|
|
case exchange.OfflineTradeFee:
|
|
fee = getOfflineTradeFee(feeBuilder.PurchasePrice, feeBuilder.Amount)
|
|
}
|
|
|
|
if fee < 0 {
|
|
fee = 0
|
|
}
|
|
|
|
return fee, nil
|
|
}
|
|
|
|
// getOfflineTradeFee calculates the worst case-scenario trading fee
|
|
func getOfflineTradeFee(price, amount float64) float64 {
|
|
return 0.002 * price * amount
|
|
}
|
|
|
|
func 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(c currency.Code) (fee float64) {
|
|
if c == currency.BTC {
|
|
fee = 0.001
|
|
}
|
|
return fee
|
|
}
|