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.