hyperquant 1.34__tar.gz → 1.35__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 (44) hide show
  1. {hyperquant-1.34 → hyperquant-1.35}/PKG-INFO +1 -1
  2. {hyperquant-1.34 → hyperquant-1.35}/pyproject.toml +1 -1
  3. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/auth.py +60 -2
  4. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/polymarket.py +78 -21
  5. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/polymarket.py +82 -23
  6. {hyperquant-1.34 → hyperquant-1.35}/uv.lock +1 -1
  7. {hyperquant-1.34 → hyperquant-1.35}/.gitignore +0 -0
  8. {hyperquant-1.34 → hyperquant-1.35}/README.md +0 -0
  9. {hyperquant-1.34 → hyperquant-1.35}/requirements-dev.lock +0 -0
  10. {hyperquant-1.34 → hyperquant-1.35}/requirements.lock +0 -0
  11. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/__init__.py +0 -0
  12. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/bitget.py +0 -0
  13. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/bitmart.py +0 -0
  14. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/coinw.py +0 -0
  15. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/deepcoin.py +0 -0
  16. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/edgex.py +0 -0
  17. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/hyperliquid.py +0 -0
  18. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/lbank.py +0 -0
  19. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
  20. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/lib/hpstore.py +0 -0
  21. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/lib/hyper_types.py +0 -0
  22. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/lib/util.py +0 -0
  23. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/lighter.py +0 -0
  24. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/apexpro.py +0 -0
  25. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/bitget.py +0 -0
  26. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/bitmart.py +0 -0
  27. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/coinw.py +0 -0
  28. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/deepcoin.py +0 -0
  29. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/edgex.py +0 -0
  30. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/hyperliquid.py +0 -0
  31. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/lbank.py +0 -0
  32. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/lighter.py +0 -0
  33. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/models/ourbit.py +0 -0
  34. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/ourbit.py +0 -0
  35. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/broker/ws.py +0 -0
  36. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/core.py +0 -0
  37. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/datavison/_util.py +0 -0
  38. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/datavison/binance.py +0 -0
  39. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/datavison/coinglass.py +0 -0
  40. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/datavison/okx.py +0 -0
  41. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/db.py +0 -0
  42. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/draw.py +0 -0
  43. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/logkit.py +0 -0
  44. {hyperquant-1.34 → hyperquant-1.35}/src/hyperquant/notikit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 1.34
3
+ Version: 1.35
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.34"
3
+ version = "1.35"
4
4
  description = "A minimal yet hyper-efficient backtesting framework for quantitative trading"
