polynode 0.6.0__tar.gz → 0.6.2__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 (42) hide show
  1. {polynode-0.6.0 → polynode-0.6.2}/PKG-INFO +1 -1
  2. {polynode-0.6.0 → polynode-0.6.2}/polynode/short_form.py +1 -1
  3. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/__init__.py +5 -0
  4. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/clob_api.py +8 -0
  5. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/cosigner.py +7 -0
  6. polynode-0.6.2/polynode/trading/position_management.py +104 -0
  7. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/trader.py +29 -0
  8. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/types.py +51 -0
  9. {polynode-0.6.0 → polynode-0.6.2}/pyproject.toml +1 -1
  10. {polynode-0.6.0 → polynode-0.6.2}/.gitignore +0 -0
  11. {polynode-0.6.0 → polynode-0.6.2}/README.md +0 -0
  12. {polynode-0.6.0 → polynode-0.6.2}/polynode/__init__.py +0 -0
  13. {polynode-0.6.0 → polynode-0.6.2}/polynode/_version.py +0 -0
  14. {polynode-0.6.0 → polynode-0.6.2}/polynode/cache/__init__.py +0 -0
  15. {polynode-0.6.0 → polynode-0.6.2}/polynode/client.py +0 -0
  16. {polynode-0.6.0 → polynode-0.6.2}/polynode/engine.py +0 -0
  17. {polynode-0.6.0 → polynode-0.6.2}/polynode/errors.py +0 -0
  18. {polynode-0.6.0 → polynode-0.6.2}/polynode/orderbook.py +0 -0
  19. {polynode-0.6.0 → polynode-0.6.2}/polynode/orderbook_state.py +0 -0
  20. {polynode-0.6.0 → polynode-0.6.2}/polynode/redemption_watcher.py +0 -0
  21. {polynode-0.6.0 → polynode-0.6.2}/polynode/subscription.py +0 -0
  22. {polynode-0.6.0 → polynode-0.6.2}/polynode/testing.py +0 -0
  23. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/constants.py +0 -0
  24. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/eip712.py +0 -0
  25. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/onboarding.py +0 -0
  26. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/privy.py +0 -0
  27. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/signer.py +0 -0
  28. {polynode-0.6.0 → polynode-0.6.2}/polynode/trading/sqlite_backend.py +0 -0
  29. {polynode-0.6.0 → polynode-0.6.2}/polynode/types/__init__.py +0 -0
  30. {polynode-0.6.0 → polynode-0.6.2}/polynode/types/enums.py +0 -0
  31. {polynode-0.6.0 → polynode-0.6.2}/polynode/types/events.py +0 -0
  32. {polynode-0.6.0 → polynode-0.6.2}/polynode/types/orderbook.py +0 -0
  33. {polynode-0.6.0 → polynode-0.6.2}/polynode/types/rest.py +0 -0
  34. {polynode-0.6.0 → polynode-0.6.2}/polynode/types/short_form.py +0 -0
  35. {polynode-0.6.0 → polynode-0.6.2}/polynode/types/ws.py +0 -0
  36. {polynode-0.6.0 → polynode-0.6.2}/polynode/ws.py +0 -0
  37. {polynode-0.6.0 → polynode-0.6.2}/tests/__init__.py +0 -0
  38. {polynode-0.6.0 → polynode-0.6.2}/tests/conftest.py +0 -0
  39. {polynode-0.6.0 → polynode-0.6.2}/tests/test_client.py +0 -0
  40. {polynode-0.6.0 → polynode-0.6.2}/tests/test_orderbook.py +0 -0
  41. {polynode-0.6.0 → polynode-0.6.2}/tests/test_trading.py +0 -0
  42. {polynode-0.6.0 → polynode-0.6.2}/tests/test_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polynode
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: Python SDK for the PolyNode real-time prediction market data platform
5
5
  Project-URL: Homepage, https://polynode.dev
6
6
  Project-URL: Documentation, https://docs.polynode.dev
@@ -37,7 +37,7 @@ COIN_SYMBOLS: dict[ShortFormCoin, str] = {
37
37
  }
38
38
 
