Skip to content

Commit

Permalink
Resolve "async_close and stop market as deprecated, but no alternativ…
Browse files Browse the repository at this point in the history
…e is provided"
  • Loading branch information
btschwertfeger committed Feb 15, 2025
1 parent 57647c0 commit 9afbe2a
Show file tree
Hide file tree
Showing 16 changed files with 370 additions and 301 deletions.
31 changes: 29 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,43 @@

## [Unreleased](https://github.com/btschwertfeger/python-kraken-sdk/tree/HEAD)

[Full Changelog](https://github.com/btschwertfeger/python-kraken-sdk/compare/v3.1.3...HEAD)
[Full Changelog](https://github.com/btschwertfeger/python-kraken-sdk/compare/v3.1.5...HEAD)

Uncategorized merged pull requests:

- Update copyright header [\#354](https://github.com/btschwertfeger/python-kraken-sdk/pull/354) ([btschwertfeger](https://github.com/btschwertfeger))
- Bump github/codeql-action from 3.28.5 to 3.28.9 [\#353](https://github.com/btschwertfeger/python-kraken-sdk/pull/353) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump actions/setup-python from 5.3.0 to 5.4.0 [\#351](https://github.com/btschwertfeger/python-kraken-sdk/pull/351) ([dependabot[bot]](https://github.com/apps/dependabot))
- Update egress rules in CI [\#349](https://github.com/btschwertfeger/python-kraken-sdk/pull/349) ([btschwertfeger](https://github.com/btschwertfeger))

## [v3.1.5](https://github.com/btschwertfeger/python-kraken-sdk/tree/v3.1.5) (2025-01-29)

[Full Changelog](https://github.com/btschwertfeger/python-kraken-sdk/compare/v3.1.4...v3.1.5)

**Fixed bugs:**

- Resolve "Documentation is somewhat incomplete" [\#348](https://github.com/btschwertfeger/python-kraken-sdk/pull/348) ([btschwertfeger](https://github.com/btschwertfeger))

Uncategorized merged pull requests:

- Bump codecov/codecov-action from 5.2.0 to 5.3.1 [\#346](https://github.com/btschwertfeger/python-kraken-sdk/pull/346) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump github/codeql-action from 3.28.2 to 3.28.5 [\#345](https://github.com/btschwertfeger/python-kraken-sdk/pull/345) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4 [\#344](https://github.com/btschwertfeger/python-kraken-sdk/pull/344) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump dependabot/fetch-metadata from 2.2.0 to 2.3.0 [\#343](https://github.com/btschwertfeger/python-kraken-sdk/pull/343) ([dependabot[bot]](https://github.com/apps/dependabot))

## [v3.1.4](https://github.com/btschwertfeger/python-kraken-sdk/tree/v3.1.4) (2025-01-25)

[Full Changelog](https://github.com/btschwertfeger/python-kraken-sdk/compare/v3.1.3...v3.1.4)

Uncategorized merged pull requests:

- Bump github/codeql-action from 3.28.1 to 3.28.2 [\#341](https://github.com/btschwertfeger/python-kraken-sdk/pull/341) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump codecov/codecov-action from 5.1.2 to 5.2.0 [\#340](https://github.com/btschwertfeger/python-kraken-sdk/pull/340) ([dependabot[bot]](https://github.com/apps/dependabot))
- Update the documentation [\#339](https://github.com/btschwertfeger/python-kraken-sdk/pull/339) ([btschwertfeger](https://github.com/btschwertfeger))
- Bump step-security/harden-runner from 2.10.3 to 2.10.4 [\#338](https://github.com/btschwertfeger/python-kraken-sdk/pull/338) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump step-security/harden-runner from 2.10.2 to 2.10.3 [\#337](https://github.com/btschwertfeger/python-kraken-sdk/pull/337) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump github/codeql-action from 3.28.0 to 3.28.1 [\#336](https://github.com/btschwertfeger/python-kraken-sdk/pull/336) ([dependabot[bot]](https://github.com/apps/dependabot))
- Switch to src-layout [\#342](https://github.com/btschwertfeger/python-kraken-sdk/pull/342) ([btschwertfeger](https://github.com/btschwertfeger))
- Update the documentation [\#339](https://github.com/btschwertfeger/python-kraken-sdk/pull/339) ([btschwertfeger](https://github.com/btschwertfeger))

## [v3.1.3](https://github.com/btschwertfeger/python-kraken-sdk/tree/v3.1.3) (2025-01-05)

Expand Down
140 changes: 70 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,10 @@ async def main():
response = await client.request("POST", "/0/private/Balance")
print(response)
finally:
await client.async_close()
await client.close()

asyncio.run(main())
if __name__ == "__main__":
asyncio.run(main())
```
Using SpotAsyncClient as a context manager; This example demonstrates the use of the context manager, which ensures the client is automatically closed after the request is completed.
Expand All @@ -215,7 +216,8 @@ async def main():
response = await client.request("POST", "/0/private/Balance")
print(response)

asyncio.run(main())
if __name__ == "__main__":
asyncio.run(main())
```
<a name="spotws"></a>
Expand Down Expand Up @@ -272,44 +274,40 @@ class Client(SpotWSClient):
# You can also un-/subscribe here using self.subscribe/self.unsubscribe.

async def main():

# Public/unauthenticated websocket client
client = Client() # only use this one if you don't need private feeds
await client.start()
await client.subscribe(
params={"channel": "ticker", "symbol": ["BTC/USD", "DOT/USD"]}
)
await client.subscribe(
params={"channel": "book", "depth": 25, "symbol": ["BTC/USD"]}
)
# wait because unsubscribing is faster than unsubscribing … (just for that example)
await asyncio.sleep(3)
# print(client.active_public_subscriptions) # to list active subscriptions
await client.unsubscribe(
params={"channel": "ticker", "symbol": ["BTC/USD", "DOT/USD"]}
)
#

# AS default, the authenticated client starts two websocket connections,
# one for authenticated and one for public messages. If there is no need
# for a public connection, it can be disabled using the ``no_public``
# parameter.
client_auth = Client(key="api-key", secret="secret-key", no_public=True)
await client_auth.start()
await client_auth.subscribe(params={"channel": "balances"})

while not client.exception_occur and not client_auth.exception_occur:
await asyncio.sleep(6)

try:
# Public/unauthenticated websocket client
client = Client() # only use this one if you don't need private feeds
await client.start()
await client.subscribe(
params={"channel": "ticker", "symbol": ["BTC/USD", "DOT/USD"]}
)
await client.subscribe(
params={"channel": "book", "depth": 25, "symbol": ["BTC/USD"]}
)
# wait because unsubscribing is faster than unsubscribing … (just for that example)
await asyncio.sleep(3)
# print(client.active_public_subscriptions) # to list active subscriptions
await client.unsubscribe(
params={"channel": "ticker", "symbol": ["BTC/USD", "DOT/USD"]}
)
#

# AS default, the authenticated client starts two websocket connections,
# one for authenticated and one for public messages. If there is no need
# for a public connection, it can be disabled using the ``no_public``
# parameter.
client_auth = Client(key="api-key", secret="secret-key", no_public=True)
await client_auth.start()
await client_auth.subscribe(params={"channel": "balances"})

while not client.exception_occur and not client_auth.exception_occur:
await asyncio.sleep(6)
finally:
await client.close()
await client_auth.close()

if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
pass
# The websocket client will send {'event': 'asyncio.CancelledError'}
# via on_message so you can handle the behavior/next actions
# individually within your strategy.
asyncio.run(main())
```
<a name="futuresusage"></a>
Expand Down Expand Up @@ -352,9 +350,10 @@ async def main():
response = await client.request("GET", "/derivatives/api/v3/accounts")
print(response)
finally:
await client.async_close()
await client.close()
asyncio.run(main())
if __name__ == "__main__":
asyncio.run(main())
```
Using FuturesAsyncClient as context manager; This example demonstrates the use
Expand All @@ -370,7 +369,8 @@ async def main():
response = await client.request("GET", "/derivatives/api/v3/accounts")
print(response)
asyncio.run(main())
if __name__ == "__main__":
asyncio.run(main())
```
<a name="futuresws"></a>
Expand All @@ -395,44 +395,44 @@ class Client(FuturesWSClient):
print(event)
async def main():
# Public/unauthenticated websocket connection
client = Client()
await client.start()
try:
# Public/unauthenticated websocket connection
client = Client()
await client.start()
products = ["PI_XBTUSD", "PF_ETHUSD"]
products = ["PI_XBTUSD", "PF_ETHUSD"]
# subscribe to a public websocket feed
await client.subscribe(feed="ticker", products=products)
# await client.subscribe(feed="book", products=products)
# …
# subscribe to a public websocket feed
await client.subscribe(feed="ticker", products=products)
# await client.subscribe(feed="book", products=products)
# …
# unsubscribe from a public websocket feed
# await client.unsubscribe(feed="ticker", products=products)
# unsubscribe from a public websocket feed
# await client.unsubscribe(feed="ticker", products=products)
# Private/authenticated websocket connection (+public)
client_auth = Client(key="key-key", secret="secret-key")
await client_auth.start()
# Private/authenticated websocket connection (+public)
client_auth = Client(key="key-key", secret="secret-key")
await client_auth.start()
# print(client_auth.get_available_private_subscription_feeds())
# print(client_auth.get_available_private_subscription_feeds())
# subscribe to a private/authenticated websocket feed
await client_auth.subscribe(feed="fills")
await client_auth.subscribe(feed="open_positions")
await client_auth.subscribe(feed="open_orders")
# …
# subscribe to a private/authenticated websocket feed
await client_auth.subscribe(feed="fills")
await client_auth.subscribe(feed="open_positions")
await client_auth.subscribe(feed="open_orders")
# …
# unsubscribe from a private/authenticated websocket feed
await client_auth.unsubscribe(feed="fills")
# unsubscribe from a private/authenticated websocket feed
await client_auth.unsubscribe(feed="fills")
while not client.exception_occur and not client_auth.exception_occur:
await asyncio.sleep(6)
while not client.exception_occur and not client_auth.exception_occur:
await asyncio.sleep(6)
finally:
await client.close()
await client_auth.close()
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
# do some exception handling …
pass
asyncio.run(main())
```
---
Expand Down
15 changes: 15 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# python-kraken-sdk usage examples

This directory contains several examples demonstrating how to use the
`python-kraken-sdk`. These examples cover various functionalities such as spot
trading, futures trading, and market data retrieval using both REST and
WebSocket APIs.

Ideal examples of successful running trading algorithms based on the
python-kraken-sdk are listed below:

- https://github.com/btschwertfeger/kraken-infinity-grid
- https://github.com/btschwertfeger/kraken-rebalance-bot

For more detailed and up-to-date usage, refer to the unit tests provided in the
main package.
69 changes: 33 additions & 36 deletions examples/futures_trading_bot_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ async def on_message(self: TradingBot, message: list | dict) -> None:
def save_exit(self: TradingBot, reason: str = "") -> None:
"""Controlled shutdown of the strategy"""
LOG.warning("Save exit triggered, reason: %s", reason)
# some ideas:
# * save the bots data
# * maybe close trades
# * enable dead man's switch
# Some ideas:
# * Save the bots data
# * Close trades
# * Enable dead man's switch
sys.exit(1)


Expand Down Expand Up @@ -129,39 +129,36 @@ async def __main(self: ManagedBot) -> None:
`True` if the websocket connection has some fatal error. This is used to
exit the asyncio loop - but you can also apply your own reconnect rules.
"""
self.__trading_strategy = TradingBot(config=self.__config)
await self.__trading_strategy.start()

await self.__trading_strategy.subscribe(
feed="ticker",
products=self.__config["products"],
)
await self.__trading_strategy.subscribe(
feed="book",
products=self.__config["products"],
)

await self.__trading_strategy.subscribe(feed="fills")
await self.__trading_strategy.subscribe(feed="open_positions")
await self.__trading_strategy.subscribe(feed="open_orders")
await self.__trading_strategy.subscribe(feed="balances")

while not self.__trading_strategy.exception_occur:
try:
# check if the strategy feels good
# maybe send a status update every day
try:
self.__trading_strategy = TradingBot(config=self.__config)
await self.__trading_strategy.start()

await self.__trading_strategy.subscribe(
feed="ticker",
products=self.__config["products"],
)
await self.__trading_strategy.subscribe(
feed="book",
products=self.__config["products"],
)

await self.__trading_strategy.subscribe(feed="fills")
await self.__trading_strategy.subscribe(feed="open_positions")
await self.__trading_strategy.subscribe(feed="open_orders")
await self.__trading_strategy.subscribe(feed="balances")

while not self.__trading_strategy.exception_occur:
# Check if the algorithm feels good
# Send a status update every day via Telegram or Mail
# …
pass

except Exception as exc:
message: str = f"Exception in main: {exc} {traceback.format_exc()}"
LOG.error(message)
self.__trading_strategy.save_exit(reason=message)

await asyncio.sleep(6)
self.__trading_strategy.save_exit(
reason="Left main loop because of exception in strategy.",
)
await asyncio.sleep(6)

except Exception as exc:
LOG.error(message := f"Exception in main: {exc} {traceback.format_exc()}")
self.__trading_strategy.save_exit(reason=message)
finally:
# Close the sessions properly.
await self.__trading_strategy.close()

def __check_credentials(self: ManagedBot) -> bool:
"""Checks the user credentials and the connection to Kraken"""
Expand Down
Loading

0 comments on commit 9afbe2a

Please sign in to comment.