Skip to content

Repository files navigation

prediction-market-backtesting

Node TypeScript Tests License

An event-driven backtesting framework for prediction-market strategies on venues such as Polymarket and Kalshi, written in strict TypeScript for Node.

Prediction-market shares are binary outcomes priced on the open interval (0, 1) that settle to 0 or 1. The framework models that domain directly — curved Polymarket taker fees, Kalshi expected-earnings fees, maker rebates, L2 order books, configurable execution latency, and long-biased position accounting — and ships a small, dependency-light engine that replays market data, matches orders, and produces a performance tearsheet.

Features

  • Event-driven engine — replays a time-ordered market-data stream, maintains a venue order book per instrument, and routes orders through a realistic matching engine (market & limit orders; IOC/FOK/GTC; reduce-only; book-walking taker fills; resting maker fills; bar/trade fallback fills).
  • Venue fee models — Polymarket curved taker fee with category-aware maker rebates, and Kalshi proportional expected-earnings fee with fee-waiver support. All money math is decimal-precise.
  • Built-in strategies — EMA crossover, mean reversion, breakout, RSI reversion, threshold momentum, and VWAP reversion, each with bar-driven and order-book-driven variants.
  • Pluggable data sources — seeded synthetic generators, CSV loaders (bars, quotes, trades), and in-memory streams, with a helper to merge multiple feeds.
  • Analysis & reporting — equity curve, returns, drawdown, annualized Sharpe/Sortino, Brier score & advantage, and trade statistics, rendered as a console tearsheet or written to JSON.
  • CLI runner — discover and run backtests from the command line.
  • Strict typing & toolingstrict TypeScript, ESLint (flat config), Prettier, and Vitest, all green in CI.

Requirements

  • Node.js >= 20

Installation

npm install

Quick start

# List the bundled example backtests
npm run cli list                       # or: npx tsx src/cli.ts list

# Run one by name or index
npm run cli run synthetic_bar_mean_reversion
npm run cli run 2

# Run every example
npm run cli run -- --all

Example output:

─────────────────────────────────────────
  synthetic_bar_mean_reversion
─────────────────────────────────────────
  Starting cash                   100.00
  Final equity                    100.82
  Total return                    0.82 %
  Realized PnL                      0.99
  Total commissions              0.17000
  Max drawdown                   -1.20 %
  Sharpe (ann.)                     5.20
  Fills                                2
  Events processed                  1500
─────────────────────────────────────────

Scripts

Script Description
npm run build Compile src/ to dist/ (ESM + type declarations)
npm run typecheck Type-check src/, test/, and backtests/
npm test Run the Vitest suite
npm run test:watch Vitest in watch mode
npm run lint / lint:fix Lint with ESLint
npm run format / format:check Format with Prettier
npm run cli Backtest runner CLI (pmbt)

Architecture

src/
  core/         Domain model — enums, identifiers, decimal/money, the
                BinaryOption instrument, market data (Bar/Trade/Quote/Book),
                the L2 OrderBook, and order events
  engine/       Backtest engine — Order/OrderFactory, Portfolio + Account,
                MatchingEngine, FeeModel interface, LatencyModel, TestClock,
                and the BacktestEngine conductor
  strategy/     Strategy base class and LongOnlyPredictionMarketStrategy
                (order plumbing, liquidity/balance-capped sizing, fee-adjusted
                risk exits)
  strategies/   Built-in strategies (bar + book variants)
  adapters/     Venue specifics — polymarket/ and kalshi/ fee models, and
                prediction-market order tags
  data/         Data sources — synthetic generators, CSV loaders, stream merge,
                and the platform/vendor/data-type taxonomy
  analysis/     Tearsheet metrics and report formatting
  backtesting/  High-level experiment layer — the Experiment manifest,
                buildReplayExperiment / runExperiment, and execution &
                market-data configuration
  cli.ts        Backtest runner CLI
  index.ts      Public API barrel
backtests/      Runnable example experiments (each exports `meta` and `run`)
test/           Vitest suites

