mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
Exchanges: Remove LakeBTC exchange implementation (#713)
* Remove LakeBTC exchange * Remove more LakeBTC remnants * Update LBank comment * go mod tiderinos * Remove LakeBTC from backup.json file
This commit is contained in:
@@ -39,7 +39,6 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
||||
| Huobi.Pro | Yes | Yes | NA |
|
||||
| ItBit | Yes | NA | No |
|
||||
| Kraken | Yes | Yes | NA |
|
||||
| LakeBTC | Yes | Yes | NA |
|
||||
| Lbank | Yes | No | NA |
|
||||
| LocalBitcoins | Yes | NA | NA |
|
||||
| OKCoin International | Yes | Yes | No |
|
||||
|
||||
@@ -46,7 +46,6 @@ const (
|
||||
pathBitstamp = "https://www.bitstamp.net/api/"
|
||||
pathHitBTC = "https://api.hitbtc.com/"
|
||||
pathBitflyer = "https://lightning.bitflyer.com/docs?lang=en"
|
||||
pathLakeBTC = "https://www.lakebtc.com/s/api_v2"
|
||||
pathKraken = "https://www.kraken.com/features/api"
|
||||
pathAlphaPoint = "https://alphapoint.github.io/slate/#introduction"
|
||||
pathYobit = "https://www.yobit.net/en/api/"
|
||||
@@ -478,8 +477,6 @@ func checkChangeLog(htmlData *HTMLScrapingData) (string, error) {
|
||||
dataStrings, err = htmlScrapeHitBTC(htmlData)
|
||||
case pathBitflyer:
|
||||
dataStrings, err = htmlScrapeBitflyer(htmlData)
|
||||
case pathLakeBTC:
|
||||
dataStrings, err = htmlScrapeLakeBTC(htmlData)
|
||||
case pathKraken:
|
||||
dataStrings, err = htmlScrapeKraken(htmlData)
|
||||
case pathAlphaPoint:
|
||||
@@ -990,28 +987,6 @@ loop:
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// htmlScrapeLakeBTC gets the check string for LakeBTC Exchange
|
||||
func htmlScrapeLakeBTC(htmlData *HTMLScrapingData) ([]string, error) {
|
||||
temp, err := http.Get(htmlData.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer temp.Body.Close()
|
||||
a, err := ioutil.ReadAll(temp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := regexp.Compile(htmlData.RegExp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
str := r.FindString(string(a))
|
||||
sha := crypto.GetSHA256([]byte(str))
|
||||
var resp []string
|
||||
resp = append(resp, crypto.HexEncodeToString(sha))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// htmlScrapeBitstamp gets the check string for Bitstamp Exchange
|
||||
func htmlScrapeBitstamp(htmlData *HTMLScrapingData) ([]string, error) {
|
||||
temp, err := http.Get(htmlData.Path)
|
||||
|
||||
@@ -344,22 +344,6 @@ func TestHTMLItBit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTMLLakeBTC(t *testing.T) {
|
||||
t.Parallel()
|
||||
data := HTMLScrapingData{TokenData: "div",
|
||||
Key: "class",
|
||||
Val: "flash-message",
|
||||
TokenDataEnd: "h2",
|
||||
TextTokenData: "h1",
|
||||
DateFormat: "",
|
||||
RegExp: `APIv\d{1}`,
|
||||
Path: "https://www.lakebtc.com/s/api_v2"}
|
||||
_, err := htmlScrapeLakeBTC(&data)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTMLScrapeExmo(t *testing.T) {
|
||||
t.Parallel()
|
||||
data := HTMLScrapingData{RegExp: `Last updated on [\s\S]*, 20\d{2}`,
|
||||
|
||||
@@ -270,23 +270,6 @@
|
||||
},
|
||||
"Disabled": false
|
||||
},
|
||||
{
|
||||
"Name": "LakeBTC",
|
||||
"CheckType": "HTML String Check",
|
||||
"Data": {
|
||||
"HTMLData": {
|
||||
"TokenData": "div",
|
||||
"Key": "class",
|
||||
"Val": "flash-message",
|
||||
"TokenDataEnd": "h2",
|
||||
"TextTokenData": "h1",
|
||||
"RegExp": "APIv\\d{1}",
|
||||
"CheckString": "de2491b95ef1f6ea334247b13f0f14f6816fb5961cc63acc0542b07fc0336dd8",
|
||||
"Path": "https://www.lakebtc.com/s/api_v2"
|
||||
}
|
||||
},
|
||||
"Disabled": false
|
||||
},
|
||||
{
|
||||
"Name": "Exmo",
|
||||
"CheckType": "HTML String Check",
|
||||
|
||||
@@ -275,23 +275,6 @@
|
||||
},
|
||||
"Disabled": false
|
||||
},
|
||||
{
|
||||
"Name": "LakeBTC",
|
||||
"CheckType": "HTML String Check",
|
||||
"Data": {
|
||||
"HTMLData": {
|
||||
"TokenData": "div",
|
||||
"Key": "class",
|
||||
"Val": "flash-message",
|
||||
"TokenDataEnd": "h2",
|
||||
"TextTokenData": "h1",
|
||||
"RegExp": "APIv\\d{1}",
|
||||
"CheckString": "de2491b95ef1f6ea334247b13f0f14f6816fb5961cc63acc0542b07fc0336dd8",
|
||||
"Path": "https://www.lakebtc.com/s/api_v2"
|
||||
}
|
||||
},
|
||||
"Disabled": false
|
||||
},
|
||||
{
|
||||
"Name": "Exmo",
|
||||
"CheckType": "HTML String Check",
|
||||
|
||||
@@ -275,23 +275,6 @@
|
||||
},
|
||||
"Disabled": false
|
||||
},
|
||||
{
|
||||
"Name": "LakeBTC",
|
||||
"CheckType": "HTML String Check",
|
||||
"Data": {
|
||||
"HTMLData": {
|
||||
"TokenData": "div",
|
||||
"Key": "class",
|
||||
"Val": "flash-message",
|
||||
"TokenDataEnd": "h2",
|
||||
"TextTokenData": "h1",
|
||||
"RegExp": "APIv\\d{1}",
|
||||
"CheckString": "de2491b95ef1f6ea334247b13f0f14f6816fb5961cc63acc0542b07fc0336dd8",
|
||||
"Path": "https://www.lakebtc.com/s/api_v2"
|
||||
}
|
||||
},
|
||||
"Disabled": false
|
||||
},
|
||||
{
|
||||
"Name": "Exmo",
|
||||
"CheckType": "HTML String Check",
|
||||
|
||||
@@ -62,7 +62,6 @@ _b in this context is an `IBotExchange` implemented struct_
|
||||
| Huobi.Pro | Yes | Yes | No |
|
||||
| ItBit | Yes | NA | No |
|
||||
| Kraken | Yes | Yes | No |
|
||||
| LakeBTC | Yes | No | No |
|
||||
| Lbank | Yes | No | Yes |
|
||||
| LocalBitcoins | Yes | NA | No |
|
||||
| OKCoin International | Yes | Yes | No |
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
{{define "exchanges lakebtc" -}}
|
||||
{{template "header" .}}
|
||||
## LakeBTC Exchange
|
||||
|
||||
### Current Features
|
||||
|
||||
+ REST Support
|
||||
+ Websocket Support
|
||||
|
||||
### How to enable
|
||||
|
||||
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
|
||||
|
||||
+ Individual package example below:
|
||||
|
||||
```go
|
||||
// Exchanges will be abstracted out in further updates and examples will be
|
||||
// supplied then
|
||||
```
|
||||
|
||||
### How to do REST public/private calls
|
||||
|
||||
+ If enabled via "configuration".json file the exchange will be added to the
|
||||
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
|
||||
the wrapper interface functions for accessing exchange data. View routines.go
|
||||
for an example of integration usage with GoCryptoTrader. Rudimentary example
|
||||
below:
|
||||
|
||||
main.go
|
||||
```go
|
||||
var l exchange.IBotExchange
|
||||
|
||||
for i := range bot.Exchanges {
|
||||
if bot.Exchanges[i].GetName() == "LakeBTC" {
|
||||
l = bot.Exchanges[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Public calls - wrapper functions
|
||||
|
||||
// Fetches current ticker information
|
||||
tick, err := l.FetchTicker()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := l.FetchOrderbook()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
|
||||
// set and AuthenticatedAPISupport is set to true
|
||||
|
||||
// Fetches current account information
|
||||
accountInfo, err := l.GetAccountInfo()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
+ If enabled via individually importing package, rudimentary example below:
|
||||
|
||||
```go
|
||||
// Public calls
|
||||
|
||||
// Fetches current ticker information
|
||||
ticker, err := l.GetTicker()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := l.GetOrderBook()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Private calls - make sure your APIKEY and APISECRET are set and
|
||||
// AuthenticatedAPISupport is set to true
|
||||
|
||||
// GetUserInfo returns account info
|
||||
accountInfo, err := l.GetUserInfo(...)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Submits an order and the exchange and returns its tradeID
|
||||
tradeID, err := l.Trade(...)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
### Please click GoDocs chevron above to view current GoDoc information for this package
|
||||
{{template "contributions"}}
|
||||
{{template "donations" .}}
|
||||
{{end}}
|
||||
@@ -40,7 +40,6 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
||||
| Huobi.Pro | Yes | Yes | NA |
|
||||
| ItBit | Yes | NA | No |
|
||||
| Kraken | Yes | Yes | NA |
|
||||
| LakeBTC | Yes | Yes | NA |
|
||||
| Lbank | Yes | No | NA |
|
||||
| LocalBitcoins | Yes | NA | NA |
|
||||
| OKCoin International | Yes | Yes | No |
|
||||
|
||||
@@ -136,11 +136,6 @@
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"lakebtc": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"lbank": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
|
||||
@@ -1847,82 +1847,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LakeBTC",
|
||||
"enabled": true,
|
||||
"verbose": false,
|
||||
"httpTimeout": 15000000000,
|
||||
"websocketResponseCheckTimeout": 30000000,
|
||||
"websocketResponseMaxLimit": 7000000000,
|
||||
"websocketTrafficTimeout": 30000000000,
|
||||
"websocketOrderbookBufferLimit": 5,
|
||||
"baseCurrencies": "USD,EUR,HKD,AUD,GBP,NZD,JPY,SGD,NGN,CHF,CAD",
|
||||
"currencyPairs": {
|
||||
"requestFormat": {
|
||||
"uppercase": true
|
||||
},
|
||||
"configFormat": {
|
||||
"uppercase": true
|
||||
},
|
||||
"useGlobalFormat": true,
|
||||
"assetTypes": [
|
||||
"spot"
|
||||
],
|
||||
"pairs": {
|
||||
"spot": {
|
||||
"enabled": "BTCUSD,BTCAUD",
|
||||
"available": "USDSGD,ETHBTC,LTCBTC,BTCNZD,BTCSGD,AUDUSD,BTCJPY,BTCEUR,BTCCAD,BTCNGN,USDCAD,USDCHF,BTCAUD,BTCUSD,USDHKD,USDNGN,EURUSD,BACETH,BTCHKD,GBPUSD,BTCCHF,USDJPY,BTCGBP,XRPBTC,NZDUSD,BCHBTC"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
"authenticatedSupport": false,
|
||||
"authenticatedWebsocketApiSupport": false,
|
||||
"endpoints": {
|
||||
"url": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
|
||||
"urlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
|
||||
"websocketURL": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API"
|
||||
},
|
||||
"credentials": {
|
||||
"key": "Key",
|
||||
"secret": "Secret"
|
||||
},
|
||||
"credentialsValidator": {
|
||||
"requiresKey": true,
|
||||
"requiresSecret": true
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"supports": {
|
||||
"restAPI": true,
|
||||
"restCapabilities": {
|
||||
"tickerBatching": true,
|
||||
"autoPairUpdates": true
|
||||
},
|
||||
"websocketAPI": true,
|
||||
"websocketCapabilities": {}
|
||||
},
|
||||
"enabled": {
|
||||
"autoPairUpdates": true,
|
||||
"websocketAPI": false
|
||||
}
|
||||
},
|
||||
"bankAccounts": [
|
||||
{
|
||||
"enabled": false,
|
||||
"bankName": "",
|
||||
"bankAddress": "",
|
||||
"bankPostalCode": "",
|
||||
"bankPostalCity": "",
|
||||
"bankCountry": "",
|
||||
"accountName": "",
|
||||
"accountNumber": "",
|
||||
"swiftCode": "",
|
||||
"iban": "",
|
||||
"supportedCurrencies": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LBank",
|
||||
"enabled": true,
|
||||
|
||||
@@ -217,7 +217,6 @@ Yes means supported, No means not yet implemented and NA means protocol unsuppor
|
||||
| ItBit | Yes | NA | No |
|
||||
| Kraken | Yes | Yes | NA |
|
||||
| Lbank | Yes | No | NA |
|
||||
| LakeBTC | Yes | No | NA |
|
||||
| LocalBitcoins | Yes | NA | NA |
|
||||
| OKCoin International | Yes | Yes | No |
|
||||
| OKEX | Yes | Yes | No |
|
||||
@@ -249,7 +248,6 @@ var Exchanges = []string{
|
||||
"huobi",
|
||||
"itbit",
|
||||
"kraken",
|
||||
"lakebtc",
|
||||
"lbank",
|
||||
"localbitcoins",
|
||||
"okcoin international",
|
||||
|
||||
@@ -84,8 +84,7 @@ A helper tool [cmd/dbseed](../cmd/dbseed/README.md) has been created for assisti
|
||||
| Huobi | Y |
|
||||
| FTX | Y |
|
||||
| itBIT | |
|
||||
| Kraken | Y |
|
||||
| LakeBTC | |
|
||||
| Kraken | Y |
|
||||
| lBank | Y |
|
||||
| Localbitcoins | |
|
||||
| Okcoin | Y |
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/huobi"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/itbit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kraken"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/lakebtc"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/lbank"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/localbitcoins"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/okcoin"
|
||||
@@ -169,8 +168,6 @@ func (m *ExchangeManager) NewExchangeByName(name string) (exchange.IBotExchange,
|
||||
exch = new(itbit.ItBit)
|
||||
case "kraken":
|
||||
exch = new(kraken.Kraken)
|
||||
case "lakebtc":
|
||||
exch = new(lakebtc.LakeBTC)
|
||||
case "lbank":
|
||||
exch = new(lbank.Lbank)
|
||||
case "localbitcoins":
|
||||
|
||||
@@ -65,7 +65,7 @@ func TestExchangeManagerRemoveExchange(t *testing.T) {
|
||||
|
||||
func TestNewExchangeByName(t *testing.T) {
|
||||
m := SetupExchangeManager()
|
||||
exchanges := []string{"binance", "bitfinex", "bitflyer", "bithumb", "bitmex", "bitstamp", "bittrex", "btc markets", "btse", "coinbene", "coinut", "exmo", "coinbasepro", "ftx", "gateio", "gemini", "hitbtc", "huobi", "itbit", "kraken", "lakebtc", "lbank", "localbitcoins", "okcoin international", "okex", "poloniex", "yobit", "zb", "fake"}
|
||||
exchanges := []string{"binance", "bitfinex", "bitflyer", "bithumb", "bitmex", "bitstamp", "bittrex", "btc markets", "btse", "coinbene", "coinut", "exmo", "coinbasepro", "ftx", "gateio", "gemini", "hitbtc", "huobi", "itbit", "kraken", "lbank", "localbitcoins", "okcoin international", "okex", "poloniex", "yobit", "zb", "fake"}
|
||||
for i := range exchanges {
|
||||
exch, err := m.NewExchangeByName(exchanges[i])
|
||||
if err != nil && exchanges[i] != "fake" {
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
# GoCryptoTrader package Lakebtc
|
||||
|
||||
<img src="/common/gctlogo.png?raw=true" width="350px" height="350px" hspace="70">
|
||||
|
||||
|
||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||
[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/lakebtc)
|
||||
[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
|
||||
|
||||
|
||||
This lakebtc package is part of the GoCryptoTrader codebase.
|
||||
|
||||
## This is still in active development
|
||||
|
||||
You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
|
||||
|
||||
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
|
||||
|
||||
## LakeBTC Exchange
|
||||
|
||||
### Current Features
|
||||
|
||||
+ REST Support
|
||||
+ Websocket Support
|
||||
|
||||
### How to enable
|
||||
|
||||
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
|
||||
|
||||
+ Individual package example below:
|
||||
|
||||
```go
|
||||
// Exchanges will be abstracted out in further updates and examples will be
|
||||
// supplied then
|
||||
```
|
||||
|
||||
### How to do REST public/private calls
|
||||
|
||||
+ If enabled via "configuration".json file the exchange will be added to the
|
||||
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
|
||||
the wrapper interface functions for accessing exchange data. View routines.go
|
||||
for an example of integration usage with GoCryptoTrader. Rudimentary example
|
||||
below:
|
||||
|
||||
main.go
|
||||
```go
|
||||
var l exchange.IBotExchange
|
||||
|
||||
for i := range bot.Exchanges {
|
||||
if bot.Exchanges[i].GetName() == "LakeBTC" {
|
||||
l = bot.Exchanges[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Public calls - wrapper functions
|
||||
|
||||
// Fetches current ticker information
|
||||
tick, err := l.FetchTicker()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := l.FetchOrderbook()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
|
||||
// set and AuthenticatedAPISupport is set to true
|
||||
|
||||
// Fetches current account information
|
||||
accountInfo, err := l.GetAccountInfo()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
+ If enabled via individually importing package, rudimentary example below:
|
||||
|
||||
```go
|
||||
// Public calls
|
||||
|
||||
// Fetches current ticker information
|
||||
ticker, err := l.GetTicker()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := l.GetOrderBook()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Private calls - make sure your APIKEY and APISECRET are set and
|
||||
// AuthenticatedAPISupport is set to true
|
||||
|
||||
// GetUserInfo returns account info
|
||||
accountInfo, err := l.GetUserInfo(...)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Submits an order and the exchange and returns its tradeID
|
||||
tradeID, err := l.Trade(...)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
### Please click GoDocs chevron above to view current GoDoc information for this package
|
||||
|
||||
## Contribution
|
||||
|
||||
Please feel free to submit any pull requests or suggest any desired features to be added.
|
||||
|
||||
When submitting a PR, please abide by our coding guidelines:
|
||||
|
||||
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
||||
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
|
||||
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
|
||||
+ Pull requests need to be based on and opened against the `master` branch.
|
||||
|
||||
## Donations
|
||||
|
||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
||||
|
||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||
|
||||
***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc***
|
||||
@@ -1,382 +0,0 @@
|
||||
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("%s %w", l.Name, exchange.ErrAuthenticatedRequestWithoutCredentialsSet)
|
||||
}
|
||||
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.CryptocurrencyDepositFee:
|
||||
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
|
||||
}
|
||||
@@ -1,502 +0,0 @@
|
||||
package lakebtc
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/core"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
)
|
||||
|
||||
var l LakeBTC
|
||||
|
||||
// Please add your own APIkeys to do correct due diligence testing.
|
||||
const (
|
||||
apiKey = ""
|
||||
apiSecret = ""
|
||||
canManipulateRealOrders = false
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
l.SetDefaults()
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig("../../testdata/configtest.json", true)
|
||||
if err != nil {
|
||||
log.Fatal("LakeBTC load config error", err)
|
||||
}
|
||||
lakebtcConfig, err := cfg.GetExchangeConfig("LakeBTC")
|
||||
if err != nil {
|
||||
log.Fatal("LakeBTC Setup() init error", err)
|
||||
}
|
||||
lakebtcConfig.API.AuthenticatedSupport = true
|
||||
lakebtcConfig.API.Credentials.Key = apiKey
|
||||
lakebtcConfig.API.Credentials.Secret = apiSecret
|
||||
lakebtcConfig.Features.Enabled.Websocket = true
|
||||
l.Websocket = sharedtestvalues.NewTestWebsocket()
|
||||
err = l.Setup(lakebtcConfig)
|
||||
if err != nil {
|
||||
log.Fatal("LakeBTC setup error", err)
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestFetchTradablePairs(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := l.FetchTradablePairs(asset.Spot)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTradablePairs err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := l.GetTicker()
|
||||
if err != nil {
|
||||
t.Error("GetTicker() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderBook(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := l.GetOrderBook("BTCUSD")
|
||||
if err != nil {
|
||||
t.Error("GetOrderBook() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTradeHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := l.GetTradeHistory("BTCUSD")
|
||||
if err != nil {
|
||||
t.Error("GetTradeHistory() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrade(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !l.ValidateAPICredentials() {
|
||||
t.Skip()
|
||||
}
|
||||
_, err := l.Trade(false, 0, 0, "USD")
|
||||
if err == nil {
|
||||
t.Error("Trade() Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOpenOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !l.ValidateAPICredentials() {
|
||||
t.Skip()
|
||||
}
|
||||
_, err := l.GetOpenOrders()
|
||||
if err == nil {
|
||||
t.Error("GetOpenOrders() Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !l.ValidateAPICredentials() {
|
||||
t.Skip()
|
||||
}
|
||||
_, err := l.GetOrders([]int64{1, 2})
|
||||
if err == nil {
|
||||
t.Error("GetOrders() Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !l.ValidateAPICredentials() {
|
||||
t.Skip()
|
||||
}
|
||||
err := l.CancelExistingOrder(1337)
|
||||
if err == nil {
|
||||
t.Error("CancelExistingOrder() Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTrades(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !l.ValidateAPICredentials() {
|
||||
t.Skip()
|
||||
}
|
||||
_, err := l.GetTrades(1337)
|
||||
if err == nil {
|
||||
t.Error("GetTrades() Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExternalAccounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !l.ValidateAPICredentials() {
|
||||
t.Skip()
|
||||
}
|
||||
_, err := l.GetExternalAccounts()
|
||||
if err == nil {
|
||||
t.Error("GetExternalAccounts() Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func setFeeBuilder() *exchange.FeeBuilder {
|
||||
return &exchange.FeeBuilder{
|
||||
Amount: 1,
|
||||
FeeType: exchange.CryptocurrencyTradeFee,
|
||||
Pair: currency.NewPairWithDelimiter(currency.BTC.String(),
|
||||
currency.LTC.String(),
|
||||
"_"),
|
||||
IsMaker: false,
|
||||
PurchasePrice: 1,
|
||||
FiatCurrency: currency.USD,
|
||||
BankTransactionType: exchange.WireTransfer,
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetFeeByTypeOfflineTradeFee logic test
|
||||
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
|
||||
var feeBuilder = setFeeBuilder()
|
||||
l.GetFeeByType(feeBuilder)
|
||||
if !areTestAPIKeysSet() {
|
||||
if feeBuilder.FeeType != exchange.OfflineTradeFee {
|
||||
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
|
||||
}
|
||||
} else {
|
||||
if feeBuilder.FeeType != exchange.CryptocurrencyTradeFee {
|
||||
t.Errorf("Expected %v, received %v", exchange.CryptocurrencyTradeFee, feeBuilder.FeeType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFee(t *testing.T) {
|
||||
t.Parallel()
|
||||
var feeBuilder = setFeeBuilder()
|
||||
// CryptocurrencyTradeFee Basic
|
||||
if _, err := l.GetFee(feeBuilder); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// CryptocurrencyTradeFee High quantity
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.Amount = 1000
|
||||
feeBuilder.PurchasePrice = 1000
|
||||
if _, err := l.GetFee(feeBuilder); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// CryptocurrencyTradeFee IsMaker
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.IsMaker = true
|
||||
if _, err := l.GetFee(feeBuilder); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// CryptocurrencyTradeFee Negative purchase price
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.PurchasePrice = -1000
|
||||
if _, err := l.GetFee(feeBuilder); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// CryptocurrencyWithdrawalFee Basic
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
|
||||
if _, err := l.GetFee(feeBuilder); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// CryptocurrencyWithdrawalFee Invalid currency
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.Pair.Base = currency.NewCode("hello")
|
||||
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
|
||||
if _, err := l.GetFee(feeBuilder); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// CryptocurrencyDepositFee Basic
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.FeeType = exchange.CryptocurrencyDepositFee
|
||||
if _, err := l.GetFee(feeBuilder); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// InternationalBankDepositFee Basic
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.FeeType = exchange.InternationalBankDepositFee
|
||||
if _, err := l.GetFee(feeBuilder); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// InternationalBankWithdrawalFee Basic
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
|
||||
feeBuilder.FiatCurrency = currency.USD
|
||||
if _, err := l.GetFee(feeBuilder); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatWithdrawPermissions(t *testing.T) {
|
||||
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
|
||||
withdrawPermissions := l.FormatWithdrawPermissions()
|
||||
if withdrawPermissions != expectedResult {
|
||||
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetActiveOrders(t *testing.T) {
|
||||
var getOrdersRequest = order.GetOrdersRequest{
|
||||
Type: order.AnyType,
|
||||
AssetType: asset.Spot,
|
||||
}
|
||||
|
||||
_, err := l.GetActiveOrders(&getOrdersRequest)
|
||||
if areTestAPIKeysSet() && err != nil {
|
||||
t.Errorf("Could not get open orders: %s", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
t.Error("Expecting an error when no keys are set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderHistory(t *testing.T) {
|
||||
var getOrdersRequest = order.GetOrdersRequest{
|
||||
Type: order.AnyType,
|
||||
AssetType: asset.Spot,
|
||||
}
|
||||
|
||||
_, err := l.GetOrderHistory(&getOrdersRequest)
|
||||
if areTestAPIKeysSet() && err != nil {
|
||||
t.Errorf("Could not get order history: %s", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
t.Error("Expecting an error when no keys are set")
|
||||
}
|
||||
}
|
||||
|
||||
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
func areTestAPIKeysSet() bool {
|
||||
return l.ValidateAPICredentials()
|
||||
}
|
||||
|
||||
func TestSubmitOrder(t *testing.T) {
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var orderSubmission = &order.Submit{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.EUR,
|
||||
},
|
||||
Side: order.Buy,
|
||||
Type: order.Limit,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
AssetType: asset.Spot,
|
||||
}
|
||||
response, err := l.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
t.Error("Expecting an error when no keys are set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelExchangeOrder(t *testing.T) {
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
|
||||
var orderCancellation = &order.Cancel{
|
||||
ID: "1",
|
||||
WalletAddress: core.BitcoinDonationAddress,
|
||||
AccountID: "1",
|
||||
Pair: currencyPair,
|
||||
AssetType: asset.Spot,
|
||||
}
|
||||
|
||||
err := l.CancelOrder(orderCancellation)
|
||||
if !areTestAPIKeysSet() && err == nil {
|
||||
t.Error("Expecting an error when no keys are set")
|
||||
}
|
||||
if areTestAPIKeysSet() && err != nil {
|
||||
t.Errorf("Could not cancel orders: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelAllExchangeOrders(t *testing.T) {
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
|
||||
var orderCancellation = &order.Cancel{
|
||||
ID: "1",
|
||||
WalletAddress: core.BitcoinDonationAddress,
|
||||
AccountID: "1",
|
||||
Pair: currencyPair,
|
||||
AssetType: asset.Spot,
|
||||
}
|
||||
|
||||
resp, err := l.CancelAllOrders(orderCancellation)
|
||||
|
||||
if !areTestAPIKeysSet() && err == nil {
|
||||
t.Error("Expecting an error when no keys are set")
|
||||
}
|
||||
if areTestAPIKeysSet() && err != nil {
|
||||
t.Errorf("Could not cancel orders: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Status) > 0 {
|
||||
t.Errorf("%v orders failed to cancel", len(resp.Status))
|
||||
}
|
||||
}
|
||||
|
||||
func TestModifyOrder(t *testing.T) {
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
_, err := l.ModifyOrder(&order.Modify{AssetType: asset.Spot})
|
||||
if err == nil {
|
||||
t.Error("ModifyOrder() Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdraw(t *testing.T) {
|
||||
withdrawCryptoRequest := withdraw.Request{
|
||||
Amount: -1,
|
||||
Currency: currency.BTC,
|
||||
Description: "WITHDRAW IT ALL",
|
||||
Crypto: withdraw.CryptoRequest{
|
||||
Address: core.BitcoinDonationAddress,
|
||||
},
|
||||
}
|
||||
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
_, err := l.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
|
||||
if !areTestAPIKeysSet() && err == nil {
|
||||
t.Error("Expecting an error when no keys are set")
|
||||
}
|
||||
if areTestAPIKeysSet() && err != nil {
|
||||
t.Errorf("Withdraw failed to be placed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdrawFiat(t *testing.T) {
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var withdrawFiatRequest = withdraw.Request{}
|
||||
_, err := l.WithdrawFiatFunds(&withdrawFiatRequest)
|
||||
if err != common.ErrFunctionNotSupported {
|
||||
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdrawInternationalBank(t *testing.T) {
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var withdrawFiatRequest = withdraw.Request{}
|
||||
_, err := l.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
|
||||
if err != common.ErrFunctionNotSupported {
|
||||
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDepositAddress(t *testing.T) {
|
||||
if areTestAPIKeysSet() {
|
||||
_, err := l.GetDepositAddress(currency.BTC, "")
|
||||
if err != nil {
|
||||
t.Error("GetDepositAddress() error", err)
|
||||
}
|
||||
} else {
|
||||
_, err := l.GetDepositAddress(currency.DASH, "")
|
||||
if err == nil {
|
||||
t.Error("GetDepositAddress() error cannot be nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestWsConn websocket connection test
|
||||
func TestWsConn(t *testing.T) {
|
||||
if !l.Websocket.IsEnabled() {
|
||||
t.Skip(stream.WebsocketNotEnabled)
|
||||
}
|
||||
err := l.WsConnect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWsTickerProcessing logic test
|
||||
func TestWsTickerProcessing(t *testing.T) {
|
||||
json := `{"btcusd":{"low":"10990.05","high":"11966.24","last":"11903.29","volume":"1803.967079","sell":"11912.39","buy":"11902.2"},"btceur":{"low":"9886.87","high":"10732.72","last":"10691.44","volume":"87.994478","sell":"10711.62","buy":"10691.44"},"btchkd":{"low":null,"high":null,"last":"51776.98","volume":null,"sell":"93307.37","buy":"93177.56"},"btcjpy":{"low":"1176039.0","high":"1272246.0","last":"1265680.0","volume":"129.021421","sell":"1266764.0","buy":"1265680.0"},"btcgbp":{"low":"9157.12","high":"9953.43","last":"9941.28","volume":"10.4997","sell":"10007.89","buy":"9941.28"},"btcaud":{"low":"16102.57","high":"17594.22","last":"17548.16","volume":"7.338316","sell":"17616.67","buy":"17549.69"},"btccad":{"low":"14541.69","high":"15834.87","last":"15763.54","volume":"30.480309","sell":"15793.45","buy":"15756.13"},"btcsgd":{"low":"15133.82","high":"16501.62","last":"16455.53","volume":"4.044026","sell":"16484.37","buy":"16462.18"},"btcchf":{"low":"10800.58","high":"11526.24","last":"11526.24","volume":"0.1765","sell":"11675.34","buy":"11632.02"},"btcnzd":{"low":null,"high":null,"last":"8340.98","volume":null,"sell":"18315.49","buy":"18221.37"},"btcngn":{"low":null,"high":null,"last":"600000.0","volume":null,"sell":null,"buy":null},"eurusd":{"low":"1.1088","high":"1.1138","last":"1.1125","volume":"2680.105249","sell":"1.1142","buy":"1.1121"},"gbpusd":{"low":"1.1934","high":"1.1958","last":"1.1934","volume":"1493.923823","sell":"1.1979","buy":"1.1903"},"usdjpy":{"low":"105.26","high":"107.25","last":"106.33","volume":"114490.2179","sell":"106.34","buy":"106.27"},"usdhkd":{"low":null,"high":null,"last":"7.851","volume":null,"sell":"7.8328","buy":"7.8286"},"usdcad":{"low":"1.3225","high":"1.3272","last":"1.3255","volume":"11033.9877","sell":"1.3258","buy":"1.3238"},"usdsgd":{"low":"1.3776","high":"1.3839","last":"1.3838","volume":"2523.75","sell":"1.3838","buy":"1.3819"},"audusd":{"low":"0.6764","high":"0.6853","last":"0.6771","volume":"5442.608321","sell":"0.6782","buy":"0.6762"},"nzdusd":{"low":null,"high":null,"last":"0.6758","volume":null,"sell":"0.6532","buy":"0.6504"},"usdchf":{"low":"0.9838","high":"0.9838","last":"0.9838","volume":"108.3352","sell":"0.9801","buy":"0.9773"},"usdngn":{"low":null,"high":null,"last":"200.0","volume":null,"sell":null,"buy":null},"ethbtc":{"low":"0.0205","high":"0.025","last":"0.0205","volume":null,"sell":"0.03","buy":"0.0194"},"ltcbtc":{"low":null,"high":null,"last":"0.0114","volume":null,"sell":"0.009","buy":"0.0073"},"bchbtc":{"low":null,"high":null,"last":"0.0544","volume":null,"sell":"0.0322","buy":"0.0274"},"xrpbtc":{"low":"0.000042","high":"0.000042","last":"0.000042","volume":null,"sell":"0.000037","buy":"0.000022"},"baceth":{"low":"0.000035","high":"0.000035","last":"0.000035","volume":null,"sell":"0.0015","buy":null}}`
|
||||
err := l.processTicker(json)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCurrencyFromChannel(t *testing.T) {
|
||||
curr := currency.NewPair(currency.LTC, currency.BTC)
|
||||
result, err := l.getCurrencyFromChannel(marketSubstring +
|
||||
curr.String() +
|
||||
globalSubstring)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !curr.Equal(result) {
|
||||
t.Errorf("currency result is not equal. Expected %v", curr)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWsOrderbookProcessing logic test
|
||||
func TestWsOrderbookProcessing(t *testing.T) {
|
||||
json := `{"asks":[["11905.66","0.0019"],["11905.73","0.0015"],["11906.43","0.0013"],["11906.62","0.0019"],["11907.25","11.087"],["11907.66","0.0006"],["11907.73","0.3113"],["11907.84","0.0006"],["11908.37","0.0016"],["11908.86","10.3786"],["11909.54","4.2955"],["11910.15","0.0012"],["11910.56","13.5505"],["11911.06","0.0011"],["11911.37","0.0023"]],"bids":[["11905.55","0.0171"],["11904.43","0.0225"],["11903.31","0.0223"],["11902.2","0.0027"],["11901.92","1.002"],["11901.6","0.0015"],["11901.49","0.0012"],["11901.08","0.0227"],["11900.93","0.0009"],["11900.53","1.662"],["11900.08","0.001"],["11900.01","3.6745"],["11899.96","0.003"],["11899.91","0.0006"],["11899.44","0.0013"]]}`
|
||||
err := l.processOrderbook(json, "market-btcusd-global")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRecentTrades(t *testing.T) {
|
||||
t.Parallel()
|
||||
currencyPair, err := currency.NewPairFromString("BTCUSD")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = l.GetRecentTrades(currencyPair, asset.Spot)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHistoricTrades(t *testing.T) {
|
||||
t.Parallel()
|
||||
currencyPair, err := currency.NewPairFromString("BTCUSD")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = l.GetHistoricTrades(currencyPair, asset.Spot, time.Now().Add(-time.Minute*15), time.Now())
|
||||
if err != nil && err != common.ErrFunctionNotSupported {
|
||||
t.Error(err)
|
||||
}
|
||||
if err == nil {
|
||||
t.Error("expected error")
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package lakebtc
|
||||
|
||||
import pusher "github.com/toorop/go-pusher"
|
||||
|
||||
// Ticker holds ticker information
|
||||
type Ticker struct {
|
||||
Last float64
|
||||
Bid float64
|
||||
Ask float64
|
||||
High float64
|
||||
Low float64
|
||||
Volume float64
|
||||
}
|
||||
|
||||
// OrderbookStructure stores price and amount for order books
|
||||
type OrderbookStructure struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
// Orderbook contains arrays of orderbook information
|
||||
type Orderbook struct {
|
||||
Bids []OrderbookStructure `json:"bids"`
|
||||
Asks []OrderbookStructure `json:"asks"`
|
||||
}
|
||||
|
||||
// TickerResponse stores temp response
|
||||
// Silly hack due to API returning null instead of strings
|
||||
type TickerResponse struct {
|
||||
Last interface{}
|
||||
Bid interface{}
|
||||
Ask interface{}
|
||||
High interface{}
|
||||
Low interface{}
|
||||
Volume interface{}
|
||||
}
|
||||
|
||||
// TradeHistory holds trade history data
|
||||
type TradeHistory struct {
|
||||
Date int64 `json:"date"`
|
||||
Price float64 `json:"price,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
TID int64 `json:"tid"`
|
||||
}
|
||||
|
||||
// AccountInfo contains account information
|
||||
type AccountInfo struct {
|
||||
Balance map[string]string `json:"balance"`
|
||||
Locked map[string]string `json:"locked"`
|
||||
Profile struct {
|
||||
Email string `json:"email"`
|
||||
UID string `json:"uid"`
|
||||
BTCDepositAddress string `json:"btc_deposit_addres"` // nolint // API misspelling
|
||||
} `json:"profile"`
|
||||
}
|
||||
|
||||
// Trade holds trade information
|
||||
type Trade struct {
|
||||
ID int64 `json:"id"`
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
// OpenOrders stores full information on your open orders
|
||||
type OpenOrders struct {
|
||||
ID int64 `json:"id"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Symbol string `json:"symbol"`
|
||||
Type string `json:"type"`
|
||||
At int64 `json:"at"`
|
||||
}
|
||||
|
||||
// Orders holds current order information
|
||||
type Orders struct {
|
||||
ID int64 `json:"id"`
|
||||
OriginalAmount float64 `json:"original_amount,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Symbol string `json:"symbol"`
|
||||
Type string `json:"type"`
|
||||
State string `json:"state"`
|
||||
At int64 `json:"at"`
|
||||
}
|
||||
|
||||
// AuthenticatedTradeHistory is a store of personalised auth trade history
|
||||
type AuthenticatedTradeHistory struct {
|
||||
Type string `json:"type"`
|
||||
Symbol string `json:"symbol"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
At int64 `json:"at"`
|
||||
}
|
||||
|
||||
// ExternalAccounts holds external account information
|
||||
type ExternalAccounts struct {
|
||||
ID int64 `json:"id,string"`
|
||||
Type string `json:"type"`
|
||||
Address string `json:"address"`
|
||||
Alias interface{} `json:"alias"`
|
||||
Currencies string `json:"currencies"`
|
||||
State string `json:"state"`
|
||||
UpdatedAt int64 `json:"updated_at,string"`
|
||||
}
|
||||
|
||||
// Withdraw holds withdrawal information
|
||||
type Withdraw struct {
|
||||
ID int64 `json:"id,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Currency string `json:"currency"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
State string `json:"state"`
|
||||
Source string `json:"source"`
|
||||
ExternalAccountID int64 `json:"external_account_id,string"`
|
||||
At int64 `json:"at"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// WebsocketConn defines a pusher websocket connection
|
||||
type WebsocketConn struct {
|
||||
Client *pusher.Client
|
||||
Ticker chan *pusher.Event
|
||||
Orderbook chan *pusher.Event
|
||||
Trade chan *pusher.Event
|
||||
}
|
||||
|
||||
// WsOrderbookUpdate contains orderbook data from websocket
|
||||
type WsOrderbookUpdate struct {
|
||||
Asks [][2]string `json:"asks"`
|
||||
Bids [][2]string `json:"bids"`
|
||||
}
|
||||
|
||||
// WsTrades contains trade data from websocket
|
||||
type WsTrades struct {
|
||||
Trades []WsTrade `json:"trades"`
|
||||
}
|
||||
|
||||
// WsTrade contains individual trade details from websocket
|
||||
type WsTrade struct {
|
||||
Type string `json:"type"`
|
||||
Date int64 `json:"date"`
|
||||
Price float64 `json:"price,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
}
|
||||
|
||||
type wsTicker struct {
|
||||
Low float64 `json:"low,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Last float64 `json:"last,string"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
Sell float64 `json:"sell,string"`
|
||||
Buy float64 `json:"buy,string"`
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
package lakebtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
"github.com/toorop/go-pusher"
|
||||
)
|
||||
|
||||
const (
|
||||
lakeBTCWSURL = "wss://ws.lakebtc.com:8085"
|
||||
marketGlobalEndpoint = "market-global"
|
||||
marketSubstring = "market-"
|
||||
globalSubstring = "-global"
|
||||
wssSchem = "wss"
|
||||
)
|
||||
|
||||
// WsConnect initiates a new websocket connection
|
||||
func (l *LakeBTC) WsConnect() error {
|
||||
if !l.Websocket.IsEnabled() || !l.IsEnabled() {
|
||||
return errors.New(stream.WebsocketNotEnabled)
|
||||
}
|
||||
|
||||
url := strings.Split(lakeBTCWSURL, "://")
|
||||
var err error
|
||||
l.WebsocketConn.Client, err = pusher.NewCustomClient(strings.ToLower(l.Name),
|
||||
url[1],
|
||||
wssSchem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = l.WebsocketConn.Client.Subscribe(marketGlobalEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = l.listenToEndpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go l.wsHandleIncomingData()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LakeBTC) listenToEndpoints() error {
|
||||
var err error
|
||||
l.WebsocketConn.Ticker, err = l.WebsocketConn.Client.Bind("tickers")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s Websocket Bind error: %s", l.Name, err)
|
||||
}
|
||||
l.WebsocketConn.Orderbook, err = l.WebsocketConn.Client.Bind("update")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s Websocket Bind error: %s", l.Name, err)
|
||||
}
|
||||
// LakeBTC does not provide enough trade data to sync to the trade database table
|
||||
// please use REST until the API is updated
|
||||
// l.WebsocketConn.Trade, err = l.WebsocketConn.Client.Bind("trades")
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions()
|
||||
func (l *LakeBTC) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) {
|
||||
var subscriptions []stream.ChannelSubscription
|
||||
enabledCurrencies, err := l.GetEnabledPairs(asset.Spot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for j := range enabledCurrencies {
|
||||
enabledCurrencies[j].Delimiter = ""
|
||||
channel := marketSubstring +
|
||||
enabledCurrencies[j].Lower().String() +
|
||||
globalSubstring
|
||||
|
||||
subscriptions = append(subscriptions, stream.ChannelSubscription{
|
||||
Channel: channel,
|
||||
Currency: enabledCurrencies[j],
|
||||
Asset: asset.Spot,
|
||||
})
|
||||
}
|
||||
return subscriptions, nil
|
||||
}
|
||||
|
||||
// Subscribe sends a websocket message to receive data from the channel
|
||||
func (l *LakeBTC) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error {
|
||||
var errs common.Errors
|
||||
for i := range channelsToSubscribe {
|
||||
err := l.WebsocketConn.Client.Subscribe(channelsToSubscribe[i].Channel)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
l.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i])
|
||||
}
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unsubscribe sends a websocket message to unsubscribe from the channel
|
||||
func (l *LakeBTC) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error {
|
||||
var errs common.Errors
|
||||
for i := range channelsToUnsubscribe {
|
||||
err := l.WebsocketConn.Client.Unsubscribe(channelsToUnsubscribe[i].Channel)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
l.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i])
|
||||
}
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// wsHandleIncomingData services incoming data from the websocket connection
|
||||
func (l *LakeBTC) wsHandleIncomingData() {
|
||||
l.Websocket.Wg.Add(1)
|
||||
defer l.Websocket.Wg.Done()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-l.Websocket.ShutdownC:
|
||||
return
|
||||
case data := <-l.WebsocketConn.Ticker:
|
||||
if l.Verbose {
|
||||
log.Debugf(log.ExchangeSys,
|
||||
"%v Websocket message received: %v", l.Name, data)
|
||||
}
|
||||
err := l.processTicker(data.Data)
|
||||
if err != nil {
|
||||
l.Websocket.DataHandler <- err
|
||||
}
|
||||
case data := <-l.WebsocketConn.Orderbook:
|
||||
if l.Verbose {
|
||||
log.Debugf(log.ExchangeSys,
|
||||
"%v Websocket message received: %v", l.Name, data)
|
||||
}
|
||||
err := l.processOrderbook(data.Data, data.Channel)
|
||||
if err != nil {
|
||||
l.Websocket.DataHandler <- err
|
||||
}
|
||||
}
|
||||
select {
|
||||
case l.Websocket.TrafficAlert <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LakeBTC) processOrderbook(obUpdate, channel string) error {
|
||||
var update WsOrderbookUpdate
|
||||
err := json.Unmarshal([]byte(obUpdate), &update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := l.getCurrencyFromChannel(channel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
book := orderbook.Base{
|
||||
Pair: p,
|
||||
LastUpdated: time.Now(),
|
||||
Asset: asset.Spot,
|
||||
Exchange: l.Name,
|
||||
VerifyOrderbook: l.CanVerifyOrderbook,
|
||||
}
|
||||
|
||||
for i := range update.Asks {
|
||||
var amount, price float64
|
||||
amount, err = strconv.ParseFloat(update.Asks[i][1], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
price, err = strconv.ParseFloat(update.Asks[i][0], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
book.Asks = append(book.Asks, orderbook.Item{
|
||||
Amount: amount,
|
||||
Price: price,
|
||||
})
|
||||
}
|
||||
|
||||
for i := range update.Bids {
|
||||
var amount, price float64
|
||||
amount, err = strconv.ParseFloat(update.Bids[i][1], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
price, err = strconv.ParseFloat(update.Bids[i][0], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
book.Bids = append(book.Bids, orderbook.Item{
|
||||
Amount: amount,
|
||||
Price: price,
|
||||
})
|
||||
}
|
||||
|
||||
return l.Websocket.Orderbook.LoadSnapshot(&book)
|
||||
}
|
||||
|
||||
func (l *LakeBTC) getCurrencyFromChannel(channel string) (currency.Pair, error) {
|
||||
curr := strings.Replace(channel, marketSubstring, "", 1)
|
||||
curr = strings.Replace(curr, globalSubstring, "", 1)
|
||||
return currency.NewPairFromString(curr)
|
||||
}
|
||||
|
||||
func (l *LakeBTC) processTicker(data string) error {
|
||||
var tUpdate map[string]wsTicker
|
||||
err := json.Unmarshal([]byte(data), &tUpdate)
|
||||
if err != nil {
|
||||
l.Websocket.DataHandler <- err
|
||||
return err
|
||||
}
|
||||
|
||||
enabled, err := l.GetEnabledPairs(asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for k, v := range tUpdate {
|
||||
returnCurrency, err := currency.NewPairFromString(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !enabled.Contains(returnCurrency, true) {
|
||||
continue
|
||||
}
|
||||
|
||||
l.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: l.Name,
|
||||
Bid: v.Buy,
|
||||
High: v.High,
|
||||
Last: v.Last,
|
||||
Low: v.Low,
|
||||
Ask: v.Sell,
|
||||
Volume: v.Volume,
|
||||
AssetType: asset.Spot,
|
||||
Pair: returnCurrency,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,654 +0,0 @@
|
||||
package lakebtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/protocol"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
)
|
||||
|
||||
// GetDefaultConfig returns a default exchange config
|
||||
func (l *LakeBTC) GetDefaultConfig() (*config.ExchangeConfig, error) {
|
||||
l.SetDefaults()
|
||||
exchCfg := new(config.ExchangeConfig)
|
||||
exchCfg.Name = l.Name
|
||||
exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
|
||||
exchCfg.BaseCurrencies = l.BaseCurrencies
|
||||
|
||||
err := l.SetupDefaults(exchCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if l.Features.Supports.RESTCapabilities.AutoPairUpdates {
|
||||
err = l.UpdateTradablePairs(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return exchCfg, nil
|
||||
}
|
||||
|
||||
// SetDefaults sets LakeBTC defaults
|
||||
func (l *LakeBTC) SetDefaults() {
|
||||
l.Name = "LakeBTC"
|
||||
l.Enabled = true
|
||||
l.Verbose = true
|
||||
l.API.CredentialsValidator.RequiresKey = true
|
||||
l.API.CredentialsValidator.RequiresSecret = true
|
||||
|
||||
requestFmt := ¤cy.PairFormat{Uppercase: true}
|
||||
configFmt := ¤cy.PairFormat{Uppercase: true}
|
||||
err := l.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
|
||||
if err != nil {
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
}
|
||||
|
||||
l.Features = exchange.Features{
|
||||
Supports: exchange.FeaturesSupported{
|
||||
REST: true,
|
||||
Websocket: true,
|
||||
RESTCapabilities: protocol.Features{
|
||||
TickerBatching: true,
|
||||
TickerFetching: true,
|
||||
TradeFetching: true,
|
||||
OrderbookFetching: true,
|
||||
AutoPairUpdates: true,
|
||||
AccountInfo: true,
|
||||
GetOrder: true,
|
||||
GetOrders: true,
|
||||
CancelOrders: true,
|
||||
CancelOrder: true,
|
||||
SubmitOrder: true,
|
||||
UserTradeHistory: true,
|
||||
CryptoWithdrawal: true,
|
||||
TradeFee: true,
|
||||
CryptoDepositFee: true,
|
||||
},
|
||||
WebsocketCapabilities: protocol.Features{
|
||||
TradeFetching: true,
|
||||
OrderbookFetching: true,
|
||||
Subscribe: true,
|
||||
Unsubscribe: true,
|
||||
},
|
||||
WithdrawPermissions: exchange.AutoWithdrawCrypto |
|
||||
exchange.WithdrawFiatViaWebsiteOnly,
|
||||
},
|
||||
Enabled: exchange.FeaturesEnabled{
|
||||
AutoPairUpdates: true,
|
||||
},
|
||||
}
|
||||
|
||||
l.Requester = request.New(l.Name,
|
||||
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
|
||||
l.API.Endpoints = l.NewEndpoints()
|
||||
err = l.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
|
||||
exchange.RestSpot: lakeBTCAPIURL,
|
||||
exchange.WebsocketSpot: lakeBTCWSURL,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
}
|
||||
l.Websocket = stream.New()
|
||||
l.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
|
||||
l.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
|
||||
l.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
|
||||
}
|
||||
|
||||
// Setup sets exchange configuration profile
|
||||
func (l *LakeBTC) Setup(exch *config.ExchangeConfig) error {
|
||||
if !exch.Enabled {
|
||||
l.SetEnabled(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := l.SetupDefaults(exch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wsRunningURL, err := l.API.Endpoints.GetURL(exchange.WebsocketSpot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.Websocket.Setup(&stream.WebsocketSetup{
|
||||
Enabled: exch.Features.Enabled.Websocket,
|
||||
Verbose: exch.Verbose,
|
||||
AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport,
|
||||
WebsocketTimeout: exch.WebsocketTrafficTimeout,
|
||||
DefaultURL: lakeBTCWSURL,
|
||||
ExchangeName: exch.Name,
|
||||
RunningURL: wsRunningURL,
|
||||
Connector: l.WsConnect,
|
||||
Subscriber: l.Subscribe,
|
||||
UnSubscriber: l.Unsubscribe,
|
||||
GenerateSubscriptions: l.GenerateDefaultSubscriptions,
|
||||
Features: &l.Features.Supports.WebsocketCapabilities,
|
||||
OrderbookBufferLimit: exch.OrderbookConfig.WebsocketBufferLimit,
|
||||
BufferEnabled: exch.OrderbookConfig.WebsocketBufferEnabled,
|
||||
})
|
||||
}
|
||||
|
||||
// Start starts the LakeBTC go routine
|
||||
func (l *LakeBTC) Start(wg *sync.WaitGroup) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
l.Run()
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// Run implements the LakeBTC wrapper
|
||||
func (l *LakeBTC) Run() {
|
||||
if l.Verbose {
|
||||
l.PrintEnabledPairs()
|
||||
}
|
||||
|
||||
if !l.GetEnabledFeatures().AutoPairUpdates {
|
||||
return
|
||||
}
|
||||
|
||||
err := l.UpdateTradablePairs(false)
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", l.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// FetchTradablePairs returns a list of the exchanges tradable pairs
|
||||
func (l *LakeBTC) FetchTradablePairs(asset asset.Item) ([]string, error) {
|
||||
result, err := l.GetTicker()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var currencies []string
|
||||
for x := range result {
|
||||
currencies = append(currencies, strings.ToUpper(x))
|
||||
}
|
||||
|
||||
return currencies, nil
|
||||
}
|
||||
|
||||
// UpdateTradablePairs updates the exchanges available pairs and stores
|
||||
// them in the exchanges config
|
||||
func (l *LakeBTC) UpdateTradablePairs(forceUpdate bool) error {
|
||||
pairs, err := l.FetchTradablePairs(asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := currency.NewPairsFromStrings(pairs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return l.UpdatePairs(p, asset.Spot, false, forceUpdate)
|
||||
}
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (l *LakeBTC) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
|
||||
ticks, err := l.GetTicker()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pairs, err := l.GetEnabledPairs(assetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range pairs {
|
||||
fpair, err := l.FormatExchangeCurrency(pairs[i], assetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, ok := ticks[fpair.String()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
tickerPrice := new(ticker.Price)
|
||||
tickerPrice.Pair = pairs[i]
|
||||
tickerPrice.Ask = c.Ask
|
||||
tickerPrice.Bid = c.Bid
|
||||
tickerPrice.Volume = c.Volume
|
||||
tickerPrice.High = c.High
|
||||
tickerPrice.Low = c.Low
|
||||
tickerPrice.Last = c.Last
|
||||
tickerPrice.ExchangeName = l.Name
|
||||
tickerPrice.AssetType = assetType
|
||||
|
||||
err = ticker.ProcessTicker(tickerPrice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return ticker.GetTicker(l.Name, p, assetType)
|
||||
}
|
||||
|
||||
// FetchTicker returns the ticker for a currency pair
|
||||
func (l *LakeBTC) FetchTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
|
||||
fPair, err := l.FormatExchangeCurrency(p, assetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tickerNew, err := ticker.GetTicker(l.Name, fPair, assetType)
|
||||
if err != nil {
|
||||
return l.UpdateTicker(fPair, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
// FetchOrderbook returns orderbook base on the currency pair
|
||||
func (l *LakeBTC) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
|
||||
fPair, err := l.FormatExchangeCurrency(p, assetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob, err := orderbook.Get(l.Name, fPair, assetType)
|
||||
if err != nil {
|
||||
return l.UpdateOrderbook(fPair, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (l *LakeBTC) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
|
||||
book := &orderbook.Base{
|
||||
Exchange: l.Name,
|
||||
Pair: p,
|
||||
Asset: assetType,
|
||||
VerifyOrderbook: l.CanVerifyOrderbook,
|
||||
}
|
||||
fPair, err := l.FormatExchangeCurrency(p, assetType)
|
||||
if err != nil {
|
||||
return book, err
|
||||
}
|
||||
|
||||
orderbookNew, err := l.GetOrderBook(fPair.String())
|
||||
if err != nil {
|
||||
return book, err
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Bids {
|
||||
book.Bids = append(book.Bids, orderbook.Item{
|
||||
Amount: orderbookNew.Bids[x].Amount,
|
||||
Price: orderbookNew.Bids[x].Price})
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Asks {
|
||||
book.Asks = append(book.Asks, orderbook.Item{
|
||||
Amount: orderbookNew.Asks[x].Amount,
|
||||
Price: orderbookNew.Asks[x].Price})
|
||||
}
|
||||
err = book.Process()
|
||||
if err != nil {
|
||||
return book, err
|
||||
}
|
||||
return orderbook.Get(l.Name, fPair, assetType)
|
||||
}
|
||||
|
||||
// UpdateAccountInfo retrieves balances for all enabled currencies for the
|
||||
// LakeBTC exchange
|
||||
func (l *LakeBTC) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
|
||||
var response account.Holdings
|
||||
response.Exchange = l.Name
|
||||
accountInfo, err := l.GetAccountInformation()
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
var currencies []account.Balance
|
||||
for x, y := range accountInfo.Balance {
|
||||
for z, w := range accountInfo.Locked {
|
||||
if z != x {
|
||||
continue
|
||||
}
|
||||
var exchangeCurrency account.Balance
|
||||
exchangeCurrency.CurrencyName = currency.NewCode(x)
|
||||
exchangeCurrency.TotalValue, _ = strconv.ParseFloat(y, 64)
|
||||
exchangeCurrency.Hold, _ = strconv.ParseFloat(w, 64)
|
||||
currencies = append(currencies, exchangeCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
response.Accounts = append(response.Accounts, account.SubAccount{
|
||||
Currencies: currencies,
|
||||
})
|
||||
|
||||
err = account.Process(&response)
|
||||
if err != nil {
|
||||
return account.Holdings{}, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// FetchAccountInfo retrieves balances for all enabled currencies
|
||||
func (l *LakeBTC) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
|
||||
acc, err := account.GetHoldings(l.Name, assetType)
|
||||
if err != nil {
|
||||
return l.UpdateAccountInfo(assetType)
|
||||
}
|
||||
|
||||
return acc, nil
|
||||
}
|
||||
|
||||
// GetFundingHistory returns funding history, deposits and
|
||||
// withdrawals
|
||||
func (l *LakeBTC) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (l *LakeBTC) GetWithdrawalsHistory(c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// GetRecentTrades returns the most recent trades for a currency and asset
|
||||
func (l *LakeBTC) GetRecentTrades(p currency.Pair, assetType asset.Item) ([]trade.Data, error) {
|
||||
var err error
|
||||
p, err = l.FormatExchangeCurrency(p, assetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp []trade.Data
|
||||
var tradeData []TradeHistory
|
||||
tradeData, err = l.GetTradeHistory(p.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range tradeData {
|
||||
tradeTS := time.Unix(tradeData[i].Date, 0)
|
||||
resp = append(resp, trade.Data{
|
||||
TID: strconv.FormatInt(tradeData[i].TID, 10),
|
||||
Exchange: l.Name,
|
||||
CurrencyPair: p,
|
||||
AssetType: assetType,
|
||||
Price: tradeData[i].Price,
|
||||
Amount: tradeData[i].Amount,
|
||||
Timestamp: tradeTS,
|
||||
})
|
||||
}
|
||||
|
||||
err = l.AddTradesToBuffer(resp...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Sort(trade.ByDate(resp))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetHistoricTrades returns historic trade data within the timeframe provided
|
||||
func (l *LakeBTC) GetHistoricTrades(_ currency.Pair, _ asset.Item, _, _ time.Time) ([]trade.Data, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (l *LakeBTC) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
|
||||
var submitOrderResponse order.SubmitResponse
|
||||
if err := s.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
fPair, err := l.FormatExchangeCurrency(s.Pair, s.AssetType)
|
||||
if err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
isBuyOrder := s.Side == order.Buy
|
||||
response, err := l.Trade(isBuyOrder,
|
||||
s.Amount,
|
||||
s.Price,
|
||||
fPair.Lower().String())
|
||||
if err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
if response.ID > 0 {
|
||||
submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10)
|
||||
}
|
||||
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
if s.Type == order.Market {
|
||||
submitOrderResponse.FullyMatched = true
|
||||
}
|
||||
return submitOrderResponse, nil
|
||||
}
|
||||
|
||||
// ModifyOrder will allow of changing orderbook placement and limit to
|
||||
// market conversion
|
||||
func (l *LakeBTC) ModifyOrder(action *order.Modify) (string, error) {
|
||||
return "", common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// CancelOrder cancels an order by its corresponding ID number
|
||||
func (l *LakeBTC) CancelOrder(o *order.Cancel) error {
|
||||
if err := o.Validate(o.StandardCancel()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
orderIDInt, err := strconv.ParseInt(o.ID, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.CancelExistingOrder(orderIDInt)
|
||||
}
|
||||
|
||||
// CancelBatchOrders cancels an orders by their corresponding ID numbers
|
||||
func (l *LakeBTC) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
|
||||
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// CancelAllOrders cancels all orders associated with a currency pair
|
||||
func (l *LakeBTC) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
|
||||
var cancelAllOrdersResponse order.CancelAllResponse
|
||||
openOrders, err := l.GetOpenOrders()
|
||||
if err != nil {
|
||||
return cancelAllOrdersResponse, err
|
||||
}
|
||||
|
||||
var ordersToCancel []string
|
||||
for i := range openOrders {
|
||||
ordersToCancel = append(ordersToCancel, strconv.FormatInt(openOrders[i].ID, 10))
|
||||
}
|
||||
|
||||
return cancelAllOrdersResponse, l.CancelExistingOrders(ordersToCancel)
|
||||
}
|
||||
|
||||
// GetOrderInfo returns order information based on order ID
|
||||
func (l *LakeBTC) GetOrderInfo(orderID string, pair currency.Pair, assetType asset.Item) (order.Detail, error) {
|
||||
var orderDetail order.Detail
|
||||
return orderDetail, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// GetDepositAddress returns a deposit address for a specified currency
|
||||
func (l *LakeBTC) GetDepositAddress(cryptocurrency currency.Code, _ string) (string, error) {
|
||||
if !strings.EqualFold(cryptocurrency.String(), currency.BTC.String()) {
|
||||
return "", fmt.Errorf("unsupported currency %s deposit address can only be BTC, manual deposit is required for other currencies",
|
||||
cryptocurrency.String())
|
||||
}
|
||||
|
||||
info, err := l.GetAccountInformation()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return info.Profile.BTCDepositAddress, nil
|
||||
}
|
||||
|
||||
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
|
||||
// submitted
|
||||
func (l *LakeBTC) WithdrawCryptocurrencyFunds(withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
|
||||
if err := withdrawRequest.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if withdrawRequest.Currency != currency.BTC {
|
||||
return nil, errors.New("only BTC supported for withdrawals")
|
||||
}
|
||||
|
||||
resp, err := l.CreateWithdraw(withdrawRequest.Amount, withdrawRequest.Description)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &withdraw.ExchangeResponse{
|
||||
ID: strconv.FormatInt(resp.ID, 10),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithdrawFiatFunds returns a withdrawal ID when a
|
||||
// withdrawal is submitted
|
||||
func (l *LakeBTC) WithdrawFiatFunds(_ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
|
||||
// withdrawal is submitted
|
||||
func (l *LakeBTC) WithdrawFiatFundsToInternationalBank(_ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetFeeByType returns an estimate of fee based on type of transaction
|
||||
func (l *LakeBTC) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
|
||||
if !l.AllowAuthenticatedRequest() && // Todo check connection status
|
||||
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
|
||||
feeBuilder.FeeType = exchange.OfflineTradeFee
|
||||
}
|
||||
return l.GetFee(feeBuilder)
|
||||
}
|
||||
|
||||
// GetActiveOrders retrieves any orders that are active/open
|
||||
func (l *LakeBTC) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := l.GetOpenOrders()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
format, err := l.GetPairFormat(asset.Spot, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var orders []order.Detail
|
||||
for i := range resp {
|
||||
var symbol currency.Pair
|
||||
symbol, err = currency.NewPairDelimiter(resp[i].Symbol,
|
||||
format.Delimiter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orderDate := time.Unix(resp[i].At, 0)
|
||||
side := order.Side(strings.ToUpper(resp[i].Type))
|
||||
|
||||
orders = append(orders, order.Detail{
|
||||
Amount: resp[i].Amount,
|
||||
ID: strconv.FormatInt(resp[i].ID, 10),
|
||||
Price: resp[i].Price,
|
||||
Side: side,
|
||||
Date: orderDate,
|
||||
Pair: symbol,
|
||||
Exchange: l.Name,
|
||||
})
|
||||
}
|
||||
|
||||
order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime)
|
||||
order.FilterOrdersBySide(&orders, req.Side)
|
||||
order.FilterOrdersByCurrencies(&orders, req.Pairs)
|
||||
|
||||
return orders, nil
|
||||
}
|
||||
|
||||
// GetOrderHistory retrieves account order information
|
||||
// Can Limit response to specific order status
|
||||
func (l *LakeBTC) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error) {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := l.GetOrders([]int64{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
format, err := l.GetPairFormat(asset.Spot, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var orders []order.Detail
|
||||
for i := range resp {
|
||||
if resp[i].State == "active" {
|
||||
continue
|
||||
}
|
||||
var symbol currency.Pair
|
||||
symbol, err = currency.NewPairDelimiter(resp[i].Symbol, format.Delimiter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orderDate := time.Unix(resp[i].At, 0)
|
||||
side := order.Side(strings.ToUpper(resp[i].Type))
|
||||
|
||||
orders = append(orders, order.Detail{
|
||||
Amount: resp[i].Amount,
|
||||
ID: strconv.FormatInt(resp[i].ID, 10),
|
||||
Price: resp[i].Price,
|
||||
Side: side,
|
||||
Date: orderDate,
|
||||
Pair: symbol,
|
||||
Exchange: l.Name,
|
||||
})
|
||||
}
|
||||
|
||||
order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime)
|
||||
order.FilterOrdersBySide(&orders, req.Side)
|
||||
order.FilterOrdersByCurrencies(&orders, req.Pairs)
|
||||
|
||||
return orders, nil
|
||||
}
|
||||
|
||||
// ValidateCredentials validates current credentials used for wrapper
|
||||
// functionality
|
||||
func (l *LakeBTC) ValidateCredentials(assetType asset.Item) error {
|
||||
_, err := l.UpdateAccountInfo(assetType)
|
||||
return l.CheckTransientError(err)
|
||||
}
|
||||
|
||||
// GetHistoricCandles returns candles between a time period for a set time interval
|
||||
func (l *LakeBTC) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
|
||||
return kline.Item{}, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetHistoricCandlesExtended returns candles between a time period for a set time interval
|
||||
func (l *LakeBTC) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
|
||||
return kline.Item{}, common.ErrFunctionNotSupported
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func (l *Lbank) Setup(exch *config.ExchangeConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts the LakeBTC go routine
|
||||
// Start starts the Lbank go routine
|
||||
func (l *Lbank) Start(wg *sync.WaitGroup) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
|
||||
@@ -225,7 +225,7 @@ func (b *Base) Verify() error {
|
||||
}
|
||||
|
||||
// Checking for both ask and bid lengths being zero has been removed and
|
||||
// a warning has been put in place some exchanges e.g. LakeBTC return zero
|
||||
// a warning has been put in place for some exchanges that return zero
|
||||
// level books. In the event that there is a massive liquidity change where
|
||||
// a book dries up, this will still update so we do not traverse potential
|
||||
// incorrect old data.
|
||||
|
||||
@@ -34,7 +34,6 @@ var Exchanges = []string{
|
||||
"huobi",
|
||||
"itbit",
|
||||
"kraken",
|
||||
"lakebtc",
|
||||
"lbank",
|
||||
"localbitcoins",
|
||||
"okcoin international",
|
||||
|
||||
@@ -80,7 +80,6 @@ _b in this context is an `IBotExchange` implemented struct_
|
||||
| Huobi.Pro | Yes | Yes | No |
|
||||
| ItBit | Yes | NA | No |
|
||||
| Kraken | Yes | Yes | No |
|
||||
| LakeBTC | Yes | No | No |
|
||||
| Lbank | Yes | No | Yes |
|
||||
| LocalBitcoins | Yes | NA | No |
|
||||
| OKCoin International | Yes | Yes | No |
|
||||
|
||||
1
go.mod
1
go.mod
@@ -25,7 +25,6 @@ require (
|
||||
github.com/thrasher-corp/gct-ta v0.0.0-20200623072738-f2b55b7f9f41
|
||||
github.com/thrasher-corp/goose v2.7.0-rc4.0.20191002032028-0f2c2a27abdb+incompatible
|
||||
github.com/thrasher-corp/sqlboiler v1.0.1-0.20191001234224-71e17f37a85e
|
||||
github.com/toorop/go-pusher v0.0.0-20180521062818-4521e2eb39fb
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
github.com/volatiletech/inflect v0.0.1 // indirect
|
||||
github.com/volatiletech/null v8.0.0+incompatible
|
||||
|
||||
2
go.sum
2
go.sum
@@ -369,8 +369,6 @@ github.com/thrasher-corp/goose v2.7.0-rc4.0.20191002032028-0f2c2a27abdb+incompat
|
||||
github.com/thrasher-corp/sqlboiler v1.0.1-0.20191001234224-71e17f37a85e h1:4kYBo2YhqqFY7aZPPEhrtPTMoAq4iCsoDITd3jseRbY=
|
||||
github.com/thrasher-corp/sqlboiler v1.0.1-0.20191001234224-71e17f37a85e/go.mod h1:JfJE+3gijF30ZJbUCzxGkU0+ymQxBfBOVp4XDObmJBE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/toorop/go-pusher v0.0.0-20180521062818-4521e2eb39fb h1:9kcmLvQdiIecpgVEL3/+J5QIP/ElRBJDljOay0SvqnA=
|
||||
github.com/toorop/go-pusher v0.0.0-20180521062818-4521e2eb39fb/go.mod h1:VTLqNCX1tXrur6pdIRCl8Q90FR7nw/mEBdyMkWMcsb0=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
|
||||
76
testdata/configtest.json
vendored
76
testdata/configtest.json
vendored
@@ -1647,82 +1647,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LakeBTC",
|
||||
"enabled": true,
|
||||
"verbose": false,
|
||||
"httpTimeout": 15000000000,
|
||||
"websocketResponseCheckTimeout": 30000000,
|
||||
"websocketResponseMaxLimit": 7000000000,
|
||||
"websocketTrafficTimeout": 30000000000,
|
||||
"websocketOrderbookBufferLimit": 5,
|
||||
"baseCurrencies": "USD,EUR,HKD,AUD,GBP,NZD,JPY,SGD,NGN,CHF,CAD",
|
||||
"currencyPairs": {
|
||||
"requestFormat": {
|
||||
"uppercase": true
|
||||
},
|
||||
"configFormat": {
|
||||
"uppercase": true
|
||||
},
|
||||
"useGlobalFormat": true,
|
||||
"assetTypes": [
|
||||
"spot"
|
||||
],
|
||||
"pairs": {
|
||||
"spot": {
|
||||
"enabled": "BTCUSD,BTCEUR,LTCBTC",
|
||||
"available": "USDCAD,USDSGD,BTCUSD,GBPUSD,LTCBTC,BCHBTC,USDJPY,USDCHF,EURUSD,ETHBTC,XRPBTC,USDHKD,BTCGBP,BTCNZD,NZDUSD,BTCNGN,BTCHKD,BTCAUD,BTCEUR,BTCCHF,AUDUSD,USDNGN,BACETH,BTCJPY,BTCCAD,BTCSGD"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
"authenticatedSupport": false,
|
||||
"authenticatedWebsocketApiSupport": false,
|
||||
"endpoints": {
|
||||
"url": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
|
||||
"urlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
|
||||
"websocketURL": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API"
|
||||
},
|
||||
"credentials": {
|
||||
"key": "Key",
|
||||
"secret": "Secret"
|
||||
},
|
||||
"credentialsValidator": {
|
||||
"requiresKey": true,
|
||||
"requiresSecret": true
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"supports": {
|
||||
"restAPI": true,
|
||||
"restCapabilities": {
|
||||
"tickerBatching": true,
|
||||
"autoPairUpdates": true
|
||||
},
|
||||
"websocketAPI": true,
|
||||
"websocketCapabilities": {}
|
||||
},
|
||||
"enabled": {
|
||||
"autoPairUpdates": true,
|
||||
"websocketAPI": true
|
||||
}
|
||||
},
|
||||
"bankAccounts": [
|
||||
{
|
||||
"enabled": false,
|
||||
"bankName": "",
|
||||
"bankAddress": "",
|
||||
"bankPostalCode": "",
|
||||
"bankPostalCity": "",
|
||||
"bankCountry": "",
|
||||
"accountName": "",
|
||||
"accountNumber": "",
|
||||
"swiftCode": "",
|
||||
"iban": "",
|
||||
"supportedCurrencies": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LocalBitcoins",
|
||||
"enabled": true,
|
||||
|
||||
1
testdata/exchangelist.csv
vendored
1
testdata/exchangelist.csv
vendored
@@ -18,7 +18,6 @@ hitbtc,
|
||||
huobi,
|
||||
itbit,
|
||||
kraken,
|
||||
lakebtc,
|
||||
lbank,
|
||||
localbitcoins,
|
||||
okcoin international,
|
||||
|
||||
|
Reference in New Issue
Block a user