Alpha on Prediction Markets: Momentum, Value, Risk. The Complete Python Guide.

Algo trading means letting code make decisions instead of gut feeling. Instead of "I think this contract will go up" you write a rule: "if the price has been rising for 14 days - buy." The computer checks this rule across thousands of contracts in seconds and doesn't get emotional.
Three key advantages:
Speed - a script scans hundreds of contracts per second. Doing it manually takes hours.
Discipline - code doesn't panic, doesn't get greedy, doesn't "wait a bit longer." Signal fires - we act.
Repeatability - you can test a strategy on historical data (backtest) before risking real money.
The process: collect data -> form a hypothesis -> test on history -> deploy. This article walks through every step.
Tools: Python and four libraries. The same ones quants at hedge funds use - just applied to a different domain.
Prediction markets
A prediction market is a platform where people buy and sell contracts on event outcomes. A contract is a yes/no question.
Example: "Will Bitcoin be above $100K by end of 2026?"
Contract at $0.77 -> the market thinks there's a 77% chance.
Buy at $0.42, event happens - you get $1, profit $0.58. Doesn't happen - you lose $0.42.
Platforms: Polymarket.
Left - how the "BTC > $100K" contract price moved over 90 days. Right - 5 contracts at once.
Why does algo trading work here? Prediction markets are young. Fewer participants, more inefficiencies. If you can estimate probabilities better than the crowd and automate it - you have an edge.
Setup
pip install pandas numpy matplotlib scipy
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import statspandas - data tables, numpy - number crunching, matplotlib - charts, scipy - statistics.
Data and returns
Data = contract price history, price = probability. From prices we calculate returns: how much a contract gained or lost per day.
# Real Polymarket markets (March 2026), simulated price history.
# For live data: curl "https://gamma-api.polymarket.com/markets?limit=20"
np.random.seed(42)
def simulate_contract(start_price, drift, vol, days=90):
changes = np.random.normal(drift, vol, days)
path = start_price + np.cumsum(changes)
return np.clip(path, 0.02, 0.98)
contracts = pd.DataFrame({
'date': pd.date_range('2025-12-30', periods=90, freq='D'),
'BTC>100K_2026': simulate_contract(0.60, 0.002, 0.015),
'Fed_cut_Jun26': simulate_contract(0.55, 0.002, 0.012),
'US_recession_26': simulate_contract(0.22, 0.001, 0.018),
'Trump_2028': simulate_contract(0.30, 0.0005, 0.014),
'Iran_ceasefire': simulate_contract(0.40, -0.002, 0.022),
})
contracts.set_index('date', inplace=True)
# Daily returns
returns = contracts.pct_change().dropna()
# Log returns (more accurate for large swings)
log_returns = np.log(contracts / contracts.shift(1)).dropna()Why log returns? If a contract drops from $0.50 to $0.25 (-50%), then rises back to $0.50 (+100%), the simple average says "+25%". But you're at zero. Log returns account for this.
How daily returns are distributed across contracts.
Momentum: which contracts are gaining probability
Idea: if a contract has been rising - it will likely keep rising. Participants update their estimates gradually, not instantly. This creates trends.
We calculate momentum across multiple timeframes (7, 14, 30 days). Rising on all three - quality trend. Only one - might be a one-off spike.
Which contracts gained over 7 and 30 days.
Value: where the market is wrong
You have your own probability estimate. The market prices it lower - the contract is undervalued. The difference is called edge.
def value_screener(df, my_estimates):
"""
Compares market price to your estimate.
edge > 0 = contract is cheaper than you think.
"""
results = pd.DataFrame()
results['market'] = df.iloc[-1]
results['my_est'] = my_estimates
results['edge'] = results['my_est'] - results['market']
return results.sort_values('edge', ascending=False)
my_estimates = pd.Series({
'BTC>100K_2026': 0.82,
'Fed_cut_Jun26': 0.75,
'US_recession_26': 0.35,
'Trump_2028': 0.30,
'Iran_ceasefire': 0.40,
})
print(value_screener(contracts, my_estimates))Edge of 0.18 means: market says 64%, I think 82%. If I'm right - I pocket the difference.
Risk and return
High returns are meaningless if you lose half your money along the way. Always measure risk.
def risk_metrics(ret, annual_factor=365):
m = {}
m['ann_return'] = ret.mean() * annual_factor
m['ann_vol'] = ret.std() * np.sqrt(annual_factor)
m['sharpe'] = m['ann_return'] / m['ann_vol'] if m['ann_vol'] != 0 else 0
cum = (1 + ret).cumprod()
m['max_dd'] = ((cum - cum.cummax()) / cum.cummax()).min()
return m
for col in returns.columns:
m = risk_metrics(returns[col])
print(f"{col}: return={m['ann_return']:.0%} vol={m['ann_vol']:.0%} sharpe={m['sharpe']:.2f} maxDD={m['max_dd']:.0%}")Sharpe ratio - return divided by volatility. > 1 means the return is worth the risk. > 2 is excellent.
Max drawdown - largest peak-to-trough drop. -40% means at the worst point you were down 40%.
Each contract is a dot. Horizontal = risk, vertical = return.
Correlation and portfolio
Don't bet everything on one contract. If two contracts move independently (low correlation), one dropping doesn't drag the other.
correlation = returns.corr()
print(correlation)< 0.3 — barely related, good for a portfolio. > 0.7 — move together, no diversification.
Heatmap: greener = lower correlation.
Backtest
A backtest tests your strategy on past data. Without it you're guessing. With it you're testing a hypothesis.
Logic: contract rose over 14 days - buy. Fell - sit in cash.
class MomentumBacktester:
def __init__(self, data, lookback=14):
self.data = data
self.lookback = lookback
def run(self):
df = self.data.copy()
df['mom'] = df.pct_change(self.lookback)
df['pos'] = np.where(df['mom'] > 0, 1, 0)
df['ret'] = df.iloc[:, 0].pct_change()
df['strat'] = df['ret'] * df['pos'].shift(1)
df['cum_hold'] = (1 + df['ret']).cumprod()
df['cum_strat'] = (1 + df['strat']).cumprod()
self.results = df.dropna()
return self
def summary(self):
r = self.results
print(f"Momentum: $1 -> ${r['cum_strat'].iloc[-1]:.4f}")
print(f"Hold: $1 -> ${r['cum_hold'].iloc[-1]:.4f}")
bt = MomentumBacktester(contracts[['BTC>100K_2026']])
bt.run().summary()Momentum strategy vs just holding the contract
Important: backtests don't guarantee future results. This is educational material, not financial advice.
What's next
Start - connect Polymarket API, download data, build charts.
Mid - your own momentum + value screener, backtest.
Advanced - automate: script checks contracts and sends alerts.
Quant - ML for probability estimation, NLP on news.
Run it, plug in real data, and you have a working screener.
trading here: polymarket.com/?r=zostaff
my tg channel: t.me/zostaffsmartarc






