From 22d250b51aafb7b5a1390b668f7b95ab3c970d52 Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Thu, 23 Nov 2017 10:41:19 +1100 Subject: [PATCH] Fixed linter issue and added tests for localbitcoins. Parralleled tests for lakebtc. --- exchanges/lakebtc/lakebtc_test.go | 11 + exchanges/localbitcoins/localbitcoins.go | 616 ++++++++++++++---- exchanges/localbitcoins/localbitcoins_test.go | 75 +++ .../localbitcoins/localbitcoins_types.go | 382 +++++++++-- 4 files changed, 910 insertions(+), 174 deletions(-) create mode 100644 exchanges/localbitcoins/localbitcoins_test.go diff --git a/exchanges/lakebtc/lakebtc_test.go b/exchanges/lakebtc/lakebtc_test.go index 8239c3bf..02edeeb4 100644 --- a/exchanges/lakebtc/lakebtc_test.go +++ b/exchanges/lakebtc/lakebtc_test.go @@ -34,6 +34,7 @@ func TestSetup(t *testing.T) { } func TestGetFee(t *testing.T) { + t.Parallel() if l.GetFee(false) != 0.2 { t.Error("Test Failed - GetFee() error") } @@ -43,6 +44,7 @@ func TestGetFee(t *testing.T) { } func TestGetTicker(t *testing.T) { + t.Parallel() _, err := l.GetTicker() if err != nil { t.Error("Test Failed - GetTicker() error", err) @@ -50,6 +52,7 @@ func TestGetTicker(t *testing.T) { } func TestGetOrderBook(t *testing.T) { + t.Parallel() _, err := l.GetOrderBook("BTCUSD") if err != nil { t.Error("Test Failed - GetOrderBook() error", err) @@ -57,6 +60,7 @@ func TestGetOrderBook(t *testing.T) { } func TestGetTradeHistory(t *testing.T) { + t.Parallel() _, err := l.GetTradeHistory("BTCUSD") if err != nil { t.Error("Test Failed - GetTradeHistory() error", err) @@ -64,6 +68,7 @@ func TestGetTradeHistory(t *testing.T) { } func TestTrade(t *testing.T) { + t.Parallel() _, err := l.Trade(0, 0, 0, "USD") if err == nil { t.Error("Test Failed - Trade() error", err) @@ -71,6 +76,7 @@ func TestTrade(t *testing.T) { } func TestGetOpenOrders(t *testing.T) { + t.Parallel() _, err := l.GetOpenOrders() if err == nil { t.Error("Test Failed - GetOpenOrders() error", err) @@ -78,6 +84,7 @@ func TestGetOpenOrders(t *testing.T) { } func TestGetOrders(t *testing.T) { + t.Parallel() _, err := l.GetOrders([]int64{1, 2}) if err == nil { t.Error("Test Failed - GetOrders() error", err) @@ -85,6 +92,7 @@ func TestGetOrders(t *testing.T) { } func TestCancelOrder(t *testing.T) { + t.Parallel() err := l.CancelOrder(1337) if err == nil { t.Error("Test Failed - CancelOrder() error", err) @@ -92,6 +100,7 @@ func TestCancelOrder(t *testing.T) { } func TestGetTrades(t *testing.T) { + t.Parallel() _, err := l.GetTrades(1337) if err == nil { t.Error("Test Failed - GetTrades() error", err) @@ -99,6 +108,7 @@ func TestGetTrades(t *testing.T) { } func TestGetExternalAccounts(t *testing.T) { + t.Parallel() _, err := l.GetExternalAccounts() if err == nil { t.Error("Test Failed - GetExternalAccounts() error", err) @@ -106,6 +116,7 @@ func TestGetExternalAccounts(t *testing.T) { } func TestCreateWithdraw(t *testing.T) { + t.Parallel() _, err := l.CreateWithdraw(0, 1337) if err == nil { t.Error("Test Failed - CreateWithdraw() error", err) diff --git a/exchanges/localbitcoins/localbitcoins.go b/exchanges/localbitcoins/localbitcoins.go index e07bbe1f..6484b20d 100644 --- a/exchanges/localbitcoins/localbitcoins.go +++ b/exchanges/localbitcoins/localbitcoins.go @@ -12,26 +12,101 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/exchanges" - "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) const ( - LOCALBITCOINS_API_URL = "https://localbitcoins.com" - LOCALBITCOINS_API_TICKER = "/bitcoinaverage/ticker-all-currencies/" - LOCALBITCOINS_API_BITCOINCHARTS = "/bitcoincharts/" - LOCALBITCOINS_API_PINCODE = "pincode/" - LOCALBITCOINS_API_WALLET = "wallet/" - LOCALBITCOINS_API_MYSELF = "myself/" - LOCALBITCOINS_API_WALLET_BALANCE = "wallet-balance/" - LOCALBITCOINS_API_WALLET_SEND = "wallet-send/" - LOCALBITCOINS_API_WALLET_SEND_PIN = "wallet-send-pin/" - LOCALBITCOINS_API_WALLET_ADDRESS = "wallet-addr/" + localbitcoinsAPIURL = "https://localbitcoins.com" + + // Autheticated Calls + localbitcoinsAPIAccountInfo = "api/account_info" + localbitcoinsAPIMyself = "myself/" + localbitcoinsAPIAds = "ads/" + localbitcoinsAPIAdGet = "ad-get/" + localbitcoinsAPIAdEdit = "ad/" + localbitcoinsAPIAdCreate = "ad-create/" + localbitcoinsAPIUpdateEquation = "ad-equation/" + localbitcoinsAPIDeleteAd = "ad-delete/" + localbitcoinsAPIRelease = "contact_release/" + localbitcoinsAPIReleaseByPin = "contact_release_pin/" + localbitcoinsAPIMarkAsPaid = "contact_mark_as_paid/" + localbitcoinsAPIMessages = "contact_messages/" + localbitcoinsAPISendMessage = "contact_message_post/" + localbitcoinsAPIDispute = "contact_dispute/" + localbitcoinsAPICancelTrade = "contact_cancel/" + localbitcoinsAPIFundTrade = "contact_fund/" + localbitcoinsAPIConfirmRealName = "contact_mark_realname/" + localbitcoinsAPIVerifyIdentity = "contact_mark_identified/" + localbitcoinsAPIInitiateTrade = "contact_create/" + localbitcoinsAPITradeInfo = "contact_info/" + localbitcoinsAPIDashboard = "dashboard/" + localbitcoinsAPIDashboardReleased = "dashboard/released/" + localbitcoinsAPIDashboardCancelled = "dashboard/canceled/" + localbitcoinsAPIDashboardClosed = "dashboard/closed/" + localbitcoinsAPIFeedback = "feedback/" + localbitcoinsAPILogout = "logout/" + localbitcoinsAPICreateInvoice = "merchant/new_invoice/" + localbitcoinsAPIGetNotification = "notifications/" + localbitcoinsAPIMarkNotification = "notifications/mark_as_read/" + localbitcoinsAPIPinCode = "pincode/" + localbitcoinsAPIVerifyUsername = "real_name_verifiers/" + localbitcoinsAPIWallet = "wallet/" + localbitcoinsAPIWalletBalance = "wallet-balance/" + localbitcoinsAPIWalletSend = "wallet-send/" + localbitcoinsAPIWalletSendPin = "wallet-send-pin/" + localbitcoinsAPIWalletAddress = "wallet-addr/" + + // Un-Autheticated Calls + localbitcoinsAPICountryCodes = "/api/countrycodes/" + localbitcoinsAPICurrencies = "/api/currencies/" + localbitcoinsAPIPaymentMethods = "/api/payment_methods/" + localbitcoinsAPIPlaces = "/api/places/" + localbitcoinsAPITicker = "/bitcoinaverage/ticker-all-currencies/" + localbitcoinsAPIBitcoincharts = "/bitcoincharts/" + localbitcoinsAPICashBuy = "/buy-bitcoins-with-cash/" + localbitcoinsAPIOnlineBuy = "/buy-bitcoins-online/" + + // Trade Types + tradeTypeLocalSell = "LOCAL_SELL" + tradeTypeLocalBuy = "LOCAL_BUY" + tradeTypeOnlineSell = "ONLINE_SELL" + tradeTypeOnlineBuy = "ONLINE_BUY" + + // Reference Types + refTypeShort = "SHORT" + refTypeLong = "LONG" + refTypeNumbers = "NUMBERS" + refTypeLetters = "LETTERS" + + // Feedback Values + feedbackTrust = "trust" + feedbackPositive = "positive" + feedbackNeutral = "neutral" + feedbackBlock = "block" + feedbackBlockWithoutFeedback = "block_without_feedback" + + // State Values + stateNotOpened = "NOT_OPENED" + stateWaitingForPayment = "WAITING_FOR_PAYMENT" + statePaid = "PAID" + stateNotPaid = "DIDNT_PAID" + statePaidLate = "PAID_IN_LATE" + statePartlyPaid = "PAID_PARTLY" + statePaidAndConfirmed = "PAID_AND_CONFIRMED" + statePaidLateConfirmed = "PAID_IN_LATE_AND_CONFIRMED" + statePaidPartlyConfirmed = "PAID_PARTLY_AND_CONFIRMED" ) +var ( + // Payment Methods + paymentMethodOne string +) + +// LocalBitcoins is the overarching type across the localbitcoins package type LocalBitcoins struct { exchange.Base } +// SetDefaults sets the package defaults for localbitcoins func (l *LocalBitcoins) SetDefaults() { l.Name = "LocalBitcoins" l.Enabled = false @@ -43,9 +118,9 @@ func (l *LocalBitcoins) SetDefaults() { l.RequestCurrencyPairFormat.Uppercase = true l.ConfigCurrencyPairFormat.Delimiter = "" l.ConfigCurrencyPairFormat.Uppercase = true - l.AssetTypes = []string{ticker.Spot} } +// Setup sets exchange configuration parameters func (l *LocalBitcoins) Setup(exch config.ExchangeConfig) { if !exch.Enabled { l.SetEnabled(false) @@ -63,107 +138,34 @@ func (l *LocalBitcoins) Setup(exch config.ExchangeConfig) { if err != nil { log.Fatal(err) } - err = l.SetAssetTypes() - if err != nil { - log.Fatal(err) - } } } +// GetFee returns the fee for maker or taker func (l *LocalBitcoins) GetFee(maker bool) float64 { if maker { return l.MakerFee - } else { - return l.TakerFee } + return l.TakerFee } -func (l *LocalBitcoins) GetTicker() (map[string]LocalBitcoinsTicker, error) { - result := make(map[string]LocalBitcoinsTicker) - err := common.SendHTTPGetRequest(LOCALBITCOINS_API_URL+LOCALBITCOINS_API_TICKER, true, l.Verbose, &result) - - if err != nil { - return result, err - } - - return result, nil -} - -func (l *LocalBitcoins) GetTrades(currency string, values url.Values) ([]LocalBitcoinsTrade, error) { - path := common.EncodeURLValues(fmt.Sprintf("%s/%s/trades.json", LOCALBITCOINS_API_URL+LOCALBITCOINS_API_BITCOINCHARTS, currency), values) - result := []LocalBitcoinsTrade{} - err := common.SendHTTPGetRequest(path, true, l.Verbose, &result) - - if err != nil { - return result, err - } - - return result, nil -} - -func (l *LocalBitcoins) GetOrderbook(currency string) (LocalBitcoinsOrderbook, error) { +// GetAccountInfo lets you retrieve the public user information on a +// LocalBitcoins user. The response contains the same information that is found +// on an account's public profile page. +func (l *LocalBitcoins) GetAccountInfo(username string, self bool) (AccountInfo, error) { type response struct { - Bids [][]string `json:"bids"` - Asks [][]string `json:"asks"` - } - - path := fmt.Sprintf("%s/%s/orderbook.json", LOCALBITCOINS_API_URL+LOCALBITCOINS_API_BITCOINCHARTS, currency) - resp := response{} - err := common.SendHTTPGetRequest(path, true, l.Verbose, &resp) - - if err != nil { - return LocalBitcoinsOrderbook{}, err - } - - orderbook := LocalBitcoinsOrderbook{} - - for _, x := range resp.Bids { - price, err := strconv.ParseFloat(x[0], 64) - if err != nil { - log.Println(err) - continue - } - amount, err := strconv.ParseFloat(x[1], 64) - if err != nil { - log.Println(err) - continue - } - orderbook.Bids = append(orderbook.Bids, LocalBitcoinsOrderbookStructure{price, amount}) - } - - for _, x := range resp.Asks { - price, err := strconv.ParseFloat(x[0], 64) - if err != nil { - log.Println(err) - continue - } - amount, err := strconv.ParseFloat(x[1], 64) - if err != nil { - log.Println(err) - continue - } - orderbook.Asks = append(orderbook.Asks, LocalBitcoinsOrderbookStructure{price, amount}) - } - - return orderbook, nil -} - -func (l *LocalBitcoins) GetAccountInfo(username string, self bool) (LocalBitcoinsAccountInfo, error) { - type response struct { - Data LocalBitcoinsAccountInfo `json:"data"` + Data AccountInfo `json:"data"` } resp := response{} if self { - err := l.SendAuthenticatedHTTPRequest("GET", LOCALBITCOINS_API_MYSELF, nil, &resp) - + err := l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIMyself, nil, &resp) if err != nil { return resp.Data, err } } else { - path := fmt.Sprintf("%s/api/account_info/%s/", LOCALBITCOINS_API_URL, username) + path := fmt.Sprintf("%s/%s/%s/", localbitcoinsAPIURL, localbitcoinsAPIAccountInfo, username) err := common.SendHTTPGetRequest(path, true, l.Verbose, &resp) - if err != nil { return resp.Data, err } @@ -172,6 +174,269 @@ func (l *LocalBitcoins) GetAccountInfo(username string, self bool) (LocalBitcoin return resp.Data, nil } +// Getads returns information of single advertisement based on the ad ID, if +// adID ommited. +// +// adID - [optional] string if ommited returns all ads +func (l *LocalBitcoins) Getads(adID string) (AdData, error) { + type response struct { + Data AdData `json:"data"` + } + resp := response{} + + if len(adID) > 0 { + return resp.Data, + l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIAdGet+adID+"/", nil, &resp) + } + + return resp.Data, + l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIAds, nil, &resp) +} + +// EditAd updates set advertisements +// +// params - see localbitcoins_types.go AdEdit for reference +// adID - string for the ad you already created +func (l *LocalBitcoins) EditAd(params AdEdit, adID string) error { + type response struct { + Data AdData `json:"data"` + } + + resp := response{} + //request := make(map[string]interface{}) + + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIAdEdit+adID+"/", nil, &resp) +} + +// CreateAd creates a new advertisement +// +// params - see localbitcoins_types.go AdCreate for reference +func (l *LocalBitcoins) CreateAd(params AdCreate) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIAdCreate, nil, nil) +} + +// UpdatePriceEquation updates price equation of an advertisement. If there are +// problems with new equation, the price and equation are not updated and +// advertisement remains visible. +// +// equation - string of equation +// adID - string of specific ad identification +func (l *LocalBitcoins) UpdatePriceEquation(equation, adID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIUpdateEquation+adID, nil, nil) +} + +// DeleteAd deletes the advertisement by adID. +// +// adID - string of specific ad identification +func (l *LocalBitcoins) DeleteAd(adID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIDeleteAd+adID, nil, nil) +} + +// ReleaseFunds releases Bitcoin trades specified by ID {contact_id}. If the +// release was successful a message is returned on the data key. +func (l *LocalBitcoins) ReleaseFunds(contactID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIRelease+contactID, nil, nil) +} + +// ReleaseFundsByPin releases Bitcoin trades specified by ID {contact_id}. if +// the current pincode is provided. If the release was successful a message is +// returned on the data key. +func (l *LocalBitcoins) ReleaseFundsByPin(pin int, contactID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIReleaseByPin+contactID, nil, nil) +} + +// MarkAsPaid marks a trade as paid. +func (l *LocalBitcoins) MarkAsPaid(contactID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIMarkAsPaid+contactID, nil, nil) +} + +// GetMessages returns all chat messages from the trade. Messages are on the message_list key. +func (l *LocalBitcoins) GetMessages(contactID string) (Message, error) { + type response struct { + MessageList Message `json:"message_list"` + } + resp := response{} + + return resp.MessageList, + l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIMessages+contactID, nil, &resp) +} + +// SendMessage posts a message and/or uploads an image to the trade. Encode +// images with multipart/form-data encoding. +func (l *LocalBitcoins) SendMessage(msg, contactID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPISendMessage+contactID, nil, nil) +} + +// Dispute starts a dispute on the specified trade ID if the requirements for +// starting the dispute has been fulfilled. +// +// topic - [optional] String Short description of issue to LocalBitcoins customer support. +func (l *LocalBitcoins) Dispute(topic, contactID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIDispute+contactID, nil, nil) +} + +// CancelTrade cancels the trade if the token owner is the Bitcoin buyer. +// Bitcoin sellers cannot cancel trades. +func (l *LocalBitcoins) CancelTrade(contactID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPICancelTrade+contactID, nil, nil) +} + +// FundTrade attempts to fund an unfunded local trade from the token owners +// wallet. Works only if the token owner is the Bitcoin seller in the trade. +func (l *LocalBitcoins) FundTrade(contactID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIFundTrade+contactID, nil, nil) +} + +// ConfirmRealName creates or updates real name confirmation. +func (l *LocalBitcoins) ConfirmRealName(contactID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIConfirmRealName+contactID, nil, nil) +} + +// VerifyIdentity marks the identity of trade partner as verified. You must be +// the advertiser in this trade. +func (l *LocalBitcoins) VerifyIdentity(contactID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIVerifyIdentity+contactID, nil, nil) +} + +// InitiateTrade sttempts to start a Bitcoin trade from the specified +// advertisement ID. +func (l *LocalBitcoins) InitiateTrade(amount int, message, adID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIInitiateTrade+adID, nil, nil) +} + +// GetTradeInfo returns information about a single trade that the token owner is +// part in. +func (l *LocalBitcoins) GetTradeInfo(contactID string) error { + return l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPITradeInfo+contactID, nil, nil) +} + +// GetCountryCodes returns a list of valid and recognized countrycodes +func (l *LocalBitcoins) GetCountryCodes() error { + return common.SendHTTPGetRequest(localbitcoinsAPIURL+localbitcoinsAPICountryCodes, true, l.Verbose, nil) +} + +// GetCurrencies returns a list of valid and recognized fiat currencies. Also +// contains human readable name for every currency and boolean that tells if +// currency is an altcoin. +func (l *LocalBitcoins) GetCurrencies() error { + return common.SendHTTPGetRequest(localbitcoinsAPIURL+localbitcoinsAPICurrencies, true, l.Verbose, nil) +} + +// GetDashboardInfo returns a list of trades on the data key contact_list. This +// API end point mirrors the website's dashboard, allowing access to contacts in +// different states. +// In addition all of these listings have buyer/ and seller/ sub-listings to +// view contacts where the token owner is either buying or selling, respectively. +// E.g. /api/dashboard/buyer/. All contacts where the token owner is +// participating are returned. +func (l *LocalBitcoins) GetDashboardInfo() (DashBoardInfo, error) { + resp := DashBoardInfo{} + + return resp, + l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIDashboard, nil, &resp) +} + +// GetDashboardReleasedTrades returns a list of all released trades where the +// token owner is either a buyer or seller. +func (l *LocalBitcoins) GetDashboardReleasedTrades() (DashBoardInfo, error) { + resp := DashBoardInfo{} + + return resp, + l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIDashboardReleased, nil, &resp) +} + +// GetDashboardCancelledTrades returns a list of all canceled trades where the +// token owner is either a buyer or seller. +func (l *LocalBitcoins) GetDashboardCancelledTrades() (DashBoardInfo, error) { + resp := DashBoardInfo{} + + return resp, + l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIDashboardCancelled, nil, &resp) +} + +// GetDashboardClosedTrades returns a list of all closed trades where the token +// owner is either a buyer or seller. +func (l *LocalBitcoins) GetDashboardClosedTrades() (DashBoardInfo, error) { + resp := DashBoardInfo{} + + return resp, + l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIDashboardClosed, nil, &resp) +} + +// SetFeedback gives feedback to user. Possible feedback values are: trust, +// positive, neutral, block, block_without_feedback, (check const values) +// This is only possible to set if there is a trade between the token owner and +// the user specified in {username} that is canceled or released. You may also +// set feedback message using msg field with few exceptions. Feedback +// block_without_feedback clears the message and with block the message is +// mandatory. +// +// feedback - string (use const valuesfor feedback) +// msg - [optional] Feedback message displayed alongside feedback on receivers +// profile page. +// username - username of trade contact +func (l *LocalBitcoins) SetFeedback(msg, feedback, username string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIFeedback, nil, nil) +} + +// Logout expires the current access token immediately. To get a new token +// afterwards, public apps will need to re-authenticate, confidential apps can +// turn in a refresh token. +func (l *LocalBitcoins) Logout() error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPILogout, nil, nil) +} + +// CreateNewInvoice creates a new invoice. +func (l *LocalBitcoins) CreateNewInvoice(currency, description, returnURL string, amount float64, internal bool) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPICreateInvoice, nil, nil) +} + +// GetInvoice returns information about a specific invoice created by the token +// owner. +func (l *LocalBitcoins) GetInvoice(invoiceID string) (Invoice, error) { + resp := Invoice{} + return resp, l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPICreateInvoice, nil, &resp) +} + +// DeleteInvoice deletes a specific invoice. Deleting invoices is possible when +// it is sure that receiver cannot accidentally pay the invoice at the same time +// as the merchant is deleting it. You can use the API request +// /api/merchant/invoice/{invoice_id}/ to check if deleting is possible. +func (l *LocalBitcoins) DeleteInvoice(invoiceID string) (Invoice, error) { + resp := Invoice{} + return resp, l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPICreateInvoice, nil, &resp) +} + +// GetNotifications returns recent notifications. +func (l *LocalBitcoins) GetNotifications() ([]NotificationInfo, error) { + resp := []NotificationInfo{} + return resp, l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIGetNotification, nil, &resp) +} + +// MarkNotifications marks a specific notification as read. +func (l *LocalBitcoins) MarkNotifications(notificationID string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIMarkNotification, nil, nil) +} + +// GetPaymentMethods returns a list of valid payment methods. Also contains name +// and code for payment methods, and possible limitations in currencies and bank +// name choices. +func (l *LocalBitcoins) GetPaymentMethods() error { + return common.SendHTTPGetRequest(localbitcoinsAPIURL+localbitcoinsAPIPaymentMethods, true, l.Verbose, nil) +} + +// GetPaymentMethodsByCountry returns a list of valid payment methods filtered +// by countrycodes. +func (l *LocalBitcoins) GetPaymentMethodsByCountry(countryCode string) error { + return common.SendHTTPGetRequest(localbitcoinsAPIURL+localbitcoinsAPIPaymentMethods+countryCode, true, l.Verbose, nil) +} + +// CheckPincode checks the given PIN code against the token owners currently +// active PIN code. You can use this method to ensure the person using the +// session is the legitimate user. +// Due to only requiring the read scope, the user is not guaranteed to have set +// a PIN code. If you protect your application using this request, please make +// the user has set a PIN code for his account. func (l *LocalBitcoins) CheckPincode(pin int) (bool, error) { type response struct { Data struct { @@ -181,64 +446,91 @@ func (l *LocalBitcoins) CheckPincode(pin int) (bool, error) { resp := response{} values := url.Values{} values.Set("pincode", strconv.Itoa(pin)) - err := l.SendAuthenticatedHTTPRequest("POST", LOCALBITCOINS_API_PINCODE, values, &resp) + err := l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIPinCode, values, &resp) if err != nil { return false, err } if !resp.Data.PinOK { - return false, errors.New("Pin invalid.") + return false, errors.New("pin invalid") } return true, nil } -func (l *LocalBitcoins) GetWalletInfo() (LocalBitcoinsWalletInfo, error) { +// GetPlaces Looks up places near lat, lon and provides full URLs to buy and +// sell listings for each. +func (l *LocalBitcoins) GetPlaces(lat, lon int, location, countryCode string) error { + return common.SendHTTPGetRequest(localbitcoinsAPIURL+localbitcoinsAPIPlaces, true, l.Verbose, nil) +} + +// VerifyUsername returns list of real name verifiers for the user. Returns a +// list only when you have a trade with the user where you are the seller. +func (l *LocalBitcoins) VerifyUsername() error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIVerifyUsername, nil, nil) +} + +// GetRecentMessages returns maximum of 25 newest trade messages. Does not +// return messages older than one month. Messages are ordered by sending time, +// and the newest one is first. +func (l *LocalBitcoins) GetRecentMessages(after string) error { + return l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIVerifyUsername, nil, nil) +} + +// GetWalletInfo gets information about the token owner's wallet balance. +func (l *LocalBitcoins) GetWalletInfo() (WalletInfo, error) { type response struct { - Data LocalBitcoinsWalletInfo `json:"data"` + Data WalletInfo `json:"data"` } resp := response{} - err := l.SendAuthenticatedHTTPRequest("GET", LOCALBITCOINS_API_WALLET, nil, &resp) + err := l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIWallet, nil, &resp) if err != nil { - return LocalBitcoinsWalletInfo{}, err + return WalletInfo{}, err } if resp.Data.Message != "OK" { - return LocalBitcoinsWalletInfo{}, errors.New("Unable to fetch wallet info.") + return WalletInfo{}, errors.New("unable to fetch wallet info") } return resp.Data, nil } -func (l *LocalBitcoins) GetWalletBalance() (LocalBitcoinsWalletBalanceInfo, error) { +// GetWalletBalance Same as GetWalletInfo(), but only returns the message, +// receiving_address and total fields. +// Use this instead if you don't care about transactions at the moment. +func (l *LocalBitcoins) GetWalletBalance() (WalletBalanceInfo, error) { type response struct { - Data LocalBitcoinsWalletBalanceInfo `json:"data"` + Data WalletBalanceInfo `json:"data"` } resp := response{} - err := l.SendAuthenticatedHTTPRequest("GET", LOCALBITCOINS_API_WALLET_BALANCE, nil, &resp) + err := l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIWalletBalance, nil, &resp) if err != nil { - return LocalBitcoinsWalletBalanceInfo{}, err + return WalletBalanceInfo{}, err } if resp.Data.Message != "OK" { - return LocalBitcoinsWalletBalanceInfo{}, errors.New("Unable to fetch wallet balance.") + return WalletBalanceInfo{}, errors.New("unable to fetch wallet balance") } return resp.Data, nil } +// WalletSend sends amount of bitcoins from the token owner's wallet to address. +// On success, the response returns a message indicating success. It is highly +// recommended to minimize the lifetime of access tokens with the money +// permission. Use Logout() to make the current token expire instantly. func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int) (bool, error) { values := url.Values{} values.Set("address", address) values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64)) - path := LOCALBITCOINS_API_WALLET_SEND + path := localbitcoinsAPIWalletSend if pin > 0 { values.Set("pincode", strconv.Itoa(pin)) - path = LOCALBITCOINS_API_WALLET_SEND_PIN + path = localbitcoinsAPIWalletSendPin } type response struct { @@ -254,12 +546,15 @@ func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int) (boo } if resp.Data.Message != "Money is being sent" { - return false, errors.New("Unable to send Bitcoins.") + return false, errors.New("unable to send Bitcoins") } return true, nil } +// GetWalletAddress returns an unused receiving address from the token owner's +// wallet. The address is returned in the address key of the response. Note that +// this API may keep returning the same (unused) address if requested repeatedly. func (l *LocalBitcoins) GetWalletAddress() (string, error) { type response struct { Data struct { @@ -268,18 +563,98 @@ func (l *LocalBitcoins) GetWalletAddress() (string, error) { } } resp := response{} - err := l.SendAuthenticatedHTTPRequest("POST", LOCALBITCOINS_API_WALLET_ADDRESS, nil, &resp) + err := l.SendAuthenticatedHTTPRequest("POST", localbitcoinsAPIWalletAddress, nil, &resp) if err != nil { return "", err } if resp.Data.Message != "OK!" { - return "", errors.New("Unable to fetch wallet address.") + return "", errors.New("unable to fetch wallet address") } return resp.Data.Address, nil } +// GetBitcoinsWithCashAd returns buy or sell as cash local advertisements. +func (l *LocalBitcoins) GetBitcoinsWithCashAd(locationID, locationSlug string, BuySide bool) error { + return common.SendHTTPGetRequest(localbitcoinsAPIURL+localbitcoinsAPICashBuy, true, l.Verbose, nil) +} + +// GetBitcoinsOnlineAd this API returns buy or sell Bitcoin online ads. +func (l *LocalBitcoins) GetBitcoinsOnlineAd(countryCode, countryName, paymentMethod string, BuySide bool) error { + return common.SendHTTPGetRequest(localbitcoinsAPIURL+localbitcoinsAPIOnlineBuy, true, l.Verbose, nil) +} + +// GetTicker returns list of all completed trades. +func (l *LocalBitcoins) GetTicker() (map[string]Ticker, error) { + result := make(map[string]Ticker) + + return result, + common.SendHTTPGetRequest(localbitcoinsAPIURL+localbitcoinsAPITicker, true, l.Verbose, &result) +} + +// GetTrades returns all closed trades in online buy and online sell categories, +// updated every 15 minutes. +func (l *LocalBitcoins) GetTrades(currency string, values url.Values) ([]Trade, error) { + path := common.EncodeURLValues(fmt.Sprintf("%s/%s/trades.json", localbitcoinsAPIURL+localbitcoinsAPIBitcoincharts, currency), values) + result := []Trade{} + + return result, common.SendHTTPGetRequest(path, true, l.Verbose, &result) +} + +// GetOrderbook returns buy and sell bitcoin online advertisements. Amount is +// the maximum amount available for the trade request. Price is the hourly +// updated price. The price is based on the price equation and commission % +// entered by the ad author. +func (l *LocalBitcoins) GetOrderbook(currency string) (Orderbook, error) { + type response struct { + Bids [][]string `json:"bids"` + Asks [][]string `json:"asks"` + } + + path := fmt.Sprintf("%s/%s/orderbook.json", localbitcoinsAPIURL+localbitcoinsAPIBitcoincharts, currency) + resp := response{} + err := common.SendHTTPGetRequest(path, true, l.Verbose, &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.Println(err) + continue + } + amount, err := strconv.ParseFloat(x[1], 64) + if err != nil { + log.Println(err) + continue + } + orderbook.Bids = append(orderbook.Bids, Price{price, amount}) + } + + for _, x := range resp.Asks { + price, err := strconv.ParseFloat(x[0], 64) + if err != nil { + log.Println(err) + continue + } + amount, err := strconv.ParseFloat(x[1], 64) + if err != nil { + log.Println(err) + continue + } + orderbook.Asks = append(orderbook.Asks, Price{price, amount}) + } + + return orderbook, nil +} + +// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to +// localbitcoins func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, values url.Values, result interface{}) (err error) { if !l.AuthenticatedAPISupport { return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, l.Name) @@ -306,16 +681,29 @@ func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, values headers["Apiauth-Signature"] = common.StringToUpper(common.HexEncodeToString(hmac)) headers["Content-Type"] = "application/x-www-form-urlencoded" - resp, err := common.SendHTTPRequest(method, LOCALBITCOINS_API_URL+path, headers, bytes.NewBuffer([]byte(payload))) + if l.Verbose { + log.Printf("Raw Path: \n%s\n", path) + } + + resp, err := common.SendHTTPRequest(method, localbitcoinsAPIURL+path, headers, bytes.NewBuffer([]byte(payload))) + if err != nil { + return err + } if l.Verbose { log.Printf("Received raw: \n%s\n", resp) } - err = common.JSONDecode([]byte(resp), &result) + errCapture := GeneralError{} + if err = common.JSONDecode([]byte(resp), &errCapture); err == nil { + if len(errCapture.Error.Message) != 0 { + return errors.New(errCapture.Error.Message) + } + } + err = common.JSONDecode([]byte(resp), &result) if err != nil { - return errors.New("unable to JSON Unmarshal response") + return err } return nil diff --git a/exchanges/localbitcoins/localbitcoins_test.go b/exchanges/localbitcoins/localbitcoins_test.go new file mode 100644 index 00000000..41492dec --- /dev/null +++ b/exchanges/localbitcoins/localbitcoins_test.go @@ -0,0 +1,75 @@ +package localbitcoins + +import ( + "testing" + + "github.com/thrasher-/gocryptotrader/config" +) + +var l LocalBitcoins + +// Please supply your own APIKEYS here for due diligence testing + +const ( + apiKey = "" + apiSecret = "" +) + +func TestSetDefaults(t *testing.T) { + l.SetDefaults() +} + +func TestSetup(t *testing.T) { + cfg := config.GetConfig() + cfg.LoadConfig("../../testdata/configtest.json") + localbitcoinsConfig, err := cfg.GetExchangeConfig("LocalBitcoins") + if err != nil { + t.Error("Test Failed - LakeBTC Setup() init error") + } + + localbitcoinsConfig.AuthenticatedAPISupport = true + localbitcoinsConfig.APIKey = apiKey + localbitcoinsConfig.APISecret = apiSecret + + l.Setup(localbitcoinsConfig) +} + +func TestGetFee(t *testing.T) { + t.Parallel() + if l.GetFee(false) != 0 || l.GetFee(true) != 0 { + t.Error("Test Failed - GetFee() error") + } +} + +func TestGetAccountInfo(t *testing.T) { + t.Parallel() + _, err := l.GetAccountInfo("", true) + if err == nil { + t.Error("Test Failed - GetAccountInfo() error", err) + } + _, err = l.GetAccountInfo("bitcoinbaron", false) + if err != nil { + t.Error("Test Failed - GetAccountInfo() error", err) + } +} + +func TestGetads(t *testing.T) { + t.Parallel() + _, err := l.Getads("") + if err == nil { + t.Error("Test Failed - Getads() - Full list, error", err) + } + _, err = l.Getads("1337") + if err == nil { + t.Error("Test Failed - Getads() error", err) + } +} + +func TestEditAd(t *testing.T) { + t.Parallel() + edit := AdEdit{} + err := l.EditAd(edit, "1337") + if err == nil { + t.Error("Test Failed - EditAd() error", err) + } +} diff --git a/exchanges/localbitcoins/localbitcoins_types.go b/exchanges/localbitcoins/localbitcoins_types.go index 337c2e00..bd82ecbb 100644 --- a/exchanges/localbitcoins/localbitcoins_types.go +++ b/exchanges/localbitcoins/localbitcoins_types.go @@ -4,7 +4,318 @@ import ( "time" ) -type LocalBitcoinsTicker struct { +// GeneralError is an error capture type +type GeneralError struct { + Error struct { + Message string `json:"message"` + ErrorCode int `json:"error_code"` + } `json:"error"` +} + +// AccountInfo holds public user information +type AccountInfo struct { + Username string `json:"username"` + FeedbackScore int `json:"feedback_score"` + FeedbackCount int `json:"feedback_count"` + RealNameVeriTrusted int `json:"real_name_verifications_trusted"` + TradingPartners int `json:"trading_partners_count"` + URL string `json:"url"` + RealNameVeriUntrusted int `json:"real_name_verifications_untrusted"` + HasFeedback bool `json:"has_feedback"` + IdentityVerifiedAt string `json:"identify_verified_at"` + TrustedCount int `json:"trusted_count"` + FeedbacksUnconfirmed int `json:"feedbacks_unconfirmed_count"` + BlockedCount int `json:"blocked_count"` + TradeVolumeText string `json:"trade_volume_text"` + HasCommonTrades bool `json:"has_common_trades"` + RealNameVeriRejected int `json:"real_name_verifications_rejected"` + AgeText string `json:"age_text"` + ConfirmedTradesText string `json:"confirmed_trade_count_text"` + CreatedAt string `json:"created_at"` +} + +// AdData references the full possible return of ad data +type AdData struct { + AdList []struct { + Data struct { + Visible bool `json:"visible"` + HiddenByOpeningHours bool `json:"hidden_by_opening_hours"` + Location string `json:"location_string"` + CountryCode string `json:"countrycode"` + City string `json:"city"` + TradeType string `json:"trade_type"` + OnlineProvider string `json:"online_provider"` + FirstTimeLimitBTC string `json:"first_time_limit_btc"` + VolumeCoefficientBTC string `json:"volume_coefficient_btc"` + SMSVerficationRequired bool `json:"sms_verification_required"` + ReferenceType string `json:"reference_type"` + DisplayReference bool `json:"display_reference"` + Currency string `json:"currency"` + Lat float64 `json:"lat"` + Lon float64 `json:"lon"` + MinAmount string `json:"min_amount"` + MaxAmount string `json:"max_amount"` + MaXAmountAvailable string `json:"max_amount_available"` + LimitToFiatAmounts string `json:"limit_to_fiat_amounts"` + AdID int64 `json:"ad_id"` + TempPriceUSD string `json:"temp_price_usd"` + Floating bool `json:"floating"` + Profile interface{} `json:"profile"` + RequireFeedBackScore int `json:"require_feedback_score"` + RequireTradeVolume float64 `json:"require_trade_volume"` + RequireTrustedByAdvertiser bool `json:"require_trusted_by_advertiser"` + PaymentWindowMinutes int `json:"payment_window_minutes"` + BankName string `json:"bank_name"` + TrackMaxAmount bool `json:"track_max_amount"` + ATMModel string `json:"atm_model"` + PriceEquation string `json:"price_equation"` + OpeningHours interface{} `json:"opening_hours"` + AccountInfo string `json:"account_info"` + AccountDetails interface{} `json:"account_details"` + } `json:"data"` + Actions struct { + PublicView string `json:"public_view"` + HTMLEdit string `json:"html_edit"` + ChangeForm string `json:"change_form"` + ContactForm string `json:"contact_form"` + } `json:"actions"` + } `json:"ad_list"` + AdCount int `json:"ad_count"` +} + +// AdEdit references an outgoing paramater type for EditAd() method +type AdEdit struct { + // Required Arguments + PriceEquation string `json:"price_equation"` + Latitude int `json:"lat"` + Longitude int `json:"lon"` + City string `json:"city"` + Location string `json:"location_string"` + CountryCode string `json:"countrycode"` + Currency string `json:"currency"` + AccountInfo string `json:"account_info"` + BankName string `json:"bank_name"` + MSG string `json:"msg"` + SMSVerficationRequired bool `json:"sms_verification_required"` + TrackMaxAmount bool `json:"track_max_amount"` + RequireTrustedByAdvertiser bool `json:"require_trusted_by_advertiser"` + RequireIdentification bool `json:"require_identification"` + + // Optional Arguments + MinAmount int `json:"min_amount"` + MaxAmount int `json:"max_amount"` + OpeningHours []string `json:"opening_hours"` + LimitToFiatAmounts string `json:"limit_to_fiat_amounts"` + Visible bool `json:"visible,int"` + + // Optional Arguments ONLINE_SELL ads + RequireTradeVolume int `json:"require_trade_volume"` + RequireFeedBackScore int `json:"require_feedback_score"` + FirstTimeLimitBTC int `json:"first_time_limit_btc"` + VolumeCoefficientBTC int `json:"volume_coefficient_btc"` + ReferenceType string `json:"reference_type"` + DisplayReference bool `json:"display_reference,int"` + + // Optional Arguments ONLINE_BUY + PaymentWindowMinutes int `json:"payment_window_minutes"` + + // Optional Arguments LOCAL_SELL + Floating bool `json:"floating,int"` +} + +// AdCreate references an outgoing paramater type for CreateAd() method +type AdCreate struct { + // Required Arguments + PriceEquation string `json:"price_equation"` + Latitude int `json:"lat"` + Longitude int `json:"lon"` + City string `json:"city"` + Location string `json:"location_string"` + CountryCode string `json:"countrycode"` + Currency string `json:"currency"` + AccountInfo string `json:"account_info"` + BankName string `json:"bank_name"` + MSG string `json:"msg"` + SMSVerficationRequired bool `json:"sms_verification_required"` + TrackMaxAmount bool `json:"track_max_amount"` + RequireTrustedByAdvertiser bool `json:"require_trusted_by_advertiser"` + RequireIdentification bool `json:"require_identification"` + OnlineProvider string `json:"online_provider"` + TradeType string `json:"trade_type"` + + // Optional Arguments + MinAmount int `json:"min_amount"` + MaxAmount int `json:"max_amount"` + OpeningHours []string `json:"opening_hours"` + LimitToFiatAmounts string `json:"limit_to_fiat_amounts"` + Visible bool `json:"visible,int"` + + // Optional Arguments ONLINE_SELL ads + RequireTradeVolume int `json:"require_trade_volume"` + RequireFeedBackScore int `json:"require_feedback_score"` + FirstTimeLimitBTC int `json:"first_time_limit_btc"` + VolumeCoefficientBTC int `json:"volume_coefficient_btc"` + ReferenceType string `json:"reference_type"` + DisplayReference bool `json:"display_reference,int"` + + // Optional Arguments ONLINE_BUY + PaymentWindowMinutes int `json:"payment_window_minutes"` + + // Optional Arguments LOCAL_SELL + Floating bool `json:"floating,int"` +} + +// Message holds the returned message data from a contact +type Message struct { + MSG string `json:"msg"` + Sender struct { + ID int64 `json:"id"` + Name string `json:"name"` + Username string `json:"username"` + TradeCount int64 `json:"trafe_count"` + LastOnline string `json:"last_online"` + } `json:"sender"` + CreatedAt string `json:"created_at"` + IsAdmin bool `json:"is_admin"` + AttachmentName string `json:"attachment_name"` + AttachmentType string `json:"attachment_type"` + AttachmentURL string `json:"attachment_url"` +} + +// DashBoardInfo holds the full range of metadata for a dashboard image +type DashBoardInfo struct { + Data struct { + CreatedAt string `json:"created_at"` + Buyer struct { + Username string `json:"username"` + TradeCount string `json:"trade_count"` + FeedbackScore string `json:"feedback_score"` + Name string `json:"name"` + LastOnline string `json:"last_online"` + RealName string `json:"real_name"` + CompanyName string `json:"company_name"` + CountryCodeByIP string `json:"countrycode_by_ip"` + CountryCodeByPhoneNUmber string `json:"countrycode_by_phone_number"` + } `json:"buyer"` + Seller struct { + Username string `json:"username"` + TradeCount string `json:"trade_count"` + FeedbackScore string `json:"feedback_score"` + Name string `json:"name"` + LastOnline string `json:"last_online"` + } `json:"seller"` + ReferenceCode string `json:"reference_code"` + Currency string `json:"currency"` + Amount float64 `json:"amount,string"` + AmountBTC float64 `json:"amount_btc,string"` + FeeBTC float64 `json:"fee_btc,string"` + ExchangeRateUpdatedAt string `json:"exchange_rate_updated_at"` + Advertisement struct { + ID int `json:"id"` + TradeType string `json:"trade_type"` + Advertiser struct { + Username string `json:"username"` + TradeCount string `json:"trade_count"` + FeedbackScore string `json:"feedback_score"` + Name string `json:"name"` + LastOnline string `json:"last_online"` + } `json:"advertiser"` + } `json:"advertisement"` + ContactID int `json:"contact_id"` + CanceledAt string `json:"canceled_at"` + EscrowedAt string `json:"escrowed_at"` + FundedAt string `json:"funded_at"` + PaymentCompletedAt string `json:"payment_completed_at"` + DisputedAt string `json:"disputed_at"` + ClosedAt string `json:"closed_at"` + ReleasedAt string `json:"released_at"` + IsBuying bool `json:"is_buying"` + IsSelling bool `json:"is_selling"` + AccountDetails struct { + ReceiverName string `json:"receiver_name"` + IBAN string `json:"iban"` + SwiftBIC string `json:"swift_bic"` + Reference string `json:"reference"` + } `json:"account_details"` + AccountInfo string `json:"account_info"` + Floating bool `json:"floating"` + } `json:"data"` + Actions struct { + MarkAsPaidURL string `json:"mark_as_paid_url"` + AdvertisementPublicView string `json:"advertisement_public_view"` + MessageURL string `json:"message_url"` + MessagePostURL string `json:"message_post_url"` + } `json:"actions"` +} + +// Invoice contains invoice data +type Invoice struct { + Invoice struct { + Description string `json:"description"` + Created string `json:"created"` + URL string `json:"url"` + Amount float64 `json:"amount,string"` + Internal bool `json:"internal"` + Currency string `json:"currency"` + State string `json:"state"` + ID string `json:"id"` + BTCAmount string `json:"btc_amount"` + BTCAddress string `json:"btc_address"` + DeletingAllowed bool `json:"deleting_allowed"` + } `json:"invoice"` +} + +// NotificationInfo holds Notification data +type NotificationInfo struct { + URL string `json:"url"` + CreatedAt string `json:"created_at"` + ContactID int64 `json:"contact_id"` + Read bool `json:"read"` + MSG string `json:"msg"` + ID string `json:"id"` +} + +// WalletInfo holds full wallet information data +type WalletInfo struct { + Message string `json:"message"` + Total Balance `json:"total"` + SentTransactions30d []WalletTransaction `json:"sent_transactions_30d"` + ReceivedTransactions30d []WalletTransaction `json:"received_transactions_30d"` + ReceivingAddressCount int `json:"receiving_address_count"` + ReceivingAddressList []WalletAddressList `json:"receiving_address_list"` +} + +// Balance is a sub-type for WalletInfo & WalletBalanceInfo +type Balance struct { + Balance float64 `json:"balance,string"` + Sendable float64 `json:"Sendable,string"` +} + +// WalletTransaction is a sub-type for WalletInfo +type WalletTransaction struct { + TXID string `json:"txid"` + Amount float64 `json:"amount,string"` + Description string `json:"description"` + TXType int `json:"tx_type"` + CreatedAt time.Time `json:"created_at"` +} + +// WalletAddressList is a sub-type for WalletInfo & WalletBalanceInfo +type WalletAddressList struct { + Address string `json:"address"` + Received float64 `json:"received,string"` +} + +// WalletBalanceInfo standard wallet balance information +type WalletBalanceInfo struct { + Message string `json:"message"` + Total Balance `json:"total"` + ReceivingAddressCount int `json:"receiving_address_count"` // always 1 + ReceivingAddressList []WalletAddressList `json:"receiving_address_list"` +} + +// Ticker contains ticker information +type Ticker struct { Avg12h float64 `json:"avg_12h,string"` Avg1h float64 `json:"avg_1h,string"` Avg24h float64 `json:"avg_24h,string"` @@ -14,71 +325,22 @@ type LocalBitcoinsTicker struct { VolumeBTC float64 `json:"volume_btc,string"` } -type LocalBitcoinsTrade struct { +// Trade holds closed trade information +type Trade struct { TID int64 `json:"tid"` Date int64 `json:"date"` Amount float64 `json:"amount,string"` Price float64 `json:"price,string"` } -type LocalBitcoinsOrderbookStructure struct { +// Orderbook is a full range of bid and asks for localbitcoins +type Orderbook struct { + Bids []Price `json:"bids"` + Asks []Price `json:"asks"` +} + +// Price is a sub-type for orderbook +type Price struct { Price float64 Amount float64 } - -type LocalBitcoinsOrderbook struct { - Bids []LocalBitcoinsOrderbookStructure `json:"bids"` - Asks []LocalBitcoinsOrderbookStructure `json:"asks"` -} - -type LocalBitcoinsAccountInfo struct { - Username string `json:"username"` - CreatedAt time.Time `json:"created_at"` - AgeText string `json:"age_text"` - TradingPartners int `json:"trading_partners_count"` - FeedbacksUnconfirmed int `json:"feedbacks_unconfirmed_count"` - TradeVolumeText string `json:"trade_volume_text"` - HasCommonTrades bool `json:"has_common_trades"` - HasFeedback bool `json:"has_feedback"` - ConfirmedTradesText string `json:"confirmed_trade_count_text"` - BlockedCount int `json:"blocked_count"` - FeedbackScore int `json:"feedback_score"` - FeedbackCount int `json:"feedback_count"` - URL string `json:"url"` - TrustedCount int `json:"trusted_count"` - IdentityVerifiedAt time.Time `json:"identify_verified_at"` -} - -type LocalBitcoinsBalance struct { - Balance float64 `json:"balance,string"` - Sendable float64 `json:"Sendable,string"` -} - -type LocalBitcoinsWalletTransaction struct { - TXID string `json:"txid"` - Amount float64 `json:"amount,string"` - Description string `json:"description"` - TXType int `json:"tx_type"` - CreatedAt time.Time `json:"created_at"` -} - -type LocalBitcoinsWalletAddressList struct { - Address string `json:"address"` - Received float64 `json:"received,string"` -} - -type LocalBitcoinsWalletInfo struct { - Message string `json:"message"` - Total LocalBitcoinsBalance `json:"total"` - SentTransactions30d []LocalBitcoinsWalletTransaction `json:"sent_transactions_30d"` - ReceivedTransactions30d []LocalBitcoinsWalletTransaction `json:"received_transactions_30d"` - ReceivingAddressCount int `json:"receiving_address_count"` - ReceivingAddressList []LocalBitcoinsWalletAddressList `json:"receiving_address_list"` -} - -type LocalBitcoinsWalletBalanceInfo struct { - Message string `json:"message"` - Total LocalBitcoinsBalance `json:"total"` - ReceivingAddressCount int `json:"receiving_address_count"` // always 1 - ReceivingAddressList []LocalBitcoinsWalletAddressList `json:"receiving_address_list"` -}