Skip to content

Commit

Permalink
logging instead of print (#37) (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
joergrs authored Jan 6, 2023
1 parent b6e50ab commit dbda81b
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 46 deletions.
5 changes: 5 additions & 0 deletions __main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import os

from krakenapi import KrakenApi
Expand All @@ -6,6 +7,10 @@
from krakendca.krakendca import KrakenDCA

if __name__ == "__main__":
logging.basicConfig(
format="%(asctime)s - %(levelname)s:%(name)s: %(message)s",
level=logging.INFO,
)
# Get parameters from configuration file.
current_directory: str = os.path.dirname(os.path.realpath(__file__))
config_file: str = current_directory + "/config.yaml"
Expand Down
27 changes: 27 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import logging

import pytest


@pytest.fixture()
def logging_capture(caplog):
"""Test fixture that captures logging output at INFO level.
Filters out any log messages that do not originate from krakendca.
Use logging_capture.read() in your test to retrieve the current log output.
"""
caplog.set_level(logging.INFO)

class FilteredLog:
def read(self) -> str:
return (
"\n".join(
record.message
for record in caplog.records
if record.name.startswith("krakendca")
)
+ "\n"
)

return FilteredLog()
43 changes: 26 additions & 17 deletions krakendca/dca.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Dollar Cost Averaging module."""
import logging
from datetime import datetime, timedelta
from typing import Optional

Expand All @@ -13,6 +14,8 @@
utc_unix_time_datetime,
)

logger = logging.getLogger(__name__)


class DCA:
"""
Expand Down Expand Up @@ -86,22 +89,22 @@ def handle_dca_logic(self) -> None:
self.check_account_balance()
# Check if didn't already DCA today
if self.count_pair_daily_orders() != 0:
print(
logger.warning(
f"No DCA for {self.pair.name}: Already placed an order "
f"today."
)
return
print("Didn't DCA already today.")
logger.info("Didn't DCA already today.")
# Get current pair ask price.
pair_ask_price = self.pair.get_pair_ask_price(self.ka, self.pair.name)
print(f"Current {self.pair.name} ask price: {pair_ask_price}.")
logger.info(f"Current {self.pair.name} ask price: {pair_ask_price}.")
# Get limit price based on limit_factor
limit_price = self.get_limit_price(
pair_ask_price, self.pair.pair_decimals
)
# Reject DCA if limit_price greater than max_price
if self.max_price != -1 and limit_price > self.max_price:
print(
logger.info(
f"No DCA for {self.pair.name}: Limit price ({limit_price}) "
f"greater than maximum price ({self.max_price})."
)
Expand All @@ -119,7 +122,7 @@ def handle_dca_logic(self) -> None:
self.send_buy_limit_order(order)
# Save order information to CSV file.
order.save_order_csv(self.orders_filepath)
print("Order information saved to CSV.")
logger.info("Order information saved to CSV.")

def get_limit_price(
self, pair_ask_price: float, pair_decimals: int
Expand All @@ -137,7 +140,7 @@ def get_limit_price(
limit_price = round(
pair_ask_price * self.limit_factor, pair_decimals
)
print(
logger.info(
f"Factor adjusted limit price ({self.limit_factor:.4f})"
f": {limit_price}."
)
Expand All @@ -153,7 +156,7 @@ def get_system_time(self) -> datetime:
kraken_time: int = self.ka.get_time()
kraken_date: datetime = utc_unix_time_datetime(kraken_time)
current_date: datetime = current_utc_datetime()
print(f"It's {kraken_date} on Kraken, {current_date} on system.")
logger.info(f"It's {kraken_date} on Kraken, {current_date} on system.")
lag_in_seconds: float = (current_date - kraken_date).seconds
if lag_in_seconds > 2:
raise OSError(
Expand All @@ -171,7 +174,7 @@ def check_account_balance(self) -> None:
:return: None
"""
trade_balance = self.ka.get_trade_balance().get("eb")
print(f"Current trade balance: {trade_balance} ZUSD.")
logger.info(f"Current trade balance: {trade_balance} ZUSD.")
balance = self.ka.get_balance()
try:
pair_base_balance = float(balance.get(self.pair.base))
Expand All @@ -183,7 +186,7 @@ def check_account_balance(self) -> None:
# No pair quote balance on Kraken account.
except TypeError:
pair_quote_balance = 0
print(
logger.info(
f"Pair balances: {pair_quote_balance} {self.pair.quote}, "
f"{pair_base_balance} {self.pair.base}."
)
Expand Down Expand Up @@ -273,11 +276,15 @@ def is_similiar_amount(order_info):
price = float(order_info.get("descr").get("price"))
order_amount = float(order_info.get("vol")) * price
except (ValueError, TypeError, KeyError) as e:
print(f"Cannot figure out order amount of {order_info}: {e}")
logger.info(
f"Cannot figure out order amount of {order_info}: {e}"
)
return True # don't skip in order to avoid repeating orders.
include_order = amount * 0.99 < order_amount < amount * 1.01
if not include_order:
print(f"Ignoring an existing/closed order of {order_amount}")
logger.info(
f"Ignoring an existing/closed order of {order_amount}"
)
return include_order

return {k: v for k, v in pair_orders.items() if is_similiar_amount(v)}
Expand All @@ -294,17 +301,19 @@ def send_buy_limit_order(self, order: Order) -> None:
f"current {order.volume}, "
f"minimum {self.pair.order_min}."
)
print(
logger.info(
f"Create a {order.price}{self.pair.quote} buy limit order of "
f"{order.volume}{self.pair.base} at "
f"{order.pair_price}{self.pair.quote}."
)
print(f"Fee expected: {order.fee}{self.pair.quote} (0.26% taker fee).")
print(
logger.info(
f"Fee expected: {order.fee}{self.pair.quote} (0.26% taker fee)."
)
logger.info(
f"Total price expected: {order.volume}{self.pair.base} for "
f"{order.total_price}{self.pair.quote}."
)
order.send_order(self.ka)
print("Order successfully created.")
print(f"TXID: {order.txid}")
print(f"Description: {order.description}")
logger.info("Order successfully created.")
logger.info(f"TXID: {order.txid}")
logger.info(f"Description: {order.description}")
11 changes: 7 additions & 4 deletions krakendca/krakendca.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Main KrakenDCA object module."""
import logging
from typing import Any, Dict, List

from krakenapi import KrakenApi
Expand All @@ -7,6 +8,8 @@
from .dca import DCA
from .pair import Pair

logger = logging.getLogger(__name__)


class KrakenDCA:
"""
Expand Down Expand Up @@ -35,7 +38,7 @@ def initialize_pairs_dca(self) -> None:
configuration file and data from Kraken.
:return: None
"""
print("Hi, current configuration:")
logger.info("Hi, current configuration:")
asset_pairs: Dict[str, Any] = self.ka.get_asset_pairs()
for dca_pair in self.config.dca_pairs:
pair: Pair = Pair.get_pair_from_kraken(
Expand All @@ -52,7 +55,7 @@ def initialize_pairs_dca(self) -> None:
"ignore_differing_orders", False
),
)
print(dca)
logger.info(dca)
self.dcas_list.append(dca)

def handle_pairs_dca(self) -> None:
Expand All @@ -66,7 +69,7 @@ def handle_pairs_dca(self) -> None:
if n_dca > 1:
pair += "s"

print(f"DCA ({n_dca} {pair}):")
logger.info(f"DCA ({n_dca} {pair}):")
for dca in self.dcas_list:
print(dca)
logger.info(dca)
dca.handle_dca_logic()
25 changes: 13 additions & 12 deletions tests/test_dca.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import vcr
from freezegun import freeze_time
from krakenapi import KrakenApi

from krakendca.dca import DCA
from krakendca.order import Order
from krakendca.pair import Pair
Expand Down Expand Up @@ -45,14 +46,14 @@ def test_init(self):
assert self.dca.orders_filepath == self.test_orders_filepath

@freeze_time("2021-04-15 21:33:28.069731")
def test_handle_dca_logic(self, capfd):
def test_handle_dca_logic(self, logging_capture):
"""Test normal execution."""
with vcr.use_cassette(
"tests/fixtures/vcr_cassettes/test_handle_dca_logic.yaml",
filter_headers=["API-Key", "API-Sign"],
):
self.dca.handle_dca_logic()
captured = capfd.readouterr()
captured = logging_capture.read()
test_output = (
"It's 2021-04-15 21:33:28 on Kraken, 2021-04-15 21:33:28 on "
"system.\n"
Expand All @@ -70,28 +71,28 @@ def test_handle_dca_logic(self, capfd):
"Order information saved to CSV.\n"
)
os.remove(self.test_orders_filepath)
assert captured.out == test_output
assert captured == test_output

@freeze_time("2021-04-16 18:54:53.069731")
def test_handle_dca_logic_error(self, capfd):
def test_handle_dca_logic_error(self, logging_capture):
"""Test execution while already DCA."""
with vcr.use_cassette(
"tests/fixtures/vcr_cassettes/test_handle_dca_logic_error.yaml",
filter_headers=["API-Key", "API-Sign"],
):
self.dca.handle_dca_logic()
captured = capfd.readouterr()
captured = logging_capture.read()
test_output = (
"It's 2021-04-16 18:54:53 on Kraken, 2021-04-16 18:54:53 on "
"system.\n"
"Current trade balance: 16524.7595 ZUSD.\n"
"Pair balances: 359.728 ZEUR, 0.128994332 XETH.\n"
"No DCA for XETHZEUR: Already placed an order today.\n"
)
assert captured.out == test_output
assert captured == test_output

@freeze_time("2021-04-15 21:33:28.069731")
def test_handle_dca_logic_ignore_other_orders(self, capfd):
def test_handle_dca_logic_ignore_other_orders(self, logging_capture):
"""Test execution with other orders present that are ignored."""

# ARRANGE
Expand All @@ -104,7 +105,7 @@ def test_handle_dca_logic_ignore_other_orders(self, capfd):
filter_headers=["API-Key", "API-Sign"],
):
self.dca.handle_dca_logic()
captured = capfd.readouterr()
captured = logging_capture.read()

# ASSERT
test_output = (
Expand All @@ -124,7 +125,7 @@ def test_handle_dca_logic_ignore_other_orders(self, capfd):
"Description: buy 0.00957589 ETHEUR @ limit 2083.16\n"
"Order information saved to CSV.\n"
)
assert captured.out == test_output
assert captured == test_output

def test_get_system_time(self):
"""Test with system time in the past."""
Expand Down Expand Up @@ -309,7 +310,7 @@ def test_send_buy_limit_order_error(self):
"tests/fixtures/vcr_cassettes/test_create_order.yaml",
filter_headers=["API-Key", "API-Sign"],
)
def test_send_buy_limit_order(self, capfd):
def test_send_buy_limit_order(self, logging_capture):
# Test valid order
order = Order(
datetime.strptime("2021-03-11 23:33:28", "%Y-%m-%d %H:%M:%S"),
Expand All @@ -324,7 +325,7 @@ def test_send_buy_limit_order(self, capfd):
20.0,
)
self.dca.send_buy_limit_order(order)
captured = capfd.readouterr()
captured = logging_capture.read()
test_output = (
"Create a 19.9481ZEUR buy limit order of 0.01029256XETH at "
"1938.11ZEUR.\n"
Expand All @@ -334,7 +335,7 @@ def test_send_buy_limit_order(self, capfd):
"TXID: OUHXFN-RTP6W-ART4VP\n"
"Description: buy 0.01029256 ETHEUR @ limit 1938.11\n"
)
assert captured.out == test_output
assert captured == test_output

@vcr.use_cassette("tests/fixtures/vcr_cassettes/test_limit_factor.yaml")
def test_limit_factor(self):
Expand Down
24 changes: 11 additions & 13 deletions tests/test_krakendca.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""krakendca.py tests module."""
import vcr
from _pytest.capture import CaptureFixture
from freezegun import freeze_time
from krakenapi import KrakenApi

from krakendca.config import Config
from krakendca.dca import DCA
from krakendca.krakendca import KrakenDCA
Expand Down Expand Up @@ -95,37 +95,35 @@ def test_initialize_pairs_dca(self) -> None:
"tests/fixtures/vcr_cassettes/test_handle_pairs_dca.yaml",
filter_headers=["API-Key", "API-Sign"],
)
def test_handle_pairs_dca(self, capfd: CaptureFixture) -> None:
def test_handle_pairs_dca(self, logging_capture) -> None:
self.kdca.handle_pairs_dca()
captured = capfd.readouterr()
assert "buy 0.00519042 ETHEUR @ limit 2882.44" in captured.out
assert "buy 0.00051336 XBTEUR @ limit 38857.2" in captured.out
captured = logging_capture.read()
assert "buy 0.00519042 ETHEUR @ limit 2882.44" in captured
assert "buy 0.00051336 XBTEUR @ limit 38857.2" in captured

@freeze_time("2022-03-26 18:37:46")
@vcr.use_cassette(
"tests/fixtures/vcr_cassettes/test_handle_pars_dca_max_price.yaml",
filter_headers=["API-Key", "API-Sign"],
)
def test_handle_pairs_dca_max_price(self, capfd: CaptureFixture) -> None:
def test_handle_pairs_dca_max_price(self, logging_capture) -> None:
self.kdca.dcas_list.pop()
self.kdca.dcas_list[0].max_price = 0
self.kdca.handle_pairs_dca()
captured = capfd.readouterr()
captured = logging_capture.read()
assert (
"No DCA for XETHZEUR: Limit price (2797.99) greater "
"than maximum price (0)." in captured.out
"than maximum price (0)." in captured
)

@freeze_time("2022-03-26 18:37:46")
@vcr.use_cassette(
"tests/fixtures/vcr_cassettes/test_handle_pars_dca_max_price.yaml",
filter_headers=["API-Key", "API-Sign"],
)
def test_handle_pairs_dca_limit_factor(
self, capfd: CaptureFixture
) -> None:
def test_handle_pairs_dca_limit_factor(self, logging_capture) -> None:
self.kdca.dcas_list.pop()
self.kdca.dcas_list[0].max_price = 0
self.kdca.handle_pairs_dca()
captured = capfd.readouterr()
assert "Factor adjusted limit price (0.9850): 2797.99." in captured.out
captured = logging_capture.read()
assert "Factor adjusted limit price (0.9850): 2797.99." in captured

0 comments on commit dbda81b

Please sign in to comment.