Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 93 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,107 @@

[![Maven Central](https://img.shields.io/maven-central/v/dev.andstuff.kraken/kraken-api)](https://central.sonatype.com/artifact/dev.andstuff.kraken/kraken-api)

Query the [Kraken API][1] in Java.
Query the [Kraken REST API][1] in Java.

### Examples
## Maven

Run the examples with:
```xml
<dependency>
<groupId>dev.andstuff.kraken</groupId>
<artifactId>kraken-api</artifactId>
<version>1.0.0</version>
</dependency>
```

## Usage

> Note: the following code examples are for the current state of the repository and not for the v1.0.0. See [here][3] for v1.0.0 code examples.

### Public endpoints

Public endpoints that have been implemented by the library, can be queried in the following way:

```java
KrakenAPI api = new KrakenAPI();

Map<String, AssetInfo> assets = api.assetInfo(List.of("BTC", "ETH"));
// {BTC=AssetInfo[assetClass=currency, alternateName=XBT, maxDecimals=10, …

Map<String, AssetPair> pairs = api.assetPairs(List.of("ETH/BTC", "ETH/USD"));
// {ETH/BTC=AssetPair[alternateName=ETHXBT, webSocketName=ETH/XBT, …
```

If the endpoint has not yet been implemented (feel free to submit a PR!), the generic `query` method can be used, which will return a `JsonNode` of the [Jackson][2] deserialization libary:

```java
JsonNode ticker = api.query(KrakenAPI.Public.TICKER, Map.of("pair", "XBTEUR"));
// {"XXBTZEUR":{"a":["62650.00000","1","1.000"],"b":["62649.90000","6","6.000"], …
```

It's also possible to specify the path directly, in case a new endpoint has been added by Kraken and not yet added in the library:

```java
JsonNode trades = api.queryPublic("Trades", Map.of("pair", "XBTUSD", "count", "1"));
// {"XXBTZUSD":[["68515.60000","0.00029628",1.7100231295628998E9,"s","m","",68007835]], …
```

### Private endpoints

Private endpoints can be queried in the same way as the public ones, but an API key and secret must be provided to the `KrakenAPI` instance:

```java
KrakenAPI api = new KrakenAPI("my key", "my secret");

JsonNode balance = api.query(KrakenAPI.Private.BALANCE);
// {"XXBT":"1234.8396278900", … :)
```

If the Kraken API call returns an error, an unchecked exception of type `KrakenException` is thrown:

```java
// API key doesn't have the permission to create orders
JsonNode order = api.query(KrakenAPI.Private.ADD_ORDER, Map.of(
"ordertype", "limit", "type", "sell",
"volume", "1", "pair", "XLTCZUSD",
"price", "1000", "oflags", "post,fciq",
"validate", "true"));
// Exception in thread "main" KrakenException(errors=[EGeneral:Permission denied])
```

### Custom REST requester

The current implementation of the library uses the JDK's HttpsURLConnection to make HTTP request. If that doesn't suit your needs and which to use something else (e.g. Spring RestTemplate, Apache HttpComponents, OkHttp), you can implement the KrakenRestRequester interface and pass it to the KrakenAPI constructor:

```java
public class MyRestTemplateRestRequester implements KrakenRestRequester {
public <T> T execute(PublicEndpoint<T> endpoint) { /* your implementation */ }
public <T> T execute(PrivateEndpoint<T> endpoint) { /* your implementation */ }
}

KrakenAPI api = new KrakenAPI(MyRestTemplateRestRequest(apiKey, apiSecret));
```

See `DefaultKrakenRestRequester` for the default implementation.

### Custom nonce generator (not yet implemented)



## Examples

The `examples` Maven module contains some examples that might be worth checking (e.g. total staking rewards summary). The examples can be run directly from your IDE, or from the command line.

For private endpoints, you need to rename `api-keys.properties.example` (located in `examples/src/main/resources/`) to `api-keys.properties` and fill in your API keys.

```shell
# input your API keys in api-keys.properties
cp examples/src/main/resources/api-keys.properties.example \
examples/src/main/resources/api-keys.properties

# build project
mvn clean install

# run Examples class
# run example classes
mvn -q -pl examples exec:java -Dexec.mainClass=dev.andstuff.kraken.example.Examples
mvn -q -pl examples exec:java -Dexec.mainClass=dev.andstuff.kraken.example.TotalRewards
```

[1]: https://docs.kraken.com/rest/
[2]: https://github.com/FasterXML/jackson
[3]: https://github.com/nyg/kraken-api-java/blob/v1.0.0/examples/src/main/java/dev/andstuff/kraken/example/Examples.java
97 changes: 56 additions & 41 deletions examples/src/main/java/dev/andstuff/kraken/example/Examples.java
Original file line number Diff line number Diff line change
@@ -1,54 +1,69 @@
package dev.andstuff.kraken.example;

import static dev.andstuff.kraken.example.ExampleHelper.readPropertiesFromFile;
import static dev.andstuff.kraken.example.PropertiesHelper.readFromFile;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import com.fasterxml.jackson.databind.JsonNode;

import dev.andstuff.kraken.api.KrakenApi;
import dev.andstuff.kraken.api.KrakenAPI;
import dev.andstuff.kraken.api.model.endpoint.market.response.AssetInfo;
import dev.andstuff.kraken.api.model.endpoint.market.response.AssetPair;
import dev.andstuff.kraken.api.model.endpoint.market.response.ServerTime;
import dev.andstuff.kraken.api.model.endpoint.market.response.SystemStatus;

public class Examples {

public static void main(String[] args) throws IOException, InvalidKeyException, NoSuchAlgorithmException {

Properties apiKeys = readPropertiesFromFile("/api-keys.properties");

KrakenApi api = new KrakenApi();
api.setKey(apiKeys.getProperty("key"));
api.setSecret(apiKeys.getProperty("secret"));

JsonNode response;
Map<String, String> input = new HashMap<>();

input.put("pair", "XBTEUR");
response = api.queryPublic(KrakenApi.Method.TICKER, input);
System.out.println(response);

input.clear();
input.put("pair", "XBTUSD,XLTCZUSD");
response = api.queryPublic(KrakenApi.Method.ASSET_PAIRS, input);
System.out.println(response);

input.clear();
input.put("asset", "ZEUR");
response = api.queryPrivate(KrakenApi.Method.BALANCE, input);
System.out.println(response);

input.clear();
input.put("ordertype", "limit");
input.put("type", "sell");
input.put("volume", "1");
input.put("pair", "XLTCZUSD");
input.put("price", "1000");
input.put("oflags", "post,fciq");
input.put("validate", "true");
response = api.queryPrivate(KrakenApi.Method.ADD_ORDER, input);
System.out.println(response);
public static void main(String[] args) {


/* Public endpoint examples */

KrakenAPI publicAPI = new KrakenAPI();

ServerTime serverTime = publicAPI.serverTime();
System.out.println(serverTime);

SystemStatus systemStatus = publicAPI.systemStatus();
System.out.println(systemStatus);

Map<String, AssetInfo> assets1 = publicAPI.assetInfo(List.of("BTC", "ETH"));
System.out.println(assets1);

Map<String, AssetInfo> assets2 = publicAPI.assetInfo(List.of("DOT", "ADA"), "currency");
System.out.println(assets2);

Map<String, AssetPair> pairs1 = publicAPI.assetPairs(List.of("ETH/BTC", "ETH/USD"));
System.out.println(pairs1);

Map<String, AssetPair> pairs2 = publicAPI.assetPairs(List.of("DOT/USD", "ADA/USD"), AssetPair.Info.MARGIN);
System.out.println(pairs2);

JsonNode ticker = publicAPI.query(KrakenAPI.Public.TICKER, Map.of("pair", "XBTEUR"));
System.out.println(ticker);

JsonNode trades = publicAPI.queryPublic("Trades", Map.of("pair", "XBTUSD", "count", "1"));
System.out.println(trades);

/* Private endpoint example */

Properties apiKeys = readFromFile("/api-keys.properties");

KrakenAPI api = new KrakenAPI(apiKeys.getProperty("key"), apiKeys.getProperty("secret"));

JsonNode balance = api.query(KrakenAPI.Private.BALANCE);
System.out.println(balance);

JsonNode order = api.query(KrakenAPI.Private.ADD_ORDER, Map.of(
"ordertype", "limit",
"type", "sell",
"volume", "1",
"pair", "XLTCZUSD",
"price", "1000",
"oflags", "post,fciq",
"validate", "true"));
System.out.println(order);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dev.andstuff.kraken.example;

import java.util.Optional;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

public class JacksonTest {

private static final ObjectMapper OBJECT_MAPPER;

static {
OBJECT_MAPPER = JsonMapper.builder()
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.addModules(new JavaTimeModule(), new Jdk8Module())
.build();
}

public static void main(String[] args) throws JsonProcessingException {
MyRecord myRecord = OBJECT_MAPPER.readValue("{}", MyRecord.class);
System.out.println(myRecord);
System.out.println(myRecord.result().isEmpty());
}

public record MyRecord(Optional<MyClass> result) {}

public class MyClass {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ExampleHelper {
public final class PropertiesHelper {

public static Properties readPropertiesFromFile(String path) {
public static Properties readFromFile(String path) {
try {
InputStream stream = Examples.class.getResourceAsStream(path);
Properties properties = new Properties();
properties.load(stream);
return properties;
}
catch (IOException e) {
throw new RuntimeException(String.format("Could not read properties file: %s", path));
throw new RuntimeException(String.format("Could not read properties from file: %s", path));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.andstuff.kraken.example;

import static dev.andstuff.kraken.example.ExampleHelper.readPropertiesFromFile;
import static dev.andstuff.kraken.example.PropertiesHelper.readFromFile;
import static java.util.Arrays.asList;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.groupingBy;
Expand All @@ -21,7 +21,7 @@

import com.fasterxml.jackson.databind.JsonNode;

import dev.andstuff.kraken.api.KrakenApi;
import dev.andstuff.kraken.api.KrakenAPI;

/**
* TODO Group by year
Expand All @@ -30,23 +30,20 @@ public class TotalRewards {

public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InterruptedException {

Properties apiKeys = readPropertiesFromFile("/api-keys.properties");
Properties apiKeys = readFromFile("/api-keys.properties");
KrakenAPI api = new KrakenAPI(apiKeys.getProperty("key"), apiKeys.getProperty("secret"));

KrakenApi api = new KrakenApi();
api.setKey(apiKeys.getProperty("key"));
api.setSecret(apiKeys.getProperty("secret"));

Map<String, String> params = new HashMap<>();
params.put("type", "staking");
params.put("without_count", "true");
params.put("ofs", "0");
Map<String, String> params = Map.of(
"type", "staking",
"without_count", "true",
"ofs", "0");

Map<String, JsonNode> rewards = new HashMap<>();

boolean hasNext = true;
while (hasNext) {

JsonNode response = api.queryPrivate(KrakenApi.Method.LEDGERS, params);
JsonNode response = api.query(KrakenAPI.Private.LEDGERS, params);
params.put("ofs", String.valueOf(Integer.parseInt(params.get("ofs")) + 50));
System.out.printf("Fetched %s rewards%n", params.get("ofs"));

Expand Down Expand Up @@ -107,12 +104,12 @@ public static void main(String[] args) throws IOException, NoSuchAlgorithmExcept
System.out.printf("Total USD: %s%n", totalRewardAmountUsd);
}

private static BigDecimal fetchRate(String asset, KrakenApi api) {
private static BigDecimal fetchRate(String asset, KrakenAPI api) {
try {
Map<String, String> tickerParams = new HashMap<>();
tickerParams.put("pair", asset + "USD");

JsonNode tickerResponse = api.queryPublic(KrakenApi.Method.TICKER, tickerParams).findValue("result");
JsonNode tickerResponse = api.query(KrakenAPI.Public.TICKER, tickerParams).findValue("result");
return new BigDecimal(tickerResponse.findValue(tickerResponse.fieldNames().next()).findValue("c").get(0).textValue());
}
catch (Exception e) {
Expand Down
Loading