hyperquant 1.48__tar.gz → 1.51__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 (46) hide show
  1. {hyperquant-1.48 → hyperquant-1.51}/PKG-INFO +1 -1
  2. {hyperquant-1.48 → hyperquant-1.51}/pyproject.toml +1 -1
  3. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/auth.py +11 -6
  4. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/polymarket.py +4 -0
  5. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/polymarket.py +48 -12
  6. {hyperquant-1.48 → hyperquant-1.51}/uv.lock +1 -1
  7. {hyperquant-1.48 → hyperquant-1.51}/.gitignore +0 -0
  8. {hyperquant-1.48 → hyperquant-1.51}/README.md +0 -0
  9. {hyperquant-1.48 → hyperquant-1.51}/requirements-dev.lock +0 -0
  10. {hyperquant-1.48 → hyperquant-1.51}/requirements.lock +0 -0
  11. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/__init__.py +0 -0
  12. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/bitget.py +0 -0
  13. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/bitmart.py +0 -0
  14. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/coinw.py +0 -0
  15. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/deepcoin.py +0 -0
  16. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/edgex.py +0 -0
  17. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/hyperliquid.py +0 -0
  18. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/lbank.py +0 -0
  19. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
  20. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/lib/hpstore.py +0 -0
  21. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/lib/hyper_types.py +0 -0
  22. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/lib/polymarket/ctfAbi.py +0 -0
  23. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/lib/polymarket/safeAbi.py +0 -0
  24. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/lib/util.py +0 -0
  25. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/lighter.py +0 -0
  26. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/apexpro.py +0 -0
  27. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/bitget.py +0 -0
  28. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/bitmart.py +0 -0
  29. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/coinw.py +0 -0
  30. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/deepcoin.py +0 -0
  31. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/edgex.py +0 -0
  32. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/hyperliquid.py +0 -0
  33. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/lbank.py +0 -0
  34. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/lighter.py +0 -0
  35. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/models/ourbit.py +0 -0
  36. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/ourbit.py +0 -0
  37. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/broker/ws.py +0 -0
  38. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/core.py +0 -0
  39. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/datavison/_util.py +0 -0
  40. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/datavison/binance.py +0 -0
  41. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/datavison/coinglass.py +0 -0
  42. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/datavison/okx.py +0 -0
  43. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/db.py +0 -0
  44. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/draw.py +0 -0
  45. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/logkit.py +0 -0
  46. {hyperquant-1.48 → hyperquant-1.51}/src/hyperquant/notikit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 1.48
3
+ Version: 1.51
4
4
  Summary: A minimal yet hyper-efficient backtesting framework for quantitative trading
5
5
  Project-URL: Homepage, https://github.com/yourusername/hyperquant
6
6
  Project-URL: Issues, https://github.com/yourusername/hyperquant/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hyperquant"
3
- version = "1.48"
3
+ version = "1.51"
4
4
  description = "A minimal yet hyper-efficient backtesting framework for quantitative trading"
