mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
* drop common uuid v4 func and imported package as needed * removed common functions regarding json marshal and unmarshal and used the json package directly. WRT unmarshal it was calling reflect and converted to string which is also checked in the JSON package so it was doing a double up, this will be a tiny gain as it was directly used in the requester package for all our outbound requests. * add in string * explicitly throw away return error value * atleast return the error that websocket initialise returns * return error when not connected * fix comment * Adds comments * move package declarations * drop append whenever we call supported * remove unused import * Change incorrect spelling * fix tests * fix go import issue
706 lines
20 KiB
Go
706 lines
20 KiB
Go
package bitstamp
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"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/order"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
|
log "github.com/thrasher-corp/gocryptotrader/logger"
|
|
)
|
|
|
|
const (
|
|
bitstampAPIURL = "https://www.bitstamp.net/api"
|
|
bitstampAPIVersion = "2"
|
|
bitstampAPITicker = "ticker"
|
|
bitstampAPITickerHourly = "ticker_hour"
|
|
bitstampAPIOrderbook = "order_book"
|
|
bitstampAPITransactions = "transactions"
|
|
bitstampAPIEURUSD = "eur_usd"
|
|
bitstampAPIBalance = "balance"
|
|
bitstampAPIUserTransactions = "user_transactions"
|
|
bitstampAPIOpenOrders = "open_orders"
|
|
bitstampAPIOrderStatus = "order_status"
|
|
bitstampAPICancelOrder = "cancel_order"
|
|
bitstampAPICancelAllOrders = "cancel_all_orders"
|
|
bitstampAPIMarket = "market"
|
|
bitstampAPIWithdrawalRequests = "withdrawal_requests"
|
|
bitstampAPIOpenWithdrawal = "withdrawal/open"
|
|
bitstampAPIBitcoinWithdrawal = "bitcoin_withdrawal"
|
|
bitstampAPILTCWithdrawal = "ltc_withdrawal"
|
|
bitstampAPIETHWithdrawal = "eth_withdrawal"
|
|
bitstampAPIBCHWithdrawal = "bch_withdrawal"
|
|
bitstampAPIBitcoinDeposit = "bitcoin_deposit_address"
|
|
bitstampAPILitecoinDeposit = "ltc_address"
|
|
bitstampAPIEthereumDeposit = "eth_address"
|
|
bitstampAPIBitcoinCashDeposit = "bch_address"
|
|
bitstampAPIUnconfirmedBitcoin = "unconfirmed_btc"
|
|
bitstampAPITransferToMain = "transfer-to-main"
|
|
bitstampAPITransferFromMain = "transfer-from-main"
|
|
bitstampAPIXrpWithdrawal = "xrp_withdrawal"
|
|
bitstampAPIXrpDeposit = "xrp_address"
|
|
bitstampAPIReturnType = "string"
|
|
bitstampAPITradingPairsInfo = "trading-pairs-info"
|
|
|
|
bitstampAuthRate = 8000
|
|
bitstampUnauthRate = 8000
|
|
bitstampTimeLayout = "2006-1-2 15:04:05"
|
|
)
|
|
|
|
// Bitstamp is the overarching type across the bitstamp package
|
|
type Bitstamp struct {
|
|
exchange.Base
|
|
WebsocketConn *wshandler.WebsocketConnection
|
|
}
|
|
|
|
// GetFee returns an estimate of fee based on type of transaction
|
|
func (b *Bitstamp) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
|
|
var fee float64
|
|
|
|
switch feeBuilder.FeeType {
|
|
case exchange.CryptocurrencyTradeFee:
|
|
balance, err := b.GetBalance()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
fee = b.CalculateTradingFee(feeBuilder.Pair.Base,
|
|
feeBuilder.Pair.Quote,
|
|
feeBuilder.PurchasePrice,
|
|
feeBuilder.Amount,
|
|
balance)
|
|
case exchange.CyptocurrencyDepositFee:
|
|
fee = 0
|
|
case exchange.InternationalBankDepositFee:
|
|
fee = getInternationalBankDepositFee(feeBuilder.Amount)
|
|
case exchange.InternationalBankWithdrawalFee:
|
|
fee = getInternationalBankWithdrawalFee(feeBuilder.Amount)
|
|
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.0025 * price * amount
|
|
}
|
|
|
|
// getInternationalBankWithdrawalFee returns international withdrawal fee
|
|
func getInternationalBankWithdrawalFee(amount float64) float64 {
|
|
fee := amount * 0.0009
|
|
|
|
if fee < 15 {
|
|
return 15
|
|
}
|
|
return fee
|
|
}
|
|
|
|
// getInternationalBankDepositFee returns international deposit fee
|
|
func getInternationalBankDepositFee(amount float64) float64 {
|
|
fee := amount * 0.0005
|
|
|
|
if fee < 7.5 {
|
|
return 7.5
|
|
}
|
|
if fee > 300 {
|
|
return 300
|
|
}
|
|
return fee
|
|
}
|
|
|
|
// CalculateTradingFee returns fee on a currency pair
|
|
func (b *Bitstamp) CalculateTradingFee(base, quote currency.Code, purchasePrice, amount float64, balances Balances) float64 {
|
|
var fee float64
|
|
if v, ok := balances[base.String()]; ok {
|
|
switch quote {
|
|
case currency.BTC:
|
|
fee = v.BTCFee
|
|
case currency.USD:
|
|
fee = v.USDFee
|
|
case currency.EUR:
|
|
fee = v.EURFee
|
|
}
|
|
}
|
|
return fee * purchasePrice * amount
|
|
}
|
|
|
|
// GetTicker returns ticker information
|
|
func (b *Bitstamp) GetTicker(currency string, hourly bool) (Ticker, error) {
|
|
response := Ticker{}
|
|
tickerEndpoint := bitstampAPITicker
|
|
|
|
if hourly {
|
|
tickerEndpoint = bitstampAPITickerHourly
|
|
}
|
|
|
|
path := fmt.Sprintf(
|
|
"%s/v%s/%s/%s/",
|
|
b.API.Endpoints.URL,
|
|
bitstampAPIVersion,
|
|
tickerEndpoint,
|
|
strings.ToLower(currency),
|
|
)
|
|
return response, b.SendHTTPRequest(path, &response)
|
|
}
|
|
|
|
// GetOrderbook Returns a JSON dictionary with "bids" and "asks". Each is a list
|
|
// of open orders and each order is represented as a list holding the price and
|
|
// the amount.
|
|
func (b *Bitstamp) GetOrderbook(currency string) (Orderbook, error) {
|
|
type response struct {
|
|
Timestamp int64 `json:"timestamp,string"`
|
|
Bids [][]string `json:"bids"`
|
|
Asks [][]string `json:"asks"`
|
|
}
|
|
resp := response{}
|
|
|
|
path := fmt.Sprintf(
|
|
"%s/v%s/%s/%s/",
|
|
b.API.Endpoints.URL,
|
|
bitstampAPIVersion,
|
|
bitstampAPIOrderbook,
|
|
strings.ToLower(currency),
|
|
)
|
|
|
|
err := b.SendHTTPRequest(path, &resp)
|
|
if err != nil {
|
|
return Orderbook{}, err
|
|
}
|
|
|
|
orderbook := Orderbook{}
|
|
orderbook.Timestamp = resp.Timestamp
|
|
|
|
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, OrderbookBase{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, OrderbookBase{price, amount})
|
|
}
|
|
|
|
return orderbook, nil
|
|
}
|
|
|
|
// GetTradingPairs returns a list of trading pairs which Bitstamp
|
|
// currently supports
|
|
func (b *Bitstamp) GetTradingPairs() ([]TradingPair, error) {
|
|
var result []TradingPair
|
|
|
|
path := fmt.Sprintf("%s/v%s/%s",
|
|
b.API.Endpoints.URL,
|
|
bitstampAPIVersion,
|
|
bitstampAPITradingPairsInfo)
|
|
|
|
return result, b.SendHTTPRequest(path, &result)
|
|
}
|
|
|
|
// GetTransactions returns transaction information
|
|
// value paramater ["time"] = "minute", "hour", "day" will collate your
|
|
// response into time intervals. Implementation of value in test code.
|
|
func (b *Bitstamp) GetTransactions(currencyPair string, values url.Values) ([]Transactions, error) {
|
|
var transactions []Transactions
|
|
path := common.EncodeURLValues(
|
|
fmt.Sprintf(
|
|
"%s/v%s/%s/%s/",
|
|
b.API.Endpoints.URL,
|
|
bitstampAPIVersion,
|
|
bitstampAPITransactions,
|
|
strings.ToLower(currencyPair),
|
|
),
|
|
values,
|
|
)
|
|
|
|
return transactions, b.SendHTTPRequest(path, &transactions)
|
|
}
|
|
|
|
// GetEURUSDConversionRate returns the conversion rate between Euro and USD
|
|
func (b *Bitstamp) GetEURUSDConversionRate() (EURUSDConversionRate, error) {
|
|
rate := EURUSDConversionRate{}
|
|
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bitstampAPIEURUSD)
|
|
|
|
return rate, b.SendHTTPRequest(path, &rate)
|
|
}
|
|
|
|
// GetBalance returns full balance of currency held on the exchange
|
|
func (b *Bitstamp) GetBalance() (Balances, error) {
|
|
var balance map[string]string
|
|
err := b.SendAuthenticatedHTTPRequest(bitstampAPIBalance, true, nil, &balance)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
balances := make(map[string]Balance)
|
|
for k := range balance {
|
|
curr := k[0:3]
|
|
_, ok := balances[strings.ToUpper(curr)]
|
|
if !ok {
|
|
avail, _ := strconv.ParseFloat(balance[curr+"_available"], 64)
|
|
bal, _ := strconv.ParseFloat(balance[curr+"_balance"], 64)
|
|
reserved, _ := strconv.ParseFloat(balance[curr+"_reserved"], 64)
|
|
withdrawalFee, _ := strconv.ParseFloat(balance[curr+"_withdrawal_fee"], 64)
|
|
currBalance := Balance{
|
|
Available: avail,
|
|
Balance: bal,
|
|
Reserved: reserved,
|
|
WithdrawalFee: withdrawalFee,
|
|
}
|
|
switch strings.ToUpper(curr) {
|
|
case currency.USD.String():
|
|
eurFee, _ := strconv.ParseFloat(balance[curr+"eur_fee"], 64)
|
|
currBalance.EURFee = eurFee
|
|
case currency.EUR.String():
|
|
usdFee, _ := strconv.ParseFloat(balance[curr+"usd_fee"], 64)
|
|
currBalance.USDFee = usdFee
|
|
default:
|
|
btcFee, _ := strconv.ParseFloat(balance[curr+"btc_fee"], 64)
|
|
currBalance.BTCFee = btcFee
|
|
eurFee, _ := strconv.ParseFloat(balance[curr+"eur_fee"], 64)
|
|
currBalance.EURFee = eurFee
|
|
usdFee, _ := strconv.ParseFloat(balance[curr+"usd_fee"], 64)
|
|
currBalance.USDFee = usdFee
|
|
}
|
|
balances[strings.ToUpper(curr)] = currBalance
|
|
}
|
|
}
|
|
return balances, nil
|
|
}
|
|
|
|
// GetUserTransactions returns an array of transactions
|
|
func (b *Bitstamp) GetUserTransactions(currencyPair string) ([]UserTransactions, error) {
|
|
type Response struct {
|
|
Date string `json:"datetime"`
|
|
TransactionID int64 `json:"id"`
|
|
Type int `json:"type,string"`
|
|
USD interface{} `json:"usd"`
|
|
EUR interface{} `json:"eur"`
|
|
XRP interface{} `json:"xrp"`
|
|
BTC interface{} `json:"btc"`
|
|
BTCUSD interface{} `json:"btc_usd"`
|
|
Fee float64 `json:"fee,string"`
|
|
OrderID int64 `json:"order_id"`
|
|
}
|
|
var response []Response
|
|
|
|
if currencyPair == "" {
|
|
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions,
|
|
true,
|
|
url.Values{},
|
|
&response); err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions+"/"+currencyPair,
|
|
true,
|
|
url.Values{},
|
|
&response); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
processNumber := func(i interface{}) float64 {
|
|
switch t := i.(type) {
|
|
case float64:
|
|
return t
|
|
case string:
|
|
amt, _ := strconv.ParseFloat(t, 64)
|
|
return amt
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
var transactions []UserTransactions
|
|
for x := range response {
|
|
tx := UserTransactions{}
|
|
tx.Date = response[x].Date
|
|
tx.TransactionID = response[x].TransactionID
|
|
tx.Type = response[x].Type
|
|
tx.EUR = processNumber(response[x].EUR)
|
|
tx.XRP = processNumber(response[x].XRP)
|
|
tx.USD = processNumber(response[x].USD)
|
|
tx.BTC = processNumber(response[x].BTC)
|
|
tx.BTCUSD = processNumber(response[x].BTCUSD)
|
|
tx.Fee = response[x].Fee
|
|
tx.OrderID = response[x].OrderID
|
|
transactions = append(transactions, tx)
|
|
}
|
|
|
|
return transactions, nil
|
|
}
|
|
|
|
// GetOpenOrders returns all open orders on the exchange
|
|
func (b *Bitstamp) GetOpenOrders(currencyPair string) ([]Order, error) {
|
|
var resp []Order
|
|
path := fmt.Sprintf(
|
|
"%s/%s", bitstampAPIOpenOrders, strings.ToLower(currencyPair),
|
|
)
|
|
|
|
return resp, b.SendAuthenticatedHTTPRequest(path, true, nil, &resp)
|
|
}
|
|
|
|
// GetOrderStatus returns an the status of an order by its ID
|
|
func (b *Bitstamp) GetOrderStatus(orderID int64) (OrderStatus, error) {
|
|
resp := OrderStatus{}
|
|
req := url.Values{}
|
|
req.Add("id", strconv.FormatInt(orderID, 10))
|
|
|
|
return resp,
|
|
b.SendAuthenticatedHTTPRequest(bitstampAPIOrderStatus, false, req, &resp)
|
|
}
|
|
|
|
// CancelExistingOrder cancels order by ID
|
|
func (b *Bitstamp) CancelExistingOrder(orderID int64) (CancelOrder, error) {
|
|
var req = url.Values{}
|
|
req.Add("id", strconv.FormatInt(orderID, 10))
|
|
|
|
var result CancelOrder
|
|
err := b.SendAuthenticatedHTTPRequest(bitstampAPICancelOrder, true, req, &result)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// CancelAllExistingOrders cancels all open orders on the exchange
|
|
func (b *Bitstamp) CancelAllExistingOrders() (bool, error) {
|
|
result := false
|
|
|
|
return result,
|
|
b.SendAuthenticatedHTTPRequest(bitstampAPICancelAllOrders, false, nil, &result)
|
|
}
|
|
|
|
// PlaceOrder places an order on the exchange.
|
|
func (b *Bitstamp) PlaceOrder(currencyPair string, price, amount float64, buy, market bool) (Order, error) {
|
|
var req = url.Values{}
|
|
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
|
req.Add("price", strconv.FormatFloat(price, 'f', -1, 64))
|
|
response := Order{}
|
|
orderType := order.Buy.Lower()
|
|
|
|
if !buy {
|
|
orderType = order.Sell.Lower()
|
|
}
|
|
|
|
var path string
|
|
if market {
|
|
path = fmt.Sprintf("%s/%s/%s", orderType, bitstampAPIMarket, strings.ToLower(currencyPair))
|
|
} else {
|
|
path = fmt.Sprintf("%s/%s", orderType, strings.ToLower(currencyPair))
|
|
}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest(path, true, req, &response)
|
|
}
|
|
|
|
// GetWithdrawalRequests returns withdrawal requests for the account
|
|
// timedelta - positive integer with max value 50000000 which returns requests
|
|
// from number of seconds ago to now.
|
|
func (b *Bitstamp) GetWithdrawalRequests(timedelta int64) ([]WithdrawalRequests, error) {
|
|
var resp []WithdrawalRequests
|
|
if timedelta > 50000000 || timedelta < 0 {
|
|
return resp, errors.New("time delta exceeded, max: 50000000 min: 0")
|
|
}
|
|
|
|
value := url.Values{}
|
|
value.Set("timedelta", strconv.FormatInt(timedelta, 10))
|
|
|
|
if timedelta == 0 {
|
|
value = url.Values{}
|
|
}
|
|
|
|
return resp,
|
|
b.SendAuthenticatedHTTPRequest(bitstampAPIWithdrawalRequests, false, value, &resp)
|
|
}
|
|
|
|
// CryptoWithdrawal withdraws a cryptocurrency into a supplied wallet, returns ID
|
|
// amount - The amount you want withdrawn
|
|
// address - The wallet address of the cryptocurrency
|
|
// symbol - the type of crypto ie "ltc", "btc", "eth"
|
|
// destTag - only for XRP default to ""
|
|
// instant - only for bitcoins
|
|
func (b *Bitstamp) CryptoWithdrawal(amount float64, address, symbol, destTag string, instant bool) (CryptoWithdrawalResponse, error) {
|
|
var req = url.Values{}
|
|
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
|
req.Add("address", address)
|
|
resp := CryptoWithdrawalResponse{}
|
|
var endpoint string
|
|
|
|
switch strings.ToLower(symbol) {
|
|
case currency.BTC.Lower().String():
|
|
if instant {
|
|
req.Add("instant", "1")
|
|
} else {
|
|
req.Add("instant", "0")
|
|
}
|
|
endpoint = bitstampAPIBitcoinWithdrawal
|
|
case currency.LTC.Lower().String():
|
|
endpoint = bitstampAPILTCWithdrawal
|
|
case currency.ETH.Lower().String():
|
|
endpoint = bitstampAPIETHWithdrawal
|
|
case currency.XRP.Lower().String():
|
|
if destTag != "" {
|
|
req.Add("destination_tag", destTag)
|
|
}
|
|
endpoint = bitstampAPIXrpWithdrawal
|
|
case currency.BCH.Lower().String():
|
|
endpoint = bitstampAPIBCHWithdrawal
|
|
default:
|
|
return resp, errors.New("incorrect symbol")
|
|
}
|
|
|
|
return resp, b.SendAuthenticatedHTTPRequest(endpoint, false, req, &resp)
|
|
}
|
|
|
|
// OpenBankWithdrawal Opens a bank withdrawal request (SEPA or international)
|
|
func (b *Bitstamp) OpenBankWithdrawal(amount float64, currency,
|
|
name, iban, bic, address, postalCode, city, country,
|
|
comment, withdrawalType string) (FIATWithdrawalResponse, error) {
|
|
var req = url.Values{}
|
|
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
|
req.Add("account_currency", currency)
|
|
req.Add("name", name)
|
|
req.Add("iban", iban)
|
|
req.Add("bic", bic)
|
|
req.Add("address", address)
|
|
req.Add("postal_code", postalCode)
|
|
req.Add("city", city)
|
|
req.Add("country", country)
|
|
req.Add("type", withdrawalType)
|
|
req.Add("comment", comment)
|
|
|
|
resp := FIATWithdrawalResponse{}
|
|
return resp, b.SendAuthenticatedHTTPRequest(bitstampAPIOpenWithdrawal, true, req, &resp)
|
|
}
|
|
|
|
// OpenInternationalBankWithdrawal Opens a bank withdrawal request (international)
|
|
func (b *Bitstamp) OpenInternationalBankWithdrawal(amount float64, currency,
|
|
name, iban, bic, address, postalCode, city, country,
|
|
bankName, bankAddress, bankPostCode, bankCity, bankCountry, internationalCurrency,
|
|
comment, withdrawalType string) (FIATWithdrawalResponse, error) {
|
|
var req = url.Values{}
|
|
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
|
req.Add("account_currency", currency)
|
|
req.Add("name", name)
|
|
req.Add("iban", iban)
|
|
req.Add("bic", bic)
|
|
req.Add("address", address)
|
|
req.Add("postal_code", postalCode)
|
|
req.Add("city", city)
|
|
req.Add("country", country)
|
|
req.Add("type", withdrawalType)
|
|
req.Add("comment", comment)
|
|
req.Add("currency", internationalCurrency)
|
|
req.Add("bank_name", bankName)
|
|
req.Add("bank_address", bankAddress)
|
|
req.Add("bank_postal_code", bankPostCode)
|
|
req.Add("bank_city", bankCity)
|
|
req.Add("bank_country", bankCountry)
|
|
|
|
resp := FIATWithdrawalResponse{}
|
|
return resp, b.SendAuthenticatedHTTPRequest(bitstampAPIOpenWithdrawal, true, req, &resp)
|
|
}
|
|
|
|
// GetCryptoDepositAddress returns a depositing address by crypto
|
|
// crypto - example "btc", "ltc", "eth", "xrp" or "bch"
|
|
func (b *Bitstamp) GetCryptoDepositAddress(crypto currency.Code) (string, error) {
|
|
var resp string
|
|
v2Resp := struct {
|
|
Address string `json:"address"`
|
|
}{}
|
|
|
|
switch crypto {
|
|
case currency.BTC:
|
|
return resp,
|
|
b.SendAuthenticatedHTTPRequest(bitstampAPIBitcoinDeposit, false, nil, &resp)
|
|
|
|
case currency.LTC:
|
|
return v2Resp.Address,
|
|
b.SendAuthenticatedHTTPRequest(bitstampAPILitecoinDeposit, true, nil, &v2Resp)
|
|
|
|
case currency.ETH:
|
|
return v2Resp.Address,
|
|
b.SendAuthenticatedHTTPRequest(bitstampAPIEthereumDeposit, true, nil, &v2Resp)
|
|
|
|
case currency.XRP:
|
|
return v2Resp.Address,
|
|
b.SendAuthenticatedHTTPRequest(bitstampAPIXrpDeposit, true, nil, &v2Resp)
|
|
|
|
case currency.BCH:
|
|
return v2Resp.Address,
|
|
b.SendAuthenticatedHTTPRequest(bitstampAPIBitcoinCashDeposit, true, nil, &v2Resp)
|
|
|
|
default:
|
|
return resp, fmt.Errorf("unsupported cryptocurrency string %s", crypto)
|
|
}
|
|
}
|
|
|
|
// GetUnconfirmedBitcoinDeposits returns unconfirmed transactions
|
|
func (b *Bitstamp) GetUnconfirmedBitcoinDeposits() ([]UnconfirmedBTCTransactions, error) {
|
|
var response []UnconfirmedBTCTransactions
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest(bitstampAPIUnconfirmedBitcoin, false, nil, &response)
|
|
}
|
|
|
|
// TransferAccountBalance transfers funds from either a main or sub account
|
|
// amount - to transfers
|
|
// currency - which currency to transfer
|
|
// subaccount - name of account
|
|
// toMain - bool either to or from account
|
|
func (b *Bitstamp) TransferAccountBalance(amount float64, currency, subAccount string, toMain bool) error {
|
|
var req = url.Values{}
|
|
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
|
req.Add("currency", currency)
|
|
|
|
if subAccount == "" {
|
|
return errors.New("missing subAccount parameter")
|
|
}
|
|
|
|
req.Add("subAccount", subAccount)
|
|
|
|
var path string
|
|
if toMain {
|
|
path = bitstampAPITransferToMain
|
|
} else {
|
|
path = bitstampAPITransferFromMain
|
|
}
|
|
|
|
var resp interface{}
|
|
|
|
return b.SendAuthenticatedHTTPRequest(path, true, req, &resp)
|
|
}
|
|
|
|
// SendHTTPRequest sends an unauthenticated HTTP request
|
|
func (b *Bitstamp) SendHTTPRequest(path string, result interface{}) error {
|
|
return b.SendPayload(http.MethodGet,
|
|
path,
|
|
nil,
|
|
nil,
|
|
result,
|
|
false,
|
|
false,
|
|
b.Verbose,
|
|
b.HTTPDebugging,
|
|
b.HTTPRecording)
|
|
}
|
|
|
|
// SendAuthenticatedHTTPRequest sends an authenticated request
|
|
func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url.Values, result interface{}) error {
|
|
if !b.AllowAuthenticatedRequest() {
|
|
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
|
|
}
|
|
|
|
n := b.Requester.GetNonce(true).String()
|
|
|
|
if values == nil {
|
|
values = url.Values{}
|
|
}
|
|
|
|
values.Set("key", b.API.Credentials.Key)
|
|
values.Set("nonce", n)
|
|
hmac := crypto.GetHMAC(crypto.HashSHA256,
|
|
[]byte(n+b.API.Credentials.ClientID+b.API.Credentials.Key),
|
|
[]byte(b.API.Credentials.Secret))
|
|
values.Set("signature", strings.ToUpper(crypto.HexEncodeToString(hmac)))
|
|
|
|
if v2 {
|
|
path = fmt.Sprintf("%s/v%s/%s/", b.API.Endpoints.URL, bitstampAPIVersion, path)
|
|
} else {
|
|
path = fmt.Sprintf("%s/%s/", b.API.Endpoints.URL, path)
|
|
}
|
|
|
|
if b.Verbose {
|
|
log.Debugf(log.ExchangeSys, "Sending POST request to "+path)
|
|
}
|
|
|
|
headers := make(map[string]string)
|
|
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
|
|
|
encodedValues := values.Encode()
|
|
readerValues := bytes.NewBufferString(encodedValues)
|
|
|
|
interim := json.RawMessage{}
|
|
|
|
errCap := struct {
|
|
Error string `json:"error"`
|
|
Status string `json:"status"`
|
|
Reason interface{} `json:"reason"`
|
|
}{}
|
|
|
|
err := b.SendPayload(http.MethodPost,
|
|
path,
|
|
headers,
|
|
readerValues,
|
|
&interim,
|
|
true,
|
|
true,
|
|
b.Verbose,
|
|
b.HTTPDebugging,
|
|
b.HTTPRecording)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := json.Unmarshal(interim, &errCap); err == nil {
|
|
if errCap.Error != "" {
|
|
return errors.New(errCap.Error)
|
|
}
|
|
if data, ok := errCap.Reason.(map[string][]string); ok {
|
|
var details string
|
|
for _, v := range data {
|
|
details += strings.Join(v, "")
|
|
}
|
|
return errors.New(details)
|
|
}
|
|
|
|
if data, ok := errCap.Reason.(string); ok {
|
|
return errors.New(data)
|
|
}
|
|
|
|
if errCap.Status != "" {
|
|
return errors.New(errCap.Status)
|
|
}
|
|
}
|
|
|
|
return json.Unmarshal(interim, result)
|
|
}
|
|
|
|
func parseTime(dateTime string) (time.Time, error) {
|
|
return time.Parse(bitstampTimeLayout, dateTime)
|
|
}
|