| import logging |
| from typing import Optional |
| import numpy as np |
|
|
| logger = logging.getLogger(__name__) |
|
|
| class BayesianFairValue: |
| def __init__(self, prior_prob: float = 0.5, prior_confidence: float = 10.0): |
| """ |
| Initialize the Bayesian belief. |
| We model the probability as a Beta distribution Beta(alpha, beta). |
| prior_prob: Initial guess for probability (e.g. 0.5) |
| prior_confidence: Total weight of prior (alpha + beta). |
| """ |
| self.alpha = prior_prob * prior_confidence |
| self.beta = (1.0 - prior_prob) * prior_confidence |
| logger.info(f"Initialized Bayesian Model with Beta({self.alpha:.2f}, {self.beta:.2f})") |
|
|
| def get_fair_value(self) -> float: |
| """Returns the current expected probability (mean of Beta dist).""" |
| return self.alpha / (self.alpha + self.beta) |
| |
| def get_confidence_interval(self, percent: float = 0.95) -> tuple[float, float]: |
| """Returns bounds for the true probability using a simple approximation.""" |
| |
| |
| mean = self.get_fair_value() |
| var = (self.alpha * self.beta) / (((self.alpha + self.beta) ** 2) * (self.alpha + self.beta + 1)) |
| std = np.sqrt(var) |
| |
| |
| z = 1.96 |
| lower = max(0.0, mean - z * std) |
| upper = min(1.0, mean + z * std) |
| return (lower, upper) |
|
|
| def update(self, market_implied_prob: float, trade_volume: float, noise_factor: float = 0.1): |
| """ |
| Bayesian update based on new market observations. |
| market_implied_prob: The price of the trade (0 to 1) |
| trade_volume: Weight of this observation (higher volume = stronger signal) |
| noise_factor: How much we trust the market (0.0 = perfect trust, 1.0 = ignore market) |
| |
| This translates an observation into pseudo-counts for the Beta distribution. |
| """ |
| if market_implied_prob <= 0 or market_implied_prob >= 1: |
| return |
| |
| |
| effective_weight = trade_volume * (1.0 - noise_factor) |
| |
| |
| observed_alpha = market_implied_prob * effective_weight |
| observed_beta = (1.0 - market_implied_prob) * effective_weight |
| |
| self.alpha += observed_alpha |
| self.beta += observed_beta |
| |
| |
| |
| decay = 0.99 |
| self.alpha *= decay |
| self.beta *= decay |
| |
| logger.debug(f"Bayesian Update: observed={market_implied_prob:.4f}, new_fv={self.get_fair_value():.4f}") |
|
|
| def evaluate_opportunity(self, current_ask: float, current_bid: float) -> Optional[dict]: |
| """ |
| Compare market prices against our Bayesian fair value. |
| If the market asks for much less than our FV, we buy YES. |
| If the market bids much more than our FV, we sell YES. |
| """ |
| fv = self.get_fair_value() |
| lower, upper = self.get_confidence_interval() |
| |
| |
| if current_ask < lower: |
| |
| edge = fv - current_ask |
| return {"action": "BUY_YES", "edge": edge, "fair_value": fv} |
| |
| if current_bid > upper: |
| |
| edge = current_bid - fv |
| return {"action": "SELL_YES", "edge": edge, "fair_value": fv} |
| |
| return None |
|
|