prediction-market-agent-tooling 0.67.2__py3-none-any.whl → 0.67.4__py3-none-any.whl
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.
- prediction_market_agent_tooling/abis/erc1155.abi.json +352 -0
- prediction_market_agent_tooling/deploy/agent.py +57 -51
- prediction_market_agent_tooling/deploy/betting_strategy.py +8 -5
- prediction_market_agent_tooling/markets/agent_market.py +24 -1
- prediction_market_agent_tooling/markets/blockchain_utils.py +5 -3
- prediction_market_agent_tooling/markets/data_models.py +23 -3
- prediction_market_agent_tooling/markets/manifold/manifold.py +2 -1
- prediction_market_agent_tooling/markets/metaculus/metaculus.py +2 -1
- prediction_market_agent_tooling/markets/omen/omen.py +2 -1
- prediction_market_agent_tooling/markets/polymarket/api.py +9 -3
- prediction_market_agent_tooling/markets/polymarket/data_models.py +5 -3
- prediction_market_agent_tooling/markets/polymarket/polymarket.py +17 -8
- prediction_market_agent_tooling/markets/seer/data_models.py +25 -1
- prediction_market_agent_tooling/markets/seer/seer.py +85 -26
- prediction_market_agent_tooling/markets/seer/seer_contracts.py +18 -0
- prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +122 -18
- prediction_market_agent_tooling/markets/seer/swap_pool_handler.py +4 -1
- prediction_market_agent_tooling/tools/contract.py +59 -0
- prediction_market_agent_tooling/tools/cow/cow_order.py +4 -1
- prediction_market_agent_tooling/tools/hexbytes_custom.py +9 -0
- prediction_market_agent_tooling/tools/httpx_cached_client.py +5 -3
- prediction_market_agent_tooling/tools/langfuse_client_utils.py +4 -0
- prediction_market_agent_tooling/tools/rephrase.py +1 -1
- prediction_market_agent_tooling/tools/singleton.py +11 -6
- prediction_market_agent_tooling/tools/tokens/auto_deposit.py +57 -0
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/METADATA +1 -1
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/RECORD +30 -29
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,352 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"inputs": [],
|
4
|
+
"stateMutability": "nonpayable",
|
5
|
+
"type": "constructor"
|
6
|
+
},
|
7
|
+
{
|
8
|
+
"anonymous": false,
|
9
|
+
"inputs": [
|
10
|
+
{
|
11
|
+
"indexed": true,
|
12
|
+
"internalType": "address",
|
13
|
+
"name": "owner",
|
14
|
+
"type": "address"
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"indexed": true,
|
18
|
+
"internalType": "address",
|
19
|
+
"name": "spender",
|
20
|
+
"type": "address"
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"indexed": false,
|
24
|
+
"internalType": "uint256",
|
25
|
+
"name": "value",
|
26
|
+
"type": "uint256"
|
27
|
+
}
|
28
|
+
],
|
29
|
+
"name": "Approval",
|
30
|
+
"type": "event"
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"anonymous": false,
|
34
|
+
"inputs": [
|
35
|
+
{
|
36
|
+
"indexed": true,
|
37
|
+
"internalType": "address",
|
38
|
+
"name": "from",
|
39
|
+
"type": "address"
|
40
|
+
},
|
41
|
+
{
|
42
|
+
"indexed": true,
|
43
|
+
"internalType": "address",
|
44
|
+
"name": "to",
|
45
|
+
"type": "address"
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"indexed": false,
|
49
|
+
"internalType": "uint256",
|
50
|
+
"name": "value",
|
51
|
+
"type": "uint256"
|
52
|
+
}
|
53
|
+
],
|
54
|
+
"name": "Transfer",
|
55
|
+
"type": "event"
|
56
|
+
},
|
57
|
+
{
|
58
|
+
"inputs": [
|
59
|
+
{
|
60
|
+
"internalType": "address",
|
61
|
+
"name": "owner",
|
62
|
+
"type": "address"
|
63
|
+
},
|
64
|
+
{
|
65
|
+
"internalType": "address",
|
66
|
+
"name": "spender",
|
67
|
+
"type": "address"
|
68
|
+
}
|
69
|
+
],
|
70
|
+
"name": "allowance",
|
71
|
+
"outputs": [
|
72
|
+
{
|
73
|
+
"internalType": "uint256",
|
74
|
+
"name": "",
|
75
|
+
"type": "uint256"
|
76
|
+
}
|
77
|
+
],
|
78
|
+
"stateMutability": "view",
|
79
|
+
"type": "function"
|
80
|
+
},
|
81
|
+
{
|
82
|
+
"inputs": [
|
83
|
+
{
|
84
|
+
"internalType": "address",
|
85
|
+
"name": "spender",
|
86
|
+
"type": "address"
|
87
|
+
},
|
88
|
+
{
|
89
|
+
"internalType": "uint256",
|
90
|
+
"name": "amount",
|
91
|
+
"type": "uint256"
|
92
|
+
}
|
93
|
+
],
|
94
|
+
"name": "approve",
|
95
|
+
"outputs": [
|
96
|
+
{
|
97
|
+
"internalType": "bool",
|
98
|
+
"name": "",
|
99
|
+
"type": "bool"
|
100
|
+
}
|
101
|
+
],
|
102
|
+
"stateMutability": "nonpayable",
|
103
|
+
"type": "function"
|
104
|
+
},
|
105
|
+
{
|
106
|
+
"inputs": [
|
107
|
+
{
|
108
|
+
"internalType": "address",
|
109
|
+
"name": "account",
|
110
|
+
"type": "address"
|
111
|
+
}
|
112
|
+
],
|
113
|
+
"name": "balanceOf",
|
114
|
+
"outputs": [
|
115
|
+
{
|
116
|
+
"internalType": "uint256",
|
117
|
+
"name": "",
|
118
|
+
"type": "uint256"
|
119
|
+
}
|
120
|
+
],
|
121
|
+
"stateMutability": "view",
|
122
|
+
"type": "function"
|
123
|
+
},
|
124
|
+
{
|
125
|
+
"inputs": [
|
126
|
+
{
|
127
|
+
"internalType": "address",
|
128
|
+
"name": "account",
|
129
|
+
"type": "address"
|
130
|
+
},
|
131
|
+
{
|
132
|
+
"internalType": "uint256",
|
133
|
+
"name": "amount",
|
134
|
+
"type": "uint256"
|
135
|
+
}
|
136
|
+
],
|
137
|
+
"name": "burn",
|
138
|
+
"outputs": [],
|
139
|
+
"stateMutability": "nonpayable",
|
140
|
+
"type": "function"
|
141
|
+
},
|
142
|
+
{
|
143
|
+
"inputs": [],
|
144
|
+
"name": "decimals",
|
145
|
+
"outputs": [
|
146
|
+
{
|
147
|
+
"internalType": "uint8",
|
148
|
+
"name": "",
|
149
|
+
"type": "uint8"
|
150
|
+
}
|
151
|
+
],
|
152
|
+
"stateMutability": "view",
|
153
|
+
"type": "function"
|
154
|
+
},
|
155
|
+
{
|
156
|
+
"inputs": [
|
157
|
+
{
|
158
|
+
"internalType": "address",
|
159
|
+
"name": "spender",
|
160
|
+
"type": "address"
|
161
|
+
},
|
162
|
+
{
|
163
|
+
"internalType": "uint256",
|
164
|
+
"name": "subtractedValue",
|
165
|
+
"type": "uint256"
|
166
|
+
}
|
167
|
+
],
|
168
|
+
"name": "decreaseAllowance",
|
169
|
+
"outputs": [
|
170
|
+
{
|
171
|
+
"internalType": "bool",
|
172
|
+
"name": "",
|
173
|
+
"type": "bool"
|
174
|
+
}
|
175
|
+
],
|
176
|
+
"stateMutability": "nonpayable",
|
177
|
+
"type": "function"
|
178
|
+
},
|
179
|
+
{
|
180
|
+
"inputs": [],
|
181
|
+
"name": "factory",
|
182
|
+
"outputs": [
|
183
|
+
{
|
184
|
+
"internalType": "contract Wrapped1155Factory",
|
185
|
+
"name": "",
|
186
|
+
"type": "address"
|
187
|
+
}
|
188
|
+
],
|
189
|
+
"stateMutability": "view",
|
190
|
+
"type": "function"
|
191
|
+
},
|
192
|
+
{
|
193
|
+
"inputs": [
|
194
|
+
{
|
195
|
+
"internalType": "address",
|
196
|
+
"name": "spender",
|
197
|
+
"type": "address"
|
198
|
+
},
|
199
|
+
{
|
200
|
+
"internalType": "uint256",
|
201
|
+
"name": "addedValue",
|
202
|
+
"type": "uint256"
|
203
|
+
}
|
204
|
+
],
|
205
|
+
"name": "increaseAllowance",
|
206
|
+
"outputs": [
|
207
|
+
{
|
208
|
+
"internalType": "bool",
|
209
|
+
"name": "",
|
210
|
+
"type": "bool"
|
211
|
+
}
|
212
|
+
],
|
213
|
+
"stateMutability": "nonpayable",
|
214
|
+
"type": "function"
|
215
|
+
},
|
216
|
+
{
|
217
|
+
"inputs": [
|
218
|
+
{
|
219
|
+
"internalType": "address",
|
220
|
+
"name": "account",
|
221
|
+
"type": "address"
|
222
|
+
},
|
223
|
+
{
|
224
|
+
"internalType": "uint256",
|
225
|
+
"name": "amount",
|
226
|
+
"type": "uint256"
|
227
|
+
}
|
228
|
+
],
|
229
|
+
"name": "mint",
|
230
|
+
"outputs": [],
|
231
|
+
"stateMutability": "nonpayable",
|
232
|
+
"type": "function"
|
233
|
+
},
|
234
|
+
{
|
235
|
+
"inputs": [],
|
236
|
+
"name": "multiToken",
|
237
|
+
"outputs": [
|
238
|
+
{
|
239
|
+
"internalType": "contract IERC1155",
|
240
|
+
"name": "",
|
241
|
+
"type": "address"
|
242
|
+
}
|
243
|
+
],
|
244
|
+
"stateMutability": "view",
|
245
|
+
"type": "function"
|
246
|
+
},
|
247
|
+
{
|
248
|
+
"inputs": [],
|
249
|
+
"name": "name",
|
250
|
+
"outputs": [
|
251
|
+
{
|
252
|
+
"internalType": "string",
|
253
|
+
"name": "",
|
254
|
+
"type": "string"
|
255
|
+
}
|
256
|
+
],
|
257
|
+
"stateMutability": "view",
|
258
|
+
"type": "function"
|
259
|
+
},
|
260
|
+
{
|
261
|
+
"inputs": [],
|
262
|
+
"name": "symbol",
|
263
|
+
"outputs": [
|
264
|
+
{
|
265
|
+
"internalType": "string",
|
266
|
+
"name": "",
|
267
|
+
"type": "string"
|
268
|
+
}
|
269
|
+
],
|
270
|
+
"stateMutability": "view",
|
271
|
+
"type": "function"
|
272
|
+
},
|
273
|
+
{
|
274
|
+
"inputs": [],
|
275
|
+
"name": "tokenId",
|
276
|
+
"outputs": [
|
277
|
+
{
|
278
|
+
"internalType": "uint256",
|
279
|
+
"name": "",
|
280
|
+
"type": "uint256"
|
281
|
+
}
|
282
|
+
],
|
283
|
+
"stateMutability": "view",
|
284
|
+
"type": "function"
|
285
|
+
},
|
286
|
+
{
|
287
|
+
"inputs": [],
|
288
|
+
"name": "totalSupply",
|
289
|
+
"outputs": [
|
290
|
+
{
|
291
|
+
"internalType": "uint256",
|
292
|
+
"name": "",
|
293
|
+
"type": "uint256"
|
294
|
+
}
|
295
|
+
],
|
296
|
+
"stateMutability": "view",
|
297
|
+
"type": "function"
|
298
|
+
},
|
299
|
+
{
|
300
|
+
"inputs": [
|
301
|
+
{
|
302
|
+
"internalType": "address",
|
303
|
+
"name": "recipient",
|
304
|
+
"type": "address"
|
305
|
+
},
|
306
|
+
{
|
307
|
+
"internalType": "uint256",
|
308
|
+
"name": "amount",
|
309
|
+
"type": "uint256"
|
310
|
+
}
|
311
|
+
],
|
312
|
+
"name": "transfer",
|
313
|
+
"outputs": [
|
314
|
+
{
|
315
|
+
"internalType": "bool",
|
316
|
+
"name": "",
|
317
|
+
"type": "bool"
|
318
|
+
}
|
319
|
+
],
|
320
|
+
"stateMutability": "nonpayable",
|
321
|
+
"type": "function"
|
322
|
+
},
|
323
|
+
{
|
324
|
+
"inputs": [
|
325
|
+
{
|
326
|
+
"internalType": "address",
|
327
|
+
"name": "sender",
|
328
|
+
"type": "address"
|
329
|
+
},
|
330
|
+
{
|
331
|
+
"internalType": "address",
|
332
|
+
"name": "recipient",
|
333
|
+
"type": "address"
|
334
|
+
},
|
335
|
+
{
|
336
|
+
"internalType": "uint256",
|
337
|
+
"name": "amount",
|
338
|
+
"type": "uint256"
|
339
|
+
}
|
340
|
+
],
|
341
|
+
"name": "transferFrom",
|
342
|
+
"outputs": [
|
343
|
+
{
|
344
|
+
"internalType": "bool",
|
345
|
+
"name": "",
|
346
|
+
"type": "bool"
|
347
|
+
}
|
348
|
+
],
|
349
|
+
"stateMutability": "nonpayable",
|
350
|
+
"type": "function"
|
351
|
+
}
|
352
|
+
]
|
@@ -22,6 +22,7 @@ from prediction_market_agent_tooling.gtypes import USD, OutcomeToken, xDai
|
|
22
22
|
from prediction_market_agent_tooling.loggers import logger
|
23
23
|
from prediction_market_agent_tooling.markets.agent_market import (
|
24
24
|
AgentMarket,
|
25
|
+
ConditionalFilterType,
|
25
26
|
FilterBy,
|
26
27
|
ProcessedMarket,
|
27
28
|
ProcessedTradedMarket,
|
@@ -48,7 +49,7 @@ from prediction_market_agent_tooling.tools.is_invalid import is_invalid
|
|
48
49
|
from prediction_market_agent_tooling.tools.is_predictable import is_predictable_binary
|
49
50
|
from prediction_market_agent_tooling.tools.langfuse_ import langfuse_context, observe
|
50
51
|
from prediction_market_agent_tooling.tools.rephrase import (
|
51
|
-
|
52
|
+
rephrase_question_to_unconditional,
|
52
53
|
)
|
53
54
|
from prediction_market_agent_tooling.tools.tokens.main_token import (
|
54
55
|
MINIMUM_NATIVE_TOKEN_IN_EOA_FOR_FEES,
|
@@ -199,7 +200,7 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
199
200
|
trade_on_markets_created_after: DatetimeUTC | None = None
|
200
201
|
get_markets_sort_by: SortBy = SortBy.CLOSING_SOONEST
|
201
202
|
get_markets_filter_by: FilterBy = FilterBy.OPEN
|
202
|
-
|
203
|
+
rephrase_conditional_markets: bool = True
|
203
204
|
|
204
205
|
# Agent behaviour when filtering fetched markets
|
205
206
|
allow_invalid_questions: bool = False
|
@@ -230,7 +231,7 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
230
231
|
self.answer_categorical_market = observe()(self.answer_categorical_market) # type: ignore[method-assign]
|
231
232
|
self.answer_scalar_market = observe()(self.answer_scalar_market) # type: ignore[method-assign]
|
232
233
|
self.process_market = observe()(self.process_market) # type: ignore[method-assign]
|
233
|
-
self.
|
234
|
+
self.rephrase_market_to_unconditional = observe()(self.rephrase_market_to_unconditional) # type: ignore[method-assign]
|
234
235
|
|
235
236
|
def update_langfuse_trace_by_market(
|
236
237
|
self, market_type: MarketType, market: AgentMarket
|
@@ -305,21 +306,21 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
305
306
|
|
306
307
|
return True
|
307
308
|
|
308
|
-
def
|
309
|
+
def rephrase_market_to_unconditional(
|
309
310
|
self,
|
310
311
|
market_: AgentMarket,
|
311
312
|
) -> AgentMarket:
|
312
313
|
"""
|
313
|
-
If `
|
314
|
+
If `rephrase_conditional_markets` is set to True,
|
314
315
|
this method will be used to rephrase the question to account for the parent's market probability in the agent's decision process.
|
315
316
|
"""
|
316
317
|
new = market_.model_copy(deep=True)
|
317
318
|
|
318
319
|
if new.parent is not None and new.parent.market.parent is not None:
|
319
|
-
new.parent.market = self.
|
320
|
+
new.parent.market = self.rephrase_market_to_unconditional(new.parent.market)
|
320
321
|
|
321
322
|
rephrased_question = (
|
322
|
-
|
323
|
+
rephrase_question_to_unconditional(
|
323
324
|
new.question,
|
324
325
|
new.parent.market.question,
|
325
326
|
new.parent.market.outcomes[new.parent.parent_outcome],
|
@@ -373,14 +374,10 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
373
374
|
return False
|
374
375
|
|
375
376
|
@property
|
376
|
-
def
|
377
|
-
|
378
|
-
|
379
|
-
return
|
380
|
-
# `include_conditional_markets` if `rephrase_conditioned_markets` is enabled.
|
381
|
-
# We can expand this method in teh future, when we implement also more complex logic about conditional markets.
|
382
|
-
# Note that conditional market isn't a type of the market like Binary or Categorical, it means that it uses outcome tokens from parent market as a collateral token in this market.
|
383
|
-
return self.rephrase_conditioned_markets
|
377
|
+
def conditional_filter_type(self) -> ConditionalFilterType:
|
378
|
+
if self.rephrase_conditional_markets:
|
379
|
+
return ConditionalFilterType.ALL
|
380
|
+
return ConditionalFilterType.ONLY_NOT_CONDITIONAL
|
384
381
|
|
385
382
|
@property
|
386
383
|
def agent_question_type(self) -> QuestionType:
|
@@ -407,7 +404,7 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
407
404
|
filter_by=self.get_markets_filter_by,
|
408
405
|
created_after=self.trade_on_markets_created_after,
|
409
406
|
question_type=self.agent_question_type,
|
410
|
-
|
407
|
+
conditional_filter_type=self.conditional_filter_type,
|
411
408
|
)
|
412
409
|
return available_markets
|
413
410
|
|
@@ -440,8 +437,8 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
440
437
|
|
441
438
|
logger.info(f"Answering market '{market.question}'.")
|
442
439
|
|
443
|
-
if self.
|
444
|
-
market = self.
|
440
|
+
if self.rephrase_conditional_markets and market.parent is not None:
|
441
|
+
market = self.rephrase_market_to_unconditional(market)
|
445
442
|
|
446
443
|
if market.is_binary:
|
447
444
|
try:
|
@@ -633,6 +630,7 @@ class DeployableTraderAgent(DeployablePredictionAgent):
|
|
633
630
|
super().initialize_langfuse()
|
634
631
|
# Auto-observe all the methods where it makes sense, so that subclassses don't need to do it manually.
|
635
632
|
self.build_trades = observe()(self.build_trades) # type: ignore[method-assign]
|
633
|
+
self.execute_trades = observe()(self.execute_trades) # type: ignore[method-assign]
|
636
634
|
|
637
635
|
def check_min_required_balance_to_trade(self, market: AgentMarket) -> None:
|
638
636
|
api_keys = APIKeys()
|
@@ -678,37 +676,9 @@ class DeployableTraderAgent(DeployablePredictionAgent):
|
|
678
676
|
trades = strategy.calculate_trades(existing_position, answer, market)
|
679
677
|
return trades
|
680
678
|
|
681
|
-
def
|
682
|
-
self,
|
683
|
-
) ->
|
684
|
-
super().before_process_market(market_type, market)
|
685
|
-
self.check_min_required_balance_to_trade(market)
|
686
|
-
|
687
|
-
def process_market(
|
688
|
-
self,
|
689
|
-
market_type: MarketType,
|
690
|
-
market: AgentMarket,
|
691
|
-
verify_market: bool = True,
|
692
|
-
) -> ProcessedTradedMarket | None:
|
693
|
-
processed_market = super().process_market(market_type, market, verify_market)
|
694
|
-
if processed_market is None:
|
695
|
-
return None
|
696
|
-
|
697
|
-
api_keys = APIKeys()
|
698
|
-
user_id = market.get_user_id(api_keys=api_keys)
|
699
|
-
|
700
|
-
try:
|
701
|
-
existing_position = market.get_position(user_id=user_id)
|
702
|
-
except Exception as e:
|
703
|
-
logger.warning(f"Could not get position for user {user_id}, exception {e}")
|
704
|
-
return None
|
705
|
-
|
706
|
-
trades = self.build_trades(
|
707
|
-
market=market,
|
708
|
-
answer=processed_market.answer,
|
709
|
-
existing_position=existing_position,
|
710
|
-
)
|
711
|
-
|
679
|
+
def execute_trades(
|
680
|
+
self, market: AgentMarket, trades: list[Trade]
|
681
|
+
) -> list[PlacedTrade]:
|
712
682
|
# It can take quite some time before agent processes all the markets, recheck here if the market didn't get closed in the meantime, to not error out completely.
|
713
683
|
# Unfortunately, we can not just add some room into closing time of the market while fetching them, because liquidity can be removed at any time by the liquidity providers.
|
714
684
|
still_tradeable = market.can_be_traded()
|
@@ -717,7 +687,8 @@ class DeployableTraderAgent(DeployablePredictionAgent):
|
|
717
687
|
f"Market {market.question=} ({market.url}) was selected to processing, but is not tradeable anymore."
|
718
688
|
)
|
719
689
|
|
720
|
-
placed_trades = []
|
690
|
+
placed_trades: list[PlacedTrade] = []
|
691
|
+
|
721
692
|
for trade in trades:
|
722
693
|
logger.info(f"Executing trade {trade} on market {market.id} ({market.url})")
|
723
694
|
|
@@ -730,7 +701,9 @@ class DeployableTraderAgent(DeployablePredictionAgent):
|
|
730
701
|
case TradeType.SELL:
|
731
702
|
# Get actual value of the position we are going to sell, and if it's less than we wanted to sell, simply sell all of it.
|
732
703
|
current_position = check_not_none(
|
733
|
-
market.get_position(
|
704
|
+
market.get_position(
|
705
|
+
market.get_user_id(api_keys=self.api_keys)
|
706
|
+
),
|
734
707
|
"Should exists if we are going to sell outcomes.",
|
735
708
|
)
|
736
709
|
|
@@ -758,6 +731,39 @@ class DeployableTraderAgent(DeployablePredictionAgent):
|
|
758
731
|
f"Trade execution skipped because, {self.place_trades=} or {still_tradeable=}."
|
759
732
|
)
|
760
733
|
|
734
|
+
return placed_trades
|
735
|
+
|
736
|
+
def before_process_market(
|
737
|
+
self, market_type: MarketType, market: AgentMarket
|
738
|
+
) -> None:
|
739
|
+
super().before_process_market(market_type, market)
|
740
|
+
self.check_min_required_balance_to_trade(market)
|
741
|
+
|
742
|
+
def process_market(
|
743
|
+
self,
|
744
|
+
market_type: MarketType,
|
745
|
+
market: AgentMarket,
|
746
|
+
verify_market: bool = True,
|
747
|
+
) -> ProcessedTradedMarket | None:
|
748
|
+
processed_market = super().process_market(market_type, market, verify_market)
|
749
|
+
if processed_market is None:
|
750
|
+
return None
|
751
|
+
|
752
|
+
user_id = market.get_user_id(api_keys=self.api_keys)
|
753
|
+
|
754
|
+
try:
|
755
|
+
existing_position = market.get_position(user_id=user_id)
|
756
|
+
except Exception as e:
|
757
|
+
logger.warning(f"Could not get position for user {user_id}, exception {e}")
|
758
|
+
return None
|
759
|
+
|
760
|
+
trades = self.build_trades(
|
761
|
+
market=market,
|
762
|
+
answer=processed_market.answer,
|
763
|
+
existing_position=existing_position,
|
764
|
+
)
|
765
|
+
placed_trades = self.execute_trades(market, trades)
|
766
|
+
|
761
767
|
traded_market = ProcessedTradedMarket(
|
762
768
|
answer=processed_market.answer, trades=placed_trades
|
763
769
|
)
|
@@ -84,7 +84,7 @@ class BettingStrategy(ABC):
|
|
84
84
|
|
85
85
|
if outcome_tokens_to_get_in_usd <= trade.amount:
|
86
86
|
raise GuaranteedLossError(
|
87
|
-
f"Trade {trade=} would result in guaranteed loss by getting only {outcome_tokens_to_get=}. Halting execution."
|
87
|
+
f"Trade {trade=} on market {market.url=} would result in guaranteed loss by getting only {outcome_tokens_to_get=}. Halting execution."
|
88
88
|
)
|
89
89
|
|
90
90
|
clean_trades.append(trade)
|
@@ -443,8 +443,11 @@ class KellyBettingStrategy(BettingStrategy):
|
|
443
443
|
kelly_bet_size = min(kelly_bet.size, max_price_impact_bet_amount)
|
444
444
|
|
445
445
|
bet_outcome = direction if kelly_bet.direction else other_direction
|
446
|
+
|
446
447
|
amounts = {
|
447
|
-
bet_outcome:
|
448
|
+
bet_outcome: BettingStrategy.cap_to_profitable_bet_amount(
|
449
|
+
market, market.get_token_in_usd(kelly_bet_size), bet_outcome
|
450
|
+
),
|
448
451
|
}
|
449
452
|
target_position = Position(market_id=market.id, amounts_current=amounts)
|
450
453
|
trades = self._build_rebalance_trades_from_positions(
|
@@ -475,12 +478,12 @@ class KellyBettingStrategy(BettingStrategy):
|
|
475
478
|
self, market: AgentMarket, kelly_bet: SimpleBet, direction: OutcomeStr
|
476
479
|
) -> CollateralToken:
|
477
480
|
def calculate_price_impact_deviation_from_target_price_impact(
|
478
|
-
|
481
|
+
bet_amount_collateral: float, # Needs to be float because it's used in minimize_scalar internally.
|
479
482
|
) -> float:
|
480
483
|
outcome_idx = market.get_outcome_index(direction)
|
481
484
|
price_impact = self.calculate_price_impact_for_bet_amount(
|
482
485
|
outcome_idx=outcome_idx,
|
483
|
-
bet_amount=
|
486
|
+
bet_amount=CollateralToken(bet_amount_collateral),
|
484
487
|
pool_balances=pool_balances,
|
485
488
|
fees=market.fees,
|
486
489
|
)
|
@@ -504,7 +507,7 @@ class KellyBettingStrategy(BettingStrategy):
|
|
504
507
|
calculate_price_impact_deviation_from_target_price_impact,
|
505
508
|
bounds=(0, 1000 * total_pool_balance),
|
506
509
|
method="bounded",
|
507
|
-
tol=1e-
|
510
|
+
tol=1e-13,
|
508
511
|
options={"maxiter": 10000},
|
509
512
|
)
|
510
513
|
return CollateralToken(optimized_bet_amount.x)
|
@@ -76,6 +76,12 @@ class QuestionType(str, Enum):
|
|
76
76
|
BINARY = "binary"
|
77
77
|
|
78
78
|
|
79
|
+
class ConditionalFilterType(Enum):
|
80
|
+
ALL = 1
|
81
|
+
ONLY_CONDITIONAL = 2
|
82
|
+
ONLY_NOT_CONDITIONAL = 3
|
83
|
+
|
84
|
+
|
79
85
|
class AgentMarket(BaseModel):
|
80
86
|
"""
|
81
87
|
Common market class that can be created from vendor specific markets.
|
@@ -147,6 +153,23 @@ class AgentMarket(BaseModel):
|
|
147
153
|
if "fees" not in data and "fee" in data:
|
148
154
|
data["fees"] = MarketFees(absolute=0.0, bet_proportion=data["fee"])
|
149
155
|
del data["fee"]
|
156
|
+
# Backward compatibility for older `AgentMarket` without `probabilities`.
|
157
|
+
if "probabilities" not in data and "current_p_yes" in data:
|
158
|
+
yes_outcome = data["outcomes"][
|
159
|
+
[o.lower() for o in data["outcomes"]].index(
|
160
|
+
YES_OUTCOME_LOWERCASE_IDENTIFIER
|
161
|
+
)
|
162
|
+
]
|
163
|
+
no_outcome = data["outcomes"][
|
164
|
+
[o.lower() for o in data["outcomes"]].index(
|
165
|
+
NO_OUTCOME_LOWERCASE_IDENTIFIER
|
166
|
+
)
|
167
|
+
]
|
168
|
+
data["probabilities"] = {
|
169
|
+
yes_outcome: data["current_p_yes"],
|
170
|
+
no_outcome: 1 - data["current_p_yes"],
|
171
|
+
}
|
172
|
+
del data["current_p_yes"]
|
150
173
|
return data
|
151
174
|
|
152
175
|
def market_outcome_for_probability_key(
|
@@ -384,7 +407,7 @@ class AgentMarket(BaseModel):
|
|
384
407
|
created_after: t.Optional[DatetimeUTC] = None,
|
385
408
|
excluded_questions: set[str] | None = None,
|
386
409
|
question_type: QuestionType = QuestionType.ALL,
|
387
|
-
|
410
|
+
conditional_filter_type: ConditionalFilterType = ConditionalFilterType.ONLY_NOT_CONDITIONAL,
|
388
411
|
) -> t.Sequence["AgentMarket"]:
|
389
412
|
raise NotImplementedError("Subclasses must implement this method")
|
390
413
|
|
@@ -37,6 +37,10 @@ def store_trades(
|
|
37
37
|
logger.warning(f"No prediction for market {market_id}, not storing anything.")
|
38
38
|
return None
|
39
39
|
|
40
|
+
logger.info(
|
41
|
+
f"Storing trades for market {market_id}, with outcomes {outcomes}, {traded_market=}."
|
42
|
+
)
|
43
|
+
|
40
44
|
probabilities = traded_market.answer.probabilities
|
41
45
|
if not probabilities:
|
42
46
|
logger.info("Skipping this since no probabilities available.")
|
@@ -56,9 +60,7 @@ def store_trades(
|
|
56
60
|
ipfs_hash_decoded = ipfscidv0_to_byte32(ipfs_hash)
|
57
61
|
|
58
62
|
# tx_hashes must be list of bytes32 (see Solidity contract).
|
59
|
-
tx_hashes = [
|
60
|
-
HexBytes(HexStr(i.id)) for i in traded_market.trades if i.id is not None
|
61
|
-
]
|
63
|
+
tx_hashes = [HexBytes(HexStr(i.id)) for i in traded_market.trades]
|
62
64
|
|
63
65
|
# Dune dashboard expects the probs to be in the same order as on the market.
|
64
66
|
probabilities_converted = [
|