Execution flow

  1. A BacktestEngine is configured with an instrument, a fee model, a latency model, and one or more strategies.
  2. A single time-ordered stream of market data is replayed. For each event the engine updates the venue order book and mark price, then dispatches it to the strategies subscribed to that data type.
  3. A strategy submits orders through its OrderFactory; the MatchingEngine fills marketable orders against resting book liquidity (or the latest reference price when only bar/trade data is available), prices each fill through the fee model, and updates the Portfolio.
  4. The engine records an equity curve and the full fill log, which the analysis layer condenses into a tearsheet.

Writing a strategy

Extend LongOnlyPredictionMarketStrategy, which provides the entry/exit plumbing: liquidity- and balance-capped sizing, reduce-only exits with a cooldown, and fee-adjusted take-profit / stop-loss handling.

import {
  LongOnlyPredictionMarketStrategy,
  BookType,
  type StrategyConfig,
  type OrderBook,
} from 'prediction-market-backtesting';

interface MyConfig extends StrategyConfig {
  threshold: number;
}

class MyStrategy extends LongOnlyPredictionMarketStrategy<MyConfig> {
  protected subscribe(): void {
    this.subscribeOrderBookDeltas(this.config.instrumentId, BookType.L2_MBP);
  }

  override onOrderBook(book: OrderBook): void {
    const ask = book.bestAskPrice();
    if (ask !== null && ask < this.config.threshold && !this.inPosition()) {
      this.submitEntry(ask, book.bestAskSize());
    }
  }
}

Defining an experiment

import {
  BinaryOption,
  PUSD,
  buildReplayExperiment,
  runExperiment,
  PolymarketFeeModel,
} from 'prediction-market-backtesting';

const experiment = buildReplayExperiment({
  name: 'my_experiment',
  description: 'demo',
  currency: PUSD,
  initialCash: 100,
  instrument: new BinaryOption({ instrumentId: 'MKT-YES.POLYMARKET', takerFee: 0.007 }),
  feeModel: new PolymarketFeeModel(),
  strategies: [
    (inst) => new MyStrategy({ instrumentId: inst.instrumentId, tradeSize: 5, threshold: 0.4 }),
  ],
  data: {
    platform: 'POLYMARKET',
    dataType: 'BOOK',
    vendor: 'CSV',
    resolveStream: () => loadQuotesAsBook('data/market.csv', { instrumentId: ... }),
  },
});

const { tearsheet } = runExperiment(experiment);

Complete, runnable examples live in backtests/syntheticBookEmaCrossover.ts and backtests/syntheticBarMeanReversion.ts.

Data sources

Three first-class stream sources are provided:

  • Synthetic — seeded random-walk book and bar generators (src/data/synthetic.ts), so the framework runs end-to-end with no external data.
  • CSV — bars, top-of-book quotes (converted to book deltas), and trades (src/data/csv.ts). Timestamps may be ISO-8601 or numeric epoch values (ns / ms / s, auto-detected).
  • In-memory — pass any time-sorted MarketData[] to an experiment's resolveStream.

Combine multiple feeds into one ordered stream with mergeStreams(...).

Fee models

  • Polymarket — curved taker fee fee = qty · rate · p · (1 − p), which peaks at p = 0.5 and decays toward the price extremes, rounded to 5 decimal places. Eligible passive (limit) fills earn a maker rebate, modeled as a negative commission and inferred from the market category.
  • Kalshi — a proportional fee on expected earnings, fee = ceil_to_cent(rate · qty · p · (1 − p)), with support for fee-waived markets.

Roadmap

Planned additions that build on the current engine:

  • Additional strategies (microprice imbalance, binary pair arbitrage, late-favorite hold).
  • Parameter optimization (grid / Bayesian search) over experiment configs.
  • Chart export for the equity, drawdown, and allocation series.
  • Live exchange data adapters for Polymarket and Kalshi.

Disclaimer

This is research and educational software for backtesting trading strategies on historical or synthetic data. It is not financial advice and comes with no warranty. Backtested results do not predict live performance.

License

MIT — see LICENSE.

About

polymarket pnl tracker. Polymarket dashboard with Home (combined P&L), Accounts (per-wallet), and Activity (wallet & group collections, active/closed positions).

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages