Quickstart
Get a full backtest running in under 30 lines. No API key required.
# pip install cobweb-py yfinance plotly import yfinance as yf from cobweb_py import CobwebSim, BacktestConfig, fix_timestamps, print_signal from cobweb_py.plots import save_equity_plot, save_metrics_table # 1. Grab SPY data df = yf.download("SPY", start="2020-01-01", end="2024-12-31") df.columns = df.columns.get_level_values(0) df = df.reset_index().rename(columns={"Date": "timestamp"}) rows = df[["timestamp","Open","High","Low","Close","Volume"]].to_dict("records") data = fix_timestamps(rows) # 2. Connect (free, no key needed) sim = CobwebSim("https://web-production-83f3e.up.railway.app") # 3. Simple momentum: long when price > 50-day SMA close = df["Close"].values sma50 = df["Close"].rolling(50).mean().values signals = [1.0 if c > s else 0.0 for c, s in zip(close, sma50)] signals[:50] = [0.0] * 50 # 4. Backtest with realistic friction bt = sim.backtest(data, signals=signals, config=BacktestConfig(exec_horizon="swing", initial_cash=100_000)) # 5. Results print(f"Return: {bt['metrics']['total_return']:.2%}") print(f"Sharpe: {bt['metrics']['sharpe_ann']:.2f}") print_signal(bt) save_equity_plot(bt, out_html="equity.html") save_metrics_table(bt, out_html="metrics.html")
Installation
# Base SDK (API calls only) pip install cobweb-py # With visualization (adds pandas + plotly) pip install cobweb-py[viz]
Requires Python 3.9+.
CobwebSim Client
The main class for interacting with the API.
sim = CobwebSim( base_url="https://web-production-83f3e.up.railway.app", api_key="YOUR_KEY", # optional if auth is disabled timeout=60, )
sim.health()
Check API status. Returns {"status": "ok", "version": "0.1.0"}.
sim.enrich(data, feature_ids)
Compute technical features from OHLCV data. Returns rows with feature columns appended. Alias: sim.features().
feats = sim.enrich("stock.csv", feature_ids=[1, 11, 36]) rows = feats["rows"] # OHLCV + ret_1d, sma_20, rsi_14 cols = feats["feature_columns"] # ['ret_1d', 'sma_20', 'rsi_14']
Accepts: CSV path, pandas DataFrame, list[dict], or {"rows": [...]}.
sim.backtest(data, signals=, config=)
Run a full backtest with execution modeling.
bt = sim.backtest( data, signals=[0.0]*50 + [1.0]*200, # or "long" / "short" / "flat" config=BacktestConfig(exec_horizon="swing"), compute_features=True, feature_ids=[70, 71], benchmark=benchmark_data, # optional ) bt["metrics"] # Sharpe, Sortino, drawdown, VaR, ... bt["trades"] # fill prices, costs, TCA breakdown bt["equity_curve"] # per-bar equity, cash, position bt["current_signal"] # "buy" / "sell" / "hold"
sim.plots(data, backtest_result, plot_ids=)
Generate chart payloads from backtest results.
pl = sim.plots(data, bt, plot_ids=[1, 5, 10]) pl["payloads"] # dict of plot data keyed by name
BacktestConfig
All fields are optional with sensible defaults.
| Parameter | Default | Description |
|---|---|---|
initial_cash | 10,000 | Starting capital |
exec_horizon | "swing" | intraday | swing | longterm |
asset_type | "equities" | equities | crypto |
fee_bps | 1.0 | Broker fee (basis points) |
half_spread_bps | 2.0 | Bid-ask half-spread |
base_slippage_bps | 1.0 | Fixed slippage component |
impact_coeff | 1.0 | Market impact multiplier |
max_leverage | 1.0 | Maximum leverage allowed |
allow_margin | false | Allow margin/short positions |
rebalance_mode | "on_signal_change" | on_signal_change | every_bar | calendar |
max_participation | None | Max fraction of bar volume per trade (overrides horizon default) |
rebalance_every_n_bars | 0 | Bar interval for calendar rebalance mode |
buy_threshold | 0.10 | Exposure threshold to trigger buy |
sell_threshold | -0.10 | Exposure threshold to trigger sell |
min_trade_units | 0.0 | Minimum trade size in units |
max_order_age_bars | 0 | Max bars a pending order stays alive (0 = unlimited) |
risk_free_annual | 0.0 | Risk-free rate for Sharpe/Sortino calculation |
periods_per_year | 252 | Trading days per year for annualization |
Scoring
Build composite scores from enriched feature data.
from cobweb_py import score_by_id, score, auto_score, auto_score_by_id # Weighted score by feature IDs (most common) scores = score_by_id(enriched_rows, {36: 0.3, 11: 0.3, 1: 0.4}) # Weighted score by column names scores = score(enriched_rows, {"rsi_14": 0.3, "sma_signal": 0.3, "ret_1d": 0.4}) # Default formula (RSI + SMA signal + returns) scores = auto_score(enriched_rows) # by column names scores = auto_score_by_id(enriched_rows) # by feature IDs
MA features (IDs 10-15) are auto-converted to (Close - MA) / MA signal form. RSI is scaled to 0-1. Other features are z-scored.
Feature / Plot Discovery
from cobweb_py import show_features, show_plots, show_categories from cobweb_py import list_features, list_plots show_features() # print all 71 features show_features("rsi") # filter by category (fuzzy match) show_plots("trades") # filter plots by category show_categories() # list all valid category names # Programmatic access (returns list of dicts): feats = list_features("vol") # [{"id": 38, "name": "vol_20", "category": "volatility"}, ...] plots = list_plots() # all 27 plots
Signals
Convert scores into discrete trading positions.
from cobweb_py import to_signals signals = to_signals(scores, entry_th=0.20, exit_th=0.05, use_shorts=False) # Returns: list of 0.0 (flat) and 1.0 (long) # With use_shorts=True: -1.0 (short), 0.0, 1.0
Pipeline
Enrich once, iterate many times with different weights and parameters.
from cobweb_py import Pipeline pipe = Pipeline("https://web-production-83f3e.up.railway.app", "stock.csv", benchmark="spy.csv", # optional benchmark data feature_ids=list(range(1, 72)), # default: all 71 ) result = pipe.run(weights={36: 0.3, 11: 0.3, 1: 0.4}) # Re-run with different params (no re-enrichment): result2 = pipe.run(weights={36: 0.5, 1: 0.5}, entry_th=0.30) # Access Plotly figures: for name, fig in result["figures"].items(): fig.show() # Result keys: metrics, signal, trades, scores, signals, equity_curve, backtest, figures
Plots (SDK)
HTML-generating convenience functions. Import from cobweb_py.plots:
from cobweb_py.plots import ( save_equity_plot, save_metrics_table, save_trades_table, save_score_plot, save_price_and_score_plot, save_features_table, save_api_payloads_to_html, payloads_to_figures, )
| Function | Output |
|---|---|
save_equity_plot(bt) | Equity curve + position units |
save_metrics_table(bt) | Styled metrics summary |
save_trades_table(bt) | Trade log with TCA breakdown |
save_score_plot(rows, scores) | Score time series |
save_price_and_score_plot(rows, scores) | Price + score overlay |
save_features_table(rows) | OHLCV + features (HTML + CSV) |
save_api_payloads_to_html(payloads) | Any API plot → HTML file |
payloads_to_figures(payloads) | API plots → Plotly figures |
payload_to_figure and payloads_to_figures are also available as top-level imports: from cobweb_py import payloads_to_figures.
Utilities
from cobweb_py import ( fix_timestamps, load_csv, align, to_signals, print_signal, get_signal, save_table, get_plot, save_all_plots, ) # Normalize timestamps for the API data = fix_timestamps(rows, dayfirst=True) # Load CSV with auto-detection data = load_csv("stock.csv") # Align two datasets by timestamp asset, bench = align(asset_data, benchmark_data) # Get current signal from backtest print_signal(bt) # Signal: BUY | exposure=+0.87 | strength=0.72 s = get_signal(bt) # {"signal": "buy", "exposure": 0.87, "strength": 0.72} # Save a lightweight HTML table save_table(rows, "out.html", title="My Table") # Fetch a single plot (auto-handles benchmark/regime/correlation deps) pl = get_plot(sim, data, bt, 5) # by ID pl = get_plot(sim, data, bt, "sharpe") # by fuzzy name # Fetch and save all plots as HTML save_all_plots(sim, data, bt, range(1, 28), out_dir="out/plots")
API Overview
Base URL: https://web-production-83f3e.up.railway.app
All endpoints accept JSON. No API key required (free tier).
| Method | Endpoint | Description |
|---|---|---|
GET | /health | Health check |
POST | /features | Compute features from OHLCV |
POST | /backtest | Run backtest simulation |
POST | /plots | Generate chart payloads |
Full interactive docs: Swagger UI
POST /features
Compute technical features from OHLCV candle data (minimum 20 rows).
# curl example
curl -X POST https://web-production-83f3e.up.railway.app/features \
-H "Content-Type: application/json" \
-d '{
"data": {"rows": [{"open":150,"high":155,"low":149,"close":153,"volume":1000000}, ...]},
"feature_ids": [1, 11, 36]
}'
POST /backtest
Run a backtest with signals and execution modeling. Signal count must match row count.
curl -X POST https://web-production-83f3e.up.railway.app/backtest \
-H "Content-Type: application/json" \
-d '{
"data": {"rows": [...]},
"signals": [0, 0, 1, 1, 1, 0, -1, ...],
"config": {"exec_horizon": "swing", "initial_cash": 100000}
}'
POST /plots
Generate chart payloads from OHLCV data and a backtest result.
curl -X POST https://web-production-83f3e.up.railway.app/plots \
-H "Content-Type: application/json" \
-d '{
"data": {"rows": [...]},
"backtest_result": { ...bt response... },
"plot_ids": [1, 5, 10]
}'
71 Features
| IDs | Category | Examples |
|---|---|---|
| 1–9 | Returns / Momentum | ret_1d, logret_1d, ret_5d, ret_20d, ret_60d, ret_252d, acceleration, percentile |
| 10–29 | Trend / Moving Averages | sma_10/20/50/200, ema_12/26, slopes, distances, spreads |
| 30–35 | MACD | macd, signal, histogram, slopes, z-score |
| 36–37 | RSI | rsi_14, rsi_pct_252 |
| 38–44 | Volatility / ATR | vol_20, vol_60, atr_14, vol-of-vol, percentiles |
| 45–51 | Bollinger Bands | mid/upper/lower, bandwidth, %B, distances |
| 52–54 | Mean Reversion | zscore_20, deviation from mean |
| 55–56 | Stochastic | %K and %D (14-period) |
| 57–62 | Volume / Range | adv_20, vol_shock, vol_z, range ratio |
| 63–67 | Risk | drawdown, skew, kurtosis |
| 68–69 | Beta / Correlation | beta_252, corr_252 |
| 70–71 | Regime | trend_regime, vol_regime |
Full list in code: from cobweb_py import show_features; show_features()
27 Plots
| IDs | Category | Plots |
|---|---|---|
| 1–15 | Performance | Equity (linear/log), vs benchmark, daily returns, rolling Sharpe/Sortino/vol/beta/alpha, drawdowns, VaR/CVaR, tail risk |
| 16–19 | Trades | Return distribution, win/loss overlay, expectancy, duration vs return |
| 20–22 | Regime | Performance by vol regime, trend regime, bull vs bear |
| 23 | Diagnostics | Feature correlation heatmap |
| 24–27 | Volume / Execution | Volume, volume shock, participation, transaction costs |
Execution Model
Every trade passes through a realistic execution simulator:
- Half-spread — bid-ask crossing cost (taker)
- Base slippage — fixed bps component
- Market impact —
impact_coeff × vol_bps × √participation - Broker fee — flat bps on notional
- Participation cap — max % of bar volume per fill
- Multi-bar slicing — unfilled quantity carries forward
Three presets adjust all parameters automatically:
| Horizon | Max Participation (eq) | Impact Coeff | Use Case |
|---|---|---|---|
intraday | 2% | 1.25 | Sub-daily bars, tight constraints |
swing | 8% | 1.0 | Daily bars, typical strategies |
longterm | 15% | 0.8 | Weekly rebalance, portfolio allocation |