poly-position-watcher 0.2.7__tar.gz → 0.2.9__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.
- {poly_position_watcher-0.2.7/poly_position_watcher.egg-info → poly_position_watcher-0.2.9}/PKG-INFO +22 -4
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/README.md +21 -3
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/_version.py +1 -1
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/api_worker.py +12 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/common/enums.py +10 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/position_service.py +44 -4
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/schema/position_model.py +9 -4
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/trade_calculator.py +21 -5
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9/poly_position_watcher.egg-info}/PKG-INFO +22 -4
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/pyproject.toml +1 -1
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/setup.py +1 -1
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/LICENSE +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/MANIFEST.in +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/__init__.py +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/common/__init__.py +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/common/logger.py +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/schema/__init__.py +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/schema/base.py +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/schema/common_model.py +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/wss_worker.py +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher.egg-info/SOURCES.txt +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher.egg-info/dependency_links.txt +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher.egg-info/requires.txt +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher.egg-info/top_level.txt +0 -0
- {poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/setup.cfg +0 -0
{poly_position_watcher-0.2.7/poly_position_watcher.egg-info → poly_position_watcher-0.2.9}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: poly-position-watcher
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: polymarket proxy wallet redeem
|
|
5
5
|
Home-page: https://github.com/tosmart01/polymarket-position-watcher
|
|
6
6
|
Author: pinbar
|
|
@@ -19,7 +19,11 @@ Dynamic: requires-python
|
|
|
19
19
|
|
|
20
20
|
# poly-position-watcher
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+

|
|
23
|
+

|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
[English](README.md) | [中文](README.zh.md)
|
|
23
27
|
|
|
24
28
|
## Overview
|
|
25
29
|
|
|
@@ -28,6 +32,9 @@ English README (default). For Chinese: [README.zh.md](README.zh.md)
|
|
|
28
32
|
- WSS real-time tracking for `TRADE` and `ORDER` (positions + orders)
|
|
29
33
|
- HTTP polling fallback for reliability
|
|
30
34
|
- Optional fee calculation (toggle + custom formula)
|
|
35
|
+
- Position fields for fill checks:
|
|
36
|
+
`size` (post-fee net size), `original_size` (pre-fee net size), `sellable_size` (on-chain confirmed size), `fee_amount` (accumulated fee amount)
|
|
37
|
+
- Failed trades are detected and returned on positions (`has_failed`, `failed_trades`)
|
|
31
38
|
|
|
32
39
|
**Note: WSS disconnects are auto-detected and reconnected.**
|
|
33
40
|
|
|
@@ -65,6 +72,10 @@ with PositionWatcherService(
|
|
|
65
72
|
order: OrderMessage = service.get_order("<order_id>")
|
|
66
73
|
print(position)
|
|
67
74
|
print(order)
|
|
75
|
+
if position:
|
|
76
|
+
print("size(post-fee):", position.size)
|
|
77
|
+
print("size(pre-fee):", position.original_size)
|
|
78
|
+
print("fee_amount:", position.fee_amount)
|
|
68
79
|
service.show_positions(limit=10)
|
|
69
80
|
service.show_orders(limit=10)
|
|
70
81
|
|
|
@@ -107,17 +118,22 @@ OrderMessage(
|
|
|
107
118
|
UserPosition(
|
|
108
119
|
price: 0.0,
|
|
109
120
|
size: 0.0,
|
|
121
|
+
original_size: 0.0,
|
|
110
122
|
volume: 0.0,
|
|
123
|
+
fee_amount: 0.0,
|
|
124
|
+
sellable_size: 0.0,
|
|
111
125
|
token_id: '',
|
|
112
126
|
last_update: 0.0,
|
|
113
127
|
market_id: None,
|
|
114
128
|
outcome: None,
|
|
115
|
-
created_at: None
|
|
129
|
+
created_at: None,
|
|
130
|
+
has_failed: False,
|
|
131
|
+
failed_trades: []
|
|
116
132
|
)
|
|
117
133
|
```
|
|
118
134
|
|
|
119
135
|
|
|
120
|
-
**Full example (`examples/
|
|
136
|
+
**Full example (`examples/example.py`)**
|
|
121
137
|
|
|
122
138
|
## Pretty printing
|
|
123
139
|
|
|
@@ -136,6 +152,8 @@ Some Polymarket markets enable taker fee / maker rebate. This library **fully su
|
|
|
136
152
|
- Enable with `enable_fee_calc=True` to apply fees using `feeRateBps` from trades/orders
|
|
137
153
|
- Customize with `fee_calc_fn` if you need a different formula
|
|
138
154
|
- Disable (default) if you prefer pre-fee positions
|
|
155
|
+
- Returned position fields:
|
|
156
|
+
`size` = post-fee net size, `original_size` = pre-fee net size, `fee_amount` = accumulated fee amount
|
|
139
157
|
|
|
140
158
|
Default fee formula (when `fee_calc_fn` is not provided):
|
|
141
159
|
`fee = 0.25 * (p * (1 - p)) ** 2 * (fee_rate_bps / 1000)`, and `new_size = (1 - fee) * size`.
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# poly-position-watcher
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
[English](README.md) | [中文](README.zh.md)
|
|
4
8
|
|
|
5
9
|
## Overview
|
|
6
10
|
|
|
@@ -9,6 +13,9 @@ English README (default). For Chinese: [README.zh.md](README.zh.md)
|
|
|
9
13
|
- WSS real-time tracking for `TRADE` and `ORDER` (positions + orders)
|
|
10
14
|
- HTTP polling fallback for reliability
|
|
11
15
|
- Optional fee calculation (toggle + custom formula)
|
|
16
|
+
- Position fields for fill checks:
|
|
17
|
+
`size` (post-fee net size), `original_size` (pre-fee net size), `sellable_size` (on-chain confirmed size), `fee_amount` (accumulated fee amount)
|
|
18
|
+
- Failed trades are detected and returned on positions (`has_failed`, `failed_trades`)
|
|
12
19
|
|
|
13
20
|
**Note: WSS disconnects are auto-detected and reconnected.**
|
|
14
21
|
|
|
@@ -46,6 +53,10 @@ with PositionWatcherService(
|
|
|
46
53
|
order: OrderMessage = service.get_order("<order_id>")
|
|
47
54
|
print(position)
|
|
48
55
|
print(order)
|
|
56
|
+
if position:
|
|
57
|
+
print("size(post-fee):", position.size)
|
|
58
|
+
print("size(pre-fee):", position.original_size)
|
|
59
|
+
print("fee_amount:", position.fee_amount)
|
|
49
60
|
service.show_positions(limit=10)
|
|
50
61
|
service.show_orders(limit=10)
|
|
51
62
|
|
|
@@ -88,17 +99,22 @@ OrderMessage(
|
|
|
88
99
|
UserPosition(
|
|
89
100
|
price: 0.0,
|
|
90
101
|
size: 0.0,
|
|
102
|
+
original_size: 0.0,
|
|
91
103
|
volume: 0.0,
|
|
104
|
+
fee_amount: 0.0,
|
|
105
|
+
sellable_size: 0.0,
|
|
92
106
|
token_id: '',
|
|
93
107
|
last_update: 0.0,
|
|
94
108
|
market_id: None,
|
|
95
109
|
outcome: None,
|
|
96
|
-
created_at: None
|
|
110
|
+
created_at: None,
|
|
111
|
+
has_failed: False,
|
|
112
|
+
failed_trades: []
|
|
97
113
|
)
|
|
98
114
|
```
|
|
99
115
|
|
|
100
116
|
|
|
101
|
-
**Full example (`examples/
|
|
117
|
+
**Full example (`examples/example.py`)**
|
|
102
118
|
|
|
103
119
|
## Pretty printing
|
|
104
120
|
|
|
@@ -117,6 +133,8 @@ Some Polymarket markets enable taker fee / maker rebate. This library **fully su
|
|
|
117
133
|
- Enable with `enable_fee_calc=True` to apply fees using `feeRateBps` from trades/orders
|
|
118
134
|
- Customize with `fee_calc_fn` if you need a different formula
|
|
119
135
|
- Disable (default) if you prefer pre-fee positions
|
|
136
|
+
- Returned position fields:
|
|
137
|
+
`size` = post-fee net size, `original_size` = pre-fee net size, `fee_amount` = accumulated fee amount
|
|
120
138
|
|
|
121
139
|
Default fee formula (when `fee_calc_fn` is not provided):
|
|
122
140
|
`fee = 0.25 * (p * (1 - p)) ** 2 * (fee_rate_bps / 1000)`, and `new_size = (1 - fee) * size`.
|
{poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/api_worker.py
RENAMED
|
@@ -262,6 +262,18 @@ class HttpFallbackManager:
|
|
|
262
262
|
if order_ids:
|
|
263
263
|
self.orders.update(order_ids)
|
|
264
264
|
logger.info(f"Added HTTP monitoring: {len(market_ids or [])} markets, {len(order_ids or [])} orders")
|
|
265
|
+
|
|
266
|
+
def set_markets(self, market_ids: list[str] | None = None):
|
|
267
|
+
"""Replace monitored markets with the provided list."""
|
|
268
|
+
with self._lock:
|
|
269
|
+
self.markets = set(market_ids or [])
|
|
270
|
+
logger.info(f"Set HTTP monitoring markets: {len(self.markets)} total")
|
|
271
|
+
|
|
272
|
+
def set_orders(self, order_ids: list[str] | None = None):
|
|
273
|
+
"""Replace monitored orders with the provided list."""
|
|
274
|
+
with self._lock:
|
|
275
|
+
self.orders = set(order_ids or [])
|
|
276
|
+
logger.info(f"Set HTTP monitoring orders: {len(self.orders)} total")
|
|
265
277
|
|
|
266
278
|
def remove(self, market_ids: list[str] = None, order_ids: list[str] = None):
|
|
267
279
|
"""Remove markets/orders from monitoring."""
|
{poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/common/enums.py
RENAMED
|
@@ -18,3 +18,13 @@ class MarketEvent(str, Enum):
|
|
|
18
18
|
PRICE_CHANGE = "price_change"
|
|
19
19
|
TICK_SIZE_CHANGE = "tick_size_change"
|
|
20
20
|
BOOK = "book"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TradeStatus(str, Enum):
|
|
24
|
+
"""Trade status values from Polymarket."""
|
|
25
|
+
|
|
26
|
+
MATCHED = "MATCHED"
|
|
27
|
+
MINED = "MINED"
|
|
28
|
+
CONFIRMED = "CONFIRMED"
|
|
29
|
+
RETRYING = "RETRYING"
|
|
30
|
+
FAILED = "FAILED"
|
|
@@ -12,7 +12,7 @@ from queue import Queue, Empty
|
|
|
12
12
|
from collections import defaultdict
|
|
13
13
|
from typing import Callable, Dict, List
|
|
14
14
|
|
|
15
|
-
from poly_position_watcher.common.enums import Side
|
|
15
|
+
from poly_position_watcher.common.enums import Side, TradeStatus
|
|
16
16
|
from poly_position_watcher.common.logger import logger
|
|
17
17
|
from poly_position_watcher.api_worker import APIWorker, HttpFallbackManager
|
|
18
18
|
|
|
@@ -27,7 +27,7 @@ from rich.console import Console
|
|
|
27
27
|
from rich.table import Table
|
|
28
28
|
from poly_position_watcher.wss_worker import PolymarketUserWS
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
FAILED_TRADE = TradeStatus.FAILED
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class PositionStore:
|
|
@@ -139,8 +139,14 @@ class PositionStore:
|
|
|
139
139
|
def build_position(
|
|
140
140
|
self, trades: list[TradeMessage], token_id, outcome: str
|
|
141
141
|
) -> UserPosition | None:
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
def _status_is(trade: TradeMessage, status: TradeStatus) -> bool:
|
|
143
|
+
return trade.status == status or trade.status == status.value
|
|
144
|
+
|
|
145
|
+
filled_trades = [i for i in trades if _status_is(i, FAILED_TRADE)]
|
|
146
|
+
success_trades = [i for i in trades if not _status_is(i, FAILED_TRADE)]
|
|
147
|
+
confirmed_trades = [
|
|
148
|
+
i for i in success_trades if _status_is(i, TradeStatus.CONFIRMED)
|
|
149
|
+
]
|
|
144
150
|
has_failed = bool(len(filled_trades))
|
|
145
151
|
if has_failed:
|
|
146
152
|
filled_size = sum([i.size for i in filled_trades])
|
|
@@ -152,10 +158,22 @@ class PositionStore:
|
|
|
152
158
|
enable_fee_calc=self.enable_fee_calc,
|
|
153
159
|
fee_calc_fn=self.fee_calc_fn,
|
|
154
160
|
)
|
|
161
|
+
sellable_size = 0.0
|
|
162
|
+
if confirmed_trades:
|
|
163
|
+
confirmed_result = calculate_position_from_trades(
|
|
164
|
+
confirmed_trades,
|
|
165
|
+
user_address=self.user_address,
|
|
166
|
+
enable_fee_calc=self.enable_fee_calc,
|
|
167
|
+
fee_calc_fn=self.fee_calc_fn,
|
|
168
|
+
)
|
|
169
|
+
sellable_size = confirmed_result.size
|
|
155
170
|
current = UserPosition(
|
|
156
171
|
price=position_result.avg_price,
|
|
157
172
|
size=position_result.size,
|
|
173
|
+
original_size=position_result.original_size,
|
|
158
174
|
volume=position_result.amount,
|
|
175
|
+
fee_amount=position_result.fee_amount,
|
|
176
|
+
sellable_size=sellable_size,
|
|
159
177
|
token_id=token_id,
|
|
160
178
|
last_update=position_result.last_update,
|
|
161
179
|
market_id=trades[0].market,
|
|
@@ -591,6 +609,28 @@ class PositionWatcherService:
|
|
|
591
609
|
|
|
592
610
|
self._http_fallback.remove(market_ids=market_ids, order_ids=order_ids)
|
|
593
611
|
|
|
612
|
+
def set_market_http_listen(self, market_ids: list[str] = None):
|
|
613
|
+
"""
|
|
614
|
+
Replace HTTP fallback market monitoring list.
|
|
615
|
+
|
|
616
|
+
:param market_ids: List of market (condition) IDs to monitor
|
|
617
|
+
"""
|
|
618
|
+
if not self.enable_http_fallback or not self._http_fallback:
|
|
619
|
+
return
|
|
620
|
+
|
|
621
|
+
self._http_fallback.set_markets(market_ids=market_ids)
|
|
622
|
+
|
|
623
|
+
def set_order_http_listen(self, order_ids: list[str] = None):
|
|
624
|
+
"""
|
|
625
|
+
Replace HTTP fallback order monitoring list.
|
|
626
|
+
|
|
627
|
+
:param order_ids: List of order IDs to monitor
|
|
628
|
+
"""
|
|
629
|
+
if not self.enable_http_fallback or not self._http_fallback:
|
|
630
|
+
return
|
|
631
|
+
|
|
632
|
+
self._http_fallback.set_orders(order_ids=order_ids)
|
|
633
|
+
|
|
594
634
|
def clear_http(self):
|
|
595
635
|
"""Clear all HTTP fallback monitoring (threads keep running)."""
|
|
596
636
|
if not self.enable_http_fallback or not self._http_fallback:
|
|
@@ -9,7 +9,7 @@ from typing import List, Optional, Literal
|
|
|
9
9
|
|
|
10
10
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
11
11
|
|
|
12
|
-
from poly_position_watcher.common.enums import Side
|
|
12
|
+
from poly_position_watcher.common.enums import Side, TradeStatus
|
|
13
13
|
from poly_position_watcher.schema.base import PrettyPrintBaseModel
|
|
14
14
|
|
|
15
15
|
|
|
@@ -52,7 +52,7 @@ class TradeMessage(BaseModel):
|
|
|
52
52
|
price: float
|
|
53
53
|
side: Side
|
|
54
54
|
size: float
|
|
55
|
-
status: str
|
|
55
|
+
status: TradeStatus | str
|
|
56
56
|
taker_order_id: str
|
|
57
57
|
timestamp: int | None = None
|
|
58
58
|
match_time: int | None = None
|
|
@@ -122,7 +122,10 @@ class OrderMessage(PrettyPrintBaseModel):
|
|
|
122
122
|
class UserPosition(PrettyPrintBaseModel):
|
|
123
123
|
price: float
|
|
124
124
|
size: float
|
|
125
|
+
original_size: float = 0.0
|
|
125
126
|
volume: float
|
|
127
|
+
fee_amount: float = 0.0
|
|
128
|
+
sellable_size: float = 0.0
|
|
126
129
|
token_id: Optional[str] = None
|
|
127
130
|
last_update: float
|
|
128
131
|
market_id: Optional[str] = None
|
|
@@ -133,8 +136,8 @@ class UserPosition(PrettyPrintBaseModel):
|
|
|
133
136
|
failed_trades: list[TradeMessage] = Field(default_factory=list)
|
|
134
137
|
|
|
135
138
|
@property
|
|
136
|
-
def failed_size(self) -> float
|
|
137
|
-
return sum(
|
|
139
|
+
def failed_size(self) -> float:
|
|
140
|
+
return sum(i.size for i in self.failed_trades)
|
|
138
141
|
|
|
139
142
|
|
|
140
143
|
class PositionDetails(BaseModel):
|
|
@@ -149,9 +152,11 @@ class PositionResult(PrettyPrintBaseModel):
|
|
|
149
152
|
"""仓位计算结果"""
|
|
150
153
|
|
|
151
154
|
size: float
|
|
155
|
+
original_size: float = 0.0
|
|
152
156
|
avg_price: float
|
|
153
157
|
realized_pnl: float
|
|
154
158
|
amount: float
|
|
159
|
+
fee_amount: float = 0.0
|
|
155
160
|
position_value: Optional[float] = None
|
|
156
161
|
unrealized_pnl: Optional[float] = None
|
|
157
162
|
total_pnl: Optional[float] = None
|
|
@@ -39,18 +39,23 @@ def calculate_position_from_trades(
|
|
|
39
39
|
|
|
40
40
|
buy_events = []
|
|
41
41
|
sell_events = []
|
|
42
|
+
total_original_size = 0.0
|
|
42
43
|
|
|
43
44
|
def apply_fee(
|
|
44
45
|
size: float, price: float, fee_rate_bps: float, trader_side: str | None
|
|
45
|
-
) -> float:
|
|
46
|
+
) -> tuple[float, float]:
|
|
46
47
|
if (
|
|
47
48
|
not enable_fee_calc
|
|
48
49
|
or fee_rate_bps <= 0
|
|
49
50
|
or trader_side != "TAKER"
|
|
50
51
|
):
|
|
51
|
-
return size
|
|
52
|
+
return size, 0.0
|
|
52
53
|
calc = fee_calc_fn or _default_fee_calc
|
|
53
|
-
|
|
54
|
+
size_after_fee = calc(size, price, fee_rate_bps)
|
|
55
|
+
fee_amount = (size - size_after_fee) * price
|
|
56
|
+
return size_after_fee, fee_amount
|
|
57
|
+
|
|
58
|
+
total_fee_amount = 0.0
|
|
54
59
|
|
|
55
60
|
# --- 1. 解析所有交易 ---
|
|
56
61
|
for trade in trades:
|
|
@@ -61,24 +66,30 @@ def calculate_position_from_trades(
|
|
|
61
66
|
if order.maker_address.upper() != user_address.upper():
|
|
62
67
|
continue
|
|
63
68
|
is_maker_order = True
|
|
64
|
-
size = apply_fee(
|
|
69
|
+
size, fee_amount = apply_fee(
|
|
65
70
|
order.size, order.price, order.fee_rate_bps, trade.trader_side
|
|
66
71
|
)
|
|
72
|
+
total_fee_amount += fee_amount
|
|
67
73
|
|
|
68
74
|
if order.side == Side.BUY:
|
|
69
75
|
buy_events.append((size, order.price, trade.match_time))
|
|
76
|
+
total_original_size += order.size
|
|
70
77
|
else:
|
|
71
78
|
sell_events.append((-size, order.price, trade.match_time))
|
|
79
|
+
total_original_size -= order.size
|
|
72
80
|
|
|
73
81
|
# taker 部分
|
|
74
82
|
if not is_maker_order and trade.maker_address.upper() == user_address.upper():
|
|
75
|
-
size = apply_fee(
|
|
83
|
+
size, fee_amount = apply_fee(
|
|
76
84
|
trade.size, trade.price, trade.fee_rate_bps, trade.trader_side
|
|
77
85
|
)
|
|
86
|
+
total_fee_amount += fee_amount
|
|
78
87
|
if trade.side == Side.BUY:
|
|
79
88
|
buy_events.append((size, trade.price, trade.match_time))
|
|
89
|
+
total_original_size += trade.size
|
|
80
90
|
else:
|
|
81
91
|
sell_events.append((-size, trade.price, trade.match_time))
|
|
92
|
+
total_original_size -= trade.size
|
|
82
93
|
|
|
83
94
|
# --- 2. 时间排序 ---
|
|
84
95
|
all_events = sorted(buy_events + sell_events, key=lambda x: x[2])
|
|
@@ -118,20 +129,25 @@ def calculate_position_from_trades(
|
|
|
118
129
|
|
|
119
130
|
# --- 4. 计算最终持仓 ---
|
|
120
131
|
total_size = clean(sum(q[0] for q in buy_queue))
|
|
132
|
+
original_size = clean(total_original_size)
|
|
121
133
|
cost_basis = clean(sum(clean(q[0]) * q[1] for q in buy_queue))
|
|
122
134
|
|
|
123
135
|
# 若因误差产生 0.0000003 的 ghost position → 完全清掉
|
|
124
136
|
if abs(total_size) < EPS:
|
|
125
137
|
total_size = 0.0
|
|
126
138
|
cost_basis = 0.0
|
|
139
|
+
if abs(original_size) < EPS:
|
|
140
|
+
original_size = 0.0
|
|
127
141
|
|
|
128
142
|
avg_price = cost_basis / total_size if total_size != 0 else 0.0
|
|
129
143
|
|
|
130
144
|
return PositionResult(
|
|
131
145
|
size=total_size,
|
|
146
|
+
original_size=original_size,
|
|
132
147
|
avg_price=avg_price,
|
|
133
148
|
realized_pnl=realized_pnl,
|
|
134
149
|
amount=cost_basis,
|
|
150
|
+
fee_amount=total_fee_amount,
|
|
135
151
|
is_long=total_size > 0,
|
|
136
152
|
is_short=total_size < 0,
|
|
137
153
|
details=PositionDetails(
|
{poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9/poly_position_watcher.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: poly-position-watcher
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: polymarket proxy wallet redeem
|
|
5
5
|
Home-page: https://github.com/tosmart01/polymarket-position-watcher
|
|
6
6
|
Author: pinbar
|
|
@@ -19,7 +19,11 @@ Dynamic: requires-python
|
|
|
19
19
|
|
|
20
20
|
# poly-position-watcher
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+

|
|
23
|
+

|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
[English](README.md) | [中文](README.zh.md)
|
|
23
27
|
|
|
24
28
|
## Overview
|
|
25
29
|
|
|
@@ -28,6 +32,9 @@ English README (default). For Chinese: [README.zh.md](README.zh.md)
|
|
|
28
32
|
- WSS real-time tracking for `TRADE` and `ORDER` (positions + orders)
|
|
29
33
|
- HTTP polling fallback for reliability
|
|
30
34
|
- Optional fee calculation (toggle + custom formula)
|
|
35
|
+
- Position fields for fill checks:
|
|
36
|
+
`size` (post-fee net size), `original_size` (pre-fee net size), `sellable_size` (on-chain confirmed size), `fee_amount` (accumulated fee amount)
|
|
37
|
+
- Failed trades are detected and returned on positions (`has_failed`, `failed_trades`)
|
|
31
38
|
|
|
32
39
|
**Note: WSS disconnects are auto-detected and reconnected.**
|
|
33
40
|
|
|
@@ -65,6 +72,10 @@ with PositionWatcherService(
|
|
|
65
72
|
order: OrderMessage = service.get_order("<order_id>")
|
|
66
73
|
print(position)
|
|
67
74
|
print(order)
|
|
75
|
+
if position:
|
|
76
|
+
print("size(post-fee):", position.size)
|
|
77
|
+
print("size(pre-fee):", position.original_size)
|
|
78
|
+
print("fee_amount:", position.fee_amount)
|
|
68
79
|
service.show_positions(limit=10)
|
|
69
80
|
service.show_orders(limit=10)
|
|
70
81
|
|
|
@@ -107,17 +118,22 @@ OrderMessage(
|
|
|
107
118
|
UserPosition(
|
|
108
119
|
price: 0.0,
|
|
109
120
|
size: 0.0,
|
|
121
|
+
original_size: 0.0,
|
|
110
122
|
volume: 0.0,
|
|
123
|
+
fee_amount: 0.0,
|
|
124
|
+
sellable_size: 0.0,
|
|
111
125
|
token_id: '',
|
|
112
126
|
last_update: 0.0,
|
|
113
127
|
market_id: None,
|
|
114
128
|
outcome: None,
|
|
115
|
-
created_at: None
|
|
129
|
+
created_at: None,
|
|
130
|
+
has_failed: False,
|
|
131
|
+
failed_trades: []
|
|
116
132
|
)
|
|
117
133
|
```
|
|
118
134
|
|
|
119
135
|
|
|
120
|
-
**Full example (`examples/
|
|
136
|
+
**Full example (`examples/example.py`)**
|
|
121
137
|
|
|
122
138
|
## Pretty printing
|
|
123
139
|
|
|
@@ -136,6 +152,8 @@ Some Polymarket markets enable taker fee / maker rebate. This library **fully su
|
|
|
136
152
|
- Enable with `enable_fee_calc=True` to apply fees using `feeRateBps` from trades/orders
|
|
137
153
|
- Customize with `fee_calc_fn` if you need a different formula
|
|
138
154
|
- Disable (default) if you prefer pre-fee positions
|
|
155
|
+
- Returned position fields:
|
|
156
|
+
`size` = post-fee net size, `original_size` = pre-fee net size, `fee_amount` = accumulated fee amount
|
|
139
157
|
|
|
140
158
|
Default fee formula (when `fee_calc_fn` is not provided):
|
|
141
159
|
`fee = 0.25 * (p * (1 - p)) ** 2 * (fee_rate_bps / 1000)`, and `new_size = (1 - fee) * size`.
|
|
@@ -24,7 +24,7 @@ for cache_dir in HERE.rglob("__pycache__"):
|
|
|
24
24
|
|
|
25
25
|
setup(
|
|
26
26
|
name=PROJECT.get("name", "poly-position-watcher"),
|
|
27
|
-
version=PROJECT.get("version", "0.2.
|
|
27
|
+
version=PROJECT.get("version", "0.2.9"),
|
|
28
28
|
description=PROJECT.get("description", ""),
|
|
29
29
|
long_description=README_PATH.read_text(encoding="utf-8") if README_PATH.exists() else "",
|
|
30
30
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
{poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/__init__.py
RENAMED
|
File without changes
|
{poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/common/__init__.py
RENAMED
|
File without changes
|
{poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/common/logger.py
RENAMED
|
File without changes
|
{poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/schema/__init__.py
RENAMED
|
File without changes
|
{poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/schema/base.py
RENAMED
|
File without changes
|
|
File without changes
|
{poly_position_watcher-0.2.7 → poly_position_watcher-0.2.9}/poly_position_watcher/wss_worker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|