Changed marketPrice to spotPrice.

Using BigDecimal instead of Double.
This commit is contained in:
Erik C. Thauvin 2021-05-26 03:11:55 -07:00
parent 07a7198455
commit f252c1602b
7 changed files with 51 additions and 48 deletions

View file

@ -2,32 +2,32 @@
[![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/cryptoprice/badge.svg?targetFile=pom.xml)](https://snyk.io/test/github/ethauvin/cryptoprice?targetFile=pom.xml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_cryptoprice&metric=alert_status)](https://sonarcloud.io/dashboard?id=ethauvin_cryptoprice) [![GitHub CI](https://github.com/ethauvin/cryptoprice/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/cryptoprice/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/cryptoprice/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/cryptoprice/tree/master) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/cryptoprice/badge.svg?targetFile=pom.xml)](https://snyk.io/test/github/ethauvin/cryptoprice?targetFile=pom.xml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_cryptoprice&metric=alert_status)](https://sonarcloud.io/dashboard?id=ethauvin_cryptoprice) [![GitHub CI](https://github.com/ethauvin/cryptoprice/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/cryptoprice/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/cryptoprice/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/cryptoprice/tree/master)
# Retrieve cryptocurrencies current market prices # Retrieve cryptocurrencies current prices
A simple Kotlin/Java/Android implementation of the spot price [Coinbase Public API](https://developers.coinbase.com/api/v2#get-spot-price). A simple Kotlin/Java/Android implementation of the spot price [Coinbase Public API](https://developers.coinbase.com/api/v2#get-spot-price).
## Examples (TL;DR) ## Examples (TL;DR)
```kotlin ```kotlin
import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice
// ... // ...
val btc = marketPrice("BTC") // Bitcoin val btc = spotPrice("BTC") // Bitcoin
println(btc.amount) println(btc.amount)
val eth = marketPrice("ETH", "EUR") // Ethereum in Euros val eth = spotPrice("ETH", "EUR") // Ethereum in Euros
println(eth.amount) println(eth.amount)
``` ```
- View [Kotlin](https://github.com/ethauvin/cryptoprice/blob/master/examples/src/main/kotlin/com/example/CryptoPriceExample.kt) or [Java](https://github.com/ethauvin/cryptoprice/blob/master/examples/src/main/java/com/example/CryptoPriceSample.java) Examples. - View [Kotlin](https://github.com/ethauvin/cryptoprice/blob/master/examples/src/main/kotlin/com/example/CryptoPriceExample.kt) or [Java](https://github.com/ethauvin/cryptoprice/blob/master/examples/src/main/java/com/example/CryptoPriceSample.java) Examples.
### Market Price ### Spot Price
The `marketPrice` function defines the following parameters: The `spotPrice` function defines the following parameters:
```kotlin ```kotlin
marketPrice( spotPrice(
base: String, // Required base: String, // Required
currency: String = "USD", currency: String = "USD",
date: LocalDate? = null, date: LocalDate? = null,
@ -43,14 +43,14 @@ Parameters | Description
A `CryptoPrice` is returned defined as follows: A `CryptoPrice` is returned defined as follows:
```kotlin ```kotlin
CryptoPrice(val base: String, val currency: String, val amount: Double) CryptoPrice(val base: String, val currency: String, val amount: BigDecimal)
``` ```
The parameter names match the [Coinbase API](https://developers.coinbase.com/api/v2#get-spot-price). The parameter names match the [Coinbase API](https://developers.coinbase.com/api/v2#get-spot-price).
To display the amount as a fomatted currency use the `toCurrency` function: To display the amount as a fomatted currency use the `toCurrency` function:
```kotlin ```kotlin
val price = CryptoPrice("BTC", "EUR", 12345.67) val price = CryptoPrice("BTC", "EUR", 12345.67.toBigDecimal())
println(price.toCurrency()) // will print €12,345.67 println(price.toCurrency()) // will print €12,345.67
``` ```
### Extending ### Extending

View file

@ -18,7 +18,7 @@ plugins {
defaultTasks(ApplicationPlugin.TASK_RUN_NAME) defaultTasks(ApplicationPlugin.TASK_RUN_NAME)
description = "Retrieve cryptocurrencies current market prices." description = "Retrieve cryptocurrencies current prices."
group = "net.thauvin.erik" group = "net.thauvin.erik"
version = "0.9.0-SNAPSHOT" version = "0.9.0-SNAPSHOT"

View file

@ -10,18 +10,18 @@ import java.util.List;
public class CryptoPriceSample { public class CryptoPriceSample {
public static void main(String[] args) { public static void main(String[] args) {
try { try {
// Get current Bitcoin market price. // Get current Bitcoin spot price.
final CryptoPrice price = CryptoPrice.marketPrice("BTC"); final CryptoPrice price = CryptoPrice.spotPrice("BTC");
System.out.println("The current Bitcoin price is " + price.toCurrency()); System.out.println("The current Bitcoin price is " + price.toCurrency());
// Get current Bitcoin market price in Euros. // Get current Bitcoin spot price in Euros.
final CryptoPrice euroPrice = CryptoPrice.marketPrice("BTC", "EUR"); final CryptoPrice euroPrice = CryptoPrice.spotPrice("BTC", "EUR");
System.out.println("The current Bitcoin price is " + euroPrice.toCurrency()); System.out.println("The current Bitcoin price is " + euroPrice.toCurrency());
System.out.println(); System.out.println();
// Get current Ethereum market price in Pound sterling. // Get current Ethereum spot price in Pound sterling.
final CryptoPrice gbpPrice = CryptoPrice.marketPrice("ETH", "GBP"); final CryptoPrice gbpPrice = CryptoPrice.spotPrice("ETH", "GBP");
System.out.println("The current Ethereum price is " + gbpPrice.toCurrency()); System.out.println("The current Ethereum price is " + gbpPrice.toCurrency());
System.out.println(); System.out.println();

View file

@ -1,22 +1,22 @@
package com.example package com.example
import net.thauvin.erik.crypto.CryptoPrice.Companion.apiCall import net.thauvin.erik.crypto.CryptoPrice.Companion.apiCall
import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice
import net.thauvin.erik.crypto.CryptoPrice.Companion.toPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.toPrice
fun main(@Suppress("UNUSED_PARAMETER") args: Array<String>) { fun main(@Suppress("UNUSED_PARAMETER") args: Array<String>) {
// Get current Bitcoin market price. // Get current Bitcoin spot price.
val price = marketPrice("BTC") val price = spotPrice("BTC")
println("The current Bitcoin price is ${price.toCurrency()}") println("The current Bitcoin price is ${price.toCurrency()}")
// Get current Bitcoin market price in Euro. // Get current Bitcoin spot price in Euro.
val euroPrice = marketPrice("BTC", "EUR") val euroPrice = spotPrice("BTC", "EUR")
println("The current Bitcoin price is ${euroPrice.toCurrency()}") println("The current Bitcoin price is ${euroPrice.toCurrency()}")
println() println()
// Get current Ethereum market price in Pound sterling. // Get current Ethereum spot price in Pound sterling.
val gbpPrice = marketPrice("ETH", "GBP") val gbpPrice = spotPrice("ETH", "GBP")
println("The current Ehtereum price is ${gbpPrice.toCurrency()}") println("The current Ehtereum price is ${gbpPrice.toCurrency()}")
println() println()

View file

@ -10,7 +10,7 @@
<artifactId>cryptoprice</artifactId> <artifactId>cryptoprice</artifactId>
<version>0.9.0-SNAPSHOT</version> <version>0.9.0-SNAPSHOT</version>
<name>cryptoprice</name> <name>cryptoprice</name>
<description>Retrieve cryptocurrencies current market prices.</description> <description>Retrieve cryptocurrencies current prices.</description>
<url>https://github.com/ethauvin/cryptoprice</url> <url>https://github.com/ethauvin/cryptoprice</url>
<licenses> <licenses>
<license> <license>

View file

@ -38,17 +38,18 @@ import okhttp3.Request
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import java.io.IOException import java.io.IOException
import java.math.BigDecimal
import java.text.NumberFormat import java.text.NumberFormat
import java.time.LocalDate import java.time.LocalDate
import java.util.Currency import java.util.Currency
import java.util.Locale import java.util.Locale
/** /**
* A small Kotlin/Java library for retrieving cryptocurrencies current market prices. * A small Kotlin/Java library for retrieving cryptocurrencies current spot prices.
* *
* @author [Erik C. Thauvin](https://erik.thauvin.net/) * @author [Erik C. Thauvin](https://erik.thauvin.net/)
*/ */
open class CryptoPrice(val base: String, val currency: String, val amount: Double) { open class CryptoPrice(val base: String, val currency: String, val amount: BigDecimal) {
companion object { companion object {
// Coinbase API URL // Coinbase API URL
private const val COINBASE_API_URL = "https://api.coinbase.com/v2/" private const val COINBASE_API_URL = "https://api.coinbase.com/v2/"
@ -63,7 +64,7 @@ open class CryptoPrice(val base: String, val currency: String, val amount: Doubl
val json = JSONObject(this) val json = JSONObject(this)
if (json.has("data")) { if (json.has("data")) {
with(json.getJSONObject("data")) { with(json.getJSONObject("data")) {
return CryptoPrice(getString("base"), getString("currency"), getString("amount").toDouble()) return CryptoPrice(getString("base"), getString("currency"), getString("amount").toBigDecimal())
} }
} else { } else {
throw CryptoException(message = "Missing price data.") throw CryptoException(message = "Missing price data.")
@ -115,14 +116,14 @@ open class CryptoPrice(val base: String, val currency: String, val amount: Doubl
@JvmStatic @JvmStatic
fun main(args: Array<String>) { fun main(args: Array<String>) {
args.forEach { args.forEach {
with(marketPrice(it)) { with(spotPrice(it)) {
println("$base:\t" + "%10s".format(toCurrency())) println("$base:\t" + "%10s".format(toCurrency()))
} }
} }
} }
/** /**
* Retrieve the current market price. * Retrieve the current spot price.
* *
* @param base The cryptocurrency ticker symbol. (`BTC`, `ETH`, `ETH2`, etc.) * @param base The cryptocurrency ticker symbol. (`BTC`, `ETH`, `ETH2`, etc.)
* @param currency The fiat currency ISO 4217 code. (`USD`, `GPB`, `EUR`, etc.) * @param currency The fiat currency ISO 4217 code. (`USD`, `GPB`, `EUR`, etc.)
@ -131,7 +132,7 @@ open class CryptoPrice(val base: String, val currency: String, val amount: Doubl
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
@Throws(CryptoException::class, IOException::class) @Throws(CryptoException::class, IOException::class)
fun marketPrice(base: String, currency: String = "USD", date: LocalDate? = null): CryptoPrice { fun spotPrice(base: String, currency: String = "USD", date: LocalDate? = null): CryptoPrice {
val params = if (date != null) mapOf("date" to "$date") else emptyMap() val params = if (date != null) mapOf("date" to "$date") else emptyMap()
return apiCall(listOf("prices", "$base-$currency", "spot"), params).toPrice() return apiCall(listOf("prices", "$base-$currency", "spot"), params).toPrice()
} }
@ -145,6 +146,7 @@ open class CryptoPrice(val base: String, val currency: String, val amount: Doubl
fun toCurrency(locale: Locale = Locale.getDefault(Locale.Category.FORMAT)): String { fun toCurrency(locale: Locale = Locale.getDefault(Locale.Category.FORMAT)): String {
return NumberFormat.getCurrencyInstance(locale).let { return NumberFormat.getCurrencyInstance(locale).let {
it.setCurrency(Currency.getInstance(currency)) it.setCurrency(Currency.getInstance(currency))
it.setMinimumFractionDigits(2)
it.format(amount) it.format(amount)
} }
} }

View file

@ -1,8 +1,9 @@
package net.thauvin.erik.crypto package net.thauvin.erik.crypto
import net.thauvin.erik.crypto.CryptoPrice.Companion.apiCall import net.thauvin.erik.crypto.CryptoPrice.Companion.apiCall
import net.thauvin.erik.crypto.CryptoPrice.Companion.marketPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice
import net.thauvin.erik.crypto.CryptoPrice.Companion.toPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.toPrice
import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
import java.util.Locale import java.util.Locale
import kotlin.test.Test import kotlin.test.Test
@ -17,37 +18,37 @@ class CryptoPriceTest {
@Test @Test
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testBTCPrice() { fun testBTCPrice() {
val price = marketPrice("BTC") val price = spotPrice("BTC")
assertEquals("BTC", price.base, "BTC") assertEquals("BTC", price.base, "BTC")
assertEquals("USD", price.currency, "is USD") assertEquals("USD", price.currency, "is USD")
assertTrue(price.amount > 0.00, "BTC > 0") assertTrue(price.amount.signum() > 0, "BTC > 0")
} }
@Test @Test
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testETHPrice() { fun testETHPrice() {
val price = marketPrice("ETH", "EUR") val price = spotPrice("ETH", "EUR")
assertEquals("ETH", price.base, "ETH") assertEquals("ETH", price.base, "ETH")
assertEquals("EUR", price.currency, "is EUR") assertEquals("EUR", price.currency, "is EUR")
assertTrue(price.amount > 0.00, "ETH > 0") assertTrue(price.amount.signum() > 0, "ETH > 0")
} }
@Test @Test
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testETH2Price() { fun testETH2Price() {
val price = marketPrice("ETH2", "GBP") val price = spotPrice("ETH2", "GBP")
assertEquals("ETH2", price.base, "ETH2") assertEquals("ETH2", price.base, "ETH2")
assertEquals("GBP", price.currency, "is GBP") assertEquals("GBP", price.currency, "is GBP")
assertTrue(price.amount > 0.00, "GBP > 0") assertTrue(price.amount.signum() > 0, "GBP > 0")
} }
@Test @Test
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testBCHPrice() { fun testBCHPrice() {
val price = marketPrice("BCH", "GBP", LocalDate.now().minusDays(1)) val price = spotPrice("BCH", "GBP", LocalDate.now().minusDays(1))
assertEquals("BCH", price.base, "BCH") assertEquals("BCH", price.base, "BCH")
assertEquals("GBP", price.currency, "is GBP") assertEquals("GBP", price.currency, "is GBP")
assertTrue(price.amount > 0.00, "BCH > 0") assertTrue(price.amount.signum() > 0, "BCH > 0")
} }
@Test @Test
@ -56,26 +57,26 @@ class CryptoPriceTest {
val price = apiCall(listOf("prices", "BTC-USD", "buy"), emptyMap()).toPrice() val price = apiCall(listOf("prices", "BTC-USD", "buy"), emptyMap()).toPrice()
assertEquals("BTC", price.base, "buy BTC") assertEquals("BTC", price.base, "buy BTC")
assertEquals("USD", price.currency, "buy BTC is USD") assertEquals("USD", price.currency, "buy BTC is USD")
assertTrue(price.amount > 0.00, "buy BTC > 0") assertTrue(price.amount.signum() > 0, "buy BTC > 0")
} }
@Test @Test
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testMarketPriceExceptions() { fun testSpotPrice() {
assertFailsWith( assertFailsWith(
message = "FOO did not fail", message = "FOO did not fail",
exceptionClass = CryptoException::class, exceptionClass = CryptoException::class,
block = { marketPrice("FOO") } block = { spotPrice("FOO") }
) )
assertFailsWith( assertFailsWith(
message = "BAR did not fail", message = "BAR did not fail",
exceptionClass = CryptoException::class, exceptionClass = CryptoException::class,
block = { marketPrice("BTC", "BAR") } block = { spotPrice("BTC", "BAR") }
) )
try { try {
marketPrice("FOOBAR") spotPrice("FOOBAR")
} catch (e: CryptoException) { } catch (e: CryptoException) {
assertTrue(e.statusCode != 400, "FOOBAR status code is not 400") assertTrue(e.statusCode != 400, "FOOBAR status code is not 400")
} }
@ -84,7 +85,7 @@ class CryptoPriceTest {
@Test @Test
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun testToCurrency() { fun testToCurrency() {
val d = 12345.67 val d = 12345.67.toBigDecimal()
val usd = CryptoPrice("BTC", "USD", d) val usd = CryptoPrice("BTC", "USD", d)
assertEquals("$12,345.67", usd.toCurrency(), "EUR format") assertEquals("$12,345.67", usd.toCurrency(), "EUR format")
@ -101,18 +102,18 @@ class CryptoPriceTest {
assertEquals("12345,67 ", fr.toCurrency(Locale.FRANCE), "EUR-FR format") assertEquals("12345,67 ", fr.toCurrency(Locale.FRANCE), "EUR-FR format")
val jp = CryptoPrice("BTC", "JPY", d) val jp = CryptoPrice("BTC", "JPY", d)
assertEquals("¥12,346", jp.toCurrency(Locale.JAPAN), "EUR-JPY format") assertEquals("¥12,345.67", jp.toCurrency(Locale.JAPAN), "EUR-JPY format")
} }
@Test @Test
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testToPrice() { fun testToPrice() {
val d = 57515.69 val d = "57515.69"
val json = "{\"data\":{\"base\":\"BTC\",\"currency\":\"USD\",\"amount\":\"$d\"}}" val json = "{\"data\":{\"base\":\"BTC\",\"currency\":\"USD\",\"amount\":\"$d\"}}"
val price = json.toPrice() val price = json.toPrice()
assertEquals("BTC", price.base, "base is BTC") assertEquals("BTC", price.base, "base is BTC")
assertEquals("USD", price.currency, "currency is USD") assertEquals("USD", price.currency, "currency is USD")
assertEquals(d, price.amount, "amount is 57515.69") assertEquals(d, price.amount.toString(), "amount is 57515.69")
assertFailsWith( assertFailsWith(
message = "double conversion did not fail", message = "double conversion did not fail",