Skip to content

Commit

Permalink
Added multiple pairs support. Added unit tests for KrakenDCA object a…
Browse files Browse the repository at this point in the history
…nd updated other classes tests. Configuration file example and README updated.
  • Loading branch information
adocquin committed Sep 12, 2021
1 parent b8f7c77 commit ca0e870
Show file tree
Hide file tree
Showing 12 changed files with 997 additions and 65 deletions.
39 changes: 24 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Kraken-DCA
Kraken-DCA is a python program to automate
[Dollar Cost Averaging](https://www.investopedia.com/terms/d/dollarcostaveraging.asp) on
[Kraken](https://kraken.com) exchange.<br>
At every launch, if no DCA pair order was already passed for the pair and delay in configuration
file, it will create a buy limit order at current pair ask price for the specified amount.
Kraken-DCA is a python program to automate pairs
[Dollar Cost Averaging](https://www.investopedia.com/terms/d/dollarcostaveraging.asp)
on as many pairs as you want on [Kraken](https://kraken.com) exchange.<br>
At every launch, if no DCA pair order was already passed for each pair and delay in
configuration file, it will create a buy limit order at current pair ask price for the specified amount.

Order history is saved in CSV format

Expand Down Expand Up @@ -61,7 +61,7 @@ Order history is saved in CSV format with following information per order:
Order history is by default saved in *orders.csv* in Kraken-DCA base directory,
the output file can be changed through docker image execution as described below.

# Usage
# How to run it
## Configuration file
If you don't use docker you must edit the default *config.yaml* file.

Expand All @@ -71,15 +71,23 @@ api:
public_key: "KRAKEN_API_PUBLIC_KEY"
private_key: "KRAKEN_API_PRIVATE_KEY"

# DCA days delay, pair to DCA and corresponding amount to buy.
dca:
delay: 2
pair: "XETHZEUR"
amount: 20
# DCA pairs configuration. You can add as many pairs as you want.
# pair: Name of the pair (list of available pairs: https://api.kraken.com/0/public/AssetPairs)
# delay: Delay in days between each buy limit order.
# amount: Amount of the order in quote asset.
dca_pairs:
- pair: "XETHZEUR"
delay: 1
amount: 15
- pair: "XXBTZEUR"
delay: 3
amount: 20
```
- In api, public_key and private_key correspond to your Kraken API key information.
- Delay is the number of days between buy orders. Set to 1 to DCA each day, 7 once per week.
- Available pairs for pair field can be found [here](https://api.kraken.com/0/public/AssetPairs) on *altname*.
- Amount of quote asset to sell to buy base asset.
- Amount is the amount of quote asset to sell to buy base asset.
- You can specify as many pairs as you want in the dca_pairs list.
More information on
[Kraken API official documentation](https://support.kraken.com/hc/en-us/articles/360000920306-Ticker-pairs).
Expand Down Expand Up @@ -151,9 +159,10 @@ More crontab execution frequency options: https://crontab.guru/
[GPL-3.0](https://github.com/FuturBroke/kraken-dca/blob/main/README.md)

# How to contribute
Thanks for your interest in contributing to the project. You can contribute freely by creating an issue, fork or create
a pull request. Before issuing a pull request, make sure the changes did not break any existing functionality by
running unit tests in the base directory:
Thanks for your interest in contributing to the project. You can contribute freely by
creating an issue, fork or create a pull request. Before issuing a pull request, make
sure the changes did not break any existing functionality by running unit tests in the
base directory:
```sh
pytest
```
4 changes: 2 additions & 2 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ api:
dca_pairs:
- pair: "XETHZEUR"
delay: 1
amount: 20
- pair: "XBTUSDT"
amount: 15
- pair: "XXBTZEUR"
delay: 3
amount: 20

36 changes: 22 additions & 14 deletions krakendca/config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import yaml
from yaml.scanner import ScannerError

CONFIG_ERROR_MSG: str = "Configuration file incorrectly formatted"


class Config:
Expand Down Expand Up @@ -27,6 +30,8 @@ def __init__(self, config_file: str) -> None:
self.__check_dca_pair_configuration(dca_pair)
except EnvironmentError:
raise FileNotFoundError("Configuration file not found.")
except ScannerError as e:
raise ScannerError(CONFIG_ERROR_MSG + f": {e}")

def __check_configuration(self):
"""
Expand All @@ -38,10 +43,10 @@ def __check_configuration(self):
raise ValueError("Please provide your Kraken API public key.")
if not self.api_private_key:
raise ValueError("Please provide your Kraken API private key.")
if not self.dca_pairs:
if not self.dca_pairs or type(self.dca_pairs) is not list:
raise ValueError("No DCA pairs specified.")
except (ValueError, AttributeError, yaml.YAMLError) as e:
raise ValueError(f"Configuration file incorrectly formatted: {e}")
except ValueError as e:
raise ValueError(CONFIG_ERROR_MSG + f": {e}")

@staticmethod
def __check_dca_pair_configuration(dca_pair: dict):
Expand All @@ -51,15 +56,18 @@ def __check_dca_pair_configuration(dca_pair: dict):
:param dca_pair: Dictionary with pair to DCA, delay in days as integer and
amount of quote asset to make the limit buy order with.
"""
if not dca_pair.get("pair"):
raise ValueError("Please provide the pair to dollar cost average.")
delay = dca_pair.get("delay")
if not delay or type(delay) is not int or delay <= 0:
raise ValueError("Please set the DCA days delay as a number > 0.")
try:
dca_pair["amount"] = float(dca_pair.get("amount"))
except ValueError:
print("Please provide an amount > 0 to DCA.")
amount = dca_pair.get("amount")
if not amount or type(amount) is not float or amount <= 0:
raise ValueError("Please provide an amount > 0 to DCA.")
if not dca_pair.get("pair"):
raise ValueError("Please provide the pair to dollar cost average.")
delay = dca_pair.get("delay")
if not delay or type(delay) is not int or delay <= 0:
raise ValueError("Please set the DCA days delay as a number > 0.")
try:
dca_pair["amount"] = float(dca_pair.get("amount"))
except TypeError:
raise ValueError("Please provide an amount > 0 to DCA.")
amount = dca_pair.get("amount")
if not amount or type(amount) is not float or amount <= 0:
raise ValueError("Please provide an amount > 0 to DCA.")
except ValueError as e:
raise ValueError(CONFIG_ERROR_MSG + f": {e}")
3 changes: 2 additions & 1 deletion krakendca/krakendca.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ def initialize_pairs_dca(self):
and data from Kraken.
"""
print("Hi, current DCA configuration:")
asset_pairs = self.ka.get_asset_pairs()
for dca_pair in self.config.dca_pairs:
pair = Pair.get_pair_from_kraken(self.ka, dca_pair.get("pair"))
pair = Pair.get_pair_from_kraken(self.ka, asset_pairs, dca_pair.get("pair"))
self.dcas_list.append(DCA(
self.ka, dca_pair.get("delay"), pair, dca_pair.get("amount")
))
Expand Down
10 changes: 5 additions & 5 deletions krakendca/pair.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,16 @@ def __init__(
self.order_min = order_min

@classmethod
def get_pair_from_kraken(cls, ka: KrakenApi, pair: str) -> T:
def get_pair_from_kraken(cls, ka: KrakenApi, asset_pairs: dict, pair: str) -> T:
"""
Initialize the Pair object using KrakenAPI and provided pair.
:param ka: KrakenApi object.
:param asset_pairs: Dictionary of available pairs on Kraken got through the API.
:param pair: Pair to dollar cost average as string.
:return: Instanced Pair object.
"""
pair_information = cls.get_pair_information(ka, pair)
pair_information = cls.get_pair_information(asset_pairs, pair)
alt_name = pair_information.get("altname")
base = pair_information.get("base")
quote = pair_information.get("quote")
Expand All @@ -81,15 +82,14 @@ def get_pair_from_kraken(cls, ka: KrakenApi, pair: str) -> T:
)

@staticmethod
def get_pair_information(ka: KrakenApi, pair: str) -> dict:
def get_pair_information(asset_pairs: dict, pair: str) -> dict:
"""
Return pair information from Kraken API.
:param ka: KrakenAPI object.
:param asset_pairs: Dictionary of available pairs on Kraken got through the API.
:param pair: Pair to find.
:return: Dict of pair information.
"""
asset_pairs = ka.get_asset_pairs()
pair_information = find_nested_dictionary(asset_pairs, pair)
if not pair_information:
available_pairs = [pair for pair in asset_pairs]
Expand Down
4 changes: 2 additions & 2 deletions tests/fixtures/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ api:
dca_pairs:
- pair: "XETHZEUR"
delay: 1
amount: 20
- pair: "XBTUSDT"
amount: 15
- pair: "XXBTZEUR"
delay: 3
amount: 20

Loading

0 comments on commit ca0e870

Please sign in to comment.