hyperquant 1.45__tar.gz → 1.47__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.45 → hyperquant-1.47}/PKG-INFO +3 -4
  2. {hyperquant-1.45 → hyperquant-1.47}/pyproject.toml +3 -4
  3. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/auth.py +132 -2
  4. hyperquant-1.47/src/hyperquant/broker/lib/polymarket/ctfAbi.py +721 -0
  5. hyperquant-1.47/src/hyperquant/broker/lib/polymarket/safeAbi.py +1138 -0
  6. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/polymarket.py +400 -54
  7. {hyperquant-1.45 → hyperquant-1.47}/uv.lock +42 -128
  8. {hyperquant-1.45 → hyperquant-1.47}/.gitignore +0 -0
  9. {hyperquant-1.45 → hyperquant-1.47}/README.md +0 -0
  10. {hyperquant-1.45 → hyperquant-1.47}/requirements-dev.lock +0 -0
  11. {hyperquant-1.45 → hyperquant-1.47}/requirements.lock +0 -0
  12. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/__init__.py +0 -0
  13. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/bitget.py +0 -0
  14. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/bitmart.py +0 -0
  15. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/coinw.py +0 -0
  16. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/deepcoin.py +0 -0
  17. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/edgex.py +0 -0
  18. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/hyperliquid.py +0 -0
  19. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/lbank.py +0 -0
  20. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
  21. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/lib/hpstore.py +0 -0
  22. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/lib/hyper_types.py +0 -0
  23. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/lib/util.py +0 -0
  24. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/lighter.py +0 -0
  25. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/apexpro.py +0 -0
  26. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/bitget.py +0 -0
  27. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/bitmart.py +0 -0
  28. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/coinw.py +0 -0
  29. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/deepcoin.py +0 -0
  30. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/edgex.py +0 -0
  31. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/hyperliquid.py +0 -0
  32. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/lbank.py +0 -0
  33. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/lighter.py +0 -0
  34. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/ourbit.py +0 -0
  35. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/models/polymarket.py +0 -0
  36. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/ourbit.py +0 -0
  37. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/broker/ws.py +0 -0
  38. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/core.py +0 -0
  39. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/datavison/_util.py +0 -0
  40. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/datavison/binance.py +0 -0
  41. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/datavison/coinglass.py +0 -0
  42. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/datavison/okx.py +0 -0
  43. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/db.py +0 -0
  44. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/draw.py +0 -0
  45. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/logkit.py +0 -0
  46. {hyperquant-1.45 → hyperquant-1.47}/src/hyperquant/notikit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 1.45
3
+ Version: 1.47
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
@@ -12,16 +12,15 @@ Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: MIT License
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Topic :: Office/Business :: Financial :: Investment
15
- Requires-Python: >=3.11
15
+ Requires-Python: >=3.13
16
16
  Requires-Dist: aiohttp>=3.13
17
+ Requires-Dist: coincurve>=21.0.0
17
18
  Requires-Dist: colorama>=0.4.6
18
19
  Requires-Dist: cryptography>=44.0.2
19
20
  Requires-Dist: duckdb>=1.2.2
20
21
  Requires-Dist: eth-account>=0.10.0
21
- Requires-Dist: lighter-sdk
22
22
  Requires-Dist: numpy>=1.21.0
23
23
  Requires-Dist: pandas>=2.2.3
24
- Requires-Dist: py-clob-client>=0.28.0
25
24
  Requires-Dist: pybotters>=1.10
26
25
  Requires-Dist: pyecharts>=2.0.8
27
26
  Requires-Dist: python-dotenv>=1.2.1
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hyperquant"
3
- version = "1.45"
3
+ version = "1.47"
4
4
  description = "A minimal yet hyper-efficient backtesting framework for quantitative trading"
