Bayes’ theorem — P(H|E) = P(E|H) × P(H) / P(E) — is how an agent updates its probability estimate when new information arrives. Start with the market price as your prior, define likelihood functions for each evidence type (polls, news, expert signals), and compute the posterior. If your posterior diverges from the market, you have edge.
Why This Matters for Agents
An autonomous betting agent doesn’t form a probability estimate once and hold it forever. Markets move because information changes — polls drop, injuries get reported, earnings come in, weather shifts. The agent that updates fastest and most accurately captures the edge before the market adjusts.
This is Layer 4 — Intelligence. Bayesian updating is the mathematical engine inside the agent’s belief system. The agent pulls current market prices from the Polymarket CLOB or Kalshi API, treats those prices as informative priors (because markets aggregate crowd information efficiently), then conditions on new evidence to produce a posterior probability. That posterior feeds directly into the expected value calculation — if posterior > market price for a YES contract, the bet is +EV. The Agent Betting Stack places this squarely in the intelligence layer: data ingestion feeds the Bayesian updater, which feeds the decision engine, which sends orders to Layer 3 (Trading).
Polyseer implements exactly this architecture — multi-agent Bayesian aggregation across independent model outputs to produce consensus posterior probabilities.
The Math
Bayes’ Theorem — The Core Formula
Bayes’ theorem relates the probability of a hypothesis H given evidence E to the probability of the evidence given the hypothesis:
P(H|E) = P(E|H) × P(H) / P(E)
Where:
- P(H) = prior probability — your belief before seeing the evidence
- P(E|H) = likelihood — probability of observing this evidence if the hypothesis is true
- P(E) = marginal likelihood (evidence) — total probability of observing this evidence under all hypotheses
- P(H|E) = posterior probability — your updated belief after seeing the evidence
For a binary outcome (event happens or doesn’t), P(E) expands via the law of total probability:
P(E) = P(E|H) × P(H) + P(E|¬H) × P(¬H)
This means you need four inputs: your prior P(H), the likelihood if the event is true P(E|H), the likelihood if the event is false P(E|¬H), and the complement prior P(¬H) = 1 - P(H).
Choosing a Prior
The prior encodes what you know before seeing the new evidence. Three strategies:
1. Market price as informative prior. The current prediction market price already aggregates information from all market participants. Using it as your prior is the efficient market baseline — you’re saying “the market is roughly right, and I’m looking for incremental edge from new information.” This is the default for most agent architectures.
2. Model-derived prior. If the agent has its own forecasting model (regression, Elo, ensemble), that model’s output becomes the prior. The agent then uses Bayesian updating to adjust this model output as new data arrives. This is common in sports betting where an agent runs a pre-game model and updates as lineups are confirmed.
3. Uninformative prior. Beta(1, 1) — the uniform distribution over [0, 1]. Use this only for genuinely novel events where no historical analog exists and no market price is available. In practice, this is rare for prediction markets since a market price almost always exists.
The Beta-Binomial Conjugate Model
For binary prediction markets, the Beta distribution is the natural prior because it’s conjugate to the Binomial likelihood. This means the posterior is also a Beta distribution, making updates a simple parameter addition.
A Beta(α, β) distribution has:
- Mean = α / (α + β)
- Variance = αβ / ((α + β)² × (α + β + 1))
- Strength = α + β (higher = more concentrated = stronger prior conviction)
If you observe s successes (evidence supporting the event) and f failures (evidence against) from Binomial-distributed data, the posterior is:
Prior: Beta(α, β)
Data: s successes, f failures
Posterior: Beta(α + s, β + f)
No integrals. No numerical optimization. Just addition. This is why agents use Beta-Binomial for real-time updating — it runs in microseconds.
Encoding a market price as a Beta prior. If the market price is 0.60 and you want a prior with strength N:
α = N × market_price = N × 0.60
β = N × (1 - market_price) = N × 0.40
Strength N controls how resistant your prior is to new evidence. N = 10 means a single data point shifts the posterior noticeably. N = 100 means the prior dominates until you accumulate substantial evidence. For a liquid prediction market, N = 20-50 is a reasonable range — the market is informative but not infallible.
Likelihood Functions for Common Evidence Types
The likelihood function P(E|H) maps each evidence type to a probability of observing that evidence conditional on the hypothesis.
Polling data (Binomial likelihood). A poll of n respondents with k supporting the event. If the event has true probability p, the probability of observing k supporters in n respondents follows a Binomial distribution:
P(k | n, p) = C(n,k) × p^k × (1-p)^(n-k)
With a Beta prior, the posterior after observing k/n in a poll is simply Beta(α + k, β + n - k). But you need to apply a credibility discount — polls are noisy, biased, and have known systematic errors. A practical approach: scale the poll data by a credibility factor c ∈ (0, 1):
Effective update: Beta(α + c×k, β + c×(n-k))
A credibility factor of c = 0.3 means the poll contributes as if it were 30% the size of its actual sample. This accounts for non-response bias, likely voter screens, and historical polling error.
Expert forecasts (point estimate likelihood). When an expert or model produces a point probability estimate p_expert, treat it as a Beta-distributed observation with adjustable precision:
Effective update: treat p_expert as Beta(c × p_expert, c × (1 - p_expert))
Where c is the expert’s track record weight. A well-calibrated forecaster (Brier score < 0.15) might get c = 15-30. A talking head on TV gets c = 2-5.
News sentiment (categorical likelihood). Binary classification: positive for H or negative for H, with a calibrated impact magnitude. An agent might classify an event as “strong positive” (likelihood ratio = 3:1), “weak positive” (1.5:1), “neutral” (1:1), “weak negative” (1:1.5), or “strong negative” (1:3).
The update via likelihood ratio (odds form of Bayes’ rule):
Posterior odds = Prior odds × Likelihood ratio
Where:
Prior odds = P(H) / P(¬H)
Likelihood ratio = P(E|H) / P(E|¬H)
This odds form is computationally convenient when the evidence comes as a signal rather than as count data.
Sequential Multi-Source Fusion
The power of Bayesian updating is that it’s sequential. Each evidence source updates the current belief, and the order doesn’t matter (for conditionally independent evidence):
Start: P(H) = prior (market price)
After poll: P(H|poll) ∝ P(poll|H) × P(H)
After expert: P(H|poll, expert) ∝ P(expert|H) × P(H|poll)
After news: P(H|poll, expert, news) ∝ P(news|H) × P(H|poll, expert)
Each posterior becomes the next prior. The final posterior incorporates all evidence sources. This is the multi-source Bayesian fusion architecture — and it’s exactly what Polyseer does across its multi-agent system, where each agent specializes in a different evidence type and their outputs are aggregated via Bayesian combination.
Worked Examples
Example 1: Political Market — New Poll Drops
Setup: An agent holds a YES position on “Will the incumbent win the 2026 midterm Senate race in Pennsylvania?” on Polymarket. Current market price: YES at $0.52. The agent uses the market price as its prior.
New evidence: A major poll (n = 1,200 likely voters) shows the incumbent leading 54% to 46%.
Prior: Beta(α=26, β=24), encoding the $0.52 market price with strength N=50.
Poll update with credibility discount c = 0.3:
Effective poll data:
k_eff = 0.3 × (0.54 × 1200) = 0.3 × 648 = 194.4
n_eff = 0.3 × 1200 = 360
failures_eff = 360 - 194.4 = 165.6
Posterior: Beta(26 + 194.4, 24 + 165.6) = Beta(220.4, 189.6)
Posterior mean: 220.4 / (220.4 + 189.6) = 220.4 / 410 = 0.5376
The agent’s posterior moves from 0.52 to 0.538. The market is still at $0.52. The difference (0.538 - 0.52 = 0.018) is small — this single poll moved the needle but didn’t create a screaming edge. The agent might wait for additional confirming evidence before sizing up.
Example 2: Sports Betting — Starting Pitcher Change
Setup: Agent has a pre-game model for Yankees vs. Red Sox with the Yankees winning at 58%. The line at BetOnline is Yankees -135 (implied 57.4%). No edge yet.
New evidence: 90 minutes before first pitch, the Yankees announce their ace is scratched due to back tightness. The replacement starter has a 5.20 ERA vs. the ace’s 2.85 ERA.
Likelihood ratio approach: Based on historical data, replacing an ace (ERA < 3.00) with a replacement-level starter (ERA > 5.00) shifts win probability by approximately -8 to -12 percentage points. The agent’s calibrated estimate: likelihood ratio of 0.45 (strong negative for Yankees win hypothesis).
Prior odds: P(H) / P(¬H) = 0.58 / 0.42 = 1.381
Likelihood ratio: 0.45
Posterior odds: 1.381 × 0.45 = 0.621
Posterior prob: 0.621 / (1 + 0.621) = 0.383
The agent’s posterior drops from 58% to 38.3%. The sportsbook line hasn’t moved yet (still Yankees -135, implying 57.4%). The agent has massive edge on the Red Sox side. It should bet Red Sox +135 immediately and size according to Kelly criterion — the edge will evaporate once sharp books adjust the line.
Example 3: Multi-Source Fusion on Kalshi
Setup: Kalshi market “Will US GDP growth exceed 3% in Q2 2026?” trading at YES = $0.35.
Evidence source 1 — Economic model: Agent’s regression model estimates 41% probability based on leading indicators (ISM, employment, consumer confidence). Credibility weight c₁ = 20.
Evidence source 2 — Fed minutes sentiment: NLP pipeline classifies latest Fed minutes as “moderately hawkish” — likelihood ratio 0.8 for GDP > 3% (hawkish = slower growth).
Evidence source 3 — Expert consensus: Survey of 15 economists, 5 say above 3%, 10 say below. Binomial update with c₃ = 0.5 (experts are somewhat informative).
Prior: Beta(7, 13) — encoding $0.35 with N=20
Update 1 (model): treat as Beta observation
Effective: Beta(7 + 20×0.41, 13 + 20×0.59) = Beta(15.2, 24.8)
Intermediate mean: 15.2 / 40.0 = 0.380
Update 2 (Fed minutes): likelihood ratio
Current odds: 0.380 / 0.620 = 0.613
Posterior odds: 0.613 × 0.8 = 0.490
Posterior prob: 0.490 / 1.490 = 0.329
Update 3 (expert survey): Beta-Binomial
Encode 0.329 as Beta(α, β) with strength ~40
α ≈ 40 × 0.329 = 13.16, β ≈ 40 × 0.671 = 26.84
Effective expert data: c×k = 0.5×5 = 2.5, c×(n-k) = 0.5×10 = 5.0
Posterior: Beta(13.16 + 2.5, 26.84 + 5.0) = Beta(15.66, 31.84)
Final posterior mean: 15.66 / 47.50 = 0.330
Final posterior: 33.0%. Market price: $0.35. The agent’s model says the market is slightly overpricing YES. The edge (2 percentage points) is small — probably not enough to overcome transaction costs on Kalshi. The agent logs the signal and waits for stronger confirming evidence.
Implementation
"""
Bayesian updating module for autonomous prediction market agents.
Supports Beta-Binomial conjugate model with multi-source fusion.
Requirements: numpy, scipy
pip install numpy scipy
"""
import numpy as np
from scipy import stats
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class EvidenceUpdate:
"""Record of a single Bayesian update."""
source: str
evidence_type: str # "poll", "expert", "news", "model"
prior_mean: float
posterior_mean: float
alpha_delta: float
beta_delta: float
credibility: float
raw_data: dict = field(default_factory=dict)
@dataclass
class BayesianState:
"""Current state of the Bayesian belief model."""
alpha: float
beta: float
updates: list[EvidenceUpdate] = field(default_factory=list)
@property
def mean(self) -> float:
return self.alpha / (self.alpha + self.beta)
@property
def variance(self) -> float:
a, b = self.alpha, self.beta
return (a * b) / ((a + b) ** 2 * (a + b + 1))
@property
def std(self) -> float:
return np.sqrt(self.variance)
@property
def strength(self) -> float:
return self.alpha + self.beta
def credible_interval(self, ci: float = 0.90) -> tuple[float, float]:
"""Return the symmetric credible interval."""
tail = (1 - ci) / 2
low = stats.beta.ppf(tail, self.alpha, self.beta)
high = stats.beta.ppf(1 - tail, self.alpha, self.beta)
return (low, high)
class BayesianUpdater:
"""
Bayesian belief updater for binary prediction markets.
Uses Beta-Binomial conjugate model for fast, exact updates.
"""
def __init__(
self,
market_price: float,
prior_strength: float = 30.0
):
"""
Initialize with a market price as the informative prior.
Args:
market_price: Current market price (0 to 1), used as prior mean.
prior_strength: Concentration parameter (higher = stronger prior).
20-50 for liquid markets, 5-15 for illiquid.
"""
if not 0 < market_price < 1:
raise ValueError(f"market_price must be in (0, 1), got {market_price}")
if prior_strength <= 0:
raise ValueError(f"prior_strength must be > 0, got {prior_strength}")
alpha = prior_strength * market_price
beta = prior_strength * (1 - market_price)
self.state = BayesianState(alpha=alpha, beta=beta)
def update_poll(
self,
support_pct: float,
sample_size: int,
credibility: float = 0.3
) -> BayesianState:
"""
Update beliefs with polling data.
Args:
support_pct: Fraction of respondents supporting the event (0 to 1).
sample_size: Number of respondents in the poll.
credibility: Discount factor for poll reliability (0 to 1).
0.3 = poll counts as 30% of its sample size.
"""
prior_mean = self.state.mean
k_eff = credibility * support_pct * sample_size
f_eff = credibility * (1 - support_pct) * sample_size
self.state.alpha += k_eff
self.state.beta += f_eff
self.state.updates.append(EvidenceUpdate(
source="poll",
evidence_type="poll",
prior_mean=prior_mean,
posterior_mean=self.state.mean,
alpha_delta=k_eff,
beta_delta=f_eff,
credibility=credibility,
raw_data={"support_pct": support_pct, "sample_size": sample_size}
))
return self.state
def update_expert(
self,
expert_prob: float,
credibility: float = 10.0
) -> BayesianState:
"""
Update beliefs with an expert or model point estimate.
Args:
expert_prob: Expert's probability estimate (0 to 1).
credibility: Weight given to this expert (higher = more influence).
Well-calibrated forecaster: 15-30. Pundit: 2-5.
"""
prior_mean = self.state.mean
alpha_add = credibility * expert_prob
beta_add = credibility * (1 - expert_prob)
self.state.alpha += alpha_add
self.state.beta += beta_add
self.state.updates.append(EvidenceUpdate(
source="expert",
evidence_type="expert",
prior_mean=prior_mean,
posterior_mean=self.state.mean,
alpha_delta=alpha_add,
beta_delta=beta_add,
credibility=credibility,
raw_data={"expert_prob": expert_prob}
))
return self.state
def update_likelihood_ratio(
self,
likelihood_ratio: float,
source: str = "news"
) -> BayesianState:
"""
Update beliefs using a likelihood ratio (odds form of Bayes).
Args:
likelihood_ratio: P(E|H) / P(E|not H).
> 1 = evidence supports H.
< 1 = evidence against H.
1.0 = neutral.
source: Label for this evidence source.
"""
prior_mean = self.state.mean
prior_odds = self.state.mean / (1 - self.state.mean)
posterior_odds = prior_odds * likelihood_ratio
posterior_prob = posterior_odds / (1 + posterior_odds)
# Re-encode as Beta with same strength
strength = self.state.strength
new_alpha = strength * posterior_prob
new_beta = strength * (1 - posterior_prob)
alpha_delta = new_alpha - self.state.alpha
beta_delta = new_beta - self.state.beta
self.state.alpha = new_alpha
self.state.beta = new_beta
self.state.updates.append(EvidenceUpdate(
source=source,
evidence_type="likelihood_ratio",
prior_mean=prior_mean,
posterior_mean=self.state.mean,
alpha_delta=alpha_delta,
beta_delta=beta_delta,
credibility=likelihood_ratio,
raw_data={"likelihood_ratio": likelihood_ratio}
))
return self.state
def edge_vs_market(self, market_price: float) -> dict:
"""
Calculate edge between posterior belief and current market price.
Args:
market_price: Current YES price on the market (0 to 1).
Returns:
Dict with edge metrics for agent decision logic.
"""
posterior = self.state.mean
edge = posterior - market_price
ci_low, ci_high = self.state.credible_interval(0.90)
# EV per dollar on YES contract
ev_yes = posterior * (1.0 - market_price) - (1 - posterior) * market_price
# EV per dollar on NO contract
ev_no = (1 - posterior) * market_price - posterior * (1.0 - market_price)
return {
"posterior": posterior,
"market_price": market_price,
"edge": edge,
"edge_pct": edge * 100,
"ev_per_dollar_yes": ev_yes,
"ev_per_dollar_no": ev_no,
"recommended_side": "YES" if ev_yes > 0 else "NO" if ev_no > 0 else "PASS",
"ci_90": (ci_low, ci_high),
"ci_excludes_market": market_price < ci_low or market_price > ci_high,
"posterior_std": self.state.std,
"n_updates": len(self.state.updates)
}
def summary(self) -> str:
"""Human-readable summary of current belief state."""
ci_low, ci_high = self.state.credible_interval(0.90)
lines = [
f"Posterior: {self.state.mean:.4f} ({self.state.mean*100:.1f}%)",
f"90% CI: [{ci_low:.4f}, {ci_high:.4f}]",
f"Strength: {self.state.strength:.1f}",
f"Updates: {len(self.state.updates)}",
]
for u in self.state.updates:
lines.append(
f" [{u.source}] {u.prior_mean:.4f} -> {u.posterior_mean:.4f} "
f"(Δ = {u.posterior_mean - u.prior_mean:+.4f})"
)
return "\n".join(lines)
# --- Demo: full multi-source fusion pipeline ---
if __name__ == "__main__":
# Scenario: Polymarket "Will incumbent win PA Senate race?" at $0.52
agent = BayesianUpdater(market_price=0.52, prior_strength=50)
print(f"Initial prior: {agent.state.mean:.4f}")
print(f"Prior strength: {agent.state.strength:.0f}\n")
# Evidence 1: Major poll shows incumbent at 54% (n=1200)
agent.update_poll(support_pct=0.54, sample_size=1200, credibility=0.3)
print(f"After poll (54%, n=1200, c=0.3): {agent.state.mean:.4f}")
# Evidence 2: FiveThirtyEight model gives incumbent 56%
agent.update_expert(expert_prob=0.56, credibility=15)
print(f"After expert (56%, c=15): {agent.state.mean:.4f}")
# Evidence 3: Negative news — scandal report (weak negative, LR=0.85)
agent.update_likelihood_ratio(likelihood_ratio=0.85, source="scandal_report")
print(f"After scandal news (LR=0.85): {agent.state.mean:.4f}")
# Check edge vs current market
print(f"\n{'='*50}")
print(agent.summary())
print(f"\n{'='*50}")
result = agent.edge_vs_market(market_price=0.52)
print(f"\nEdge analysis vs $0.52 market:")
print(f" Posterior: {result['posterior']:.4f}")
print(f" Edge: {result['edge_pct']:+.2f}%")
print(f" EV/$ (YES): {result['ev_per_dollar_yes']:+.4f}")
print(f" EV/$ (NO): {result['ev_per_dollar_no']:+.4f}")
print(f" Recommendation: {result['recommended_side']}")
print(f" 90% CI: [{result['ci_90'][0]:.4f}, {result['ci_90'][1]:.4f}]")
print(f" CI excludes market: {result['ci_excludes_market']}")
Limitations and Edge Cases
1. Prior sensitivity in low-data regimes. When evidence is scarce (one poll, one expert opinion), the posterior is dominated by the prior. An agent using market price with N=50 as its prior will barely move on a single data point. This is by design — one poll isn’t enough to overcome the aggregated wisdom of the market. But it means the agent’s edge detection is conservative in low-information environments.
2. Likelihood misspecification. The entire framework depends on correct likelihood functions. If your poll credibility discount is wrong (you use c=0.3 but the polls are systematically biased by 6 points in a specific direction), your posterior will be biased. Calibrate likelihood parameters on historical data — backtest your Bayesian updater against past prediction markets with known outcomes.
3. Conditional independence assumption. Multi-source fusion assumes evidence sources are conditionally independent given H. In practice, polls influence expert forecasts, expert forecasts influence market prices, and market prices influence news coverage. Treating correlated sources as independent leads to double-counting evidence and overconfident posteriors. Mitigations: reduce credibility weights for correlated sources, or use a hierarchical model that explicitly models dependencies.
4. Non-conjugate likelihoods. The Beta-Binomial model handles binary evidence cleanly. But some evidence types (continuous data, structured multi-dimensional features) don’t fit conjugate models. For these, you need numerical methods — MCMC sampling or variational inference via pymc or numpyro. These are slower and more complex to deploy in a real-time trading agent.
5. Model vs. market: who’s wrong? When your posterior diverges from the market price, you’re implicitly claiming you have information the market hasn’t priced in, or that you’re processing the same information more accurately. Before trading on Bayesian edge, verify that your information source truly isn’t reflected in the price. If a poll has been public for 4 hours and the market hasn’t moved, the market may have already priced it in through other participants — your update is stale.
6. Computational precision at extremes. Beta distributions near 0 or 1 (very high α or β relative to the other) can cause floating-point issues. Use log-space computations when working with posteriors above 0.99 or below 0.01.
FAQ
How do prediction market agents use Bayes’ theorem?
Prediction market agents use Bayes’ theorem to update their probability estimates when new evidence arrives. The agent starts with a prior (often the current market price), defines a likelihood function for each evidence type (polls, news, expert forecasts), and computes a posterior probability. If the posterior diverges from the market price, the agent has identified potential edge. The posterior feeds directly into the expected value calculation to determine whether and how much to trade.
What is the best prior to use for a prediction market model?
The most common approach is using the current market price as an informative prior, since prediction markets aggregate information efficiently. For a binary event, encode this as a Beta distribution — a market price of 0.60 can be represented as Beta(60, 40) with strength 100, or Beta(6, 4) with strength 10 if you want the prior to be weaker. Uninformative priors (Beta(1,1) = uniform) are appropriate only for genuinely novel events with no historical analog.
How do you combine multiple evidence sources in Bayesian updating?
Apply Bayes’ rule sequentially — each evidence source updates the prior into a posterior, which becomes the prior for the next update. For independent evidence sources, the order doesn’t matter. The posterior after incorporating polls, expert forecasts, and news sentiment is P(H|E1,E2,E3) proportional to P(E3|H) times P(E2|H) times P(E1|H) times P(H). This is the multi-source fusion approach used by tools like Polyseer.
What is a conjugate prior and why does it matter for betting agents?
A conjugate prior produces a posterior in the same distribution family as the prior, making updates computationally trivial. For binary prediction markets, the Beta distribution is conjugate to the Binomial likelihood — updating just adds observed successes and failures to the shape parameters. This lets an agent update beliefs in microseconds without numerical integration, critical for real-time trading on the Polymarket CLOB.
How does Bayesian updating connect to expected value in betting?
Bayesian updating produces the agent’s posterior probability estimate. This estimate feeds directly into the EV formula: EV = p_posterior times payoff minus cost. If the posterior probability exceeds the market-implied probability (the price), the bet has positive expected value. See the Expected Value guide for the complete EV framework that consumes Bayesian posteriors as inputs. For position sizing on +EV bets, the Kelly Criterion takes the posterior as its probability input.
What’s Next
Bayesian updating gives you the posterior. The next question: how do you score whether your posteriors are good?
- Next in the cross-linking map: Prediction Market Scoring Rules — logarithmic scoring, Brier scores, and how to evaluate whether your Bayesian model is well-calibrated.
- Size your bets: The Kelly Criterion takes your Bayesian posterior as input and tells you how much of your bankroll to wager.
- Where this sits in the stack: The Agent Betting Stack shows how the Bayesian updater connects to data ingestion (Layer 1), wallet management (Layer 2), and order execution (Layer 3).
- See it deployed: Polyseer uses multi-agent Bayesian aggregation as its core intelligence architecture.
- Betting bots in production: The Betting Bots hub covers autonomous systems that implement these Bayesian pipelines end to end.
