mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-05 23:16:53 +00:00
Added support for Binance exchange
This commit is contained in:
@@ -20,6 +20,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
|||||||
|----------|------|-----------|-----|
|
|----------|------|-----------|-----|
|
||||||
| Alphapoint | Yes | Yes | NA |
|
| Alphapoint | Yes | Yes | NA |
|
||||||
| ANXPRO | Yes | No | NA |
|
| ANXPRO | Yes | No | NA |
|
||||||
|
| Bitfinex | Yes | No | NA |
|
||||||
| Bitfinex | Yes | Yes | NA |
|
| Bitfinex | Yes | Yes | NA |
|
||||||
| Bithumb | Yes | NA | NA |
|
| Bithumb | Yes | NA | NA |
|
||||||
| Bitstamp | Yes | Yes | NA |
|
| Bitstamp | Yes | Yes | NA |
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func TestGetEnabledExchanges(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exchanges := cfg.GetEnabledExchanges()
|
exchanges := cfg.GetEnabledExchanges()
|
||||||
if len(exchanges) != 21 {
|
if len(exchanges) != 22 {
|
||||||
t.Error(
|
t.Error(
|
||||||
"Test failed. TestGetEnabledExchanges. Enabled exchanges value mismatch",
|
"Test failed. TestGetEnabledExchanges. Enabled exchanges value mismatch",
|
||||||
)
|
)
|
||||||
@@ -141,7 +141,7 @@ func TestGetDisabledExchanges(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCountEnabledExchanges(t *testing.T) {
|
func TestCountEnabledExchanges(t *testing.T) {
|
||||||
defaultEnabledExchanges := 21
|
defaultEnabledExchanges := 22
|
||||||
GetConfigEnabledExchanges := GetConfig()
|
GetConfigEnabledExchanges := GetConfig()
|
||||||
err := GetConfigEnabledExchanges.LoadConfig(ConfigTestFile)
|
err := GetConfigEnabledExchanges.LoadConfig(ConfigTestFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -80,6 +80,27 @@
|
|||||||
"Index": "BTC"
|
"Index": "BTC"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Name": "Binance",
|
||||||
|
"Enabled": true,
|
||||||
|
"Verbose": false,
|
||||||
|
"Websocket": false,
|
||||||
|
"UseSandbox": false,
|
||||||
|
"RESTPollingDelay": 10,
|
||||||
|
"AuthenticatedAPISupport": false,
|
||||||
|
"APIKey": "Key",
|
||||||
|
"APISecret": "Secret",
|
||||||
|
"AvailablePairs": "ETHBTC,LTCBTC,BNBBTC,NEOBTC,123456,QTUMETH,EOSETH,SNTETH,BNTETH,BCCBTC,GASBTC,BNBETH,BTCUSDT,ETHUSDT,HSRBTC,OAXETH,DNTETH,MCOETH,ICNETH,MCOBTC,WTCBTC,WTCETH,LRCBTC,LRCETH,QTUMBTC,YOYOBTC,OMGBTC,OMGETH,ZRXBTC,ZRXETH,STRATBTC,STRATETH,SNGLSBTC,SNGLSETH,BQXBTC,BQXETH,KNCBTC,KNCETH,FUNBTC,FUNETH,SNMBTC,SNMETH,NEOETH,IOTABTC,IOTAETH,LINKBTC,LINKETH,XVGBTC,XVGETH,CTRBTC,CTRETH,SALTBTC,SALTETH,MDABTC,MDAETH,MTLBTC,MTLETH,SUBBTC,SUBETH,EOSBTC,SNTBTC,ETCETH,ETCBTC,MTHBTC,MTHETH,ENGBTC,ENGETH,DNTBTC,ZECBTC,ZECETH,BNTBTC,ASTBTC,ASTETH,DASHBTC,DASHETH,OAXBTC,ICNBTC,BTGBTC,BTGETH,EVXBTC,EVXETH,REQBTC,REQETH,VIBBTC,VIBETH,HSRETH,TRXBTC,TRXETH,POWRBTC,POWRETH,ARKBTC,ARKETH,YOYOETH,XRPBTC,XRPETH,MODBTC,MODETH,ENJBTC,ENJETH,STORJBTC,STORJETH,BNBUSDT,VENBNB,YOYOBNB,POWRBNB,VENBTC,VENETH,KMDBTC,KMDETH,NULSBNB,RCNBTC,RCNETH,RCNBNB,NULSBTC,NULSETH,RDNBTC,RDNETH,RDNBNB,XMRBTC,XMRETH,DLTBNB,WTCBNB,DLTBTC,DLTETH,AMBBTC,AMBETH,AMBBNB,BCCETH,BCCUSDT,BCCBNB,BATBTC,BATETH,BATBNB,BCPTBTC,BCPTETH,BCPTBNB,ARNBTC,ARNETH,GVTBTC,GVTETH,CDTBTC,CDTETH,GXSBTC,GXSETH,NEOUSDT,NEOBNB,POEBTC,POEETH,QSPBTC,QSPETH,QSPBNB,BTSBTC,BTSETH,BTSBNB,XZCBTC,XZCETH,XZCBNB,LSKBTC,LSKETH,LSKBNB,TNTBTC,TNTETH,FUELBTC,FUELETH,MANABTC,MANAETH,BCDBTC,BCDETH,DGDBTC,DGDETH,IOTABNB,ADXBTC,ADXETH,ADXBNB,ADABTC,ADAETH,PPTBTC,PPTETH,CMTBTC,CMTETH,CMTBNB,XLMBTC,XLMETH,XLMBNB,CNDBTC,CNDETH,CNDBNB,LENDBTC,LENDETH,WABIBTC,WABIETH,WABIBNB,LTCETH,LTCUSDT,LTCBNB,TNBBTC,TNBETH,WAVESBTC,WAVESETH,WAVESBNB,GTOBTC,GTOETH,GTOBNB,ICXBTC,ICXETH,ICXBNB,OSTBTC,OSTETH,OSTBNB,ELFBTC,ELFETH,AIONBTC,AIONETH,AIONBNB,NEBLBTC,NEBLETH,NEBLBNB,BRDBTC,BRDETH,BRDBNB,MCOBNB,EDOBTC,EDOETH,WINGSBTC,WINGSETH,NAVBTC,NAVETH,NAVBNB,LUNBTC,LUNETH,TRIGBTC,TRIGETH,TRIGBNB,APPCBTC,APPCETH,APPCBNB,VIBEBTC,VIBEETH,RLCBTC,RLCETH,RLCBNB,INSBTC,INSETH,PIVXBTC,PIVXETH,PIVXBNB,IOSTBTC,IOSTETH,CHATBTC,CHATETH",
|
||||||
|
"EnabledPairs": "BTCUSDT",
|
||||||
|
"BaseCurrencies": "USD",
|
||||||
|
"AssetTypes": "SPOT",
|
||||||
|
"ConfigCurrencyPairFormat": {
|
||||||
|
"Uppercase": true
|
||||||
|
},
|
||||||
|
"RequestCurrencyPairFormat": {
|
||||||
|
"Uppercase": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Name": "Bitfinex",
|
"Name": "Bitfinex",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/thrasher-/gocryptotrader/common"
|
"github.com/thrasher-/gocryptotrader/common"
|
||||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/anx"
|
"github.com/thrasher-/gocryptotrader/exchanges/anx"
|
||||||
|
"github.com/thrasher-/gocryptotrader/exchanges/binance"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/bitfinex"
|
"github.com/thrasher-/gocryptotrader/exchanges/bitfinex"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/bithumb"
|
"github.com/thrasher-/gocryptotrader/exchanges/bithumb"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/bitstamp"
|
"github.com/thrasher-/gocryptotrader/exchanges/bitstamp"
|
||||||
@@ -128,6 +129,8 @@ func LoadExchange(name string) error {
|
|||||||
switch nameLower {
|
switch nameLower {
|
||||||
case "anx":
|
case "anx":
|
||||||
exch = new(anx.ANX)
|
exch = new(anx.ANX)
|
||||||
|
case "binance":
|
||||||
|
exch = new(binance.Binance)
|
||||||
case "bitfinex":
|
case "bitfinex":
|
||||||
exch = new(bitfinex.Bitfinex)
|
exch = new(bitfinex.Bitfinex)
|
||||||
case "bithumb":
|
case "bithumb":
|
||||||
|
|||||||
497
exchanges/binance/binance.go
Normal file
497
exchanges/binance/binance.go
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
package binance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/thrasher-/gocryptotrader/common"
|
||||||
|
"github.com/thrasher-/gocryptotrader/config"
|
||||||
|
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||||
|
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Binance is the overarching type across the Bithumb package
|
||||||
|
type Binance struct {
|
||||||
|
exchange.Base
|
||||||
|
|
||||||
|
// valid string list that a required by the exchange
|
||||||
|
validLimits []string
|
||||||
|
validIntervals []string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
apiURL = "https://api.binance.com"
|
||||||
|
|
||||||
|
// Public endpoints
|
||||||
|
exchangeInfo = "/api/v1/exchangeInfo"
|
||||||
|
orderBookDepth = "/api/v1/depth"
|
||||||
|
recentTrades = "/api/v1/trades"
|
||||||
|
historicalTrades = "/api/v1/historicalTrades"
|
||||||
|
aggregatedTrades = "/api/v1/aggTrades"
|
||||||
|
candleStick = "/api/v1/klines"
|
||||||
|
priceChange = "/api/v1/ticker/24hr"
|
||||||
|
symbolPrice = "/api/v3/ticker/price"
|
||||||
|
bestPrice = "/api/v3/ticker/bookTicker"
|
||||||
|
|
||||||
|
// Authenticated endpoints
|
||||||
|
|
||||||
|
newOrderTest = "/api/v3/order/test"
|
||||||
|
newOrder = "/api/v3/order"
|
||||||
|
queryOrder = "/api/v3/order"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetDefaults sets the basic defaults for Binance
|
||||||
|
func (b *Binance) SetDefaults() {
|
||||||
|
b.Name = "Binance"
|
||||||
|
b.Enabled = false
|
||||||
|
b.Verbose = false
|
||||||
|
b.Websocket = false
|
||||||
|
b.RESTPollingDelay = 10
|
||||||
|
b.RequestCurrencyPairFormat.Delimiter = ""
|
||||||
|
b.RequestCurrencyPairFormat.Uppercase = true
|
||||||
|
b.ConfigCurrencyPairFormat.Delimiter = ""
|
||||||
|
b.ConfigCurrencyPairFormat.Uppercase = true
|
||||||
|
b.AssetTypes = []string{ticker.Spot}
|
||||||
|
b.SetValues()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup takes in the supplied exchange configuration details and sets params
|
||||||
|
func (b *Binance) Setup(exch config.ExchangeConfig) {
|
||||||
|
if !exch.Enabled {
|
||||||
|
b.SetEnabled(false)
|
||||||
|
} else {
|
||||||
|
b.Enabled = true
|
||||||
|
b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||||
|
b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||||
|
b.RESTPollingDelay = exch.RESTPollingDelay
|
||||||
|
b.Verbose = exch.Verbose
|
||||||
|
b.Websocket = exch.Websocket
|
||||||
|
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||||
|
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||||
|
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||||
|
err := b.SetCurrencyPairFormat()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = b.SetAssetTypes()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExchangeValidCurrencyPairs returns the full pair list from the exchange
|
||||||
|
// at the moment do not integrate with config currency pairs automatically
|
||||||
|
func (b *Binance) GetExchangeValidCurrencyPairs() (string, error) {
|
||||||
|
var validCurrencyPairs []string
|
||||||
|
|
||||||
|
info, err := b.GetExchangeInfo()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, symbol := range info.Symbols {
|
||||||
|
validCurrencyPairs = append(validCurrencyPairs, symbol.Symbol)
|
||||||
|
}
|
||||||
|
return common.JoinStrings(validCurrencyPairs, ","), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExchangeInfo returns exchange information. Check binance_types for more
|
||||||
|
// information
|
||||||
|
func (b *Binance) GetExchangeInfo() (ExchangeInfo, error) {
|
||||||
|
var resp ExchangeInfo
|
||||||
|
path := apiURL + exchangeInfo
|
||||||
|
|
||||||
|
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrderBook returns full orderbook information
|
||||||
|
//
|
||||||
|
// symbol: string of currency pair
|
||||||
|
// limit: returned limit amount
|
||||||
|
func (b *Binance) GetOrderBook(symbol string, limit int64) (OrderBook, error) {
|
||||||
|
orderbook, resp := OrderBook{}, OrderBookData{}
|
||||||
|
|
||||||
|
if err := b.CheckLimit(limit); err != nil {
|
||||||
|
return orderbook, err
|
||||||
|
}
|
||||||
|
if err := b.CheckSymbol(symbol); err != nil {
|
||||||
|
return orderbook, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", common.StringToUpper(symbol))
|
||||||
|
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s?%s", apiURL, orderBookDepth, params.Encode())
|
||||||
|
|
||||||
|
if err := common.SendHTTPGetRequest(path, true, b.Verbose, &resp); err != nil {
|
||||||
|
return orderbook, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, asks := range resp.Asks {
|
||||||
|
var ASK struct {
|
||||||
|
Price float64
|
||||||
|
Quantity float64
|
||||||
|
}
|
||||||
|
for i, ask := range asks.([]interface{}) {
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
ASK.Price, _ = strconv.ParseFloat(ask.(string), 64)
|
||||||
|
case 1:
|
||||||
|
ASK.Quantity, _ = strconv.ParseFloat(ask.(string), 64)
|
||||||
|
}
|
||||||
|
orderbook.Asks = append(orderbook.Asks, ASK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bids := range resp.Bids {
|
||||||
|
var BID struct {
|
||||||
|
Price float64
|
||||||
|
Quantity float64
|
||||||
|
}
|
||||||
|
for i, bid := range bids.([]interface{}) {
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
BID.Price, _ = strconv.ParseFloat(bid.(string), 64)
|
||||||
|
case 1:
|
||||||
|
BID.Quantity, _ = strconv.ParseFloat(bid.(string), 64)
|
||||||
|
}
|
||||||
|
orderbook.Bids = append(orderbook.Bids, BID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return orderbook, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRecentTrades returns recent trade activity
|
||||||
|
//
|
||||||
|
// symbol: string of currency pair
|
||||||
|
// limit: returned limit amount WARNING: MAX 500!
|
||||||
|
func (b *Binance) GetRecentTrades(symbol string, limit int64) ([]RecentTrade, error) {
|
||||||
|
resp := []RecentTrade{}
|
||||||
|
|
||||||
|
if err := b.CheckLimit(limit); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if err := b.CheckSymbol(symbol); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", common.StringToUpper(symbol))
|
||||||
|
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s?%s", apiURL, recentTrades, params.Encode())
|
||||||
|
|
||||||
|
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHistoricalTrades returns historical trade activity
|
||||||
|
//
|
||||||
|
// symbol: string of currency pair
|
||||||
|
// limit: returned limit amount WARNING: MAX 500! (NOT REQUIRED)
|
||||||
|
// fromID:
|
||||||
|
func (b *Binance) GetHistoricalTrades(symbol string, limit, fromID int64) ([]HistoricalTrade, error) {
|
||||||
|
resp := []HistoricalTrade{}
|
||||||
|
|
||||||
|
if err := b.CheckLimit(limit); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if err := b.CheckSymbol(symbol); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", common.StringToUpper(symbol))
|
||||||
|
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||||
|
params.Set("fromid", strconv.FormatInt(fromID, 10))
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s?%s", apiURL, historicalTrades, params.Encode())
|
||||||
|
|
||||||
|
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAggregatedTrades returns aggregated trade activity
|
||||||
|
//
|
||||||
|
// symbol: string of currency pair
|
||||||
|
// limit: returned limit amount WARNING: MAX 500!
|
||||||
|
func (b *Binance) GetAggregatedTrades(symbol string, limit int64) ([]AggregatedTrade, error) {
|
||||||
|
resp := []AggregatedTrade{}
|
||||||
|
|
||||||
|
if err := b.CheckLimit(limit); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if err := b.CheckSymbol(symbol); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", common.StringToUpper(symbol))
|
||||||
|
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s?%s", apiURL, aggregatedTrades, params.Encode())
|
||||||
|
|
||||||
|
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCandleStickData returns candle stick data
|
||||||
|
//
|
||||||
|
// symbol:
|
||||||
|
// limit:
|
||||||
|
// interval
|
||||||
|
func (b *Binance) GetCandleStickData(symbol, interval string, limit int64) ([]CandleStick, error) {
|
||||||
|
var resp interface{}
|
||||||
|
var kline []CandleStick
|
||||||
|
|
||||||
|
if err := b.CheckLimit(limit); err != nil {
|
||||||
|
return kline, err
|
||||||
|
}
|
||||||
|
if err := b.CheckSymbol(symbol); err != nil {
|
||||||
|
return kline, err
|
||||||
|
}
|
||||||
|
if err := b.CheckIntervals(interval); err != nil {
|
||||||
|
return kline, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", common.StringToUpper(symbol))
|
||||||
|
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||||
|
params.Set("interval", interval)
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s?%s", apiURL, candleStick, params.Encode())
|
||||||
|
|
||||||
|
if err := common.SendHTTPGetRequest(path, true, b.Verbose, &resp); err != nil {
|
||||||
|
return kline, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, responseData := range resp.([]interface{}) {
|
||||||
|
var candle CandleStick
|
||||||
|
for i, individualData := range responseData.([]interface{}) {
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
candle.OpenTime = individualData.(float64)
|
||||||
|
case 1:
|
||||||
|
candle.Open, _ = strconv.ParseFloat(individualData.(string), 64)
|
||||||
|
case 2:
|
||||||
|
candle.High, _ = strconv.ParseFloat(individualData.(string), 64)
|
||||||
|
case 3:
|
||||||
|
candle.Low, _ = strconv.ParseFloat(individualData.(string), 64)
|
||||||
|
case 4:
|
||||||
|
candle.Close, _ = strconv.ParseFloat(individualData.(string), 64)
|
||||||
|
case 5:
|
||||||
|
candle.Volume, _ = strconv.ParseFloat(individualData.(string), 64)
|
||||||
|
case 6:
|
||||||
|
candle.CloseTime = individualData.(float64)
|
||||||
|
case 7:
|
||||||
|
candle.QuoteAssetVolume, _ = strconv.ParseFloat(individualData.(string), 64)
|
||||||
|
case 8:
|
||||||
|
candle.TradeCount = individualData.(float64)
|
||||||
|
case 9:
|
||||||
|
candle.TakerBuyAssetVolume, _ = strconv.ParseFloat(individualData.(string), 64)
|
||||||
|
case 10:
|
||||||
|
candle.TakerBuyQuoteAssetVolume, _ = strconv.ParseFloat(individualData.(string), 64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kline = append(kline, candle)
|
||||||
|
}
|
||||||
|
return kline, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPriceChangeStats returns price change statistics for the last 24 hours
|
||||||
|
//
|
||||||
|
// symbol: string of currency pair
|
||||||
|
func (b *Binance) GetPriceChangeStats(symbol string) (PriceChangeStats, error) {
|
||||||
|
resp := PriceChangeStats{}
|
||||||
|
|
||||||
|
if err := b.CheckSymbol(symbol); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", common.StringToUpper(symbol))
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s?%s", apiURL, priceChange, params.Encode())
|
||||||
|
|
||||||
|
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestSpotPrice returns latest spot price of symbol
|
||||||
|
//
|
||||||
|
// symbol: string of currency pair
|
||||||
|
func (b *Binance) GetLatestSpotPrice(symbol string) (SymbolPrice, error) {
|
||||||
|
resp := SymbolPrice{}
|
||||||
|
|
||||||
|
if err := b.CheckSymbol(symbol); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", common.StringToUpper(symbol))
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s?%s", apiURL, symbolPrice, params.Encode())
|
||||||
|
|
||||||
|
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBestPrice returns the latest best price for symbol
|
||||||
|
//
|
||||||
|
// symbol: string of currency pair
|
||||||
|
func (b *Binance) GetBestPrice(symbol string) (BestPrice, error) {
|
||||||
|
resp := BestPrice{}
|
||||||
|
|
||||||
|
if err := b.CheckSymbol(symbol); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", common.StringToUpper(symbol))
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s?%s", apiURL, bestPrice, params.Encode())
|
||||||
|
|
||||||
|
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOrderTest sends a new order
|
||||||
|
func (b *Binance) NewOrderTest() (interface{}, error) {
|
||||||
|
var resp interface{}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s", apiURL, newOrderTest)
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", "BTCUSDT")
|
||||||
|
params.Set("side", "BUY")
|
||||||
|
params.Set("type", "MARKET")
|
||||||
|
params.Set("quantity", "0.1")
|
||||||
|
|
||||||
|
return resp, b.SendAuthHTTPRequest("POST", path, params, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOrder sends a new order to Binance
|
||||||
|
func (b *Binance) NewOrder(o NewOrderRequest) (NewOrderResponse, error) {
|
||||||
|
var resp NewOrderResponse
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s", apiURL, newOrderTest)
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", o.Symbol)
|
||||||
|
params.Set("side", o.Side)
|
||||||
|
params.Set("type", o.TradeType)
|
||||||
|
params.Set("timeInForce", o.TimeInForce)
|
||||||
|
params.Set("quantity", strconv.FormatFloat(o.Quantity, 'f', -1, 64))
|
||||||
|
params.Set("price", strconv.FormatFloat(o.Price, 'f', -1, 64))
|
||||||
|
params.Set("newClientOrderID", o.NewClientOrderID)
|
||||||
|
params.Set("stopPrice", strconv.FormatFloat(o.StopPrice, 'f', -1, 64))
|
||||||
|
params.Set("icebergQty", strconv.FormatFloat(o.IcebergQty, 'f', -1, 64))
|
||||||
|
params.Set("newOrderRespType", o.NewOrderRespType)
|
||||||
|
|
||||||
|
if err := b.SendAuthHTTPRequest("POST", path, params, &resp); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Code != 0 {
|
||||||
|
return resp, errors.New(resp.Msg)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryOrder returns information on a past order
|
||||||
|
func (b *Binance) QueryOrder(symbol, origClientOrderID string, orderID int64) (QueryOrderData, error) {
|
||||||
|
var resp QueryOrderData
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s%s", apiURL, queryOrder)
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("symbol", common.StringToUpper(symbol))
|
||||||
|
params.Set("origClientOrderId", origClientOrderID)
|
||||||
|
params.Set("orderId", strconv.FormatInt(orderID, 10))
|
||||||
|
|
||||||
|
if err := b.SendAuthHTTPRequest("GET", path, params, &resp); err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Code != 0 {
|
||||||
|
return resp, errors.New(resp.Msg)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendAuthHTTPRequest something
|
||||||
|
func (b *Binance) SendAuthHTTPRequest(method, path string, params url.Values, result interface{}) error {
|
||||||
|
if !b.AuthenticatedAPISupport {
|
||||||
|
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if params == nil {
|
||||||
|
params = url.Values{}
|
||||||
|
}
|
||||||
|
params.Set("recvWindow", strconv.FormatInt(5000, 10))
|
||||||
|
params.Set("timestamp", strconv.FormatInt(time.Now().Unix()*1000, 10))
|
||||||
|
|
||||||
|
signature := params.Encode()
|
||||||
|
hmacSigned := common.GetHMAC(common.HashSHA256, []byte(signature), []byte(b.APISecret))
|
||||||
|
hmacSignedStr := common.HexEncodeToString(hmacSigned)
|
||||||
|
params.Set("signature", hmacSignedStr)
|
||||||
|
|
||||||
|
if b.Nonce.Get() == 0 {
|
||||||
|
b.Nonce.Set(time.Now().UnixNano() / int64(time.Millisecond))
|
||||||
|
} else {
|
||||||
|
b.Nonce.Inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := make(map[string]string)
|
||||||
|
headers["X-MBX-APIKEY"] = b.APIKey
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||||
|
|
||||||
|
if b.Verbose {
|
||||||
|
log.Printf("sent path: \n%s\n", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := common.SendHTTPRequest(method, path, headers, bytes.NewBufferString(params.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Verbose {
|
||||||
|
log.Printf("Received raw: \n%s\n", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = common.JSONDecode([]byte(resp), &result); err != nil {
|
||||||
|
return errors.New("sendAuthenticatedHTTPRequest: Unable to JSON Unmarshal response." + err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckLimit checks value against a variable list
|
||||||
|
func (b *Binance) CheckLimit(limit int64) error {
|
||||||
|
if !common.DataContains(b.validLimits, strconv.FormatInt(limit, 10)) {
|
||||||
|
return errors.New("Incorrect limit values - valid values are 5, 10, 20, 50, 100, 500, 1000")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckSymbol checks value against a variable list
|
||||||
|
func (b *Binance) CheckSymbol(symbol string) error {
|
||||||
|
if !common.DataContains(b.AvailablePairs, symbol) {
|
||||||
|
return errors.New("Incorrect symbol values - please check available pairs in configuration")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckIntervals checks value against a variable list
|
||||||
|
func (b *Binance) CheckIntervals(interval string) error {
|
||||||
|
if !common.DataContains(b.validIntervals, interval) {
|
||||||
|
return errors.New(`Incorrect interval values - valid values are "1m","3m","5m","15m","30m","1h","2h","4h","6h","8h","12h","1d","3d","1w","1M"`)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValues sets the default valid values
|
||||||
|
func (b *Binance) SetValues() {
|
||||||
|
b.validLimits = []string{"5", "10", "20", "50", "100", "500", "1000"}
|
||||||
|
b.validIntervals = []string{"1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h", "1d", "3d", "1w", "1M"}
|
||||||
|
}
|
||||||
130
exchanges/binance/binance_test.go
Normal file
130
exchanges/binance/binance_test.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package binance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/thrasher-/gocryptotrader/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Please supply your own keys here for due diligence testing
|
||||||
|
const (
|
||||||
|
testAPIKey = ""
|
||||||
|
testAPISecret = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
var b Binance
|
||||||
|
|
||||||
|
func TestSetDefaults(t *testing.T) {
|
||||||
|
b.SetDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetup(t *testing.T) {
|
||||||
|
cfg := config.GetConfig()
|
||||||
|
cfg.LoadConfig("../../testdata/configtest.json")
|
||||||
|
binanceConfig, err := cfg.GetExchangeConfig("Binance")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Test Failed - Binance Setup() init error")
|
||||||
|
}
|
||||||
|
|
||||||
|
binanceConfig.AuthenticatedAPISupport = true
|
||||||
|
binanceConfig.APIKey = testAPIKey
|
||||||
|
binanceConfig.APISecret = testAPISecret
|
||||||
|
|
||||||
|
b.Setup(binanceConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetExchangeValidCurrencyPairs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.GetExchangeValidCurrencyPairs()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Test Failed - Binance GetExchangeValidCurrencyPairs() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOrderBook(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.GetOrderBook("BTCUSDT", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Test Failed - Binance GetOrderBook() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetRecentTrades(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.GetRecentTrades("BTCUSDT", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Test Failed - Binance GetRecentTrades() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetHistoricalTrades(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.GetHistoricalTrades("BTCUSDT", 5, 1337)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Test Failed - Binance GetHistoricalTrades() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAggregatedTrades(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.GetAggregatedTrades("BTCUSDT", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Test Failed - Binance GetAggregatedTrades() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCandleStickData(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.GetCandleStickData("BTCUSDT", "1d", 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Test Failed - Binance GetCandleStickData() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPriceChangeStats(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.GetPriceChangeStats("BTCUSDT")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Test Failed - Binance GetPriceChangeStats() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetLatestSpotPrice(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.GetLatestSpotPrice("BTCUSDT")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Test Failed - Binance GetLatestSpotPrice() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetBestPrice(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.GetBestPrice("BTCUSDT")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Test Failed - Binance GetBestPrice() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewOrderTest(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.NewOrderTest()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Test Failed - Binance NewOrderTest() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewOrder(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.NewOrder(NewOrderRequest{})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Test Failed - Binance NewOrder() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQueryOrder(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := b.QueryOrder("", "", 1337)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Test Failed - Binance QueryOrder() error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
204
exchanges/binance/binance_types.go
Normal file
204
exchanges/binance/binance_types.go
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
package binance
|
||||||
|
|
||||||
|
// ExchangeInfo holds the full exchange information type
|
||||||
|
type ExchangeInfo struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Timezone string `json:"timezone"`
|
||||||
|
Servertime int64 `json:"serverTime"`
|
||||||
|
RateLimits []struct {
|
||||||
|
RateLimitType string `json:"rateLimitType"`
|
||||||
|
Interval string `json:"interval"`
|
||||||
|
Limit int `json:"limit"`
|
||||||
|
} `json:"rateLimits"`
|
||||||
|
ExchangeFilters interface{} `json:"exchangeFilters"`
|
||||||
|
Symbols []struct {
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
BaseAsset string `json:"baseAsset"`
|
||||||
|
BaseAssetPrecision int `json:"baseAssetPrecision"`
|
||||||
|
QuoteAsset string `json:"quoteAsset"`
|
||||||
|
QuotePrecision int `json:"quotePrecision"`
|
||||||
|
OrderTypes []string `json:"orderTypes"`
|
||||||
|
IcebergAllowed bool `json:"icebergAllowed"`
|
||||||
|
Filters []struct {
|
||||||
|
FilterType string `json:"filterType"`
|
||||||
|
MinPrice float64 `json:"minPrice,string"`
|
||||||
|
MaxPrice float64 `json:"maxPrice,string"`
|
||||||
|
TickSize float64 `json:"tickSize,string"`
|
||||||
|
MinQty float64 `json:"minQty,string"`
|
||||||
|
MaxQty float64 `json:"maxQty,string"`
|
||||||
|
StepSize float64 `json:"stepSize,string"`
|
||||||
|
MinNotional float64 `json:"minNotional,string"`
|
||||||
|
} `json:"filters"`
|
||||||
|
} `json:"symbols"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderBookData is resp data from orderbook endpoint
|
||||||
|
type OrderBookData struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
LastUpdateID int64 `json:"lastUpdateId"`
|
||||||
|
Bids []interface{} `json:"bids"`
|
||||||
|
Asks []interface{} `json:"asks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderBook actual structured data that can be used for orderbook
|
||||||
|
type OrderBook struct {
|
||||||
|
Code int
|
||||||
|
Msg string
|
||||||
|
Bids []struct {
|
||||||
|
Price float64
|
||||||
|
Quantity float64
|
||||||
|
}
|
||||||
|
Asks []struct {
|
||||||
|
Price float64
|
||||||
|
Quantity float64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecentTrade holds recent trade data
|
||||||
|
type RecentTrade struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Price float64 `json:"price,string"`
|
||||||
|
Quantity float64 `json:"qty,string"`
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
IsBuyerMaker bool `json:"isBuyerMaker"`
|
||||||
|
IsBestMatch bool `json:"isBestMatch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HistoricalTrade holds recent trade data
|
||||||
|
type HistoricalTrade struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Price float64 `json:"price,string"`
|
||||||
|
Quantity float64 `json:"qty,string"`
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
IsBuyerMaker bool `json:"isBuyerMaker"`
|
||||||
|
IsBestMatch bool `json:"isBestMatch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AggregatedTrade holds aggregated trade information
|
||||||
|
type AggregatedTrade struct {
|
||||||
|
ATradeID int64 `json:"a"`
|
||||||
|
Price float64 `json:"p,string"`
|
||||||
|
Quantity float64 `json:"q,string"`
|
||||||
|
FirstTradeID int64 `json:"f"`
|
||||||
|
LastTradeID int64 `json:"l"`
|
||||||
|
TimeStamp int64 `json:"T"`
|
||||||
|
Maker bool `json:"m"`
|
||||||
|
BestMatchPrice bool `json:"M"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CandleStick holds kline data
|
||||||
|
type CandleStick struct {
|
||||||
|
OpenTime float64
|
||||||
|
Open float64
|
||||||
|
High float64
|
||||||
|
Low float64
|
||||||
|
Close float64
|
||||||
|
Volume float64
|
||||||
|
CloseTime float64
|
||||||
|
QuoteAssetVolume float64
|
||||||
|
TradeCount float64
|
||||||
|
TakerBuyAssetVolume float64
|
||||||
|
TakerBuyQuoteAssetVolume float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// PriceChangeStats contains statistics for the last 24 hours trade
|
||||||
|
type PriceChangeStats struct {
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
PriceChange float64 `json:"priceChange,string"`
|
||||||
|
PriceChangePercent float64 `json:"priceChangePercent,string"`
|
||||||
|
WeightedAvgPrice float64 `json:"weightedAvgPrice,string"`
|
||||||
|
PrevClosePrice float64 `json:"prevClosePrice,string"`
|
||||||
|
LastPrice float64 `json:"lastPrice,string"`
|
||||||
|
LastQty float64 `json:"lastQty,string"`
|
||||||
|
BidPrice float64 `json:"bidPrice,string"`
|
||||||
|
AskPrice float64 `json:"askPrice,string"`
|
||||||
|
OpenPrice float64 `json:"openPrice,string"`
|
||||||
|
HighPrice float64 `json:"highPrice,string"`
|
||||||
|
LowPrice float64 `json:"lowPrice,string"`
|
||||||
|
Volume float64 `json:"volume,string"`
|
||||||
|
QuoteVolume float64 `json:"quoteVolume,string"`
|
||||||
|
OpenTime int64 `json:"openTime"`
|
||||||
|
CloseTime int64 `json:"closeTime"`
|
||||||
|
FirstID int64 `json:"fristId"`
|
||||||
|
LastID int64 `json:"lastId"`
|
||||||
|
Count int64 `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SymbolPrice holds basic symbol price
|
||||||
|
type SymbolPrice struct {
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
Price float64 `json:"price,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BestPrice holds best price data
|
||||||
|
type BestPrice struct {
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
BidPrice float64 `json:"bidPrice,string"`
|
||||||
|
BidQty float64 `json:"bidQty,string"`
|
||||||
|
AskPrice float64 `json:"askPrice,string"`
|
||||||
|
AskQty float64 `json:"askQty,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOrderRequest request type
|
||||||
|
type NewOrderRequest struct {
|
||||||
|
Symbol string
|
||||||
|
Side string
|
||||||
|
TradeType string
|
||||||
|
TimeInForce string
|
||||||
|
Quantity float64
|
||||||
|
Price float64
|
||||||
|
NewClientOrderID string
|
||||||
|
StopPrice float64
|
||||||
|
IcebergQty float64
|
||||||
|
NewOrderRespType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOrderResponse is the return structured response from the exchange
|
||||||
|
type NewOrderResponse struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
OrderID int64 `json:"orderId"`
|
||||||
|
ClientOrderID string `json:"clientOrderId"`
|
||||||
|
TransactionTime int64 `json:"transactTime"`
|
||||||
|
Price float64 `json:"price,string"`
|
||||||
|
OrigQty float64 `json:"origQty,string"`
|
||||||
|
ExecutedQty float64 `json:"executedQty,string"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
TimeInForce string `json:"timeInForce"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Side string `json:"side"`
|
||||||
|
Fills []struct {
|
||||||
|
Price float64 `json:"price,string"`
|
||||||
|
Qty float64 `json:"qty,string"`
|
||||||
|
Commission float64 `json:"commission,string"`
|
||||||
|
CommissionAsset float64 `json:"commissionAsset,string"`
|
||||||
|
} `json:"fills"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryOrderData holds query order data
|
||||||
|
type QueryOrderData struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
OrderID int64 `json:"orderId"`
|
||||||
|
ClientOrderID string `json:"clientOrderId"`
|
||||||
|
Price float64 `json:"price,string"`
|
||||||
|
OrigQty float64 `json:"origQty,string"`
|
||||||
|
ExecutedQty float64 `json:"executedQty,string"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
TimeInForce string `json:"timeInForce"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Side string `json:"side"`
|
||||||
|
StopPrice float64 `json:"stopPrice,string"`
|
||||||
|
IcebergQty float64 `json:"icebergQty,string"`
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
IsWorking bool `json:"isWorking"`
|
||||||
|
}
|
||||||
95
exchanges/binance/binance_wrapper.go
Normal file
95
exchanges/binance/binance_wrapper.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package binance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/thrasher-/gocryptotrader/common"
|
||||||
|
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||||
|
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||||
|
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||||
|
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start starts the OKEX go routine
|
||||||
|
func (b *Binance) Start() {
|
||||||
|
go b.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run implements the OKEX wrapper
|
||||||
|
func (b *Binance) Run() {
|
||||||
|
if b.Verbose {
|
||||||
|
log.Printf("%s Websocket: %s. (url: %s).\n", b.GetName(), common.IsEnabled(b.Websocket), b.WebsocketURL)
|
||||||
|
log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay)
|
||||||
|
log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateTicker updates and returns the ticker for a currency pair
|
||||||
|
func (b *Binance) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||||
|
var tickerPrice ticker.Price
|
||||||
|
|
||||||
|
tick, err := b.GetPriceChangeStats(p.Pair().String())
|
||||||
|
if err != nil {
|
||||||
|
return tickerPrice, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tickerPrice.Pair = p
|
||||||
|
tickerPrice.Ask = tick.AskPrice
|
||||||
|
tickerPrice.Bid = tick.BidPrice
|
||||||
|
tickerPrice.High = tick.HighPrice
|
||||||
|
tickerPrice.Last = tick.LastPrice
|
||||||
|
tickerPrice.Low = tick.LowPrice
|
||||||
|
tickerPrice.Volume = tick.LastQty
|
||||||
|
|
||||||
|
ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
|
||||||
|
|
||||||
|
return ticker.GetTicker(b.Name, p, assetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTickerPrice returns the ticker for a currency pair
|
||||||
|
func (b *Binance) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||||
|
tickerNew, err := ticker.GetTicker(b.GetName(), p, assetType)
|
||||||
|
if err != nil {
|
||||||
|
return b.UpdateTicker(p, assetType)
|
||||||
|
}
|
||||||
|
return tickerNew, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrderbookEx returns orderbook base on the currency pair
|
||||||
|
func (b *Binance) GetOrderbookEx(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||||
|
ob, err := orderbook.GetOrderbook(b.GetName(), currency, assetType)
|
||||||
|
if err != nil {
|
||||||
|
return b.UpdateOrderbook(currency, assetType)
|
||||||
|
}
|
||||||
|
return ob, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||||
|
func (b *Binance) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||||
|
var orderBook orderbook.Base
|
||||||
|
currency := p.GetFirstCurrency().String()
|
||||||
|
|
||||||
|
orderbookNew, err := b.GetOrderBook(currency, 1000)
|
||||||
|
if err != nil {
|
||||||
|
return orderBook, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bids := range orderbookNew.Bids {
|
||||||
|
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: bids.Quantity, Price: bids.Price})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, asks := range orderbookNew.Asks {
|
||||||
|
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: asks.Quantity, Price: asks.Price})
|
||||||
|
}
|
||||||
|
|
||||||
|
orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType)
|
||||||
|
return orderbook.GetOrderbook(b.Name, p, assetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||||
|
// Bithumb exchange
|
||||||
|
func (b *Binance) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||||
|
var response exchange.AccountInfo
|
||||||
|
return response, errors.New("not implemented")
|
||||||
|
}
|
||||||
21
testdata/configtest.json
vendored
21
testdata/configtest.json
vendored
@@ -80,6 +80,27 @@
|
|||||||
"Index": "BTC"
|
"Index": "BTC"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Name": "Binance",
|
||||||
|
"Enabled": true,
|
||||||
|
"Verbose": false,
|
||||||
|
"Websocket": false,
|
||||||
|
"UseSandbox": false,
|
||||||
|
"RESTPollingDelay": 10,
|
||||||
|
"AuthenticatedAPISupport": false,
|
||||||
|
"APIKey": "Key",
|
||||||
|
"APISecret": "Secret",
|
||||||
|
"AvailablePairs": "ETHBTC,LTCBTC,BNBBTC,NEOBTC,123456,QTUMETH,EOSETH,SNTETH,BNTETH,BCCBTC,GASBTC,BNBETH,BTCUSDT,ETHUSDT,HSRBTC,OAXETH,DNTETH,MCOETH,ICNETH,MCOBTC,WTCBTC,WTCETH,LRCBTC,LRCETH,QTUMBTC,YOYOBTC,OMGBTC,OMGETH,ZRXBTC,ZRXETH,STRATBTC,STRATETH,SNGLSBTC,SNGLSETH,BQXBTC,BQXETH,KNCBTC,KNCETH,FUNBTC,FUNETH,SNMBTC,SNMETH,NEOETH,IOTABTC,IOTAETH,LINKBTC,LINKETH,XVGBTC,XVGETH,CTRBTC,CTRETH,SALTBTC,SALTETH,MDABTC,MDAETH,MTLBTC,MTLETH,SUBBTC,SUBETH,EOSBTC,SNTBTC,ETCETH,ETCBTC,MTHBTC,MTHETH,ENGBTC,ENGETH,DNTBTC,ZECBTC,ZECETH,BNTBTC,ASTBTC,ASTETH,DASHBTC,DASHETH,OAXBTC,ICNBTC,BTGBTC,BTGETH,EVXBTC,EVXETH,REQBTC,REQETH,VIBBTC,VIBETH,HSRETH,TRXBTC,TRXETH,POWRBTC,POWRETH,ARKBTC,ARKETH,YOYOETH,XRPBTC,XRPETH,MODBTC,MODETH,ENJBTC,ENJETH,STORJBTC,STORJETH,BNBUSDT,VENBNB,YOYOBNB,POWRBNB,VENBTC,VENETH,KMDBTC,KMDETH,NULSBNB,RCNBTC,RCNETH,RCNBNB,NULSBTC,NULSETH,RDNBTC,RDNETH,RDNBNB,XMRBTC,XMRETH,DLTBNB,WTCBNB,DLTBTC,DLTETH,AMBBTC,AMBETH,AMBBNB,BCCETH,BCCUSDT,BCCBNB,BATBTC,BATETH,BATBNB,BCPTBTC,BCPTETH,BCPTBNB,ARNBTC,ARNETH,GVTBTC,GVTETH,CDTBTC,CDTETH,GXSBTC,GXSETH,NEOUSDT,NEOBNB,POEBTC,POEETH,QSPBTC,QSPETH,QSPBNB,BTSBTC,BTSETH,BTSBNB,XZCBTC,XZCETH,XZCBNB,LSKBTC,LSKETH,LSKBNB,TNTBTC,TNTETH,FUELBTC,FUELETH,MANABTC,MANAETH,BCDBTC,BCDETH,DGDBTC,DGDETH,IOTABNB,ADXBTC,ADXETH,ADXBNB,ADABTC,ADAETH,PPTBTC,PPTETH,CMTBTC,CMTETH,CMTBNB,XLMBTC,XLMETH,XLMBNB,CNDBTC,CNDETH,CNDBNB,LENDBTC,LENDETH,WABIBTC,WABIETH,WABIBNB,LTCETH,LTCUSDT,LTCBNB,TNBBTC,TNBETH,WAVESBTC,WAVESETH,WAVESBNB,GTOBTC,GTOETH,GTOBNB,ICXBTC,ICXETH,ICXBNB,OSTBTC,OSTETH,OSTBNB,ELFBTC,ELFETH,AIONBTC,AIONETH,AIONBNB,NEBLBTC,NEBLETH,NEBLBNB,BRDBTC,BRDETH,BRDBNB,MCOBNB,EDOBTC,EDOETH,WINGSBTC,WINGSETH,NAVBTC,NAVETH,NAVBNB,LUNBTC,LUNETH,TRIGBTC,TRIGETH,TRIGBNB,APPCBTC,APPCETH,APPCBNB,VIBEBTC,VIBEETH,RLCBTC,RLCETH,RLCBNB,INSBTC,INSETH,PIVXBTC,PIVXETH,PIVXBNB,IOSTBTC,IOSTETH,CHATBTC,CHATETH",
|
||||||
|
"EnabledPairs": "BTCUSDT",
|
||||||
|
"BaseCurrencies": "USD",
|
||||||
|
"AssetTypes": "SPOT",
|
||||||
|
"ConfigCurrencyPairFormat": {
|
||||||
|
"Uppercase": true
|
||||||
|
},
|
||||||
|
"RequestCurrencyPairFormat": {
|
||||||
|
"Uppercase": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Name": "Bitfinex",
|
"Name": "Bitfinex",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user