5
5
  authors = [
6
6
  { name = "MissinA", email = "1421329142@qq.com" }
@@ -14,14 +14,13 @@ dependencies = [
14
14
  "numpy>=1.21.0", # Added numpy as a new dependency
15
15
  "duckdb>=1.2.2",
16
16
  "pybotters>=1.10",
17
- "lighter-sdk",
18
17
  "eth-account>=0.10.0",
19
18
  "web3>=7.14.0",
20
19
  "python-dotenv>=1.2.1",
21
- "py-clob-client>=0.28.0",
20
+ "coincurve>=21.0.0",
22
21
  ]
23
22
  readme = "README.md"
24
- requires-python = ">=3.11"
23
+ requires-python = ">=3.13"
25
24
 
26
25
 
27
26
  keywords = ["quant", "backtesting", "trading", "hyperquant"]
@@ -3,6 +3,7 @@ import hmac
3
3
  import urllib.parse
4
4
  import time
5
5
  import hashlib
6
+ from functools import lru_cache
6
7
  from typing import Any
7
8
  from aiohttp import ClientWebSocketResponse, FormData, JsonPayload
8
9
  from multidict import CIMultiDict
@@ -13,9 +14,9 @@ from urllib.parse import urlencode
13
14
  from datetime import datetime, timezone
14
15
  from eth_account import Account
15
16
  from eth_account.messages import encode_typed_data
17
+ from eth_utils import keccak, to_checksum_address
16
18
  import secrets
17
19
  from random import random
18
- from datetime import datetime, timezone
19
20
 
20
21
 
21
22
  POLY_ADDRESS = "POLY_ADDRESS"
@@ -27,6 +28,53 @@ POLY_PASSPHRASE = "POLY_PASSPHRASE"
27
28
  CLOB_AUTH_DOMAIN = {"name": "ClobAuthDomain", "version": "1"}
28
29
  CLOB_AUTH_MESSAGE = "This message attests that I control the given wallet"
29
30
 
31
+ _PM_DOMAIN_TYPEHASH = keccak(b"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
32
+ _PM_ORDER_TYPEHASH = keccak(
33
+ b"Order(uint256 salt,address maker,address signer,address taker,uint256 tokenId,uint256 makerAmount,uint256 takerAmount,uint256 expiration,uint256 nonce,uint256 feeRateBps,uint8 side,uint8 signatureType)"
34
+ )
35
+ _PM_DOMAIN_NAME_HASH = keccak(text="Polymarket CTF Exchange")
36
+ _PM_DOMAIN_VERSION_HASH = keccak(text="1")
37
+
38
+
39
+ def _int_to_32(val: int) -> bytes:
40
+ return int(val).to_bytes(32, "big", signed=False)
41
+
42
+
43
+ def _addr_to_32(addr: str) -> bytes:
44
+ # Normalize to checksum to match eth signing behavior
45
+ normalized = to_checksum_address(addr)
46
+ return int(normalized, 16).to_bytes(32, "big", signed=False)
47
+
48
+
49
+ @lru_cache(maxsize=32)
50
+ def _pm_domain_separator(chain_id: int, verifying_contract: str) -> bytes:
51
+ return keccak(
52
+ _PM_DOMAIN_TYPEHASH
53
+ + _PM_DOMAIN_NAME_HASH
54
+ + _PM_DOMAIN_VERSION_HASH
55
+ + _int_to_32(chain_id)
56
+ + _addr_to_32(verifying_contract),
57
+ )
58
+
59
+
60
+ def _pm_order_hash(message: dict[str, Any]) -> bytes:
61
+ """Manually hash the Polymarket Order struct for EIP-712."""
62
+ return keccak(
63
+ _PM_ORDER_TYPEHASH
64
+ + _int_to_32(message["salt"])
65
+ + _addr_to_32(message["maker"])
66
+ + _addr_to_32(message["signer"])
67
+ + _addr_to_32(message["taker"])
68
+ + _int_to_32(message["tokenId"])
69
+ + _int_to_32(message["makerAmount"])
70
+ + _int_to_32(message["takerAmount"])
71
+ + _int_to_32(message["expiration"])
72
+ + _int_to_32(message["nonce"])
73
+ + _int_to_32(message["feeRateBps"])
74
+ + _int_to_32(message["side"])
75
+ + _int_to_32(message["signatureType"]),
76
+ )
77
+
30
78
 
31
79
  def md5_hex(s: str) -> str:
32
80
  return hashlib.md5(s.encode("utf-8")).hexdigest()
@@ -484,7 +532,7 @@ class Auth:
484
532
  return args
485
533
 
486
534
  @staticmethod
487
- def polymarket(args: tuple[str, URL], kwargs: dict[str, Any]) -> tuple[str, URL]:
535
+ def polymarket_back(args: tuple[str, URL], kwargs: dict[str, Any]) -> tuple[str, URL]:
488
536
  method: str = args[0].upper()
489
537
  url: URL = args[1]
490
538
  headers: CIMultiDict = kwargs["headers"]
@@ -679,6 +727,11 @@ class Auth:
679
727
 
680
728
  return args
681
729
 
730
+ @staticmethod
731
+ def polymarket(args: tuple[str, URL], kwargs: dict[str, Any]) -> tuple[str, URL]:
732
+ """Alias kept for backward compatibility with pybotters host registration."""
733
+ return Auth.polymarket_back(args, kwargs)
734
+
682
735
  # --------------------------
683
736
  # Polymarket order signing
684
737
  # --------------------------
@@ -792,6 +845,83 @@ class Auth:
792
845
  }
793
846
  return out
794
847
 
848
+ @staticmethod
849
+ def sign_polymarket_order2(
850
+ *,
851
+ private_key: str,
852
+ chain_id: int,
853
+ exchange_address: str,
854
+ order: dict[str, Any],
855
+ ) -> dict[str, Any]:
856
+ """
857
+ Fast EIP-712 signer using coincurve + manual hashing.
858
+
859
+ Avoids heavy typed-data helpers by caching type/domain hashes and
860
+ signing the final digest directly.
861
+ """
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
866
+
867
+ try:
868
+ now_ts = datetime.now().replace(tzinfo=timezone.utc).timestamp()
869
+ generated_salt = round(now_ts * random())
870
+ except Exception:
871
+ generated_salt = int(time.time())
872
+
873
+ side = int(order.get("side", 0))
874
+ signature_type = int(order.get("signatureType", 1))
875
+ signer_addr = order.get("signer") or Account.from_key(private_key).address
876
+ taker_addr = order.get("taker") or "0x0000000000000000000000000000000000000000"
877
+
878
+ message = {
879
+ "salt": int(order.get("salt") or generated_salt),
880
+ "maker": order.get("maker"),
881
+ "signer": signer_addr,
882
+ "taker": taker_addr,
883
+ "tokenId": int(order["tokenId"]),
884
+ "makerAmount": int(order["makerAmount"]),
885
+ "takerAmount": int(order["takerAmount"]),
886
+ "expiration": int(order.get("expiration", 0)),
887
+ "nonce": int(order.get("nonce", 0)),
888
+ "feeRateBps": int(order.get("feeRateBps", 0)),
889
+ "side": side,
890
+ "signatureType": signature_type,
891
+ }
892
+
893
+ # Normalize addresses for hashing
894
+ exchange_addr = to_checksum_address(exchange_address)
895
+ message["maker"] = to_checksum_address(message["maker"])
896
+ message["signer"] = to_checksum_address(message["signer"])
897
+ message["taker"] = to_checksum_address(message["taker"])
898
+
899
+ domain_sep = _pm_domain_separator(int(chain_id), exchange_addr)
900
+ msg_hash = _pm_order_hash(message)
901
+ typed_hash = keccak(b"\x19\x01" + domain_sep + msg_hash)
902
+
903
+ 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)
905
+ r, s, rec_id = sig65[:32], sig65[32:64], sig65[64]
906
+ v = rec_id + 27 # align with eth_account v
907
+ signature = "0x" + (r + s + bytes([v])).hex()
908
+
909
+ return {
910
+ "salt": int(message["salt"]),
911
+ "maker": message["maker"],
912
+ "signer": message["signer"],
913
+ "taker": message["taker"],
914
+ "tokenId": str(message["tokenId"]),
915
+ "makerAmount": str(message["makerAmount"]),
916
+ "takerAmount": str(message["takerAmount"]),
917
+ "expiration": str(message["expiration"]),
918
+ "nonce": str(message["nonce"]),
919
+ "feeRateBps": str(message["feeRateBps"]),
920
+ "side": "BUY" if side == 0 else "SELL",
921
+ "signatureType": int(signature_type),
922
+ "signature": signature,
923
+ }
924
+
795
925
  pybotters.auth.Hosts.items["futures.ourbit.com"] = pybotters.auth.Item(
796
926
  "ourbit", Auth.ourbit
797
927
  )