5
5
  authors = [
6
6
  { name = "MissinA", email = "1421329142@qq.com" }
@@ -499,9 +499,67 @@ class Auth:
499
499
  if not creds:
500
500
  return args
501
501
 
502
+ if isinstance(creds, tuple):
503
+ creds = list(creds)
504
+ session.__dict__["_apis"][api_name] = creds
505
+
502
506
  private_key = creds[0] if len(creds) > 0 and creds[0] else None
503
- chain_id = session.__dict__.get("_polymarket_chain_id", 137)
504
- api_meta = session.__dict__.get("_polymarket_api_creds", {})
507
+ if private_key:
508
+ pk_str = str(private_key)
509
+ if not pk_str.startswith("0x"):
510
+ pk_str = f"0x{pk_str}"
511
+ try:
512
+ session.__dict__["_apis"][api_name][0] = pk_str
513
+ except Exception:
514
+ pass
515
+ private_key = pk_str
516
+
517
+ packed_extra = creds[2] if len(creds) > 2 else None
518
+ packed_api_key = packed_api_secret = packed_passphrase = None
519
+ packed_chain_id = packed_wallet = None
520
+ if isinstance(packed_extra, (list, tuple)):
521
+ def _packed_value(idx: int):
522
+ if idx >= len(packed_extra):
523
+ return None
524
+ value = packed_extra[idx]
525
+ if isinstance(value, str):
526
+ value = value.strip()
527
+ return value or None
528
+
529
+ packed_api_key = _packed_value(0)
530
+ packed_api_secret = _packed_value(1)
531
+ packed_passphrase = _packed_value(2)
532
+ packed_chain_id = _packed_value(3)
533
+ packed_wallet = _packed_value(4)
534
+ elif isinstance(packed_extra, str):
535
+ packed_wallet = packed_extra or None
536
+
537
+ existing_chain_id = session.__dict__.get("_polymarket_chain_id")
538
+ if existing_chain_id is None:
539
+ chain_id = 137
540
+ if packed_chain_id is not None:
541
+ try:
542
+ chain_id = int(packed_chain_id)
543
+ except (TypeError, ValueError):
544
+ chain_id = 137
545
+ session.__dict__["_polymarket_chain_id"] = chain_id
546
+ else:
547
+ chain_id = existing_chain_id
548
+
549
+ api_meta = session.__dict__.get("_polymarket_api_creds") or {}
550
+ if (not api_meta.get("api_key") or not api_meta.get("api_secret") or not api_meta.get("api_passphrase")) and (
551
+ packed_api_key and packed_api_secret and packed_passphrase
552
+ ):
553
+ api_meta = {
554
+ "api_key": packed_api_key,
555
+ "api_secret": packed_api_secret,
556
+ "api_passphrase": packed_passphrase,
557
+ }
558
+ session.__dict__["_polymarket_api_creds"] = api_meta
559
+
560
+ if packed_wallet and len(creds) > 2 and isinstance(creds[2], (list, tuple)):
561
+ creds[2] = packed_wallet
562
+
505
563
  api_key = api_meta.get("api_key")
506
564
  api_secret = api_meta.get("api_secret")
507
565
  api_passphrase = api_meta.get("api_passphrase")
@@ -18,6 +18,50 @@ class Position(DataStore):
18
18
  def _on_response(self, msg: Item) -> None:
19
19
  self._update(msg)
20
20
 
21
+ def on_trade(self, trade: Item) -> None:
22
+ status = str(trade.get("status") or "").upper()
23
+ if status not in {"MATCHED"}:
24
+ return
25
+
26
+ asset_id = trade.get("asset_id")
27
+ outcome = trade.get("outcome")
28
+ side = str(trade.get("side") or "").upper()
29
+ size_raw = trade.get("size")
30
+
31
+ if not asset_id or not outcome or side not in {"BUY", "SELL"}:
32
+ return
33
+
34
+ try:
35
+ size = float(size_raw)
36
+ except (TypeError, ValueError):
37
+ return
38
+
39
+ key = {"asset": asset_id, "outcome": outcome}
40
+ existing = self.get(key) or {}
41
+
42
+ cur_size = float(existing.get("size") or 0.0)
43
+ cur_total_bought = float(existing.get("totalBought") or 0.0)
44
+
45
+ if side == "BUY":
46
+ new_size = cur_size + size
47
+ total_bought = cur_total_bought + size
48
+ else: # SELL
49
+ new_size = cur_size - size
50
+ total_bought = cur_total_bought
51
+
52
+ rec: dict[str, Any] = {
53
+ "asset": asset_id,
54
+ "outcome": outcome,
55
+ "side": side,
56
+ "size": new_size,
57
+ "totalBought": total_bought,
58
+ }
59
+
60
+ if existing:
61
+ self._update([rec])
62
+ else:
63
+ self._insert([rec])
64
+
21
65
 
22
66
  class Fill(DataStore):
23
67
  """Fill records keyed by maker order id."""
@@ -157,13 +201,17 @@ class MyTrade(DataStore):
157
201
  class Trade(DataStore):
158
202
  """User trades keyed by trade id."""
159
203
 
160
- _KEYS = ["asset"]
204
+ _KEYS = ["asset_id"]
161
205
  _MAXLEN = 500
162
206
 
163
207
 
164
208
  def _on_message(self, msg: dict[str, Any]) -> None:
165
- payload = msg.get("payload") or {}
209
+ payload = msg or {}
166
210
  if payload:
211
+ size = float(payload.get("size"))
212
+ price = float(payload.get("price"))
213
+ # share = size / price
214
+ # payload['shares'] = share
167
215
  self._insert([payload])
168
216
 
169
217
  class Book(DataStore):
@@ -583,27 +631,35 @@ class PolymarketDataStore(DataStoreCollection):
583
631
  def trade(self) -> Trade:
584
632
  """
585
633
  _key asset
586
-
634
+ MATCHED进行快速捕捉
587
635
  .. code:: json
588
636
  {
589
- "asset": "28343900911859986062638672238388649192722581036873866155064320792236495661418",
590
- "bio": "",
591
- "conditionId": "0x730a593d5b63a39d316139a7f4bc1dd9fd05d858f6d96f659629c371bc81cd4d",
592
- "eventSlug": "btc-updown-15m-1762951500",
593
- "icon": "https://polymarket-upload.s3.us-east-2.amazonaws.com/BTC+fullsize.png",
594
- "name": "",
595
- "outcome": "Down",
596
- "outcomeIndex": 1,
597
- "price": 0.11,
598
- "profileImage": "",
599
- "proxyWallet": "0x7459f51304037Da66c47CeA58F078D7f603fD011",
600
- "pseudonym": "",
601
- "side": "SELL",
602
- "size": 84,
603
- "slug": "btc-updown-15m-1762951500",
604
- "timestamp": 1762952057,
605
- "title": "Bitcoin Up or Down - November 12, 7:45AM-8:00AM ET",
606
- "transactionHash": "0x6853cab8901779c494fe15f0eb3350aebfb182125347ca67b8bd9706babbddca"
637
+ "asset_id": "52114319501245915516055106046884209969926127482827954674443846427813813222426",
638
+ "event_type": "trade",
639
+ "id": "28c4d2eb-bbea-40e7-a9f0-b2fdb56b2c2e",
640
+ "last_update": "1672290701",
641
+ "maker_orders": [
642
+ {
643
+ "asset_id": "52114319501245915516055106046884209969926127482827954674443846427813813222426",
644
+ "matched_amount": "10",
645
+ "order_id": "0xff354cd7ca7539dfa9c28d90943ab5779a4eac34b9b37a757d7b32bdfb11790b",
646
+ "outcome": "YES",
647
+ "owner": "9180014b-33c8-9240-a14b-bdca11c0a465",
648
+ "price": "0.57"
649
+ }
650
+ ],
651
+ "market": "0xbd31dc8a20211944f6b70f31557f1001557b59905b7738480ca09bd4532f84af",
652
+ "matchtime": "1672290701",
653
+ "outcome": "YES",
654
+ "owner": "9180014b-33c8-9240-a14b-bdca11c0a465",
655
+ "price": "0.57",
656
+ "side": "BUY",
657
+ "size": "10",
658
+ "status": "MATCHED",
659
+ "taker_order_id": "0x06bc63e346ed4ceddce9efd6b3af37c8f8f440c92fe7da6b2d0f9e4ccbc50c42",
660
+ "timestamp": "1672290701",
661
+ "trade_owner": "9180014b-33c8-9240-a14b-bdca11c0a465",
662
+ "type": "TRADE"
607
663
  }
608
664
  """
609
665
  return self._get("trade")
@@ -624,5 +680,6 @@ class PolymarketDataStore(DataStoreCollection):
624
680
  elif msg_type == "trade":
625
681
  self.trade._on_message(m)
626
682
  self.fill._on_trade(m)
683
+ self.position.on_trade(m)
627
684
  elif msg_type == 'orders_matched':
628
685
  self.trade._on_message(m)
@@ -4,6 +4,7 @@ import asyncio
4
4
  from contextlib import suppress
5
5
  from datetime import UTC, datetime
6
6
  from functools import lru_cache
7
+ import os
7
8
  from typing import Any, Iterable, Iterator, Literal, Mapping, Sequence
8
9
 
9
10
  import json
@@ -38,6 +39,14 @@ DEFAULT_POLYGON_RPCS = (
38
39
  "https://rpc.ankr.com/polygon",
39
40
  )
40
41
 
42
+ def parse_field(value):
43
+ """尝试将字符串 JSON 转为对象,否则原样返回"""
44
+ if isinstance(value, str):
45
+ try:
46
+ return json.loads(value)
47
+ except json.JSONDecodeError:
48
+ return value
49
+ return value
41
50
 
42
51
  def _iter_offsets(window: int) -> Iterator[int]:
43
52
  yield 0
@@ -94,7 +103,7 @@ class Polymarket:
94
103
  self._ws_personal: pybotters.ws.WebSocketApp | None = None
95
104
  self.auth = False
96
105
 
97
- self._ensure_session_entry(private_key=private_key, funder=funder)
106
+ self._ensure_session_entry(private_key=private_key, funder=funder, chain_id=chain_id)
98
107
 
99
108
  async def __aenter__(self) -> "Polymarket":
100
109
  if self.auth:
@@ -266,6 +275,7 @@ class Polymarket:
266
275
  self._ws_personal = self.client.ws_connect(
267
276
  "wss://ws-subscriptions-clob.polymarket.com/ws/user",
268
277
  hdlr_json=effective_cb,
278
+ heartbeat=30,
269
279
  auth=None,
270
280
  )
271
281
  await self._ws_personal._event.wait()
@@ -298,7 +308,7 @@ class Polymarket:
298
308
  wsapp = self.client.ws_connect(
299
309
  RTS_DATA_ENDPOINT,
300
310
  hdlr_str=callback,
301
- heartbeat=5
311
+ heartbeat=30
302
312
  )
303
313
  await wsapp._event.wait()
304
314
  await wsapp.current_ws.send_json(payload)
@@ -314,9 +324,12 @@ class Polymarket:
314
324
  return await self._rest("GET", f"/markets/{market_id}")
315
325
 
316
326
  async def get_market_by_slug(self, slug: str) -> Any:
317
- """Fetch a market using its human-readable slug."""
318
- # https://gamma-api.polymarket.com/markets/slug/{slug}
319
- return await self._rest("GET", f"/slug/{slug}", host='https://gamma-api.polymarket.com/markets')
327
+ """Fetch a market using its human-readable slug.
328
+ https://docs.polymarket.com/api-reference/markets/get-market-by-slug
329
+ """
330
+ market:dict = await self._rest("GET", f"/slug/{slug}", host='https://gamma-api.polymarket.com/markets')
331
+ market = {k: parse_field(v) for k, v in market.items()}
332
+ return market
320
333
 
321
334
  async def get_order_book(self, token_id: str) -> Any:
322
335
  return await self._rest("GET", "/book", params={"token_id": token_id})
@@ -1169,14 +1182,7 @@ class Polymarket:
1169
1182
  if not event:
1170
1183
  continue
1171
1184
 
1172
- def parse_field(value):
1173
- """尝试将字符串 JSON 转为对象,否则原样返回"""
1174
- if isinstance(value, str):
1175
- try:
1176
- return json.loads(value)
1177
- except json.JSONDecodeError:
1178
- return value
1179
- return value
1185
+
1180
1186
 
1181
1187
  event = {k: parse_field(v) for k, v in event.items()}
1182
1188
 
@@ -1316,6 +1322,7 @@ class Polymarket:
1316
1322
  *,
1317
1323
  private_key: str | None,
1318
1324
  funder: str | None,
1325
+ chain_id: int | None,
1319
1326
  ) -> None:
