edgekit

Introduction

edgekit is a systematic-trading research toolkit. It takes a trading idea from raw OHLC bars to a validated, sized, deployable strategy — and it is built around one uncomfortable assumption: most apparent alpha is fake.

Most backtests are wrong before they are interesting. The number is inflated by look-ahead bias, or it is market beta wearing a strategy costume, or it is a pattern mined from noise. edgekit treats a good backtest as the start of skepticism, not the end — the library exists to try to break your own result before the market does.

→Prime directive
Assume every edge is fake until proven otherwise. Your job is to disprove your own strategy. What survives the gauntlet is real.

What it gives you#

  • A causal engine. Gap-aware fills, pessimistic stops, cost charged in R. Look-ahead is a tested property, not a hope.
  • The validation gauntlet. Monte-Carlo permutation, purged walk-forward, PBO, deflated Sharpe, cost-stress, and an is-it-beta regression — the difference between a real edge and a lucky one.
  • Position sizing & portfolio construction. Risk-parity, HRP, vol-targeting, CPPI, drawdown-throttle, sized to a prop-firm drawdown budget.
  • Prop-firm tooling. Monte-Carlo pass-rate and days-to-pass for FTMO, CryptoFundTrader and BrightFunded evaluations.
  • An ML layer. Triple-barrier labels, purged/embargoed walk-forward, meta-labeling, and a cloud-safe tree export that matches scikit-learn within 1e-6 at runtime.
  • Reporting. Self-contained HTML reports and matplotlib charts, base64-inlined, zero external requests.

Thirty seconds of edgekit#

The whole library is designed to read like the pipeline it implements:

pipeline.py
import edgekit as ek

# 1. load + keep the Nasdaq regular cash session (09:30-16:00 New York)
bars = ek.data.load_bars("US100_M1.csv")
rth  = bars[ek.data.rth_mask(bars.index, start="09:30", end="16:00", tz="America/New_York")]

# 2. causal backtest — trades priced in R
trades = ek.strategy.ORB(or_bars=30, target_r=2.0).backtest(rth, warmup=5, bars_per_day=390)
stats  = ek.trade_stats(trades.r, dates=trades.date)   # 2,666 trades | PF 0.71 | EV -0.214R

# 3. PROVE it — the gauntlet's job is to REJECT. This one is net-negative:
p = ek.validation.mcpt(trades.r.sum(), null_stat, n=1000)   # entry timing beats random...
#   ...but PF 0.71 after costs (0.71 / 0.45 / 0.29 at 1x/2x/3x) — dead. Stop here.

# 4. only a survivor gets sized to a 10%-max / 5%-daily drawdown budget
sized = ek.sizing.size_to_dd(daily_r, dd_budget=0.095, account=100_000, daily_cap=0.045)

# 5. ...and shipped: prop-firm pass-rate sim + a self-contained HTML report
rate = ek.challenge.simulate(daily_pnl, ek.challenge.FTMO_1STEP)
ek.report.Report("ORB - rejected").kpi_row(cards).write("orb.html")

Why it exists#

edgekit is extracted from a large quant-research repo where a good core coexisted with ~280 standalone scripts that inlined the same machinery over and over. The same Hawkes indicator was pasted 52 times; the trade-stats builder ~47 times; the permutation test ~60 times. edgekit is that machinery consolidated into one installable, tested package — so a fix or an improvement happens once, with a test guarding it.

Was pastedTimesNow lives in
hawkes()52indicators
trade-stats builder~47metrics
permutation test~60validation
report scaffold + CSS87viz / report
✦A note on honesty
edgekit never hides the haircut. A drawdown-matched backtest is a ceiling, not an expectation — the docs and the tooling consistently push you toward the number you can actually trade, not the one that looks best.