01Start Here
What NED is, in five lines — and how to use this guide.
NED in five lines:
- NED is a fleet of trading bots on Polymarket, a prediction market. Each bot must prove its edge on honest ledgers before any real money.
- Everything is PAPER:
real_capital = NOfleet-wide until a bot's canonical gate is met AND the owner flips the switches. - The repo is the shared brain. Your agent (Claude Code / Antigravity) reads
AGENTS.mdand already knows the project — you supply the owner's operating style. - Your lane as a friend is Togi, the sports bot. One sport = one new file, found automatically, zero shared edits — two friends literally cannot merge-conflict.
- Every edge claim goes through the same loop: measure it first, judge it on the honest lower bound, and let the owner promote. The graveyard of killed ideas is half the moat.
How to use this guide
- Read 02 Prompting first — the mindset, the shared vocabulary, and the phrase-book. That's how you talk to your agent like the owner does.
- 04 The Edge Loop and 05 Case Studies teach how the owner actually works. The method behind every verdict, told through the real studies (real numbers, no invention).
- 06 The Trap List — each trap has a body attached. Read it before testing any idea.
- 07 The Two Seats and 08 Per-Sport Recipe are the operational how-to: how we ship without stepping on each other, and exactly what a sport file is.
- 09 Ground Rules is the constitution recap — everyone inherits it automatically via
AGENTS.md. - 03 Vocabulary is a searchable reference — come back to it whenever a term is unfamiliar.
- 10 Owner's Data Points is the owner's map of what matters in each sport and where to look.
GitHub & the context files — if you've never collaborated on a repo
GitHub is where the project's shared copy lives. It holds the source code and nothing else. No money data ever goes there — the live ledgers, the bankroll, and every key stay on the owner's machines and are excluded from git. Your copy starts with empty ledgers. That's correct, not broken.
Three words cover almost everything you'll do:
- CLONE — download your own full copy of the project to your machine.
- BRANCH — a private working line inside your copy, so your changes touch nothing shared until they're ready.
- PR (pull request) — "here's my change, please review it." The owner reviews and folds it into the main project.
The context files. The repo carries its own memory. These files teach your agent everything about the project:
| File | What it is |
|---|---|
| AGENTS.md | the rulebook every agent reads first — CLAUDE.md and GEMINI.md are the same file |
| BUILD_STATE.md | what's been built + the latest verdicts — read the top checkpoint |
| NED_CONTEXT.md | the architecture — what each bot is and how the pieces fit |
| PROMPTING_GUIDE.md EDGE_PLAYBOOK.md FRIENDS_COLLAB.md | the three guides this page packages — prompting style, edge method, the collab model |
| docs/togi-onboarding.md | the sport how-to — exactly how to add your one sport file |
| docs/SPORT_DATA_POINTS.md | the owner's per-sport data-point map (rendered as section 10 here) |
| docs/FREE_DATA_SOURCES.md | every free data source per sport, verified + point-in-time graded — read before wiring any feed |
The point: you don't have to teach your agent the project — point it at these files and it knows.
First thing you ever tell your agent: "Read AGENTS.md, BUILD_STATE.md (the top checkpoint), and NEXT_SESSION_PLAN.md before you touch anything. Then [your task]." The agent absorbs the fleet, the rules, and where we are — then you prompt it in the style this guide teaches.
02Prompting — Talk Like the Owner
The repo teaches your agent the project. This page teaches you the owner's operating style: the mindset, the phrases, and the shape of a good prompt.
The one-paragraph version
Talk to the agent like a sharp, trusting boss who has zero tolerance for self-deception. Point it at one thing, hand it the wheel, name the guardrails, and demand proof. You don't need to be formal or write clean prose. Messy, conversational, even voice-to-text rambling is fine — as long as every prompt lands three things: the goal, the guardrails (what must never happen), and "verify it / prove it / tell me honestly." That's the whole game.
The mindset — six values in every prompt
- Honesty over hope. Never accept a pretty number. "Make sure it's exactly what would happen on Polymarket." "Small n is honest, not flattered." If tests fail, you want to hear it. A result you can't trust is worse than no result.
- Edge before scale. Prove the edge first. Don't add bots or size to paper over a weak one. One lane at a time — build it out fully, watch it, then move on. Scale = more proven lanes, not a bigger bet.
- Anti-overfit is religion. A locked strategy is never re-tuned ("just one more filter" is banned). You measure a signal before you wire it live, and everything faces an out-of-sample gate — a test on data the idea never saw. A loop that optimizes on its own data just finds "prettier noise faster."
- Delegate, then verify. "You've got the wheel — go." But in the same breath: test it, adversarially review it, don't tell me it's done until it's proven. Trust is high; the bar for "done" is higher.
- Gates, not vibes. Nothing goes real until it clears a gate — a big enough sample
n, a Wilson lower bound above breakeven.real_capital = NOuntil the math says otherwise. Report the claim as the lower bound, not the point estimate. - Speed is an edge. Get information as fast as possible — faster feeds, warmer connections, lower latency — across every bot.
The owner's phrase-book — real phrases, steal them
| Phrase | What it signals | When to use |
|---|---|---|
| "You've got the wheel — go." | full delegation; make the calls, don't ask permission for every step | starting a work block you trust the agent to run |
| "…but never re-tune, keep it paper, zero orders." | the guardrails that ride along with delegation | always attach to a "go" |
| "Prove it out / test it, then tell me." | don't report success until it's verified | after asking for any build or fix |
| "Look at our losses — how much did we miss by, are they clustered, are we accounting for X?" | an honest post-mortem, not a reassurance | when something underperforms |
| "Make sure it's exactly what would happen on Polymarket." | match reality (fees, spread, fills) to the cent | any P&L / edge claim |
| "One lane at a time." | finish and prove one thing before the next | when scope starts sprawling |
| "Measure it before we wire it live." | study first, deploy behind the gate | any new signal idea |
| "Don't relitigate — treat the concept as proven, validate our implementation." | don't re-argue settled strategy; check the code does it right | when the agent second-guesses a proven idea |
| "Verify, don't auto-change it — flag it for me." | surface, don't silently mutate config | anything touching live settings/mappings |
| "Plan and update, then launch." | write the plan + docs first, then build | before a big build |
| "Update everything to a T — compact but full, every detail and where we're going." | persist state so the next session (or a friend) can pick up | end of a work block |
| "Say the word / say when." | a checkpoint before something irreversible or outward-facing | before deploys, sends, spends |
| "Additional stuff you fill in or learn yourself too." | go beyond the literal ask; bring back what you found | research / exploration tasks |
| "Learn off the losses — no full-stop." | don't panic-halt a working system; learn from misses | when a bot has a bad run |
The anatomy of an owner-style prompt
Not every prompt has all five, but the good ones hit Goal → Wheel → Guardrails → Verify → Persist:
- Goal — one thing, concretely. "Add a signal that fades order-flow imbalance on my sport."
- Wheel — hand over the how. "You've got the wheel — figure out the cleanest way."
- Guardrails — what must never happen. "Paper only, zero orders, don't touch the engine files, don't re-tune anything locked."
- Verify — the proof you want. "Run the test suite, keep it green, and show me the honest read — Wilson LB, not the point estimate."
- Persist — capture it. "Then update the docs so the next session and my friends can follow."
Template you can copy:
"Working on [one lane / one question]. You've got the wheel — [do the thing], but [guardrails: paper, zero orders, no re-tune, don't touch engine files]. Prove it — run the tests, and give me the honest read (small n is fine, just say so). Then update the docs to a T."
Worked example — adding a sport to Togi
Weak prompt (what NOT to do):
"make my sport bot win more, tune it until the win rate is good"
That asks the agent to overfit (banned), has no guardrails, and no proof standard. The agent that follows this project's rules will push back on it.
Owner-style prompt (do this):
"New lane: add [my sport] to Togi. You've got the wheel — one new file in the sports package, follow the existing sport as the template so it auto-discovers. Keep it paper, zero orders, don't touch the frozen engine files, and don't re-tune anything that's locked. Wire the staleness edge the same way the other sports do. Run the mm test suite and keep it green, add tests for my sport, and give me the honest readout — what's the edge, and how far is it from the gate. Then commit to a branch and update the docs so I can PR it."
Notice: one file, template-following, guardrails named, tests demanded, gate-honest, persisted. That's the whole style.
The real thing — prompts the owner actually sent, verbatim
Everything above is the theory. These six are the practice: real kickoff prompts from the owner's own sessions, lightly trimmed. Read them for the shape, not the polish — messy words, sharp intent.
a. "Ok you got the wheel…" — total delegation WHEEL
"Ok you got the wheel im gonna sleep do whats needed and rest till the next session, goodnight."
Ran the full overnight build loop solo — built, tested, verified, documented — and left a clean checkpoint for the morning.
Total delegation works here because the guardrails live in the repo (AGENTS.md), not in the prompt. The agent wakes up already fenced in.
Hand the wheel explicitly; the constitution rides along automatically.
b. "audit the losses…" — questions, not answers POST-MORTEM
"audit the losses and see what kinda has changed, where are we losing, by how much, are they near by eachother, is the heatwave making us sell more than usual, are we fully accounting for the heatwave"
Produced the loss audit (case study e): regression-to-mean verdict, heat fully absorbed by calibration, losses concentrated-not-correlated.
It names the QUESTIONS, not the answers — five falsifiable checks, zero suggested conclusions. The agent can't just agree with a hunch; it has to measure.
Interrogate losses with specific questions. Never say "make it win more."
c. "exactly what they would have been on Polymarket" — anti-fantasy P&L REALITY-MATCH
"Make sure wins and losses are exactly what they would have been on Polymarket."
Verified the live fee formula on-chain-exact: 0.05·p·(1−p), taker-only, tick 0.001 (case study f).
It demands reality-matching to the cent — the anti-fantasy-P&L instinct. A ledger that drifts from the venue is a story, not a result.
Regularly ask: "would the real venue have paid this?"
d. "verify the volume numbers… find the trustable source" — source hierarchy VERIFY-OWN-NUMBERS
"Quickly verify the volume numbers that you have per market, per bucket... Find the trustable API or source... separate the different types of volumes and look at them entirely and respectively for what they mean, analyzing wise and trading wise."
Found its own error (bucket vs event volume), then built the source guide: screening vs real depth vs executed tape.
It assumes numbers can be wrong and demands the source hierarchy. "Volume" means three different things across three endpoints — the prompt forces that apart.
Make the agent verify ITS OWN numbers against the primary source.
e. "find out why the volumes down and verify it" — the push-back MOST IMPORTANT
"yes but we won't lose the volume right trade days should come back with higher volume ?? find out why the volumes down and verify it."
Its first two answers were WRONG — a phantom drought from its own filter bug, then "market ghost town" from a bad metric. The owner's push-back forced the real, verified answer: market volume was fine at ~$848k/day — the EDGES left with the heatwave, and depth thinned.
The owner did not accept the first confident answer. Or the second. A good agent, told to "verify it," will audit itself and sometimes overturn its own diagnosis.
When an answer smells off, push back and say "verify it." Persistence beats politeness.
f. "update all the context files… to a t" — persist everything PERSIST
"update all the context files, the roadmaps, where we are, where we go, to a t."
Refreshed every context doc + memory so the NEXT session (or a friend) starts fully loaded.
It treats documentation as part of the work, not an afterthought. The repo stays the shared brain only if every session writes back to it.
End every work block with a persist-everything prompt.
Map them back to the anatomy — Goal → Wheel → Guardrails → Verify → Persist:
- a is pure Wheel — and shows the Guardrails can live in the repo instead of the prompt.
- b is a sharp Goal plus built-in Verify — each question is a falsifiable check.
- c and d are Verify at its strictest: match reality to the cent, and re-derive your own numbers from the primary source.
- e is Verify applied twice — the push-back loop when the first answer fails the smell test.
- f is Persist — the closing move of every work block.
The bans — the owner never asks for these; don't either
- ❌ "Just tune it until it wins." (overfitting — the #1 banned move)
- ❌ Re-tuning a locked strategy's spec (selection, band, lead, sizing, fees, calibration).
- ❌ Relitigating a proven concept instead of checking our implementation of it.
- ❌ Accepting "done" without tests / a verified read.
- ❌ Touching live money, live ledgers, or the frozen engine files (that's the owner-maintainer's lane).
- ❌ Dressing up a small sample as evidence ("it worked 3 times!").
Getting started — point the agent at the context
You don't have to explain the project — it's in the repo. First thing, tell your agent:
"ReadAGENTS.md,BUILD_STATE.md(the top checkpoint), andNEXT_SESSION_PLAN.mdbefore you touch anything. Then [your task]."
03Vocabulary — the Shared Language
The agent knows these terms cold. Using them makes you 3× faster and keeps you inside the guardrails. Plain-English glosses — type to filter.
| Term | What it means |
|---|---|
| the edge | the real, measured reason we make money on a market |
| the lane | one strategy line (weather taker, sports staleness, market-making…) |
| the gate | the bar a lane must clear before it's trusted / goes real (a sample n, a Wilson LB) |
| accrue / accruing | keep collecting data toward the gate; "it's accruing to 300" = not there yet |
| the tape / the lake | the recorded data we replay (trades, order books, fair values) — read-only, never edited by hand |
| the board | the live cockpit that shows each lane's health |
| the readout / the verdict | the honest result of a study ("BB-Z fade = dead; FLOW = promising") |
paper / real_capital = NO | everything is simulated until the owner flips real money on |
| fv / fv_sharp | fair value / the sharp (Pinnacle-derived) fair-value reference |
| basis | the gap between our fair value and the market's price |
| staleness | the market lagging a fresh signal — the window we lift the stale price in |
net edge / MIN_NET_EDGE | edge after fees; the floor a bet must clear (locked at 2¢) |
| Wilson LB | the honest lower bound of a win-rate/edge given the sample; the claim we quote |
| ICIR / CLV | consistency of a signal / closing-line value — "did the market move our way" |
| suggestion-mode | the code recommends a change; the owner applies it — nothing self-mutates |
| the plug-in surface | the only files a collaborator edits (a signal file, params) — never the engine |
| frozen engine files | core files only the maintainer touches, via review (fair/quoter/tape/recorder/…) |
| per-sport package / auto-discovered | add a sport = one new file; the system finds it — zero shared edits |
| Thor / Loki | weather takers (daily high / low) — LOCKED spec, never re-tuned |
| Togi | the sports staleness taker — this is the lane you (friends) build, sport by sport |
| Odin / Hodr / Freyr | the market-maker configs (directional / pure / rewards) |
| Mimir / Muninn / Bifrost | the fair-value engine / the data lake / the London VPS |
04The Edge Loop — the Whole Method on One Page
Every edge in this project — validated, accruing, or dead — went through the same seven steps. No exceptions, no shortcuts, and the order matters.
CONCEPT
Comes from the mentor list. The seven strategies (MM, latency arb, cross-exchange arb, resolution arb, MM + directional view, MM rewards, vulture) are treated as PROVEN — we never relitigate the concept. What we test is our implementation of it: our data, our latency, our fills, our fees, on our venue.
PAPER-IMPLEMENT OUR VERSION
A read-only study or a zero-order paper bot. In mm/ the package is zero-order by construction (no wallet, no key, no order call exists). The study reads the lake (Muninn), never places anything.
TRUE POINT-IN-TIME BACKTEST
Only data knowable at decision time. Three legs, all real: the real INPUT as-of decision time (for Thor: the forecast actually issued ~24h before, via the historical-forecast API — never ERA5 reanalysis), real FILLABILITY (a real ask/depth at our price, not a theoretical mid), real OUTCOME (on-chain outcomePrices, never a proxy).
MEASURE ON THE REAL TAPE
The proprietary lake (books + trade prints, recv-clock NTP-stamped) is the measurement surface. A fill exists only on a real print at/through our level. Fees are the exact live fee (0.05·p·(1−p), taker-entry-only — verified against the live market metadata, see case study f).
PRE-REGISTERED GATE
Declared BEFORE the data comes in: a sample size (n ≥ 300 for the MM studies; the canonical Thor gate is the frozen 3-condition data.gate) and the Wilson-95 lower bound as the claim — never the point estimate. Read once, verdict permanent.
VERDICT
One of three words: VALIDATED (gate met, edge positive at the LB), ACCRUING (honest at any n, keep collecting, no early promotion), DEAD (gate met, edge negative — or a methodology leak found). Verdicts are terminal. A DEAD edge stays dead; we don't re-run it with one more filter until it flatters. Re-opening requires a structural change in the world (e.g. Freyr rewards reopens only if Polymarket changes the 1000-share min-size gate), not a re-tune.
ONLY THE OWNER PROMOTES
Everything up to here is SUGGESTION-mode. The study recommends; the owner flips. Nothing self-wires, nothing auto-raises its own cap, and real_capital = NO until the canonical gate is met AND the owner flips the governor switches.
The single most important property of this loop: an idea is measured before it ever trades. BB-Z fade (case study c) lost money on paper without ever costing a cent, because the study ran first. That is the loop earning its keep.
How to prompt for this — the edge-work phrasings
The phrase-book in 02 Prompting covers the general style. These are the edge-work phrasings specifically — the owner's moves and what each one tells the agent to do:
| The owner says | The agent should do |
|---|---|
| "Audit the losses." | Case study e, in full: distance-to-miss, bias vs calibration, concentration vs correlation. Deliver one of three verdicts — leak / break / variance — and if it's variance, say "do nothing" out loud. Never propose a new filter as the fix. |
| "Make sure wins/losses are exactly what they'd be on Polymarket." | Re-derive P&L from the live mechanics: the real fee formula, the real tick, the real ask/depth at entry, settle vs on-chain outcomePrices. Reconcile to the cent; report any gap as a finding, not a rounding note. |
| "Measure it before we wire it live." | Build a read-only study on our own tape (the flow_study / staleness_study pattern): pre-register the gate and the Wilson-LB claim FIRST, run zero-order, report honestly at any n. Wiring is a separate, later, owner-gated step. |
| "Look into X, make sure we aren't overpaying." | Case study f: pull the live source, reproduce the exact number, name which endpoint is authoritative and why. Bring back the side-findings too (that's how the 0.001 tick was found). |
| "Verify the numbers, find the trustable source." | Rank the candidate sources by what they actually measure, reconcile them against each other (event = Σ buckets), and write the ranking down so the next study doesn't re-litigate it. |
| "Don't relitigate — the concept is proven; validate OUR implementation." | Skip the "does this strategy work in theory" essay. Test our data, our latency, our fills, our venue — and scope the verdict to exactly what was measured (moneyline ≠ totals). |
| "Adversarially review it before you report it." | Attack your own study the way review attacked the maker study: is n independent? is the clock pre-trade? is the reference fresh? does the cohort survive incl-exits? Only then write the verdict. |
The standing frame around all of it: paper only. Zero orders in mm/. No re-tunes to a locked spec. Suggestion-mode everywhere. The plug-in surface is mm/signals/ + mm/params.py + your Togi sport file. Engine files are single-maintainer. Only the owner promotes. The loop finds the edges; the constitution keeps them honest.
05Case Studies — How It Actually Went
Every number below is a real number from this repo's studies. If you're about to test an idea, read these first: the graveyard is half the moat.
a. STALENESS — the validated one VALIDATEDACCRUING to n≥300
The build order named it up front: "staleness study = the decisive $/day fork." The mentor concept (latency/staleness) was taken as proven; the question was whether OUR sharp reference and OUR lake could see it on Polymarket.
mm/staleness_study.py — read-only, on the live fv tape. When the de-vigged sharp reference (Pinnacle) steps, how long does Polymarket's retail quote take to reprice, and what does lifting the stale price at various delays earn net of the honest fee and the depth clamp? A 15 s probe inside a sub-60 s window, per-window Wilson CI, pre-registered n ≥ 300 accrual gate.
The sub-60 s window is VALIDATED: +2.8¢ @ 15 s, decaying to −9¢ @ 60 s, around a ~51 s reprice lag. The first concrete sign the MM directional edge is real. Still ACCRUING to the n ≥ 300 gate — reported honestly at any n; weeks to fill at the free sharp cadence.
The edge has a shape — it isn't "staleness is good," it's "+2.8¢ if you're inside 15 s and negative past a minute." Measuring the decay curve is what made the finding actionable (it's also what justified reversing the earlier "no" on the $30/mo Odds API minute tier — a decision now backed by a validated edge instead of hope).
b. MAKER TILT — the refuted one DEAD (moneyline-scoped)
The make-or-break test for the "directional" half of MM + directional view. The question: when Polymarket sits off a FRESH sharp (a standing basis), does PM converge to us — so Odin's maker tilt earns? Owner-style framing: prove the mechanism on the real tape before investing any more in tuning the tilt.
mm/maker_study.py, read-only on the real fv tape, look-ahead-safe: find basis events, measure gross convergence at fixed horizons, stratify by sharp freshness (ref_age).
The first cut counted 7,414 per-row "events." Adversarial review of our own verdict found that was a ~29× autocorrelation over-count — the same standing basis re-sampled every few seconds, not independent observations. After episode-dedup: ~258 fresh basis episodes across ~36 tokens. The gate went from "met" to ACCRUING in one honest step. A fresh-sharp filter was added so a stale reference couldn't manufacture fake basis.
Gross convergence at h=300 s is NEGATIVE across every fresh stratum (ref_age ≤300 s −1.32¢, ≤600 s −0.76¢, ≤1800 s −0.78¢; hit rate ~41%). A fresher sharp does NOT flip it positive.
The moneyline standing-basis tilt does not earn, even fresh. It explains why Odin ≈ Hodr (the tilt adds ~0). But the scope is MONEYLINE ONLY — the fv tape is ~100% moneyline (the wide lane is 100% sharp_missing), so this says NOTHING about totals/runline; building the de-vigged totals/runline sharp reference is the prerequisite to test that, not refuted by this. The sharp's edge is in its STEPS (the staleness taker) not its standing LEVEL (the maker tilt).
Two, actually: adversarially review your own verdict before you report it (the 29× over-count was found by us, on our own study, before it banked), and n must be ~independent samples — a per-row count over an autocorrelated series is not an n.
c. BB-Z / FLOW — measure-before-wire BB-Z: DEADFLOW: ACCRUING
The owner sent the "loop engineering" reel and the TruthTick terminal: "explore both, how we use for maker/taker; plan and update, then launch." The synthesis framed the terminal as an instrument, not an alpha source — but it surfaced two candidate signals, FLOW (order-flow imbalance) and BB-Z (Bollinger-z mean reversion). Standing rule applied: "measure it before we wire it live."
mm/flow_study.py — new, read-only, zero orders. Both signals measured on OUR OWN lake (tick bars off trades, mid band off books), per-horizon Wilson CI, honest 0.05·p·(1−p) fee, pre-registered n ≥ 300 gate. Wiring either as a live mm/signals/ nudge was DEFERRED behind the gate + an out-of-sample check + owner promotion — declared before the first readout, not after.
- BB-Z fade = DECISIVELY NEGATIVE, gate MET (n = 12,319). Mean net −0.83¢ → −1.02¢ across h=2/5/10, gross hit-rate under 33%, tight Wilson CIs. Fading a book-mid extreme loses because the extreme is mostly a genuine repricing that continues — coherent with the staleness edge (go WITH the step). DO NOT wire.
- FLOW go-with = PROMISING but ACCRUING (n = 33). h=1: +11.1¢ mean net, 58% profitable, 67% hit — but the Wilson LB is only 0.32. A lead, not a verdict. Keep accruing.
This is the loop's whole value in one readout: a signal that sounds right (mean reversion! every terminal ships it!) was measured on our tape and killed before it ever traded. And the FLOW half shows the discipline cuts both ways — a +11.1¢ point estimate with n=33 gets reported as "a lead," because the LB is the claim.
d. NET-EDGE FLOOR A/B — the discarded improvement RESOLVED → DISCARDED
After the first losing days the owner asked to "do sizing 10x better on a scaling level." The sizing workflow's verdict: flat 2% wins as the SIZE. Every up-sizing scheme — Kelly, edge-proportional, cheap-tilt — is negative out-of-sample once the ~$125 1-tick depth clamp bites. The one surviving candidate was raising the net-edge FLOOR from 2% toward ~5%. That one works by not betting, so it needs no fillable size.
Not a re-tune — a shadow A/B: _floor_ab() in ned_api.py (surfaced as data.floor_ab) measured what each floor 3–7% WOULD have done on the real deploy book, with a forward out-of-sample window accruing after a declared cutover date. The live MIN_NET_EDGE stayed at 2.0 the entire time. Pre-registered: re-evaluate after the fresh forward fills arrive, read once, verdict permanent.
118 fresh OOS fills refuted it — every 3–7% floor showed negative lift. RESOLVED → DISCARDED (2026-07-08). Keep MIN_NET_EDGE = 2.0.
This is what the no-re-tune rule looks like when an idea is actually good on the training data (+5–7pp in-sample, positive in 7/7 walk-forward splits): you shadow it, you let fresh out-of-sample data vote, and when the OOS says no, the verdict is permanent. The floor-A/B discipline working IS the result — not a failure, a $0 lesson instead of a live one.
e. LOSS AUDIT — regression vs break VARIANCE — do nothing
The owner's actual phrasing (see the phrase-book): "Look at our losses — how much did we miss by, are they clustered, are we accounting for X?" The prompt asks for a post-mortem, not a reassurance.
Decompose the WR dip on the real ledger: distance-to-bucket on every loss, model bias vs the walk-forward calibration, geographic concentration, and cross-loss correlation (dispersion) — because "concentrated" and "correlated" are different risks.
Whole-book settled WR 38.3% (69/180) — squarely in the 35–39% design range for buckets paying ~3:1 (incl-exits, the gate basis, ≈33–34%). The dip is regression to the 38% mean, not a break: losses are ~1 °C adjacent-bucket coin-flips, broad rather than a new failure mode. HEAT is fully accounted for — the raw model runs +0.34 °C warm but the walk-forward calibration absorbs it (actual − BET bucket = −0.17 °C, effectively unbiased). Losses are concentrated (91% Asia+Europe, 8–20 bets/cycle) but NOT correlated (dispersion ~1.0) → the real risk is mechanical single-cycle variance, and the candidate fix (a region/cycle sub-cap) stays a SUGGESTION-only shadow, not a wired change. sao_paulo + munich = persistent warm WATCH flags — confirmed station MATCHES, genuine variance.
An honest loss audit has three possible outputs: a leak (fix the plumbing), a break (a new failure mode — investigate), or variance (do nothing). Most dips are variance, and "do nothing" is a real answer — the banned move is treating variance as a break and "fixing" it with a new filter.
f. FEE / VOLUME VERIFICATION — never trust a number you haven't reproduced CONFIRMED
Owner-style asks, paraphrased: look into the fees and make sure we aren't overpaying; verify the volume numbers and find the trustable source. Thor's P&L had always assumed a fee model; assumption ≠ verification.
Pull the live market metadata and reproduce the number, don't cite a docs page. Same for volume: reconcile the aggregate against its parts, and rank the sources by what they actually measure.
- Fee CONFIRMED LIVE + EXACT (2026-07-08): Polymarket weather markets carry
feesEnabled=true,feeType=weather_fees, rate 0.05, exponent 1, takerOnly=true, makerRebate 0.25 — exactly Thor's0.05·p·(1−p)taker-entry-only model. Not overpaying; if anything slightly conservative on the entry. Bonus finding you only get by looking: tick size on current weather markets = 0.001, not 0.01. - Volume RECONCILED: event volume = Σ its bucket sub-markets (Seoul Jul-9: $88.6k event ≈ $88.7k summed). And the sources ranked by use: gamma
volume24hrfor screening; the CLOB/bookfor real resting depth (the truth — gammaliquidityis only a proxy); the data-api/tradesexecuted tape for VWAP/backtests.
Every constant in a P&L model is a claim. Verify it against the live source, reproduce it exactly, and write down WHICH source is authoritative for which question — because the same word ("volume," "liquidity") means three different things across three endpoints.
06The Trap List
Each one has a body attached — a real study in this repo that hit it (or dodged it).
Optimizing on your own build data finds "prettier noise faster." Every extra iteration is another chance to overfit. The defense is constitutional: NO RE-TUNES, EVER, on a locked spec.
BODY: the floor A/B (case d) — good in-sample, refuted OOS — and dodged in BB-Z (case c).
Trades arrive ~10 s late vs ~5 s book polls, so a "prevailing" book keyed on receive time is actually post-trade. Distrust ANY recv-time markout study.
BODY: killed vulture v1 outright — its founding +$56–74/day vanished on an honest pre-trade clock.
A per-row count over a slowly-moving series is not a sample size. n = ~independent samples, always.
BODY: the maker study (case b) — 7,414 "events" were ~29× over-counted; ~258 real episodes.
Dropping exits (or any losing cohort) from the denominator.
BODY: the gate counts exits as losses — owner ruling; and the maker book only "looked up" until incl-exits showed −$2,495.
Don't score a prediction against a SIGN-FOLDED realized move — one already credited in the prediction's own direction, like a clv column. That correlation manufactures fake skill.
BODY: the ICIR lens in signal_audit.py deliberately correlates the SIGNED nudge against the UN-folded realized move for exactly this reason (report-only, never a gate).
An edge% means nothing without fillable dollars. The 1-tick depth clamps Thor to ~$125 per market.
BODY: killed every up-sizing scheme in the sizing study (case d): Kelly and friends all die at the clamp.
A "basis" against an old reference is mostly the reference being wrong.
BODY: the maker study (case b) added a fresh-sharp filter for this; and the wide lane is 100% sharp_missing (Pinnacle moneyline de-vig ≠ a totals/runline reference), so a moneyline verdict must never be read as a totals/runline verdict.
A hot day-slice or a fat point estimate on tiny n. The Wilson lower bound is the claim; day-slice WR is noise.
BODY: FLOW (case c) at n=33 shows +11.1¢/67% hit but Wilson LB 0.32 → reported as "a lead."
07The Two Seats
Two friends, two tool setups, one loop. Ownership per AGENTS.md: the owner runs Loki, the live fleet, and the mm/ plug-in surface; the friends own Togi sport sets A and B.
The model, in brief: the repo is the shared brain — ~/ned on the owner's machine, with a private GitHub remote (git@github.com:bavatharannanthan7-lang/ned-v1.1.git — the owner controls access). It is source-only. Live ledgers (*.jsonl), bankroll.json, ned_access.json, caches, and the data lake are all gitignored; they live on the owner's Mac + the Bifrost VPS only. Your clone starts with empty ledgers — that's correct, not broken. You develop against fixtures and dry runs. The owner runs the one authoritative live fleet.
One sport = ONE new file, and you can never conflict. Every sport is a sibling file that calls core.register(...) on import. togi/__init__.py auto-discovers every module. So adding a sport touches zero shared files — two friends owning two sports literally cannot produce a merge conflict. The same pattern exists for data-point ideas: one new file in iterations/mm/signals/. The owner is the maintainer. He reviews and merges PRs, runs everything live, and solely maintains the engine files. Everyone inherits the constitution automatically — CLAUDE.md and GEMINI.md are symlinks to AGENTS.md, so whatever agent you run reads the same rules.
SEAT A
Fully self-serve: your agent does the whole loop. The Max plan has the usage headroom for long agent sessions — the team now runs TWO Max accounts — the owner and Seat A.
Your loop: branch → agent writes/edits your one file → python3 -m togi → suite green → small PR → rebase when asked. Keep every change inside your file; if you genuinely must touch a shared file, say so in the group chat first and keep that PR tiny.
DAY-1 CHECKLIST
SEAT B
One person, two tools. Antigravity CLI is your DAILY coding agent — it runs the same loop as Seat A, word for word, and reads the same constitution (GEMINI.md is a symlink to AGENTS.md, so nothing is Claude-specific). Claude Free is your judgment tool for the high-level moments.
"But Claude Free only gives me a handful of prompts a day." The stack is designed so that's ENOUGH:
- The repo context files teach the project (
AGENTS.md,BUILD_STATE.md, the guides) — agents need almost no explaining. - Antigravity CLI does the daily coding. It reads the same
AGENTS.mdand is unlimited by Claude's plan. - Claude Free is reserved for where its judgment matters most: designing the sport file, reviewing a tricky edge idea, sanity-checking a verdict. A few sharp prompts a day, written with the prompt patterns in this guide — one well-formed prompt beats ten vague ones.
Antigravity cautions: never edit CLAUDE.md or GEMINI.md — they're symlinks; the real file is AGENTS.md, and even that is the owner's to maintain. Antigravity is likelier to helpfully "improve" shared files than to stay in one file, so put the fence in the prompt every time: "Keep the change to iterations/togi/<sport>.py only; flag any shared-file edit before making it." The paste-ready brief at the bottom of docs/togi-onboarding.md already says this — use it verbatim. Run the same suite the same way; green means the same thing for everyone.
DAY-1 CHECKLIST
No agent handy? You can also land the file yourself with plain git (six commands):
git clone git@github.com:bavatharannanthan7-lang/ned-v1.1.git ned
cd ned
git switch -c feat/togi-<sport>
# save Claude's file as iterations/togi/<sport>.py, then:
git add iterations/togi/<sport>.py
git commit -m "togi: add <SPORT> sport module"
git push -u origin feat/togi-<sport>
08The Per-Sport Recipe
What a sport file actually is, mirrored from the real one (iterations/togi/mlb.py).
"""TOGI · <SPORT> — pre-game moneylines."""
from togi import core
core.register(
"<SPORT>", # must match sports_feed.SPORTS: MLB / NBA / NFL / NHL / WNBA
home_edge=0.035, # home-field prob bump (MLB uses +3.5pp)
min_edge=0.07, # log floor — below this the divergence is noise
max_edge=0.15, # log cap — above this the model is MISSING a feature, not finding edge
# prob_fn=my_model, # def my_model(game, cfg) -> P(home win); the seam where Mimir FV plugs in
)
That's the whole registration. The engine signature is
core.register(code, *, home_edge, min_edge=0.07, max_edge=0.15, prob_fn=None, model="log5")
— your file provides the config, core.py provides scan → dedup shadow-log → settle vs on-chain outcomePrices. Your prob_fn (if you pass one) receives the game dict from sports_feed.games() (home/away with rec/wpct, state) and returns P(home win) in (0,1), or None to skip. core only bets state == "pre" games — leak-safe by construction.
Where the edge actually comes from — read this before "improving" the model
Togi is a sports staleness-taker, not an out-modeler. The live tape measured MLB moneyline spreads at a median 1.0¢ and falsified record-based log5 "edges" of 14–47pp as phantoms — out-modeling a sharp-copied line FAILS, and that verdict is part of the moat. The winnable edge: a de-vigged sharp reference (Pinnacle via The Odds API, mm/sharp.py, game-gated scheduler on the free tier) feeds Mimir (mm/fair.py — de-vig → confidence → microprice → blend → basis → is_stale), and is_stale fires when the sharp just stepped AND Polymarket's basis is abnormally wide — the market lagging a fresh signal. The sub-60 s window is validated (+2.8¢ @ 15 s decaying past the ~51 s reprice lag) and accruing to its n≥300 gate. Togi consumes that same staleness event as a paper taker fill; your sport file's prob_fn is the seam where Mimir's blended FV plugs in when the owner sequences that lane. Until then the default record model runs as the honest negative control — expect it to lose to the line; that IS its finding.
Data-point ideas go in the other plug-in surface
A pitcher/lineup/park/rest idea isn't a sport — it's one file in iterations/mm/signals/<name>.py calling signals.register(name, fn, cap_c=0.5) with fn(ctx) -> (delta_p, confidence, note). Auto-discovered, hard-capped at 0.5¢ until it self-validates on the tape (total cap 1¢), scored in SUGGESTION-mode — the harness recommends a cap change, the owner applies it. Copy example_probable_pitcher.py. Signal fns must be fail-open and read from cached/local stores (the live ones read the mlb_features cache, the weather cache, or the lake events tape) — never make blocking network calls inside a scan pass (timer passes do the fetching).
What it must pass before the owner merges
- The full suite green:
cd iterations && NED_PUSH_DISABLE=1 python3 -m unittest discover -s mm/tests -q(any test that could touch notify paths MUST setNED_PUSH_DISABLE=1— that's the notify contract). - A dry
python3 -m togithat prints sane reads for your sport. - Honest n reporting. Report what the sample supports and say how small it is — Wilson lower bound, not the point estimate. "It worked 3 times" is not evidence; small n stated honestly is fine.
- The diff stays inside your file(s). One sport file (or one signal file), tests if you added logic.
What NOT to do
- No engine edits —
togi/core.py,sports_feed.py, and allmm/engine files are single-maintainer. - No re-tunes of anything locked, and no re-tuning your own sport into the market after the fact ("just one more filter" is the banned move).
- No out-modeling the line and no relitigating falsified ideas (log5-as-edge, spread-capture on 1¢ moneylines, BB-Z fade — the graveyard is part of the moat).
- No network calls inside scan-time signal fns; no committing ledgers (
togi_shadow.jsonlis live single-owner data and gitignored); no hand-editing any live book, ever.
09Ground Rules — the Constitution Recap
From AGENTS.md. Everyone inherits these automatically — the repo teaches your agent. Everything is PAPER, real_capital = NO, fleet-wide; that never changes.
- PAPER only.
real_capital = NOfleet-wide until the canonical gate (data.gate) is met AND the owner flips the governor switches. - Zero orders in
mm/— by construction: no wallet, no key, no order call exists in the package. - One lane at a time (owner directive) — don't start a lane the owner hasn't sequenced.
- NO RE-TUNES, EVER on locked specs; the tuning levers were exhausted to null by repeated audits.
- Engine files are single-maintainer. The plug-in surface is
togi/<sport>.py,mm/signals/, andmm/params.py— nothing else. - Single-owner live data. Ledgers, launchd/systemd jobs, keys: owner's machines only, all gitignored. Never write or "fix" a live book by hand — code changes only, via PR.
- Gates, not vibes. Every edge claim = the live gate object / Wilson lower bound, never a stale snapshot or a point estimate. Exits COUNT as losses in the gate.
- SUGGESTION-mode everywhere. Studies and scorers recommend; the owner applies. Nothing self-mutates its own deploy.
- Settle against reality. On-chain
outcomePricesalways; fees exact; a fill exists only on a real print. Measure a signal before wiring it live, and prove it out-of-sample. - Rewards/rebates are ledgered separately and never flip a gate green.
Branch per person, rebase, small PRs, the owner merges and runs live. When in doubt: ask in the group chat before editing anything shared — that one habit prevents every collision this setup was designed to avoid.
10Owner's Per-Sport Data Points
The owner's domain map: what matters in each sport, where to look, and how each point feeds the model. The data-point lists are the owner's own words, organized. The "how it feeds the model" framing is the build-side synthesis. The constitution still applies: one point = one measured signal, paper only, no re-tunes, the scoreboard decides.
How ANY data point becomes edge here — read this first
The anchor is the bookmaker line, not the stat. We already ingest de-vigged sharp odds (Pinnacle via The Odds API — mm/sharp.py, budget-gated). That line already prices most public stats. So the bar for every data point below is never "is this stat good?" It is "does this stat move the probability BEYOND what the sharp line already knows?" The owner's phrasing is exactly right: "everything you can plug into your model to see if Vegas is missing something." In our machinery, "is Vegas missing it" has a precise score: CLV — does the point consistently beat the closing line? A point that does, earns. A point that doesn't, shrinks. Every source named below is verified + PIT-graded in docs/FREE_DATA_SOURCES.md — read it before wiring anything.
A data point can earn in exactly three ways:
- NUDGE — a small capped adjustment vs the sharp line (the
mm/signals/pattern: one file, 0.5¢ starter cap, self-scoring earn→1.0¢ / hold / shrink). This is where most fundamentals land — MLB already runs four (platoon splits, pitcher form, weather/park, scratch-veto). - VETO — information the line may not have absorbed yet (a late scratch, an inactive, a weather flip) that blocks a take entirely. Often the highest-value form, because it's about SPEED, not disagreement.
- MODEL FEATURE — for markets with no live sharp reference (many props), the points feed the sport's
prob_fndirectly and BECOME the fair value. This is where deep domain maps like the NFL one below matter most, because there's no sharp line doing the work for you.
Honest expectation: the staleness studies taught us fundamentals are mostly priced — the durable edges are speed (reacting to sharp moves) plus many small residual nudges. Expect a good point to earn 0.5–1¢, not 10¢. Twenty measured half-cent points that survive their gates is a real model; one 10¢ opinion is a blown bankroll.
NFL — the owner's deepest map (for the NFL friend)
Team context
| Point | Why it matters | Where to look |
|---|---|---|
| Home / away splits | teams are not the same team on the road | nflverse (nflreadpy — nfl_data_py is deprecated), Pro-Football-Reference |
| Team power rankings | a stable prior beneath weekly noise | build from EPA/play (nflverse) rather than pundit lists |
| Strength of schedule | context for every rate stat | schedules in nflverse / PFR |
| Rest — days since last game, short weeks, Thursday games, post-bye | fatigue + prep time are real, measurable, and sometimes underpriced | schedule math (ESPN feed — sports_feed.py already speaks ESPN) |
Coaching & scheme (OC / DC)
| Point | Why it matters | Where to look |
|---|---|---|
| OC / DC identity + tendencies | the scheme is the context every player stat lives in | nflverse FTN charting, team pages |
| Zone vs man coverage rate | the owner's core idea: players don't perform vs "a defense," they perform vs a COVERAGE | nflverse participation (2016–22 in-season-safe; 2023+ releases only post-season — a look-ahead trap for backtests) |
| Blitz rate | pressure changes everything downstream | FTN/nflverse |
| Zone-scheme run rate (inside/outside zone) | run-game matchup vs front | FTN/nflverse play-by-play |
The matchup engine (player-vs-coverage — the heart of the props model)
| Point | Why it matters | Where to look |
|---|---|---|
| WR/TE/RB production vs man vs zone (receptions, yards, targets) | a WR who feasts on zone facing a zone-heavy DC is a real, specific edge — this is PROP fair value | FTN charting joined to play-by-play |
| RB receiving vs zone coverage | checkdown volume is scheme-driven | same |
| WR power rankings (separation, target share) | who actually gets open + fed | nflverse target/route data |
| Red-zone targets | TD props live and die here | play-by-play splits |
Pressure ↔ QB conversion (the owner's two-sided read)
| Point | Why it matters | Where to look |
|---|---|---|
| Defense: blitz rate, pressure rate, time-to-pressure | how fast the pocket collapses | FTN/nflverse |
| O-line quality (pressure rate allowed, pass-block win rate) | converts the defense's numbers into THIS game | nflverse; PFF if ever paid |
| QB time-to-throw | fast-release QBs neutralize pressure; slow ones amplify it | NFL Next Gen Stats (public pages) |
| QB performance under pressure vs clean pocket | the split that decides pressure-heavy matchups | nflverse splits |
How NFL feeds the model: moneyline/spread → team-level nudges vs the sharp. But the real prize is the matchup engine → player props — receptions/yards vs coverage type. Props are exactly the "softer markets" our market-making work already flagged as the winnable pond (wider spreads, weaker lines, sometimes no sharp reference at all — so the model feature path applies). The synergy: owner's map picks the direction, the friend builds it as signals/prob_fn, our lake + books data says which prop markets are liquid enough to take.
MLB — the NFL pattern applied (for the MLB friend)
Four of these are ALREADY LIVE as capped signals (platoon splits, pitcher form, weather/park, scratch-veto) — the MLB friend extends the same pattern, one file per point.
Pitching (the "offense" of MLB edges)
| Point | Why it matters | Where to look |
|---|---|---|
| Pitch count trends / times-through-order | starters decay predictably; lines lag the decay | MLB StatsAPI (we already pull probable pitchers) |
| Pitch arsenal — mix, per-pitch velocity, movement | arsenal vs lineup is the MLB matchup engine | Baseball Savant / Statcast (pybaseball, free) |
| Velocity trend | the earliest fatigue/injury tell — a veto candidate | Statcast game-by-game velo |
| The whole mound — bullpen quality + usage last 3 days | a gassed bullpen flips late-game probability | StatsAPI reliever gameLogs + Statcast (FanGraphs is CAPTCHA-blocked for scripts) |
Hitting & splits
| Point | Why it matters | Where to look |
|---|---|---|
| R/L handedness splits (batter vs pitcher hand) | already live as platoon_splits — the template | StatsAPI / Baseball-Reference |
| Team season batting (BA/OPS/wRC+), recent form | the lineup-wide prior | StatsAPI byDateRange (FG blocks scripts) |
| Batters vs pitch types | arsenal × lineup interaction | Statcast |
The "lines" analog + context
| Point | Why it matters | Where to look |
|---|---|---|
| Catcher framing / team defense (OAA/DRS) | MLB's O-line: invisible runs | Statcast OAA, FanGraphs DRS |
| Park factors + weather | already live as weather_park | built |
| Rest / schedule — series game #, travel, doubleheaders, SOS | same rest logic as NFL | schedule math (ESPN/StatsAPI) |
NBA · UFC — owner's maps coming next
The owner knows these deepest and will dictate the points (rest/back-to-backs, rotations, style matchups for NBA; camp, reach, style, cardio for UFC). Same recipe will apply: anchor on the sharp, one point = one measured signal, props where the lines are soft.
The combination play — why this three-way split wins
Owner's domain map (what actually matters in the sport) × friend's build (one signal file per point, measured honestly) × the platform (sharp anchor + the data lake + self-scoring + gates) = the model.
- The owner doesn't have to code; the friends don't have to know where the edge hides; the platform doesn't have to have opinions. Each supplies the piece the others can't.
- Every point self-scores (hit-rate Wilson-LB, CLV vs the close, ICIR consistency). The scoreboard decides, not the loudest voice — a point the owner loves that doesn't beat the close shrinks to 0¢, and that's the system working, not an insult.
- Bookmaker odds run through everything twice: as the anchor each nudge is measured against, and as the speed edge (staleness) when the sharp moves and the market lags — both already built.
- Priority order for a new sport: (1) the veto-class points first (scratches/inactives — cheap, high value), (2) the matchup engine for props (where lines are softest), (3) team-level nudges last (where lines are sharpest). That's deliberately backwards from how most people start — and it's why it works.