mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
322 lines
8.5 KiB
Go
322 lines
8.5 KiB
Go
package currency
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/thrasher-/gocryptotrader/common"
|
|
)
|
|
|
|
// Rate holds the current exchange rates for the currency pair.
|
|
type Rate struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"Name"`
|
|
Rate float64 `json:",string"`
|
|
Date string `json:"Date"`
|
|
Time string `json:"Time"`
|
|
Ask float64 `json:",string"`
|
|
Bid float64 `json:",string"`
|
|
}
|
|
|
|
// YahooJSONResponseInfo is a sub type that holds JSON response info
|
|
type YahooJSONResponseInfo struct {
|
|
Count int `json:"count"`
|
|
Created time.Time `json:"created"`
|
|
Lang string `json:"lang"`
|
|
}
|
|
|
|
// YahooJSONResponse holds Yahoo API responses
|
|
type YahooJSONResponse struct {
|
|
Query struct {
|
|
YahooJSONResponseInfo
|
|
Results struct {
|
|
Rate []Rate `json:"rate"`
|
|
}
|
|
}
|
|
}
|
|
|
|
// FixerResponse contains the data fields for the Fixer API response
|
|
type FixerResponse struct {
|
|
Base string `json:"base"`
|
|
Date string `json:"date"`
|
|
Rates map[string]float64 `json:"rates"`
|
|
}
|
|
|
|
const (
|
|
maxCurrencyPairsPerRequest = 350
|
|
yahooYQLURL = "https://query.yahooapis.com/v1/public/yql?"
|
|
yahooDatabase = "store://datatables.org/alltableswithkeys"
|
|
fixerAPI = "http://api.fixer.io/latest"
|
|
// DefaultCurrencies has the default minimum of FIAT values
|
|
DefaultCurrencies = "USD,AUD,EUR,CNY"
|
|
// DefaultCryptoCurrencies has the default minimum of crytpocurrency values
|
|
DefaultCryptoCurrencies = "BTC,LTC,ETH,DOGE,DASH,XRP,XMR"
|
|
)
|
|
|
|
// Variables for package which includes base error strings & exportable
|
|
// queries
|
|
var (
|
|
CurrencyStore map[string]Rate
|
|
CurrencyStoreFixer map[string]float64
|
|
BaseCurrencies []string
|
|
CryptoCurrencies []string
|
|
ErrCurrencyDataNotFetched = errors.New("yahoo currency data has not been fetched yet")
|
|
ErrCurrencyNotFound = errors.New("unable to find specified currency")
|
|
ErrQueryingYahoo = errors.New("unable to query Yahoo currency values")
|
|
ErrQueryingYahooZeroCount = errors.New("yahoo returned zero currency data")
|
|
YahooEnabled = false
|
|
)
|
|
|
|
// SetProvider sets the currency exchange service used by the currency
|
|
// converter
|
|
func SetProvider(yahooEnabled bool) {
|
|
if yahooEnabled {
|
|
YahooEnabled = true
|
|
return
|
|
}
|
|
YahooEnabled = false
|
|
}
|
|
|
|
// SwapProvider swaps the currency exchange service used by the curency
|
|
// converter
|
|
func SwapProvider() {
|
|
if YahooEnabled {
|
|
YahooEnabled = false
|
|
return
|
|
}
|
|
YahooEnabled = true
|
|
}
|
|
|
|
// GetProvider returns the currency exchange service used by the currency
|
|
// converter
|
|
func GetProvider() string {
|
|
if YahooEnabled {
|
|
return "yahoo"
|
|
}
|
|
return "fixer"
|
|
}
|
|
|
|
// IsDefaultCurrency checks if the currency passed in matches the default
|
|
// FIAT currency
|
|
func IsDefaultCurrency(currency string) bool {
|
|
defaultCurrencies := common.SplitStrings(DefaultCurrencies, ",")
|
|
return common.DataContains(defaultCurrencies, common.StringToUpper(currency))
|
|
}
|
|
|
|
// IsDefaultCryptocurrency checks if the currency passed in matches the default
|
|
// CRYPTO currency
|
|
func IsDefaultCryptocurrency(currency string) bool {
|
|
cryptoCurrencies := common.SplitStrings(DefaultCryptoCurrencies, ",")
|
|
return common.DataContains(cryptoCurrencies, common.StringToUpper(currency))
|
|
}
|
|
|
|
// IsFiatCurrency checks if the currency passed is an enabled FIAT currency
|
|
func IsFiatCurrency(currency string) bool {
|
|
if len(BaseCurrencies) == 0 {
|
|
log.Println("IsFiatCurrency: BaseCurrencies string variable not populated")
|
|
return false
|
|
}
|
|
return common.DataContains(BaseCurrencies, common.StringToUpper(currency))
|
|
}
|
|
|
|
// IsCryptocurrency checks if the currency passed is an enabled CRYPTO currency.
|
|
func IsCryptocurrency(currency string) bool {
|
|
if len(CryptoCurrencies) == 0 {
|
|
log.Println(
|
|
"IsCryptocurrency: CryptoCurrencies string variable not populated",
|
|
)
|
|
return false
|
|
}
|
|
return common.DataContains(CryptoCurrencies, common.StringToUpper(currency))
|
|
}
|
|
|
|
// Update updates the local crypto currency or base currency store
|
|
func Update(input []string, cryptos bool) {
|
|
for x := range input {
|
|
if cryptos {
|
|
if !common.DataContains(CryptoCurrencies, input[x]) {
|
|
CryptoCurrencies = append(CryptoCurrencies, input[x])
|
|
}
|
|
} else {
|
|
if !common.DataContains(BaseCurrencies, input[x]) {
|
|
BaseCurrencies = append(BaseCurrencies, input[x])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// SeedCurrencyData takes the desired FIAT currency string, if not defined the
|
|
// function will assign it the default values. The function will query
|
|
// yahoo for the currency values and will seed currency data.
|
|
func SeedCurrencyData(fiatCurrencies string) error {
|
|
if fiatCurrencies == "" {
|
|
fiatCurrencies = DefaultCurrencies
|
|
}
|
|
|
|
if YahooEnabled {
|
|
return QueryYahooCurrencyValues(fiatCurrencies)
|
|
}
|
|
|
|
return FetchFixerCurrencyData()
|
|
}
|
|
|
|
// MakecurrencyPairs takes all supported currency and turns them into pairs.
|
|
func MakecurrencyPairs(supportedCurrencies string) string {
|
|
currencies := common.SplitStrings(supportedCurrencies, ",")
|
|
var pairs []string
|
|
count := len(currencies)
|
|
for i := 0; i < count; i++ {
|
|
currency := currencies[i]
|
|
for j := 0; j < count; j++ {
|
|
if currency != currencies[j] {
|
|
pairs = append(pairs, currency+currencies[j])
|
|
}
|
|
}
|
|
}
|
|
return common.JoinStrings(pairs, ",")
|
|
}
|
|
|
|
// ConvertCurrency for example converts $1 USD to the equivalent Japanese Yen
|
|
// or vice versa.
|
|
func ConvertCurrency(amount float64, from, to string) (float64, error) {
|
|
from = common.StringToUpper(from)
|
|
to = common.StringToUpper(to)
|
|
|
|
if from == to {
|
|
return amount, nil
|
|
}
|
|
|
|
if YahooEnabled {
|
|
currency := from + to
|
|
_, ok := CurrencyStore[currency]
|
|
if !ok {
|
|
err := SeedCurrencyData(currency[:len(from)] + "," + currency[len(to):])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
|
|
result, ok := CurrencyStore[currency]
|
|
if !ok {
|
|
return 0, ErrCurrencyNotFound
|
|
}
|
|
return amount * result.Rate, nil
|
|
}
|
|
|
|
_, ok := CurrencyStoreFixer[from]
|
|
if !ok {
|
|
err := FetchFixerCurrencyData()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
|
|
var resultFrom float64
|
|
var resultTo float64
|
|
|
|
// First check if we're converting to USD, USD doesn't exist in the rates map
|
|
if to == "USD" {
|
|
resultFrom, ok = CurrencyStoreFixer[from]
|
|
if !ok {
|
|
return 0, ErrCurrencyNotFound
|
|
}
|
|
return amount / resultFrom, nil
|
|
}
|
|
|
|
// Check to see if we're converting from USD
|
|
if from == "USD" {
|
|
resultTo, ok = CurrencyStoreFixer[to]
|
|
if !ok {
|
|
return 0, ErrCurrencyNotFound
|
|
}
|
|
return resultTo * amount, nil
|
|
}
|
|
|
|
// Otherwise convert to USD, then to the target currency
|
|
resultFrom, ok = CurrencyStoreFixer[from]
|
|
if !ok {
|
|
return 0, ErrCurrencyNotFound
|
|
}
|
|
|
|
converted := amount / resultFrom
|
|
resultTo, ok = CurrencyStoreFixer[to]
|
|
if !ok {
|
|
return 0, ErrCurrencyNotFound
|
|
}
|
|
|
|
return converted * resultTo, nil
|
|
}
|
|
|
|
// FetchFixerCurrencyData seeds the variable C
|
|
func FetchFixerCurrencyData() error {
|
|
var result FixerResponse
|
|
values := url.Values{}
|
|
values.Set("base", "USD")
|
|
url := common.EncodeURLValues(fixerAPI, values)
|
|
|
|
CurrencyStoreFixer = make(map[string]float64)
|
|
|
|
err := common.SendHTTPGetRequest(url, true, false, &result)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
CurrencyStoreFixer = result.Rates
|
|
return nil
|
|
}
|
|
|
|
// FetchYahooCurrencyData seeds the variable CurrencyStore; this is a
|
|
// map[string]Rate
|
|
func FetchYahooCurrencyData(currencyPairs []string) error {
|
|
values := url.Values{}
|
|
values.Set(
|
|
"q", fmt.Sprintf("SELECT * from yahoo.finance.xchange WHERE pair in (\"%s\")",
|
|
common.JoinStrings(currencyPairs, ",")),
|
|
)
|
|
values.Set("format", "json")
|
|
values.Set("env", yahooDatabase)
|
|
|
|
headers := make(map[string]string)
|
|
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
|
|
|
resp, err := common.SendHTTPRequest(
|
|
"POST", yahooYQLURL, headers, strings.NewReader(values.Encode()),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("Currency recv: %s", resp)
|
|
|
|
yahooResp := YahooJSONResponse{}
|
|
err = common.JSONDecode([]byte(resp), &yahooResp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if yahooResp.Query.Count == 0 {
|
|
return ErrQueryingYahooZeroCount
|
|
}
|
|
|
|
for i := 0; i < yahooResp.Query.YahooJSONResponseInfo.Count; i++ {
|
|
CurrencyStore[yahooResp.Query.Results.Rate[i].ID] = yahooResp.Query.Results.Rate[i]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// QueryYahooCurrencyValues takes in desired currencies, creates pairs then
|
|
// uses FetchYahooCurrencyData to seed CurrencyStore
|
|
func QueryYahooCurrencyValues(currencies string) error {
|
|
CurrencyStore = make(map[string]Rate)
|
|
currencyPairs := common.SplitStrings(MakecurrencyPairs(currencies), ",")
|
|
log.Printf(
|
|
"%d fiat currency pairs generated. Fetching Yahoo currency data (this may take a minute)..\n",
|
|
len(currencyPairs),
|
|
)
|
|
return FetchYahooCurrencyData(currencyPairs)
|
|
}
|