
Backtesting Frameworks
Prototype and stress-test trading strategies with event-driven backtests before risking real capital.
Overview
backtesting-frameworks is an agent skill for the Validate phase that teaches event-driven Python backtesting patterns for orders, fills, and positions.
Install
npx skills add https://github.com/wshobson/agents --skill backtesting-frameworksWhat is this skill?
- Event-driven backtester skeleton with Order, Fill, and Position dataclasses
- OrderSide, OrderType, and market/limit/stop order modeling patterns
- Position update logic for buys, sells, average cost, and realized PnL
- Pandas/NumPy-oriented implementation patterns for bars and signals
- Decimal-based money handling to avoid float rounding bugs
- Event-driven pattern with Order, Fill, Position, and OrderSide/OrderType enums
- Decimal-based quantity and price fields for financial precision
Adoption & trust: 11.2k installs on skills.sh; 36.5k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You have trading rules but no trustworthy simulation that updates positions, costs, and PnL the way a real engine would.
Who is it for?
Indie quant builders prototyping Python strategies who want explicit order/fill/position code instead of opaque platform backtests.
Skip if: Builders who only need portfolio reporting, live execution, or regulated production risk systems without writing simulation code.
When should I use this skill?
You need to implement or extend a Python event-driven backtester for strategy validation on historical data.
What do I get? / Deliverables
You get working backtester scaffolding your agent can extend with your data feed, fees, and signals before you ship or trade live.
- Event-driven backtester module with order and position accounting
- Extensible hooks for commission, slippage, and signal-driven orders
Recommended Skills
Journey fit
Backtesting sits in Validate because solo builders use it to prove a strategy’s historical behavior before committing to full implementation or live trading. Prototype is the canonical shelf: you simulate fills, positions, and PnL on historical data as a proof step, not production deployment.
How it compares
Use as procedural backtest templates in your repo, not as a hosted charting or broker UI substitute.
Common Questions / FAQ
Who is backtesting-frameworks for?
Solo builders and small teams validating trading or signal strategies in Python with Claude Code, Cursor, Codex, or similar agents.
When should I use backtesting-frameworks?
During Validate when you need a prototype backtest; also early Build if you are standing up the simulation core before integrations.
Is backtesting-frameworks safe to install?
Review the Security Audits panel on this Prism page and inspect the skill source in the wshobson/agents repo before granting shell or filesystem access to your agent.
SKILL.md
READMESKILL.md - Backtesting Frameworks
# backtesting-frameworks — detailed worked examples ## Implementation Patterns ### Pattern 1: Event-Driven Backtester ```python from abc import ABC, abstractmethod from dataclasses import dataclass, field from datetime import datetime from decimal import Decimal from enum import Enum from typing import Dict, List, Optional import pandas as pd import numpy as np class OrderSide(Enum): BUY = "buy" SELL = "sell" class OrderType(Enum): MARKET = "market" LIMIT = "limit" STOP = "stop" @dataclass class Order: symbol: str side: OrderSide quantity: Decimal order_type: OrderType limit_price: Optional[Decimal] = None stop_price: Optional[Decimal] = None timestamp: Optional[datetime] = None @dataclass class Fill: order: Order fill_price: Decimal fill_quantity: Decimal commission: Decimal slippage: Decimal timestamp: datetime @dataclass class Position: symbol: str quantity: Decimal = Decimal("0") avg_cost: Decimal = Decimal("0") realized_pnl: Decimal = Decimal("0") def update(self, fill: Fill) -> None: if fill.order.side == OrderSide.BUY: new_quantity = self.quantity + fill.fill_quantity if new_quantity != 0: self.avg_cost = ( (self.quantity * self.avg_cost + fill.fill_quantity * fill.fill_price) / new_quantity ) self.quantity = new_quantity else: self.realized_pnl += fill.fill_quantity * (fill.fill_price - self.avg_cost) self.quantity -= fill.fill_quantity @dataclass class Portfolio: cash: Decimal positions: Dict[str, Position] = field(default_factory=dict) def get_position(self, symbol: str) -> Position: if symbol not in self.positions: self.positions[symbol] = Position(symbol=symbol) return self.positions[symbol] def process_fill(self, fill: Fill) -> None: position = self.get_position(fill.order.symbol) position.update(fill) if fill.order.side == OrderSide.BUY: self.cash -= fill.fill_price * fill.fill_quantity + fill.commission else: self.cash += fill.fill_price * fill.fill_quantity - fill.commission def get_equity(self, prices: Dict[str, Decimal]) -> Decimal: equity = self.cash for symbol, position in self.positions.items(): if position.quantity != 0 and symbol in prices: equity += position.quantity * prices[symbol] return equity class Strategy(ABC): @abstractmethod def on_bar(self, timestamp: datetime, data: pd.DataFrame) -> List[Order]: pass @abstractmethod def on_fill(self, fill: Fill) -> None: pass class ExecutionModel(ABC): @abstractmethod def execute(self, order: Order, bar: pd.Series) -> Optional[Fill]: pass class SimpleExecutionModel(ExecutionModel): def __init__(self, slippage_bps: float = 10, commission_per_share: float = 0.01): self.slippage_bps = slippage_bps self.commission_per_share = commission_per_share def execute(self, order: Order, bar: pd.Series) -> Optional[Fill]: if order.order_type == OrderType.MARKET: base_price = Decimal(str(bar["open"])) # Apply slippage slippage_mult = 1 + (self.slippage_bps / 10000) if order.side == OrderSide.BUY: fill_price = base_price * Decimal(str(slippage_mult)) else: fill_price = base_price / Decimal(str(slippage_mult)) commission = order.quantity * Decimal(str(self.commission_per_share)) slippage = abs(fill_price - base_price) * order.quantity return Fill( order=order, fill_price=fill_price, fill_quantity=order.quantity, commission=commission, slippage=slippage, timestamp=bar.