---
name: pin_fair >= 0.15 is an EXECUTION-ONLY floor — never apply it to the forward test
description: User instruction 2026-07-25 — no real money on bets whose Pinnacle fair is under 15%. Deliberately look-ahead-derived and deliberately confined to order placement; the signal still fires, still logs, and still counts unfiltered in the frozen forward test. Wired as daily_signals.pin_fair_ok(), shared by the Kalshi and Poly-US placers.
metadata:
  type: project
---

User instruction, 2026-07-25, verbatim framing: *"another look ahead thing, but I
want it to be about execution only not testing: don't bet on anything where pin
fair is under 15%."*

## The distinction that matters

They know the cutoff was chosen by looking at outcomes. They are buying **risk
comfort with real dollars**, not claiming an edge. The whole value of the
instruction is that it stops at the order layer:

- **Execution** — blocked. No Kalshi order, no Poly-US order.
- **Signal / pending_bets / frozen forward test** — untouched. Bets below 15%
  still fire and still count, so the strategy keeps being measured unfiltered.

**Never apply AUTO_TRADE_MIN_PIN_FAIR anywhere in `research/` or to the forward
test.** If it leaks there, the test starts fitting the same data that chose the
cutoff and the evaluation is worthless. This is the same firewall as the BSA
exclusion — cf. [[project_pin_calibration_verified]], where BSA was excluded from
real orders on identical reasoning while its signals kept accruing.

## Where it lives

`daily_signals.AUTO_TRADE_MIN_PIN_FAIR = 0.15` and `daily_signals.pin_fair_ok()`.
Called by BOTH `daily_signals.place_auto_order` (Kalshi) and
`poly_us_trader.place_auto_order` (imports it rather than reimplementing) — if the
two ever diverge, a signal blocked on one venue could still fund the other.
Boundary is inclusive: exactly 0.15 trades, 0.1499 does not. A missing/unparseable
`pin_fair` ALLOWS the order and prints a warning (xlow entries always carry the
field; absence means a non-xlow caller, and blocking those would be a much bigger
change than was asked for).

Requires a **live_monitor restart** to take effect — `daily_signals` is cached in
the running process. See [[feedback_restart_live_monitor_after_config]].

## What it would have cut (honest numbers)

Forward test at the time of wiring, n=100: **13 bets had pin_fair < 0.15 and all
13 lost.** Removing them takes the forward test from n=100 / -2.7% ROI to
**n=87 / +11.8%**.

That number is much weaker than it looks and should not be quoted as evidence:

- Those 13 were priced 0.090–0.140, **expected wins ≈ 1.6**, and
  **P(0 wins | prices are fair) = 18.1%.** A perfectly fair longshot bucket goes
  0-for-13 about one time in five.
- Removing a bucket *selected because it lost* mechanically produces a better
  remainder. The +11.8% is an arithmetic consequence, not a discovery.
- It heavily overlaps the already-logged passive `XLOW_PRICE_FLOOR = 0.10`
  variant (sub-10c bucket was 0W-12L in the backtest) — see
  [[project_t60_variant]] for how passive variants are tracked. Same underlying
  observation, not independent confirmation.

The genuine reason to like the filter is the convergent prior noted in
`forward_test_unique_book.py`: profit in the backtest lived at 15–25% win
probability, the >=12% CLV cap and the [25,100) CLV wipeout point the same way.
Even so, treat it as a preference until n is far larger — cf.
[[project_xlow_rejection_criteria]], where the real decision point is n=1,000.

To revisit: re-run the split with
`research/forward_test_unique_book.py`'s `fire_xlow` instrumented to record
`pin_fair` (the bet dicts only carry clv/px/won by default).
