Add optional fiat rate to balance history
parent
0340cef13c
commit
225ac85a2a
|
@ -303,7 +303,7 @@ type BalanceHistory struct {
|
|||
Txs uint32 `json:"txs"`
|
||||
ReceivedSat *Amount `json:"received"`
|
||||
SentSat *Amount `json:"sent"`
|
||||
FiatRate string `json:"fiatrate,omitempty"`
|
||||
FiatRate string `json:"fiatRate,omitempty"`
|
||||
Txid string `json:"txid,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -914,8 +914,26 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
|
|||
return &bh, nil
|
||||
}
|
||||
|
||||
func (w *Worker) setFiatRateToBalanceHistories(histories BalanceHistories, fiat string) error {
|
||||
for i := range histories {
|
||||
bh := &histories[i]
|
||||
t := time.Unix(int64(bh.Time), 0)
|
||||
ticker, err := w.db.FiatRatesFindTicker(&t)
|
||||
if err != nil {
|
||||
glog.Errorf("Error finding ticker by date %v. Error: %v", t, err)
|
||||
continue
|
||||
} else if ticker == nil {
|
||||
continue
|
||||
}
|
||||
if rate, found := ticker.Rates[fiat]; found {
|
||||
bh.FiatRate = string(rate)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBalanceHistory returns history of balance for given address
|
||||
func (w *Worker) GetBalanceHistory(address string, fromTime, toTime time.Time) (BalanceHistories, error) {
|
||||
func (w *Worker) GetBalanceHistory(address string, fromTime, toTime time.Time, fiat string) (BalanceHistories, error) {
|
||||
bhs := make(BalanceHistories, 0)
|
||||
start := time.Now()
|
||||
addrDesc, _, err := w.getAddrDescAndNormalizeAddress(address)
|
||||
|
@ -940,6 +958,12 @@ func (w *Worker) GetBalanceHistory(address string, fromTime, toTime time.Time) (
|
|||
}
|
||||
}
|
||||
bha := bhs.SortAndAggregate(3600)
|
||||
if fiat != "" {
|
||||
err = w.setFiatRateToBalanceHistories(bha, fiat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
glog.Info("GetBalanceHistory ", address, ", blocks ", fromHeight, "-", toHeight, ", count ", len(bha), " finished in ", time.Since(start))
|
||||
return bha, nil
|
||||
}
|
||||
|
@ -1117,29 +1141,27 @@ func (w *Worker) GetBlocks(page int, blocksOnPage int) (*Blocks, error) {
|
|||
|
||||
// getFiatRatesResult checks if CurrencyRatesTicker contains all necessary data and returns formatted result
|
||||
func (w *Worker) getFiatRatesResult(currency string, ticker *db.CurrencyRatesTicker) (*db.ResultTickerAsString, error) {
|
||||
resultRates := make(map[string]json.Number)
|
||||
rates := make(map[string]json.Number, 2)
|
||||
timeFormatted := ticker.Timestamp.Format(db.FiatRatesTimeFormat)
|
||||
|
||||
// Check if both USD rate and the desired currency rate exist in the result
|
||||
for _, currencySymbol := range []string{"usd", currency} {
|
||||
if _, found := ticker.Rates[currencySymbol]; !found {
|
||||
availableCurrencies := make([]string, 0, len(ticker.Rates))
|
||||
for availableCurrency := range ticker.Rates {
|
||||
availableCurrencies = append(availableCurrencies, string(availableCurrency))
|
||||
}
|
||||
sort.Strings(availableCurrencies) // sort to get deterministic results
|
||||
availableCurrenciesString := strings.Join(availableCurrencies, ", ")
|
||||
return nil, NewAPIError(fmt.Sprintf("Currency %q is not available for timestamp %s. Available currencies are: %s.", currency, timeFormatted, availableCurrenciesString), true)
|
||||
if rate, found := ticker.Rates[currency]; !found {
|
||||
availableCurrencies := make([]string, 0, len(ticker.Rates))
|
||||
for availableCurrency := range ticker.Rates {
|
||||
availableCurrencies = append(availableCurrencies, availableCurrency)
|
||||
}
|
||||
resultRates[currencySymbol] = ticker.Rates[currencySymbol]
|
||||
if currencySymbol == "usd" && currency == "usd" {
|
||||
break
|
||||
sort.Strings(availableCurrencies) // sort to get deterministic results
|
||||
return nil, NewAPIError(fmt.Sprintf("Currency %q is not available for timestamp %s. Available currencies are: %s", currency, timeFormatted, strings.Join(availableCurrencies, ",")), true)
|
||||
} else {
|
||||
rates[currency] = rate
|
||||
}
|
||||
// add default usd currency
|
||||
if currency != "usd" {
|
||||
if rate, found := ticker.Rates["usd"]; found {
|
||||
rates["usd"] = rate
|
||||
}
|
||||
}
|
||||
|
||||
result := &db.ResultTickerAsString{
|
||||
Timestamp: timeFormatted,
|
||||
Rates: resultRates,
|
||||
Rates: rates,
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
@ -591,7 +591,7 @@ func (w *Worker) GetXpubUtxo(xpub string, onlyConfirmed bool, gap int) (Utxos, e
|
|||
}
|
||||
|
||||
// GetXpubBalanceHistory returns history of balance for given xpub
|
||||
func (w *Worker) GetXpubBalanceHistory(xpub string, fromTime, toTime time.Time, gap int) (BalanceHistories, error) {
|
||||
func (w *Worker) GetXpubBalanceHistory(xpub string, fromTime, toTime time.Time, fiat string, gap int) (BalanceHistories, error) {
|
||||
bhs := make(BalanceHistories, 0)
|
||||
start := time.Now()
|
||||
fromUnix, fromHeight, toUnix, toHeight := w.balanceHistoryHeightsFromTo(fromTime, toTime)
|
||||
|
@ -623,6 +623,12 @@ func (w *Worker) GetXpubBalanceHistory(xpub string, fromTime, toTime time.Time,
|
|||
}
|
||||
}
|
||||
bha := bhs.SortAndAggregate(3600)
|
||||
if fiat != "" {
|
||||
err = w.setFiatRateToBalanceHistories(bha, fiat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
glog.Info("GetUtxoBalanceHistory ", xpub[:16], ", blocks ", fromHeight, "-", toHeight, ", count ", len(bha), ", finished in ", time.Since(start))
|
||||
return bha, nil
|
||||
}
|
||||
|
|
|
@ -1058,11 +1058,12 @@ func (s *PublicServer) apiBalanceHistory(r *http.Request, apiVersion int) (inter
|
|||
// time.RFC3339
|
||||
toTime, _ = time.Parse("2006-01-02", t)
|
||||
}
|
||||
history, err = s.api.GetXpubBalanceHistory(r.URL.Path[i+1:], fromTime, toTime, gap)
|
||||
fiat := r.URL.Query().Get("fiatcurrency")
|
||||
history, err = s.api.GetXpubBalanceHistory(r.URL.Path[i+1:], fromTime, toTime, fiat, gap)
|
||||
if err == nil {
|
||||
s.metrics.ExplorerViews.With(common.Labels{"action": "api-xpub-balancehistory"}).Inc()
|
||||
} else {
|
||||
history, err = s.api.GetBalanceHistory(r.URL.Path[i+1:], fromTime, toTime)
|
||||
history, err = s.api.GetBalanceHistory(r.URL.Path[i+1:], fromTime, toTime, fiat)
|
||||
s.metrics.ExplorerViews.With(common.Labels{"action": "api-address-balancehistory"}).Inc()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,39 +154,54 @@ func newPostRequest(u string, body string) *http.Request {
|
|||
return r
|
||||
}
|
||||
|
||||
// InitTestFiatRates initializes test data for /api/v2/tickers endpoint
|
||||
func InitTestFiatRates(d *db.RocksDB) error {
|
||||
convertedDate, err := db.FiatRatesConvertDate("20191121140000")
|
||||
func insertFiatRate(date string, rates map[string]json.Number, d *db.RocksDB) error {
|
||||
convertedDate, err := db.FiatRatesConvertDate(date)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ticker := &db.CurrencyRatesTicker{
|
||||
Timestamp: convertedDate,
|
||||
Rates: map[string]json.Number{
|
||||
"usd": "7814.5",
|
||||
"eur": "7100.0",
|
||||
},
|
||||
Rates: rates,
|
||||
}
|
||||
err = d.FiatRatesStoreTicker(ticker)
|
||||
if err != nil {
|
||||
return d.FiatRatesStoreTicker(ticker)
|
||||
}
|
||||
|
||||
// InitTestFiatRates initializes test data for /api/v2/tickers endpoint
|
||||
func InitTestFiatRates(d *db.RocksDB) error {
|
||||
if err := insertFiatRate("20180320020000", map[string]json.Number{
|
||||
"usd": "2000.0",
|
||||
"eur": "1300.0",
|
||||
}, d); err != nil {
|
||||
return err
|
||||
}
|
||||
convertedDate, err = db.FiatRatesConvertDate("20191121143015")
|
||||
if err != nil {
|
||||
if err := insertFiatRate("20180320030000", map[string]json.Number{
|
||||
"usd": "2001.0",
|
||||
"eur": "1301.0",
|
||||
}, d); err != nil {
|
||||
return err
|
||||
}
|
||||
ticker = &db.CurrencyRatesTicker{
|
||||
Timestamp: convertedDate,
|
||||
Rates: map[string]json.Number{
|
||||
"usd": "7914.5",
|
||||
"eur": "7134.1",
|
||||
},
|
||||
}
|
||||
err = d.FiatRatesStoreTicker(ticker)
|
||||
if err != nil {
|
||||
if err := insertFiatRate("20180320040000", map[string]json.Number{
|
||||
"usd": "2002.0",
|
||||
"eur": "1302.0",
|
||||
}, d); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
if err := insertFiatRate("20180321055521", map[string]json.Number{
|
||||
"usd": "2003.0",
|
||||
"eur": "1303.0",
|
||||
}, d); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertFiatRate("20191121140000", map[string]json.Number{
|
||||
"usd": "7814.5",
|
||||
"eur": "7100.0",
|
||||
}, d); err != nil {
|
||||
return err
|
||||
}
|
||||
return insertFiatRate("20191121143015", map[string]json.Number{
|
||||
"usd": "7914.5",
|
||||
"eur": "7134.1",
|
||||
}, d)
|
||||
}
|
||||
|
||||
func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||
|
@ -572,7 +587,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
|||
status: http.StatusOK,
|
||||
contentType: "application/json; charset=utf-8",
|
||||
body: []string{
|
||||
`{"data_timestamp":"20191121140000","rates":{"usd":7814.5}}`,
|
||||
`{"data_timestamp":"20180321055521","rates":{"usd":2003.0}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -590,7 +605,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
|||
status: http.StatusOK,
|
||||
contentType: "application/json; charset=utf-8",
|
||||
body: []string{
|
||||
`{"error":"Currency \"does_not_exist\" is not available for timestamp 20191121140000. Available currencies are: eur, usd."}`,
|
||||
`{"error":"Currency \"does_not_exist\" is not available for timestamp 20191121140000. Available currencies are: eur,usd"}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -764,6 +779,15 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
|||
`[{"time":1521514800,"txs":1,"received":"9876","sent":"0"},{"time":1521594000,"txs":1,"received":"9000","sent":"9876"}]`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "apiBalanceHistory Addr5 v2 fiatcurrency=eur",
|
||||
r: newGetRequest(ts.URL + "/api/v2/balancehistory/2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1?fiatcurrency=eur"),
|
||||
status: http.StatusOK,
|
||||
contentType: "application/json; charset=utf-8",
|
||||
body: []string{
|
||||
`[{"time":1521514800,"txs":1,"received":"9876","sent":"0","fiatRate":"1301.0"},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","fiatRate":"1303.0"}]`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "apiBalanceHistory Addr2 v2 from=2018-03-20&to=2018-03-21",
|
||||
r: newGetRequest(ts.URL + "/api/v2/balancehistory/mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz?from=2018-03-20&to=2018-03-21"),
|
||||
|
@ -791,6 +815,15 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
|||
`[{"time":1521514800,"txs":1,"received":"1","sent":"0"}]`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "apiBalanceHistory xpub v2 from=2018-03-20&to=2018-03-21&fiatcurrency=usd",
|
||||
r: newGetRequest(ts.URL + "/api/v2/balancehistory/" + dbtestdata.Xpub + "?from=2018-03-20&to=2018-03-21&fiatcurrency=usd"),
|
||||
status: http.StatusOK,
|
||||
contentType: "application/json; charset=utf-8",
|
||||
body: []string{
|
||||
`[{"time":1521514800,"txs":1,"received":"1","sent":"0","fiatRate":"2001.0"}]`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "apiBalanceHistory xpub v2 from=2018-03-21",
|
||||
r: newGetRequest(ts.URL + "/api/v2/balancehistory/" + dbtestdata.Xpub + "?from=2018-03-21"),
|
||||
|
@ -1199,7 +1232,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
|||
"currency": "does-not-exist",
|
||||
},
|
||||
},
|
||||
want: `{"id":"20","data":{"error":{"message":"Currency \"does-not-exist\" is not available for timestamp 20191121143015. Available currencies are: eur, usd."}}}`,
|
||||
want: `{"id":"20","data":{"error":{"message":"Currency \"does-not-exist\" is not available for timestamp 20191121143015. Available currencies are: eur,usd"}}}`,
|
||||
},
|
||||
{
|
||||
name: "websocket getFiatRatesForDates missing date",
|
||||
|
@ -1330,16 +1363,17 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
|||
want: `{"id":"32","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0"},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1"}]}`,
|
||||
},
|
||||
{
|
||||
name: "websocket getBalanceHistory xpub from=2018-03-20&to=2018-03-21",
|
||||
name: "websocket getBalanceHistory xpub from=2018-03-20&to=2018-03-21&fiat=usd",
|
||||
req: websocketReq{
|
||||
Method: "getBalanceHistory",
|
||||
Params: map[string]interface{}{
|
||||
"descriptor": dbtestdata.Xpub,
|
||||
"from": "2018-03-20",
|
||||
"to": "2018-03-21",
|
||||
"fiat": "usd",
|
||||
},
|
||||
},
|
||||
want: `{"id":"33","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0"}]}`,
|
||||
want: `{"id":"33","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","fiatRate":"2001.0"}]}`,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -276,9 +276,9 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs
|
|||
return
|
||||
}
|
||||
}
|
||||
rv, err = s.api.GetXpubBalanceHistory(r.Descriptor, fromTime, toTime, r.Gap)
|
||||
rv, err = s.api.GetXpubBalanceHistory(r.Descriptor, fromTime, toTime, r.Fiat, r.Gap)
|
||||
if err != nil {
|
||||
rv, err = s.api.GetBalanceHistory(r.Descriptor, fromTime, toTime)
|
||||
rv, err = s.api.GetBalanceHistory(r.Descriptor, fromTime, toTime, r.Fiat)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
|
|
@ -163,8 +163,8 @@
|
|||
|
||||
function getBalanceHistory() {
|
||||
const descriptor = document.getElementById('getBalanceHistoryDescriptor').value.trim();
|
||||
const from = parseInt(document.getElementById("getBalanceHistoryFrom").value);
|
||||
const to = parseInt(document.getElementById("getBalanceHistoryTo").value);
|
||||
const from = document.getElementById("getBalanceHistoryFrom").value.trim();
|
||||
const to = document.getElementById("getBalanceHistoryTo").value.trim();
|
||||
const fiat = document.getElementById("getBalanceHistoryFiat").value.trim();
|
||||
const method = 'getBalanceHistory';
|
||||
const params = {
|
||||
|
|
Loading…
Reference in New Issue