Files
gocryptotrader/exchanges/btcc/btcc.go
soxipy fb4e2d1452 localbitcoins fixes (#177)
* General LocalBitcoin fixes

* Added override variables to config for exchange packages to allow different API URL's
2018-08-27 14:19:29 +10:00

642 lines
16 KiB
Go

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