39
39
  INTERVAL_VARIANT: dict[ShortFormInterval, str] = {
40
- "5m": "five", "15m": "fifteen", "1h": "sixty",
40
+ "5m": "fiveminute", "15m": "fifteen", "1h": "hourly",
41
41
  }
42
42
 
43
43
  WINDOW_SECONDS: dict[ShortFormInterval, int] = {
@@ -5,11 +5,13 @@ from .constants import * # noqa: F401, F403
5
5
  from .signer import NormalizedSigner, normalize_signer
6
6
  from .cosigner import build_l2_headers, send_via_cosigner
7
7
  from .onboarding import derive_safe_address, derive_proxy_address, detect_wallet_type
8
+ from .position_management import build_split_txn, build_merge_txn, build_convert_txn
8
9
  from .trader import PolyNodeTrader
9
10
  from .privy import PrivySigner, PrivyConfig
10
11
 
11
12
  __all__ = [
12
13
  "PolyNodeTrader",
14
+ "BuilderCredentials",
13
15
  "PrivySigner",
14
16
  "PrivyConfig",
15
17
  "NormalizedSigner",
@@ -19,4 +21,7 @@ __all__ = [
19
21
  "derive_safe_address",
20
22
  "derive_proxy_address",
21
23
  "detect_wallet_type",
24
+ "build_split_txn",
25
+ "build_merge_txn",
26
+ "build_convert_txn",
22
27
  ]
@@ -17,6 +17,7 @@ async def post_order(
17
17
  credentials: dict[str, str],
18
18
  wallet_address: str,
19
19
  order_body: str,
20
+ builder_credentials: Any | None = None,
20
21
  ) -> dict[str, Any]:
21
22
  """Submit a signed order to the CLOB."""
22
23
  from .cosigner import send_via_cosigner
@@ -36,6 +37,7 @@ async def post_order(
36
37
  polynode_key,
37
38
  fallback_direct,
38
39
  {"method": "POST", "path": "/order", "body": order_body, "headers": headers},
40
+ builder_credentials=builder_credentials,
39
41
  )
40
42
 
41
43
 
@@ -46,6 +48,7 @@ async def cancel_order(
46
48
  credentials: dict[str, str],
47
49
  wallet_address: str,
48
50
  order_id: str,
51
+ builder_credentials: Any | None = None,
49
52
  ) -> dict[str, Any]:
50
53
  """Cancel a specific order."""
51
54
  from .cosigner import send_via_cosigner
@@ -67,6 +70,7 @@ async def cancel_order(
67
70
  polynode_key,
68
71
  fallback_direct,
69
72
  {"method": "DELETE", "path": "/order", "body": body, "headers": headers},
73
+ builder_credentials=builder_credentials,
70
74
  )
71
75
 
72
76
 
@@ -77,6 +81,7 @@ async def cancel_all_orders(
77
81
  credentials: dict[str, str],
78
82
  wallet_address: str,
79
83
  market: str | None = None,
84
+ builder_credentials: Any | None = None,
80
85
  ) -> dict[str, Any]:
81
86
  """Cancel all orders, optionally for a specific market."""
82
87
  from .cosigner import send_via_cosigner
@@ -99,6 +104,7 @@ async def cancel_all_orders(
99
104
  polynode_key,
100
105
  fallback_direct,
101
106
  {"method": "DELETE", "path": path, "body": body, "headers": headers},
107
+ builder_credentials=builder_credentials,
102
108
  )
103
109
 
104
110
 
@@ -110,6 +116,7 @@ async def get_open_orders(
110
116
  wallet_address: str,
111
117
  market: str | None = None,
112
118
  asset_id: str | None = None,
119
+ builder_credentials: Any | None = None,
113
120
  ) -> list[dict[str, Any]]:
114
121
  """Get open orders from the CLOB."""
115
122
  from .cosigner import send_via_cosigner
@@ -137,6 +144,7 @@ async def get_open_orders(
137
144
  polynode_key,
138
145
  fallback_direct,
139
146
  {"method": "GET", "path": path, "headers": headers},
147
+ builder_credentials=builder_credentials,
140
148
  )
141
149
 
142
150
  if isinstance(result, list):
@@ -48,10 +48,17 @@ async def send_via_cosigner(
48
48
  polynode_key: str,
49
49
  fallback_direct: bool,
50
50
  request: dict[str, Any],
51
+ builder_credentials: Any | None = None,
51
52
  ) -> Any:
52
53
  """Send a request through the co-signer with optional fallback to direct CLOB."""
53
54
  if cosigner_url:
54
55
  try:
56
+ if builder_credentials is not None:
57
+ request["builder_credentials"] = {
58
+ "key": builder_credentials.key,
59
+ "secret": builder_credentials.secret,
60
+ "passphrase": builder_credentials.passphrase,
61
+ }
55
62
  async with httpx.AsyncClient(timeout=10.0) as http:
56
63
  resp = await http.post(
57
64
  f"{cosigner_url}/submit",
@@ -0,0 +1,104 @@
1
+ """
2
+ Position management — build transactions for split, merge, and convert on Polymarket.
3
+
4
+ These functions return TransactionRequest objects containing the contract address
5
+ and ABI-encoded calldata. Submit them via the Polymarket relayer (gasless for
6
+ Safe wallets) or directly on-chain.
7
+
8
+ from polynode.trading.position_management import build_split_txn, build_convert_txn
9
+
10
+ # Split $100 into YES + NO tokens
11
+ tx = build_split_txn("0xabc...conditionId", 100.0, neg_risk=True)
12
+
13
+ # Convert NO positions on outcomes 0 and 1
14
+ tx = build_convert_txn("0xdef...marketId", [0, 1], 50.0)
15
+ """
16
+
17
+ from .constants import CTF, NEG_RISK_ADAPTER, USDC
18
+ from .types import TransactionRequest
19
+
20
+
21
+ def build_split_txn(condition_id: str, amount: float, neg_risk: bool = True) -> TransactionRequest:
22
+ """Build a split transaction: USDC -> YES + NO outcome tokens.
23
+
24
+ Routes to NegRiskAdapter for neg-risk markets, CTF for standard markets.
25
+ Amount is in USDC (e.g. 100.0 = $100).
26
+ """
27
+ amount_raw = int(amount * 1_000_000)
28
+
29
+ if neg_risk:
30
+ # NegRiskAdapter.splitPosition(bytes32 conditionId, uint256 amount)
31
+ data = "0xa3d7da1d" + _encode_bytes32(condition_id) + _encode_uint256(amount_raw)
32
+ return TransactionRequest(to=NEG_RISK_ADAPTER, data=data)
33
+ else:
34
+ # CTF.splitPosition(address, bytes32, bytes32, uint256[], uint256)
35
+ data = (
36
+ "0x72108503"
37
+ + _encode_address(USDC)
38
+ + "00" * 32 # parentCollectionId = bytes32(0)
39
+ + _encode_bytes32(condition_id)
40
+ + _encode_uint256(160) # offset to partition array
41
+ + _encode_uint256(amount_raw)
42
+ + _encode_uint256(2) # partition length
43
+ + _encode_uint256(1)
44
+ + _encode_uint256(2)
45
+ )
46
+ return TransactionRequest(to=CTF, data=data)
47
+
48
+
49
+ def build_merge_txn(condition_id: str, amount: float, neg_risk: bool = True) -> TransactionRequest:
50
+ """Build a merge transaction: YES + NO outcome tokens -> USDC."""
51
+ amount_raw = int(amount * 1_000_000)
52
+
53
+ if neg_risk:
54
+ # NegRiskAdapter.mergePositions(bytes32 conditionId, uint256 amount)
55
+ data = "0x5d03f453" + _encode_bytes32(condition_id) + _encode_uint256(amount_raw)
56
+ return TransactionRequest(to=NEG_RISK_ADAPTER, data=data)
57
+ else:
58
+ # CTF.mergePositions(address, bytes32, bytes32, uint256[], uint256)
59
+ data = (
60
+ "0xcad9440e"
61
+ + _encode_address(USDC)
62
+ + "00" * 32
63
+ + _encode_bytes32(condition_id)
64
+ + _encode_uint256(160)
65
+ + _encode_uint256(amount_raw)
66
+ + _encode_uint256(2)
67
+ + _encode_uint256(1)
68
+ + _encode_uint256(2)
69
+ )
70
+ return TransactionRequest(to=CTF, data=data)
71
+
72
+
73
+ def build_convert_txn(market_id: str, outcome_indices: list[int], amount: float) -> TransactionRequest:
74
+ """Build a convert transaction: NO positions -> USDC + YES on complementary outcomes.
75
+
76
+ Only works on neg-risk multi-outcome markets.
77
+ outcome_indices: which outcomes' NOs to convert (e.g. [0, 1]).
78
+ """
79
+ amount_raw = int(amount * 1_000_000)
80
+
81
+ # Compute indexSet bitmask
82
+ index_set = 0
83
+ for idx in outcome_indices:
84
+ index_set |= 1 << idx
85
+
86
+ # NegRiskAdapter.convertPositions(bytes32 marketId, uint256 indexSet, uint256 amount)
87
+ data = "0xc64748c4" + _encode_bytes32(market_id) + _encode_uint256(index_set) + _encode_uint256(amount_raw)
88
+ return TransactionRequest(to=NEG_RISK_ADAPTER, data=data)
89
+
90
+
91
+ # ── Encoding helpers ──
92
+
93
+ def _encode_bytes32(hex_str: str) -> str:
94
+ s = hex_str.removeprefix("0x")
95
+ return s.zfill(64)[:64]
96
+
97
+
98
+ def _encode_address(addr: str) -> str:
99
+ s = addr.removeprefix("0x")
100
+ return s.lower().zfill(64)
101
+
102
+
103
+ def _encode_uint256(val: int) -> str:
104
+ return hex(val)[2:].zfill(64)
@@ -60,6 +60,7 @@ class PolyNodeTrader:
60
60
  self._fallback_direct = c.fallback_direct
61
61
  self._default_sig_type = c.default_signature_type
62
62
  self._rpc_url = c.rpc_url
63
+ self._builder_credentials = c.builder_credentials
63
64
 
64
65
  self._db: TradingSqliteBackend | None = None
65
66
  self._active_signer: NormalizedSigner | None = None
@@ -382,6 +383,7 @@ class PolyNodeTrader:
382
383
  result = await send_via_cosigner(
383
384
  self._cosigner_url, self._polynode_key, self._fallback_direct,
384
385
  {"method": "POST", "path": "/order", "body": body_str, "headers": headers},
386
+ builder_credentials=self._builder_credentials,
385
387
  )
386
388
 
387
389
  order_id = result.get("orderID") or result.get("orderId")
@@ -409,6 +411,7 @@ class PolyNodeTrader:
409
411
  self._cosigner_url, self._polynode_key, self._fallback_direct,
410
412
  {"apiKey": creds.api_key, "apiSecret": creds.api_secret, "apiPassphrase": creds.api_passphrase},
411
413
  creds.wallet_address, order_id,
414
+ builder_credentials=self._builder_credentials,
412
415
  )
413
416
  return CancelResult(
414
417
  canceled=result.get("canceled", []),
@@ -421,6 +424,7 @@ class PolyNodeTrader:
421
424
  self._cosigner_url, self._polynode_key, self._fallback_direct,
422
425
  {"apiKey": creds.api_key, "apiSecret": creds.api_secret, "apiPassphrase": creds.api_passphrase},
423
426
  creds.wallet_address, market,
427
+ builder_credentials=self._builder_credentials,
424
428
  )
425
429
  return CancelResult(
426
430
  canceled=result.get("canceled", []),
@@ -435,6 +439,7 @@ class PolyNodeTrader:
435
439
  self._cosigner_url, self._polynode_key, self._fallback_direct,
436
440
  {"apiKey": creds.api_key, "apiSecret": creds.api_secret, "apiPassphrase": creds.api_passphrase},
437
441
  creds.wallet_address, market, asset_id,
442
+ builder_credentials=self._builder_credentials,
438
443
  )
439
444
  return [
440
445
  OpenOrder(
@@ -473,6 +478,30 @@ class PolyNodeTrader:
473
478
  db.upsert_market_meta(meta)
474
479
  return meta
475
480
 
481
+ # ── Position Management (split/merge/convert) ──
482
+
483
+ def split(self, params: "SplitParams") -> "TransactionRequest":
484
+ """Build a split transaction: USDC -> YES + NO outcome tokens.
485
+
486
+ Returns a TransactionRequest to submit via the Polymarket relayer or on-chain.
487
+ For gasless execution, use the TypeScript SDK's trader.split().
488
+ """
489
+ from .position_management import build_split_txn
490
+ return build_split_txn(params.condition_id, params.amount, neg_risk=True)
491
+
492
+ def merge(self, params: "MergeParams") -> "TransactionRequest":
493
+ """Build a merge transaction: YES + NO outcome tokens -> USDC."""
494
+ from .position_management import build_merge_txn
495
+ return build_merge_txn(params.condition_id, params.amount, neg_risk=True)
496
+
497
+ def convert(self, params: "ConvertParams") -> "TransactionRequest":
498
+ """Build a convert transaction: NO positions -> USDC + YES on complementary outcomes.
499
+
500
+ Only works on neg-risk multi-outcome markets.
501
+ """
502
+ from .position_management import build_convert_txn
503
+ return build_convert_txn(params.market_id, params.outcome_indices, params.amount)
504
+
476
505
  # ── History ──
477
506
 
478
507
  def get_order_history(self, params: HistoryParams | None = None) -> list[OrderHistoryRow]:
@@ -33,6 +33,14 @@ class GeneratedWallet:
33
33
  address: str
34
34
 
35
35
 
36
+ @dataclass
37
+ class BuilderCredentials:
38
+ """Polymarket builder credentials for order attribution."""
39
+ key: str
40
+ secret: str
41
+ passphrase: str
42
+
43
+
36
44
  @dataclass
37
45
  class TraderConfig:
38
46
  polynode_key: str = ""
@@ -41,6 +49,7 @@ class TraderConfig:
41
49
  fallback_direct: bool = True
42
50
  default_signature_type: SignatureType = SignatureType.POLY_GNOSIS_SAFE
43
51
  rpc_url: str = "https://polygon-bor-rpc.publicnode.com"
52
+ builder_credentials: BuilderCredentials | None = None
44
53
 
45
54
 
46
55
  @dataclass
@@ -144,6 +153,48 @@ class OpenOrder:
144
153
  order_type: str
145
154
 
146
155
 
156
+ # ── Position Management (split/merge/convert) ──
157
+
158
+
159
+ @dataclass
160
+ class SplitParams:
161
+ """Parameters for splitting USDC into YES + NO outcome tokens."""
162
+ condition_id: str
163
+ amount: float # USDC amount (e.g. 100.0 = $100)
164
+
165
+
166
+ @dataclass
167
+ class MergeParams:
168
+ """Parameters for merging YES + NO outcome tokens back into USDC."""
169
+ condition_id: str
170
+ amount: float
171
+
172
+
173
+ @dataclass
174
+ class ConvertParams:
175
+ """Parameters for converting NO positions on selected outcomes.
176
+ Only works on neg-risk multi-outcome markets."""
177
+ market_id: str
178
+ outcome_indices: list[int] # e.g. [0, 1]
179
+ amount: float
180
+
181
+
182
+ @dataclass
183
+ class TransactionRequest:
184
+ """A pre-built transaction ready for submission."""
185
+ to: str
186
+ data: str # hex-encoded calldata
187
+ value: str = "0"
188
+
189
+
190
+ @dataclass
191
+ class PositionResult:
192
+ """Result of a position management operation."""
193
+ success: bool
194
+ tx_hash: str | None = None
195
+ error: str | None = None
196
+
197
+
147
198
  @dataclass
148
199
  class MarketMeta:
149
200
  token_id: str
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "polynode"
7
- version = "0.6.0"
7
+ version = "0.6.2"
8
8
  description = "Python SDK for the PolyNode real-time prediction market data platform"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
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