hyperquant 1.52__tar.gz → 1.54__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.52 → hyperquant-1.54}/PKG-INFO +1 -1
  2. {hyperquant-1.52 → hyperquant-1.54}/pyproject.toml +1 -1
  3. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/polymarket.py +129 -72
  4. {hyperquant-1.52 → hyperquant-1.54}/uv.lock +1 -1
  5. {hyperquant-1.52 → hyperquant-1.54}/.gitignore +0 -0
  6. {hyperquant-1.52 → hyperquant-1.54}/README.md +0 -0
  7. {hyperquant-1.52 → hyperquant-1.54}/requirements-dev.lock +0 -0
  8. {hyperquant-1.52 → hyperquant-1.54}/requirements.lock +0 -0
  9. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/__init__.py +0 -0
  10. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/auth.py +0 -0
  11. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/bitget.py +0 -0
  12. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/bitmart.py +0 -0
  13. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/coinw.py +0 -0
  14. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/deepcoin.py +0 -0
  15. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/edgex.py +0 -0
  16. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/hyperliquid.py +0 -0
  17. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/lbank.py +0 -0
  18. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
  19. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/lib/hpstore.py +0 -0
  20. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/lib/hyper_types.py +0 -0
  21. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/lib/polymarket/ctfAbi.py +0 -0
  22. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/lib/polymarket/safeAbi.py +0 -0
  23. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/lib/util.py +0 -0
  24. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/lighter.py +0 -0
  25. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/apexpro.py +0 -0
  26. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/bitget.py +0 -0
  27. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/bitmart.py +0 -0
  28. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/coinw.py +0 -0
  29. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/deepcoin.py +0 -0
  30. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/edgex.py +0 -0
  31. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/hyperliquid.py +0 -0
  32. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/lbank.py +0 -0
  33. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/lighter.py +0 -0
  34. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/ourbit.py +0 -0
  35. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/models/polymarket.py +0 -0
  36. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/ourbit.py +0 -0
  37. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/broker/ws.py +0 -0
  38. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/core.py +0 -0
  39. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/datavison/_util.py +0 -0
  40. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/datavison/binance.py +0 -0
  41. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/datavison/coinglass.py +0 -0
  42. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/datavison/okx.py +0 -0
  43. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/db.py +0 -0
  44. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/draw.py +0 -0
  45. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/logkit.py +0 -0
  46. {hyperquant-1.52 → hyperquant-1.54}/src/hyperquant/notikit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 1.52
3
+ Version: 1.54
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.52"
3
+ version = "1.54"
4
4
  description = "A minimal yet hyper-efficient backtesting framework for quantitative trading"
5
5
  authors = [
6
6
  { name = "MissinA", email = "1421329142@qq.com" }
@@ -108,7 +108,10 @@ CONDITIONAL_TOKENS_SPLIT_ABI = [
108
108
  },
109
109
  ]
110
110
  DEFAULT_POLYGON_RPCS = (
111
- # "https://polygon.llamarpc.com",
111
+ "https://1rpc.io/matic",
112
+ "https://polygon-bor-rpc.publicnode.com",
113
+ "https://polygon.drpc.org",
114
+ # Keep historical endpoints as fallback for users with API access.
112
115
  "https://polygon-rpc.com",
113
116
  "https://rpc.ankr.com/polygon",
114
117
  )
@@ -1846,14 +1849,22 @@ class Polymarket:
1846
1849
  if dry_run:
1847
1850
  results.append({**item, "tx": None, "dry_run": True})
1848
1851
  continue
1849
- tx_hash = await asyncio.to_thread(
1850
- self._claim_via_safe_sync,
1851
- candidates,
1852
- item["condition_id"],
1853
- item["index_sets"],
1854
- gas,
1855
- verbose,
1856
- )
1852
+ try:
1853
+ tx_hash = await asyncio.to_thread(
1854
+ self._claim_via_safe_sync,
1855
+ candidates,
1856
+ item["condition_id"],
1857
+ item["index_sets"],
1858
+ gas,
1859
+ verbose,
1860
+ )
1861
+ except Exception as exc:
1862
+ raise RuntimeError(
1863
+ "claim_positions failed for "
1864
+ f"condition_id={item.get('condition_id')} "
1865
+ f"index_sets={item.get('index_sets')} "
1866
+ f"size={item.get('size')}: {exc}"
1867
+ ) from exc
1857
1868
  result_row = {**item, "tx": tx_hash, "dry_run": False}
1858
1869
  if include_receipt:
1859
1870
  try:
@@ -1976,22 +1987,7 @@ class Polymarket:
1976
1987
  last_error: Exception | None = None
1977
1988
  w3 = None
1978
1989
  rpc_used = None
1979
- for url in candidates:
1980
- try:
1981
- w3 = Web3(Web3.HTTPProvider(url, request_kwargs={"timeout": 30}))
1982
- with suppress(Exception):
1983
- from web3.middleware import geth_poa_middleware, ExtraDataToPOAMiddleware
1984
- try:
1985
- w3.middleware_onion.inject(geth_poa_middleware, layer=0)
1986
- except Exception:
1987
- w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
1988
- rpc_used = url
1989
- break
1990
- except Exception as exc:
1991
- last_error = exc
1992
- continue
1993
- if w3 is None:
1994
- raise RuntimeError(f"All RPC endpoints failed: {candidates}") from last_error
1990
+ safe_nonce = None
1995
1991
 