1320
1327
  session = getattr(self.client, "_session", None)
1321
1328
  if session is None:
@@ -1328,27 +1335,66 @@ class Polymarket:
1328
1335
  if not entry and not private_key:
1329
1336
  return
1330
1337
 
1338
+ packed = entry[2] if len(entry) > 2 else None
1339
+ if not isinstance(packed, (list, tuple)):
1340
+ packed = None
1341
+
1342
+ def _packed_value(idx: int) -> Any | None:
1343
+ if packed is None:
1344
+ return None
1345
+ if idx >= len(packed):
1346
+ return None
1347
+ value = packed[idx]
1348
+ if isinstance(value, str):
1349
+ value = value.strip()
1350
+ return value or None
1351
+
1352
+ packed_api_key = _packed_value(0)
1353
+ packed_api_secret = _packed_value(1)
1354
+ packed_passphrase = _packed_value(2)
1355
+ packed_chain_id = _packed_value(3)
1356
+ packed_wallet = _packed_value(4)
1357
+
1331
1358
  while len(entry) < 3:
1332
1359
  entry.append("")
1333
1360
 
1334
- existing_pk = entry[0]
1361
+ existing_pk = entry[0] if entry else None
1335
1362
  normalized_pk: str | None = None
1336
- if private_key:
1337
- normalized_pk = (
1338
- private_key if private_key.startswith("0x") else f"0x{private_key}"
1339
- )
1340
- elif existing_pk:
1363
+ candidate_pk = private_key or existing_pk
1364
+ if candidate_pk:
1365
+ candidate_pk = str(candidate_pk)
1341
1366
  normalized_pk = (
1342
- existing_pk if existing_pk.startswith("0x") else f"0x{existing_pk}"
1367
+ candidate_pk if candidate_pk.startswith("0x") else f"0x{candidate_pk}"
1343
1368
  )
