recall_guard.portfolio.cmmd
recall_guard.portfolio.cmmd
Top-quintile p_memorized filter for the cmmd-backtest pipeline.
Covers Reqs 6.1, 6.2, and 6.4:
- 6.1: drop the top
(1 - quantile)slice of the parse-OKp_memorizeddistribution (default = top 20%). - 6.2: return the empirical threshold so the orchestrator can record
it in the manifest's
backtestblock. - 6.4: filter only. Never mutate
predicted_direction(or any other attribute) of a surviving row, and never reorder.
Layer rules (.sentrux/rules.toml)
portfolio is order=1 and harness is order=0, so this module
cannot import harness.evaluator.Record directly. Instead the
public function accepts any object exposing parse_ok: bool and
p_memorized: float | None (structural typing via
:class:typing.Protocol). The orchestrator passes real Record
instances; the unit tests pass types.SimpleNamespace stand-ins.
Surviving rows come back in input order, so callers can join the
output back to their original (date, ticker) indexing without
re-sorting.
apply_cmmd_filter
apply_cmmd_filter(records, quantile=0.8)
Drop the top (1 - quantile) slice of parse-OK p_memorized.
Two-stage filter:
- Drop rows where
parse_okis False orp_memorizedis None or non-finite (NaN/inf cannot rank, and a single NaN would poison the quantile into dropping everything). The percentile is computed over the surviving distribution only, so failed rows cannot bias the cutoff. - Drop the
floor((1 - quantile) * n)highest-p_memorizedsurvivors by rank. Ties at the cut boundary are broken deterministically: among equal scores, later input positions are dropped first, so the earliest rows survive. Rank-based dropping guarantees the intended slice is removed even when many rows tie at the cutoff value (an inclusive<= thresholdrule would keep the entire tied block and filter nothing).
The returned list preserves the input order of surviving rows (Req 6.4) and the function never mutates a row.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
records
|
list[R]
|
Iterable of record-shaped objects. Anything matching the
:class: |
required |
quantile
|
float
|
Cut-point in |
0.8
|
Returns:
| Type | Description |
|---|---|
tuple[list[R], float]
|
The surviving records (original order) and the empirical
|
Raises:
| Type | Description |
|---|---|
ValueError
|
When |
Source code in recall_guard/portfolio/cmmd.py
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | |