typed-hyperliquid 1.2.2__tar.gz → 1.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. {typed_hyperliquid-1.2.2/src/typed_hyperliquid.egg-info → typed_hyperliquid-1.2.3}/PKG-INFO +1 -1
  2. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/pyproject.toml +1 -1
  3. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/__init__.py +1 -1
  4. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/core/__init__.pyi +2 -1
  5. typed_hyperliquid-1.2.3/src/hyperliquid/core/exc.py +15 -0
  6. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_fills_by_time.py +20 -6
  7. typed_hyperliquid-1.2.3/src/hyperliquid/info/pagination.py +94 -0
  8. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/funding_history.py +18 -6
  9. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/user_funding.py +24 -6
  10. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/user_non_funding_ledger_updates.py +19 -6
  11. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3/src/typed_hyperliquid.egg-info}/PKG-INFO +1 -1
  12. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/typed_hyperliquid.egg-info/SOURCES.txt +1 -0
  13. typed_hyperliquid-1.2.2/src/hyperliquid/core/exc.py +0 -1
  14. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/LICENSE +0 -0
  15. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/README.md +0 -0
  16. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/setup.cfg +0 -0
  17. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/core/__init__.py +0 -0
  18. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/core/util.py +0 -0
  19. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/core/validation.py +0 -0
  20. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/core/ws/__init__.py +0 -0
  21. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/core/ws/client.py +0 -0
  22. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/__init__.py +0 -0
  23. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/agent_enable_dex_abstraction.py +0 -0
  24. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/agent_set_abstraction.py +0 -0
  25. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/approve_agent.py +0 -0
  26. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/approve_builder_fee.py +0 -0
  27. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/cancel.py +0 -0
  28. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/cancel_by_cloid.py +0 -0
  29. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/core/__init__.py +0 -0
  30. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/core/mixin.py +0 -0
  31. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/core/signing.py +0 -0
  32. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/modify_order.py +0 -0
  33. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/modify_orders.py +0 -0
  34. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/noop.py +0 -0
  35. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/order.py +0 -0
  36. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/reserve_request_weight.py +0 -0
  37. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/schedule_cancel.py +0 -0
  38. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/send_asset.py +0 -0
  39. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/send_to_evm_with_data.py +0 -0
  40. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/spot_transfer.py +0 -0
  41. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/staking_deposit.py +0 -0
  42. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/staking_withdraw.py +0 -0
  43. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/token_delegate.py +0 -0
  44. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/twap_cancel.py +0 -0
  45. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/twap_order.py +0 -0
  46. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/update_isolated_margin.py +0 -0
  47. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/update_leverage.py +0 -0
  48. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/usd_class_transfer.py +0 -0
  49. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/usdc_transfer.py +0 -0
  50. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/user_dex_abstraction.py +0 -0
  51. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/user_set_abstraction.py +0 -0
  52. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/validator_l1_stream.py +0 -0
  53. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/vault_transfer.py +0 -0
  54. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/exchange/withdraw3.py +0 -0
  55. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/__init__.py +0 -0
  56. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/core.py +0 -0
  57. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/__init__.py +0 -0
  58. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/aligned_quote_token_info.py +0 -0
  59. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/all_borrow_lend_reserve_states.py +0 -0
  60. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/all_mids.py +0 -0
  61. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/borrow_lend_reserve_state.py +0 -0
  62. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/borrow_lend_user_state.py +0 -0
  63. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/builder_fee_approved.py +0 -0
  64. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/candle_snapshot.py +0 -0
  65. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/frontend_open_orders.py +0 -0
  66. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/l2_book.py +0 -0
  67. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/open_orders.py +0 -0
  68. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/order_status.py +0 -0
  69. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/staking_delegations.py +0 -0
  70. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/staking_history.py +0 -0
  71. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/staking_rewards.py +0 -0
  72. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/staking_summary.py +0 -0
  73. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/sub_accounts.py +0 -0
  74. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_abstraction.py +0 -0
  75. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_dex_abstraction.py +0 -0
  76. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_fees.py +0 -0
  77. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_fills.py +0 -0
  78. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_historical_orders.py +0 -0
  79. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_portfolio.py +0 -0
  80. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_rate_limit.py +0 -0
  81. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_referral.py +0 -0
  82. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_role.py +0 -0
  83. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_twap_slice_fills.py +0 -0
  84. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/user_vault_equities.py +0 -0
  85. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/methods/vault_details.py +0 -0
  86. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/__init__.py +0 -0
  87. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/active_asset_data.py +0 -0
  88. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/all_perp_metas.py +0 -0
  89. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/clearinghouse_state.py +0 -0
  90. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/perp_annotation.py +0 -0
  91. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/perp_categories.py +0 -0
  92. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/perp_deploy_auction_status.py +0 -0
  93. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/perp_dex_limits.py +0 -0
  94. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/perp_dex_status.py +0 -0
  95. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/perp_dexs.py +0 -0
  96. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/perp_meta.py +0 -0
  97. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/perp_meta_and_asset_ctxs.py +0 -0
  98. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/perps_at_open_interest_cap.py +0 -0
  99. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/perps/predicted_fundings.py +0 -0
  100. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/spot/__init__.py +0 -0
  101. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/spot/spot_clearinghouse_state.py +0 -0
  102. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/spot/spot_deploy_state.py +0 -0
  103. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/spot/spot_meta.py +0 -0
  104. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/spot/spot_meta_and_asset_ctxs.py +0 -0
  105. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/spot/spot_pair_deploy_auction_status.py +0 -0
  106. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/info/spot/token_details.py +0 -0
  107. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/py.typed +0 -0
  108. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/__init__.py +0 -0
  109. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/active_asset_ctx.py +0 -0
  110. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/active_asset_data.py +0 -0
  111. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/all_dexs_asset_ctxs.py +0 -0
  112. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/all_mids.py +0 -0
  113. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/bbo.py +0 -0
  114. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/candle.py +0 -0
  115. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/clearinghouse_state.py +0 -0
  116. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/core.py +0 -0
  117. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/l2_book.py +0 -0
  118. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/notification.py +0 -0
  119. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/open_orders.py +0 -0
  120. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/order_updates.py +0 -0
  121. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/trades.py +0 -0
  122. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/twap_states.py +0 -0
  123. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/user_events.py +0 -0
  124. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/user_fills.py +0 -0
  125. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/user_fundings.py +0 -0
  126. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/user_non_funding_ledger_updates.py +0 -0
  127. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/user_twap_history.py +0 -0
  128. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/user_twap_slice_fills.py +0 -0
  129. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/hyperliquid/streams/web_data3.py +0 -0
  130. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/typed_hyperliquid.egg-info/dependency_links.txt +0 -0
  131. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/typed_hyperliquid.egg-info/requires.txt +0 -0
  132. {typed_hyperliquid-1.2.2 → typed_hyperliquid-1.2.3}/src/typed_hyperliquid.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typed-hyperliquid
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: A fully typed, validated async client for the Hyperliquid API.
5
5
  Author-email: Marcel Claramunt <marcel@tribulnation.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "typed-hyperliquid"
