hyperquant 1.37__tar.gz → 1.39__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.37 → hyperquant-1.39}/PKG-INFO +1 -1
  2. {hyperquant-1.37 → hyperquant-1.39}/pyproject.toml +1 -1
  3. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/polymarket.py +64 -0
  4. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/polymarket.py +84 -0
  5. {hyperquant-1.37 → hyperquant-1.39}/uv.lock +1 -1
  6. {hyperquant-1.37 → hyperquant-1.39}/.gitignore +0 -0
  7. {hyperquant-1.37 → hyperquant-1.39}/README.md +0 -0
  8. {hyperquant-1.37 → hyperquant-1.39}/requirements-dev.lock +0 -0
  9. {hyperquant-1.37 → hyperquant-1.39}/requirements.lock +0 -0
  10. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/__init__.py +0 -0
  11. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/auth.py +0 -0
  12. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/bitget.py +0 -0
  13. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/bitmart.py +0 -0
  14. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/coinw.py +0 -0
  15. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/deepcoin.py +0 -0
  16. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/edgex.py +0 -0
  17. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/hyperliquid.py +0 -0
  18. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lbank.py +0 -0
  19. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
  20. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lib/hpstore.py +0 -0
  21. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lib/hyper_types.py +0 -0
  22. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lib/util.py +0 -0
  23. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lighter.py +0 -0
  24. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/apexpro.py +0 -0
  25. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/bitget.py +0 -0
  26. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/bitmart.py +0 -0
  27. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/coinw.py +0 -0
  28. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/deepcoin.py +0 -0
  29. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/edgex.py +0 -0
  30. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/hyperliquid.py +0 -0
  31. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/lbank.py +0 -0
  32. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/lighter.py +0 -0
  33. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/ourbit.py +0 -0
  34. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/ourbit.py +0 -0
  35. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/ws.py +0 -0
  36. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/core.py +0 -0
  37. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/datavison/_util.py +0 -0
  38. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/datavison/binance.py +0 -0
  39. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/datavison/coinglass.py +0 -0
  40. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/datavison/okx.py +0 -0
  41. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/db.py +0 -0
  42. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/draw.py +0 -0
  43. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/logkit.py +0 -0
  44. {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/notikit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 1.37
3
+ Version: 1.39
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.37"
3
+ version = "1.39"
4
4
  description = "A minimal yet hyper-efficient backtesting framework for quantitative trading"
5
5
  authors = [
6
6
  { name = "MissinA", email = "1421329142@qq.com" }
@@ -29,6 +29,7 @@ class Position(DataStore):
29
29
  outcome = trade.get("outcome")
30
30
  side = str(trade.get("side") or "").upper()
31
31
  size_raw = trade.get("size")
32
+ price_raw = trade.get("price")
32
33
 
33
34
  if not asset_id or not outcome or side not in {"BUY", "SELL"}:
34
35
  return
@@ -37,19 +38,37 @@ class Position(DataStore):
37
38
  size = float(size_raw)
38
39
  except (TypeError, ValueError):
39
40
  return
41
+ try:
42
+ price = float(price_raw)
43
+ except (TypeError, ValueError):
44
+ price = None
40
45
 
41
46
  key = {"asset": asset_id, "outcome": outcome}
42
47
  existing = self.get(key) or {}
43
48
 
44
49
  cur_size = float(existing.get("size") or 0.0)
45
50
  cur_total_bought = float(existing.get("totalBought") or 0.0)
51
+ cur_avg_price = float(existing.get("avgPrice") or 0.0)
52
+ cur_cost = cur_size * cur_avg_price
46
53
 
47
54
  if side == "BUY":
48
55
  new_size = cur_size + size
49
56
  total_bought = cur_total_bought + size
57
+ # 未拿到成交价时使用当前均价兜底,避免均价被拉低
58
+ effective_price = price if price is not None else cur_avg_price
59
+ new_cost = cur_cost + size * effective_price
50
60
  else: # SELL
51
61
  new_size = cur_size - size
52
62
  total_bought = cur_total_bought
63
+ # 卖出按照当前均价释放成本
64
+ new_cost = cur_cost - min(size, cur_size) * cur_avg_price
65
+
66
+ if new_size <= 0:
67
+ new_size = 0.0
68
+ avg_price = 0.0
69
+ new_cost = 0.0
70
+ else:
71
+ avg_price = max(new_cost, 0.0) / new_size
53
72
 
54
73
  rec: dict[str, Any] = {
55
74
  "asset": asset_id,
@@ -57,6 +76,7 @@ class Position(DataStore):
57
76
  "side": side,
58
77
  "size": new_size,
59
78
  "totalBought": total_bought,
79
+ "avgPrice": avg_price,
60
80
  }
61
81
 
62
82
  if existing:
@@ -394,6 +414,36 @@ class _SideBook:
394
414
  except (TypeError, ValueError):
395
415
  return None
396
416
 
417
+ class Price(DataStore):
418
+ _KEYS = ["s"]
419
+
420
+ def _on_message(self, msg: dict[str, Any]) -> None:
421
+ payload = msg.get('payload') or {}
422
+ data = payload.get('data') or {}
423
+ symbol = payload.get('symbol')
424
+
425
+ if not symbol:
426
+ return
427
+
428
+ _next = self.get({'s': symbol}) or {}
429
+ _next_price = _next.get('p')
430
+ last_price = None
431
+
432
+ if data and isinstance(data, list):
433
+ last_price = data[-1].get('value')
434
+ if 'value' in payload:
435
+ last_price = payload.get('value')
436
+
437
+ if last_price is None:
438
+ return
439
+
440
+ record = {'s': symbol, 'p': last_price}
441
+ key = {'s': symbol}
442
+ if self.get(key):
443
+ self._update([record])
444
+ else:
445
+ self._insert([record])
446
+
397
447
 
398
448
  class BBO(DataStore):
399
449
  _KEYS = ["s", "S"]
@@ -612,6 +662,7 @@ class PolymarketDataStore(DataStoreCollection):
612
662
  self._create("mytrade", datastore_class=MyTrade)
613
663
  self._create("fill", datastore_class=Fill)
614
664
  self._create("trade", datastore_class=Trade)
665
+ self._create("price", datastore_class=Price)
615
666
 
616
667
  @property
617
668
  def book(self) -> Book:
@@ -762,6 +813,13 @@ class PolymarketDataStore(DataStoreCollection):
762
813
  """
763
814
 
764
815
  return self._get("trade")
816
+
817
+ @property
818
+ def price(self) -> Price:
819
+ """Price DataStore
820
+ _key: s
821
+ """
822
+ return self._get("price")
765
823
 
766
824
  @property
767
825
  def fill(self) -> Fill:
@@ -837,6 +895,12 @@ class PolymarketDataStore(DataStoreCollection):
837
895
  # 判定msg是否为list
838
896
  lst_msg = msg if isinstance(msg, list) else [msg]
839
897
  for m in lst_msg:
898
+ if m == '':
899
+ continue
900
+ topic = m.get("topic") or ""
901
+ if topic in {'crypto_prices_chainlink', 'crypto_prices'}:
902
+ self.price._on_message(m)
903
+ continue
840
904
  raw_type = m.get("event_type") or m.get("type")
841
905
  if not raw_type:
842
906
  continue
@@ -240,6 +240,89 @@ class Polymarket:
240
240
  orders = results["orders"]
241
241
  self.store.orders._on_response(orders)
242
242
 
243
+ async def sub_rts_prices(
244
+ self,
245
+ symbols: Sequence[str] | str | None = None,
246
+ *,
247
+ source: Literal["chainlink", "binance"] = "chainlink",
248
+ server_filter: bool = False,
249
+ ) -> pybotters.ws.WebSocketApp:
250
+ """Subscribe to Polymarket RTDS prices (Chainlink or Binance sources).
251
+
252
+ Parameters
253
+ ----------
254
+ symbols
255
+ Requested symbols (Chainlink prefers ``eth/usd`` format, Binance
256
+ uses ``ethusdt``).
257
+ source
258
+ Either ``"chainlink"`` (default) or ``"binance"``.
259
+ server_filter
260
+ When ``True`` the request payload includes the filter exactly as the
261
+ docs specify (e.g. ``{"symbol":"btc/usd"}``). In practice the
262
+ server sometimes stops streaming after returning the first snapshot
263
+ when filters are present, so the default behaviour is to subscribe
264
+ to the full feed and filter locally.
265
+ """
266
+
267
+ if isinstance(symbols, str):
268
+ requested = [symbols]
269
+ elif symbols:
270
+ requested = list(symbols)
271
+ else:
272
+ requested = []
273
+
274
+ target_symbols = {s.lower() for s in requested if s}
275
+
276
+ if source == "chainlink":
277
+ topic = "crypto_prices_chainlink"
278
+ sub_type = "*"
279
+ if server_filter and target_symbols:
280
+ if len(target_symbols) == 1:
281
+ filters = json.dumps({"symbol": next(iter(target_symbols))})
282
+ else:
283
+ filters = json.dumps({"symbols": sorted(target_symbols)})
284
+ else:
285
+ filters = None
286
+ else:
287
+ topic = "crypto_prices"
288
+ sub_type = "update"
289
+ filters = None
290
+ if server_filter and target_symbols:
291
+ filters = ",".join(sorted(target_symbols))
292
+
293
+ subscription: dict[str, Any] = {"topic": topic, "type": sub_type}
294
+ if filters:
295
+ subscription["filters"] = filters
296
+
297
+ payload = {
298
+ "action": "subscribe",
299
+ "subscriptions": [subscription],
300
+ }
301
+
302
+ def callback(msg, ws):
303
+ if not msg:
304
+ return
305
+ try:
306
+ data = json.loads(msg)
307
+ except json.JSONDecodeError:
308
+ return
309
+
310
+ payload = data.get("payload") or {}
311
+ symbol = str(payload.get("symbol") or "").lower()
312
+ if (not server_filter) and target_symbols and symbol and symbol not in target_symbols:
313
+ return
314
+
315
+ self.store.onmessage(data, ws)
316
+
317
+ wsapp = self.client.ws_connect(
318
+ RTS_DATA_ENDPOINT,
319
+ hdlr_str=callback,
320
+ heartbeat=5,
321
+ )
322
+
323
+ await wsapp._event.wait()
324
+ await wsapp.current_ws.send_json(payload)
325
+ return wsapp
243
326
 
244
327
 
245
328
  async def sub_books(
@@ -917,6 +1000,7 @@ class Polymarket:
917
1000
  if isinstance(fee_resp, dict):
918
1001
  return int(fee_resp.get("base_fee", 0))
919
1002
  return int(fee_resp or 0)
1003
+
920
1004
 
921
1005
  async def _signed_request_via_session(
922
1006
  self, method: str, path: str, body: Mapping[str, Any] | list[Any] | None
@@ -945,7 +945,7 @@ wheels = [
945
945
 
946
946
  [[package]]
947
947
  name = "hyperquant"
948
- version = "1.36"
948
+ version = "1.38"
949
949
  source = { editable = "." }
950
950
  dependencies = [
951
951
  { name = "aiohttp" },
File without changes
File without changes
File without changes