Alpha research on Polymarket: signal backtesting
The Marketlens SDK has two backtest engines for two different questions. The execution engine asks "would my orders have filled" and replays every book update. The alpha engine asks "is this signal profitable" and replays one bar per market at a fixed cadence, which makes it fast enough to test a signal across thousands of markets and months of data. This guide covers the alpha engine.
One bar per market
Set resolution= to the bar cadence and the engine calls on_bar once per market per bar. price="mid" builds bars from order book metrics at 1m to 1d; price="close" builds them from trade candles at 1s to 1d:
bar.t, bar.mid, bar.spread # price="mid": order book metrics
bar.bid_depth, bar.ask_depth
bar.open, bar.high, bar.low, bar.close, bar.volume # price="close"fill="next" executes your targets at the next bar, which rules out lookahead; fill="close" executes on the same bar.
Targets, not orders
An AlphaStrategy declares desired exposure instead of placing orders. ctx.target_weight(0.10) holds YES worth 10% of equity, negative values hold the NO side, zero is flat, and ctx.target_position(n) targets a share count instead. Targets persist until changed, so re-asserting the same target trades nothing. The engine trades the difference at the fill bar's price, applying slippage_bps and fees.
from marketlens import MarketLens
from marketlens.backtest import AlphaStrategy
class MomentumTilt(AlphaStrategy):
def on_market_start(self, ctx, market, bar):
self._prev = None
def on_bar(self, ctx, market, bar):
if self._prev is not None:
move = bar.mid - self._prev
ctx.target_weight(max(-1.0, min(1.0, 50 * move)))
self._prev = bar.mid
client = MarketLens()
result = client.backtest(
strategy=MomentumTilt(),
id="nyc-daily-weather",
initial_cash=10_000,
resolution="1m",
price="mid",
fill="next",
slippage_bps=5,
after="2026-07-07T18:00:00Z",
before="2026-07-08T02:00:00Z",
)
print(result.summary())Pass a list of series or market ids to test the signal across a whole universe in one run, and data_dir= to cache the window locally for fast re-runs.
Reference prices without lookahead
For crypto series the signal often lives in the underlying, not the market. The context serves the underlying's spot price aligned to backtest time, truncated so later prices are never visible:
spot = ctx.reference_price() # underlying spot at the current bar
history = ctx.reference_prices() # [(timestamp_ms, price), ...] so farGraduate winners to the execution engine
The alpha engine assumes your trades happen at the bar price plus slippage. That is the right assumption for finding signals and the wrong one for sizing them. When a signal survives across windows and markets, port it to a tick level Strategy and rerun it through the execution engine with queue position, latency, and real depth. Reading the output of either engine is covered in the results guide; the full parameter reference is in the alpha docs.
FAQ
Common questions
How do I backtest a trading signal on Polymarket?
Subclass AlphaStrategy from the Marketlens SDK, set a per market target inside the on_bar hook, and run it through the backtest call. The engine replays one bar per market at your chosen resolution and trades each market to the target, instead of replaying every book update.
How is the alpha backtest different from the order-level backtest?
The execution engine fills orders against real book depth with queue priority, latency, and slippage. The alpha engine checks each market at a fixed interval and trades it to a target position, applying slippage and fees; it measures whether a signal is profitable rather than how it executes.
What data and resolutions does the alpha backtest use?
It reads order book metrics bars from 1 minute to 1 day, or trade candle bars from 1 second to 1 day. Both stream from the API or download once to a local Parquet cache for fast re-runs.
When should I switch from the alpha engine to the execution engine?
Once a signal survives the alpha backtest across windows and markets. The alpha engine assumes you trade at the bar price plus slippage; the execution engine then checks whether real book depth, latency, and queue position would have let those trades happen.
Try it
Test your first signal today
The free tier includes 5M events per day with full API and full archive access, no card required.
$ pip install marketlens