7
- version = "1.2.2"
7
+ version = "1.2.3"
8
8
  authors = [
9
9
  {name="Marcel Claramunt", email="marcel@tribulnation.com"}
10
10
  ]
@@ -7,7 +7,7 @@ from typed_core.exceptions import (
7
7
  ApiError, BadRequest, AuthError, RateLimited, LogicError,
8
8
  )
9
9
  from typed_core.http import HttpClient
10
- from .core import HYPERLIQUID_MAINNET, HYPERLIQUID_TESTNET
10
+ from .core import HYPERLIQUID_MAINNET, HYPERLIQUID_TESTNET, PaginationError
11
11
  from .core.ws import SocketClient
12
12
  from .info import Info
13
13
  from .exchange import Exchange
@@ -1,7 +1,7 @@
1
1
  from .ws import SocketClient
2
2
  from .util import HYPERLIQUID_MAINNET, HYPERLIQUID_TESTNET, timestamp
3
3
  from .validation import TypedDict
4
- from .exc import Error, ApiError, AuthError, NetworkError, RateLimited, ValidationError, LogicError
4
+ from .exc import Error, ApiError, AuthError, NetworkError, RateLimited, ValidationError, LogicError, PaginationError
5
5
 
6
6
  __all__ = [
7
7
  'SocketClient',
@@ -16,4 +16,5 @@ __all__ = [
16
16
  'RateLimited',
17
17
  'ValidationError',
18
18
  'LogicError',
19
+ 'PaginationError',
19
20
  ]
@@ -0,0 +1,15 @@
1
+ from typed_core import Error, ApiError, AuthError, NetworkError, RateLimited, ValidationError, LogicError
2
+
3
+ class PaginationError(Error):
4
+ """A paginated sweep could not continue without silently losing entries.
5
+
6
+ Raised by the `*_paged` info helpers when the upstream endpoint cannot be
7
+ walked safely: either the order of entries sharing a millisecond changed
8
+ between requests, or a single millisecond holds a whole page of entries, in
9
+ which case the endpoint has no way to express the rest of that millisecond.
10
+
11
+ Stopping is deliberate. The alternative is to skip past the millisecond, which
12
+ drops entries with no error and no gap indicator.
13
+ """
14
+ def __str__(self):
15
+ return super().__str__()
@@ -2,6 +2,10 @@ from typing_extensions import Literal, NotRequired, AsyncIterable
2
2
  from hyperliquid.core import TypedDict
3
3
  import pydantic
4
4
  from hyperliquid.info.core import InfoMixin
5
+ from hyperliquid.info.pagination import paginate
6
+
7
+ PAGE_SIZE = 2000
8
+ """Maximum number of fills `user_fills_by_time` returns per call."""
5
9
 
6
10
  class UserFill(TypedDict):
7
11
  coin: str
@@ -75,18 +79,28 @@ class UserFillsByTime(InfoMixin):
75
79
  A single call to `user_fills_by_time` returns at most 2000 fills, so prefer this
76
80
  whenever the requested range may hold more.
77
81
 
82
+ Many fills share a millisecond, so pages are advanced to the last timestamp seen
83
+ rather than past it, and the re-fetched entries are dropped by position. `tid` is
84
+ not used as a key: every `Spot Dust Conversion` fill carries the sentinel `tid: 0`.
85
+
78
86
  Args:
79
87
  user: Account address.
80
88
  start_time: Start time in milliseconds, inclusive.
81
89
  end_time: End time in milliseconds, inclusive.
82
90
  aggregate_by_time: Aggregate partial fills in the same block.
83
91
 
92
+ Raises:
93
+ PaginationError: The range cannot be walked without losing fills.
94
+
84
95
  References:
85
96
  - [Hyperliquid API docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time)
86
97
  """
87
- while end_time is None or start_time < end_time:
88
- fills = await self.user_fills_by_time(user, start_time, end_time=end_time, aggregate_by_time=aggregate_by_time)
89
- if not fills:
90
- break
91
- yield fills
92
- start_time = max(entry['time'] for entry in fills) + 1
98
+ async def fetch(cursor: int) -> list[UserFill]:
99
+ """Fetch one page of fills starting at `cursor`."""
100
+ return await self.user_fills_by_time(
101
+ user, cursor, end_time=end_time, aggregate_by_time=aggregate_by_time,
102
+ )
103
+ async for page in paginate(
104
+ fetch, start_time=start_time, end_time=end_time, page_size=PAGE_SIZE,
105
+ ):
106
+ yield page
@@ -0,0 +1,94 @@
1
+ """Time-cursor pagination shared by the `*_paged` info helpers.
2
+
3
+ Hyperliquid's time-ranged info endpoints return the *oldest* entries of
4
+ `[start_time, end_time]`, capped at a per-endpoint page size, so walking a long
5
+ range means re-issuing the request with the cursor moved forward.
6
+
7
+ Timestamps are milliseconds and are not unique: a single millisecond routinely
8
+ holds dozens of entries. Moving the cursor to `last_time + 1` therefore drops
9
+ every entry that shares the last millisecond of a truncated page, silently and
10
+ permanently. The cursor is instead moved to `last_time`, which re-fetches that
11
+ millisecond in full, and the already-yielded prefix is removed.
12
+
13
+ The overlap is removed *by position*, not by an id or a content hash, because
14
+ neither is trustworthy:
15
+
16
+ - `tid` is not unique for fills. Every `Spot Dust Conversion` fill carries the
17
+ sentinel `tid: 0`, so keying on it collapses unrelated fills.
18
+ - Funding entries carry no id at all, and for ledger updates `(time, hash)` is
19
+ explicitly not a primary key: one transaction can emit several deltas.
20
+ - Content hashing would additionally collapse records that are legitimately
21
+ identical, which nothing in the payloads rules out.
22
+
23
+ Position is safe because the endpoints return entries in ascending time order,
24
+ `start_time` is inclusive, and the relative order of entries sharing a
25
+ millisecond is stable across requests. The entries already yielded for the
26
+ cursor millisecond are therefore exactly the prefix of the next response, and
27
+ that prefix is verified on every page rather than assumed.
28
+
29
+ Callers must pass the endpoint's real page cap as `page_size`. It is the signal
30
+ that separates "this page is short because the range is exhausted" from "this
31
+ page is short because it was cut", and declaring it larger than the true cap
32
+ would reintroduce silent loss.
33
+ """
34
+ from typing_extensions import Any, AsyncIterable, Awaitable, Callable, Mapping, TypeVar
35
+ from hyperliquid.core import PaginationError
36
+
37
+ Entry = TypeVar('Entry', bound=Mapping[str, Any])
38
+
39
+ async def paginate(
40
+ fetch: Callable[[int], Awaitable[list[Entry]]], *,
41
+ start_time: int, end_time: int | None, page_size: int,
42
+ ) -> AsyncIterable[list[Entry]]:
43
+ """Walk a time-ranged Hyperliquid endpoint, yielding every entry exactly once.
44
+
45
+ Args:
46
+ fetch: Fetch one page starting at the given timestamp, inclusive. It must
47
+ apply the same `end_time` on every call.
48
+ start_time: Start time in milliseconds, inclusive.
49
+ end_time: End time in milliseconds, inclusive.
50
+ page_size: Maximum number of entries the endpoint returns per call.
51
+
52
+ Raises:
53
+ PaginationError: The upstream order of entries sharing a millisecond changed
54
+ mid-sweep, or a single millisecond fills a whole page, so the rest of that
55
+ millisecond is unreachable and continuing would drop it.
56
+
57
+ References:
58
+ - [Hyperliquid API docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint)
59
+ """
60
+ cursor = start_time
61
+ overlap: list[Entry] = []
62
+ while end_time is None or cursor <= end_time:
63
+ page = await fetch(cursor)
64
+ if not page:
65
+ return
66
+ if page[:len(overlap)] != overlap:
67
+ raise PaginationError(
68
+ f'Hyperliquid returned different entries for timestamp {cursor} than it '
69
+ 'did on the previous page. Pagination relies on the order of entries '
70
+ 'sharing a timestamp being stable across requests; it was not, so the '
71
+ 'sweep stopped instead of dropping or duplicating entries.'
72
+ )
73
+ fresh = page[len(overlap):]
74
+ if fresh:
75
+ yield fresh
76
+ last_time = max(entry['time'] for entry in page)
77
+ if last_time > cursor:
78
+ cursor = last_time
79
+ overlap = [entry for entry in page if entry['time'] == last_time]
80
+ elif len(page) >= page_size:
81
+ # Every entry of a full page shares `cursor`, so the page was truncated
82
+ # inside that millisecond. The endpoint pages by time only, so the rest of
83
+ # it is unreachable and moving on would drop it.
84
+ raise PaginationError(
85
+ f'Timestamp {cursor} holds at least {len(page)} entries, filling a whole '
86
+ 'page. Hyperliquid pages by time only, so the rest of that millisecond '
87
+ 'cannot be requested, and the sweep stopped instead of skipping it. '
88
+ f'Resume from {cursor + 1} to continue while accepting that loss.'
89
+ )
90
+ else:
91
+ # A page shorter than `page_size` was not truncated, so every entry at
92
+ # `cursor` is already yielded and the millisecond can be stepped over.
93
+ cursor += 1
94
+ overlap = []
@@ -2,6 +2,10 @@ from typing_extensions import AsyncIterable
2
2
  from hyperliquid.core import TypedDict
3
3
  import pydantic
4
4
  from hyperliquid.info.core import InfoMixin
5
+ from hyperliquid.info.pagination import paginate
6
+
7
+ PAGE_SIZE = 500
8
+ """Maximum number of entries `funding_history` returns per call."""
5
9
 
6
10
  class FundingHistoryEntry(TypedDict):
7
11
  coin: str
@@ -44,17 +48,25 @@ class FundingHistory(InfoMixin):
44
48
  A single call to `funding_history` returns at most 500 entries, so prefer this
45
49
  whenever the requested range may hold more.
46
50
 
51
+ Pages are advanced to the last timestamp seen rather than past it, and the
52
+ re-fetched entries are dropped by position, so entries sharing a millisecond
53
+ are never skipped at a page boundary.
54
+
47
55
  Args:
48
56
  coin: Coin, e.g. "ETH".
49
57
  start_time: Start time in milliseconds, inclusive.
50
58
  end_time: End time in milliseconds, inclusive.
51
59
 
60
+ Raises:
61
+ PaginationError: The range cannot be walked without losing entries.
62
+
52
63
  References:
53
64
  - [Hyperliquid API docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-historical-funding-rates)
54
65
  """
55
- while end_time is None or start_time < end_time:
56
- fundings = await self.funding_history(coin, start_time, end_time=end_time)
57
- if not fundings:
58
- break
59
- yield fundings
60
- start_time = max(entry['time'] for entry in fundings) + 1
66
+ async def fetch(cursor: int) -> list[FundingHistoryEntry]:
67
+ """Fetch one page of funding rates starting at `cursor`."""
68
+ return await self.funding_history(coin, cursor, end_time=end_time)
69
+ async for page in paginate(
70
+ fetch, start_time=start_time, end_time=end_time, page_size=PAGE_SIZE,
71
+ ):
72
+ yield page
@@ -2,6 +2,10 @@ from typing_extensions import AsyncIterable, Literal
2
2
  from hyperliquid.core import TypedDict
3
3
  import pydantic
4
4
  from hyperliquid.info.core import InfoMixin
5
+ from hyperliquid.info.pagination import paginate
6
+
7
+ PAGE_SIZE = 500
8
+ """Maximum number of entries `user_funding` returns per call."""
5
9
 
6
10
  class FundingDelta(TypedDict):
7
11
  type: Literal['funding']
@@ -54,14 +58,28 @@ class UserFunding(InfoMixin):
54
58
  A single call to `user_funding` returns at most 500 entries, so prefer this
55
59
  whenever the requested range may hold more.
56
60
 
61
+ Every open position is funded on the same millisecond, so pages are advanced to
62
+ the last timestamp seen rather than past it, and the re-fetched entries are
63
+ dropped by position. Funding entries carry no unique id, so no key-based
64
+ deduplication is possible.
65
+
57
66
  Args:
58
67
  user: Account address.
59
68
  start_time: Start time in milliseconds, inclusive.
60
69
  end_time: End time in milliseconds, inclusive.
70
+
71
+ Raises:
72
+ PaginationError: The range cannot be walked without losing entries. Notably,
73
+ an account holding 500 or more funded positions cannot be swept at all,
74
+ since one funding millisecond then fills a whole page.
75
+
76
+ References:
77
+ - [Hyperliquid API docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-a-users-funding-history-or-non-funding-ledger-updates)
61
78
  """
62
- while end_time is None or start_time < end_time:
63
- fundings = await self.user_funding(user, start_time, end_time=end_time)
64
- if not fundings:
65
- break
66
- yield fundings
67
- start_time = max(entry['time'] for entry in fundings) + 1
79
+ async def fetch(cursor: int) -> list[UserFundingEntry]:
80
+ """Fetch one page of funding entries starting at `cursor`."""
81
+ return await self.user_funding(user, cursor, end_time=end_time)
82
+ async for page in paginate(
83
+ fetch, start_time=start_time, end_time=end_time, page_size=PAGE_SIZE,
84
+ ):
85
+ yield page
@@ -4,6 +4,10 @@ from decimal import Decimal
4
4
  from hyperliquid.core import TypedDict
5
5
  import pydantic
6
6
  from hyperliquid.info.core import InfoMixin
7
+ from hyperliquid.info.pagination import paginate
8
+
9
+ PAGE_SIZE = 2000
10
+ """Maximum number of entries `user_non_funding_ledger_updates` returns per call."""
7
11
 
8
12
  class DepositDelta(TypedDict):
9
13
  """Funds deposited into the account."""
@@ -317,17 +321,26 @@ class UserNonFundingLedgerUpdates(InfoMixin):
317
321
  the rest, so any account with more history is truncated without an error. Prefer this
318
322
  over `user_non_funding_ledger_updates` whenever complete history matters.
319
323
 
324
+ One transaction can emit several deltas on the same millisecond, so pages are
325
+ advanced to the last timestamp seen rather than past it, and the re-fetched
326
+ entries are dropped by position. `(time, hash)` is not used as a key because it
327
+ is not unique, and hashing the body would collapse identical deltas.
328
+
320
329
  Args:
321
330
  user: Account address.
322
331
  start_time: Start time in milliseconds, inclusive.
323
332
  end_time: End time in milliseconds, inclusive.
324
333
 
334
+ Raises:
335
+ PaginationError: The range cannot be walked without losing entries.
336
+
325
337
  References:
326
338
  - [Hyperliquid API docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-a-users-funding-history-or-non-funding-ledger-updates)
327
339
  """
328
- while end_time is None or start_time < end_time:
329
- updates = await self.user_non_funding_ledger_updates(user, start_time, end_time=end_time)
330
- if not updates:
331
- break
332
- yield updates
333
- start_time = max(entry['time'] for entry in updates) + 1
340
+ async def fetch(cursor: int) -> UserNonFundingLedgerUpdatesResponse:
341
+ """Fetch one page of ledger updates starting at `cursor`."""
342
+ return await self.user_non_funding_ledger_updates(user, cursor, end_time=end_time)
343
+ async for page in paginate(
344
+ fetch, start_time=start_time, end_time=end_time, page_size=PAGE_SIZE,
345
+ ):
346
+ yield page
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typed-hyperliquid
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: A fully typed, validated async client for the Hyperliquid API.
5
5
  Author-email: Marcel Claramunt <marcel@tribulnation.com>
6
6
  License-Expression: MIT
@@ -45,6 +45,7 @@ src/hyperliquid/exchange/core/mixin.py
45
45
  src/hyperliquid/exchange/core/signing.py
46
46
  src/hyperliquid/info/__init__.py
47
47
  src/hyperliquid/info/core.py
48
+ src/hyperliquid/info/pagination.py
48
49
  src/hyperliquid/info/methods/__init__.py
49
50
  src/hyperliquid/info/methods/aligned_quote_token_info.py
50
51
  src/hyperliquid/info/methods/all_borrow_lend_reserve_states.py
@@ -1 +0,0 @@
1
- from typed_core import Error, ApiError, AuthError, NetworkError, RateLimited, ValidationError, LogicError