prediction-market-agent-tooling 0.64.12.dev659__py3-none-any.whl → 0.65.0__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/benchmark/agents.py +19 -16
- prediction_market_agent_tooling/benchmark/benchmark.py +94 -84
- prediction_market_agent_tooling/benchmark/utils.py +8 -9
- prediction_market_agent_tooling/chains.py +4 -0
- prediction_market_agent_tooling/config.py +4 -3
- prediction_market_agent_tooling/deploy/agent.py +85 -125
- prediction_market_agent_tooling/deploy/agent_example.py +20 -10
- prediction_market_agent_tooling/deploy/betting_strategy.py +222 -96
- prediction_market_agent_tooling/deploy/constants.py +4 -0
- prediction_market_agent_tooling/jobs/jobs_models.py +15 -4
- prediction_market_agent_tooling/jobs/omen/omen_jobs.py +3 -3
- prediction_market_agent_tooling/markets/agent_market.py +145 -50
- prediction_market_agent_tooling/markets/blockchain_utils.py +10 -1
- prediction_market_agent_tooling/markets/data_models.py +83 -17
- prediction_market_agent_tooling/markets/manifold/api.py +18 -7
- prediction_market_agent_tooling/markets/manifold/data_models.py +23 -16
- prediction_market_agent_tooling/markets/manifold/manifold.py +18 -18
- prediction_market_agent_tooling/markets/manifold/utils.py +7 -12
- prediction_market_agent_tooling/markets/markets.py +2 -1
- prediction_market_agent_tooling/markets/metaculus/metaculus.py +29 -4
- prediction_market_agent_tooling/markets/omen/data_models.py +17 -32
- prediction_market_agent_tooling/markets/omen/omen.py +65 -108
- prediction_market_agent_tooling/markets/omen/omen_contracts.py +2 -5
- prediction_market_agent_tooling/markets/omen/omen_resolving.py +13 -13
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +18 -12
- prediction_market_agent_tooling/markets/polymarket/data_models.py +7 -3
- prediction_market_agent_tooling/markets/polymarket/data_models_web.py +7 -3
- prediction_market_agent_tooling/markets/polymarket/polymarket.py +5 -4
- prediction_market_agent_tooling/markets/seer/data_models.py +0 -83
- prediction_market_agent_tooling/markets/seer/price_manager.py +44 -30
- prediction_market_agent_tooling/markets/seer/seer.py +105 -105
- prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +34 -41
- prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +1 -1
- prediction_market_agent_tooling/tools/cow/cow_order.py +10 -3
- prediction_market_agent_tooling/tools/is_predictable.py +2 -3
- prediction_market_agent_tooling/tools/langfuse_client_utils.py +4 -4
- prediction_market_agent_tooling/tools/omen/sell_positions.py +3 -2
- prediction_market_agent_tooling/tools/utils.py +26 -13
- {prediction_market_agent_tooling-0.64.12.dev659.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/METADATA +2 -2
- {prediction_market_agent_tooling-0.64.12.dev659.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/RECORD +43 -52
- prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py +0 -68
- prediction_market_agent_tooling/monitor/markets/manifold.py +0 -90
- prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -43
- prediction_market_agent_tooling/monitor/markets/omen.py +0 -88
- prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -49
- prediction_market_agent_tooling/monitor/monitor.py +0 -406
- prediction_market_agent_tooling/monitor/monitor_app.py +0 -149
- prediction_market_agent_tooling/monitor/monitor_settings.py +0 -27
- prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -146
- prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -12
- {prediction_market_agent_tooling-0.64.12.dev659.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.64.12.dev659.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.64.12.dev659.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/entry_points.txt +0 -0
@@ -1,43 +0,0 @@
|
|
1
|
-
import typing as t
|
2
|
-
|
3
|
-
from google.cloud.functions_v2.types.functions import Function
|
4
|
-
|
5
|
-
from prediction_market_agent_tooling.config import APIKeys
|
6
|
-
from prediction_market_agent_tooling.deploy.constants import MARKET_TYPE_KEY
|
7
|
-
from prediction_market_agent_tooling.gtypes import DatetimeUTC
|
8
|
-
from prediction_market_agent_tooling.markets.data_models import ResolvedBet
|
9
|
-
from prediction_market_agent_tooling.markets.markets import MarketType
|
10
|
-
from prediction_market_agent_tooling.monitor.monitor import DeployedAgent
|
11
|
-
|
12
|
-
|
13
|
-
class DeployedMetaculusAgent(DeployedAgent):
|
14
|
-
user: int
|
15
|
-
|
16
|
-
@property
|
17
|
-
def public_id(self) -> str:
|
18
|
-
return str(self.user)
|
19
|
-
|
20
|
-
def get_resolved_bets(self) -> list[ResolvedBet]:
|
21
|
-
raise NotImplementedError("TODO: Implement to allow betting on Metaculus.")
|
22
|
-
|
23
|
-
@staticmethod
|
24
|
-
def from_api_keys(
|
25
|
-
name: str,
|
26
|
-
start_time: DatetimeUTC,
|
27
|
-
api_keys: APIKeys,
|
28
|
-
) -> "DeployedMetaculusAgent":
|
29
|
-
return DeployedMetaculusAgent(
|
30
|
-
name=name,
|
31
|
-
start_time=start_time,
|
32
|
-
user=api_keys.metaculus_user_id,
|
33
|
-
)
|
34
|
-
|
35
|
-
@classmethod
|
36
|
-
def from_all_gcp_functions(
|
37
|
-
cls: t.Type["DeployedMetaculusAgent"],
|
38
|
-
filter_: t.Callable[[Function], bool] = lambda function: function.labels[
|
39
|
-
MARKET_TYPE_KEY
|
40
|
-
]
|
41
|
-
== MarketType.METACULUS.value,
|
42
|
-
) -> t.Sequence["DeployedMetaculusAgent"]:
|
43
|
-
return super().from_all_gcp_functions(filter_=filter_)
|
@@ -1,88 +0,0 @@
|
|
1
|
-
import typing as t
|
2
|
-
|
3
|
-
from google.cloud.functions_v2.types.functions import Function
|
4
|
-
|
5
|
-
from prediction_market_agent_tooling.config import APIKeys
|
6
|
-
from prediction_market_agent_tooling.deploy.constants import MARKET_TYPE_KEY
|
7
|
-
from prediction_market_agent_tooling.gtypes import ChecksumAddress, DatetimeUTC
|
8
|
-
from prediction_market_agent_tooling.markets.data_models import ResolvedBet
|
9
|
-
from prediction_market_agent_tooling.markets.markets import MarketType
|
10
|
-
from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
11
|
-
OmenSubgraphHandler,
|
12
|
-
)
|
13
|
-
from prediction_market_agent_tooling.monitor.monitor import (
|
14
|
-
DeployedAgent,
|
15
|
-
KubernetesCronJob,
|
16
|
-
)
|
17
|
-
|
18
|
-
|
19
|
-
class DeployedOmenAgent(DeployedAgent):
|
20
|
-
omen_public_key: ChecksumAddress
|
21
|
-
|
22
|
-
@property
|
23
|
-
def public_id(self) -> str:
|
24
|
-
return self.omen_public_key
|
25
|
-
|
26
|
-
def get_resolved_bets(self) -> list[ResolvedBet]:
|
27
|
-
# For monitoring of deployed agent, return only resolved bets with valid answer.
|
28
|
-
subgraph_handler = OmenSubgraphHandler()
|
29
|
-
bets = subgraph_handler.get_resolved_bets_with_valid_answer(
|
30
|
-
better_address=self.omen_public_key,
|
31
|
-
start_time=self.start_time,
|
32
|
-
end_time=self.end_time,
|
33
|
-
)
|
34
|
-
return [b.to_generic_resolved_bet() for b in bets]
|
35
|
-
|
36
|
-
@classmethod
|
37
|
-
def from_env_vars_without_prefix(
|
38
|
-
cls: t.Type["DeployedOmenAgent"],
|
39
|
-
env_vars: dict[str, t.Any] | None = None,
|
40
|
-
extra_vars: dict[str, t.Any] | None = None,
|
41
|
-
) -> "DeployedOmenAgent":
|
42
|
-
# If omen_public_key is not provided, try to use it from APIKeys initialized from env_vars (will work in case that secret private key was in the env).
|
43
|
-
api_keys = APIKeys(**env_vars) if env_vars else None
|
44
|
-
if (
|
45
|
-
env_vars
|
46
|
-
and "omen_public_key" not in env_vars
|
47
|
-
and api_keys
|
48
|
-
and api_keys.BET_FROM_PRIVATE_KEY is not None
|
49
|
-
and api_keys.BET_FROM_PRIVATE_KEY
|
50
|
-
!= APIKeys().BET_FROM_PRIVATE_KEY # Check that it didn't get if from the default env.
|
51
|
-
):
|
52
|
-
env_vars["omen_public_key"] = api_keys.bet_from_address
|
53
|
-
return super().from_env_vars_without_prefix(
|
54
|
-
env_vars=env_vars, extra_vars=extra_vars
|
55
|
-
)
|
56
|
-
|
57
|
-
@staticmethod
|
58
|
-
def from_api_keys(
|
59
|
-
name: str,
|
60
|
-
start_time: DatetimeUTC,
|
61
|
-
api_keys: APIKeys,
|
62
|
-
) -> "DeployedOmenAgent":
|
63
|
-
return DeployedOmenAgent(
|
64
|
-
name=name,
|
65
|
-
start_time=start_time,
|
66
|
-
omen_public_key=api_keys.bet_from_address,
|
67
|
-
)
|
68
|
-
|
69
|
-
@classmethod
|
70
|
-
def from_all_gcp_functions(
|
71
|
-
cls: t.Type["DeployedOmenAgent"],
|
72
|
-
filter_: t.Callable[[Function], bool] = lambda function: function.labels[
|
73
|
-
MARKET_TYPE_KEY
|
74
|
-
]
|
75
|
-
== MarketType.OMEN.value,
|
76
|
-
) -> t.Sequence["DeployedOmenAgent"]:
|
77
|
-
return super().from_all_gcp_functions(filter_=filter_)
|
78
|
-
|
79
|
-
@classmethod
|
80
|
-
def from_all_gcp_cronjobs(
|
81
|
-
cls: t.Type["DeployedOmenAgent"],
|
82
|
-
namespace: str,
|
83
|
-
filter_: t.Callable[
|
84
|
-
[KubernetesCronJob], bool
|
85
|
-
] = lambda cronjob: cronjob.metadata.labels[MARKET_TYPE_KEY]
|
86
|
-
== MarketType.OMEN.value,
|
87
|
-
) -> t.Sequence["DeployedOmenAgent"]:
|
88
|
-
return super().from_all_gcp_cronjobs(namespace=namespace, filter_=filter_)
|
@@ -1,49 +0,0 @@
|
|
1
|
-
import typing as t
|
2
|
-
|
3
|
-
from google.cloud.functions_v2.types.functions import Function
|
4
|
-
|
5
|
-
from prediction_market_agent_tooling.config import APIKeys
|
6
|
-
from prediction_market_agent_tooling.deploy.constants import MARKET_TYPE_KEY
|
7
|
-
from prediction_market_agent_tooling.gtypes import ChecksumAddress, DatetimeUTC
|
8
|
-
from prediction_market_agent_tooling.markets.data_models import ResolvedBet
|
9
|
-
from prediction_market_agent_tooling.markets.markets import MarketType
|
10
|
-
from prediction_market_agent_tooling.monitor.monitor import DeployedAgent
|
11
|
-
|
12
|
-
|
13
|
-
class DeployedPolymarketAgent(DeployedAgent):
|
14
|
-
# Note: Public key seems like the right option to identify agent, but as we aren't implementing rest of the logic right now,
|
15
|
-
# it might not be the correct one and it's okay to change this (and related stuff) if needed.
|
16
|
-
polymarket_public_key: ChecksumAddress
|
17
|
-
|
18
|
-
@property
|
19
|
-
def public_id(self) -> str:
|
20
|
-
return self.polymarket_public_key
|
21
|
-
|
22
|
-
def get_resolved_bets(self) -> list[ResolvedBet]:
|
23
|
-
raise NotImplementedError("TODO: Implement to allow betting on Polymarket.")
|
24
|
-
|
25
|
-
@staticmethod
|
26
|
-
def from_api_keys(
|
27
|
-
name: str,
|
28
|
-
start_time: DatetimeUTC,
|
29
|
-
api_keys: APIKeys,
|
30
|
-
) -> "DeployedPolymarketAgent":
|
31
|
-
return DeployedPolymarketAgent(
|
32
|
-
name=name,
|
33
|
-
start_time=start_time,
|
34
|
-
polymarket_public_key=api_keys.bet_from_address,
|
35
|
-
)
|
36
|
-
|
37
|
-
@classmethod
|
38
|
-
def from_all_gcp_functions(
|
39
|
-
cls: t.Type["DeployedPolymarketAgent"],
|
40
|
-
filter_: t.Callable[[Function], bool] = lambda function: function.labels[
|
41
|
-
MARKET_TYPE_KEY
|
42
|
-
]
|
43
|
-
== MarketType.POLYMARKET.value,
|
44
|
-
) -> t.Sequence["DeployedPolymarketAgent"]:
|
45
|
-
return super().from_all_gcp_functions(filter_=filter_)
|
46
|
-
|
47
|
-
@staticmethod
|
48
|
-
def get_user_id(api_keys: APIKeys) -> str:
|
49
|
-
return api_keys.bet_from_address
|
@@ -1,406 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import os
|
3
|
-
import typing as t
|
4
|
-
from itertools import groupby
|
5
|
-
|
6
|
-
import altair as alt
|
7
|
-
import numpy as np
|
8
|
-
import pandas as pd
|
9
|
-
import streamlit as st
|
10
|
-
from google.cloud.functions_v2.types.functions import Function
|
11
|
-
from pydantic import BaseModel
|
12
|
-
|
13
|
-
from prediction_market_agent_tooling.config import APIKeys
|
14
|
-
from prediction_market_agent_tooling.deploy.gcp.kubernetes_models import (
|
15
|
-
KubernetesCronJob,
|
16
|
-
)
|
17
|
-
from prediction_market_agent_tooling.deploy.gcp.utils import (
|
18
|
-
gcp_get_secret_value,
|
19
|
-
get_gcp_configmap_data,
|
20
|
-
get_gcp_function,
|
21
|
-
list_gcp_cronjobs,
|
22
|
-
list_gcp_functions,
|
23
|
-
)
|
24
|
-
from prediction_market_agent_tooling.loggers import logger
|
25
|
-
from prediction_market_agent_tooling.markets.agent_market import AgentMarket
|
26
|
-
from prediction_market_agent_tooling.markets.data_models import Resolution, ResolvedBet
|
27
|
-
from prediction_market_agent_tooling.tools.parallelism import par_map
|
28
|
-
from prediction_market_agent_tooling.tools.utils import (
|
29
|
-
DatetimeUTC,
|
30
|
-
check_not_none,
|
31
|
-
should_not_happen,
|
32
|
-
)
|
33
|
-
|
34
|
-
C = t.TypeVar("C", bound="DeployedAgent")
|
35
|
-
|
36
|
-
|
37
|
-
class DeployedAgent(BaseModel):
|
38
|
-
PREFIX: t.ClassVar["str"] = "deployedagent_var_"
|
39
|
-
|
40
|
-
name: str
|
41
|
-
|
42
|
-
start_time: DatetimeUTC
|
43
|
-
end_time: DatetimeUTC | None = None # TODO: If we want end time, we need to store agents somewhere, not just query them from functions.
|
44
|
-
|
45
|
-
raw_labels: dict[str, str] | None = None
|
46
|
-
raw_env_vars: dict[str, str] | None = None
|
47
|
-
|
48
|
-
def model_dump_prefixed(self) -> dict[str, t.Any]:
|
49
|
-
return {
|
50
|
-
self.PREFIX + k: v for k, v in self.model_dump().items() if v is not None
|
51
|
-
}
|
52
|
-
|
53
|
-
def get_resolved_bets(self) -> list[ResolvedBet]:
|
54
|
-
raise NotImplementedError("Subclasses must implement this method.")
|
55
|
-
|
56
|
-
@property
|
57
|
-
def public_id(self) -> str:
|
58
|
-
raise NotImplementedError("Subclasses must implement this method.")
|
59
|
-
|
60
|
-
@classmethod
|
61
|
-
def from_env_vars_without_prefix(
|
62
|
-
cls: t.Type[C],
|
63
|
-
env_vars: dict[str, t.Any] | None = None,
|
64
|
-
extra_vars: dict[str, t.Any] | None = None,
|
65
|
-
) -> C:
|
66
|
-
return cls.model_validate((env_vars or dict(os.environ)) | (extra_vars or {}))
|
67
|
-
|
68
|
-
@classmethod
|
69
|
-
def from_env_vars(
|
70
|
-
cls: t.Type[C],
|
71
|
-
env_vars: dict[str, t.Any] | None = None,
|
72
|
-
extra_vars: dict[str, t.Any] | None = None,
|
73
|
-
) -> C:
|
74
|
-
return cls.from_env_vars_without_prefix(
|
75
|
-
env_vars={
|
76
|
-
k.replace(cls.PREFIX, ""): v
|
77
|
-
for k, v in (env_vars or dict(os.environ)).items()
|
78
|
-
if k.startswith(cls.PREFIX)
|
79
|
-
},
|
80
|
-
extra_vars=extra_vars,
|
81
|
-
)
|
82
|
-
|
83
|
-
@staticmethod
|
84
|
-
def from_api_keys(
|
85
|
-
name: str,
|
86
|
-
start_time: DatetimeUTC,
|
87
|
-
api_keys: APIKeys,
|
88
|
-
) -> "DeployedAgent":
|
89
|
-
raise NotImplementedError("Subclasses must implement this method.")
|
90
|
-
|
91
|
-
@classmethod
|
92
|
-
def from_gcp_function(cls: t.Type[C], function: Function) -> C:
|
93
|
-
return cls.from_env_vars_without_prefix(
|
94
|
-
env_vars=dict(function.service_config.environment_variables),
|
95
|
-
extra_vars={
|
96
|
-
"raw_labels": dict(function.labels),
|
97
|
-
"raw_env_vars": dict(function.service_config.environment_variables),
|
98
|
-
},
|
99
|
-
)
|
100
|
-
|
101
|
-
@classmethod
|
102
|
-
def from_gcp_function_name(cls: t.Type[C], function_name: str) -> C:
|
103
|
-
return cls.from_gcp_function(get_gcp_function(function_name))
|
104
|
-
|
105
|
-
@classmethod
|
106
|
-
def from_all_gcp_functions(
|
107
|
-
cls: t.Type[C], filter_: t.Callable[[Function], bool] = lambda x: True
|
108
|
-
) -> t.Sequence[C]:
|
109
|
-
agents: list[C] = []
|
110
|
-
|
111
|
-
for function in list_gcp_functions():
|
112
|
-
if not filter_(function):
|
113
|
-
continue
|
114
|
-
|
115
|
-
logger.info(f"Loading function: {function.name}")
|
116
|
-
|
117
|
-
try:
|
118
|
-
agents.append(cls.from_gcp_function(function))
|
119
|
-
except ValueError as e:
|
120
|
-
logger.warning(
|
121
|
-
f"Could not parse `{function.name}` into {cls.__name__}: {e}."
|
122
|
-
)
|
123
|
-
|
124
|
-
return agents
|
125
|
-
|
126
|
-
@classmethod
|
127
|
-
def from_gcp_cronjob(cls: t.Type[C], cronjob: KubernetesCronJob) -> C:
|
128
|
-
secret_env_name_to_env_value = {
|
129
|
-
e.name: json.loads(gcp_get_secret_value(e.valueFrom.secretKeyRef.name))[
|
130
|
-
e.valueFrom.secretKeyRef.key
|
131
|
-
]
|
132
|
-
for e in cronjob.spec.jobTemplate.spec.template.spec.containers[0].env
|
133
|
-
}
|
134
|
-
configmap_env_name_to_env_value = {
|
135
|
-
key: value
|
136
|
-
for e in cronjob.spec.jobTemplate.spec.template.spec.containers[0].envFrom
|
137
|
-
for key, value in get_gcp_configmap_data(
|
138
|
-
cronjob.metadata.namespace, e.configMapRef.name
|
139
|
-
).items()
|
140
|
-
}
|
141
|
-
|
142
|
-
return cls.from_env_vars_without_prefix(
|
143
|
-
env_vars=secret_env_name_to_env_value | configmap_env_name_to_env_value,
|
144
|
-
)
|
145
|
-
|
146
|
-
@classmethod
|
147
|
-
def from_all_gcp_cronjobs(
|
148
|
-
cls: t.Type[C],
|
149
|
-
namespace: str,
|
150
|
-
filter_: t.Callable[[KubernetesCronJob], bool] = lambda x: True,
|
151
|
-
) -> t.Sequence[C]:
|
152
|
-
agents: list[C] = []
|
153
|
-
|
154
|
-
for cronjob in list_gcp_cronjobs(namespace).items:
|
155
|
-
if not filter_(cronjob):
|
156
|
-
continue
|
157
|
-
|
158
|
-
logger.info(f"Loading cronjob: {cronjob.metadata.name}")
|
159
|
-
|
160
|
-
try:
|
161
|
-
agents.append(cls.from_gcp_cronjob(cronjob))
|
162
|
-
except ValueError as e:
|
163
|
-
logger.warning(
|
164
|
-
f"Could not parse `{cronjob.metadata.name}` into {cls.__name__}: {e}."
|
165
|
-
)
|
166
|
-
|
167
|
-
return agents
|
168
|
-
|
169
|
-
|
170
|
-
def monitor_agent(agent: DeployedAgent) -> None:
|
171
|
-
# Info
|
172
|
-
col1, col2, col3 = st.columns(3)
|
173
|
-
col1.markdown(f"Name: `{agent.name}`")
|
174
|
-
col2.markdown(f"Start Time: `{agent.start_time}`")
|
175
|
-
col3.markdown(f"Public ID: `{agent.public_id}`")
|
176
|
-
|
177
|
-
show_agent_bets = st.checkbox(
|
178
|
-
"Show resolved bets statistics", value=False, key=f"{agent.name}_show_bets"
|
179
|
-
)
|
180
|
-
if not show_agent_bets:
|
181
|
-
return
|
182
|
-
|
183
|
-
agent_bets = agent.get_resolved_bets()
|
184
|
-
if not agent_bets:
|
185
|
-
st.warning(f"No resolved bets found for {agent.name}.")
|
186
|
-
return
|
187
|
-
bets_info = {
|
188
|
-
"Market Question": [bet.market_question for bet in agent_bets],
|
189
|
-
"Bet Amount": [bet.amount for bet in agent_bets],
|
190
|
-
"Bet Outcome": [bet.outcome for bet in agent_bets],
|
191
|
-
"Created Time": [bet.created_time for bet in agent_bets],
|
192
|
-
"Resolved Time": [bet.resolved_time for bet in agent_bets],
|
193
|
-
"Is Correct": [bet.is_correct for bet in agent_bets],
|
194
|
-
"Profit": [round(bet.profit, 2) for bet in agent_bets],
|
195
|
-
}
|
196
|
-
|
197
|
-
# Time column to use for x-axes and sorting
|
198
|
-
x_axis_column = st.selectbox(
|
199
|
-
"X-axis column",
|
200
|
-
["Created Time", "Resolved Time"],
|
201
|
-
key=f"{agent.name}_x_axis_column",
|
202
|
-
)
|
203
|
-
|
204
|
-
bets_df = (
|
205
|
-
pd.DataFrame(bets_info)
|
206
|
-
.sort_values(by=x_axis_column, ascending=False)
|
207
|
-
.reset_index(drop=True)
|
208
|
-
)
|
209
|
-
bets_df["x-axis-day"] = bets_df[x_axis_column].dt.date
|
210
|
-
|
211
|
-
# Metrics
|
212
|
-
col1, col2 = st.columns(2)
|
213
|
-
col1.metric(label="Number of bets", value=f"{len(agent_bets)}")
|
214
|
-
col2.metric(label="% Correct", value=f"{100 * bets_df['Is Correct'].mean():.2f}%")
|
215
|
-
|
216
|
-
# Chart of grouped accuracy per day
|
217
|
-
per_day_accuracy = bets_df.groupby("x-axis-day")["Is Correct"].mean()
|
218
|
-
per_day_accuracy_chart = (
|
219
|
-
alt.Chart(per_day_accuracy.reset_index())
|
220
|
-
.encode(
|
221
|
-
x=alt.X("x-axis-day", axis=alt.Axis(format="%Y-%m-%d"), title=None),
|
222
|
-
y=alt.Y("Is Correct", axis=alt.Axis(format=".2f")),
|
223
|
-
)
|
224
|
-
.interactive()
|
225
|
-
)
|
226
|
-
st.altair_chart(
|
227
|
-
per_day_accuracy_chart.mark_line()
|
228
|
-
+ per_day_accuracy_chart.transform_loess("x-axis-day", "Is Correct").mark_line(
|
229
|
-
color="red", strokeDash=[5, 5]
|
230
|
-
),
|
231
|
-
use_container_width=True,
|
232
|
-
)
|
233
|
-
|
234
|
-
# Chart of cumulative profit per day
|
235
|
-
profit_info = {
|
236
|
-
"Time": bets_df[x_axis_column],
|
237
|
-
"Cumulative Profit": bets_df["Profit"].astype(float),
|
238
|
-
}
|
239
|
-
profit_df = pd.DataFrame(profit_info)
|
240
|
-
profit_df["Date"] = pd.to_datetime(profit_df["Time"].dt.date)
|
241
|
-
profit_df = (
|
242
|
-
profit_df.groupby("Date")["Cumulative Profit"].sum().cumsum().reset_index()
|
243
|
-
)
|
244
|
-
profit_df["Cumulative Profit"] = profit_df["Cumulative Profit"].astype(float)
|
245
|
-
st.altair_chart(
|
246
|
-
alt.Chart(profit_df)
|
247
|
-
.mark_line()
|
248
|
-
.encode(
|
249
|
-
x=alt.X("Date", axis=alt.Axis(format="%Y-%m-%d"), title=None),
|
250
|
-
y=alt.Y("Cumulative Profit", axis=alt.Axis(format=".2f")),
|
251
|
-
)
|
252
|
-
.interactive(),
|
253
|
-
use_container_width=True,
|
254
|
-
)
|
255
|
-
|
256
|
-
# Table of resolved bets
|
257
|
-
show_bet_history = st.checkbox(
|
258
|
-
"Show resolved bet history", value=False, key=f"{agent.name}_show_bet_history"
|
259
|
-
)
|
260
|
-
if show_bet_history:
|
261
|
-
st.subheader("Resolved Bet History")
|
262
|
-
st.table(bets_df.drop(columns=["x-axis-day"]))
|
263
|
-
|
264
|
-
|
265
|
-
def monitor_market(
|
266
|
-
open_markets: t.Sequence[AgentMarket], resolved_markets: t.Sequence[AgentMarket]
|
267
|
-
) -> None:
|
268
|
-
col1, col2 = st.columns(2)
|
269
|
-
col1.metric(label="Number open markets", value=f"{len(open_markets)}")
|
270
|
-
col2.metric(label="Number resolved markets", value=f"{len(resolved_markets)}")
|
271
|
-
|
272
|
-
monitor_brier_score(resolved_markets)
|
273
|
-
monitor_market_outcome_bias(open_markets, resolved_markets)
|
274
|
-
|
275
|
-
|
276
|
-
def monitor_brier_score(resolved_markets: t.Sequence[AgentMarket]) -> None:
|
277
|
-
"""
|
278
|
-
https://en.wikipedia.org/wiki/Brier_score
|
279
|
-
|
280
|
-
Calculate the Brier score for the resolved markets. Display a chart of the
|
281
|
-
rolling mean squared error for, and stats for:
|
282
|
-
|
283
|
-
- the overall brier score
|
284
|
-
- the brier score for the last 30 markets
|
285
|
-
"""
|
286
|
-
st.subheader("Brier Score (0-2, lower is better)")
|
287
|
-
|
288
|
-
# We need to use `get_last_trade_p_yes` instead of `current_p_yes` because, for resolved markets, the probabilities can be fixed to 0 and 1 (for example, on Omen).
|
289
|
-
# And for the brier score, we need the true market prediction, not its resolution after the outcome is known.
|
290
|
-
# If no trades were made, take it as 0.5 because the platform didn't provide any valuable information.
|
291
|
-
created_time_and_squared_errors_summed_across_outcomes = par_map(
|
292
|
-
list(resolved_markets),
|
293
|
-
lambda m: (
|
294
|
-
m.created_time,
|
295
|
-
(
|
296
|
-
(p_yes - m.boolean_outcome) ** 2
|
297
|
-
+ ((1 - p_yes) - (1 - m.boolean_outcome)) ** 2
|
298
|
-
if (p_yes := m.get_last_trade_p_yes()) is not None
|
299
|
-
else None
|
300
|
-
),
|
301
|
-
),
|
302
|
-
)
|
303
|
-
created_time_and_squared_errors_summed_across_outcomes_with_trades = [
|
304
|
-
x
|
305
|
-
for x in created_time_and_squared_errors_summed_across_outcomes
|
306
|
-
if x[1] is not None
|
307
|
-
]
|
308
|
-
df = pd.DataFrame(
|
309
|
-
created_time_and_squared_errors_summed_across_outcomes_with_trades,
|
310
|
-
columns=["Date", "Squared Error"],
|
311
|
-
).sort_values(by="Date")
|
312
|
-
|
313
|
-
# Compute rolling mean squared error for last 30 markets
|
314
|
-
df["Rolling Mean Squared Error"] = df["Squared Error"].rolling(window=30).mean()
|
315
|
-
|
316
|
-
st.write(f"Based on {len(df)} markets with at least one trade.")
|
317
|
-
|
318
|
-
col1, col2 = st.columns(2)
|
319
|
-
col1.metric(label="Overall", value=f"{df['Squared Error'].mean():.3f}")
|
320
|
-
col2.metric(
|
321
|
-
label="Last 30 markets", value=f"{df['Squared Error'].tail(30).mean():.3f}"
|
322
|
-
)
|
323
|
-
|
324
|
-
st.altair_chart(
|
325
|
-
alt.Chart(df)
|
326
|
-
.mark_line(interpolate="basis")
|
327
|
-
.encode(
|
328
|
-
x="Date:T",
|
329
|
-
y=alt.Y("Rolling Mean Squared Error:Q", scale=alt.Scale(domain=[0, 1])),
|
330
|
-
)
|
331
|
-
.interactive(),
|
332
|
-
use_container_width=True,
|
333
|
-
)
|
334
|
-
|
335
|
-
|
336
|
-
def monitor_market_outcome_bias(
|
337
|
-
open_markets: t.Sequence[AgentMarket], resolved_markets: t.Sequence[AgentMarket]
|
338
|
-
) -> None:
|
339
|
-
st.subheader("Market Outcome Bias")
|
340
|
-
|
341
|
-
date_to_open_yes_proportion = {
|
342
|
-
d: np.mean([int(m.current_p_yes > 0.5) for m in markets])
|
343
|
-
for d, markets in groupby(
|
344
|
-
open_markets,
|
345
|
-
lambda x: check_not_none(x.created_time, "Only markets with created time can be used here.").date(), # type: ignore # Bug, it says `Never has no attribute "date" [attr-defined]` with Mypy, but in VSCode it works correctly.
|
346
|
-
)
|
347
|
-
}
|
348
|
-
date_to_resolved_yes_proportion = {
|
349
|
-
d: np.mean([int(m.boolean_outcome) for m in markets])
|
350
|
-
for d, markets in groupby(
|
351
|
-
resolved_markets,
|
352
|
-
lambda x: check_not_none(x.created_time, "Only markets with created time can be used here.").date(), # type: ignore # Bug, it says `Never has no attribute "date" [attr-defined]` with Mypy, but in VSCode it works correctly.
|
353
|
-
)
|
354
|
-
}
|
355
|
-
|
356
|
-
df_open = pd.DataFrame(
|
357
|
-
date_to_open_yes_proportion.items(), columns=["date", "open_proportion"]
|
358
|
-
)
|
359
|
-
df_open["open_label"] = "Open's yes proportion"
|
360
|
-
df_resolved = pd.DataFrame(
|
361
|
-
date_to_resolved_yes_proportion.items(), columns=["date", "resolved_proportion"]
|
362
|
-
)
|
363
|
-
df_resolved["resolved_label"] = "Resolved's yes proportion"
|
364
|
-
|
365
|
-
df = pd.merge(df_open, df_resolved, on="date")
|
366
|
-
|
367
|
-
open_chart = (
|
368
|
-
alt.Chart(df)
|
369
|
-
.mark_line()
|
370
|
-
.encode(x="date:T", y="open_proportion:Q", color="open_label:N")
|
371
|
-
)
|
372
|
-
|
373
|
-
resolved_chart = (
|
374
|
-
alt.Chart(df)
|
375
|
-
.mark_line()
|
376
|
-
.encode(x="date:T", y="resolved_proportion:Q", color="resolved_label:N")
|
377
|
-
)
|
378
|
-
|
379
|
-
if len(df) > 0:
|
380
|
-
st.altair_chart(
|
381
|
-
alt.layer(open_chart, resolved_chart).interactive(),
|
382
|
-
use_container_width=True,
|
383
|
-
)
|
384
|
-
|
385
|
-
all_open_markets_yes_mean = np.mean(
|
386
|
-
[int(m.current_p_yes > 0.5) for m in open_markets]
|
387
|
-
)
|
388
|
-
all_resolved_markets_yes_mean = np.mean(
|
389
|
-
[
|
390
|
-
(
|
391
|
-
1
|
392
|
-
if m.resolution == Resolution.YES
|
393
|
-
else (
|
394
|
-
0
|
395
|
-
if m.resolution == Resolution.NO
|
396
|
-
else should_not_happen(f"Unexpected resolution: {m.resolution}")
|
397
|
-
)
|
398
|
-
)
|
399
|
-
for m in resolved_markets
|
400
|
-
]
|
401
|
-
)
|
402
|
-
st.markdown(
|
403
|
-
f"Mean proportion of 'YES' in open markets: {all_open_markets_yes_mean:.2f}"
|
404
|
-
"\n\n"
|
405
|
-
f"Mean proportion of 'YES' in resolved markets: {all_resolved_markets_yes_mean:.2f}"
|
406
|
-
)
|