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.

ParameterDefaultDescription
initial_cash10,000Starting capital
exec_horizon"swing"intraday | swing | longterm
asset_type"equities"equities | crypto
fee_bps1.0Broker fee (basis points)
half_spread_bps2.0Bid-ask half-spread
base_slippage_bps1.0Fixed slippage component
impact_coeff1.0Market impact multiplier
max_leverage1.0Maximum leverage allowed
allow_marginfalseAllow margin/short positions
rebalance_mode"on_signal_change"on_signal_change | every_bar | calendar
max_participationNoneMax fraction of bar volume per trade (overrides horizon default)
rebalance_every_n_bars0Bar interval for calendar rebalance mode
buy_threshold0.10Exposure threshold to trigger buy
sell_threshold-0.10Exposure threshold to trigger sell
min_trade_units0.0Minimum trade size in units
max_order_age_bars0Max bars a pending order stays alive (0 = unlimited)
risk_free_annual0.0Risk-free rate for Sharpe/Sortino calculation
periods_per_year252Trading 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,
)
FunctionOutput
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).

MethodEndpointDescription
GET/healthHealth check
POST/featuresCompute features from OHLCV
POST/backtestRun backtest simulation
POST/plotsGenerate 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

IDsCategoryExamples
1–9Returns / Momentumret_1d, logret_1d, ret_5d, ret_20d, ret_60d, ret_252d, acceleration, percentile
10–29Trend / Moving Averagessma_10/20/50/200, ema_12/26, slopes, distances, spreads
30–35MACDmacd, signal, histogram, slopes, z-score
36–37RSIrsi_14, rsi_pct_252
38–44Volatility / ATRvol_20, vol_60, atr_14, vol-of-vol, percentiles
45–51Bollinger Bandsmid/upper/lower, bandwidth, %B, distances
52–54Mean Reversionzscore_20, deviation from mean
55–56Stochastic%K and %D (14-period)
57–62Volume / Rangeadv_20, vol_shock, vol_z, range ratio
63–67Riskdrawdown, skew, kurtosis
68–69Beta / Correlationbeta_252, corr_252
70–71Regimetrend_regime, vol_regime

Full list in code: from cobweb_py import show_features; show_features()

27 Plots

IDsCategoryPlots
1–15PerformanceEquity (linear/log), vs benchmark, daily returns, rolling Sharpe/Sortino/vol/beta/alpha, drawdowns, VaR/CVaR, tail risk
16–19TradesReturn distribution, win/loss overlay, expectancy, duration vs return
20–22RegimePerformance by vol regime, trend regime, bull vs bear
23DiagnosticsFeature correlation heatmap
24–27Volume / ExecutionVolume, volume shock, participation, transaction costs

Execution Model

Every trade passes through a realistic execution simulator:

Three presets adjust all parameters automatically:

HorizonMax Participation (eq)Impact CoeffUse Case
intraday2%1.25Sub-daily bars, tight constraints
swing8%1.0Daily bars, typical strategies
longterm15%0.8Weekly rebalance, portfolio allocation