1996
1992
  pk_env = os.getenv("PK")
1997
1993
  if pk_env:
@@ -2004,23 +2000,11 @@ class Polymarket:
2004
2000
  raise RuntimeError("Safe/proxy wallet address未知, 请在 apis['polymarket'][2] 或构造函数 funder 设置")
2005
2001
 
2006
2002
  ctf_addr = self._contracts(self.chain_id, False)["conditional_tokens"]
2007
- ctf = w3.eth.contract(address=w3.to_checksum_address(ctf_addr), abi=CONDITIONAL_TOKENS_ABI)
2008
- safe = w3.eth.contract(address=w3.to_checksum_address(safe_addr), abi=SAFE_ABI)
2009
2003
 
2010
2004
  cond_bytes = bytes.fromhex(condition_id.replace("0x", ""))
2011
2005
  if len(cond_bytes) != 32:
2012
2006
  raise ValueError("condition_id must be 32-byte hex string")
2013
2007
 
2014
- redeem_calldata = ctf.encode_abi(
2015
- "redeemPositions",
2016
- args=[
2017
- w3.to_checksum_address(USDC_CONTRACT),
2018
- bytes(32),
2019
- cond_bytes,
2020
- index_sets,
2021
- ],
2022
- )
2023
-
2024
2008
  safe_tx_gas = 0
2025
2009
  base_gas = 0
2026
2010
  gas_price = 0
@@ -2029,13 +2013,55 @@ class Polymarket:
2029
2013
  value = 0
2030
2014
  operation = 0 # CALL
2031
2015
 
2032
- try:
2033
- safe_nonce = safe.functions.nonce().call()
2034
- except Web3RPCError as exc:
2035
- # 速率限制等错误直接抛出,便于调用层切换 RPC
2036
- raise RuntimeError(f"获取 Safe nonce 失败 (rpc={rpc_used}): {exc}") from exc
2037
- except Exception as exc:
2038
- raise RuntimeError("无法获取 Safe nonce, 请确认 funder 地址为有效 Safe") from exc
2016
+ ctf = None
2017
+ safe = None
2018
+ redeem_calldata = None
2019
+ for url in candidates:
2020
+ try:
2021
+ w3_candidate = Web3(Web3.HTTPProvider(url, request_kwargs={"timeout": 30}))
2022
+ with suppress(Exception):
2023
+ from web3.middleware import geth_poa_middleware, ExtraDataToPOAMiddleware
2024
+ try:
2025
+ w3_candidate.middleware_onion.inject(geth_poa_middleware, layer=0)
2026
+ except Exception:
2027
+ w3_candidate.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
2028
+
2029
+ ctf_candidate = w3_candidate.eth.contract(
2030
+ address=w3_candidate.to_checksum_address(ctf_addr),
2031
+ abi=CONDITIONAL_TOKENS_ABI,
2032
+ )
2033
+ safe_candidate = w3_candidate.eth.contract(
2034
+ address=w3_candidate.to_checksum_address(safe_addr),
2035
+ abi=SAFE_ABI,
2036
+ )
2037
+ redeem_calldata_candidate = ctf_candidate.encode_abi(
2038
+ "redeemPositions",
2039
+ args=[
2040
+ w3_candidate.to_checksum_address(USDC_CONTRACT),
2041
+ bytes(32),
2042
+ cond_bytes,
2043
+ index_sets,
2044
+ ],
2045
+ )
2046
+ nonce_candidate = safe_candidate.functions.nonce().call()
2047
+
2048
+ w3 = w3_candidate
2049
+ rpc_used = url
2050
+ ctf = ctf_candidate
2051
+ safe = safe_candidate
2052
+ redeem_calldata = redeem_calldata_candidate
2053
+ safe_nonce = nonce_candidate
2054
+ break
2055
+ except Exception as exc:
2056
+ last_error = exc
2057
+ if verbose:
2058
+ print(f"[claim] rpc nonce probe failed: {url} -> {exc}")
2059
+ continue
2060
+
2061
+ if w3 is None or safe is None or ctf is None or redeem_calldata is None or safe_nonce is None:
2062
+ raise RuntimeError(
2063
+ f"无法获取 Safe nonce (tried rpc={candidates}); last_error={last_error}"
2064
+ ) from last_error
2039
2065
 
2040
2066
  try:
