diff --git a/.github/workflows/dependabot_auto_approve.yaml b/.github/workflows/dependabot_auto_approve.yaml
index a4dee9c..dc91b0d 100644
--- a/.github/workflows/dependabot_auto_approve.yaml
+++ b/.github/workflows/dependabot_auto_approve.yaml
@@ -26,6 +26,7 @@ jobs:
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve a PR
+ if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }}
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
diff --git a/README.md b/README.md
index 831f7de..0e2cd10 100644
--- a/README.md
+++ b/README.md
@@ -45,17 +45,9 @@ regarding the use of this software.
## Features
-Available Clients:
-
-- NFT REST Clients
-- Spot REST Clients
-- Spot Websocket Clients (Websocket API v1 and v2)
-- Spot Orderbook Clients (Websocket API v1 and v2)
-- Futures REST Clients
-- Futures Websocket Client
-
General:
+- command-line interface
- access both public and private, REST and websocket endpoints
- responsive error handling and custom exceptions
- extensive example scripts (see `/examples` and `/tests`)
@@ -63,6 +55,15 @@ General:
- releases are permanently archived at [Zenodo](https://zenodo.org/badge/latestdoi/510751854)
- releases before v2.0.0 also support Python 3.7+
+Available Clients:
+
+- NFT REST Clients
+- Spot REST Clients
+- Spot Websocket Clients (Websocket API v1 and v2)
+- Spot Orderbook Clients (Websocket API v1 and v2)
+- Futures REST Clients
+- Futures Websocket Client
+
Documentation:
- [https://python-kraken-sdk.readthedocs.io/en/stable](https://python-kraken-sdk.readthedocs.io/en/stable)
@@ -84,6 +85,8 @@ new releases.
## Table of Contents
- [ Installation and setup ](#installation)
+- [ Command-line interface ](#cliusage)
+- [ SDK Usage Hints ](#sdkusage)
- [ Spot Clients ](#spotusage)
- [REST API](#spotrest)
- [Websocket API V2](#spotws)
@@ -96,8 +99,6 @@ new releases.
- [ Notes ](#notes)
- [ References ](#references)
----
-
# 🛠 Installation and setup
@@ -123,7 +124,59 @@ API permissions, rate limits, update the
python-kraken-sdk, see the [Troubleshooting](#trouble) section, and if the error
persists please open an issue.
----
+
+
+# 📍 Command-line interface
+
+The python-kraken-sdk provides a command-line interface to access the Kraken API
+using basic instructions while performing authentication tasks in the
+background. The Spot, NFT and Futures API are accessible and follow the pattern
+`kraken {spot,futures} [OPTIONS] URL`. See examples below.
+
+```bash
+# get server time
+kraken spot https://api.kraken.com/0/public/Time
+{'unixtime': 1716707589, 'rfc1123': 'Sun, 26 May 24 07:13:09 +0000'}
+
+# get user's balances
+kraken spot --api-key= --secret-key= -X POST https://api.kraken.com/0/private/Balance
+{'ATOM': '17.28229999', 'BCH': '0.0000077100', 'ZUSD': '1000.0000'}
+
+# get user's trade balances
+kraken spot --api-key= --secret-key= -X POST https://api.kraken.com/0/private/TradeBalance --data '{"asset": "DOT"}'
+{'eb': '2.8987347115', 'tb': '1.1694303513', 'm': '0.0000000000', 'uv': '0', 'n': '0.0000000000', 'c': '0.0000000000', 'v': '0.0000000000', 'e': '1.1694303513', 'mf': '1.1694303513'}
+
+# get 1D candles for a futures instrument
+kraken futures https://futures.kraken.com/api/charts/v1/spot/PI_XBTUSD/1d
+{'candles': [{'time': 1625616000000, 'open': '34557.84000000000', 'high': '34803.20000000000', 'low': '33816.32000000000', 'close': '33880.22000000000', 'volume': '0' ...
+
+# get user's open futures positions
+kraken futures --api-key= --secret-key= https://futures.kraken.com/derivatives/api/v3/openpositions
+{'result': 'success', 'openPositions': [], 'serverTime': '2024-05-26T07:15:38.91Z'}
+```
+
+... All endpoints of the Kraken Spot and Futurs API can be accessed like that.
+
+
+
+# 📍 SDK Usage Hints
+
+The python-kraken-sdk provides lots of functions to easily access most of the
+REST and websocket endpoints of the Kraken Cryptocurrency Exchange API. Since
+these endpoints and their parameters may change, all implemented endpoints are
+tested on a regular basis.
+
+If certain parameters or settings are not available, or
+specific endpoints are hidden and not implemented, it is always possible to
+execute requests to the endpoints directly using the `_request` method provided
+by any client. This is demonstrated below.
+
+```python
+from kraken.spot import User
+
+user = User(key="", secret="")
+print(user._request(method="POST", uri="/0/private/Balance"))
+```
diff --git a/doc/examples/command_line_interface.rst b/doc/examples/command_line_interface.rst
new file mode 100644
index 0000000..f9ccabb
--- /dev/null
+++ b/doc/examples/command_line_interface.rst
@@ -0,0 +1,38 @@
+.. -*- coding: utf-8 -*-
+.. Copyright (C) 2024 Benjamin Thomas Schwertfeger
+.. GitHub: https://github.com/btschwertfeger
+
+.. _section-command-line-interface-examples:
+
+Command-line Interface
+======================
+
+The python-kraken-sdk provides a command-line interface to access the Kraken API
+using basic instructions while performing authentication tasks in the
+background. The Spot, NFT and Futures API are accessible and follow the pattern
+``kraken {spot,futures} [OPTIONS] URL``. All endpoints of the Kraken Spot and
+Futurs API can be accessed like that. See examples below.
+
+.. code-block:: bash
+ :linenos:
+ :caption: Command-line Interface Examples
+
+ # get server time
+ kraken spot https://api.kraken.com/0/public/Time
+ {'unixtime': 1716707589, 'rfc1123': 'Sun, 26 May 24 07:13:09 +0000'}
+
+ # get user's balances
+ kraken spot --api-key= --secret-key= -X POST https://api.kraken.com/0/private/Balance
+ {'ATOM': '17.28229999', 'BCH': '0.0000077100', 'ZUSD': '1000.0000'}
+
+ # get user's trade balances
+ kraken spot --api-key= --secret-key= -X POST https://api.kraken.com/0/private/TradeBalance --data '{"asset": "DOT"}'
+ {'eb': '2.8987347115', 'tb': '1.1694303513', 'm': '0.0000000000', 'uv': '0', 'n': '0.0000000000', 'c': '0.0000000000', 'v': '0.0000000000', 'e': '1.1694303513', 'mf': '1.1694303513'}
+
+ # get 1D candles for a futures instrument
+ kraken futures https://futures.kraken.com/api/charts/v1/spot/PI_XBTUSD/1d
+ {'candles': [{'time': 1625616000000, 'open': '34557.84000000000', 'high': '34803.20000000000', 'low': '33816.32000000000', 'close': '33880.22000000000', 'volume': '0' ...
+
+ # get user's open futures positions
+ kraken futures --api-key= --secret-key= https://futures.kraken.com/derivatives/api/v3/openpositions
+ {'result': 'success', 'openPositions': [], 'serverTime': '2024-05-26T07:15:38.91Z'}
diff --git a/doc/examples/rest_example_usage.rst b/doc/examples/rest_example_usage.rst
index 287c356..2f23f5d 100644
--- a/doc/examples/rest_example_usage.rst
+++ b/doc/examples/rest_example_usage.rst
@@ -7,6 +7,25 @@
Usage Examples
==============
+The python-kraken-sdk provides lots of functions to easily access most of the
+REST and websocket endpoints of the Kraken Cryptocurrency Exchange API. Since
+these endpoints and their parameters may change, all implemented endpoints are
+tested on a regular basis.
+
+If certain parameters or settings are not available, or specific endpoints are
+hidden and not implemented, it is always possible to execute requests to the
+endpoints directly using the ``_request`` method provided by all clients. This
+is demonstrated below.
+
+.. code-block:: python
+ :linenos:
+ :caption: Usage of the basic _request method
+
+ from kraken.spot import User
+
+ user = User(key="", secret="")
+ print(user._request(method="POST", uri="/0/private/Balance"))
+
The repository of the `python-kraken-sdk`_ provides some example scripts that
demonstrate some of the implemented methods. Please see the sections listed
below.
diff --git a/doc/index.rst b/doc/index.rst
index a1b1bd8..d1a87d0 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -22,6 +22,7 @@ Welcome to python-kraken-sdk's documentation!
introduction.rst
getting_started/getting_started.rst
+ examples/command_line_interface.rst
examples/rest_example_usage.rst
examples/trading_bot_templates.rst
spot/rest.rst
diff --git a/doc/introduction.rst b/doc/introduction.rst
index d90738c..2d724b5 100644
--- a/doc/introduction.rst
+++ b/doc/introduction.rst
@@ -56,17 +56,9 @@ regarding the use of this software.
Features
--------
-Available Clients:
-
-- NFT REST Clients
-- Spot REST Clients
-- Spot Websocket Clients (Websocket API v1 and v2)
-- Spot Orderbook Clients (Websocket API v1 and v2)
-- Futures REST Clients
-- Futures Websocket Client
-
General:
+- command-line interface
- access both public and private, REST and websocket endpoints
- responsive error handling and custom exceptions
- extensive examples
@@ -74,6 +66,14 @@ General:
- releases are permanently archived at `Zenodo `_
- releases before v2.0.0 also support Python 3.7+
+Available Clients:
+
+- NFT REST Clients
+- Spot REST Clients
+- Spot Websocket Clients (Websocket API v1 and v2)
+- Spot Orderbook Clients (Websocket API v1 and v2)
+- Futures REST Clients
+- Futures Websocket Client
Important Notice
-----------------
diff --git a/kraken/base_api/__init__.py b/kraken/base_api/__init__.py
index 41a1c3e..90656a5 100644
--- a/kraken/base_api/__init__.py
+++ b/kraken/base_api/__init__.py
@@ -212,7 +212,12 @@ def __init__(
self.__err_handler: KrakenErrorHandler = KrakenErrorHandler()
self.__session: requests.Session = requests.Session()
- self.__session.headers.update({"User-Agent": "python-kraken-sdk"})
+ self.__session.headers.update(
+ {
+ "User-Agent": "python-kraken-sdk"
+ " (https://github.com/btschwertfeger/python-kraken-sdk)",
+ },
+ )
def _request( # noqa: PLR0913 # pylint: disable=too-many-arguments
self: KrakenSpotBaseAPI,
@@ -483,7 +488,12 @@ def __init__(
self.__err_handler: KrakenErrorHandler = KrakenErrorHandler()
self.__session: requests.Session = requests.Session()
- self.__session.headers.update({"User-Agent": "python-kraken-sdk"})
+ self.__session.headers.update(
+ {
+ "User-Agent": "python-kraken-sdk"
+ " (https://github.com/btschwertfeger/python-kraken-sdk)",
+ },
+ )
def _request( # noqa: PLR0913 # pylint: disable=too-many-arguments
self: KrakenFuturesBaseAPI,
diff --git a/kraken/cli.py b/kraken/cli.py
new file mode 100644
index 0000000..8bf6167
--- /dev/null
+++ b/kraken/cli.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# Copyright (C) 2024 Benjamin Thomas Schwertfeger
+# GitHub: https://github.com/btschwertfeger
+#
+# pylint: disable=import-outside-toplevel
+
+"""
+Module implementing the command-line interface for the python-kraken-sdk.
+"""
+
+from __future__ import annotations
+
+import logging
+import sys
+from re import sub as re_sub
+from typing import TYPE_CHECKING, Any
+
+from click import echo
+from cloup import (
+ Choice,
+ HelpFormatter,
+ HelpTheme,
+ Style,
+ argument,
+ group,
+ option,
+ pass_context,
+)
+from orjson import JSONDecodeError
+from orjson import loads as orloads
+
+if TYPE_CHECKING:
+ from cloup import Context
+
+
+def print_version(ctx: Context, param: Any, value: Any) -> None: # noqa: ANN401, ARG001
+ """Prints the version of the package"""
+ if not value or ctx.resilient_parsing:
+ return
+ from importlib.metadata import version # noqa: PLC0415
+
+ echo(version("python-kraken-sdk"))
+ ctx.exit()
+
+
+@group(
+ context_settings={
+ "auto_envvar_prefix": "KRAKEN",
+ "help_option_names": ["-h", "--help"],
+ },
+ formatter_settings=HelpFormatter.settings(
+ theme=HelpTheme(
+ invoked_command=Style(fg="bright_yellow"),
+ heading=Style(fg="bright_white", bold=True),
+ constraint=Style(fg="magenta"),
+ col1=Style(fg="bright_yellow"),
+ ),
+ ),
+ no_args_is_help=True,
+)
+@option(
+ "--version",
+ is_flag=True,
+ callback=print_version,
+ expose_value=False,
+ is_eager=True,
+)
+@option(
+ "-v",
+ "--verbose",
+ required=False,
+ is_flag=True,
+ help="Increase verbosity",
+)
+@pass_context
+def cli(ctx: Context, **kwargs: dict) -> None:
+ """Command-line tool to access the Kraken Cryptocurrency Exchange API"""
+ ctx.obj = kwargs
+
+ logging.basicConfig(
+ format="%(asctime)s %(levelname)8s | %(message)s",
+ datefmt="%Y/%m/%d %H:%M:%S",
+ level=logging.INFO if not kwargs["verbose"] else logging.DEBUG,
+ )
+
+
+@cli.command()
+@option(
+ "-X",
+ required=True,
+ type=Choice(["GET", "POST", "PUT", "DELETE"]),
+ default="GET",
+ help="Request method",
+ show_default="GET",
+)
+@option(
+ "-d",
+ "--data",
+ required=False,
+ type=str,
+ help="Payload as valid JSON string",
+)
+@option(
+ "--timeout",
+ required=False,
+ type=int,
+ default=10,
+ help="Timeout in seconds",
+)
+@option(
+ "--api-key",
+ required=False,
+ type=str,
+ default="",
+ help="Kraken Public API Key",
+)
+@option(
+ "--secret-key",
+ required=False,
+ type=str,
+ default="",
+ help="Kraken Secret API Key",
+)
+@argument("url", type=str, required=True)
+@pass_context
+def spot(ctx: Context, url: str, **kwargs: dict) -> None: # noqa: ARG001
+ """Access the Kraken Spot REST API"""
+ from kraken.base_api import KrakenSpotBaseAPI # noqa: PLC0415
+
+ logging.debug("Initialize the Kraken client")
+ client = KrakenSpotBaseAPI(
+ key=kwargs["api_key"], # type: ignore[arg-type]
+ secret=kwargs["secret_key"], # type: ignore[arg-type]
+ )
+
+ try:
+ response = client._request( # noqa: SLF001 # pylint: disable=protected-access,no-value-for-parameter
+ method=kwargs["x"], # type: ignore[arg-type]
+ uri=(uri := re_sub(r"https://.*.com", "", url)),
+ params=orloads(kwargs.get("data") or "{}"),
+ timeout=kwargs["timeout"], # type: ignore[arg-type]
+ auth="private" in uri.lower(),
+ )
+ except JSONDecodeError as exc:
+ logging.error(f"Could not parse the passed data. {exc}") # noqa: G004
+ except Exception as exc: # noqa: BLE001
+ logging.error(f"Exception occurred: {exc}") # noqa: G004
+ sys.exit(1)
+ else:
+ echo(response)
+ sys.exit(0)
+
+
+@cli.command()
+@option(
+ "-X",
+ required=True,
+ type=Choice(["GET", "POST", "PUT", "DELETE"]),
+ default="GET",
+ help="Request method",
+ show_default="GET",
+)
+@option(
+ "-d",
+ "--data",
+ required=False,
+ type=str,
+ help="POST parameters as valid JSON string",
+)
+@option(
+ "-q",
+ "--query",
+ required=False,
+ type=str,
+ help="Query parameters as valid JSON string",
+)
+@option(
+ "--timeout",
+ required=False,
+ type=int,
+ default=10,
+ help="Timeout in seconds",
+)
+@option(
+ "--api-key",
+ required=False,
+ type=str,
+ default="",
+ help="Kraken Public API Key",
+)
+@option(
+ "--secret-key",
+ required=False,
+ type=str,
+ default="",
+ help="Kraken Secret API Key",
+)
+@argument("url", type=str, required=True)
+@pass_context
+def futures(ctx: Context, url: str, **kwargs: dict) -> None: # noqa: ARG001
+ """Access the Kraken Futures REST API"""
+ from kraken.base_api import KrakenFuturesBaseAPI # noqa: PLC0415
+
+ logging.debug("Initialize the Kraken client")
+ client = KrakenFuturesBaseAPI(
+ key=kwargs["api_key"], # type: ignore[arg-type]
+ secret=kwargs["secret_key"], # type: ignore[arg-type]
+ )
+
+ try:
+ response = client._request( # noqa: SLF001 # pylint: disable=protected-access,no-value-for-parameter
+ method=kwargs["x"], # type: ignore[arg-type]
+ uri=(uri := re_sub(r"https://.*.com", "", url)),
+ post_params=orloads(kwargs.get("data") or "{}"),
+ query_params=orloads(kwargs.get("query") or "{}"),
+ timeout=kwargs["timeout"], # type: ignore[arg-type]
+ auth="derivatives" in uri.lower(),
+ )
+ except JSONDecodeError as exc:
+ logging.error(f"Could not parse the passed data. {exc}") # noqa: G004
+ except Exception as exc: # noqa: BLE001
+ logging.error(f"Exception occurred: {exc}") # noqa: G004
+ sys.exit(1)
+ else:
+ echo(response)
+ sys.exit(0)
diff --git a/pyproject.toml b/pyproject.toml
index cb59b59..862ba86 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -16,11 +16,18 @@ authors = [
maintainers = [
{ name = "Benjamin Thomas Schwertfeger", email = "contact@b-schwertfeger.de" },
]
-description = "Collection of REST and websocket clients to interact with the Kraken cryptocurrency exchange."
+description = "Command-line tool and collection of REST and websocket clients to interact with the Kraken cryptocurrency exchange."
readme = "README.md"
license = { file = "LICENSE" }
requires-python = ">=3.11"
-dependencies = ["asyncio>=3.4", "requests", "websockets"]
+dependencies = [
+ "asyncio>=3.4",
+ "requests",
+ "websockets",
+ "click",
+ "cloup",
+ "orjson",
+]
keywords = ["crypto", "trading", "kraken", "exchange", "api"]
classifiers = [
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
@@ -39,6 +46,9 @@ classifiers = [
"Operating System :: Unix",
]
+[project.scripts]
+kraken = "kraken.cli:cli"
+
[project.urls]
"Homepage" = "https://github.com/btschwertfeger/python-kraken-sdk"
"Bug Tracker" = "https://github.com/btschwertfeger/python-kraken-sdk/issues"
diff --git a/tests/cli/basic.sh b/tests/cli/basic.sh
new file mode 100755
index 0000000..deba301
--- /dev/null
+++ b/tests/cli/basic.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+kraken spot https://api.kraken.com/0/public/Time
+kraken spot /0/public/Time
+
+kraken spot -X POST https://api.kraken.com/0/private/Balance
+kraken spot -X POST https://api.kraken.com/0/private/TradeBalance -d '{"asset": "DOT"}'
+
+kraken futures https://futures.kraken.com/api/charts/v1/spot/PI_XBTUSD/1d
+kraken futures /api/charts/v1/spot/PI_XBTUSD/1d
+
+kraken futures https://futures.kraken.com/derivatives/api/v3/openpositions
+# kraken futures -X POST https://futures.kraken.com/derivatives/api/v3/editorder -d '{"cliOrdID": "12345", "limitPrice": 10}'
diff --git a/tests/cli/conftest.py b/tests/cli/conftest.py
new file mode 100644
index 0000000..081c4c4
--- /dev/null
+++ b/tests/cli/conftest.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# Copyright (C) 2024 Benjamin Thomas Schwertfeger
+# GitHub: https://github.com/btschwertfeger
+#
+
+"""Module implementing fixtures for testing"""
+
+from __future__ import annotations
+
+import os
+
+import pytest
+from click.testing import CliRunner
+
+
+@pytest.fixture()
+def cli_runner() -> CliRunner:
+ """Provide a cli-runner for testing the CLI"""
+ return CliRunner()
+
+
+@pytest.fixture()
+def _with_cli_env_vars() -> None:
+ """Setup some environment variables for th CLI tests"""
+ os.environ["KRAKEN_SPOT_API_KEY"] = os.getenv("SPOT_API_KEY", "")
+ os.environ["KRAKEN_SPOT_SECRET_KEY"] = os.getenv("SPOT_SECRET_KEY", "")
+ os.environ["KRAKEN_FUTURES_API_KEY"] = os.getenv("FUTURES_API_KEY", "")
+ os.environ["KRAKEN_FUTURES_SECRET_KEY"] = os.getenv("FUTURES_SECRET_KEY", "")
+
+ yield
+
+ for var in (
+ "KRAKEN_SPOT_API_KEY",
+ "KRAKEN_SPOT_SECRET_KEY",
+ "KRAKEN_FUTURES_API_KEY",
+ "KRAKEN_FUTURES_SECRET_KEY",
+ ):
+ if os.getenv(var):
+ del os.environ[var]
diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py
new file mode 100644
index 0000000..18d7b97
--- /dev/null
+++ b/tests/cli/test_cli.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# Copyright (C) 2024 Benjamin Thomas Schwertfeger
+# GitHub: https://github.com/btschwertfeger
+#
+
+"""Module implementing unit tests for the command-line interface"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from kraken.cli import cli
+
+if TYPE_CHECKING:
+
+ from click.testing import CliRunner
+import pytest
+
+
+@pytest.mark.spot()
+def test_cli_version(cli_runner: CliRunner) -> None:
+
+ result = cli_runner.invoke(cli, ["--version"])
+ assert result.exit_code == 0, result.exception
+
+
+@pytest.mark.spot()
+def test_cli_spot_public(cli_runner: CliRunner) -> None:
+ result = cli_runner.invoke(cli, ["spot", "https://api.kraken.com/0/public/Time"])
+ assert result.exit_code == 0, result.exception
+
+ result = cli_runner.invoke(cli, ["spot", "/0/public/Time"])
+ assert result.exit_code == 0, result.exception
+
+
+@pytest.mark.usefixtures("_with_cli_env_vars")
+@pytest.mark.spot()
+@pytest.mark.spot_auth()
+def test_cli_spot_private(
+ cli_runner: CliRunner,
+) -> None:
+ result = cli_runner.invoke(
+ cli,
+ ["spot", "-X", "POST", "https://api.kraken.com/0/private/Balance"],
+ )
+ assert result.exit_code == 0, result.exception
+
+ result = cli_runner.invoke(
+ cli,
+ ["spot", "-X", "POST", "/0/private/Balance", "-d", '\'{"asset": "DOT"}\''],
+ )
+ assert result.exit_code == 0, result.exception
+
+
+@pytest.mark.futures()
+def test_cli_futures_public(cli_runner: CliRunner) -> None:
+ result = cli_runner.invoke(
+ cli,
+ ["futures", "https://futures.kraken.com/api/charts/v1/spot/PI_XBTUSD/1d"],
+ )
+ assert result.exit_code == 0, result.exception
+
+ result = cli_runner.invoke(cli, ["futures", "/api/charts/v1/spot/PI_XBTUSD/1d"])
+ assert result.exit_code == 0, result.exception
+
+
+@pytest.mark.usefixtures("_with_cli_env_vars")
+@pytest.mark.futures()
+@pytest.mark.futures_auth()
+def test_cli_futures_private(
+ cli_runner: CliRunner,
+) -> None:
+ result = cli_runner.invoke(
+ cli,
+ ["futures", "https://futures.kraken.com/derivatives/api/v3/openpositions"],
+ )
+ assert result.exit_code == 0, result.exception
diff --git a/tests/nft/test_nft_trade.py b/tests/nft/test_nft_trade.py
index 9d0c4ef..7435dc3 100644
--- a/tests/nft/test_nft_trade.py
+++ b/tests/nft/test_nft_trade.py
@@ -75,7 +75,6 @@ def test_nft_trade_modify_auction(nft_auth_trade: Trade) -> None:
) == ["EAPI:Invalid arguments:No auction with the provided ID"]
-@pytest.mark.wip()
@pytest.mark.nft()
@pytest.mark.nft_auth()
@pytest.mark.nft_trade()