prediction-market-agent-tooling 0.64.12.dev660__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.
Files changed (51) hide show
  1. prediction_market_agent_tooling/benchmark/agents.py +19 -16
  2. prediction_market_agent_tooling/benchmark/benchmark.py +94 -84
  3. prediction_market_agent_tooling/benchmark/utils.py +8 -9
  4. prediction_market_agent_tooling/deploy/agent.py +85 -125
  5. prediction_market_agent_tooling/deploy/agent_example.py +20 -10
  6. prediction_market_agent_tooling/deploy/betting_strategy.py +222 -96
  7. prediction_market_agent_tooling/deploy/constants.py +4 -0
  8. prediction_market_agent_tooling/jobs/jobs_models.py +15 -4
  9. prediction_market_agent_tooling/jobs/omen/omen_jobs.py +3 -3
  10. prediction_market_agent_tooling/markets/agent_market.py +145 -50
  11. prediction_market_agent_tooling/markets/blockchain_utils.py +10 -1
  12. prediction_market_agent_tooling/markets/data_models.py +83 -17
  13. prediction_market_agent_tooling/markets/manifold/api.py +18 -7
  14. prediction_market_agent_tooling/markets/manifold/data_models.py +23 -16
  15. prediction_market_agent_tooling/markets/manifold/manifold.py +18 -18
  16. prediction_market_agent_tooling/markets/manifold/utils.py +7 -12
  17. prediction_market_agent_tooling/markets/markets.py +2 -1
  18. prediction_market_agent_tooling/markets/metaculus/metaculus.py +29 -4
  19. prediction_market_agent_tooling/markets/omen/data_models.py +17 -32
  20. prediction_market_agent_tooling/markets/omen/omen.py +65 -108
  21. prediction_market_agent_tooling/markets/omen/omen_contracts.py +2 -5
  22. prediction_market_agent_tooling/markets/omen/omen_resolving.py +13 -13
  23. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +18 -12
  24. prediction_market_agent_tooling/markets/polymarket/data_models.py +7 -3
  25. prediction_market_agent_tooling/markets/polymarket/data_models_web.py +7 -3
  26. prediction_market_agent_tooling/markets/polymarket/polymarket.py +5 -4
  27. prediction_market_agent_tooling/markets/seer/data_models.py +0 -83
  28. prediction_market_agent_tooling/markets/seer/price_manager.py +44 -30
  29. prediction_market_agent_tooling/markets/seer/seer.py +105 -105
  30. prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +34 -41
  31. prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +1 -1
  32. prediction_market_agent_tooling/tools/cow/cow_order.py +10 -3
  33. prediction_market_agent_tooling/tools/is_predictable.py +2 -3
  34. prediction_market_agent_tooling/tools/langfuse_client_utils.py +4 -4
  35. prediction_market_agent_tooling/tools/omen/sell_positions.py +3 -2
  36. prediction_market_agent_tooling/tools/utils.py +26 -13
  37. {prediction_market_agent_tooling-0.64.12.dev660.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/METADATA +2 -2
  38. {prediction_market_agent_tooling-0.64.12.dev660.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/RECORD +41 -51
  39. prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py +0 -68
  40. prediction_market_agent_tooling/monitor/markets/manifold.py +0 -90
  41. prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -43
  42. prediction_market_agent_tooling/monitor/markets/omen.py +0 -88
  43. prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -49
  44. prediction_market_agent_tooling/monitor/monitor.py +0 -406
  45. prediction_market_agent_tooling/monitor/monitor_app.py +0 -149
  46. prediction_market_agent_tooling/monitor/monitor_settings.py +0 -27
  47. prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -146
  48. prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -12
  49. {prediction_market_agent_tooling-0.64.12.dev660.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/LICENSE +0 -0
  50. {prediction_market_agent_tooling-0.64.12.dev660.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/WHEEL +0 -0
  51. {prediction_market_agent_tooling-0.64.12.dev660.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
- )