hyperquant 1.44__tar.gz → 1.46__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.44 → hyperquant-1.46}/PKG-INFO +3 -2
- {hyperquant-1.44 → hyperquant-1.46}/pyproject.toml +3 -2
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/auth.py +132 -2
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/polymarket.py +106 -50
- {hyperquant-1.44 → hyperquant-1.46}/uv.lock +42 -2
- {hyperquant-1.44 → hyperquant-1.46}/.gitignore +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/README.md +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/requirements-dev.lock +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/requirements.lock +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/__init__.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/bitget.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/bitmart.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/coinw.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/deepcoin.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/edgex.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/lbank.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/lib/hpstore.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/lib/hyper_types.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/lib/util.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/lighter.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/apexpro.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/bitget.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/bitmart.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/coinw.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/deepcoin.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/edgex.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/hyperliquid.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/lbank.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/lighter.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/ourbit.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/models/polymarket.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/ourbit.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/broker/ws.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/core.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/db.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/draw.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/src/hyperquant/logkit.py +0 -0
- {hyperquant-1.44 → hyperquant-1.46}/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.46
|
|
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,7 +13,8 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
15
15
|
Requires-Python: >=3.11
|
|
16
|
-
Requires-Dist: aiohttp>=3.
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "hyperquant"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.46"
|
|
4
4
|
description = "A minimal yet hyper-efficient backtesting framework for quantitative trading"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "MissinA", email = "1421329142@qq.com" }
|
|
@@ -9,7 +9,7 @@ dependencies = [
|
|
|
9
9
|
"pyecharts>=2.0.8",
|
|
10
10
|
"pandas>=2.2.3",
|
|
11
11
|
"colorama>=0.4.6",
|
|
12
|
-
"aiohttp>=3.
|
|
12
|
+
"aiohttp>=3.13",
|
|
13
13
|
"cryptography>=44.0.2",
|
|
14
14
|
"numpy>=1.21.0", # Added numpy as a new dependency
|
|
15
15
|
"duckdb>=1.2.2",
|
|
@@ -19,6 +19,7 @@ dependencies = [
|
|
|
19
19
|
"web3>=7.14.0",
|
|
20
20
|
"python-dotenv>=1.2.1",
|
|
21
21
|
"py-clob-client>=0.28.0",
|
|
22
|
+
"coincurve>=21.0.0",
|
|
22
23
|
]
|
|
23
24
|
readme = "README.md"
|
|
24
25
|
requires-python = ">=3.11"
|
|
@@ -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
|
|
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
|
)
|
|
@@ -66,7 +66,7 @@ CONDITIONAL_TOKENS_ABI = [
|
|
|
66
66
|
}
|
|
67
67
|
]
|
|
68
68
|
DEFAULT_POLYGON_RPCS = (
|
|
69
|
-
"https://polygon.llamarpc.com",
|
|
69
|
+
# "https://polygon.llamarpc.com",
|
|
70
70
|
"https://polygon-rpc.com",
|
|
71
71
|
"https://rpc.ankr.com/polygon",
|
|
72
72
|
)
|
|
@@ -931,10 +931,23 @@ class Polymarket:
|
|
|
931
931
|
raise RuntimeError("Polymarket private key not configured in apis.json")
|
|
932
932
|
if not str(private_key).startswith("0x"):
|
|
933
933
|
private_key = f"0x{private_key}"
|
|
934
|
+
try:
|
|
935
|
+
# Normalize cached session key to avoid repeated normalization work
|
|
936
|
+
if session and isinstance(apis, dict):
|
|
937
|
+
apis[API_NAME][0] = private_key
|
|
938
|
+
except Exception:
|
|
939
|
+
pass
|
|
940
|
+
|
|
941
|
+
cache_key = (private_key, self.funder)
|
|
942
|
+
cached = getattr(self, "_signing_ctx_cache", None)
|
|
943
|
+
if cached and cached.get("key") == cache_key:
|
|
944
|
+
return cached["ctx"]
|
|
934
945
|
|
|
935
946
|
signer_addr = _A.from_key(private_key).address
|
|
936
947
|
maker_addr = self.funder or signer_addr
|
|
937
|
-
|
|
948
|
+
ctx = (private_key, maker_addr, signer_addr)
|
|
949
|
+
self._signing_ctx_cache = {"key": cache_key, "ctx": ctx}
|
|
950
|
+
return ctx
|
|
938
951
|
|
|
939
952
|
async def _build_signed_order(
|
|
940
953
|
self,
|
|
@@ -983,24 +996,45 @@ class Polymarket:
|
|
|
983
996
|
side_flag = 0 if side == "BUY" else 1
|
|
984
997
|
sig_type = int(self.signature_type)
|
|
985
998
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
999
|
+
try:
|
|
1000
|
+
return Auth.sign_polymarket_order2(
|
|
1001
|
+
private_key=private_key,
|
|
1002
|
+
chain_id=self.chain_id,
|
|
1003
|
+
exchange_address=contract["exchange"],
|
|
1004
|
+
order={
|
|
1005
|
+
"maker": maker_addr,
|
|
1006
|
+
"signer": signer_addr,
|
|
1007
|
+
"taker": taker or ZERO_ADDRESS,
|
|
1008
|
+
"tokenId": str(token_id),
|
|
1009
|
+
"makerAmount": int(maker_amount),
|
|
1010
|
+
"takerAmount": int(taker_amount),
|
|
1011
|
+
"expiration": int(expiration or 0),
|
|
1012
|
+
"nonce": int(nonce or 0),
|
|
1013
|
+
"feeRateBps": int(fee_bps or 0),
|
|
1014
|
+
"side": side_flag,
|
|
1015
|
+
"signatureType": sig_type,
|
|
1016
|
+
},
|
|
1017
|
+
)
|
|
1018
|
+
except RuntimeError:
|
|
1019
|
+
# Fallback when coincurve is unavailable
|
|
1020
|
+
return Auth.sign_polymarket_order(
|
|
1021
|
+
private_key=private_key,
|
|
1022
|
+
chain_id=self.chain_id,
|
|
1023
|
+
exchange_address=contract["exchange"],
|
|
1024
|
+
order={
|
|
1025
|
+
"maker": maker_addr,
|
|
1026
|
+
"signer": signer_addr,
|
|
1027
|
+
"taker": taker or ZERO_ADDRESS,
|
|
1028
|
+
"tokenId": str(token_id),
|
|
1029
|
+
"makerAmount": int(maker_amount),
|
|
1030
|
+
"takerAmount": int(taker_amount),
|
|
1031
|
+
"expiration": int(expiration or 0),
|
|
1032
|
+
"nonce": int(nonce or 0),
|
|
1033
|
+
"feeRateBps": int(fee_bps or 0),
|
|
1034
|
+
"side": side_flag,
|
|
1035
|
+
"signatureType": sig_type,
|
|
1036
|
+
},
|
|
1037
|
+
)
|
|
1004
1038
|
|
|
1005
1039
|
async def _build_signed_market_order(
|
|
1006
1040
|
self,
|
|
@@ -1053,24 +1087,45 @@ class Polymarket:
|
|
|
1053
1087
|
side_flag = 0 if side == "BUY" else 1
|
|
1054
1088
|
sig_type = int(self.signature_type)
|
|
1055
1089
|
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1090
|
+
try:
|
|
1091
|
+
return Auth.sign_polymarket_order2(
|
|
1092
|
+
private_key=private_key,
|
|
1093
|
+
chain_id=self.chain_id,
|
|
1094
|
+
exchange_address=contract["exchange"],
|
|
1095
|
+
order={
|
|
1096
|
+
"maker": maker_addr,
|
|
1097
|
+
"signer": signer_addr,
|
|
1098
|
+
"taker": taker or ZERO_ADDRESS,
|
|
1099
|
+
"tokenId": str(token_id),
|
|
1100
|
+
"makerAmount": int(maker_amount),
|
|
1101
|
+
"takerAmount": int(taker_amount),
|
|
1102
|
+
"expiration": 0,
|
|
1103
|
+
"nonce": int(nonce or 0),
|
|
1104
|
+
"feeRateBps": int(fee_bps or 0),
|
|
1105
|
+
"side": side_flag,
|
|
1106
|
+
"signatureType": sig_type,
|
|
1107
|
+
},
|
|
1108
|
+
)
|
|
1109
|
+
except RuntimeError:
|
|
1110
|
+
# Fallback when coincurve is unavailable
|
|
1111
|
+
return Auth.sign_polymarket_order(
|
|
1112
|
+
private_key=private_key,
|
|
1113
|
+
chain_id=self.chain_id,
|
|
1114
|
+
exchange_address=contract["exchange"],
|
|
1115
|
+
order={
|
|
1116
|
+
"maker": maker_addr,
|
|
1117
|
+
"signer": signer_addr,
|
|
1118
|
+
"taker": taker or ZERO_ADDRESS,
|
|
1119
|
+
"tokenId": str(token_id),
|
|
1120
|
+
"makerAmount": int(maker_amount),
|
|
1121
|
+
"takerAmount": int(taker_amount),
|
|
1122
|
+
"expiration": 0,
|
|
1123
|
+
"nonce": int(nonce or 0),
|
|
1124
|
+
"feeRateBps": int(fee_bps or 0),
|
|
1125
|
+
"side": side_flag,
|
|
1126
|
+
"signatureType": sig_type,
|
|
1127
|
+
},
|
|
1128
|
+
)
|
|
1074
1129
|
|
|
1075
1130
|
async def _resolve_tick_size(self, token_id: str, tick_size: str | float | None) -> str:
|
|
1076
1131
|
if tick_size is not None:
|
|
@@ -1107,9 +1162,16 @@ class Polymarket:
|
|
|
1107
1162
|
api_secret = creds.get("api_secret")
|
|
1108
1163
|
api_passphrase = creds.get("api_passphrase")
|
|
1109
1164
|
|
|
1110
|
-
|
|
1111
|
-
private_key
|
|
1112
|
-
|
|
1165
|
+
# Reuse signing context cache
|
|
1166
|
+
private_key, _, signer_addr = self._get_signing_context()
|
|
1167
|
+
|
|
1168
|
+
cache_key = (api_key, api_secret, api_passphrase, private_key)
|
|
1169
|
+
cached = getattr(self, "_rest_sign_cache", None)
|
|
1170
|
+
if cached and cached.get("key") == cache_key:
|
|
1171
|
+
secret_bytes = cached["secret"]
|
|
1172
|
+
else:
|
|
1173
|
+
secret_bytes = base64.urlsafe_b64decode(api_secret)
|
|
1174
|
+
self._rest_sign_cache = {"key": cache_key, "secret": secret_bytes}
|
|
1113
1175
|
|
|
1114
1176
|
ts = int(time.time())
|
|
1115
1177
|
request_path = path
|
|
@@ -1121,13 +1183,12 @@ class Polymarket:
|
|
|
1121
1183
|
serialized = (
|
|
1122
1184
|
str(payload_obj).replace("'", '"') if payload_obj is not None else ""
|
|
1123
1185
|
)
|
|
1124
|
-
secret_bytes = base64.urlsafe_b64decode(api_secret)
|
|
1125
1186
|
msg = f"{ts}{method}{request_path}{serialized}"
|
|
1126
1187
|
sig = hmac.new(secret_bytes, msg.encode("utf-8"), hashlib.sha256).digest()
|
|
1127
1188
|
sign_b64 = base64.urlsafe_b64encode(sig).decode("utf-8")
|
|
1128
1189
|
|
|
1129
1190
|
headers = {
|
|
1130
|
-
"POLY_ADDRESS":
|
|
1191
|
+
"POLY_ADDRESS": signer_addr,
|
|
1131
1192
|
"POLY_SIGNATURE": sign_b64,
|
|
1132
1193
|
"POLY_TIMESTAMP": str(ts),
|
|
1133
1194
|
"POLY_API_KEY": api_key,
|
|
@@ -1594,7 +1655,8 @@ class Polymarket:
|
|
|
1594
1655
|
try:
|
|
1595
1656
|
acct = _A.from_key(private_key)
|
|
1596
1657
|
sender = acct.address
|
|
1597
|
-
nonce
|
|
1658
|
+
# 使用 pending 避免重复使用已在 mempool 的 nonce 触发 replacement underpriced
|
|
1659
|
+
nonce = w3.eth.get_transaction_count(sender, "pending")
|
|
1598
1660
|
gas_price_chain = w3.eth.gas_price
|
|
1599
1661
|
except Exception as exc:
|
|
1600
1662
|
raise RuntimeError("获取 sender nonce/gas_price 失败") from exc
|
|
@@ -1691,12 +1753,6 @@ class Polymarket:
|
|
|
1691
1753
|
if hourly_match:
|
|
1692
1754
|
return hourly_match
|
|
1693
1755
|
|
|
1694
|
-
# 1小时市场等特殊 slug(比如 bitcoin-up-or-down-november-18-10am-et)
|
|
1695
|
-
# 直接传入完整 slug 即可,不再拼接时间戳
|
|
1696
|
-
direct_match = await _try_slug(base_slug)
|
|
1697
|
-
if direct_match:
|
|
1698
|
-
return direct_match
|
|
1699
|
-
|
|
1700
1756
|
now_ts = int(datetime.now(UTC).timestamp())
|
|
1701
1757
|
base_ts = (now_ts // interval) * interval
|
|
1702
1758
|
|
|
@@ -462,6 +462,44 @@ wheels = [
|
|
|
462
462
|
{ url = "https://files.pythonhosted.org/packages/40/40/f259e2bf986d39717427bc12baa8189cd43f9675e81cd3bcab639e593614/ckzg-2.1.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:df66d2be54d91f74aded4ceb71e7b1f789e2636a3015f438904a22ec9de750f1", size = 101018 },
|
|
463
463
|
]
|
|
464
464
|
|
|
465
|
+
[[package]]
|
|
466
|
+
name = "coincurve"
|
|
467
|
+
version = "21.0.0"
|
|
468
|
+
source = { registry = "https://pypi.org/simple" }
|
|
469
|
+
sdist = { url = "https://files.pythonhosted.org/packages/6f/a2/f2a38eb05b747ed3e54e1be33be339d4a14c1f5cc6a6e2b342b5e8160d51/coincurve-21.0.0.tar.gz", hash = "sha256:8b37ce4265a82bebf0e796e21a769e56fdbf8420411ccbe3fafee4ed75b6a6e5", size = 128986 }
|
|
470
|
+
wheels = [
|
|
471
|
+
{ url = "https://files.pythonhosted.org/packages/19/5a/9aaa096d830b5d1386335759e73038a5352f8cd670efed55d242f92d0bce/coincurve-21.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:65ec42cab9c60d587fb6275c71f0ebc580625c377a894c4818fb2a2b583a184b", size = 1390936 },
|
|
472
|
+
{ url = "https://files.pythonhosted.org/packages/8a/e4/37dd30ed171432e32c075a03237915c0e69a5a524a807f380d910b276a2a/coincurve-21.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5828cd08eab928db899238874d1aab12fa1236f30fe095a3b7e26a5fc81df0a3", size = 1384762 },
|
|
473
|
+
{ url = "https://files.pythonhosted.org/packages/09/fd/78870f4babed4981feb9b97b3189aec0f01a1a24be8a1ac04807dc68aa0d/coincurve-21.0.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de1cac75182de9f71ce41415faafcaf788303e21cbd0188064e268d61625e5", size = 1597025 },
|
|
474
|
+
{ url = "https://files.pythonhosted.org/packages/9d/fb/b4850f8afc941655ef4c1204b50f9e21f841c6a64aa83a559277ca305cbd/coincurve-21.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07cda058d9394bea30d57a92fdc18ee3ca6b5bc8ef776a479a2ffec917105836", size = 1603987 },
|
|
475
|
+
{ url = "https://files.pythonhosted.org/packages/9d/b7/df41dbcec3f70e383fa024949ce8956ff3b2a1b9eac330fba18c2115eece/coincurve-21.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9070804d7c71badfe4f0bf19b728cfe7c70c12e733938ead6b1db37920b745c0", size = 1604762 },
|
|
476
|
+
{ url = "https://files.pythonhosted.org/packages/70/84/1b2437fc22590073eefb3da0418648b2d5b768951ef851822be8c164b998/coincurve-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:669ab5db393637824b226de058bb7ea0cb9a0236e1842d7b22f74d4a8a1f1ff1", size = 1637469 },
|
|
477
|
+
{ url = "https://files.pythonhosted.org/packages/9c/4b/893763b3964b3044071a450fdada4c5024dc16f7644258a7bd06cf41e2ba/coincurve-21.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3bcd538af097b3914ec3cb654262e72e224f95f2e9c1eb7fbd75d843ae4e528e", size = 1601177 },
|
|
478
|
+
{ url = "https://files.pythonhosted.org/packages/77/45/d2f42159cb461f5b070ff848244f1b83f3ea9ec3a3435368f9be33e4e276/coincurve-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45b6a5e6b5536e1f46f729829d99ce1f8f847308d339e8880fe7fa1646935c10", size = 1635597 },
|
|
479
|
+
{ url = "https://files.pythonhosted.org/packages/9a/7c/528cff0aa17acd6c64b10c4bd8bb0adb6c96420be4e170916150537f36f6/coincurve-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:87597cf30dfc05fa74218810776efacf8816813ab9fa6ea1490f94e9f8b15e77", size = 1328626 },
|
|
480
|
+
{ url = "https://files.pythonhosted.org/packages/cb/91/845b00da05b132e7bb3f3d1c4c301c195b39a9dc8f9962295ff340a27f18/coincurve-21.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:b992d1b1dac85d7f542d9acbcf245667438839484d7f2b032fd032256bcd778e", size = 1325365 },
|
|
481
|
+
{ url = "https://files.pythonhosted.org/packages/f3/61/a2d9e109f99b6f5e65e653ac998b0944c5b82c568ac142fcbb381a4803be/coincurve-21.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f60ad56113f08e8c540bb89f4f35f44d434311433195ffff22893ccfa335070c", size = 1391948 },
|
|
482
|
+
{ url = "https://files.pythonhosted.org/packages/24/5a/2da75ee00a722ef1fa068ada3bc34c564595ead86fef573434e2f0cb0a5c/coincurve-21.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1cb1cd19fb0be22e68ecb60ad950b41f18b9b02eebeffaac9391dc31f74f08f2", size = 1384958 },
|
|
483
|
+
{ url = "https://files.pythonhosted.org/packages/dc/50/6bf0bf7e8a9a9dd419ecc1e479dcb9fbfe657029276ad703806a25a2bef2/coincurve-21.0.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05d7e255a697b3475d7ae7640d3bdef3d5bc98ce9ce08dd387f780696606c33b", size = 1606576 },
|
|
484
|
+
{ url = "https://files.pythonhosted.org/packages/bd/ab/9e89908fdd09ad522938085587aaa821b022f4def16c286c5580cfc85811/coincurve-21.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a366c314df7217e3357bb8c7d2cda540b0bce180705f7a0ce2d1d9e28f62ad4", size = 1613642 },
|
|
485
|
+
{ url = "https://files.pythonhosted.org/packages/b7/75/050b6fd08978de85a7b480f0f220ab6a30967c0910119f3096a8dd40befc/coincurve-21.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b04778b75339c6e46deb9ae3bcfc2250fbe48d1324153e4310fc4996e135715", size = 1616974 },
|
|
486
|
+
{ url = "https://files.pythonhosted.org/packages/d7/62/2740ba0cafebf45708633635fecadcbe582d7a3ed1ce8b4637921feceaf8/coincurve-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8efcbdcd50cc219989a2662e6c6552f455efc000a15dd6ab3ebf4f9b187f41a3", size = 1644133 },
|
|
487
|
+
{ url = "https://files.pythonhosted.org/packages/94/14/1f27c3048c4084fa85ef65f42a4ca631f2b184336e6d9446fecec20e0a7f/coincurve-21.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6df44b4e3b7acdc1453ade52a52e3f8a5b53ecdd5a06bd200f1ec4b4e250f7d9", size = 1619918 },
|
|
488
|
+
{ url = "https://files.pythonhosted.org/packages/ca/22/7ec3ec4c8e7764daa25767d6674cb5741ea2d9b39ff758e9918d22a4b49b/coincurve-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bcc0831f07cb75b91c35c13b1362e7b9dc76c376b27d01ff577bec52005e22a8", size = 1645797 },
|
|
489
|
+
{ url = "https://files.pythonhosted.org/packages/fb/60/87982b7499943ab12605df7b14f6001fff331aca0881b260682461e2309d/coincurve-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:5dd7b66b83b143f3ad3861a68fc0279167a0bae44fe3931547400b7a200e90b1", size = 1329255 },
|
|
490
|
+
{ url = "https://files.pythonhosted.org/packages/62/c0/65b60b371579570931daca8a3f67debfc1482908b8ed03432297274a27da/coincurve-21.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:78dbe439e8cb22389956a4f2f2312813b4bd0531a0b691d4f8e868c7b366555d", size = 1325973 },
|
|
491
|
+
{ url = "https://files.pythonhosted.org/packages/b3/40/cce55adaec37a588eb24b67da8eb68926546458e12ed2c4c2a21deb93d4c/coincurve-21.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9df5ceb5de603b9caf270629996710cf5ed1d43346887bc3895a11258644b65b", size = 1391762 },
|
|
492
|
+
{ url = "https://files.pythonhosted.org/packages/ca/7a/628a30281d246ce98aea56592e0c8e79b03a93ee8b85d688db3388130c2d/coincurve-21.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:154467858d23c48f9e5ab380433bc2625027b50617400e2984cc16f5799ab601", size = 1384921 },
|
|
493
|
+
{ url = "https://files.pythonhosted.org/packages/61/cc/719c5da31e6ba07e438abcf962f7a365eb69a06a0621ca4f2a484f344e09/coincurve-21.0.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f57f07c44d14d939bed289cdeaba4acb986bba9f729a796b6a341eab1661eedc", size = 1606559 },
|
|
494
|
+
{ url = "https://files.pythonhosted.org/packages/b2/ee/dd14237013d732e7fc3248c0c33a1d36b88b5378dfa3e624a50a23fb6f19/coincurve-21.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fb03e3a388a93d31ed56a442bdec7983ea404490e21e12af76fb1dbf097082a", size = 1613684 },
|
|
495
|
+
{ url = "https://files.pythonhosted.org/packages/f0/05/eaa7f36a03376ced1c19e0cb563341cc83fe48f5734b2effe8f16d0ee0ab/coincurve-21.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d09ba4fd9d26b00b06645fcd768c5ad44832a1fa847ebe8fb44970d3204c3cb7", size = 1617001 },
|
|
496
|
+
{ url = "https://files.pythonhosted.org/packages/39/32/fc75f1dd914ac95eb2704425c7ca1a9f509f982e15d05e0ca895b9e6ea9c/coincurve-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1a1e7ee73bc1b3bcf14c7b0d1f44e6485785d3b53ef7b16173c36d3cefa57f93", size = 1643924 },
|
|
497
|
+
{ url = "https://files.pythonhosted.org/packages/1a/4b/8c6e65b5755e26fc02077803879747615c1c327047328d1784bccb4ff4c3/coincurve-21.0.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ad05952b6edc593a874df61f1bc79db99d716ec48ba4302d699e14a419fe6f51", size = 1619964 },
|
|
498
|
+
{ url = "https://files.pythonhosted.org/packages/64/bc/d0a743305ff9fa26e72b4c77b534d5958ec8030b3772555a7172a0c134e5/coincurve-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d2bf350ced38b73db9efa1ff8fd16a67a1cb35abb2dda50d89661b531f03fd3", size = 1645526 },
|
|
499
|
+
{ url = "https://files.pythonhosted.org/packages/9d/44/ab082e2dc8c9a45774f1bb9961f58b43c0882b866f5c469ead932d45a35d/coincurve-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:54d9500c56d5499375e579c3917472ffcf804c3584dd79052a79974280985c74", size = 1329285 },
|
|
500
|
+
{ url = "https://files.pythonhosted.org/packages/f3/94/407f6fc811310f15b1fc7255f436f6a9040854213beeb10093f56b5b7fd3/coincurve-21.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:773917f075ec4b94a7a742637d303a3a082616a115c36568eb6c873a8d950d18", size = 1326027 },
|
|
501
|
+
]
|
|
502
|
+
|
|
465
503
|
[[package]]
|
|
466
504
|
name = "colorama"
|
|
467
505
|
version = "0.4.6"
|
|
@@ -945,10 +983,11 @@ wheels = [
|
|
|
945
983
|
|
|
946
984
|
[[package]]
|
|
947
985
|
name = "hyperquant"
|
|
948
|
-
version = "1.
|
|
986
|
+
version = "1.45"
|
|
949
987
|
source = { editable = "." }
|
|
950
988
|
dependencies = [
|
|
951
989
|
{ name = "aiohttp" },
|
|
990
|
+
{ name = "coincurve" },
|
|
952
991
|
{ name = "colorama" },
|
|
953
992
|
{ name = "cryptography" },
|
|
954
993
|
{ name = "duckdb" },
|
|
@@ -970,7 +1009,8 @@ dev = [
|
|
|
970
1009
|
|
|
971
1010
|
[package.metadata]
|
|
972
1011
|
requires-dist = [
|
|
973
|
-
{ name = "aiohttp", specifier = ">=3.
|
|
1012
|
+
{ name = "aiohttp", specifier = ">=3.13" },
|
|
1013
|
+
{ name = "coincurve", specifier = ">=21.0.0" },
|
|
974
1014
|
{ name = "colorama", specifier = ">=0.4.6" },
|
|
975
1015
|
{ name = "cryptography", specifier = ">=44.0.2" },
|
|
976
1016
|
{ name = "duckdb", specifier = ">=1.2.2" },
|
|
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
|