1344
1369
 
1345
1370
  if not normalized_pk:
1346
1371
  raise RuntimeError("Polymarket需要钱包私钥 (apis['polymarket'][0])")
1347
1372
 
1348
1373
  entry[0] = normalized_pk
1349
- if funder:
1350
- entry[2] = funder
1351
- self.funder = entry[2] or self.funder
1374
+
1375
+ existing_wallet = entry[2] if isinstance(entry[2], str) and entry[2] else None
1376
+ effective_wallet = funder or packed_wallet or existing_wallet or self.funder
1377
+ if effective_wallet:
1378
+ entry[2] = effective_wallet
1379
+ self.funder = effective_wallet
1380
+ else:
1381
+ entry[2] = ""
1382
+
1383
+ derived_chain_id: int | None = None
1384
+ if packed_chain_id is not None:
1385
+ try:
1386
+ derived_chain_id = int(packed_chain_id)
1387
+ except (TypeError, ValueError):
1388
+ derived_chain_id = None
1389
+ if chain_id is None and derived_chain_id is not None:
1390
+ self.chain_id = derived_chain_id
1391
+
1392
+ if packed_api_key and packed_api_secret and packed_passphrase:
1393
+ session.__dict__["_polymarket_api_creds"] = {
1394
+ "api_key": packed_api_key,
1395
+ "api_secret": packed_api_secret,
1396
+ "api_passphrase": packed_passphrase,
1397
+ }
1352
1398
 
