From 278df2456943a7e17f6bf41446fb409299538d54 Mon Sep 17 00:00:00 2001 From: Benjamin Thomas Schwertfeger Date: Fri, 10 Jan 2025 08:25:09 +0100 Subject: [PATCH 1/2] Resolve "Add customizable fee" --- src/kraken_infinity_grid/cli.py | 26 ++++++++++++++++++++++++-- src/kraken_infinity_grid/gridbot.py | 2 +- src/kraken_infinity_grid/setup.py | 7 ++++++- src/kraken_infinity_grid/telegram.py | 2 +- tests/test_setup.py | 13 +++++++++++-- tests/test_telegram.py | 2 +- 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/kraken_infinity_grid/cli.py b/src/kraken_infinity_grid/cli.py index 294eb02..5f752fe 100644 --- a/src/kraken_infinity_grid/cli.py +++ b/src/kraken_infinity_grid/cli.py @@ -32,7 +32,18 @@ def ensure_larger_than_zero( ) -> Any: # noqa: ANN401 """Ensure the value is larger than 0""" if value <= 0: - ctx.fail(f"Value for option '{param.name}' must be larger than 0") + ctx.fail(f"Value for option '{param.name}' must be larger than 0!") + return value + + +def ensure_larger_equal_zero( + ctx: Context, + param: Any, # noqa: ANN401 + value: Any, # noqa: ANN401 +) -> Any: # noqa: ANN401 + """Ensure the value is larger than 0""" + if value is not None and value < 0: + ctx.fail(f"Value for option '{param.name}' must be larger then or equal to 0!") return value @@ -136,7 +147,7 @@ def cli(ctx: Context, **kwargs: dict) -> None: "--name", required=True, type=STRING, - help="The name of the bot.", + help="The name of the instance, displayed in telegram messages.", ) @option( "--base-currency", @@ -219,6 +230,17 @@ def cli(ctx: Context, **kwargs: dict) -> None: callback=ensure_larger_than_zero, help="A reference number to identify the bots orders with.", ) +@option( + "--fee", + type=FLOAT, + required=False, + callback=ensure_larger_equal_zero, + help=""" + The fee percentage to respect, e.g. '0.0026' for 0.26 %. This value does not + change the actual paid fee, instead it used to estimate order sizes. If not + passed, the highest maker fee will be used. + """, +) @option( "--sqlite-file", type=STRING, diff --git a/src/kraken_infinity_grid/gridbot.py b/src/kraken_infinity_grid/gridbot.py index 387f71d..ab1ca8a 100644 --- a/src/kraken_infinity_grid/gridbot.py +++ b/src/kraken_infinity_grid/gridbot.py @@ -151,7 +151,7 @@ def __init__( self.interval: float = float(config["interval"]) self.amount_per_grid: float = float(config["amount_per_grid"]) - self.amount_per_grid_plus_fee: float | None = None + self.amount_per_grid_plus_fee: float | None = config.get("fee") self.max_investment: float = config["max_investment"] self.n_open_buy_orders: int = config["n_open_buy_orders"] diff --git a/src/kraken_infinity_grid/setup.py b/src/kraken_infinity_grid/setup.py index ca34692..5987b7d 100644 --- a/src/kraken_infinity_grid/setup.py +++ b/src/kraken_infinity_grid/setup.py @@ -202,7 +202,12 @@ def __check_asset_pair_parameter(self: Self) -> None: self.__s.zbase_currency = data["base"] # XXBT self.__s.xquote_currency = data["quote"] # ZEUR self.__s.cost_decimals = data["cost_decimals"] # 5, i.e., 0.00001 EUR - self.__s.fee = float(data["fees_maker"][0][1]) / 100 + + if self.__s.fee is None: + # This is the case if the '--fee' parameter was not passed, then we + # take the highest maker fee. + self.__s.fee = float(data["fees_maker"][0][1]) / 100 + self.__s.amount_per_grid_plus_fee = self.__s.amount_per_grid * ( 1 + self.__s.fee ) diff --git a/src/kraken_infinity_grid/telegram.py b/src/kraken_infinity_grid/telegram.py index f950849..0ce9e61 100644 --- a/src/kraken_infinity_grid/telegram.py +++ b/src/kraken_infinity_grid/telegram.py @@ -91,7 +91,7 @@ def send_bot_update(self: Self) -> None: # noqa: C901 ) message += f"├ Available {self.__s.base_currency} » {balances['base_available'] - float(self.__s.configuration.get()['vol_of_unfilled_remaining'])}\n" # noqa: E501 message += f"├ Unfilled surplus of {self.__s.base_currency} » {self.__s.configuration.get()['vol_of_unfilled_remaining']}\n" # noqa: E501 - message += f"├ Bot-managed wealth » {round(balances['base_balance'] * self.__s.ticker.last + balances['quote_balance'], self.__s.cost_decimals)} {self.__s.quote_currency}\n" # noqa: E501 + message += f"├ Wealth » {round(balances['base_balance'] * self.__s.ticker.last + balances['quote_balance'], self.__s.cost_decimals)} {self.__s.quote_currency}\n" # noqa: E501 message += f"└ Investment » {round(self.__s.investment, self.__s.cost_decimals)} / {self.__s.max_investment} {self.__s.quote_currency}\n\n" # noqa: E501 message += "💠 Orders\n" diff --git a/tests/test_setup.py b/tests/test_setup.py index afefa32..f47540c 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -226,7 +226,15 @@ def test_update_order_book( ) +@pytest.mark.wip +@pytest.mark.parametrize( + "input_fee,asset_fee,order_size", # noqa: PT006 + [(None, 0.0026, 100.26), (0.02, 0.02, 102)], +) def test_check_asset_pair_parameter( + input_fee: float, + asset_fee: float, + order_size: float, setup_manager: SetupManager, strategy: mock.Mock, ) -> None: @@ -246,15 +254,16 @@ def test_check_asset_pair_parameter( } strategy.symbol = "BTC/USD" strategy.amount_per_grid = 100 + strategy.fee = input_fee setup_manager._SetupManager__check_asset_pair_parameter() - assert strategy.fee == 0.0026 + assert strategy.fee == asset_fee assert strategy.altname == "BTC/USD" assert strategy.zbase_currency == "XXBT" assert strategy.xquote_currency == "ZEUR" assert strategy.cost_decimals == 5 - assert strategy.amount_per_grid_plus_fee == pytest.approx(100.26) + assert strategy.amount_per_grid_plus_fee == pytest.approx(order_size) def test_check_configuration_changes( diff --git a/tests/test_telegram.py b/tests/test_telegram.py index 8cc7f1f..ec2505e 100644 --- a/tests/test_telegram.py +++ b/tests/test_telegram.py @@ -128,7 +128,7 @@ def test_send_bot_update( assert "├ Available USD » 50.0" in message assert "├ Available BTC » 0.4" in message assert "├ Unfilled surplus of BTC » 0.1" in message - assert "├ Bot-managed wealth » 50100.0 USD" in message + assert "├ Wealth » 50100.0 USD" in message assert "└ Investment » 1000.0 / 2000.0 USD" in message assert "💠 Orders" in message assert "├ Amount per Grid » 10.0 USD" in message From ac57364a05deb2b8b754adc1745b1af49fc67ff0 Mon Sep 17 00:00:00 2001 From: Benjamin Thomas Schwertfeger Date: Fri, 10 Jan 2025 08:36:13 +0100 Subject: [PATCH 2/2] Update readme --- README.md | 3 ++- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 03aa7f4..c5cfa68 100644 --- a/README.md +++ b/README.md @@ -172,9 +172,10 @@ these steps: | `KRAKEN_RUN_BASE_CURRENCY` | `str` | The base currency e.g., `BTC`. | | `KRAKEN_RUN_QUOTE_CURRENCY` | `str` | The quote currency e.g., `USD`. | | `KRAKEN_RUN_AMOUNT_PER_GRID` | `float` | The amount to use per grid interval e.g., `100` (USD). | -| `KRAKEN_RUN_INTERVAL` | `float` | The interval between orders e.g., `0.04` to have 4 % intervals). | +| `KRAKEN_RUN_INTERVAL` | `float` | The interval between orders e.g., `0.04` to have 4 % intervals. | | `KRAKEN_RUN_N_OPEN_BUY_ORDERS` | `int` | The number of concurrent open buy orders, e.g., `3`. | | `KRAKEN_RUN_MAX_INVESTMENT` | `str` | The maximum investment amount, e.g. `1000` USD. | +| `KRAKEN_RUN_FEE` | `float` | A custom fee percentage, e.g. `0.0026` for 0.26 % fee. | | `KRAKEN_RUN_STRATEGY` | `str` | The trading strategy (e.g., `GridHODL`, `GridSell`, `SWING`, or `DCA`). | | `KRAKEN_RUN_TELEGRAM_TOKEN` | `str` | The Telegram bot token for notifications. | | `KRAKEN_RUN_TELEGRAM_CHAT_ID` | `str` | The Telegram chat ID for notifications. | diff --git a/pyproject.toml b/pyproject.toml index d8f9718..ec653eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ package-dir = { "" = "src" } [tool.setuptools.packages.find] where = ["src"] include = ["kraken_infinity_grid*"] -exclude = [".env"] +exclude = [".env", "tests"] [tool.setuptools_scm] write_to = "src/kraken_infinity_grid/_version.py"