5
5
  authors = [
6
6
  { name = "MissinA", email = "1421329142@qq.com" }
@@ -13,11 +13,18 @@ import json as pyjson
13
13
  from urllib.parse import urlencode
14
14
  from datetime import datetime, timezone
15
15
  from eth_account import Account
16
- from eth_account.messages import encode_typed_data
16
+ try:
17
+ from eth_account.messages import encode_typed_data
18
+ except ImportError:
19
+ from eth_account.messages import encode_structured_data as encode_typed_data
17
20
  from eth_utils import keccak, to_checksum_address
18
21
  import secrets
19
22
  from random import random
20
23
 
24
+ try:
25
+ from coincurve import PrivateKey as _CoincurvePrivateKey
26
+ except Exception: # pragma: no cover - optional dependency
27
+ _CoincurvePrivateKey = None
21
28
 
22
29
  POLY_ADDRESS = "POLY_ADDRESS"
23
30
  POLY_SIGNATURE = "POLY_SIGNATURE"
@@ -859,10 +866,8 @@ class Auth:
859
866
  Avoids heavy typed-data helpers by caching type/domain hashes and
860
867
  signing the final digest directly.
861
868
  """
862
- try:
863
- from coincurve import PrivateKey
864
- except Exception as e: # pragma: no cover - optional dependency
865
- raise RuntimeError("coincurve is required for sign_polymarket_order2") from e
869
+ if _CoincurvePrivateKey is None:
870
+ raise RuntimeError("coincurve is required for sign_polymarket_order2")
866
871
 
867
872
  try:
868
873
  now_ts = datetime.now().replace(tzinfo=timezone.utc).timestamp()
@@ -901,7 +906,7 @@ class Auth:
901
906
  typed_hash = keccak(b"\x19\x01" + domain_sep + msg_hash)
902
907
 
903
908
  pk_bytes = bytes.fromhex(private_key[2:] if private_key.startswith("0x") else private_key)
904
- sig65 = PrivateKey(pk_bytes).sign_recoverable(typed_hash, hasher=None)
909
+ sig65 = _CoincurvePrivateKey(pk_bytes).sign_recoverable(typed_hash, hasher=None)
905
910
  r, s, rec_id = sig65[:32], sig65[32:64], sig65[64]
906
911
  v = rec_id + 27 # align with eth_account v
907
912
  signature = "0x" + (r + s + bytes([v])).hex()
@@ -185,6 +185,7 @@ class Position(DataStore):
185
185
  "side": side,
186
186
  "size": new_size,
187
187
  "totalBought": total_bought,
188
+ "totalAvgPrice": avg_price,
188
189
  "avgPrice": avg_price,
189
190
  "ts": int(time.time() * 1000),
190
191
  }
@@ -876,6 +877,9 @@ class PolymarketDataStore(DataStoreCollection):
876
877
 
877
878
  # 🧮 累计交易信息
878
879
  "totalBought": 123, # 累计买入数量(含历史)
880
+
881
+ # 新增字段
882
+ "totalAvgPrice": 123, # 累计买入均价(含历史)
879
883
 
880
884
  # ⚙️ 状态标志
881
885
  "redeemable": True, # 是否可赎回(True 表示市场已结算且你是赢家,可提取 USDC)
@@ -29,6 +29,7 @@ DEFAULT_BASE_SLUG = "btc-updown-15m"
29
29
  HOURLY_BITCOIN_BASE_SLUG = "bitcoin-up-or-down"
30
30
  DEFAULT_INTERVAL = 15 * 60
31
31
  DEFAULT_WINDOW = 8
32
+ TICK_SIZE_CACHE_TTL_SECS = 300
32
33
  API_NAME = "polymarket"
33
34
  END_CURSOR = "LTE="
34
35
  USDC_CONTRACT = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"
@@ -254,6 +255,7 @@ class Polymarket:
254
255
  self._ws_public_ready = asyncio.Event()
255
256
  self._ws_personal: pybotters.ws.WebSocketApp | None = None
256
257
  self.auth = False
258
+ self._tick_size_cache: dict[str, tuple[str, float]] = {}
257
259
 
258
260
  self._ensure_session_entry(private_key=private_key, funder=funder, chain_id=chain_id)
259
261
 
@@ -272,6 +274,25 @@ class Polymarket:
272
274
  self._ws_public = None
273
275
  self._ws_public_ready.clear()
274
276
 
277
+ async def prewarm_connections(
278
+ self,
279
+ endpoints: Sequence[str] | None = None,
280
+ timeout: float = 1.0,
281
+ ) -> None:
282
+ """Pre-warm HTTP connections to reduce first-request latency."""
283
+ if endpoints is None:
284
+ endpoints = ("/ok", "/time")
285
+ session = getattr(self.client, "_session", None)
286
+ if session is None:
287
+ raise RuntimeError("pybotters client session missing")
288
+ for endpoint in endpoints:
289
+ url = f"{self.rest_api}{endpoint}"
290
+ try:
291
+ async with session.get(url, timeout=timeout) as resp:
292
+ await resp.read()
293
+ except Exception:
294
+ continue
295
+
275
296
  # ------------------------------------------------------------------
276
297
  # Store helpers
277
298
 
@@ -1011,9 +1032,12 @@ class Polymarket:
1011
1032
  request_path = path
1012
1033
  url = f"{self.rest_api}{request_path}"
1013
1034
  payload_obj = dict(body) if isinstance(body, dict) else body
1014
- serialized = (
1015
- str(payload_obj).replace("'", '"') if payload_obj is not None else ""
1016
- )
1035
+ if payload_obj is None:
1036
+ serialized = ""
1037
+ elif isinstance(payload_obj, str):
1038
+ serialized = payload_obj
1039
+ else:
1040
+ serialized = json.dumps(payload_obj, separators=(",", ":"), ensure_ascii=False)
1017
1041
  secret_bytes = base64.urlsafe_b64decode(api_secret)
1018
1042
  msg = f"{ts}{method}{request_path}{serialized}"
1019
1043
  sig = hmac.new(secret_bytes, msg.encode("utf-8"), hashlib.sha256).digest()
@@ -1247,18 +1271,24 @@ class Polymarket:
1247
1271
  async def _resolve_tick_size(self, token_id: str, tick_size: str | float | None) -> str:
1248
1272
  if tick_size is not None:
1249
1273
  return str(tick_size)
1274
+ cached = self._tick_size_cache.get(token_id)
1275
+ now = time.time()
1276
+ if cached and (now - cached[1]) < TICK_SIZE_CACHE_TTL_SECS:
1277
+ return cached[0]
1250
1278
  tick_resp = await self.get_tick_size(token_id)
1251
1279
  if isinstance(tick_resp, dict):
1252
- return str(tick_resp.get("minimum_tick_size") or tick_resp.get("tick_size") or "0.01")
1253
- return str(tick_resp)
1280
+ resolved = str(
1281
+ tick_resp.get("minimum_tick_size") or tick_resp.get("tick_size") or "0.01"
1282
+ )
1283
+ else:
1284
+ resolved = str(tick_resp)
1285
+ self._tick_size_cache[token_id] = (resolved, now)
1286
+ return resolved
1254
1287
 
1255
1288
  async def _resolve_fee_rate(self, token_id: str, fee_rate_bps: int | None) -> int:
1256
1289
  if fee_rate_bps is not None:
1257
1290
  return int(fee_rate_bps)
1258
- fee_resp = await self.get_fee_rate(token_id)
1259
- if isinstance(fee_resp, dict):
1260
- return int(fee_resp.get("base_fee", 0))
1261
- return int(fee_resp or 0)
1291
+ return 0
1262
1292
 
1263
1293
 
1264
1294
  async def _signed_request_via_session(
@@ -1297,9 +1327,12 @@ class Polymarket:
1297
1327
  payload_obj = dict(body)
1298
1328
  else:
1299
1329
  payload_obj = body
1300
- serialized = (
1301
- str(payload_obj).replace("'", '"') if payload_obj is not None else ""
1302
- )
1330
+ if payload_obj is None:
1331
+ serialized = ""
1332
+ elif isinstance(payload_obj, str):
1333
+ serialized = payload_obj
1334
+ else:
1335
+ serialized = json.dumps(payload_obj, separators=(",", ":"), ensure_ascii=False)
1303
1336
  msg = f"{ts}{method}{request_path}{serialized}"
1304
1337
  sig = hmac.new(secret_bytes, msg.encode("utf-8"), hashlib.sha256).digest()
1305
1338
  sign_b64 = base64.urlsafe_b64encode(sig).decode("utf-8")
@@ -2129,6 +2162,9 @@ class Polymarket:
2129
2162
  hourly_match = await _try_slug(hourly_slug)
2130
2163
  if hourly_match:
2131
2164
  return hourly_match
2165
+
2166
+ if '5m' in base_slug:
2167
+ interval = 5 * 60
2132
2168
 
2133
2169
  now_ts = int(datetime.now(UTC).timestamp())
2134
2170
  base_ts = (now_ts // interval) * interval
@@ -694,7 +694,7 @@ wheels = [
694
694
 
695
695
  [[package]]
696
696
  name = "hyperquant"
697
- version = "1.47"
697
+ version = "1.5"
698
698
  source = { editable = "." }
699
699
  dependencies = [
700
700
  { name = "aiohttp" },
File without changes
File without changes
File without changes