1353
1399
  apis[API_NAME] = entry
1354
1400
  session.__dict__["_apis"] = apis
@@ -1356,6 +1402,19 @@ class Polymarket:
1356
1402
  session.__dict__["_polymarket_signature_type"] = self.signature_type
1357
1403
  self.auth = True
1358
1404
 
1405
+ @staticmethod
1406
+ def load_poly_api():
1407
+ from dotenv import load_dotenv
1408
+
1409
+ load_dotenv()
1410
+ pk = os.getenv("PK")
1411
+ api_key = os.getenv("CLOB_API_KEY")
1412
+ api_secret = os.getenv("CLOB_API_SECRET")
1413
+ passphrase = os.getenv("CLOB_API_PASSPHRASE")
1414
+ chain_id = os.getenv("CHAIN_ID") or 137
1415
+ wallet_address = os.getenv("POLY_WALLET_ADDRESS")
1416
+ return [pk, "", (api_key, api_secret, passphrase, chain_id, wallet_address)]
1417
+
1359
1418
  @lru_cache(maxsize=8)
1360
1419
  def _get_web3(rpc_url: str):
1361
1420
  from web3 import Web3
@@ -945,7 +945,7 @@ wheels = [
945
945
 
946
946
  [[package]]
947
947
  name = "hyperquant"
948
- version = "1.33"
948
+ version = "1.34"
949
949
  source = { editable = "." }
950
950
  dependencies = [
951
951
  { name = "aiohttp" },
File without changes
File without changes
File without changes