hyperquant 1.48__tar.gz → 1.49__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.
- {hyperquant-1.48 → hyperquant-1.49}/PKG-INFO +1 -1
- {hyperquant-1.48 → hyperquant-1.49}/pyproject.toml +1 -1
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/auth.py +11 -6
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/polymarket.py +45 -12
- {hyperquant-1.48 → hyperquant-1.49}/uv.lock +1 -1
- {hyperquant-1.48 → hyperquant-1.49}/.gitignore +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/README.md +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/requirements-dev.lock +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/requirements.lock +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/__init__.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/bitget.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/bitmart.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/coinw.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/deepcoin.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/edgex.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/lbank.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/lib/hpstore.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/lib/hyper_types.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/lib/polymarket/ctfAbi.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/lib/polymarket/safeAbi.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/lib/util.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/lighter.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/apexpro.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/bitget.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/bitmart.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/coinw.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/deepcoin.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/edgex.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/hyperliquid.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/lbank.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/lighter.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/ourbit.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/models/polymarket.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/ourbit.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/broker/ws.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/core.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/db.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/draw.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/logkit.py +0 -0
- {hyperquant-1.48 → hyperquant-1.49}/src/hyperquant/notikit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperquant
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.49
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
863
|
-
|
|
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 =
|
|
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()
|
|
@@ -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
|
-
|
|
1015
|
-
|
|
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
|
-
|
|
1253
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1301
|
-
|
|
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")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|