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.
- {hyperquant-1.37 → hyperquant-1.39}/PKG-INFO +1 -1
- {hyperquant-1.37 → hyperquant-1.39}/pyproject.toml +1 -1
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/polymarket.py +64 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/polymarket.py +84 -0
- {hyperquant-1.37 → hyperquant-1.39}/uv.lock +1 -1
- {hyperquant-1.37 → hyperquant-1.39}/.gitignore +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/README.md +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/requirements-dev.lock +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/requirements.lock +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/__init__.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/auth.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/bitget.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/bitmart.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/coinw.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/deepcoin.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/edgex.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lbank.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lib/hpstore.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lib/hyper_types.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lib/util.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/lighter.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/apexpro.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/bitget.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/bitmart.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/coinw.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/deepcoin.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/edgex.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/hyperliquid.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/lbank.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/lighter.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/models/ourbit.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/ourbit.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/broker/ws.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/core.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/db.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/draw.py +0 -0
- {hyperquant-1.37 → hyperquant-1.39}/src/hyperquant/logkit.py +0 -0
- {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.
|
|
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
|
|
@@ -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
|
|
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
|