poly-position-watcher 0.3.0__tar.gz → 0.3.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.
- {poly_position_watcher-0.3.0/poly_position_watcher.egg-info → poly_position_watcher-0.3.2}/PKG-INFO +53 -2
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/README.md +52 -1
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/_version.py +1 -1
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/position_service.py +8 -5
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/schema/position_model.py +14 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/trade_calculator.py +12 -6
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2/poly_position_watcher.egg-info}/PKG-INFO +53 -2
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher.egg-info/SOURCES.txt +1 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/pyproject.toml +1 -1
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/setup.py +1 -1
- poly_position_watcher-0.3.2/tests/test_position_model.py +59 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/tests/test_trade_calculator.py +6 -6
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/LICENSE +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/MANIFEST.in +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/__init__.py +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/api_worker.py +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/common/__init__.py +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/common/enums.py +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/common/logger.py +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/schema/__init__.py +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/schema/base.py +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/schema/common_model.py +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/wss_worker.py +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher.egg-info/dependency_links.txt +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher.egg-info/requires.txt +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher.egg-info/top_level.txt +0 -0
- {poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/setup.cfg +0 -0
{poly_position_watcher-0.3.0/poly_position_watcher.egg-info → poly_position_watcher-0.3.2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: poly-position-watcher
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: polymarket proxy wallet redeem
|
|
5
5
|
Home-page: https://github.com/tosmart01/polymarket-position-watcher
|
|
6
6
|
Author: pinbar
|
|
@@ -95,6 +95,55 @@ with PositionWatcherService(
|
|
|
95
95
|
# service.clear_http() # Clear all monitoring items, threads continue running
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
Important:
|
|
99
|
+
- When `enable_fee_calc=True`, you must register market fee metadata with `set_market_fee_schedule(...)` or `set_market_fee_schedules(...)`.
|
|
100
|
+
- `get_position()` does not fetch `/markets` automatically.
|
|
101
|
+
- If a market is missing `feeSchedule`, fee calculation is skipped for that market and a warning is logged once.
|
|
102
|
+
|
|
103
|
+
Where does `feeSchedule` come from:
|
|
104
|
+
- Fetch a market or event from the Gamma API, then read the market object's `feeSchedule`.
|
|
105
|
+
- Your trade payload uses `trade.market` as the market `conditionId`, so register fee metadata with `conditionId` as the key.
|
|
106
|
+
- Official docs:
|
|
107
|
+
[Fees](https://docs.polymarket.com/trading/fees),
|
|
108
|
+
[Get event by id](https://docs.polymarket.com/api-reference/events/get-event-by-id),
|
|
109
|
+
[List markets](https://docs.polymarket.com/api-reference/markets/list-markets),
|
|
110
|
+
[Get market by slug](https://docs.polymarket.com/api-reference/markets/get-market-by-slug)
|
|
111
|
+
|
|
112
|
+
Example: fetch an event and register all nested market fee schedules
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import requests
|
|
116
|
+
|
|
117
|
+
event = requests.get(
|
|
118
|
+
"https://gamma-api.polymarket.com/events/<event_id>",
|
|
119
|
+
timeout=10,
|
|
120
|
+
).json()
|
|
121
|
+
|
|
122
|
+
fee_schedule_map = {
|
|
123
|
+
market["conditionId"]: market.get("feeSchedule")
|
|
124
|
+
for market in event.get("markets", [])
|
|
125
|
+
if market.get("feeSchedule")
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
service.set_market_fee_schedules(fee_schedule_map)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Example: fetch a single market and register its fee schedule
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import requests
|
|
135
|
+
|
|
136
|
+
market = requests.get(
|
|
137
|
+
"https://gamma-api.polymarket.com/markets/slug/<market-slug>",
|
|
138
|
+
timeout=10,
|
|
139
|
+
).json()
|
|
140
|
+
|
|
141
|
+
service.set_market_fee_schedule(
|
|
142
|
+
market["conditionId"],
|
|
143
|
+
market.get("feeSchedule"),
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
98
147
|
|
|
99
148
|
|
|
100
149
|
Example output:
|
|
@@ -155,13 +204,15 @@ Some Polymarket markets enable taker fee / maker rebate. This library supports f
|
|
|
155
204
|
|
|
156
205
|
- Enable with `enable_fee_calc=True`
|
|
157
206
|
- Register `condition_id -> feeSchedule` through `service.set_market_fee_schedule(...)` or `service.set_market_fee_schedules(...)`
|
|
207
|
+
- This registration step is required if you want fee-aware positions; the watcher does not auto-fetch `/markets`
|
|
208
|
+
- In practice, use the Gamma market/event response's `market.get("feeSchedule")`
|
|
158
209
|
- Optionally override the fee handler with `fee_calc_fn`
|
|
159
210
|
- Disable (default) if you prefer pre-fee positions
|
|
160
211
|
- Returned position fields:
|
|
161
212
|
`size` = post-fee net size, `original_size` = pre-fee net size, `fee_amount` = accumulated fee amount
|
|
162
213
|
|
|
163
214
|
Default fee formula (when `fee_calc_fn` is not provided):
|
|
164
|
-
`fee = size *
|
|
215
|
+
`fee = size * rate * price * (1 - price)`.
|
|
165
216
|
|
|
166
217
|
On taker buys, the fee is deducted in shares, so `size` is reduced by `fee / price`.
|
|
167
218
|
On taker sells, the fee is charged in USDC, so position size is unchanged and only `fee_amount` increases.
|
|
@@ -76,6 +76,55 @@ with PositionWatcherService(
|
|
|
76
76
|
# service.clear_http() # Clear all monitoring items, threads continue running
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
Important:
|
|
80
|
+
- When `enable_fee_calc=True`, you must register market fee metadata with `set_market_fee_schedule(...)` or `set_market_fee_schedules(...)`.
|
|
81
|
+
- `get_position()` does not fetch `/markets` automatically.
|
|
82
|
+
- If a market is missing `feeSchedule`, fee calculation is skipped for that market and a warning is logged once.
|
|
83
|
+
|
|
84
|
+
Where does `feeSchedule` come from:
|
|
85
|
+
- Fetch a market or event from the Gamma API, then read the market object's `feeSchedule`.
|
|
86
|
+
- Your trade payload uses `trade.market` as the market `conditionId`, so register fee metadata with `conditionId` as the key.
|
|
87
|
+
- Official docs:
|
|
88
|
+
[Fees](https://docs.polymarket.com/trading/fees),
|
|
89
|
+
[Get event by id](https://docs.polymarket.com/api-reference/events/get-event-by-id),
|
|
90
|
+
[List markets](https://docs.polymarket.com/api-reference/markets/list-markets),
|
|
91
|
+
[Get market by slug](https://docs.polymarket.com/api-reference/markets/get-market-by-slug)
|
|
92
|
+
|
|
93
|
+
Example: fetch an event and register all nested market fee schedules
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import requests
|
|
97
|
+
|
|
98
|
+
event = requests.get(
|
|
99
|
+
"https://gamma-api.polymarket.com/events/<event_id>",
|
|
100
|
+
timeout=10,
|
|
101
|
+
).json()
|
|
102
|
+
|
|
103
|
+
fee_schedule_map = {
|
|
104
|
+
market["conditionId"]: market.get("feeSchedule")
|
|
105
|
+
for market in event.get("markets", [])
|
|
106
|
+
if market.get("feeSchedule")
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
service.set_market_fee_schedules(fee_schedule_map)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Example: fetch a single market and register its fee schedule
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import requests
|
|
116
|
+
|
|
117
|
+
market = requests.get(
|
|
118
|
+
"https://gamma-api.polymarket.com/markets/slug/<market-slug>",
|
|
119
|
+
timeout=10,
|
|
120
|
+
).json()
|
|
121
|
+
|
|
122
|
+
service.set_market_fee_schedule(
|
|
123
|
+
market["conditionId"],
|
|
124
|
+
market.get("feeSchedule"),
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
79
128
|
|
|
80
129
|
|
|
81
130
|
Example output:
|
|
@@ -136,13 +185,15 @@ Some Polymarket markets enable taker fee / maker rebate. This library supports f
|
|
|
136
185
|
|
|
137
186
|
- Enable with `enable_fee_calc=True`
|
|
138
187
|
- Register `condition_id -> feeSchedule` through `service.set_market_fee_schedule(...)` or `service.set_market_fee_schedules(...)`
|
|
188
|
+
- This registration step is required if you want fee-aware positions; the watcher does not auto-fetch `/markets`
|
|
189
|
+
- In practice, use the Gamma market/event response's `market.get("feeSchedule")`
|
|
139
190
|
- Optionally override the fee handler with `fee_calc_fn`
|
|
140
191
|
- Disable (default) if you prefer pre-fee positions
|
|
141
192
|
- Returned position fields:
|
|
142
193
|
`size` = post-fee net size, `original_size` = pre-fee net size, `fee_amount` = accumulated fee amount
|
|
143
194
|
|
|
144
195
|
Default fee formula (when `fee_calc_fn` is not provided):
|
|
145
|
-
`fee = size *
|
|
196
|
+
`fee = size * rate * price * (1 - price)`.
|
|
146
197
|
|
|
147
198
|
On taker buys, the fee is deducted in shares, so `size` is reduced by `fee / price`.
|
|
148
199
|
On taker sells, the fee is charged in USDC, so position size is unchanged and only `fee_amount` increases.
|
|
@@ -195,16 +195,19 @@ class PositionStore:
|
|
|
195
195
|
def _status_is(trade: TradeMessage, status: TradeStatus) -> bool:
|
|
196
196
|
return trade.status == status or trade.status == status.value
|
|
197
197
|
|
|
198
|
-
|
|
198
|
+
failed_trades = [i for i in trades if _status_is(i, FAILED_TRADE)]
|
|
199
199
|
success_trades = [i for i in trades if not _status_is(i, FAILED_TRADE)]
|
|
200
200
|
confirmed_trades = [
|
|
201
201
|
i for i in success_trades if _status_is(i, TradeStatus.CONFIRMED)
|
|
202
202
|
]
|
|
203
|
-
has_failed = bool(len(
|
|
203
|
+
has_failed = bool(len(failed_trades))
|
|
204
204
|
if has_failed:
|
|
205
|
-
|
|
205
|
+
failed_size = sum(i.size for i in failed_trades)
|
|
206
|
+
failed_trade_ids = [trade.id for trade in failed_trades]
|
|
206
207
|
logger.warning(
|
|
207
|
-
|
|
208
|
+
"Found failed trades, total size: {}, ids: {}",
|
|
209
|
+
failed_size,
|
|
210
|
+
failed_trade_ids,
|
|
208
211
|
)
|
|
209
212
|
market_slug = next(
|
|
210
213
|
(trade.market_slug for trade in trades if trade.market_slug), ""
|
|
@@ -240,7 +243,7 @@ class PositionStore:
|
|
|
240
243
|
created_at=datetime.fromtimestamp(position_result.last_update),
|
|
241
244
|
has_failed=has_failed,
|
|
242
245
|
market_slug=market_slug,
|
|
243
|
-
failed_trades=
|
|
246
|
+
failed_trades=failed_trades,
|
|
244
247
|
)
|
|
245
248
|
return current
|
|
246
249
|
# if exists_pos := self.positions.get(token_id):
|
|
@@ -139,6 +139,20 @@ class UserPosition(PrettyPrintBaseModel):
|
|
|
139
139
|
def failed_size(self) -> float:
|
|
140
140
|
return sum(i.size for i in self.failed_trades)
|
|
141
141
|
|
|
142
|
+
@property
|
|
143
|
+
def failed_trade_ids(self) -> list[str]:
|
|
144
|
+
return [trade.id for trade in self.failed_trades]
|
|
145
|
+
|
|
146
|
+
def __str__(self):
|
|
147
|
+
lines = []
|
|
148
|
+
for name, value in self.__dict__.items():
|
|
149
|
+
if name == "failed_trades":
|
|
150
|
+
value = self.failed_trade_ids
|
|
151
|
+
lines.append(f"{name}: {value!r}")
|
|
152
|
+
return f"{self.__class__.__name__}(\n " + ",\n ".join(lines) + "\n)"
|
|
153
|
+
|
|
154
|
+
__repr__ = __str__
|
|
155
|
+
|
|
142
156
|
|
|
143
157
|
class PositionDetails(BaseModel):
|
|
144
158
|
"""仓位详细信息"""
|
|
@@ -8,12 +8,15 @@ from collections import deque
|
|
|
8
8
|
from typing import Any, Callable, List, Mapping
|
|
9
9
|
|
|
10
10
|
from poly_position_watcher.common.enums import Side
|
|
11
|
+
from poly_position_watcher.common.logger import logger
|
|
11
12
|
from poly_position_watcher.schema.position_model import (
|
|
12
13
|
PositionResult,
|
|
13
14
|
PositionDetails,
|
|
14
15
|
TradeMessage,
|
|
15
16
|
)
|
|
16
17
|
|
|
18
|
+
_MISSING_FEE_SCHEDULE_WARNED_MARKETS: set[str] = set()
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
def _default_fee_calc(
|
|
19
22
|
size: float,
|
|
@@ -22,18 +25,15 @@ def _default_fee_calc(
|
|
|
22
25
|
fee_schedule: Mapping[str, Any],
|
|
23
26
|
) -> tuple[float, float]:
|
|
24
27
|
rate = float(fee_schedule.get("rate") or 0.0)
|
|
25
|
-
exponent = float(fee_schedule.get("exponent") or 0.0)
|
|
26
28
|
if size <= 0 or price <= 0 or rate <= 0:
|
|
27
29
|
return size, 0.0
|
|
28
30
|
|
|
29
|
-
fee_amount = round(
|
|
30
|
-
max(size * price * rate * ((price * (1 - price)) ** exponent), 0.0),
|
|
31
|
-
4,
|
|
32
|
-
)
|
|
31
|
+
fee_amount = round(max(size * rate * price * (1 - price), 0.0), 5)
|
|
33
32
|
if fee_amount <= 0:
|
|
34
33
|
return size, 0.0
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
side_value = getattr(side, "value", side)
|
|
36
|
+
if str(side_value).upper() == "BUY":
|
|
37
37
|
fee_size = fee_amount / price
|
|
38
38
|
return max(size - fee_size, 0.0), fee_amount
|
|
39
39
|
|
|
@@ -78,6 +78,12 @@ def calculate_position_from_trades(
|
|
|
78
78
|
else None
|
|
79
79
|
)
|
|
80
80
|
if not enable_fee_calc or not fee_schedule:
|
|
81
|
+
if enable_fee_calc and market_id and market_id not in _MISSING_FEE_SCHEDULE_WARNED_MARKETS:
|
|
82
|
+
logger.warning(
|
|
83
|
+
"Fee calculation enabled but feeSchedule is missing for market {}; fee is skipped until it is registered.",
|
|
84
|
+
market_id,
|
|
85
|
+
)
|
|
86
|
+
_MISSING_FEE_SCHEDULE_WARNED_MARKETS.add(market_id)
|
|
81
87
|
return size, 0.0
|
|
82
88
|
taker_only = bool(fee_schedule.get("takerOnly", False))
|
|
83
89
|
if taker_only and trader_side != "TAKER":
|
{poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2/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.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: polymarket proxy wallet redeem
|
|
5
5
|
Home-page: https://github.com/tosmart01/polymarket-position-watcher
|
|
6
6
|
Author: pinbar
|
|
@@ -95,6 +95,55 @@ with PositionWatcherService(
|
|
|
95
95
|
# service.clear_http() # Clear all monitoring items, threads continue running
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
Important:
|
|
99
|
+
- When `enable_fee_calc=True`, you must register market fee metadata with `set_market_fee_schedule(...)` or `set_market_fee_schedules(...)`.
|
|
100
|
+
- `get_position()` does not fetch `/markets` automatically.
|
|
101
|
+
- If a market is missing `feeSchedule`, fee calculation is skipped for that market and a warning is logged once.
|
|
102
|
+
|
|
103
|
+
Where does `feeSchedule` come from:
|
|
104
|
+
- Fetch a market or event from the Gamma API, then read the market object's `feeSchedule`.
|
|
105
|
+
- Your trade payload uses `trade.market` as the market `conditionId`, so register fee metadata with `conditionId` as the key.
|
|
106
|
+
- Official docs:
|
|
107
|
+
[Fees](https://docs.polymarket.com/trading/fees),
|
|
108
|
+
[Get event by id](https://docs.polymarket.com/api-reference/events/get-event-by-id),
|
|
109
|
+
[List markets](https://docs.polymarket.com/api-reference/markets/list-markets),
|
|
110
|
+
[Get market by slug](https://docs.polymarket.com/api-reference/markets/get-market-by-slug)
|
|
111
|
+
|
|
112
|
+
Example: fetch an event and register all nested market fee schedules
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import requests
|
|
116
|
+
|
|
117
|
+
event = requests.get(
|
|
118
|
+
"https://gamma-api.polymarket.com/events/<event_id>",
|
|
119
|
+
timeout=10,
|
|
120
|
+
).json()
|
|
121
|
+
|
|
122
|
+
fee_schedule_map = {
|
|
123
|
+
market["conditionId"]: market.get("feeSchedule")
|
|
124
|
+
for market in event.get("markets", [])
|
|
125
|
+
if market.get("feeSchedule")
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
service.set_market_fee_schedules(fee_schedule_map)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Example: fetch a single market and register its fee schedule
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import requests
|
|
135
|
+
|
|
136
|
+
market = requests.get(
|
|
137
|
+
"https://gamma-api.polymarket.com/markets/slug/<market-slug>",
|
|
138
|
+
timeout=10,
|
|
139
|
+
).json()
|
|
140
|
+
|
|
141
|
+
service.set_market_fee_schedule(
|
|
142
|
+
market["conditionId"],
|
|
143
|
+
market.get("feeSchedule"),
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
98
147
|
|
|
99
148
|
|
|
100
149
|
Example output:
|
|
@@ -155,13 +204,15 @@ Some Polymarket markets enable taker fee / maker rebate. This library supports f
|
|
|
155
204
|
|
|
156
205
|
- Enable with `enable_fee_calc=True`
|
|
157
206
|
- Register `condition_id -> feeSchedule` through `service.set_market_fee_schedule(...)` or `service.set_market_fee_schedules(...)`
|
|
207
|
+
- This registration step is required if you want fee-aware positions; the watcher does not auto-fetch `/markets`
|
|
208
|
+
- In practice, use the Gamma market/event response's `market.get("feeSchedule")`
|
|
158
209
|
- Optionally override the fee handler with `fee_calc_fn`
|
|
159
210
|
- Disable (default) if you prefer pre-fee positions
|
|
160
211
|
- Returned position fields:
|
|
161
212
|
`size` = post-fee net size, `original_size` = pre-fee net size, `fee_amount` = accumulated fee amount
|
|
162
213
|
|
|
163
214
|
Default fee formula (when `fee_calc_fn` is not provided):
|
|
164
|
-
`fee = size *
|
|
215
|
+
`fee = size * rate * price * (1 - price)`.
|
|
165
216
|
|
|
166
217
|
On taker buys, the fee is deducted in shares, so `size` is reduced by `fee / price`.
|
|
167
218
|
On taker sells, the fee is charged in USDC, so position size is unchanged and only `fee_amount` increases.
|
|
@@ -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.3.
|
|
27
|
+
version=PROJECT.get("version", "0.3.1"),
|
|
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",
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from poly_position_watcher.schema.position_model import TradeMessage, UserPosition
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def build_failed_trade(trade_id: str) -> TradeMessage:
|
|
9
|
+
return TradeMessage(
|
|
10
|
+
type="TRADE",
|
|
11
|
+
event_type="trade",
|
|
12
|
+
asset_id="0xtoken",
|
|
13
|
+
id=trade_id,
|
|
14
|
+
maker_orders=[],
|
|
15
|
+
transaction_hash=f"0xhash-{trade_id}",
|
|
16
|
+
market="0xmarket",
|
|
17
|
+
maker_address="0xuser",
|
|
18
|
+
outcome="YES",
|
|
19
|
+
owner="0xuser",
|
|
20
|
+
price=0.25,
|
|
21
|
+
side="BUY",
|
|
22
|
+
size=10.0,
|
|
23
|
+
status="FAILED",
|
|
24
|
+
taker_order_id=f"0xorder-{trade_id}",
|
|
25
|
+
timestamp=1,
|
|
26
|
+
match_time=1,
|
|
27
|
+
last_update=1,
|
|
28
|
+
trade_owner="0xuser",
|
|
29
|
+
trader_side="TAKER",
|
|
30
|
+
fee_rate_bps=0,
|
|
31
|
+
market_slug="test-market",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class UserPositionTests(unittest.TestCase):
|
|
36
|
+
def test_failed_trade_ids_are_exposed_in_string_output(self) -> None:
|
|
37
|
+
position = UserPosition(
|
|
38
|
+
price=0.25,
|
|
39
|
+
size=10.0,
|
|
40
|
+
original_size=10.0,
|
|
41
|
+
volume=2.5,
|
|
42
|
+
fee_amount=0.0,
|
|
43
|
+
sellable_size=10.0,
|
|
44
|
+
token_id="0xtoken",
|
|
45
|
+
last_update=1,
|
|
46
|
+
market_id="0xmarket",
|
|
47
|
+
outcome="YES",
|
|
48
|
+
has_failed=True,
|
|
49
|
+
failed_trades=[build_failed_trade("failed-1"), build_failed_trade("failed-2")],
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
self.assertEqual(position.failed_trade_ids, ["failed-1", "failed-2"])
|
|
53
|
+
rendered = str(position)
|
|
54
|
+
self.assertIn("failed_trades: ['failed-1', 'failed-2']", rendered)
|
|
55
|
+
self.assertNotIn("transaction_hash", rendered)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
unittest.main()
|
|
@@ -53,8 +53,8 @@ def build_taker_trade(
|
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
def expected_fee(size: float, price: float, rate: float
|
|
57
|
-
return round(size *
|
|
56
|
+
def expected_fee(size: float, price: float, rate: float) -> float:
|
|
57
|
+
return round(size * rate * price * (1 - price), 5)
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
class TradeCalculatorFeeTests(unittest.TestCase):
|
|
@@ -139,7 +139,7 @@ class TradeCalculatorFeeTests(unittest.TestCase):
|
|
|
139
139
|
fee_schedule_by_market={MARKET_ID: FEE_SCHEDULE},
|
|
140
140
|
)
|
|
141
141
|
|
|
142
|
-
fee_amount = expected_fee(100.0, 0.25, 0.0175
|
|
142
|
+
fee_amount = expected_fee(100.0, 0.25, 0.0175)
|
|
143
143
|
expected_size = 100.0 - fee_amount / 0.25
|
|
144
144
|
|
|
145
145
|
self.assertTrue(math.isclose(result.fee_amount, fee_amount, rel_tol=0, abs_tol=1e-9))
|
|
@@ -166,7 +166,7 @@ class TradeCalculatorFeeTests(unittest.TestCase):
|
|
|
166
166
|
fee_schedule_by_market={MARKET_ID: FEE_SCHEDULE},
|
|
167
167
|
)
|
|
168
168
|
|
|
169
|
-
fee_amount = expected_fee(100.0, 0.25, 0.0175
|
|
169
|
+
fee_amount = expected_fee(100.0, 0.25, 0.0175)
|
|
170
170
|
net_proceeds = 100.0 * 0.25 - fee_amount
|
|
171
171
|
|
|
172
172
|
self.assertTrue(math.isclose(result.fee_amount, fee_amount, rel_tol=0, abs_tol=1e-9))
|
|
@@ -183,7 +183,7 @@ class TradeCalculatorFeeTests(unittest.TestCase):
|
|
|
183
183
|
price=0.25,
|
|
184
184
|
match_time=1,
|
|
185
185
|
)
|
|
186
|
-
buy_fee = expected_fee(100.0, 0.25, 0.0175
|
|
186
|
+
buy_fee = expected_fee(100.0, 0.25, 0.0175)
|
|
187
187
|
net_buy_size = 100.0 - buy_fee / 0.25
|
|
188
188
|
|
|
189
189
|
sell_trade = build_taker_trade(
|
|
@@ -201,7 +201,7 @@ class TradeCalculatorFeeTests(unittest.TestCase):
|
|
|
201
201
|
fee_schedule_by_market={MARKET_ID: FEE_SCHEDULE},
|
|
202
202
|
)
|
|
203
203
|
|
|
204
|
-
sell_fee = expected_fee(net_buy_size, 0.4, 0.0175
|
|
204
|
+
sell_fee = expected_fee(net_buy_size, 0.4, 0.0175)
|
|
205
205
|
expected_realized_pnl = (net_buy_size * 0.4 - sell_fee) - 25.0
|
|
206
206
|
|
|
207
207
|
self.assertTrue(math.isclose(result.size, 0.0, rel_tol=0, abs_tol=1e-9))
|
|
File without changes
|
|
File without changes
|
{poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/__init__.py
RENAMED
|
File without changes
|
{poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/api_worker.py
RENAMED
|
File without changes
|
{poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/common/__init__.py
RENAMED
|
File without changes
|
{poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/common/enums.py
RENAMED
|
File without changes
|
{poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/common/logger.py
RENAMED
|
File without changes
|
{poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/schema/__init__.py
RENAMED
|
File without changes
|
{poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/schema/base.py
RENAMED
|
File without changes
|
|
File without changes
|
{poly_position_watcher-0.3.0 → poly_position_watcher-0.3.2}/poly_position_watcher/wss_worker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|