Skip to content

Commit bb5cd7b

Browse files
dev-update-packages (#7)
* edits and test attempts * flexing with tests * refactor(tests): update private API tests and pytest config - Refactor private API tests to use manual server/client creation - Add proper cleanup with try/finally blocks - Ensure server is closed after each test - Set explicit asyncio_default_fixture_loop_scope in pytest config The changes improve test isolation and follow the integration tests pattern. Each test now creates its own server and client instances, making tests more reliable and easier to debug. The pytest configuration update removes deprecation warning by explicitly setting the event loop scope to "function". * chore(lint): enable Ruff unsafe fixes Enable unsafe fixes in Ruff configuration to allow automatic fixing of docstring formatting and other similar issues. This will help maintain consistent code style with less manual intervention. * remomve unused * ruff check finished * preparation for 1st release * removed private client removed multiexchange changed config for pyright * changes for pyright * edits for starting stdio or sse * claude desktop client not working for now * update packages bump version fix client and tests * update packages bump version fix client and tests
1 parent b70158f commit bb5cd7b

File tree

18 files changed

+437
-53
lines changed

18 files changed

+437
-53
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ repos:
99
- id: check-toml
1010

1111
- repo: https://github.com/astral-sh/ruff-pre-commit
12-
rev: v0.11.7
12+
rev: v0.12.10
1313
hooks:
1414
- id: ruff
1515
args: [--fix]
1616
- id: ruff-format
1717

1818
- repo: https://github.com/RobertCraigie/pyright-python
19-
rev: v1.1.400
19+
rev: v1.1.404
2020
hooks:
2121
- id: pyright

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ top of [aiowhitebit](https://github.com/doubledare704/aiowhitebit) library and [
1616
- Caching with disk persistence
1717
- Rate limiting and circuit breaker patterns
1818

19+
### New in aiowhitebit 0.3.0 Integration
20+
21+
- **BookTicker WebSocket Streams**: Real-time best bid/ask price updates
22+
- **Funding History for Futures**: Access historical funding rates for futures markets
23+
- **Enhanced WebSocket Events**: All WebSocket events now include optional metadata (event_time, update_id)
24+
1925
## Quick Start
2026

2127
```bash
@@ -56,6 +62,14 @@ async def main():
5662
orderbook = await client.get_orderbook("BTC_USDT")
5763
print("Order book:", orderbook)
5864

65+
# Get funding history for futures market
66+
funding_history = await client.get_funding_history("BTC_USDT")
67+
print("Funding history:", funding_history)
68+
69+
# Subscribe to BookTicker for real-time best bid/ask
70+
subscription = await client.bookticker_subscribe("BTC_USDT")
71+
print("BookTicker subscription:", subscription)
72+
5973
if __name__ == "__main__":
6074
asyncio.run(main())
6175
```
@@ -93,10 +107,15 @@ if __name__ == "__main__":
93107
- `get_fee(market: str)`: Get trading fees
94108
- `get_server_status()`: Get server status
95109
- `get_asset_status_list()`: Get status of all assets
110+
- `get_funding_history(market: str)`: Get funding rate history for futures markets
96111

97112
### WebSocket API
98113
- `get_last_price(market: str)`: Get real-time price
99114
- `get_market_depth(market: str)`: Get real-time order book
115+
- `bookticker_subscribe(market: str)`: Subscribe to BookTicker stream for best bid/ask prices
116+
- `bookticker_unsubscribe(market: str)`: Unsubscribe from BookTicker stream
117+
- `connect_websocket()`: Connect to WebSocket API
118+
- `disconnect_websocket()`: Disconnect from WebSocket API
100119

101120
### Resources
102121
- `whitebit://markets`: Get all markets information

pyproject.toml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
[build-system]
2-
requires = ["setuptools>=42", "wheel"]
2+
requires = ["setuptools>=68.2.2", "wheel"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "aiowhitebit-mcp"
7-
version = "0.2.7"
7+
version = "0.3.0"
88
description = "MCP server and client for WhiteBit cryptocurrency exchange API"
99
readme = "README.md"
1010
authors = [
@@ -25,20 +25,20 @@ classifiers = [
2525
keywords = ["whitebit", "cryptocurrency", "exchange", "api", "mcp", "claude"]
2626
requires-python = ">=3.10"
2727
dependencies = [
28-
"aiowhitebit==0.2.5",
29-
"fastmcp==2.10.2",
30-
"pydantic>=2.11.7",
31-
"aiohttp>=3.12.13",
28+
"aiowhitebit==0.4.0",
29+
"fastmcp==2.12.5",
30+
"pydantic>=2.12.3",
31+
"aiohttp>=3.13.1",
3232
]
3333

3434
[project.optional-dependencies]
3535
dev = [
36-
"pytest>=8.4.0",
37-
"pytest-asyncio>=1.0.0",
38-
"pytest-cov>=6.2.1",
39-
"ruff>=0.11.13",
40-
"pyright>=1.1.402",
41-
"pre-commit>=4.2.0",
36+
"pytest>=8.4.2",
37+
"pytest-asyncio>=1.2.0",
38+
"pytest-cov>=7.0.0",
39+
"ruff>=0.14.1",
40+
"pyright>=1.1.406",
41+
"pre-commit>=4.3.0",
4242
]
4343

4444
[project.urls]

requirements-dev.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Development dependencies
2-
pytest~=8.4.0
3-
pytest-asyncio~=1.0.0
4-
pytest-cov==6.2.1
5-
ruff==0.11.13
6-
pyright==1.1.402
7-
pre-commit==4.2.0
2+
pytest~=8.4.2
3+
pytest-asyncio~=1.2.0
4+
pytest-cov==7.0.0
5+
ruff==0.14.1
6+
pyright==1.1.406
7+
pre-commit==4.3.0
88

99
# Project dependencies
1010
-r requirements.txt

requirements.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
aiowhitebit==0.2.5
2-
fastmcp==2.10.2
1+
aiowhitebit==0.4.0
2+
fastmcp==2.12.5
33

4-
pydantic~=2.11.7
5-
aiohttp~=3.12.13
4+
pydantic~=2.12.3
5+
aiohttp~=3.13.1
66
setuptools~=80.9.0

setup.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@
1313

1414
# Define development requirements
1515
development_requires = [
16-
"pytest>=8.4.0",
17-
"pytest-asyncio>=1.0.0",
18-
"pytest-cov>=6.2.1",
19-
"ruff>=0.11.13",
20-
"pyright>=1.1.402",
21-
"pre-commit>=4.2.0",
16+
"pytest>=8.4.2",
17+
"pytest-asyncio>=1.2.0",
18+
"pytest-cov>=7.0.0",
19+
"ruff>=0.14.1",
20+
"pyright>=1.1.406",
21+
"pre-commit>=4.3.0",
2222
]
2323

2424
setup(
2525
name="aiowhitebit-mcp",
26-
version="0.2.7",
26+
version="0.3.0",
2727
description="MCP server and client for WhiteBit cryptocurrency exchange API",
2828
long_description=long_description,
2929
long_description_content_type="text/markdown",
@@ -33,10 +33,10 @@
3333
packages=find_packages(where="src"),
3434
package_dir={"": "src"},
3535
install_requires=[
36-
"aiowhitebit==0.2.5",
37-
"fastmcp==2.10.2",
38-
"pydantic>=2.11.7",
39-
"aiohttp>=3.12.13",
36+
"aiowhitebit==0.4.0",
37+
"fastmcp==2.12.5",
38+
"pydantic>=2.12.3",
39+
"aiohttp>=3.13.1",
4040
],
4141
entry_points={
4242
"console_scripts": [

src/aiowhitebit_mcp/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
Provides Message Control Protocol (MCP) server and client for WhiteBit exchange.
44
"""
55

6-
__version__ = "0.2.7"
6+
__version__ = "0.3.0"

src/aiowhitebit_mcp/client.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,33 @@ async def get_market_depth(self, market: str) -> str:
122122
result = await self.client.call_tool("get_market_depth", {"market": {"market": market}})
123123
return self._extract_text(result)
124124

125+
async def get_funding_history(self, market: str) -> str:
126+
"""Get funding rate history for a futures market.
127+
128+
Args:
129+
market: Market pair (e.g., 'BTC_USDT')
130+
"""
131+
result = await self.client.call_tool("get_funding_history", {"market": {"market": market}})
132+
return self._extract_text(result)
133+
134+
async def bookticker_subscribe(self, market: str) -> str:
135+
"""Subscribe to BookTicker stream for a market.
136+
137+
Args:
138+
market: Market pair (e.g., 'BTC_USDT')
139+
"""
140+
result = await self.client.call_tool("bookticker_subscribe", {"market": {"market": market}})
141+
return self._extract_text(result)
142+
143+
async def bookticker_unsubscribe(self, market: str) -> str:
144+
"""Unsubscribe from BookTicker stream for a market.
145+
146+
Args:
147+
market: Market pair (e.g., 'BTC_USDT')
148+
"""
149+
result = await self.client.call_tool("bookticker_unsubscribe", {"market": {"market": market}})
150+
return self._extract_text(result)
151+
125152
def _extract_resource_text(self, response: list[TextResourceContents | BlobResourceContents]) -> str:
126153
"""Extract text from resource response.
127154

src/aiowhitebit_mcp/metrics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class MetricsCollector:
127127
"""Collector for WhiteBit MCP server metrics.
128128
129129
This class collects metrics about the WhiteBit MCP server's performance
130-
and usage, and provides methods for reporting those metrics.
130+
and usage and provides methods for reporting those metrics.
131131
"""
132132

133133
def __init__(self):

src/aiowhitebit_mcp/proxy.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from aiowhitebit.models.public.v4 import (
1111
AssetStatus,
1212
Fee,
13+
FundingHistoryItem,
14+
FundingHistoryResponse,
1315
MarketActivity,
1416
MarketInfo,
1517
Orderbook,
@@ -110,9 +112,8 @@ async def get_server_time(self) -> ServerTime:
110112
time_data = result.model_dump()
111113
logger.debug(f"get_server_time result (using dict): {time_data}")
112114
return result
113-
except Exception as e:
114-
logger.error(f"Error in get_server_time: {e}")
115-
logger.debug(traceback.format_exc())
115+
except Exception:
116+
logger.exception("Error in get_server_time")
116117
return ServerTime(time=1000000000)
117118

118119
@optimized(ttl_seconds=60, rate_limit_name="public") # Server status doesn't change often
@@ -130,9 +131,8 @@ async def get_server_status(self) -> ServerStatus:
130131
logger.debug("Calling get_server_status")
131132
return await self._original_client.get_server_status()
132133

133-
except Exception as e:
134-
logger.error(f"Error in get_server_status: {e}")
135-
logger.debug(traceback.format_exc())
134+
except Exception:
135+
logger.exception("Error in get_server_status")
136136
return ServerStatus(["pong"]) # Return a mock object for testing
137137

138138
@cached(cache_name="market_info", ttl=300, persist=True) # Market info changes infrequently
@@ -203,9 +203,8 @@ async def get_orderbook(self, market: str, limit: int = 100, level: int = 0) ->
203203
bids_count = len(orderbook_data.get("bids", []))
204204
logger.debug(f"get_orderbook result (using dict): {asks_count} asks, {bids_count} bids")
205205
return result
206-
except Exception as e:
207-
logger.error(f"Error in get_orderbook for {market}: {e}")
208-
logger.debug(traceback.format_exc())
206+
except Exception:
207+
logger.exception(f"Error in get_orderbook for {market}:")
209208
return Orderbook(
210209
ticker_id=market, asks=[], bids=[], timestamp=1000000000
211210
) # Return a mock object for testing
@@ -285,6 +284,34 @@ async def get_asset_status_list(self) -> list[AssetStatus]:
285284
logger.debug(traceback.format_exc())
286285
return cast("list[AssetStatus]", [{"name": "BTC", "status": "active"}])
287286

287+
@optimized(ttl_seconds=300, rate_limit_name="public") # Funding history changes infrequently
288+
@circuit_breaker(name="public_v4_get_funding_history", failure_threshold=3, recovery_timeout=30.0, timeout=10.0)
289+
@rate_limited("public")
290+
async def get_funding_history(self, market: str) -> "FundingHistoryResponse":
291+
"""Get funding rate history for a futures market.
292+
293+
Args:
294+
market: Market symbol (e.g., "BTC_USDT")
295+
296+
Returns:
297+
FundingHistoryResponse: List of funding rate history items containing:
298+
- Timestamp of the funding rate
299+
- Funding rate value
300+
301+
Raises:
302+
Exception: If there is an error communicating with the WhiteBit API
303+
"""
304+
try:
305+
logger.debug(f"Calling get_funding_history for {market}")
306+
result = await self._original_client.get_funding_history(market)
307+
logger.debug(f"get_funding_history result: {len(result.result)} items")
308+
return result
309+
except Exception:
310+
logger.exception("Error in get_funding_history: ")
311+
return FundingHistoryResponse(
312+
result=[FundingHistoryItem(timestamp=1000000000, funding_rate="rate")]
313+
) # Return a mock object for testing
314+
288315
async def close(self) -> None:
289316
"""Close the client and release resources.
290317

0 commit comments

Comments
 (0)