A cross-repo audit verifying that the NEW standalone algo (/Users/levander/coding/pmv2, crate cohort_algo + pmv2-contracts) mirrors the PREVIOUS algo (/Users/levander/coding/polymarket_fetch, crate polymarket-fetch, src/pmv2) — especially the decision math. Verdict: core decision math is byte-for-byte identical; the only material behavior changes are deliberate (gate relocation, wire-format redesign, safety hardening) plus one genuine candidate behavior loss (the EV-floor gate was dropped).
For Agents — verdict at a glance
- Math = byte-for-byte identical (verified on disk): trader scoring, consensus trigger, sizing.
- Architecture shift (intentional): the old executor-side gate battery moved UPSTREAM into the producer; same formulas, new location.
- 1 possible accidental loss: the EV-floor gate was dropped (operator chose “just report, don’t change” — likely delegated downstream to @deploy’s position-manager).
- Deliberate divergences: wire format v1→v2 redesign, reference_price entry-band check dropped, safety hardening (fail-closed breaker + exit guards), operational constant tuning.
- Method: 4 parallel comparison agents; all load-bearing math re-verified directly against disk by the main agent.
Repos compared
| OLD (previous algo) | NEW (standalone algo) | |
|---|---|---|
| Path | /Users/levander/coding/polymarket_fetch | /Users/levander/coding/pmv2 |
| Crate(s) | polymarket-fetch (src/pmv2) | cohort_algo + pmv2-contracts |
| Gate battery home | autotrade engine (consumer/execution side) | producer (watch/cohort signal path) |
| Wire | cohort.signals schema_v1 | `pmv2.order.{source}.entry |
See cohort-algo-component for the new component, cohort-signals-service-split-and-exit-signals for the in-monorepo carve it was ported from, and cohort-algo-v2-wire-contract-cutover for the v2 wire cutover.
Identical core decision math (verified on disk)
Trader scoring (model.rs)
Byte-for-byte identical:
CONF_*confidence bands: 0.06 / 0.035 / 0.02 / 0.008.consistency_factor.long_term_factor.trader_score = e · (0.5 · long_term + 0.5)(long-term as a 0.5–1.0 trust multiplier — never zeroes a real edge).bet_confidence = ((entry − lost) / (won − lost)).clamp(0, 1).
Cross-ref the design in pmv2-two-tier-trader-scoring and the scoring-math corrections in correctness-pass-six-fix-commits.
Consensus (category_consensus.rs / alerts_consumer.rs)
Trigger + window logic identical:
- Consensus fires at exactly
holders.len() == 2. MIN_COHORT_SCORE = 0.0.CONSENSUS_WINDOW = 48h.EXIT_DEBOUNCE_SECS = 60.EXIT_FANOUT_PERMITS = 4.eligible_buyuses<=;eligible_selluses strict>.position_keptusesinitial_value > 1.0.truncate(25).
The 2-trader convergence gate matches the tracked-alerts-consumer design.
Sizing (positions.rs → signals/sizing.rs)
Old decide_copy (positions.rs) → new size_usd (signals/sizing.rs), identical:
score_weight = clamp(score, 0, 1) · 10 + 1
raw = base · score_weight · conviction
size_usd = clamp(raw, min, max)
The old inline score-threshold gate (score <= threshold → None) moved to score_threshold_gate with the same strict > comparison.
For Agents — sizing portfolio caps
The sizing formula is identical, but portfolio caps are deliberately EXCLUDED from the producer’s
size_usd— those stay withposition_manager(it holds the ledger). See cohort-algo-v2-wire-contract-cutover and position-manager-interface-design.
Architectural insight — the gate battery moved UPSTREAM
The old gate battery (chase, liveness, entry-band, category, min_conviction, ev_floor, stale) lived in the autotrade ENGINE (consumer / execution side). The new algo pulls it UPSTREAM into the producer (watch/cohort.rs → signal_gate_decision + apply_io_gates) with identical formulas.
This matches the babylon #292 split (producer owns signal-level gates; PM owns mode/breaker/portfolio-caps because PM has no GammaClient). The pre-live gating work that closed 4 of these gates is documented in cohort-pre-live-signal-gating.
Genuine divergences
1. EV-floor gate DROPPED (the one possible accidental loss)
- OLD:
ev_floor_gate(size, size · 0.02 via ROUND_TRIP_COST_RATE, min_round_trip_edge_bps)atengine.rs:154. - NEW: no equivalent. The
min_round_trip_edge_bpsconfig field is still loaded but inert. - Operator decision: “just report, don’t change.” Likely intentionally delegated to @deploy’s downstream position-manager.
- This is the one item that reads as a possible accidental behavior loss — flag for review before live-arming.
Possible behavior loss
The EV-floor gate has no new equivalent and
min_round_trip_edge_bpsis loaded-but-inert. Note: cohort-pre-live-signal-gating independently judged the OLDev_floor_gateto be degenerate (size cancels out → it reduced tobps > 200, price-blind) and dropped it deliberately, with operators expected to setmax_entry_premium_bpsnet of round-trip cost. So the drop may be both “accidental-looking” AND “the right call” — but confirm the downstream EV protection actually exists before arming.
2. reference_price entry-band check dropped
- OLD: the engine checked the entry band on BOTH
best_askANDreference_price. - NEW: the producer checks
best_askonly.
3. Wire format v1→v2 redesign (deliberate, not mirroring)
Full redesign, not a port. See cohort-algo-v2-wire-contract-cutover for the canonical write-up.
- Subject:
cohort.signals→pmv2.order.{source}.entry/.exit. schema_version:1(i64) →2(u32).- Envelope: flat
f64→ nestedmarketobject + serde-taggedPricingenum withDecimal(string-encoded). - New fields:
size_usd,kind(entry/exit),produced_at,valid_until_ts(entry TTL = 300s viaFIRST_MOVER_TTL_SECS). - Exit reshaped: from a full Sell-sided
TradeSignal(reference_price = cur_price) into a minimalExitSignal(no side / pricing / token / metadata). - v1 and v2 are mutually unparseable.
4. Safety hardening (new is MORE conservative)
- Breaker read-error default flipped fail-open(false) → fail-closed(true).
- New exit guard
snapshot_trustworthy— skip exit when the prior snapshot was nonempty but a fresh fetch returns empty (avoids mass false-exit on an empty Data-API200 []). Same class as the carve’s bug fix in cohort-algo-component. - New exit guard
should_record_exit— only delete the position row when the Sell actually published (avoids the dropped-publish-still-deletes-row capital trap, also a carve fix).
5. Operational constant tuning (category_consensus.rs)
Consensus formula unchanged; only operational knobs retuned:
BET_LINK_TIMEOUT_SECS: 25 → 90.CONSENSUS_CACHE_WINDOW_SECS: 600 → 3600.
Method
Audit performed via 4 parallel comparison agents, each owning one surface:
- sizing / gates,
- firstmover / sources / config,
- consensus / exit timing,
- wire / envelope.
All load-bearing math was re-verified directly against disk by the main agent — specifically:
decide_copyvssize_usd(formula byte-compare),ev_floorenforcement grep (confirmed no new call site),engine.rsgate call site (theengine.rs:154EV-floor invocation).
Standing rule applied
Subagent reports were verified against disk before being trusted — consistent with the project’s “always verify subagent output against disk” discipline (one subagent has hallucinated in this slice’s history).
Related
- cohort-algo-component — the standalone NEW component
- cohort-algo-v2-wire-contract-cutover — the v1→v2 wire redesign (divergence 3)
- cohort-pre-live-signal-gating — the 4 first-mover gates + the deliberate ev_floor drop rationale
- cohort-signals-service-split-and-exit-signals — the in-monorepo carve the new algo was ported from
- pmv2-two-tier-trader-scoring — the trader-scoring design (identical math here)
- correctness-pass-six-fix-commits — scoring-math corrections carried into both
- tracked-alerts-consumer — the 2-trader consensus convergence gate
- position-manager-interface-design — downstream executor (where EV protection may now live)