prediction-market-agent-tooling 0.48.14__tar.gz → 0.48.16__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.
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/PKG-INFO +1 -1
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/deploy/agent.py +9 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/deploy/betting_strategy.py +23 -6
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/gtypes.py +1 -1
- prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/jobs/jobs.py +45 -0
- prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/jobs/jobs_models.py +53 -0
- prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/jobs/omen/omen_jobs.py +113 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/omen/data_models.py +3 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/omen/omen.py +90 -4
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +17 -8
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/monitor/monitor.py +5 -1
- prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/py.typed +0 -0
- prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +142 -0
- prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +132 -0
- prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/tools/betting_strategies/utils.py +6 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/is_predictable.py +3 -0
- prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/tools/langfuse_client_utils.py +159 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/pyproject.toml +1 -1
- prediction_market_agent_tooling-0.48.14/prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +0 -60
- prediction_market_agent_tooling-0.48.14/prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -116
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/README.md +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/depositablewrapper_erc20.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/erc20.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/erc4626.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/omen_dxdao.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/omen_fpmm.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/omen_fpmm_conditionaltokens.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/omen_fpmm_factory.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/omen_kleros.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/omen_oracle.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/omen_realitio.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/omen_thumbnailmapping.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/abis/proxy.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/benchmark/__init__.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/benchmark/agents.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/benchmark/benchmark.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/benchmark/utils.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/config.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/deploy/agent_example.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/deploy/constants.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/deploy/gcp/deploy.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/deploy/gcp/utils.py +0 -0
- {prediction_market_agent_tooling-0.48.14/prediction_market_agent_tooling/markets/manifold → prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/jobs}/__init__.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/loggers.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/agent_market.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/categorize.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/data_models.py +0 -0
- {prediction_market_agent_tooling-0.48.14/prediction_market_agent_tooling/markets/omen → prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/markets/manifold}/__init__.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/manifold/api.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/manifold/data_models.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/manifold/manifold.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/manifold/utils.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/markets.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/metaculus/api.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/metaculus/data_models.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/metaculus/metaculus.py +0 -0
- /prediction_market_agent_tooling-0.48.14/prediction_market_agent_tooling/py.typed → /prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/markets/omen/__init__.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/omen/omen_contracts.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/omen/omen_resolving.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/polymarket/api.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/polymarket/data_models.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/polymarket/data_models_web.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/polymarket/polymarket.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/markets/polymarket/utils.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/monitor/markets/manifold.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/monitor/markets/omen.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/monitor/monitor_app.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/monitor/monitor_settings.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/balances.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/cache.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/contract.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/costs.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/gnosis_rpc.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/google.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/hexbytes_custom.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/image_gen/image_gen.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/langfuse_.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/parallelism.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/safe.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/singleton.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/streamlit_user_login.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/tavily_storage/tavily_storage.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/utils.py +0 -0
- {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.16}/prediction_market_agent_tooling/tools/web3_utils.py +0 -0
@@ -426,6 +426,8 @@ class DeployableTraderAgent(DeployableAgent):
|
|
426
426
|
market: AgentMarket,
|
427
427
|
verify_market: bool = True,
|
428
428
|
) -> ProcessedMarket | None:
|
429
|
+
logger.info(f"Processing market {market.question=} from {market.url=}.")
|
430
|
+
|
429
431
|
self.before_process_market(market_type, market)
|
430
432
|
|
431
433
|
if verify_market and not self.verify_market(market_type, market):
|
@@ -462,6 +464,7 @@ class DeployableTraderAgent(DeployableAgent):
|
|
462
464
|
processed_market = ProcessedMarket(answer=answer, trades=trades)
|
463
465
|
self.update_langfuse_trace_by_processed_market(market_type, processed_market)
|
464
466
|
|
467
|
+
logger.info(f"Processed market {market.question=} from {market.url=}.")
|
465
468
|
return processed_market
|
466
469
|
|
467
470
|
def after_process_market(
|
@@ -495,7 +498,11 @@ class DeployableTraderAgent(DeployableAgent):
|
|
495
498
|
"""
|
496
499
|
Processes bets placed by agents on a given market.
|
497
500
|
"""
|
501
|
+
logger.info("Start processing of markets.")
|
498
502
|
available_markets = self.get_markets(market_type)
|
503
|
+
logger.info(
|
504
|
+
f"Fetched {len(available_markets)=} markets to process, going to process {self.bet_on_n_markets_per_run=}."
|
505
|
+
)
|
499
506
|
processed = 0
|
500
507
|
|
501
508
|
for market in available_markets:
|
@@ -510,6 +517,8 @@ class DeployableTraderAgent(DeployableAgent):
|
|
510
517
|
if processed == self.bet_on_n_markets_per_run:
|
511
518
|
break
|
512
519
|
|
520
|
+
logger.info("All markets processed.")
|
521
|
+
|
513
522
|
def after_process_markets(self, market_type: MarketType) -> None:
|
514
523
|
pass
|
515
524
|
|
@@ -11,8 +11,10 @@ from prediction_market_agent_tooling.markets.data_models import (
|
|
11
11
|
)
|
12
12
|
from prediction_market_agent_tooling.markets.omen.data_models import get_boolean_outcome
|
13
13
|
from prediction_market_agent_tooling.tools.betting_strategies.kelly_criterion import (
|
14
|
-
|
14
|
+
get_kelly_bet_full,
|
15
|
+
get_kelly_bet_simplified,
|
15
16
|
)
|
17
|
+
from prediction_market_agent_tooling.tools.utils import check_not_none
|
16
18
|
|
17
19
|
|
18
20
|
class BettingStrategy(ABC):
|
@@ -168,11 +170,26 @@ class KellyBettingStrategy(BettingStrategy):
|
|
168
170
|
market: AgentMarket,
|
169
171
|
) -> list[Trade]:
|
170
172
|
adjusted_bet_amount = self.adjust_bet_amount(existing_position, market)
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
173
|
+
outcome_token_pool = check_not_none(market.outcome_token_pool)
|
174
|
+
kelly_bet = (
|
175
|
+
get_kelly_bet_full(
|
176
|
+
yes_outcome_pool_size=outcome_token_pool[
|
177
|
+
market.get_outcome_str_from_bool(True)
|
178
|
+
],
|
179
|
+
no_outcome_pool_size=outcome_token_pool[
|
180
|
+
market.get_outcome_str_from_bool(False)
|
181
|
+
],
|
182
|
+
estimated_p_yes=answer.p_yes,
|
183
|
+
max_bet=adjusted_bet_amount,
|
184
|
+
confidence=answer.confidence,
|
185
|
+
)
|
186
|
+
if market.has_token_pool()
|
187
|
+
else get_kelly_bet_simplified(
|
188
|
+
adjusted_bet_amount,
|
189
|
+
market.current_p_yes,
|
190
|
+
answer.p_yes,
|
191
|
+
answer.confidence,
|
192
|
+
)
|
176
193
|
)
|
177
194
|
|
178
195
|
amounts = {
|
@@ -27,7 +27,7 @@ PrivateKey = NewType("PrivateKey", SecretStr)
|
|
27
27
|
xDai = NewType("xDai", float)
|
28
28
|
GNO = NewType("GNO", float)
|
29
29
|
ABI = NewType("ABI", str)
|
30
|
-
OmenOutcomeToken = NewType("OmenOutcomeToken",
|
30
|
+
OmenOutcomeToken = NewType("OmenOutcomeToken", Wei)
|
31
31
|
OutcomeStr = NewType("OutcomeStr", str)
|
32
32
|
Probability = NewType("Probability", float)
|
33
33
|
Mana = NewType("Mana", float) # Manifold's "currency"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import typing as t
|
2
|
+
|
3
|
+
from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
|
4
|
+
from prediction_market_agent_tooling.jobs.omen.omen_jobs import OmenJobAgentMarket
|
5
|
+
from prediction_market_agent_tooling.markets.agent_market import FilterBy, SortBy
|
6
|
+
from prediction_market_agent_tooling.markets.markets import MarketType
|
7
|
+
|
8
|
+
JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET: dict[MarketType, type[JobAgentMarket]] = {
|
9
|
+
MarketType.OMEN: OmenJobAgentMarket,
|
10
|
+
}
|
11
|
+
|
12
|
+
|
13
|
+
@t.overload
|
14
|
+
def get_jobs(
|
15
|
+
market_type: t.Literal[MarketType.OMEN],
|
16
|
+
limit: int | None,
|
17
|
+
filter_by: FilterBy = FilterBy.OPEN,
|
18
|
+
sort_by: SortBy = SortBy.NONE,
|
19
|
+
) -> t.Sequence[OmenJobAgentMarket]:
|
20
|
+
...
|
21
|
+
|
22
|
+
|
23
|
+
@t.overload
|
24
|
+
def get_jobs(
|
25
|
+
market_type: MarketType,
|
26
|
+
limit: int | None,
|
27
|
+
filter_by: FilterBy = FilterBy.OPEN,
|
28
|
+
sort_by: SortBy = SortBy.NONE,
|
29
|
+
) -> t.Sequence[JobAgentMarket]:
|
30
|
+
...
|
31
|
+
|
32
|
+
|
33
|
+
def get_jobs(
|
34
|
+
market_type: MarketType,
|
35
|
+
limit: int | None,
|
36
|
+
filter_by: FilterBy = FilterBy.OPEN,
|
37
|
+
sort_by: SortBy = SortBy.NONE,
|
38
|
+
) -> t.Sequence[JobAgentMarket]:
|
39
|
+
job_class = JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET[market_type]
|
40
|
+
markets = job_class.get_jobs(
|
41
|
+
limit=limit,
|
42
|
+
sort_by=sort_by,
|
43
|
+
filter_by=filter_by,
|
44
|
+
)
|
45
|
+
return markets
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import typing as t
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from datetime import datetime
|
4
|
+
|
5
|
+
from pydantic import BaseModel
|
6
|
+
|
7
|
+
from prediction_market_agent_tooling.markets.agent_market import AgentMarket
|
8
|
+
from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
9
|
+
FilterBy,
|
10
|
+
SortBy,
|
11
|
+
)
|
12
|
+
|
13
|
+
|
14
|
+
class SimpleJob(BaseModel):
|
15
|
+
id: str
|
16
|
+
job: str
|
17
|
+
reward: float
|
18
|
+
currency: str
|
19
|
+
deadline: datetime
|
20
|
+
|
21
|
+
|
22
|
+
class JobAgentMarket(AgentMarket, ABC):
|
23
|
+
CATEGORY: t.ClassVar[str]
|
24
|
+
|
25
|
+
@property
|
26
|
+
@abstractmethod
|
27
|
+
def job(self) -> str:
|
28
|
+
"""Holds description of the job that needs to be done."""
|
29
|
+
|
30
|
+
@property
|
31
|
+
@abstractmethod
|
32
|
+
def deadline(self) -> datetime:
|
33
|
+
"""Deadline for the job completion."""
|
34
|
+
|
35
|
+
@abstractmethod
|
36
|
+
def get_reward(self, max_bond: float) -> float:
|
37
|
+
"""Reward for completing this job."""
|
38
|
+
|
39
|
+
@classmethod
|
40
|
+
@abstractmethod
|
41
|
+
def get_jobs(
|
42
|
+
cls, limit: int | None, filter_by: FilterBy, sort_by: SortBy
|
43
|
+
) -> t.Sequence["JobAgentMarket"]:
|
44
|
+
"""Get all available jobs."""
|
45
|
+
|
46
|
+
def to_simple_job(self, max_bond: float) -> SimpleJob:
|
47
|
+
return SimpleJob(
|
48
|
+
id=self.id,
|
49
|
+
job=self.job,
|
50
|
+
reward=self.get_reward(max_bond),
|
51
|
+
currency=self.currency.value,
|
52
|
+
deadline=self.deadline,
|
53
|
+
)
|
prediction_market_agent_tooling-0.48.16/prediction_market_agent_tooling/jobs/omen/omen_jobs.py
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
import typing as t
|
2
|
+
from datetime import datetime
|
3
|
+
|
4
|
+
from web3 import Web3
|
5
|
+
|
6
|
+
from prediction_market_agent_tooling.deploy.betting_strategy import (
|
7
|
+
Currency,
|
8
|
+
KellyBettingStrategy,
|
9
|
+
ProbabilisticAnswer,
|
10
|
+
TradeType,
|
11
|
+
)
|
12
|
+
from prediction_market_agent_tooling.gtypes import Probability
|
13
|
+
from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
|
14
|
+
from prediction_market_agent_tooling.markets.omen.omen import (
|
15
|
+
BetAmount,
|
16
|
+
OmenAgentMarket,
|
17
|
+
OmenMarket,
|
18
|
+
)
|
19
|
+
from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
20
|
+
FilterBy,
|
21
|
+
OmenSubgraphHandler,
|
22
|
+
SortBy,
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
|
27
|
+
CATEGORY = "jobs"
|
28
|
+
|
29
|
+
@property
|
30
|
+
def job(self) -> str:
|
31
|
+
"""Omen market's have only question, so that's where the job description is."""
|
32
|
+
return self.question
|
33
|
+
|
34
|
+
@property
|
35
|
+
def deadline(self) -> datetime:
|
36
|
+
return self.close_time
|
37
|
+
|
38
|
+
def get_reward(self, max_bond: float) -> float:
|
39
|
+
return compute_job_reward(self, max_bond)
|
40
|
+
|
41
|
+
@classmethod
|
42
|
+
def get_jobs(
|
43
|
+
cls, limit: int | None, filter_by: FilterBy, sort_by: SortBy
|
44
|
+
) -> t.Sequence["OmenJobAgentMarket"]:
|
45
|
+
markets = OmenSubgraphHandler().get_omen_binary_markets_simple(
|
46
|
+
limit=limit,
|
47
|
+
filter_by=filter_by,
|
48
|
+
sort_by=sort_by,
|
49
|
+
category=cls.CATEGORY,
|
50
|
+
)
|
51
|
+
return [OmenJobAgentMarket.from_omen_market(market) for market in markets]
|
52
|
+
|
53
|
+
@staticmethod
|
54
|
+
def from_omen_market(market: OmenMarket) -> "OmenJobAgentMarket":
|
55
|
+
return OmenJobAgentMarket.from_omen_agent_market(
|
56
|
+
OmenAgentMarket.from_data_model(market)
|
57
|
+
)
|
58
|
+
|
59
|
+
@staticmethod
|
60
|
+
def from_omen_agent_market(market: OmenAgentMarket) -> "OmenJobAgentMarket":
|
61
|
+
return OmenJobAgentMarket(
|
62
|
+
id=market.id,
|
63
|
+
question=market.question,
|
64
|
+
description=market.description,
|
65
|
+
outcomes=market.outcomes,
|
66
|
+
outcome_token_pool=market.outcome_token_pool,
|
67
|
+
resolution=market.resolution,
|
68
|
+
created_time=market.created_time,
|
69
|
+
close_time=market.close_time,
|
70
|
+
current_p_yes=market.current_p_yes,
|
71
|
+
url=market.url,
|
72
|
+
volume=market.volume,
|
73
|
+
creator=market.creator,
|
74
|
+
collateral_token_contract_address_checksummed=market.collateral_token_contract_address_checksummed,
|
75
|
+
market_maker_contract_address_checksummed=market.market_maker_contract_address_checksummed,
|
76
|
+
condition=market.condition,
|
77
|
+
finalized_time=market.finalized_time,
|
78
|
+
fee=market.fee,
|
79
|
+
)
|
80
|
+
|
81
|
+
|
82
|
+
def compute_job_reward(
|
83
|
+
market: OmenAgentMarket, max_bond: float, web3: Web3 | None = None
|
84
|
+
) -> float:
|
85
|
+
# Because jobs are powered by prediction markets, potentional reward depends on job's liquidity and our will to bond (bet) our xDai into our job completion.
|
86
|
+
required_trades = KellyBettingStrategy(max_bet_amount=max_bond).calculate_trades(
|
87
|
+
existing_position=None,
|
88
|
+
# We assume that we finish the job and so the probability of the market happening will be 100%.
|
89
|
+
answer=ProbabilisticAnswer(p_yes=Probability(1.0), confidence=1.0),
|
90
|
+
market=market,
|
91
|
+
)
|
92
|
+
|
93
|
+
assert (
|
94
|
+
len(required_trades) == 1
|
95
|
+
), f"Shouldn't process same job twice: {required_trades}"
|
96
|
+
trade = required_trades[0]
|
97
|
+
assert trade.trade_type == TradeType.BUY, "Should only buy on job markets."
|
98
|
+
assert trade.outcome, "Should buy only YES on job markets."
|
99
|
+
assert (
|
100
|
+
trade.amount.currency == Currency.xDai
|
101
|
+
), "Should work only on real-money markets."
|
102
|
+
|
103
|
+
reward = (
|
104
|
+
market.get_buy_token_amount(
|
105
|
+
bet_amount=BetAmount(
|
106
|
+
amount=trade.amount.amount, currency=trade.amount.currency
|
107
|
+
),
|
108
|
+
direction=trade.outcome,
|
109
|
+
).amount
|
110
|
+
- trade.amount.amount
|
111
|
+
)
|
112
|
+
|
113
|
+
return reward
|
@@ -9,6 +9,7 @@ from prediction_market_agent_tooling.gtypes import (
|
|
9
9
|
ChecksumAddress,
|
10
10
|
HexAddress,
|
11
11
|
HexBytes,
|
12
|
+
HexStr,
|
12
13
|
OmenOutcomeToken,
|
13
14
|
Probability,
|
14
15
|
Wei,
|
@@ -30,8 +31,10 @@ from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai
|
|
30
31
|
|
31
32
|
OMEN_TRUE_OUTCOME = "Yes"
|
32
33
|
OMEN_FALSE_OUTCOME = "No"
|
34
|
+
OMEN_BINARY_MARKET_OUTCOMES = [OMEN_TRUE_OUTCOME, OMEN_FALSE_OUTCOME]
|
33
35
|
INVALID_ANSWER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
34
36
|
INVALID_ANSWER_HEX_BYTES = HexBytes(INVALID_ANSWER)
|
37
|
+
INVALID_ANSWER_STR = HexStr(INVALID_ANSWER_HEX_BYTES.hex())
|
35
38
|
OMEN_BASE_URL = "https://aiomen.eth.limo"
|
36
39
|
PRESAGIO_BASE_URL = "https://presagio.pages.dev"
|
37
40
|
|
@@ -97,10 +97,6 @@ class OmenAgentMarket(AgentMarket):
|
|
97
97
|
close_time: datetime
|
98
98
|
fee: float # proportion, from 0 to 1
|
99
99
|
|
100
|
-
INVALID_MARKET_ANSWER: HexStr = HexStr(
|
101
|
-
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
102
|
-
)
|
103
|
-
|
104
100
|
_binary_market_p_yes_history: list[Probability] | None = None
|
105
101
|
description: str | None = (
|
106
102
|
None # Omen markets don't have a description, so just default to None.
|
@@ -577,6 +573,66 @@ class OmenAgentMarket(AgentMarket):
|
|
577
573
|
def get_user_url(cls, keys: APIKeys) -> str:
|
578
574
|
return get_omen_user_url(keys.bet_from_address)
|
579
575
|
|
576
|
+
def get_buy_token_amount(
|
577
|
+
self, bet_amount: BetAmount, direction: bool
|
578
|
+
) -> TokenAmount:
|
579
|
+
"""
|
580
|
+
Note: this is only valid if the market instance's token pool is
|
581
|
+
up-to-date with the smart contract.
|
582
|
+
"""
|
583
|
+
outcome_token_pool = check_not_none(self.outcome_token_pool)
|
584
|
+
amount = get_buy_outcome_token_amount(
|
585
|
+
investment_amount=bet_amount.amount,
|
586
|
+
buy_direction=direction,
|
587
|
+
yes_outcome_pool_size=outcome_token_pool[OMEN_TRUE_OUTCOME],
|
588
|
+
no_outcome_pool_size=outcome_token_pool[OMEN_FALSE_OUTCOME],
|
589
|
+
fee=self.fee,
|
590
|
+
)
|
591
|
+
return TokenAmount(amount=amount, currency=self.currency)
|
592
|
+
|
593
|
+
def _get_buy_token_amount_from_smart_contract(
|
594
|
+
self, bet_amount: BetAmount, direction: bool
|
595
|
+
) -> TokenAmount:
|
596
|
+
received_token_amount_wei = Wei(
|
597
|
+
self.get_contract().calcBuyAmount(
|
598
|
+
investment_amount=xdai_to_wei(xDai(bet_amount.amount)),
|
599
|
+
outcome_index=self.get_outcome_index(
|
600
|
+
self.get_outcome_str_from_bool(direction)
|
601
|
+
),
|
602
|
+
)
|
603
|
+
)
|
604
|
+
received_token_amount = float(wei_to_xdai(received_token_amount_wei))
|
605
|
+
return TokenAmount(amount=received_token_amount, currency=self.currency)
|
606
|
+
|
607
|
+
def get_new_p_yes(self, bet_amount: BetAmount, direction: bool) -> Probability:
|
608
|
+
"""
|
609
|
+
Calculate the new p_yes based on the bet amount and direction.
|
610
|
+
"""
|
611
|
+
if not self.has_token_pool():
|
612
|
+
raise ValueError("Outcome token pool is required to calculate new p_yes.")
|
613
|
+
|
614
|
+
outcome_token_pool = check_not_none(self.outcome_token_pool)
|
615
|
+
yes_outcome_pool_size = outcome_token_pool[self.get_outcome_str_from_bool(True)]
|
616
|
+
no_outcome_pool_size = outcome_token_pool[self.get_outcome_str_from_bool(False)]
|
617
|
+
|
618
|
+
new_yes_outcome_pool_size = yes_outcome_pool_size + (
|
619
|
+
bet_amount.amount * (1 - self.fee)
|
620
|
+
)
|
621
|
+
new_no_outcome_pool_size = no_outcome_pool_size + (
|
622
|
+
bet_amount.amount * (1 - self.fee)
|
623
|
+
)
|
624
|
+
|
625
|
+
received_token_amount = self.get_buy_token_amount(bet_amount, direction).amount
|
626
|
+
if direction:
|
627
|
+
new_yes_outcome_pool_size -= received_token_amount
|
628
|
+
else:
|
629
|
+
new_no_outcome_pool_size -= received_token_amount
|
630
|
+
|
631
|
+
new_p_yes = new_no_outcome_pool_size / (
|
632
|
+
new_yes_outcome_pool_size + new_no_outcome_pool_size
|
633
|
+
)
|
634
|
+
return Probability(new_p_yes)
|
635
|
+
|
580
636
|
|
581
637
|
def get_omen_user_url(address: ChecksumAddress) -> str:
|
582
638
|
return f"https://gnosisscan.io/address/{address}"
|
@@ -1165,3 +1221,33 @@ def withdraw_wxdai_to_xdai_to_keep_balance(
|
|
1165
1221
|
logger.info(
|
1166
1222
|
f"Withdrew {need_to_withdraw} wxDai to keep the balance above the minimum required balance {min_required_balance}."
|
1167
1223
|
)
|
1224
|
+
|
1225
|
+
|
1226
|
+
def get_buy_outcome_token_amount(
|
1227
|
+
investment_amount: float,
|
1228
|
+
buy_direction: bool,
|
1229
|
+
yes_outcome_pool_size: float,
|
1230
|
+
no_outcome_pool_size: float,
|
1231
|
+
fee: float,
|
1232
|
+
) -> float:
|
1233
|
+
"""
|
1234
|
+
Calculates the amount of outcome tokens received for a given investment
|
1235
|
+
|
1236
|
+
Taken from https://github.com/gnosis/conditional-tokens-market-makers/blob/6814c0247c745680bb13298d4f0dd7f5b574d0db/contracts/FixedProductMarketMaker.sol#L264
|
1237
|
+
"""
|
1238
|
+
investment_amount_minus_fees = investment_amount * (1 - fee)
|
1239
|
+
buy_token_pool_balance = (
|
1240
|
+
yes_outcome_pool_size if buy_direction else no_outcome_pool_size
|
1241
|
+
)
|
1242
|
+
|
1243
|
+
pool_balance = no_outcome_pool_size if buy_direction else yes_outcome_pool_size
|
1244
|
+
denominator = pool_balance + investment_amount_minus_fees
|
1245
|
+
ending_outcome_balance = buy_token_pool_balance * pool_balance / denominator
|
1246
|
+
|
1247
|
+
if ending_outcome_balance <= 0:
|
1248
|
+
raise ValueError("must have non-zero balances")
|
1249
|
+
|
1250
|
+
result = (
|
1251
|
+
buy_token_pool_balance + investment_amount_minus_fees - ending_outcome_balance
|
1252
|
+
)
|
1253
|
+
return result
|
@@ -19,8 +19,7 @@ from prediction_market_agent_tooling.gtypes import (
|
|
19
19
|
from prediction_market_agent_tooling.loggers import logger
|
20
20
|
from prediction_market_agent_tooling.markets.agent_market import FilterBy, SortBy
|
21
21
|
from prediction_market_agent_tooling.markets.omen.data_models import (
|
22
|
-
|
23
|
-
OMEN_TRUE_OUTCOME,
|
22
|
+
OMEN_BINARY_MARKET_OUTCOMES,
|
24
23
|
OmenBet,
|
25
24
|
OmenMarket,
|
26
25
|
OmenPosition,
|
@@ -204,7 +203,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
204
203
|
self,
|
205
204
|
creator: t.Optional[HexAddress] = None,
|
206
205
|
creator_in: t.Optional[t.Sequence[HexAddress]] = None,
|
207
|
-
outcomes: list[str] =
|
206
|
+
outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES,
|
208
207
|
created_after: t.Optional[datetime] = None,
|
209
208
|
opened_before: t.Optional[datetime] = None,
|
210
209
|
opened_after: t.Optional[datetime] = None,
|
@@ -217,6 +216,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
217
216
|
id_in: list[str] | None = None,
|
218
217
|
excluded_questions: set[str] | None = None,
|
219
218
|
collateral_token_address_in: tuple[ChecksumAddress, ...] | None = None,
|
219
|
+
category: str | None = None,
|
220
220
|
) -> dict[str, t.Any]:
|
221
221
|
where_stms: dict[str, t.Any] = {
|
222
222
|
"isPendingArbitration": False,
|
@@ -282,6 +282,9 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
282
282
|
finalized_after
|
283
283
|
)
|
284
284
|
|
285
|
+
if category:
|
286
|
+
where_stms["category"] = category
|
287
|
+
|
285
288
|
# `excluded_question_titles` can not be an empty list, otherwise the API bugs out and returns nothing.
|
286
289
|
excluded_question_titles = [""]
|
287
290
|
if excluded_questions:
|
@@ -324,8 +327,10 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
324
327
|
# Additional filters, these can not be modified by the enums above.
|
325
328
|
created_after: datetime | None = None,
|
326
329
|
excluded_questions: set[str] | None = None, # question titles
|
327
|
-
collateral_token_address_in:
|
328
|
-
|
330
|
+
collateral_token_address_in: (
|
331
|
+
tuple[ChecksumAddress, ...] | None
|
332
|
+
) = SAFE_COLLATERAL_TOKEN_MARKETS,
|
333
|
+
category: str | None = None,
|
329
334
|
) -> t.List[OmenMarket]:
|
330
335
|
"""
|
331
336
|
Simplified `get_omen_binary_markets` method, which allows to fetch markets based on the filter_by and sort_by values.
|
@@ -363,6 +368,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
363
368
|
created_after=created_after,
|
364
369
|
excluded_questions=excluded_questions,
|
365
370
|
collateral_token_address_in=collateral_token_address_in,
|
371
|
+
category=category,
|
366
372
|
)
|
367
373
|
|
368
374
|
def get_omen_binary_markets(
|
@@ -383,9 +389,11 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
383
389
|
excluded_questions: set[str] | None = None, # question titles
|
384
390
|
sort_by_field: FieldPath | None = None,
|
385
391
|
sort_direction: str | None = None,
|
386
|
-
outcomes: list[str] =
|
387
|
-
collateral_token_address_in:
|
388
|
-
|
392
|
+
outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES,
|
393
|
+
collateral_token_address_in: (
|
394
|
+
tuple[ChecksumAddress, ...] | None
|
395
|
+
) = SAFE_COLLATERAL_TOKEN_MARKETS,
|
396
|
+
category: str | None = None,
|
389
397
|
) -> t.List[OmenMarket]:
|
390
398
|
"""
|
391
399
|
Complete method to fetch Omen binary markets with various filters, use `get_omen_binary_markets_simple` for simplified version that uses FilterBy and SortBy enums.
|
@@ -406,6 +414,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
406
414
|
excluded_questions=excluded_questions,
|
407
415
|
liquidity_bigger_than=liquidity_bigger_than,
|
408
416
|
collateral_token_address_in=collateral_token_address_in,
|
417
|
+
category=category,
|
409
418
|
)
|
410
419
|
|
411
420
|
# These values can not be set to `None`, but they can be omitted.
|
@@ -211,7 +211,11 @@ def monitor_agent(agent: DeployedAgent) -> None:
|
|
211
211
|
key=f"{agent.name}_x_axis_column",
|
212
212
|
)
|
213
213
|
|
214
|
-
bets_df =
|
214
|
+
bets_df = (
|
215
|
+
pd.DataFrame(bets_info)
|
216
|
+
.sort_values(by=x_axis_column, ascending=False)
|
217
|
+
.reset_index(drop=True)
|
218
|
+
)
|
215
219
|
bets_df["x-axis-day"] = bets_df[x_axis_column].dt.date
|
216
220
|
|
217
221
|
# Metrics
|
File without changes
|
@@ -0,0 +1,142 @@
|
|
1
|
+
from prediction_market_agent_tooling.tools.betting_strategies.utils import SimpleBet
|
2
|
+
|
3
|
+
|
4
|
+
def check_is_valid_probability(probability: float) -> None:
|
5
|
+
if not 0 <= probability <= 1:
|
6
|
+
raise ValueError("Probability must be between 0 and 1")
|
7
|
+
|
8
|
+
|
9
|
+
def get_kelly_bet_simplified(
|
10
|
+
max_bet: float,
|
11
|
+
market_p_yes: float,
|
12
|
+
estimated_p_yes: float,
|
13
|
+
confidence: float,
|
14
|
+
) -> SimpleBet:
|
15
|
+
"""
|
16
|
+
Calculate the optimal bet amount using the Kelly Criterion for a binary outcome market.
|
17
|
+
|
18
|
+
From https://en.wikipedia.org/wiki/Kelly_criterion:
|
19
|
+
|
20
|
+
f* = p - q / b
|
21
|
+
|
22
|
+
where:
|
23
|
+
- f* is the fraction of the current bankroll to wager
|
24
|
+
- p is the probability of a win
|
25
|
+
- q = 1-p is the probability of a loss
|
26
|
+
- b is the proportion of the bet gained with a win
|
27
|
+
|
28
|
+
Note: this calculation does not factor in that the bet changes the market
|
29
|
+
odds. This means the calculation is only accurate if the bet size is small
|
30
|
+
compared to the market volume. See discussion here for more detail:
|
31
|
+
https://github.com/gnosis/prediction-market-agent-tooling/pull/330#discussion_r1698269328
|
32
|
+
"""
|
33
|
+
check_is_valid_probability(market_p_yes)
|
34
|
+
check_is_valid_probability(estimated_p_yes)
|
35
|
+
check_is_valid_probability(confidence)
|
36
|
+
|
37
|
+
if estimated_p_yes > market_p_yes:
|
38
|
+
bet_direction = True
|
39
|
+
market_prob = market_p_yes
|
40
|
+
else:
|
41
|
+
bet_direction = False
|
42
|
+
market_prob = 1 - market_p_yes
|
43
|
+
|
44
|
+
# Handle the case where market_prob is 0
|
45
|
+
if market_prob == 0:
|
46
|
+
market_prob = 1e-10
|
47
|
+
|
48
|
+
edge = abs(estimated_p_yes - market_p_yes) * confidence
|
49
|
+
odds = (1 / market_prob) - 1
|
50
|
+
kelly_fraction = edge / odds
|
51
|
+
|
52
|
+
# Ensure bet size is non-negative does not exceed the wallet balance
|
53
|
+
bet_size = min(kelly_fraction * max_bet, max_bet)
|
54
|
+
|
55
|
+
return SimpleBet(direction=bet_direction, size=bet_size)
|
56
|
+
|
57
|
+
|
58
|
+
def get_kelly_bet_full(
|
59
|
+
yes_outcome_pool_size: float,
|
60
|
+
no_outcome_pool_size: float,
|
61
|
+
estimated_p_yes: float,
|
62
|
+
confidence: float,
|
63
|
+
max_bet: float,
|
64
|
+
fee: float = 0.0, # proportion, 0 to 1
|
65
|
+
) -> SimpleBet:
|
66
|
+
"""
|
67
|
+
Calculate the optimal bet amount using the Kelly Criterion for a binary outcome market.
|
68
|
+
|
69
|
+
'Full' as in it accounts for how the bet changes the market odds.
|
70
|
+
|
71
|
+
Taken from https://github.com/valory-xyz/trader/blob/main/strategies/kelly_criterion/kelly_criterion.py
|
72
|
+
|
73
|
+
with derivation in PR description: https://github.com/valory-xyz/trader/pull/119
|
74
|
+
|
75
|
+
```
|
76
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
77
|
+
you may not use this file except in compliance with the License.
|
78
|
+
You may obtain a copy of the License at
|
79
|
+
|
80
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
81
|
+
|
82
|
+
Unless required by applicable law or agreed to in writing, software
|
83
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
84
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
85
|
+
See the License for the specific language governing permissions and
|
86
|
+
limitations under the License.
|
87
|
+
```
|
88
|
+
"""
|
89
|
+
check_is_valid_probability(estimated_p_yes)
|
90
|
+
check_is_valid_probability(confidence)
|
91
|
+
check_is_valid_probability(fee)
|
92
|
+
|
93
|
+
if max_bet == 0:
|
94
|
+
return SimpleBet(direction=True, size=0)
|
95
|
+
|
96
|
+
x = yes_outcome_pool_size
|
97
|
+
y = no_outcome_pool_size
|
98
|
+
p = estimated_p_yes
|
99
|
+
c = confidence
|
100
|
+
b = max_bet
|
101
|
+
f = 1 - fee
|
102
|
+
|
103
|
+
if x == y:
|
104
|
+
# Add a delta to prevent division by zero
|
105
|
+
y += 1e-10
|
106
|
+
|
107
|
+
numerator = (
|
108
|
+
-4 * x**2 * y
|
109
|
+
+ b * y**2 * p * c * f
|
110
|
+
+ 2 * b * x * y * p * c * f
|
111
|
+
+ b * x**2 * p * c * f
|
112
|
+
- 2 * b * y**2 * f
|
113
|
+
- 2 * b * x * y * f
|
114
|
+
+ (
|
115
|
+
(
|
116
|
+
4 * x**2 * y
|
117
|
+
- b * y**2 * p * c * f
|
118
|
+
- 2 * b * x * y * p * c * f
|
119
|
+
- b * x**2 * p * c * f
|
120
|
+
+ 2 * b * y**2 * f
|
121
|
+
+ 2 * b * x * y * f
|
122
|
+
)
|
123
|
+
** 2
|
124
|
+
- (
|
125
|
+
4
|
126
|
+
* (x**2 * f - y**2 * f)
|
127
|
+
* (
|
128
|
+
-4 * b * x * y**2 * p * c
|
129
|
+
- 4 * b * x**2 * y * p * c
|
130
|
+
+ 4 * b * x * y**2
|
131
|
+
)
|
132
|
+
)
|
133
|
+
)
|
134
|
+
** (1 / 2)
|
135
|
+
)
|
136
|
+
denominator = 2 * (x**2 * f - y**2 * f)
|
137
|
+
kelly_bet_amount = numerator / denominator
|
138
|
+
|
139
|
+
# Clip the bet size to max_bet to account for rounding errors.
|
140
|
+
return SimpleBet(
|
141
|
+
direction=kelly_bet_amount > 0, size=min(max_bet, abs(kelly_bet_amount))
|
142
|
+
)
|