solv1-alpha — a Rust walk-forward validation harness (workspace member alpha/ in repo ~/coding/slv1, sibling to the now-shelved solv1-rig) — is built and green. Its whole job is to render a structured go/no-go verdict on the Solana meme-coin copy-trading thesis from historical wallet data, before any live latency rig or capital is committed. 16 tasks, subagent-driven on Opus (each task Opus-implemented + Opus-reviewed), plus a final whole-branch review that returned 1 must-fix (now applied).
For Agents
This harness is offline analysis only — it reads historical trades and simulates paper fills; it never signs or sends a transaction. It answers “is there a copyable edge at all?”, which is upstream of the latency question solv1-rig measures.
solv1-rigis shelved pending this verdict.The build is complete AND the first real verdict is now in: NO-GO on naive copy-trading. The original RPC run stalled on an ingestion blocker (not a code bug — see solv1-alpha-data-source-pivot-2026-07-04); after pivoting to the GMGN-ingester + 1s-kline decay, run
f02ed44f(3wk train / 1wk test) rendered a verdict: cohort +23.0% vs a random meme basket +109.5% → NO-GO, but the edge concentrates entirely in position/long-holds (+296.1%) while scalps bleed (−20.5%). All analysis stages (score → filters → cohort → decay → mirror → benchmarks → verdict) ran as designed.Key integrity facts: the final review confirmed walk-forward integrity is sound — no train/test leak, and the split boundary is applied consistently across all 10 query sites. The train/test split is anchored on
run.created_at − test_weeks(never on data max). The verdict prints a survivorship upper-bound caveat verbatim — every headline number is a ceiling, because the wallet universe is leaderboard-selected. Storage: 7solv1_alpha_*tables on the Supabasemgmtproject (mkofmdtdldxgmmolxxhc).
What Shipped
One crate (alpha/, tokio async, sqlx/Postgres), a single binary with the full pipeline plus a report readout. 16 feature tasks + 1 whole-branch-review must-fix, subagent-driven on Opus, every task individually reviewed. 48 tests green, exercised against a live throwaway Postgres.
Commit range 67183a6..2f1f69b — 17 commits (16 feat(alpha) + the closing fix(alpha)):
| Stage | Commit | Module(s) |
|---|---|---|
scaffold: workspace member, config :6543 guard, CLI skeleton | 4f87bbc | config.rs main.rs |
solv1_alpha schema v1 (applied to mgmt via MCP) | 442621f | migrations/0002_solv1_alpha.sql |
| core types + batched trade sink | e83e80e | types.rs db.rs |
| Chainstack RPC client (pagination + bounded retry) | de7faae | rpc.rs |
| swap parser from token-balance deltas (per-venue fixtures) | 9c5f2ab | swap.rs |
| GMGN/Dragon universe fetch (+ seed-list fallback) | 964ac71 | universe.rs |
resumable RPC indexer → solv1_alpha_trades | 74e1c10 | index.rs |
| training-window scoring (SQL), split boundary enforced | e7cdc74 | score.rs |
| walk-forward lookahead guard | 4d21363 | cohort.rs |
| wash / self-dealing filter | a35a3c3 | filters.rs |
| copy-bait sell-into-followers filter | 028a358 | filters.rs |
| age/funding filter + cohort selection | a0401cc | filters.rs cohort.rs |
| follower-fill decay from subsequent fills | 737369e | decay.rs |
| paper-mirror test-window trades (decay + fees) | 863a4c5 | mirror.rs |
| SOL-hold + random-basket benchmarks | bc99bd5 | bench.rs |
| structured go/no-go verdict + full-pipeline run | 70a3078 | run.rs |
must-fix: skip reversed-order mirror positions; guard sol_size in capacity_sweep | 2f1f69b | mirror.rs bench.rs |
Pipeline
Ten stages, offline, end-to-end in run.rs:
Validation pipeline (offline, historical → verdict)
graph LR U["universe<br/>GMGN leaderboard"] --> IDX["index<br/>RPC history + swap parse"] IDX --> SPLIT{{"walk-forward split<br/><b>anchor = created_at − test_weeks</b>"}} SPLIT -->|train| SCORE["score<br/>net-SOL-flow PnL,<br/>win-rate per token"] SCORE --> FILT["adverse-selection filters<br/>wash · copy-bait · age/funding"] FILT --> COH["cohort select<br/>precedence-ordered cut_reason"] COH --> GUARD["lookahead guard<br/>non-tautological<br/>trade_count recompute"] GUARD -->|test window| MIR["paper-mirror<br/>decayed fills + fees<br/>sol_size sweep"] SPLIT -->|test| MIR MIR --> BENCH["benchmarks<br/>SOL-hold + random basket"] BENCH --> V["go/no-go verdict<br/><b>4 conditions + caveat</b>"] style SPLIT fill:#3d2020,stroke:#c1666b,color:#fff style GUARD fill:#3d2020,stroke:#c1666b,color:#fff style V fill:#2d2d2d,stroke:#e9c46a,color:#fffThe two red nodes are the anti-leak spine — see Walk-Forward Integrity. Cohort selection is trained on the train window only; the paper-mirror only ever fills against the test window.
Walk-Forward Integrity
This is the load-bearing property — a backtest that leaks future information into wallet selection produces a fictitious edge. The final whole-branch review specifically audited this and confirmed it sound:
- Split anchored on
run.created_at − test_weeks, never onmax(timestamp)of the data. Anchoring on the data’s own maximum would silently move the boundary as more history arrives; anchoring on the run clock keeps the test window a fixed, honest hold-out. - The split boundary is applied consistently across all 10 query sites. Every SQL that touches the boundary uses the same comparison — no stage accidentally reads the full range. This uniformity was the specific thing the review verified.
- Wallet scoring and cohort selection see the TRAIN window only. The cohort (which wallets we would copy) is chosen without any visibility into test-window outcomes.
- Lookahead guard is non-tautological. It re-computes
trade_countfrom the test window independently rather than reusing the training figure, so the guard cannot trivially pass by construction — it can actually fail and drop a wallet whose activity doesn’t survive the split.
Survivorship is not eliminated, only bounded
The wallet universe comes from a GMGN 7d-PnL leaderboard — i.e. wallets that already won. No walk-forward split can remove that selection; it can only prevent additional leakage. The verdict therefore prints a survivorship upper-bound caveat verbatim: every headline PnL/edge number is a ceiling, and live forward performance will be strictly lower. This caveat is a first-class output, not a footnote.
Adverse-Selection Filters
Copy trading fails not on math but on who you copy. Three filters strip wallets whose realized edge is unrepeatable-by-you, each stamping a cut_reason resolved in precedence order (so a wallet cut for multiple reasons reports the dominant one):
- Wash / self-dealing — round-trip density: wallets churning the same token in tight in/out cycles are trading against themselves (or inflating volume), not finding alpha.
- Copy-bait — profit-weighted sell-into-tracked-inflows: wallets whose profits concentrate in selling into the very inflows a copier would create. Their edge is the copiers; mirroring them is buying their exit.
- Age / funding (sybil-proxy) — wallet age and funding-graph shape as a proxy for sybil / insider clusters that can’t be reproduced by an outside follower.
Cohort selection consumes the filtered set; the go/no-go verdict then checks that filtered performance exceeds raw (condition 4 below) — i.e. the filters must earn their keep, not just shrink the sample.
Paper-Mirror & Benchmarks
- Paper-mirror simulates a follower copying the selected cohort across the test window only. Entry/exit fills are decayed from the wallet’s actual subsequent on-chain fills (not a fixed haircut) — the follower lands after the leader, so the realized price is modeled from what actually traded next. Fills are net of fees, and a
sol_sizecapacity sweep measures how the edge erodes as position size scales (a real strategy must survive size). The must-fix2f1f69bhardened this: reversed-order positions are skipped andsol_sizeis guarded in the sweep. - Benchmarks give the verdict something to beat:
- SOL-hold — buy-and-hold SOL over the same window (the opportunity-cost floor).
- Reproducible random meme basket — a seeded random basket of meme tokens, so “did we beat picking at random?” is answerable and re-runnable deterministically.
Go/No-Go Verdict
run.rs emits a structured verdict gated on four conditions, all of which must hold:
- Beat SOL-hold by ≥ 10 percentage points — clear the opportunity-cost floor with margin.
- Beat the random meme basket — the edge is wallet-selection, not just meme-beta.
- Edge is not scalp-only — survives the
sol_sizecapacity sweep, i.e. isn’t an artifact that vanishes at tradeable size. - Filtered cohort outperforms raw — the adverse-selection filters add value.
Alongside the verdict the run prints the survivorship upper-bound caveat verbatim and 10 known limitations, so no reader mistakes a GO for a guarantee.
Storage
- Supabase
mgmtproject (refmkofmdtdldxgmmolxxhc), 7 tables prefixedsolv1_alpha_— schemamigrations/0002_solv1_alpha.sql, applied live via the Supabase MCP and mirrored in-repo. Distinct from thesolv1_-prefixed tables the rig owns on the same project. - Same connection discipline as the rig: Supavisor session mode
:5432(config hard-fails on:6543), RLS-on / FORCE-off, owner-writes. Full rationale: supabase-from-gcp-connection-gotchas.
Status & Verdict
- Build: on git
main(local), harness commit range67183a6..2f1f69b. 48 tests green against a throwaway Postgres. - Verdict: IN → NO-GO on naive copy-trading. Run
f02ed44f(3wk train / 1wk test, 28-day GMGN harvest, local Docker Postgres): cohort of 8 net +23.0% (real-kline-decayed, after fees) vs a random 50-meme basket +109.5% and below the SOL-hold+10pp bar. Key finding: the edge is entirely in position/long-holds (+296.1%); scalps lose −20.5% to entry decay; filtered−raw +14.8pp (filters add value). Survivorship upper bound → the NO-GO is hard. Refinement to test next: copy positions only, never scalps. - Path taken to the verdict: the first RPC run stalled on an ingestion blocker (non-archival Chainstack node + bot-polluted leaderboard — solv1-alpha-data-source-pivot-2026-07-04); the GMGN-ingester (browser-harvest + 1s-kline decay, now BUILT) replaced indexing tasks A4–A7, and the analysis stack A8–A16 (score → verdict) ran reused unchanged.
Related
- solv1-alpha-first-verdict-no-go-2026-07-04 — the first real verdict this harness produced (NO-GO; position-holds refinement)
- slv1 — project overview / strategy pipeline
- solv1-alpha-data-source-pivot-2026-07-04 — the ingestion blocker that stalled the first real run + the GMGN fix (read this next)
- solv1-alpha-gmgn-ingester-increment-2026-07-04 — the chosen browser-harvest → file-ingest increment that replaces RPC indexing (A4–A7)
- solv1-rig-increment-1-build-2026-07-03 — the shelved latency rig this verdict gates
- slv1-copy-trading-feasibility-2026-07-03 — the feasibility study this whole effort adjudicates
- supabase-from-gcp-connection-gotchas — pooler-mode + RLS-via-ownership storage recipe (shared with the rig)
- Spec (source of truth, detail):
~/coding/slv1/docs/superpowers/specs/2026-07-03-alpha-validation-design.md - Plan (source of truth, task-by-task):
~/coding/slv1/docs/superpowers/plans/2026-07-04-alpha-validation.md - LOG · TOPICS