hyperquant 1.32__tar.gz → 1.34__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.32 → hyperquant-1.34}/PKG-INFO +1 -1
- {hyperquant-1.32 → hyperquant-1.34}/pyproject.toml +1 -1
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/polymarket.py +5 -1
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/polymarket.py +208 -5
- {hyperquant-1.32 → hyperquant-1.34}/uv.lock +1 -1
- {hyperquant-1.32 → hyperquant-1.34}/.gitignore +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/README.md +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/requirements-dev.lock +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/requirements.lock +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/__init__.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/auth.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/bitget.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/bitmart.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/coinw.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/deepcoin.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/edgex.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/lbank.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/lib/hpstore.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/lib/hyper_types.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/lib/util.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/lighter.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/apexpro.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/bitget.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/bitmart.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/coinw.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/deepcoin.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/edgex.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/hyperliquid.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/lbank.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/lighter.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/models/ourbit.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/ourbit.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/broker/ws.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/core.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/db.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/draw.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/src/hyperquant/logkit.py +0 -0
- {hyperquant-1.32 → hyperquant-1.34}/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.34
|
|
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
|
|
@@ -292,7 +292,7 @@ class Detail(DataStore):
|
|
|
292
292
|
|
|
293
293
|
@staticmethod
|
|
294
294
|
def _normalize_entry(market: dict[str, Any], token: dict[str, Any]) -> dict[str, Any]:
|
|
295
|
-
slug = market.get("
|
|
295
|
+
slug = market.get("slug")
|
|
296
296
|
outcome = token.get("outcome")
|
|
297
297
|
alias = slug if outcome is None else f"{slug}:{outcome}"
|
|
298
298
|
|
|
@@ -364,10 +364,14 @@ class Detail(DataStore):
|
|
|
364
364
|
|
|
365
365
|
for token in tokens:
|
|
366
366
|
normalized = self._normalize_entry(market, token)
|
|
367
|
+
slug: str = market.get("slug")
|
|
368
|
+
# 取最后一个'-'之前部分
|
|
369
|
+
base_slug = slug.rsplit("-", 1)[0] if slug else slug
|
|
367
370
|
# Add or update additional fields from market
|
|
368
371
|
normalized.update({
|
|
369
372
|
"condition_id": market.get("conditionId"),
|
|
370
373
|
"slug": market.get("slug"),
|
|
374
|
+
"base_slug": base_slug,
|
|
371
375
|
"end_date": market.get("endDate"),
|
|
372
376
|
"start_date": market.get("startDate"),
|
|
373
377
|
"icon": market.get("icon"),
|
|
@@ -498,7 +498,7 @@ class Polymarket:
|
|
|
498
498
|
side: str,
|
|
499
499
|
price: float,
|
|
500
500
|
size: float,
|
|
501
|
-
order_type:
|
|
501
|
+
order_type: Literal["GTC", 'FOK'] = "GTC",
|
|
502
502
|
tick_size: str | float | None = None,
|
|
503
503
|
fee_rate_bps: int | None = None,
|
|
504
504
|
expiration: int | None = None,
|
|
@@ -523,6 +523,9 @@ class Polymarket:
|
|
|
523
523
|
- nonce: onchain nonce, default 0
|
|
524
524
|
"""
|
|
525
525
|
|
|
526
|
+
if price <= 0:
|
|
527
|
+
raise ValueError("price must be positive; use place_market_order for market orders")
|
|
528
|
+
|
|
526
529
|
# Ensure L2 creds exist
|
|
527
530
|
if not self._api_creds():
|
|
528
531
|
raise RuntimeError("Polymarket API credentials missing; call create_or_derive_api_creds first")
|
|
@@ -552,6 +555,117 @@ class Polymarket:
|
|
|
552
555
|
}
|
|
553
556
|
return await self._signed_request_via_session("POST", "/order", payload)
|
|
554
557
|
|
|
558
|
+
async def place_market_order(
|
|
559
|
+
self,
|
|
560
|
+
*,
|
|
561
|
+
token_id: str,
|
|
562
|
+
side: str,
|
|
563
|
+
amount: float,
|
|
564
|
+
order_type: Literal["FOK", "GTC", "FAK", "GTD"] = "FOK",
|
|
565
|
+
price: float | None = None,
|
|
566
|
+
tick_size: str | float | None = None,
|
|
567
|
+
fee_rate_bps: int | None = None,
|
|
568
|
+
taker: str = ZERO_ADDRESS,
|
|
569
|
+
neg_risk: bool = False,
|
|
570
|
+
owner: str | None = None,
|
|
571
|
+
nonce: int | None = None,
|
|
572
|
+
) -> Any:
|
|
573
|
+
"""Create, sign and submit a market order similar to ``py_clob_client``.
|
|
574
|
+
|
|
575
|
+
BUY orders treat ``amount`` as collateral (USDC); SELL orders treat it as shares.
|
|
576
|
+
"""
|
|
577
|
+
|
|
578
|
+
if amount <= 0:
|
|
579
|
+
raise ValueError("amount must be greater than 0 for market orders")
|
|
580
|
+
|
|
581
|
+
if not self._api_creds():
|
|
582
|
+
raise RuntimeError("Polymarket API credentials missing; call create_or_derive_api_creds first")
|
|
583
|
+
|
|
584
|
+
private_key, maker_addr, signer_addr = self._get_signing_context()
|
|
585
|
+
owner_key = self._owner_key(owner)
|
|
586
|
+
order_type_str = (order_type or "FOK").upper()
|
|
587
|
+
|
|
588
|
+
if price is None or price <= 0:
|
|
589
|
+
price = await self._calculate_market_price(
|
|
590
|
+
token_id=token_id,
|
|
591
|
+
side=side,
|
|
592
|
+
amount=amount,
|
|
593
|
+
order_type=order_type_str,
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
signed_dict = await self._build_signed_market_order(
|
|
597
|
+
private_key=private_key,
|
|
598
|
+
maker_addr=maker_addr,
|
|
599
|
+
signer_addr=signer_addr,
|
|
600
|
+
token_id=token_id,
|
|
601
|
+
side=side,
|
|
602
|
+
amount=amount,
|
|
603
|
+
price=price,
|
|
604
|
+
tick_size=tick_size,
|
|
605
|
+
fee_rate_bps=fee_rate_bps,
|
|
606
|
+
taker=taker,
|
|
607
|
+
neg_risk=neg_risk,
|
|
608
|
+
nonce=nonce,
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
payload = {
|
|
612
|
+
"order": signed_dict,
|
|
613
|
+
"owner": owner_key,
|
|
614
|
+
"orderType": order_type_str,
|
|
615
|
+
}
|
|
616
|
+
return await self._signed_request_via_session("POST", "/order", payload)
|
|
617
|
+
|
|
618
|
+
async def _calculate_market_price(
|
|
619
|
+
self,
|
|
620
|
+
*,
|
|
621
|
+
token_id: str,
|
|
622
|
+
side: str,
|
|
623
|
+
amount: float,
|
|
624
|
+
order_type: str,
|
|
625
|
+
) -> float:
|
|
626
|
+
side_flag = side.upper()
|
|
627
|
+
if side_flag not in {"BUY", "SELL"}:
|
|
628
|
+
raise ValueError("side must be 'BUY' or 'SELL'")
|
|
629
|
+
if amount <= 0:
|
|
630
|
+
raise ValueError("amount must be greater than 0 for market pricing")
|
|
631
|
+
|
|
632
|
+
book = await self.get_order_book(token_id)
|
|
633
|
+
if not isinstance(book, Mapping):
|
|
634
|
+
raise RuntimeError("Polymarket order book unavailable for market order")
|
|
635
|
+
|
|
636
|
+
key = "asks" if side_flag == "BUY" else "bids"
|
|
637
|
+
raw_levels = book.get(key) or []
|
|
638
|
+
levels: list[tuple[float, float]] = []
|
|
639
|
+
for lvl in raw_levels:
|
|
640
|
+
try:
|
|
641
|
+
price = float(lvl.get("price"))
|
|
642
|
+
size = float(lvl.get("size"))
|
|
643
|
+
except (TypeError, ValueError):
|
|
644
|
+
continue
|
|
645
|
+
if price is None or size is None:
|
|
646
|
+
continue
|
|
647
|
+
levels.append((price, size))
|
|
648
|
+
|
|
649
|
+
if not levels:
|
|
650
|
+
raise RuntimeError(f"Polymarket market order has no {key} liquidity")
|
|
651
|
+
|
|
652
|
+
total = 0.0
|
|
653
|
+
if side_flag == "BUY":
|
|
654
|
+
for price, size in reversed(levels):
|
|
655
|
+
total += price * size
|
|
656
|
+
if total >= amount:
|
|
657
|
+
return price
|
|
658
|
+
else:
|
|
659
|
+
for price, size in reversed(levels):
|
|
660
|
+
total += size
|
|
661
|
+
if total >= amount:
|
|
662
|
+
return price
|
|
663
|
+
|
|
664
|
+
if (order_type or "FOK").upper() == "FOK":
|
|
665
|
+
raise RuntimeError("Polymarket market order exceeds available liquidity")
|
|
666
|
+
|
|
667
|
+
return levels[0][0]
|
|
668
|
+
|
|
555
669
|
async def _signed_request_via_session(
|
|
556
670
|
self, method: str, path: str, body: Mapping[str, Any] | list[Any] | None
|
|
557
671
|
) -> Any:
|
|
@@ -686,6 +800,76 @@ class Polymarket:
|
|
|
686
800
|
},
|
|
687
801
|
)
|
|
688
802
|
|
|
803
|
+
async def _build_signed_market_order(
|
|
804
|
+
self,
|
|
805
|
+
*,
|
|
806
|
+
private_key: str,
|
|
807
|
+
maker_addr: str,
|
|
808
|
+
signer_addr: str,
|
|
809
|
+
token_id: str,
|
|
810
|
+
side: str,
|
|
811
|
+
amount: float,
|
|
812
|
+
price: float,
|
|
813
|
+
tick_size: str | float | None,
|
|
814
|
+
fee_rate_bps: int | None,
|
|
815
|
+
taker: str,
|
|
816
|
+
neg_risk: bool,
|
|
817
|
+
nonce: int | None,
|
|
818
|
+
) -> dict[str, Any]:
|
|
819
|
+
side = side.upper()
|
|
820
|
+
tick = await self._resolve_tick_size(token_id, tick_size)
|
|
821
|
+
fee_bps = await self._resolve_fee_rate(token_id, fee_rate_bps)
|
|
822
|
+
|
|
823
|
+
price_d, size_d, amt_d = self._rounding_for_tick(tick)
|
|
824
|
+
price = float(self._round_normal(price, price_d))
|
|
825
|
+
if price <= 0:
|
|
826
|
+
raise ValueError("market price must be positive")
|
|
827
|
+
|
|
828
|
+
amt = float(amount)
|
|
829
|
+
if amt <= 0:
|
|
830
|
+
raise ValueError("amount must be greater than 0")
|
|
831
|
+
|
|
832
|
+
maker_amt_raw = self._round_down(amt, size_d)
|
|
833
|
+
if maker_amt_raw <= 0:
|
|
834
|
+
raise ValueError("amount too small for current tick size")
|
|
835
|
+
|
|
836
|
+
if side == "BUY":
|
|
837
|
+
taker_amt_raw = maker_amt_raw / price
|
|
838
|
+
elif side == "SELL":
|
|
839
|
+
taker_amt_raw = maker_amt_raw * price
|
|
840
|
+
else:
|
|
841
|
+
raise ValueError("side must be 'BUY' or 'SELL'")
|
|
842
|
+
|
|
843
|
+
if self._decimal_places(taker_amt_raw) > amt_d:
|
|
844
|
+
tmp = self._round_up(taker_amt_raw, amt_d + 4)
|
|
845
|
+
taker_amt_raw = tmp if self._decimal_places(tmp) <= amt_d else self._round_down(tmp, amt_d)
|
|
846
|
+
|
|
847
|
+
maker_amount = self._to_token_decimals(maker_amt_raw)
|
|
848
|
+
taker_amount = self._to_token_decimals(taker_amt_raw)
|
|
849
|
+
|
|
850
|
+
contract = self._contracts(self.chain_id, neg_risk)
|
|
851
|
+
side_flag = 0 if side == "BUY" else 1
|
|
852
|
+
sig_type = int(self.signature_type)
|
|
853
|
+
|
|
854
|
+
return Auth.sign_polymarket_order(
|
|
855
|
+
private_key=private_key,
|
|
856
|
+
chain_id=self.chain_id,
|
|
857
|
+
exchange_address=contract["exchange"],
|
|
858
|
+
order={
|
|
859
|
+
"maker": maker_addr,
|
|
860
|
+
"signer": signer_addr,
|
|
861
|
+
"taker": taker or ZERO_ADDRESS,
|
|
862
|
+
"tokenId": str(token_id),
|
|
863
|
+
"makerAmount": int(maker_amount),
|
|
864
|
+
"takerAmount": int(taker_amount),
|
|
865
|
+
"expiration": 0,
|
|
866
|
+
"nonce": int(nonce or 0),
|
|
867
|
+
"feeRateBps": int(fee_bps or 0),
|
|
868
|
+
"side": side_flag,
|
|
869
|
+
"signatureType": sig_type,
|
|
870
|
+
},
|
|
871
|
+
)
|
|
872
|
+
|
|
689
873
|
async def _resolve_tick_size(self, token_id: str, tick_size: str | float | None) -> str:
|
|
690
874
|
if tick_size is not None:
|
|
691
875
|
return str(tick_size)
|
|
@@ -878,6 +1062,7 @@ class Polymarket:
|
|
|
878
1062
|
|
|
879
1063
|
async def get_trades(self, params: Mapping[str, Any] | None = None) -> list[Any]:
|
|
880
1064
|
return await self._paginate("/data/trades", params)
|
|
1065
|
+
|
|
881
1066
|
|
|
882
1067
|
async def get_notifications(self, signature_type: int | None = None) -> Any:
|
|
883
1068
|
sig = signature_type if signature_type is not None else self.signature_type
|
|
@@ -889,12 +1074,30 @@ class Polymarket:
|
|
|
889
1074
|
return await self._rest("DELETE", "/notifications", params=params)
|
|
890
1075
|
|
|
891
1076
|
async def get_balance_allowance(self, **params: Any) -> Any:
|
|
892
|
-
|
|
1077
|
+
query = dict(params or {})
|
|
1078
|
+
query.setdefault("signature_type", self.signature_type)
|
|
1079
|
+
return await self._rest("GET", "/balance-allowance", params=query or None)
|
|
893
1080
|
|
|
894
1081
|
async def update_balance_allowance(self, **params: Any) -> Any:
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1082
|
+
body = dict(params or {})
|
|
1083
|
+
body.setdefault("signature_type", self.signature_type)
|
|
1084
|
+
return await self._rest("POST", "/balance-allowance/update", json=body or None)
|
|
1085
|
+
|
|
1086
|
+
async def get_usdc(self):
|
|
1087
|
+
data = await self.get_balance_allowance(asset_type='COLLATERAL')
|
|
1088
|
+
balance = float(data.get('balance', 0.0))
|
|
1089
|
+
if balance > 0:
|
|
1090
|
+
balance = balance / 1e6
|
|
1091
|
+
return balance
|
|
1092
|
+
|
|
1093
|
+
async def get_position(self, token_id: str) -> Any:
|
|
1094
|
+
data = await self.get_balance_allowance(asset_type='CONDITIONAL', token_id=token_id)
|
|
1095
|
+
position = float(data.get('balance', 0.0))
|
|
1096
|
+
if position > 0:
|
|
1097
|
+
position = position / 1e6
|
|
1098
|
+
return position
|
|
1099
|
+
|
|
1100
|
+
async def get_usdc_web3(
|
|
898
1101
|
self,
|
|
899
1102
|
wallet: str = None,
|
|
900
1103
|
rpc_urls: Sequence[str] | None = 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
|