Skip to content

Commit 34915c4

Browse files
committed
feat!: implement Ticker, Ledgers and QueryLedgers endpoints (#12)
1 parent 2d7e766 commit 34915c4

36 files changed

+858
-196
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ target
33
*.iml
44
.idea
55
api-keys.properties
6+
*.txt
7+
*.csv

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,6 @@ See `DefaultKrakenRestRequester` for the default implementation.
8686

8787
### Custom nonce generator (not yet implemented)
8888

89-
90-
9189
## Examples
9290

9391
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.
@@ -99,10 +97,12 @@ For private endpoints, you need to rename `api-keys.properties.example` (located
9997
mvn clean install
10098

10199
# run example classes
102-
mvn -q -pl examples exec:java -Dexec.mainClass=dev.andstuff.kraken.example.Examples
100+
mvn -q -pl examples exec:java -Dexec.mainClass=dev.andstuff.kraken.example.SimpleExamples
103101
mvn -q -pl examples exec:java -Dexec.mainClass=dev.andstuff.kraken.example.TotalRewards
104102
```
105103

106104
[1]: https://docs.kraken.com/rest/
105+
107106
[2]: https://github.com/FasterXML/jackson
107+
108108
[3]: https://github.com/nyg/kraken-api-java/blob/v1.0.0/examples/src/main/java/dev/andstuff/kraken/example/Examples.java

examples/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
<properties>
1515
<log4j.version>2.23.0</log4j.version>
16+
<opencsv.version>5.9</opencsv.version>
1617
</properties>
1718

1819
<dependencies>
@@ -21,6 +22,8 @@
2122
<artifactId>kraken-api</artifactId>
2223
<version>${project.version}</version>
2324
</dependency>
25+
26+
<!-- Logging -->
2427
<dependency>
2528
<groupId>org.apache.logging.log4j</groupId>
2629
<artifactId>log4j-core</artifactId>
@@ -33,6 +36,13 @@
3336
<version>${log4j.version}</version>
3437
<scope>runtime</scope>
3538
</dependency>
39+
40+
<!-- Third-party -->
41+
<dependency>
42+
<groupId>com.opencsv</groupId>
43+
<artifactId>opencsv</artifactId>
44+
<version>${opencsv.version}</version>
45+
</dependency>
3646
</dependencies>
3747

3848
<build>

examples/src/main/java/dev/andstuff/kraken/example/Examples.java renamed to examples/src/main/java/dev/andstuff/kraken/example/SimpleExamples.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
package dev.andstuff.kraken.example;
22

3-
import static dev.andstuff.kraken.example.PropertiesHelper.readFromFile;
3+
import static dev.andstuff.kraken.example.helper.CredentialsHelper.readFromFile;
44

55
import java.util.List;
66
import java.util.Map;
7-
import java.util.Properties;
87

98
import com.fasterxml.jackson.databind.JsonNode;
109

1110
import dev.andstuff.kraken.api.KrakenAPI;
11+
import dev.andstuff.kraken.api.model.KrakenCredentials;
12+
import dev.andstuff.kraken.api.model.endpoint.market.params.AssetPairParams;
1213
import dev.andstuff.kraken.api.model.endpoint.market.response.AssetInfo;
1314
import dev.andstuff.kraken.api.model.endpoint.market.response.AssetPair;
1415
import dev.andstuff.kraken.api.model.endpoint.market.response.ServerTime;
1516
import dev.andstuff.kraken.api.model.endpoint.market.response.SystemStatus;
1617
import lombok.extern.slf4j.Slf4j;
1718

1819
@Slf4j
19-
public class Examples {
20+
public class SimpleExamples {
2021

2122
public static void main(String[] args) {
2223

@@ -39,7 +40,7 @@ public static void main(String[] args) {
3940
Map<String, AssetPair> pairs1 = publicAPI.assetPairs(List.of("ETH/BTC", "ETH/USD"));
4041
log.info("{}", pairs1);
4142

42-
Map<String, AssetPair> pairs2 = publicAPI.assetPairs(List.of("DOT/USD", "ADA/USD"), AssetPair.Info.MARGIN);
43+
Map<String, AssetPair> pairs2 = publicAPI.assetPairs(List.of("DOT/USD", "ADA/USD"), AssetPairParams.Info.MARGIN);
4344
log.info("{}", pairs2);
4445

4546
JsonNode ticker = publicAPI.query(KrakenAPI.Public.TICKER, Map.of("pair", "XBTEUR"));
@@ -50,8 +51,8 @@ public static void main(String[] args) {
5051

5152
/* Private endpoint example */
5253

53-
Properties apiKeys = readFromFile("/api-keys.properties");
54-
KrakenAPI api = new KrakenAPI(apiKeys.getProperty("key"), apiKeys.getProperty("secret"));
54+
KrakenCredentials credentials = readFromFile("/api-keys.properties");
55+
KrakenAPI api = new KrakenAPI(credentials);
5556

5657
JsonNode balance = api.query(KrakenAPI.Private.BALANCE);
5758
log.info("{}", balance);
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package dev.andstuff.kraken.example;
2+
3+
import static dev.andstuff.kraken.example.helper.CredentialsHelper.readFromFile;
4+
import static java.util.function.Predicate.not;
5+
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.Set;
9+
10+
import dev.andstuff.kraken.api.KrakenAPI;
11+
import dev.andstuff.kraken.api.model.KrakenCredentials;
12+
import dev.andstuff.kraken.api.model.KrakenException;
13+
import dev.andstuff.kraken.api.model.endpoint.account.params.LedgerInfoParams;
14+
import dev.andstuff.kraken.api.model.endpoint.account.response.LedgerEntry;
15+
import dev.andstuff.kraken.api.model.endpoint.account.response.LedgerInfo;
16+
import dev.andstuff.kraken.example.reward.AssetRates;
17+
import dev.andstuff.kraken.example.reward.StakingRewards;
18+
import dev.andstuff.kraken.example.reward.csv.CsvLedgerEntries;
19+
import dev.andstuff.kraken.example.reward.csv.CsvStakingRewardsSummary;
20+
import lombok.RequiredArgsConstructor;
21+
import lombok.extern.slf4j.Slf4j;
22+
23+
/**
24+
* Generates a CSV file containing all the ledger entries corresponding to
25+
* staking rewards, and another CSV file containing the summary of staking
26+
* rewards earned each year for each asset.
27+
*/
28+
@Slf4j
29+
@RequiredArgsConstructor
30+
public class StakingRewardsSummaryExample {
31+
32+
private static final int SLEEP_BETWEEN_API_CALLS = 2000;
33+
34+
private final KrakenAPI api;
35+
36+
public static void main(String[] args) {
37+
KrakenCredentials credentials = readFromFile("/api-keys.properties");
38+
new StakingRewardsSummaryExample(new KrakenAPI(credentials))
39+
.generate("rewards.csv", "rewards-summary.csv");
40+
}
41+
42+
public void generate(String rewardsFileName, String rewardSummaryFileName) {
43+
List<LedgerEntry> rewards = fetchStakingRewards();
44+
StakingRewards stakingRewards = new StakingRewards(rewards);
45+
AssetRates rates = fetchRatesFor(stakingRewards.getAssets());
46+
47+
new CsvLedgerEntries(rewards).writeToFile(rewardsFileName);
48+
new CsvStakingRewardsSummary(stakingRewards, rates).writeToFile(rewardSummaryFileName);
49+
}
50+
51+
private List<LedgerEntry> fetchStakingRewards() {
52+
53+
List<LedgerEntry> rewards = new ArrayList<>();
54+
LedgerInfoParams params = LedgerInfoParams.builder()
55+
.assetType(LedgerInfoParams.Type.STAKING)
56+
.withoutCount(true)
57+
.build();
58+
59+
boolean hasNext = true;
60+
while (hasNext) {
61+
LedgerInfo ledgerInfo = api.ledgerInfo(params);
62+
params = params.withNextResultOffset();
63+
hasNext = ledgerInfo.hasNext();
64+
65+
rewards.addAll(ledgerInfo.stakingRewards());
66+
log.info("Fetched {} staking rewards", rewards.size());
67+
68+
try {
69+
Thread.sleep(SLEEP_BETWEEN_API_CALLS);
70+
}
71+
catch (InterruptedException e) {
72+
log.warn("Thread was interrupted");
73+
Thread.currentThread().interrupt();
74+
}
75+
}
76+
77+
return rewards;
78+
}
79+
80+
private AssetRates fetchRatesFor(Set<String> assets) {
81+
try {
82+
List<String> pairs = assets.stream()
83+
.map(asset -> asset + AssetRates.REFERENCE_ASSET)
84+
.filter(not(AssetRates.REFERENCE_PAIR::equals))
85+
.toList();
86+
return new AssetRates(api.ticker(pairs));
87+
}
88+
catch (KrakenException e) {
89+
throw new RuntimeException("Couldn't fetch rates", e);
90+
}
91+
}
92+
}

examples/src/main/java/dev/andstuff/kraken/example/TotalRewards.java

Lines changed: 0 additions & 117 deletions
This file was deleted.

examples/src/main/java/dev/andstuff/kraken/example/PropertiesHelper.java renamed to examples/src/main/java/dev/andstuff/kraken/example/helper/CredentialsHelper.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
package dev.andstuff.kraken.example;
1+
package dev.andstuff.kraken.example.helper;
22

33
import java.io.IOException;
44
import java.io.InputStream;
55
import java.util.Properties;
66

7+
import dev.andstuff.kraken.api.model.KrakenCredentials;
78
import lombok.AccessLevel;
89
import lombok.NoArgsConstructor;
910

1011
@NoArgsConstructor(access = AccessLevel.PRIVATE)
11-
public final class PropertiesHelper {
12+
public final class CredentialsHelper {
1213

13-
public static Properties readFromFile(String path) {
14+
public static KrakenCredentials readFromFile(String path) {
1415
try {
15-
InputStream stream = Examples.class.getResourceAsStream(path);
16+
InputStream stream = CredentialsHelper.class.getResourceAsStream(path);
1617
Properties properties = new Properties();
1718
properties.load(stream);
18-
return properties;
19+
return new KrakenCredentials(properties.getProperty("key"), properties.getProperty("secret"));
1920
}
2021
catch (IOException e) {
2122
throw new RuntimeException(String.format("Could not read properties from file: %s", path));
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package dev.andstuff.kraken.example.helper;
2+
3+
import com.opencsv.bean.ColumnPositionMappingStrategy;
4+
import com.opencsv.bean.CsvBindByName;
5+
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
6+
7+
public class HeaderAndPositionMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
8+
9+
@Override
10+
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
11+
super.generateHeader(bean);
12+
13+
int fieldCount = getFieldMap().values().size();
14+
String[] header = new String[fieldCount];
15+
16+
for (int i = 0; i < fieldCount; i++) {
17+
header[i] = findField(i).getField().getDeclaredAnnotation(CsvBindByName.class).column();
18+
}
19+
20+
return header;
21+
}
22+
}

0 commit comments

Comments
 (0)