2041
2067
  safe_tx_hash = safe.functions.getTransactionHash(
@@ -2065,43 +2091,71 @@ class Polymarket:
2065
2091
  try:
2066
2092
  acct = _A.from_key(private_key)
2067
2093
  sender = acct.address
2068
- # 使用 pending 避免重复使用已在 mempool 的 nonce 触发 replacement underpriced
2069
- nonce = w3.eth.get_transaction_count(sender)
2070
- gas_price_chain = w3.eth.gas_price
2071
2094
  except Exception as exc:
2072
- raise RuntimeError("获取 sender nonce/gas_price 失败") from exc
2073
-
2074
- tx = safe.functions.execTransaction(
2075
- w3.to_checksum_address(ctf_addr),
2076
- value,
2077
- redeem_calldata,
2078
- operation,
2079
- safe_tx_gas,
2080
- base_gas,
2081
- gas_price,
2082
- w3.to_checksum_address(gas_token),
2083
- w3.to_checksum_address(refund_receiver),
2084
- sig_bytes,
2085
- ).build_transaction(
2086
- {
2087
- "from": sender,
2088
- "nonce": nonce,
2089
- "gas": gas,
2090
- "gasPrice": gas_price_chain,
2091
- }
2092
- )
2095
+ raise RuntimeError("获取 sender 地址失败") from exc
2093
2096
 
2094
- signed_tx = w3.eth.account.sign_transaction(tx, private_key)
2095
2097
  send_errors: list[str] = []
2096
2098
  for attempt in range(3):
2097
2099
  try:
2100
+ nonce = w3.eth.get_transaction_count(sender, "pending")
2101
+ gas_price_chain = int(w3.eth.gas_price)
2102
+ if attempt > 0:
2103
+ # retry 时小幅提价,减少卡在同价位的概率
2104
+ gas_price_chain = int(gas_price_chain * (1.0 + 0.10 * attempt))
2105
+
2106
+ exec_tx = safe.functions.execTransaction(
2107
+ w3.to_checksum_address(ctf_addr),
2108
+ value,
2109
+ redeem_calldata,
2110
+ operation,
2111
+ safe_tx_gas,
2112
+ base_gas,
2113
+ gas_price,
2114
+ w3.to_checksum_address(gas_token),
2115
+ w3.to_checksum_address(refund_receiver),
2116
+ sig_bytes,
2117
+ )
2118
+ try:
2119
+ simulated = exec_tx.call({"from": sender})
2120
+ except Exception as sim_exc:
2121
+ simulated = None
2122
+ if verbose:
2123
+ print(f"[claim] preflight call error (continue): {sim_exc}")
2124
+ if simulated is False:
2125
+ err = (
2126
+ f"Safe preflight failed (execTransaction call returned false): "
2127
+ f"safe_nonce={safe_nonce} sender_nonce={nonce}"
2128
+ )
2129
+ send_errors.append(err)
2130
+ if verbose:
2131
+ print(f"Safe redeem attempt {attempt+1} failed: {err}")
2132
+ break
2133
+
2134
+ tx = exec_tx.build_transaction(
2135
+ {
2136
+ "from": sender,
2137
+ "nonce": nonce,
2138
+ "gas": gas,
2139
+ "gasPrice": gas_price_chain,
2140
+ }
2141
+ )
2142
+
2143
+ signed_tx = w3.eth.account.sign_transaction(tx, private_key)
2098
2144
  raw_tx = getattr(signed_tx, "rawTransaction", None) or getattr(signed_tx, "raw_transaction", None)
2099
2145
  if raw_tx is None: # pragma: no cover
2100
2146
  raise AttributeError("Signed transaction missing rawTransaction/raw_transaction")
2101
2147
  tx_hash = w3.eth.send_raw_transaction(raw_tx)
2102
2148
  receipt = w3.eth.wait_for_transaction_receipt(tx_hash, poll_latency=2, timeout=180)
2103
2149
  if receipt.get("status") != 1:
2104
- raise RuntimeError(f"Safe redeemPositions failed status!=1: {tx_hash.hex()}")
2150
+ err = (
2151
+ f"Safe redeemPositions reverted on-chain: tx={tx_hash.hex()} "
2152
+ f"safe_nonce={safe_nonce} sender_nonce={nonce}"
2153
+ )
2154
+ send_errors.append(err)
2155
+ if verbose:
2156
+ print(f"Safe redeem attempt {attempt+1} failed: {err}")
2157
+ # 已经上链并回执失败,继续重试通常只会制造 nonce 噪音
2158
+ break
2105
2159
  if verbose:
2106
2160
  print(
2107
2161
  {
@@ -2162,10 +2216,13 @@ class Polymarket:
2162
2216
  hourly_match = await _try_slug(hourly_slug)
2163
2217
  if hourly_match:
2164
2218
  return hourly_match
2165
-
2166
- if '5m' in base_slug:
2219
+
2220
+ if '-15m-' in base_slug or base_slug.endswith('-15m'):
2221
+ interval = 15 * 60
2222
+ elif '-5m-' in base_slug or base_slug.endswith('-5m'):
2167
2223
  interval = 5 * 60
2168
2224
 
2225
+
2169
2226
  now_ts = int(datetime.now(UTC).timestamp())
2170
2227
  base_ts = (now_ts // interval) * interval
2171
2228
 
@@ -694,7 +694,7 @@ wheels = [
694
694
 
695
695
  [[package]]
696
696
  name = "hyperquant"
697
- version = "1.51"
697
+ version = "1.53"
698
698
  source = { editable = "." }
699
699
  dependencies = [
700
700
  { name = "aiohttp" },
File without changes
File without changes
File without changes