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 homeautotrade engine (consumer/execution side)producer (watch/cohort signal path)
Wirecohort.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_buy uses <=; eligible_sell uses strict >.
  • position_kept uses initial_value > 1.0.
  • truncate(25).

The 2-trader convergence gate matches the tracked-alerts-consumer design.

Sizing (positions.rssignals/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 with position_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.rssignal_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) at engine.rs:154.
  • NEW: no equivalent. The min_round_trip_edge_bps config 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_bps is loaded-but-inert. Note: cohort-pre-live-signal-gating independently judged the OLD ev_floor_gate to be degenerate (size cancels out → it reduced to bps > 200, price-blind) and dropped it deliberately, with operators expected to set max_entry_premium_bps net 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_ask AND reference_price.
  • NEW: the producer checks best_ask only.

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.signalspmv2.order.{source}.entry / .exit.
  • schema_version: 1 (i64) → 2 (u32).
  • Envelope: flat f64 → nested market object + serde-tagged Pricing enum with Decimal (string-encoded).
  • New fields: size_usd, kind (entry/exit), produced_at, valid_until_ts (entry TTL = 300s via FIRST_MOVER_TTL_SECS).
  • Exit reshaped: from a full Sell-sided TradeSignal (reference_price = cur_price) into a minimal ExitSignal (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-API 200 []). 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:

  1. sizing / gates,
  2. firstmover / sources / config,
  3. consensus / exit timing,
  4. wire / envelope.

All load-bearing math was re-verified directly against disk by the main agent — specifically:

  • decide_copy vs size_usd (formula byte-compare),
  • ev_floor enforcement grep (confirmed no new call site),
  • engine.rs gate call site (the engine.rs:154 EV-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).