The fitness of each bot is determined by a single primary metric: Return on Investment (ROI) percentage.
genetic/evolution.py
def evaluate_fitness(bot: GeneticBot) -> float: """ Fitness = ROI (%). Bots with fewer than MIN_SETTLED_TRADES get a large penalty. """ acct = bot.account if acct.n_settled < MIN_SETTLED_TRADES: return INACTIVE_FITNESS_PENALTY return acct.roi_pct
Buys when ask price falls within genome-specified bounds.
genetic/bot.py
def _signal_price_level(self, snap: MarketSnapshot) -> tuple[str, float] | None: """Buy when ask is in a specific price range.""" p = self.params lo, hi = p["price_threshold_low"], p["price_threshold_high"] if lo <= snap.yes_ask <= hi: return ("yes", snap.yes_ask) if lo <= snap.no_ask <= hi: return ("no", snap.no_ask) return None
Use case: Capture value at specific price points
momentum - Follow price direction
Buys based on recent price trend over lookback window.
genetic/bot.py
def _signal_momentum(self, snap: MarketSnapshot) -> tuple[str, float] | None: """Buy based on price direction over lookback window.""" p = self.params history = self.feed.get_history(snap.ticker) lookback = p["momentum_lookback_ticks"] if len(history) < lookback + 1: return None old_price = history[-(lookback + 1)][1] cur_price = history[-1][1] if old_price == 0: return None pct_change = (cur_price - old_price) / old_price trigger = p["momentum_trigger_pct"] if pct_change > trigger: return ("yes", abs(pct_change)) elif pct_change < -trigger: return ("no", abs(pct_change)) return None
Use case: Ride trending markets
mean_reversion - Bet on return to average
Buys when price deviates significantly from historical mean.
genetic/bot.py
def _signal_mean_reversion(self, snap: MarketSnapshot) -> tuple[str, float] | None: """Buy when price deviates from rolling mean by z-score threshold.""" p = self.params history = self.feed.get_history(snap.ticker) if len(history) < 10: return None prices = [h[1] for h in history] mean = statistics.mean(prices) stdev = statistics.stdev(prices) if len(prices) > 1 else 0.001 if stdev < 0.001: return None z = (prices[-1] - mean) / stdev threshold = p["mean_rev_zscore"] if z > threshold: # Price way above mean -> expect drop return ("no", abs(z)) elif z < -threshold: # Price way below mean -> expect rise return ("yes", abs(z)) return None
Use case: Fade overreactions
value - Buy cheapest side vs fair value
Assumes fair value is 50¢, buys whichever side offers better value.
genetic/bot.py
def _signal_value(self, snap: MarketSnapshot) -> tuple[str, float] | None: """Buy whichever side is cheapest (edge vs 50/50 fair value).""" p = self.params edge_min = p["value_edge_min"] yes_edge = 0.50 - snap.yes_ask # Positive if yes is cheap no_edge = 0.50 - snap.no_ask # Positive if no is cheap if yes_edge > edge_min: return ("yes", yes_edge) if no_edge > edge_min: return ("no", no_edge) return None
Use case: Exploit mispricing vs 50/50 baseline
contrarian - Bet against confident crowd
Fades the market when one side becomes very expensive (confident).
genetic/bot.py
def _signal_contrarian(self, snap: MarketSnapshot) -> tuple[str, float] | None: """Bet against the crowd when market is very confident.""" p = self.params threshold = p["contrarian_threshold"] if snap.yes_ask > threshold: return ("no", snap.yes_ask - threshold) if snap.no_ask > threshold: return ("yes", snap.no_ask - threshold) return None
def _apply_settlements(self): """Apply any cached settlement data to open positions.""" for bot_id, acct in self.accounts.items(): to_close: list[str] = [] for ticker, pos in acct.open_positions.items(): result = self.feed.get_settlement(ticker) if result is None: continue won = result == pos.side pos.settled = True pos.result = result pos.payout = float(pos.contracts) if won else 0.0 pos.profit = pos.payout - pos.cost pos.settle_time = datetime.now(timezone.utc) acct.cash += pos.payout acct.daily_pnl += pos.profit acct.closed_positions.append(pos) to_close.append(ticker) for ticker in to_close: del acct.open_positions[ticker]
Settlement Timeline:
Trading period (24h): Continuous settlement checks every 30s
Targeted checks (every 5 min): Query API for held positions past close time
Settlement wait (up to 4h): Focused polling for remaining positions
Force close: Any unsettled positions counted as total losses
Unsettled positions are penalized heavily. Bots that hold positions in markets that don’t settle quickly will have lower fitness.