Skip to content

Commit 2c6194c

Browse files
authoredFeb 16, 2025··
Resolve "async_close and stop market as deprecated, but no alternative is provided" (#359)
1 parent 4e08e9e commit 2c6194c

17 files changed

+378
-313
lines changed
 

‎CHANGELOG.md

+29-2
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,43 @@
22

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

5-
[Full Changelog](https://github.com/btschwertfeger/python-kraken-sdk/compare/v3.1.3...HEAD)
5+
[Full Changelog](https://github.com/btschwertfeger/python-kraken-sdk/compare/v3.1.5...HEAD)
6+
7+
Uncategorized merged pull requests:
8+
9+
- Update copyright header [\#354](https://github.com/btschwertfeger/python-kraken-sdk/pull/354) ([btschwertfeger](https://github.com/btschwertfeger))
10+
- 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))
11+
- 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))
12+
- Update egress rules in CI [\#349](https://github.com/btschwertfeger/python-kraken-sdk/pull/349) ([btschwertfeger](https://github.com/btschwertfeger))
13+
14+
## [v3.1.5](https://github.com/btschwertfeger/python-kraken-sdk/tree/v3.1.5) (2025-01-29)
15+
16+
[Full Changelog](https://github.com/btschwertfeger/python-kraken-sdk/compare/v3.1.4...v3.1.5)
17+
18+
**Fixed bugs:**
19+
20+
- Resolve "Documentation is somewhat incomplete" [\#348](https://github.com/btschwertfeger/python-kraken-sdk/pull/348) ([btschwertfeger](https://github.com/btschwertfeger))
21+
22+
Uncategorized merged pull requests:
23+
24+
- 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))
25+
- 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))
26+
- 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))
27+
- 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))
28+
29+
## [v3.1.4](https://github.com/btschwertfeger/python-kraken-sdk/tree/v3.1.4) (2025-01-25)
30+
31+
[Full Changelog](https://github.com/btschwertfeger/python-kraken-sdk/compare/v3.1.3...v3.1.4)
632

733
Uncategorized merged pull requests:
834

935
- 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))
1036
- 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))
11-
- Update the documentation [\#339](https://github.com/btschwertfeger/python-kraken-sdk/pull/339) ([btschwertfeger](https://github.com/btschwertfeger))
1237
- 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))
1338
- 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))
1439
- 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))
40+
- Switch to src-layout [\#342](https://github.com/btschwertfeger/python-kraken-sdk/pull/342) ([btschwertfeger](https://github.com/btschwertfeger))
41+
- Update the documentation [\#339](https://github.com/btschwertfeger/python-kraken-sdk/pull/339) ([btschwertfeger](https://github.com/btschwertfeger))
1542

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

‎README.md

+76-76
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ regarding the use of this software.
4747

4848
General:
4949

50-
- command-line interface
51-
- access both public and private, REST and websocket endpoints
52-
- responsive error handling and custom exceptions
53-
- extensive example scripts (see `/examples` and `/tests`)
54-
- tested using the [pytest](https://docs.pytest.org/en/7.3.x/) framework
55-
- releases are permanently archived at [Zenodo](https://zenodo.org/badge/latestdoi/510751854)
50+
- Command-line interface
51+
- Access both public and private, REST and websocket endpoints
52+
- Responsive error handling and custom exceptions
53+
- Extensive example scripts (see `/examples` and `/tests`)
54+
- Tested using the [pytest](https://docs.pytest.org/en/7.3.x/) framework
55+
- Releases are permanently archived at [Zenodo](https://zenodo.org/badge/latestdoi/510751854)
5656

5757
Available Clients:
5858

@@ -199,9 +199,10 @@ async def main():
199199
response = await client.request("POST", "/0/private/Balance")
200200
print(response)
201201
finally:
202-
await client.async_close()
202+
await client.close()
203203

204-
asyncio.run(main())
204+
if __name__ == "__main__":
205+
asyncio.run(main())
205206
```
206207
207208
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.
@@ -215,7 +216,8 @@ async def main():
215216
response = await client.request("POST", "/0/private/Balance")
216217
print(response)
217218

218-
asyncio.run(main())
219+
if __name__ == "__main__":
220+
asyncio.run(main())
219221
```
220222
221223
<a name="spotws"></a>
@@ -272,44 +274,40 @@ class Client(SpotWSClient):
272274
# You can also un-/subscribe here using self.subscribe/self.unsubscribe.
273275

274276
async def main():
275-
276-
# Public/unauthenticated websocket client
277-
client = Client() # only use this one if you don't need private feeds
278-
await client.start()
279-
await client.subscribe(
280-
params={"channel": "ticker", "symbol": ["BTC/USD", "DOT/USD"]}
281-
)
282-
await client.subscribe(
283-
params={"channel": "book", "depth": 25, "symbol": ["BTC/USD"]}
284-
)
285-
# wait because unsubscribing is faster than unsubscribing … (just for that example)
286-
await asyncio.sleep(3)
287-
# print(client.active_public_subscriptions) # to list active subscriptions
288-
await client.unsubscribe(
289-
params={"channel": "ticker", "symbol": ["BTC/USD", "DOT/USD"]}
290-
)
291-
#
292-
293-
# AS default, the authenticated client starts two websocket connections,
294-
# one for authenticated and one for public messages. If there is no need
295-
# for a public connection, it can be disabled using the ``no_public``
296-
# parameter.
297-
client_auth = Client(key="api-key", secret="secret-key", no_public=True)
298-
await client_auth.start()
299-
await client_auth.subscribe(params={"channel": "balances"})
300-
301-
while not client.exception_occur and not client_auth.exception_occur:
302-
await asyncio.sleep(6)
303-
277+
try:
278+
# Public/unauthenticated websocket client
279+
client = Client() # only use this one if you don't need private feeds
280+
await client.start()
281+
await client.subscribe(
282+
params={"channel": "ticker", "symbol": ["BTC/USD", "DOT/USD"]}
283+
)
284+
await client.subscribe(
285+
params={"channel": "book", "depth": 25, "symbol": ["BTC/USD"]}
286+
)
287+
# wait because unsubscribing is faster than unsubscribing … (just for that example)
288+
await asyncio.sleep(3)
289+
# print(client.active_public_subscriptions) # to list active subscriptions
290+
await client.unsubscribe(
291+
params={"channel": "ticker", "symbol": ["BTC/USD", "DOT/USD"]}
292+
)
293+
#
294+
295+
# AS default, the authenticated client starts two websocket connections,
296+
# one for authenticated and one for public messages. If there is no need
297+
# for a public connection, it can be disabled using the ``no_public``
298+
# parameter.
299+
client_auth = Client(key="api-key", secret="secret-key", no_public=True)
300+
await client_auth.start()
301+
await client_auth.subscribe(params={"channel": "balances"})
302+
303+
while not client.exception_occur and not client_auth.exception_occur:
304+
await asyncio.sleep(6)
305+
finally:
306+
await client.close()
307+
await client_auth.close()
304308

305309
if __name__ == "__main__":
306-
try:
307-
asyncio.run(main())
308-
except KeyboardInterrupt:
309-
pass
310-
# The websocket client will send {'event': 'asyncio.CancelledError'}
311-
# via on_message so you can handle the behavior/next actions
312-
# individually within your strategy.
310+
asyncio.run(main())
313311
```
314312
315313
<a name="futuresusage"></a>
@@ -352,9 +350,10 @@ async def main():
352350
response = await client.request("GET", "/derivatives/api/v3/accounts")
353351
print(response)
354352
finally:
355-
await client.async_close()
353+
await client.close()
356354
357-
asyncio.run(main())
355+
if __name__ == "__main__":
356+
asyncio.run(main())
358357
```
359358
360359
Using FuturesAsyncClient as context manager; This example demonstrates the use
@@ -370,7 +369,8 @@ async def main():
370369
response = await client.request("GET", "/derivatives/api/v3/accounts")
371370
print(response)
372371
373-
asyncio.run(main())
372+
if __name__ == "__main__":
373+
asyncio.run(main())
374374
```
375375
376376
<a name="futuresws"></a>
@@ -395,44 +395,44 @@ class Client(FuturesWSClient):
395395
print(event)
396396
397397
async def main():
398-
# Public/unauthenticated websocket connection
399-
client = Client()
400-
await client.start()
398+
try:
399+
# Public/unauthenticated websocket connection
400+
client = Client()
401+
await client.start()
401402
402-
products = ["PI_XBTUSD", "PF_ETHUSD"]
403+
products = ["PI_XBTUSD", "PF_ETHUSD"]
403404
404-
# subscribe to a public websocket feed
405-
await client.subscribe(feed="ticker", products=products)
406-
# await client.subscribe(feed="book", products=products)
407-
# …
405+
# subscribe to a public websocket feed
406+
await client.subscribe(feed="ticker", products=products)
407+
# await client.subscribe(feed="book", products=products)
408+
# …
408409
409-
# unsubscribe from a public websocket feed
410-
# await client.unsubscribe(feed="ticker", products=products)
410+
# unsubscribe from a public websocket feed
411+
# await client.unsubscribe(feed="ticker", products=products)
411412
412-
# Private/authenticated websocket connection (+public)
413-
client_auth = Client(key="key-key", secret="secret-key")
414-
await client_auth.start()
413+
# Private/authenticated websocket connection (+public)
414+
client_auth = Client(key="key-key", secret="secret-key")
415+
await client_auth.start()
415416
416-
# print(client_auth.get_available_private_subscription_feeds())
417+
# print(client_auth.get_available_private_subscription_feeds())
417418
418-
# subscribe to a private/authenticated websocket feed
419-
await client_auth.subscribe(feed="fills")
420-
await client_auth.subscribe(feed="open_positions")
421-
await client_auth.subscribe(feed="open_orders")
422-
# …
419+
# subscribe to a private/authenticated websocket feed
420+
await client_auth.subscribe(feed="fills")
421+
await client_auth.subscribe(feed="open_positions")
422+
await client_auth.subscribe(feed="open_orders")
423+
# …
423424
424-
# unsubscribe from a private/authenticated websocket feed
425-
await client_auth.unsubscribe(feed="fills")
425+
# unsubscribe from a private/authenticated websocket feed
426+
await client_auth.unsubscribe(feed="fills")
426427
427-
while not client.exception_occur and not client_auth.exception_occur:
428-
await asyncio.sleep(6)
428+
while not client.exception_occur and not client_auth.exception_occur:
429+
await asyncio.sleep(6)
430+
finally:
431+
await client.close()
432+
await client_auth.close()
429433
430434
if __name__ == "__main__":
431-
try:
432-
asyncio.run(main())
433-
except KeyboardInterrupt:
434-
# do some exception handling …
435-
pass
435+
asyncio.run(main())
436436
```
437437
438438
---

‎doc/introduction.rst

+6-6
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ Features
6060

6161
General:
6262

63-
- command-line interface
64-
- access both public and private, REST and websocket endpoints
65-
- responsive error handling and custom exceptions
66-
- extensive examples
67-
- tested using the `pytest <https://docs.pytest.org/en/7.3.x/>`_ framework
68-
- releases are permanently archived at `Zenodo <https://zenodo.org/badge/latestdoi/510751854>`_
63+
- Command-line interface
64+
- Access both public and private, REST and websocket endpoints
65+
- Responsive error handling and custom exceptions
66+
- Extensive examples
67+
- Tested using the `pytest <https://docs.pytest.org/en/7.3.x/>`_ framework
68+
- Releases are permanently archived at `Zenodo <https://zenodo.org/badge/latestdoi/510751854>`_
6969

7070
Available Clients:
7171

‎examples/README.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# python-kraken-sdk usage examples
2+
3+
This directory contains several examples demonstrating how to use the
4+
`python-kraken-sdk`. These examples cover various functionalities such as spot
5+
trading, futures trading, and market data retrieval using both REST and
6+
WebSocket APIs.
7+
8+
Ideal examples of successful running trading algorithms based on the
9+
python-kraken-sdk are listed below:
10+
11+
- https://github.com/btschwertfeger/kraken-infinity-grid
12+
- https://github.com/btschwertfeger/kraken-rebalance-bot
13+
14+
For more detailed and up-to-date usage, refer to the unit tests provided in the
15+
main package.

‎examples/futures_trading_bot_template.py

+33-36
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ async def on_message(self: TradingBot, message: list | dict) -> None:
8080
def save_exit(self: TradingBot, reason: str = "") -> None:
8181
"""Controlled shutdown of the strategy"""
8282
LOG.warning("Save exit triggered, reason: %s", reason)
83-
# some ideas:
84-
# * save the bots data
85-
# * maybe close trades
86-
# * enable dead man's switch
83+
# Some ideas:
84+
# * Save the bots data
85+
# * Close trades
86+
# * Enable dead man's switch
8787
sys.exit(1)
8888

8989

@@ -129,39 +129,36 @@ async def __main(self: ManagedBot) -> None:
129129
`True` if the websocket connection has some fatal error. This is used to
130130
exit the asyncio loop - but you can also apply your own reconnect rules.
131131
"""
132-
self.__trading_strategy = TradingBot(config=self.__config)
133-
await self.__trading_strategy.start()
134-
135-
await self.__trading_strategy.subscribe(
136-
feed="ticker",
137-
products=self.__config["products"],
138-
)
139-
await self.__trading_strategy.subscribe(
140-
feed="book",
141-
products=self.__config["products"],
142-
)
143-
144-
await self.__trading_strategy.subscribe(feed="fills")
145-
await self.__trading_strategy.subscribe(feed="open_positions")
146-
await self.__trading_strategy.subscribe(feed="open_orders")
147-
await self.__trading_strategy.subscribe(feed="balances")
148-
149-
while not self.__trading_strategy.exception_occur:
150-
try:
151-
# check if the strategy feels good
152-
# maybe send a status update every day
132+
try:
133+
self.__trading_strategy = TradingBot(config=self.__config)
134+
await self.__trading_strategy.start()
135+
136+
await self.__trading_strategy.subscribe(
137+
feed="ticker",
138+
products=self.__config["products"],
139+
)
140+
await self.__trading_strategy.subscribe(
141+
feed="book",
142+
products=self.__config["products"],
143+
)
144+
145+
await self.__trading_strategy.subscribe(feed="fills")
146+
await self.__trading_strategy.subscribe(feed="open_positions")
147+
await self.__trading_strategy.subscribe(feed="open_orders")
148+
await self.__trading_strategy.subscribe(feed="balances")
149+
150+
while not self.__trading_strategy.exception_occur:
151+
# Check if the algorithm feels good
152+
# Send a status update every day via Telegram or Mail
153153
# …
154-
pass
155-
156-
except Exception as exc:
157-
message: str = f"Exception in main: {exc} {traceback.format_exc()}"
158-
LOG.error(message)
159-
self.__trading_strategy.save_exit(reason=message)
160-
161-
await asyncio.sleep(6)
162-
self.__trading_strategy.save_exit(
163-
reason="Left main loop because of exception in strategy.",
164-
)
154+
await asyncio.sleep(6)
155+
156+
except Exception as exc:
157+
LOG.error(message := f"Exception in main: {exc} {traceback.format_exc()}")
158+
self.__trading_strategy.save_exit(reason=message)
159+
finally:
160+
# Close the sessions properly.
161+
await self.__trading_strategy.close()
165162

166163
def __check_credentials(self: ManagedBot) -> bool:
167164
"""Checks the user credentials and the connection to Kraken"""

0 commit comments

Comments
 (0)
Please sign in to comment.